manifest: embed builtin resources
This commit is contained in:
parent
607923dfbd
commit
365ec8b7fa
|
|
@ -25,9 +25,6 @@
|
|||
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Environment variable that points at installed project resources.
|
||||
pub const RESOURCE_DIR_ENV: &str = "INSOMNIA_RESOURCE_DIR";
|
||||
|
||||
/// 設定ディレクトリ。`profiles.toml`, `providers.toml`, `models.toml`,
|
||||
/// `prompts/` などが置かれる。
|
||||
pub fn config_dir() -> Option<PathBuf> {
|
||||
|
|
@ -75,32 +72,6 @@ pub fn user_prompts_dir() -> Option<PathBuf> {
|
|||
user_prompts_dir_from_config_dir(config_dir())
|
||||
}
|
||||
|
||||
/// Root resource directory used for bundled prompts, profiles, catalogs, and docs.
|
||||
pub fn resource_dir() -> Option<PathBuf> {
|
||||
if let Some(p) = env_path(RESOURCE_DIR_ENV) {
|
||||
return Some(p);
|
||||
}
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(prefix) = exe.parent().and_then(|bin| bin.parent()) {
|
||||
let installed = prefix.join("share").join("insomnia").join("resources");
|
||||
if installed.exists() {
|
||||
return Some(installed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.join("resources"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Bundled Lua profile registry directory. Missing directories are treated as
|
||||
/// an empty builtin registry by discovery.
|
||||
pub fn builtin_profiles_dir() -> Option<PathBuf> {
|
||||
Some(resource_dir()?.join("profiles"))
|
||||
}
|
||||
|
||||
/// `<config_dir>/prompts.toml` — user prompt pack。
|
||||
pub fn user_pack_file() -> Option<PathBuf> {
|
||||
user_pack_file_from_config_dir(config_dir())
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ use crate::{
|
|||
|
||||
const PROFILE_FORMAT_V1: &str = "insomnia.lua-profile.v1";
|
||||
const BUILTIN_DEFAULT_PROFILE_NAME: &str = "default";
|
||||
const BUILTIN_DEFAULT_PROFILE: &str = include_str!("../../../resources/profiles/default.lua");
|
||||
const BUILTIN_MODEL_CATALOG: &str = include_str!("../../../resources/models/builtin.toml");
|
||||
const DEFAULT_POD_NAME: &str = "insomnia";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
|
|
@ -126,7 +128,10 @@ pub enum ProfileSource {
|
|||
Registry {
|
||||
source: ProfileRegistrySource,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
path: Option<PathBuf>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
provenance: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -134,15 +139,62 @@ pub enum ProfileSource {
|
|||
pub struct ProfileRegistryEntry {
|
||||
pub source: ProfileRegistrySource,
|
||||
pub name: String,
|
||||
pub path: PathBuf,
|
||||
pub path: Option<PathBuf>,
|
||||
pub provenance: String,
|
||||
pub description: Option<String>,
|
||||
pub is_default: bool,
|
||||
artifact: ProfileRegistryArtifact,
|
||||
}
|
||||
|
||||
impl ProfileRegistryEntry {
|
||||
pub fn qualified_name(&self) -> String {
|
||||
format!("{}:{}", self.source, self.name)
|
||||
}
|
||||
|
||||
fn path(
|
||||
source: ProfileRegistrySource,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
description: Option<String>,
|
||||
) -> Self {
|
||||
let provenance = path.display().to_string();
|
||||
Self {
|
||||
source,
|
||||
name,
|
||||
path: Some(path.clone()),
|
||||
provenance,
|
||||
description,
|
||||
is_default: false,
|
||||
artifact: ProfileRegistryArtifact::Path(path),
|
||||
}
|
||||
}
|
||||
|
||||
fn embedded(
|
||||
source: ProfileRegistrySource,
|
||||
name: &'static str,
|
||||
label: &'static str,
|
||||
content: &'static str,
|
||||
description: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
source,
|
||||
name: name.to_string(),
|
||||
path: None,
|
||||
provenance: label.to_string(),
|
||||
description,
|
||||
is_default: false,
|
||||
artifact: ProfileRegistryArtifact::Embedded { label, content },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum ProfileRegistryArtifact {
|
||||
Path(PathBuf),
|
||||
Embedded {
|
||||
label: &'static str,
|
||||
content: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
@ -241,7 +293,6 @@ struct ProfileDefault {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProfileDiscovery {
|
||||
builtin_dir: Option<PathBuf>,
|
||||
user_config: Option<PathBuf>,
|
||||
project_config: Option<PathBuf>,
|
||||
}
|
||||
|
|
@ -249,27 +300,19 @@ pub struct ProfileDiscovery {
|
|||
impl ProfileDiscovery {
|
||||
pub fn for_cwd(cwd: &Path) -> Self {
|
||||
Self {
|
||||
builtin_dir: paths::builtin_profiles_dir(),
|
||||
user_config: paths::user_profiles_path(),
|
||||
project_config: find_project_profiles_from(cwd),
|
||||
}
|
||||
}
|
||||
pub fn with_sources(
|
||||
builtin_dir: Option<PathBuf>,
|
||||
user_config: Option<PathBuf>,
|
||||
project_config: Option<PathBuf>,
|
||||
) -> Self {
|
||||
pub fn with_sources(user_config: Option<PathBuf>, project_config: Option<PathBuf>) -> Self {
|
||||
Self {
|
||||
builtin_dir,
|
||||
user_config,
|
||||
project_config,
|
||||
}
|
||||
}
|
||||
pub fn discover(&self) -> Result<ProfileRegistry, ProfileError> {
|
||||
let mut registry = ProfileRegistry::default();
|
||||
if let Some(dir) = &self.builtin_dir {
|
||||
discover_profile_dir(&mut registry, ProfileRegistrySource::Builtin, dir)?;
|
||||
}
|
||||
add_builtin_profiles(&mut registry);
|
||||
if let Some(path) = &self.user_config {
|
||||
load_profile_registry_file(&mut registry, ProfileRegistrySource::User, path)?;
|
||||
}
|
||||
|
|
@ -371,15 +414,27 @@ impl ProfileResolver {
|
|||
)),
|
||||
ProfileSelector::Named { .. } | ProfileSelector::Default => {
|
||||
let entry = registry.select(selector)?.clone();
|
||||
self.resolve_path(
|
||||
&entry.path,
|
||||
ProfileSource::Registry {
|
||||
let source = ProfileSource::Registry {
|
||||
source: entry.source,
|
||||
name: entry.name,
|
||||
path: absolutize(&entry.path)?,
|
||||
},
|
||||
options,
|
||||
)
|
||||
name: entry.name.clone(),
|
||||
path: entry.path.as_deref().map(absolutize).transpose()?,
|
||||
provenance: (entry.path.is_none()).then(|| entry.provenance.clone()),
|
||||
};
|
||||
self.resolve_registry_entry(&entry, source, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_registry_entry(
|
||||
&self,
|
||||
entry: &ProfileRegistryEntry,
|
||||
source: ProfileSource,
|
||||
options: ProfileResolveOptions,
|
||||
) -> Result<ResolvedProfile, ProfileError> {
|
||||
match &entry.artifact {
|
||||
ProfileRegistryArtifact::Path(path) => self.resolve_path(path, source, options),
|
||||
ProfileRegistryArtifact::Embedded { label, content } => {
|
||||
self.resolve_embedded_profile(label, content, source, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -431,6 +486,30 @@ impl ProfileResolver {
|
|||
raw_artifact,
|
||||
)
|
||||
}
|
||||
|
||||
fn resolve_embedded_profile(
|
||||
&self,
|
||||
label: &'static str,
|
||||
content: &'static str,
|
||||
source: ProfileSource,
|
||||
options: ProfileResolveOptions,
|
||||
) -> Result<ResolvedProfile, ProfileError> {
|
||||
let workspace_base = absolutize(
|
||||
self.workspace_base
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| Path::new(".")),
|
||||
)?;
|
||||
let lua_value = evaluate_embedded_lua_profile(label, content)?;
|
||||
let raw_artifact = lua_value.clone();
|
||||
resolve_lua_profile_value(
|
||||
source,
|
||||
&workspace_base,
|
||||
&workspace_base,
|
||||
options,
|
||||
lua_value,
|
||||
raw_artifact,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_lua_profile_value(
|
||||
|
|
@ -591,13 +670,12 @@ fn load_profile_registry_file(
|
|||
let base = path.parent().unwrap_or_else(|| Path::new("."));
|
||||
for (name, entry_config) in config.profile {
|
||||
let (entry_path, description) = entry_config.into_parts();
|
||||
registry.push_entry(ProfileRegistryEntry {
|
||||
registry.push_entry(ProfileRegistryEntry::path(
|
||||
source,
|
||||
name,
|
||||
path: join_if_relative(base, &entry_path),
|
||||
join_if_relative(base, &entry_path),
|
||||
description,
|
||||
is_default: false,
|
||||
});
|
||||
));
|
||||
}
|
||||
if let Some(default) = config.default {
|
||||
let (default_source, default_name) = parse_profile_ref(&default);
|
||||
|
|
@ -625,49 +703,14 @@ fn find_project_profiles_from(start: &Path) -> Option<PathBuf> {
|
|||
None
|
||||
}
|
||||
|
||||
fn discover_profile_dir(
|
||||
registry: &mut ProfileRegistry,
|
||||
source: ProfileRegistrySource,
|
||||
dir: &Path,
|
||||
) -> Result<(), ProfileError> {
|
||||
if !dir.is_dir() {
|
||||
return Ok(());
|
||||
}
|
||||
for entry in std::fs::read_dir(dir).map_err(|source| ProfileError::ConfigRead {
|
||||
path: dir.to_path_buf(),
|
||||
source,
|
||||
})? {
|
||||
let entry = entry.map_err(|source| ProfileError::ConfigRead {
|
||||
path: dir.to_path_buf(),
|
||||
source,
|
||||
})?;
|
||||
let path = entry.path();
|
||||
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("lua") {
|
||||
if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
|
||||
registry.push_entry(ProfileRegistryEntry {
|
||||
source,
|
||||
name: name.to_string(),
|
||||
path,
|
||||
description: None,
|
||||
is_default: false,
|
||||
});
|
||||
}
|
||||
} else if path.is_dir() {
|
||||
let profile = path.join("profile.lua");
|
||||
if profile.is_file()
|
||||
&& let Some(name) = path.file_name().and_then(|s| s.to_str())
|
||||
{
|
||||
registry.push_entry(ProfileRegistryEntry {
|
||||
source,
|
||||
name: name.to_string(),
|
||||
path: profile,
|
||||
description: None,
|
||||
is_default: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
fn add_builtin_profiles(registry: &mut ProfileRegistry) {
|
||||
registry.push_entry(ProfileRegistryEntry::embedded(
|
||||
ProfileRegistrySource::Builtin,
|
||||
BUILTIN_DEFAULT_PROFILE_NAME,
|
||||
"builtin:default",
|
||||
BUILTIN_DEFAULT_PROFILE,
|
||||
Some("Bundled default Insomnia coding profile".into()),
|
||||
));
|
||||
}
|
||||
|
||||
fn parse_profile_ref(raw: &str) -> (Option<ProfileRegistrySource>, String) {
|
||||
|
|
@ -687,15 +730,38 @@ fn evaluate_lua_profile(
|
|||
path: path.to_path_buf(),
|
||||
source,
|
||||
})?;
|
||||
evaluate_lua_profile_source(
|
||||
&content,
|
||||
path.display().to_string(),
|
||||
LocalModuleRoot::Filesystem(module_root.to_path_buf()),
|
||||
)
|
||||
}
|
||||
|
||||
fn evaluate_embedded_lua_profile(
|
||||
label: &'static str,
|
||||
content: &'static str,
|
||||
) -> Result<serde_json::Value, ProfileError> {
|
||||
evaluate_lua_profile_source(
|
||||
content,
|
||||
label.to_string(),
|
||||
LocalModuleRoot::Disabled { label },
|
||||
)
|
||||
}
|
||||
|
||||
fn evaluate_lua_profile_source(
|
||||
content: &str,
|
||||
chunk_name: String,
|
||||
module_root: LocalModuleRoot,
|
||||
) -> Result<serde_json::Value, ProfileError> {
|
||||
let lua = Lua::new_with(
|
||||
StdLib::TABLE | StdLib::STRING | StdLib::MATH | StdLib::UTF8,
|
||||
LuaOptions::default(),
|
||||
)
|
||||
.map_err(ProfileError::Lua)?;
|
||||
install_lua_api(&lua, module_root.to_path_buf())?;
|
||||
install_lua_api(&lua, module_root)?;
|
||||
let value: LuaValue = lua
|
||||
.load(&content)
|
||||
.set_name(path.display().to_string())
|
||||
.load(content)
|
||||
.set_name(chunk_name)
|
||||
.eval()
|
||||
.map_err(ProfileError::Lua)?;
|
||||
match value {
|
||||
|
|
@ -706,7 +772,7 @@ fn evaluate_lua_profile(
|
|||
}
|
||||
}
|
||||
|
||||
fn install_lua_api(lua: &Lua, module_root: PathBuf) -> Result<(), ProfileError> {
|
||||
fn install_lua_api(lua: &Lua, module_root: LocalModuleRoot) -> Result<(), ProfileError> {
|
||||
let loader = Rc::new(RefCell::new(LocalModuleLoader {
|
||||
root: module_root,
|
||||
cache: HashMap::new(),
|
||||
|
|
@ -743,11 +809,16 @@ fn install_lua_api(lua: &Lua, module_root: PathBuf) -> Result<(), ProfileError>
|
|||
}
|
||||
|
||||
struct LocalModuleLoader {
|
||||
root: PathBuf,
|
||||
root: LocalModuleRoot,
|
||||
cache: HashMap<String, RegistryKey>,
|
||||
loading: HashSet<String>,
|
||||
}
|
||||
|
||||
enum LocalModuleRoot {
|
||||
Filesystem(PathBuf),
|
||||
Disabled { label: &'static str },
|
||||
}
|
||||
|
||||
fn require_module(
|
||||
lua: &Lua,
|
||||
loader: &Rc<RefCell<LocalModuleLoader>>,
|
||||
|
|
@ -773,7 +844,19 @@ fn require_module(
|
|||
)));
|
||||
}
|
||||
}
|
||||
let path = local_module_path(&loader.borrow().root, name).map_err(mlua::Error::RuntimeError)?;
|
||||
let path = {
|
||||
let state = loader.borrow();
|
||||
match &state.root {
|
||||
LocalModuleRoot::Filesystem(root) => {
|
||||
local_module_path(root, name).map_err(mlua::Error::RuntimeError)?
|
||||
}
|
||||
LocalModuleRoot::Disabled { label } => {
|
||||
return Err(mlua::Error::RuntimeError(format!(
|
||||
"local require `{name}` is not available for embedded profile `{label}`"
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
let content = std::fs::read_to_string(&path).map_err(|e| {
|
||||
mlua::Error::RuntimeError(format!(
|
||||
"failed to read local module `{name}` ({}): {e}",
|
||||
|
|
@ -1042,9 +1125,7 @@ fn model_context_window(model: Option<&ModelManifest>) -> Option<u64> {
|
|||
}
|
||||
fn builtin_model_context_window(reference: &str) -> Option<u64> {
|
||||
let (provider, model_id) = reference.split_once('/')?;
|
||||
let path = paths::resource_dir()?.join("models").join("builtin.toml");
|
||||
let content = std::fs::read_to_string(path).ok()?;
|
||||
let parsed: toml::Value = toml::from_str(&content).ok()?;
|
||||
let parsed: toml::Value = toml::from_str(BUILTIN_MODEL_CATALOG).ok()?;
|
||||
for entry in parsed.get("model")?.as_array()? {
|
||||
let table = entry.as_table()?;
|
||||
if table.get("provider")?.as_str()? == provider && table.get("id")?.as_str()? == model_id {
|
||||
|
|
@ -1187,58 +1268,8 @@ pub enum ProfileError {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::{ReasoningControl, ReasoningEffort, SchemeKind};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn env_lock() -> MutexGuard<'static, ()> {
|
||||
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
LOCK.get_or_init(|| Mutex::new(()))
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
}
|
||||
struct EnvGuard {
|
||||
vars: Vec<(&'static str, Option<String>)>,
|
||||
_lock: MutexGuard<'static, ()>,
|
||||
}
|
||||
impl EnvGuard {
|
||||
fn new(overrides: &[(&'static str, Option<&str>)]) -> Self {
|
||||
let lock = env_lock();
|
||||
let names = [
|
||||
"INSOMNIA_CONFIG_DIR",
|
||||
"INSOMNIA_RESOURCE_DIR",
|
||||
"INSOMNIA_HOME",
|
||||
"XDG_CONFIG_HOME",
|
||||
"HOME",
|
||||
];
|
||||
let saved: Vec<_> = names.iter().map(|n| (*n, std::env::var(n).ok())).collect();
|
||||
unsafe {
|
||||
for (n, _) in &saved {
|
||||
std::env::remove_var(n);
|
||||
}
|
||||
for (n, v) in overrides {
|
||||
if let Some(v) = v {
|
||||
std::env::set_var(n, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
vars: saved,
|
||||
_lock: lock,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for EnvGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
for (n, v) in &self.vars {
|
||||
match v {
|
||||
Some(v) => std::env::set_var(n, v),
|
||||
None => std::env::remove_var(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn write_profile(dir: &Path, name: &str, body: &str) -> PathBuf {
|
||||
let path = dir.join(name);
|
||||
std::fs::write(&path, body).unwrap();
|
||||
|
|
@ -1266,14 +1297,15 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn builtin_default_profile_is_registered_as_default() {
|
||||
let registry = ProfileDiscovery::with_sources(paths::builtin_profiles_dir(), None, None)
|
||||
let registry = ProfileDiscovery::with_sources(None, None)
|
||||
.discover()
|
||||
.unwrap();
|
||||
let default = registry.default_entry().unwrap();
|
||||
assert_eq!(default.source, ProfileRegistrySource::Builtin);
|
||||
assert_eq!(default.name, BUILTIN_DEFAULT_PROFILE_NAME);
|
||||
assert!(default.is_default);
|
||||
assert!(default.path.ends_with("resources/profiles/default.lua"));
|
||||
assert_eq!(default.path, None);
|
||||
assert_eq!(default.provenance, "builtin:default");
|
||||
}
|
||||
#[test]
|
||||
fn resolves_plain_lua_profile_with_runtime_pod_name_and_scope_intent() {
|
||||
|
|
@ -1458,6 +1490,15 @@ return profile {
|
|||
resolved.profile.as_ref().unwrap().name.as_deref(),
|
||||
Some("default")
|
||||
);
|
||||
assert_eq!(
|
||||
resolved.source,
|
||||
ProfileSource::Registry {
|
||||
source: ProfileRegistrySource::Builtin,
|
||||
name: "default".into(),
|
||||
path: None,
|
||||
provenance: Some("builtin:default".into()),
|
||||
}
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn unsupported_profile_extension_has_clear_diagnostic() {
|
||||
|
|
@ -1486,14 +1527,19 @@ return profile {
|
|||
)
|
||||
.unwrap();
|
||||
std::fs::write(&project_config, "default = \"project:coder\"\n[profile.coder]\npath = \"profiles/project-coder.lua\"\ndescription = \"Project coder\"\n").unwrap();
|
||||
let registry =
|
||||
ProfileDiscovery::with_sources(None, Some(user_config), Some(project_config))
|
||||
let registry = ProfileDiscovery::with_sources(Some(user_config), Some(project_config))
|
||||
.discover()
|
||||
.unwrap();
|
||||
let default = registry.default_entry().unwrap();
|
||||
assert_eq!(default.source, ProfileRegistrySource::Project);
|
||||
assert_eq!(default.name, "coder");
|
||||
assert!(default.path.ends_with("profiles/project-coder.lua"));
|
||||
assert!(
|
||||
default
|
||||
.path
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.ends_with("profiles/project-coder.lua")
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn default_marks_direct_profile_entry() {
|
||||
|
|
@ -1506,7 +1552,7 @@ return profile {
|
|||
"default = \"coder\"\n[profile]\ncoder = \"profiles/coder.lua\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
let registry = ProfileDiscovery::with_sources(None, None, Some(project_config))
|
||||
let registry = ProfileDiscovery::with_sources(None, Some(project_config))
|
||||
.discover()
|
||||
.unwrap();
|
||||
let default = registry.default_entry().unwrap();
|
||||
|
|
@ -1525,20 +1571,18 @@ return profile {
|
|||
#[test]
|
||||
fn unqualified_ambiguous_names_fail_closed() {
|
||||
let mut registry = ProfileRegistry::default();
|
||||
registry.push_entry(ProfileRegistryEntry {
|
||||
source: ProfileRegistrySource::User,
|
||||
name: "coder".to_string(),
|
||||
path: PathBuf::from("/user/coder.lua"),
|
||||
description: None,
|
||||
is_default: false,
|
||||
});
|
||||
registry.push_entry(ProfileRegistryEntry {
|
||||
source: ProfileRegistrySource::Project,
|
||||
name: "coder".to_string(),
|
||||
path: PathBuf::from("/project/coder.lua"),
|
||||
description: None,
|
||||
is_default: false,
|
||||
});
|
||||
registry.push_entry(ProfileRegistryEntry::path(
|
||||
ProfileRegistrySource::User,
|
||||
"coder".to_string(),
|
||||
PathBuf::from("/user/coder.lua"),
|
||||
None,
|
||||
));
|
||||
registry.push_entry(ProfileRegistryEntry::path(
|
||||
ProfileRegistrySource::Project,
|
||||
"coder".to_string(),
|
||||
PathBuf::from("/project/coder.lua"),
|
||||
None,
|
||||
));
|
||||
let err = registry
|
||||
.select(&ProfileSelector::named("coder"))
|
||||
.unwrap_err();
|
||||
|
|
@ -1549,6 +1593,9 @@ return profile {
|
|||
"coder",
|
||||
))
|
||||
.unwrap();
|
||||
assert_eq!(selected.path, PathBuf::from("/project/coder.lua"));
|
||||
assert_eq!(
|
||||
selected.path.as_deref(),
|
||||
Some(Path::new("/project/coder.lua"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -932,7 +932,7 @@ mod tests {
|
|||
std::fs::write(®istry_path, registry_toml).unwrap();
|
||||
AvailableProfiles {
|
||||
registry: Some(
|
||||
ProfileDiscovery::with_sources(None, None, Some(registry_path))
|
||||
ProfileDiscovery::with_sources(None, Some(registry_path))
|
||||
.discover()
|
||||
.unwrap(),
|
||||
),
|
||||
|
|
@ -1319,7 +1319,7 @@ return profile {
|
|||
let project_config = project.join(".insomnia/profiles.toml");
|
||||
let ambiguous = AvailableProfiles {
|
||||
registry: Some(
|
||||
ProfileDiscovery::with_sources(None, Some(user_config), Some(project_config))
|
||||
ProfileDiscovery::with_sources(Some(user_config), Some(project_config))
|
||||
.discover()
|
||||
.unwrap(),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
INSOMNIA では、プロセス境界で本当に必要な場合を除き、環境変数の利用を避ける。新しい ambient な入力を増やすより、明示的な profile / manifest / config file / typed secret reference / CLI argument を優先する。
|
||||
|
||||
それでも、path discovery、runtime directory、package resource lookup、外部 provider の credential 慣習との移行互換のために、一部の環境変数はまだサポートしている。この文書に載せた環境変数は公開 surface として扱う。ただし、fallback 変数は独立した設定項目ではなく、対応する main key の解決順の一部として扱う。開発・テスト都合だけの環境変数は追加しない。
|
||||
それでも、path discovery、runtime directory、外部 provider の credential 慣習との移行互換のために、一部の環境変数はまだサポートしている。この文書に載せた環境変数は公開 surface として扱う。ただし、fallback 変数は独立した設定項目ではなく、対応する main key の解決順の一部として扱う。開発・テスト都合だけの環境変数は追加しない。
|
||||
|
||||
## 原則
|
||||
|
||||
|
|
@ -23,7 +23,6 @@ Path 系の環境変数は論理的な key ごとに立項する。`XDG_*` や `
|
|||
| `config_dir` | `INSOMNIA_CONFIG_DIR` | `$INSOMNIA_HOME/config` → `$XDG_CONFIG_HOME/insomnia` → `$HOME/.config/insomnia` | 人が書く設定・override の置き場。`profiles.toml`、prompt override、model/provider override など。 |
|
||||
| `data_dir` | `INSOMNIA_DATA_DIR` | `$INSOMNIA_HOME` → `$HOME/.insomnia` | プログラムが書く永続データの置き場。session log、Pod metadata など、再起動後も restore / replay の根拠になるもの。通常ユーザー向けの primary knob ではなく、migration、test、isolated data store 用の advanced override。 |
|
||||
| `runtime_dir` | `INSOMNIA_RUNTIME_DIR` | `$INSOMNIA_HOME/run` → `$XDG_RUNTIME_DIR/insomnia` → `$HOME/.insomnia/run` | socket、pid/status file、live registry mirror など、再起動で捨ててよい runtime state の置き場。 |
|
||||
| `resource_dir` | `INSOMNIA_RESOURCE_DIR` | installed executable から見た `share/insomnia/resources` → build tree の `resources/` | bundled prompts、builtin profiles、provider/model catalog など、package が所有する immutable-ish な builtin asset の置き場。通常 user configuration ではなく、packaging / development / debug 用の override。 |
|
||||
|
||||
空の path 環境変数は、`manifest::paths` では原則として unset 相当に扱う。
|
||||
|
||||
|
|
@ -35,13 +34,9 @@ Path 系の環境変数は論理的な key ごとに立項する。`XDG_*` や `
|
|||
|
||||
このため、socket や pid file を `data_dir` に置かない。永続データと揮発 runtime state は分ける。
|
||||
|
||||
### `resource_dir` と `config_dir` の違い
|
||||
### Builtin assets と `config_dir`
|
||||
|
||||
`resource_dir` は package-owned builtin asset の場所である。installed package では `share/insomnia/resources` に置かれ、binary version と対応する。ユーザーが普段編集する場所ではない。
|
||||
|
||||
`config_dir` は user/project-owned override の場所である。`profiles.toml` や prompt/model/provider override はここに置き、package update で上書きされない。
|
||||
|
||||
つまり、builtin fallback は `resource_dir`、user override は `config_dir` で扱う。`INSOMNIA_RESOURCE_DIR` を user configuration の代わりに使わない。
|
||||
Builtin profiles and catalogs are embedded in the binary at build time. User/project-owned overrides remain under `config_dir` and project `.insomnia/` files such as `profiles.toml`; package runtime resource lookup is not a supported configuration surface.
|
||||
|
||||
## Credential と外部 auth
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ The default package is implemented by `package.nix` and builds the Cargo package
|
|||
The package output contains:
|
||||
|
||||
- `bin/insomnia` — terminal UI and `insomnia pod ...` runtime entrypoint.
|
||||
- `share/insomnia/resources/` — bundled runtime resources, including `resources/prompts/`.
|
||||
- `share/doc/insomnia/nix.md` — this document.
|
||||
- `share/doc/insomnia/environment.md` — environment-variable policy and supported variables.
|
||||
|
||||
|
|
@ -47,7 +46,7 @@ The Nix package does not put user configuration, sessions, sockets, or other mut
|
|||
| Persistent data (`sessions/`, Pod metadata) | `INSOMNIA_DATA_DIR` | `$INSOMNIA_HOME` | `$HOME/.insomnia` |
|
||||
| Runtime state (sockets, lock files, live registry) | `INSOMNIA_RUNTIME_DIR` | `$INSOMNIA_HOME/run` | `$XDG_RUNTIME_DIR/insomnia`, then `$HOME/.insomnia/run` |
|
||||
|
||||
Normal fresh startup is profile-based. The package ships a builtin default profile, user/project `profiles.toml` files may select or define profiles, and `insomnia pod --manifest <PATH>` remains a one-file compatibility/debug input. `INSOMNIA_USER_MANIFEST` and ambient `.insomnia/manifest.toml` discovery are not part of normal Pod/TUI startup. See [`environment.md`](environment.md) for the environment-variable policy; new configuration should prefer profiles/manifests/config files over additional environment variables.
|
||||
Normal fresh startup is profile-based. The package includes the builtin default profile in the binary, user/project `profiles.toml` files may select or define profiles, and `insomnia pod --manifest <PATH>` remains a one-file compatibility/debug input. `INSOMNIA_USER_MANIFEST` and ambient `.insomnia/manifest.toml` discovery are not part of normal Pod/TUI startup. See [`environment.md`](environment.md) for the environment-variable policy; new configuration should prefer profiles/manifests/config files over additional environment variables.
|
||||
|
||||
## Validation
|
||||
|
||||
|
|
@ -56,7 +55,8 @@ The package derivation has a credential-free install check that verifies:
|
|||
- `insomnia pod --help` starts successfully.
|
||||
- `insomnia` is installed and reaches argument parsing.
|
||||
- `bin/insomnia-pod` is not installed.
|
||||
- bundled prompt resources and this Nix usage document are present in the output.
|
||||
- embedded builtin profiles do not require `share/insomnia/resources` at runtime.
|
||||
- this Nix usage document is present in the output.
|
||||
|
||||
For full validation before handing changes to review, run:
|
||||
|
||||
|
|
@ -73,4 +73,3 @@ These checks do not require provider credentials.
|
|||
|
||||
- The package currently installs only the `insomnia` command; development-only wrappers from `devshell.nix` are not part of the installable package.
|
||||
- The TUI does not currently expose a conventional `--help` / `--version` CLI path, so the package smoke check uses an argument-parse failure path for the TUI rather than launching an interactive session.
|
||||
- Bundled resources are installed under `share/insomnia/resources/` for packaging completeness and inspection. Built-in prompt/resource loading remains governed by the existing application code and user/project override rules.
|
||||
|
|
|
|||
|
|
@ -119,12 +119,12 @@ Use `--manifest` only when you need the complete low-level Manifest escape hatch
|
|||
|
||||
## Builtin defaults
|
||||
|
||||
Base defaults that are independent of profile choice live in Rust constants under `crates/manifest/src/defaults.rs` and in `PodManifestConfig::builtin_defaults()`. The bundled default role profile lives at `resources/profiles/default.lua` and is discovered as `builtin:default`.
|
||||
Base defaults that are independent of profile choice live in Rust constants under `crates/manifest/src/defaults.rs` and in `PodManifestConfig::builtin_defaults()`. The default role profile is embedded from `resources/profiles/default.lua` at build time and is discovered as `builtin:default`.
|
||||
|
||||
デフォルト値を変更するときは、次のどちらを変更するのかを明確にする。
|
||||
|
||||
- all manifests/profiles の baseline default: Rust defaults
|
||||
- ordinary dogfooding/default role: `resources/profiles/default.lua`
|
||||
- ordinary dogfooding/default role: embedded `builtin:default` profile sourced from `resources/profiles/default.lua`
|
||||
|
||||
## Path resolution
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ rustPlatform.buildRustPackage rec {
|
|||
filter = sourceFilter;
|
||||
};
|
||||
|
||||
cargoHash = "sha256-fisV77ZqAPsI0eLZIqw06HTj1CfmnL3NBHhjruZPZUE=";
|
||||
cargoHash = "sha256-Nu+QAXwRhqqSwgc5/9XLwQEpjEnF54tWoEknM17wYq8=";
|
||||
|
||||
depsExtraArgs = {
|
||||
# nixpkgs 25.11's fetchCargoVendor still uses crates.io's API
|
||||
|
|
@ -94,8 +94,6 @@ rustPlatform.buildRustPackage rec {
|
|||
postInstall = ''
|
||||
install -Dm644 docs/nix.md "$out/share/doc/insomnia/nix.md"
|
||||
install -Dm644 docs/environment.md "$out/share/doc/insomnia/environment.md"
|
||||
mkdir -p "$out/share/insomnia"
|
||||
cp -R resources "$out/share/insomnia/resources"
|
||||
'';
|
||||
|
||||
doInstallCheck = true;
|
||||
|
|
@ -105,13 +103,13 @@ rustPlatform.buildRustPackage rec {
|
|||
"$out/bin/insomnia" pod --help >/dev/null
|
||||
test -x "$out/bin/insomnia"
|
||||
test ! -e "$out/bin/insomnia-pod"
|
||||
test ! -e "$out/share/insomnia/resources"
|
||||
if "$out/bin/insomnia" --session not-a-uuid 2>insomnia.err; then
|
||||
echo "insomnia unexpectedly accepted an invalid --session value" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q "invalid --session UUID" insomnia.err
|
||||
|
||||
test -d "$out/share/insomnia/resources/prompts"
|
||||
test -f "$out/share/doc/insomnia/nix.md"
|
||||
test -f "$out/share/doc/insomnia/environment.md"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user