fix: remove direct worker local workspace discovery
This commit is contained in:
parent
1251edae04
commit
00d6c3a7c4
|
|
@ -4,24 +4,18 @@ use std::process::ExitCode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
PromptLoader, Worker, WorkerController, WorkerFilesystemAuthority, WorkerWorkspaceContext,
|
PromptLoader, Worker, WorkerController, WorkerFilesystemAuthority, WorkerWorkspaceContext,
|
||||||
WorkspaceId,
|
|
||||||
};
|
};
|
||||||
use clap::{CommandFactory, FromArgMatches, Parser};
|
use clap::{CommandFactory, FromArgMatches, Parser};
|
||||||
use manifest::{
|
use manifest::{Permission, ScopeConfig, ScopeRule, WorkerManifest, WorkerManifestConfig, paths};
|
||||||
Permission, ProfileResolveOptions, ProfileResolver, ProfileSelector, ScopeConfig, ScopeRule,
|
|
||||||
WorkerManifest, WorkerManifestConfig, paths,
|
|
||||||
plugin::{PluginDiscoveryOptions, resolve_plugin_config_for_startup},
|
|
||||||
};
|
|
||||||
use session_store::{CombinedStore, FsWorkerStore, WorkerMetadataStore};
|
use session_store::{CombinedStore, FsWorkerStore, WorkerMetadataStore};
|
||||||
use session_store::{FsStore, SegmentId, Store};
|
use session_store::{FsStore, SegmentId, Store};
|
||||||
use ticket::config::TicketRole;
|
use ticket::config::TicketRole;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(about = "Spawn a Worker process from a profile or a single manifest file")]
|
#[command(about = "Spawn a Worker process from a manifest or builtin defaults")]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// Profile to evaluate. Accepts an explicit path, `path:<path>`, a
|
/// Legacy profile selector. Direct worker startup no longer resolves
|
||||||
/// discovered profile name, `default`, or a source-qualified name such as
|
/// workspace-local profiles; pass a Backend-resolved manifest instead.
|
||||||
/// `project:coder`.
|
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "PROFILE",
|
value_name = "PROFILE",
|
||||||
|
|
@ -29,8 +23,8 @@ struct Cli {
|
||||||
)]
|
)]
|
||||||
profile: Option<String>,
|
profile: Option<String>,
|
||||||
|
|
||||||
/// Runtime workspace root for profile discovery, default Worker naming, and process context.
|
/// Process root/cwd context for direct worker startup. This is not Workspace
|
||||||
/// Defaults to the current directory.
|
/// authority and does not enable `.yoi` discovery. Defaults to current dir.
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long, value_name = "PATH")]
|
||||||
workspace: Option<PathBuf>,
|
workspace: Option<PathBuf>,
|
||||||
|
|
||||||
|
|
@ -106,22 +100,8 @@ fn runtime_workspace_root(cli: &Cli) -> Result<PathBuf, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_workspace_context(workspace_root: &Path) -> WorkerWorkspaceContext {
|
fn runtime_workspace_context() -> WorkerWorkspaceContext {
|
||||||
WorkerWorkspaceContext::local_filesystem(read_workspace_id_hint(workspace_root))
|
WorkerWorkspaceContext::no_workspace()
|
||||||
}
|
|
||||||
|
|
||||||
fn read_workspace_id_hint(workspace_root: &Path) -> Option<WorkspaceId> {
|
|
||||||
let path = workspace_root.join(".yoi/workspace.toml");
|
|
||||||
let contents = std::fs::read_to_string(path).ok()?;
|
|
||||||
let value = toml::from_str::<toml::Value>(&contents).ok()?;
|
|
||||||
let id = value.get("id")?.as_str()?.to_string();
|
|
||||||
match WorkspaceId::new(id) {
|
|
||||||
Ok(id) => Some(id),
|
|
||||||
Err(err) => {
|
|
||||||
tracing::warn!("ignoring invalid workspace id in .yoi/workspace.toml: {err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_worker_name(cli: &Cli, workspace_root: &Path) -> String {
|
fn runtime_worker_name(cli: &Cli, workspace_root: &Path) -> String {
|
||||||
|
|
@ -158,63 +138,39 @@ fn sanitise_worker_name(raw: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_manifest(cli: &Cli) -> Result<(WorkerManifest, PromptLoader), String> {
|
fn resolve_manifest(cli: &Cli) -> Result<(WorkerManifest, PromptLoader), String> {
|
||||||
resolve_manifest_with_profile_loader(cli, load_profile)
|
let process_root = runtime_workspace_root(cli)?;
|
||||||
}
|
let runtime_worker_name = runtime_worker_name(cli, &process_root);
|
||||||
|
let ((mut manifest, loader), apply_direct_launch_policy) = if let Some(config_json) =
|
||||||
fn resolve_manifest_with_profile_loader<F>(
|
cli.spawn_config_json.as_deref()
|
||||||
cli: &Cli,
|
{
|
||||||
load_profile_fn: F,
|
(load_spawn_config_json(config_json)?, false)
|
||||||
) -> Result<(WorkerManifest, PromptLoader), String>
|
} else if let Some(profile) = &cli.profile {
|
||||||
where
|
return Err(format!(
|
||||||
F: FnOnce(&ProfileSelector, &Path, &str) -> Result<(WorkerManifest, PromptLoader), String>,
|
"--profile {profile} requires Backend-resolved manifest authority; direct worker startup does not discover workspace-local profiles"
|
||||||
{
|
));
|
||||||
let workspace_root = runtime_workspace_root(cli)?;
|
} else if let Some(path) = &cli.manifest {
|
||||||
let runtime_worker_name = runtime_worker_name(cli, &workspace_root);
|
(
|
||||||
let ((mut manifest, loader), manifest_source) =
|
load_single_manifest(path, cli.worker.as_deref(), &runtime_worker_name)?,
|
||||||
if let Some(config_json) = cli.spawn_config_json.as_deref() {
|
false,
|
||||||
(
|
)
|
||||||
load_spawn_config_json(config_json)?,
|
} else {
|
||||||
ManifestSource::SpawnConfig,
|
if cli.project.is_some() {
|
||||||
)
|
return Err(
|
||||||
} else if let Some(profile) = &cli.profile {
|
"--project is no longer supported; direct worker startup uses --manifest, \
|
||||||
let selector = ProfileSelector::parse_cli(profile);
|
--spawn-config-json, or builtin defaults only"
|
||||||
(
|
|
||||||
load_profile_fn(&selector, &workspace_root, &runtime_worker_name)?,
|
|
||||||
ManifestSource::ProfileLaunch,
|
|
||||||
)
|
|
||||||
} else if let Some(path) = &cli.manifest {
|
|
||||||
(
|
|
||||||
load_single_manifest(path, cli.worker.as_deref(), &runtime_worker_name)?,
|
|
||||||
ManifestSource::ManifestFile,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
if cli.project.is_some() {
|
|
||||||
return Err(
|
|
||||||
"--project is no longer supported; normal startup uses profile discovery/default, \
|
|
||||||
and --manifest <PATH> is the only one-file manifest mode"
|
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let selector = ProfileSelector::Default;
|
(load_builtin_default_manifest(&runtime_worker_name)?, true)
|
||||||
(
|
};
|
||||||
load_profile_fn(&selector, &workspace_root, &runtime_worker_name)?,
|
|
||||||
ManifestSource::ProfileLaunch,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if manifest_source == ManifestSource::ProfileLaunch {
|
if apply_direct_launch_policy {
|
||||||
apply_profile_launch_policy(&mut manifest, &workspace_root, cli.ticket_role.as_deref())?;
|
apply_profile_launch_policy(&mut manifest, &process_root, cli.ticket_role.as_deref())?;
|
||||||
}
|
}
|
||||||
apply_session_restore_overrides(&mut manifest, cli)?;
|
apply_session_restore_overrides(&mut manifest, cli)?;
|
||||||
apply_plugin_resolution_plan(&mut manifest, &workspace_root);
|
|
||||||
Ok((manifest, loader))
|
Ok((manifest, loader))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_plugin_resolution_plan(manifest: &mut WorkerManifest, workspace_root: &Path) {
|
|
||||||
let options = PluginDiscoveryOptions::new(workspace_root);
|
|
||||||
manifest.plugins = resolve_plugin_config_for_startup(&manifest.plugins, &options);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_session_restore_overrides(manifest: &mut WorkerManifest, cli: &Cli) -> Result<(), String> {
|
fn apply_session_restore_overrides(manifest: &mut WorkerManifest, cli: &Cli) -> Result<(), String> {
|
||||||
if let Some(worker_name) = cli.worker.as_deref() {
|
if let Some(worker_name) = cli.worker.as_deref() {
|
||||||
manifest.worker.name = worker_name.to_string();
|
manifest.worker.name = worker_name.to_string();
|
||||||
|
|
@ -230,20 +186,14 @@ fn load_spawn_config_json(config_json: &str) -> Result<(WorkerManifest, PromptLo
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
Ok((manifest, PromptLoader::builtins_only()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_profile(
|
fn load_builtin_default_manifest(
|
||||||
selector: &ProfileSelector,
|
|
||||||
workspace_root: &Path,
|
|
||||||
worker_name: &str,
|
worker_name: &str,
|
||||||
) -> Result<(WorkerManifest, PromptLoader), String> {
|
) -> Result<(WorkerManifest, PromptLoader), String> {
|
||||||
let resolver = ProfileResolver::new().with_workspace_base(workspace_root);
|
let mut config = WorkerManifestConfig::builtin_defaults();
|
||||||
let options = ProfileResolveOptions::with_worker_name(worker_name);
|
config.worker.name = Some(worker_name.to_string());
|
||||||
let resolved = resolver.resolve(selector, options).map_err(|e| {
|
let manifest = WorkerManifest::try_from(config)
|
||||||
format!(
|
.map_err(|e| format!("failed to resolve builtin worker defaults: {e}"))?;
|
||||||
"failed to resolve profile {}: {e}",
|
Ok((manifest, PromptLoader::builtins_only()))
|
||||||
selector.display_label()
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok((resolved.manifest, PromptLoader::builtins_only()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_runtime_profile_manifest(
|
pub fn resolve_runtime_profile_manifest(
|
||||||
|
|
@ -268,7 +218,7 @@ pub fn resolve_runtime_profile_manifest_from_manifest(
|
||||||
apply_profile_launch_policy(&mut manifest, workspace_root, None)?;
|
apply_profile_launch_policy(&mut manifest, workspace_root, None)?;
|
||||||
// Do not run plugin discovery here: runtime-created Workers receive their
|
// Do not run plugin discovery here: runtime-created Workers receive their
|
||||||
// resolved manifest/profile archive from Backend authority, not by scanning
|
// resolved manifest/profile archive from Backend authority, not by scanning
|
||||||
// the materialized workdir's `.yoi/plugins`.
|
// materialized workdir-local plugin stores.
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
Ok((manifest, PromptLoader::builtins_only()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +232,7 @@ pub fn resolve_runtime_profile_manifest_from_manifest_without_filesystem(
|
||||||
}
|
}
|
||||||
manifest.scope = ScopeConfig::default();
|
manifest.scope = ScopeConfig::default();
|
||||||
manifest.delegation_scope = ScopeConfig::default();
|
manifest.delegation_scope = ScopeConfig::default();
|
||||||
// Same as the filesystem-capable runtime path: no local `.yoi` discovery.
|
// Same as the filesystem-capable runtime path: no local discovery.
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
Ok((manifest, PromptLoader::builtins_only()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,13 +277,6 @@ fn load_single_manifest(
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
Ok((manifest, PromptLoader::builtins_only()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum ManifestSource {
|
|
||||||
ProfileLaunch,
|
|
||||||
ManifestFile,
|
|
||||||
SpawnConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_rule(target: PathBuf) -> ScopeRule {
|
fn read_rule(target: PathBuf) -> ScopeRule {
|
||||||
ScopeRule {
|
ScopeRule {
|
||||||
target,
|
target,
|
||||||
|
|
@ -547,7 +490,7 @@ async fn run_cli_inner(cli: Cli) -> ExitCode {
|
||||||
let store = CombinedStore::new(session_store, worker_metadata_store);
|
let store = CombinedStore::new(session_store, worker_metadata_store);
|
||||||
let filesystem_authority =
|
let filesystem_authority =
|
||||||
WorkerFilesystemAuthority::local(workspace_root.clone(), cwd.clone());
|
WorkerFilesystemAuthority::local(workspace_root.clone(), cwd.clone());
|
||||||
let workspace_context = runtime_workspace_context(&workspace_root);
|
let workspace_context = runtime_workspace_context();
|
||||||
|
|
||||||
let mut worker = if cli.adopt {
|
let mut worker = if cli.adopt {
|
||||||
let callback = match cli.callback.clone() {
|
let callback = match cli.callback.clone() {
|
||||||
|
|
@ -862,226 +805,46 @@ permission = "write"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn profile_launch_preserves_workspace_override_scope_allow_in_final_manifest() {
|
fn profile_resolution_is_rejected_for_direct_worker_startup() {
|
||||||
|
let cli = Cli::try_parse_from(["yoi worker", "--profile", "project:coder"]).unwrap();
|
||||||
|
|
||||||
|
let err = resolve_manifest(&cli).unwrap_err();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
err.contains("--profile project:coder requires Backend-resolved manifest authority")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normal_startup_uses_builtin_defaults_without_local_profile_discovery() {
|
||||||
let tmp = TempDir::new().unwrap();
|
let tmp = TempDir::new().unwrap();
|
||||||
let workspace = tmp.path().join("runtime-workspace");
|
let workspace = tmp.path().join("runtime-workspace");
|
||||||
let external = tmp.path().join("external-readable");
|
|
||||||
let yoi_dir = workspace.join(".yoi");
|
let yoi_dir = workspace.join(".yoi");
|
||||||
std::fs::create_dir_all(&workspace).unwrap();
|
|
||||||
std::fs::create_dir_all(&external).unwrap();
|
|
||||||
std::fs::create_dir_all(&yoi_dir).unwrap();
|
std::fs::create_dir_all(&yoi_dir).unwrap();
|
||||||
write(
|
write(
|
||||||
&yoi_dir.join("override.local.toml"),
|
&yoi_dir.join("override.local.toml"),
|
||||||
&format!(
|
|
||||||
r#"
|
|
||||||
[[scope.allow]]
|
|
||||||
target = "{}"
|
|
||||||
permission = "read"
|
|
||||||
recursive = true
|
|
||||||
"#,
|
|
||||||
external.display()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let profile = tmp.path().join("profile.toml");
|
|
||||||
write(
|
|
||||||
&profile,
|
|
||||||
r#"
|
r#"
|
||||||
slug = "override-scope"
|
[worker]
|
||||||
|
name = "from-local-override"
|
||||||
|
|
||||||
[model]
|
[engine]
|
||||||
scheme = "anthropic"
|
language = "override"
|
||||||
model_id = "test-model"
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
let cli = Cli::try_parse_from([
|
|
||||||
"yoi worker",
|
|
||||||
"--workspace",
|
|
||||||
workspace.to_str().unwrap(),
|
|
||||||
"--profile",
|
|
||||||
profile.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let (manifest, _loader) = resolve_manifest(&cli).unwrap();
|
|
||||||
let snapshot = serde_json::to_value(&manifest).unwrap();
|
|
||||||
let snapshot_scope: ScopeConfig =
|
|
||||||
serde_json::from_value(snapshot["scope"].clone()).unwrap();
|
|
||||||
|
|
||||||
assert_scope_contains(&manifest.scope.allow, &external, Permission::Read);
|
|
||||||
assert_scope_contains(&manifest.scope.allow, &workspace, Permission::Write);
|
|
||||||
assert_scope_contains(
|
|
||||||
&manifest.scope.deny,
|
|
||||||
&workspace.join(".worktree"),
|
|
||||||
Permission::Write,
|
|
||||||
);
|
|
||||||
assert_scope_contains(&snapshot_scope.allow, &external, Permission::Read);
|
|
||||||
assert_scope_contains(&snapshot_scope.allow, &workspace, Permission::Write);
|
|
||||||
assert_scope_contains(
|
|
||||||
&snapshot_scope.deny,
|
|
||||||
&workspace.join(".worktree"),
|
|
||||||
Permission::Write,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn profile_uses_selected_profile() {
|
|
||||||
let tmp = TempDir::new().unwrap();
|
|
||||||
let profile = tmp.path().join("profile.toml");
|
|
||||||
let cli = Cli::try_parse_from([
|
|
||||||
"yoi worker",
|
|
||||||
"--profile",
|
|
||||||
profile.to_str().unwrap(),
|
|
||||||
"--worker",
|
|
||||||
"from-profile-name",
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
let mut called = false;
|
|
||||||
|
|
||||||
let (manifest, loader) =
|
|
||||||
resolve_manifest_with_profile_loader(&cli, |selector, _workspace_root, worker_name| {
|
|
||||||
called = true;
|
|
||||||
assert_eq!(selector, &ProfileSelector::path(profile.clone()));
|
|
||||||
assert_eq!(worker_name, "from-profile-name");
|
|
||||||
let mut manifest =
|
|
||||||
WorkerManifest::from_toml(&manifest_toml("from-profile", tmp.path())).unwrap();
|
|
||||||
manifest.worker.name = worker_name.to_string();
|
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(called);
|
|
||||||
assert_eq!(manifest.worker.name, "from-profile-name");
|
|
||||||
assert!(loader.user_dir().is_none());
|
|
||||||
assert!(loader.workspace_dir().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn profile_accepts_source_qualified_discovered_name() {
|
|
||||||
let tmp = TempDir::new().unwrap();
|
|
||||||
let cli = Cli::try_parse_from([
|
|
||||||
"yoi worker",
|
|
||||||
"--profile",
|
|
||||||
"project:coder",
|
|
||||||
"--worker",
|
|
||||||
"from-profile-name",
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
let mut called = false;
|
|
||||||
|
|
||||||
let (manifest, _loader) =
|
|
||||||
resolve_manifest_with_profile_loader(&cli, |selector, _workspace_root, worker_name| {
|
|
||||||
called = true;
|
|
||||||
assert_eq!(
|
|
||||||
selector,
|
|
||||||
&ProfileSelector::source_named(
|
|
||||||
manifest::ProfileRegistrySource::Project,
|
|
||||||
"coder"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
let mut manifest =
|
|
||||||
WorkerManifest::from_toml(&manifest_toml("from-profile", tmp.path())).unwrap();
|
|
||||||
manifest.worker.name = worker_name.to_string();
|
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(called);
|
|
||||||
assert_eq!(manifest.worker.name, "from-profile-name");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn profile_without_explicit_worker_uses_workspace_basename_not_selector() {
|
|
||||||
let tmp = TempDir::new().unwrap();
|
|
||||||
let workspace = tmp.path().join("other-workspace");
|
|
||||||
std::fs::create_dir(&workspace).unwrap();
|
|
||||||
let cli = Cli::try_parse_from([
|
|
||||||
"yoi worker",
|
|
||||||
"--workspace",
|
|
||||||
workspace.to_str().unwrap(),
|
|
||||||
"--profile",
|
|
||||||
"project:companion",
|
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
let mut called = false;
|
|
||||||
|
|
||||||
let (manifest, _loader) =
|
|
||||||
resolve_manifest_with_profile_loader(&cli, |selector, workspace_root, worker_name| {
|
|
||||||
called = true;
|
|
||||||
assert_eq!(
|
|
||||||
selector,
|
|
||||||
&ProfileSelector::source_named(
|
|
||||||
manifest::ProfileRegistrySource::Project,
|
|
||||||
"companion"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert_eq!(workspace_root, workspace.as_path());
|
|
||||||
assert_eq!(worker_name, "other-workspace");
|
|
||||||
let mut manifest =
|
|
||||||
WorkerManifest::from_toml(&manifest_toml("profile-selector-name", tmp.path()))
|
|
||||||
.unwrap();
|
|
||||||
manifest.worker.name = worker_name.to_string();
|
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(called);
|
|
||||||
assert_eq!(manifest.worker.name, "other-workspace");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn normal_startup_uses_default_profile() {
|
|
||||||
let tmp = TempDir::new().unwrap();
|
|
||||||
let workspace = tmp.path().join("runtime-workspace");
|
|
||||||
std::fs::create_dir(&workspace).unwrap();
|
|
||||||
let cli = Cli::try_parse_from(["yoi worker", "--workspace", workspace.to_str().unwrap()])
|
let cli = Cli::try_parse_from(["yoi worker", "--workspace", workspace.to_str().unwrap()])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut called = false;
|
|
||||||
|
|
||||||
let (manifest, _loader) =
|
let (manifest, loader) = resolve_manifest(&cli).unwrap();
|
||||||
resolve_manifest_with_profile_loader(&cli, |selector, _workspace_root, worker_name| {
|
|
||||||
called = true;
|
|
||||||
assert_eq!(selector, &ProfileSelector::Default);
|
|
||||||
assert_eq!(worker_name, "runtime-workspace");
|
|
||||||
let mut manifest =
|
|
||||||
WorkerManifest::from_toml(&manifest_toml("from-default-profile", tmp.path()))
|
|
||||||
.unwrap();
|
|
||||||
manifest.worker.name = worker_name.to_string();
|
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(called);
|
|
||||||
assert_eq!(manifest.worker.name, "runtime-workspace");
|
assert_eq!(manifest.worker.name, "runtime-workspace");
|
||||||
assert_eq!(manifest.scope.allow.len(), 2);
|
assert_ne!(manifest.engine.language, "override");
|
||||||
assert_scope_contains(&manifest.scope.allow, tmp.path(), Permission::Write);
|
assert!(loader.user_dir().is_none());
|
||||||
|
assert!(loader.workspace_dir().is_none());
|
||||||
assert_scope_contains(&manifest.scope.allow, &workspace, Permission::Write);
|
assert_scope_contains(&manifest.scope.allow, &workspace, Permission::Write);
|
||||||
assert_eq!(manifest.scope.deny.len(), 1);
|
|
||||||
assert_scope_contains(
|
|
||||||
&manifest.scope.deny,
|
|
||||||
&tmp.path().join("runtime-workspace/.worktree"),
|
|
||||||
Permission::Write,
|
|
||||||
);
|
|
||||||
assert_eq!(manifest.delegation_scope.allow.len(), 2);
|
|
||||||
assert_eq!(
|
|
||||||
manifest.delegation_scope.allow[0].target,
|
|
||||||
tmp.path().join("runtime-workspace")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
manifest.delegation_scope.allow[0].permission,
|
|
||||||
Permission::Read
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
manifest.delegation_scope.allow[1].target,
|
|
||||||
tmp.path().join("runtime-workspace/.worktree")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
manifest.delegation_scope.allow[1].permission,
|
|
||||||
Permission::Write
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn orchestrator_profile_launch_gets_read_root_and_worktree_delegation_from_launch_policy() {
|
fn orchestrator_direct_startup_gets_read_root_and_worktree_delegation_from_launch_policy() {
|
||||||
let tmp = TempDir::new().unwrap();
|
let tmp = TempDir::new().unwrap();
|
||||||
let workspace = tmp.path().join("original-workspace");
|
let workspace = tmp.path().join("original-workspace");
|
||||||
std::fs::create_dir(&workspace).unwrap();
|
std::fs::create_dir(&workspace).unwrap();
|
||||||
|
|
@ -1089,41 +852,17 @@ model_id = "test-model"
|
||||||
"yoi worker",
|
"yoi worker",
|
||||||
"--workspace",
|
"--workspace",
|
||||||
workspace.to_str().unwrap(),
|
workspace.to_str().unwrap(),
|
||||||
"--profile",
|
|
||||||
"builtin:orchestrator",
|
|
||||||
"--ticket-role",
|
"--ticket-role",
|
||||||
"orchestrator",
|
"orchestrator",
|
||||||
])
|
])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (manifest, _loader) =
|
let (manifest, _loader) = resolve_manifest(&cli).unwrap();
|
||||||
resolve_manifest_with_profile_loader(&cli, |selector, _workspace_root, worker_name| {
|
|
||||||
assert_eq!(
|
|
||||||
selector,
|
|
||||||
&ProfileSelector::source_named(
|
|
||||||
manifest::ProfileRegistrySource::Builtin,
|
|
||||||
"orchestrator"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
let mut manifest = WorkerManifest::from_toml(&manifest_toml(
|
|
||||||
"from-orchestrator-profile",
|
|
||||||
tmp.path(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
manifest.worker.name = worker_name.to_string();
|
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(manifest.scope.allow.len(), 2);
|
|
||||||
assert_scope_contains(&manifest.scope.allow, tmp.path(), Permission::Write);
|
|
||||||
assert_scope_contains(&manifest.scope.allow, &workspace, Permission::Read);
|
assert_scope_contains(&manifest.scope.allow, &workspace, Permission::Read);
|
||||||
assert!(manifest.scope.deny.is_empty());
|
assert!(manifest.scope.deny.is_empty());
|
||||||
assert_eq!(manifest.delegation_scope.allow.len(), 2);
|
assert_eq!(manifest.delegation_scope.allow.len(), 2);
|
||||||
assert_eq!(
|
assert_eq!(manifest.delegation_scope.allow[0].target, workspace);
|
||||||
manifest.delegation_scope.allow[0].target,
|
|
||||||
tmp.path().join("original-workspace")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
manifest.delegation_scope.allow[0].permission,
|
manifest.delegation_scope.allow[0].permission,
|
||||||
Permission::Read
|
Permission::Read
|
||||||
|
|
@ -1136,23 +875,12 @@ model_id = "test-model"
|
||||||
manifest.delegation_scope.allow[1].permission,
|
manifest.delegation_scope.allow[1].permission,
|
||||||
Permission::Write
|
Permission::Write
|
||||||
);
|
);
|
||||||
assert!(
|
|
||||||
!manifest
|
|
||||||
.delegation_scope
|
|
||||||
.allow
|
|
||||||
.iter()
|
|
||||||
.any(|rule| rule.target == tmp.path().join("original-workspace")
|
|
||||||
&& rule.permission == Permission::Write)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn project_flag_no_longer_enables_ambient_manifest_cascade() {
|
fn project_flag_no_longer_enables_ambient_manifest_cascade() {
|
||||||
let cli = Cli::try_parse_from(["yoi worker", "--project", "."]).unwrap();
|
let cli = Cli::try_parse_from(["yoi worker", "--project", "."]).unwrap();
|
||||||
let err = resolve_manifest_with_profile_loader(&cli, |_, _, _| {
|
let err = resolve_manifest(&cli).unwrap_err();
|
||||||
panic!("default profile loader must not run when deprecated --project is present")
|
|
||||||
})
|
|
||||||
.unwrap_err();
|
|
||||||
assert!(err.contains("--project is no longer supported"));
|
assert!(err.contains("--project is no longer supported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1234,25 +962,11 @@ permission = "write"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn worker_flag_with_no_manifest_creates_from_default_profile_with_typed_name() {
|
fn worker_flag_with_no_manifest_creates_from_builtin_defaults_with_typed_name() {
|
||||||
let tmp = TempDir::new().unwrap();
|
|
||||||
let cli = Cli::try_parse_from(["yoi worker", "--worker", "agent"]).unwrap();
|
let cli = Cli::try_parse_from(["yoi worker", "--worker", "agent"]).unwrap();
|
||||||
let mut called = false;
|
|
||||||
|
|
||||||
let (manifest, _loader) =
|
let (manifest, _loader) = resolve_manifest(&cli).unwrap();
|
||||||
resolve_manifest_with_profile_loader(&cli, |selector, _workspace_root, worker_name| {
|
|
||||||
called = true;
|
|
||||||
assert_eq!(selector, &ProfileSelector::Default);
|
|
||||||
assert_eq!(worker_name, "agent");
|
|
||||||
let mut manifest =
|
|
||||||
WorkerManifest::from_toml(&manifest_toml("from-default-profile", tmp.path()))
|
|
||||||
.unwrap();
|
|
||||||
manifest.worker.name = worker_name.to_string();
|
|
||||||
Ok((manifest, PromptLoader::builtins_only()))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(called);
|
|
||||||
assert_eq!(manifest.worker.name, "agent");
|
assert_eq!(manifest.worker.name, "agent");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user