diff --git a/src/lib/legacy-oclif-dispatch.test.ts b/src/lib/legacy-oclif-dispatch.test.ts index a1e8a32bf36..bcaffa0723b 100644 --- a/src/lib/legacy-oclif-dispatch.test.ts +++ b/src/lib/legacy-oclif-dispatch.test.ts @@ -50,4 +50,33 @@ describe("resolveSandboxOclifDispatch", () => { usage: "recover", }); }); + + it("routes policy-add missing-value errors through a raw oclif adapter", () => { + expect(resolveSandboxOclifDispatch("alpha", "policy-add", ["--from-file"])).toEqual({ + kind: "oclif", + commandId: "sandbox:policy-add:raw", + args: ["alpha", "--from-file"], + }); + }); + + it("routes skill help and unknown subcommands through oclif", () => { + expect(resolveSandboxOclifDispatch("alpha", "skill", ["--help"])).toEqual({ + kind: "oclif", + commandId: "sandbox:skill", + args: ["alpha", "--help"], + }); + expect(resolveSandboxOclifDispatch("alpha", "skill", ["bogus"])).toEqual({ + kind: "oclif", + commandId: "sandbox:skill", + args: ["alpha", "bogus"], + }); + }); + + it("routes snapshot unknown subcommands through oclif", () => { + expect(resolveSandboxOclifDispatch("alpha", "snapshot", ["bogus"])).toEqual({ + kind: "oclif", + commandId: "sandbox:snapshot", + args: ["alpha", "bogus"], + }); + }); }); diff --git a/src/lib/legacy-oclif-dispatch.ts b/src/lib/legacy-oclif-dispatch.ts index f254e688965..1bbcfdd6f95 100644 --- a/src/lib/legacy-oclif-dispatch.ts +++ b/src/lib/legacy-oclif-dispatch.ts @@ -19,11 +19,6 @@ export type UsageErrorDispatch = { lines: string[]; }; -export type LegacyDispatch = { - kind: "legacy"; - target: "policy-add" | "skill" | "snapshot"; -}; - export type UnknownSubcommandDispatch = { kind: "unknownSubcommand"; command: "credentials" | "channels"; @@ -39,7 +34,6 @@ export type DispatchResult = | OclifDispatch | HelpDispatch | UsageErrorDispatch - | LegacyDispatch | UnknownSubcommandDispatch | UnknownActionDispatch; @@ -126,7 +120,7 @@ export function resolveSandboxOclifDispatch( }; } if (hasMissingFlagValue(actionArgs, "--from-file") || hasMissingFlagValue(actionArgs, "--from-dir")) { - return { kind: "legacy", target: "policy-add" }; + return { kind: "oclif", commandId: "sandbox:policy-add:raw", args: [sandboxName, ...actionArgs] }; } return { kind: "oclif", commandId: "sandbox:policy-add", args: [sandboxName, ...actionArgs] }; case "policy-remove": @@ -145,13 +139,13 @@ export function resolveSandboxOclifDispatch( const skillSub = actionArgs[0]; const skillArgs = actionArgs.slice(1); if (!skillSub || skillSub === "help" || skillSub === "--help" || skillSub === "-h") { - return { kind: "legacy", target: "skill" }; + return { kind: "oclif", commandId: "sandbox:skill", args: [sandboxName, ...actionArgs] }; } if (skillSub === "install") { - if (hasHelpFlag(skillArgs)) return { kind: "legacy", target: "skill" }; + if (hasHelpFlag(skillArgs)) return { kind: "oclif", commandId: "sandbox:skill", args: [sandboxName, ...actionArgs] }; return { kind: "oclif", commandId: "sandbox:skill:install", args: [sandboxName, ...skillArgs] }; } - return { kind: "legacy", target: "skill" }; + return { kind: "oclif", commandId: "sandbox:skill", args: [sandboxName, ...actionArgs] }; } case "rebuild": if (hasHelpFlag(actionArgs)) return { kind: "help", usage: "rebuild [--yes|--force] [--verbose|-v]" }; @@ -176,7 +170,7 @@ export function resolveSandboxOclifDispatch( if (hasHelpFlag(snapshotArgs)) return { kind: "help", usage: "snapshot restore [selector] [--to ]" }; return { kind: "oclif", commandId: "sandbox:snapshot:restore", args: [sandboxName, ...snapshotArgs] }; } - return { kind: "legacy", target: "snapshot" }; + return { kind: "oclif", commandId: "sandbox:snapshot", args: [sandboxName, ...actionArgs] }; } case "shields": { const shieldsSub = actionArgs[0]; diff --git a/src/lib/oclif-commands.ts b/src/lib/oclif-commands.ts index b80d3efdd85..fa68a90cb7f 100644 --- a/src/lib/oclif-commands.ts +++ b/src/lib/oclif-commands.ts @@ -29,7 +29,11 @@ import { GarbageCollectImagesCommand, UpgradeSandboxesCommand, } from "./maintenance-cli-commands"; -import { PolicyAddCommand, PolicyRemoveCommand } from "./policy-mutate-cli-commands"; +import { + PolicyAddCommand, + PolicyAddRawCommand, + PolicyRemoveCommand, +} from "./policy-mutate-cli-commands"; import RebuildCliCommand from "./rebuild-cli-command"; import RecoverCliCommand from "./recover-cli-command"; import SandboxDoctorCliCommand from "./sandbox-doctor-cli-command"; @@ -46,8 +50,9 @@ import { ShieldsUpCommand, } from "./shields-cli-commands"; import ShareCommand from "./share-command"; -import SkillInstallCliCommand from "./skill-install-cli-command"; +import SkillInstallCliCommand, { SkillCliCommand } from "./skill-install-cli-command"; import { + SnapshotCommand, SnapshotCreateCommand, SnapshotListCommand, SnapshotRestoreCommand, @@ -83,6 +88,7 @@ export default { "sandbox:doctor": SandboxDoctorCliCommand, "sandbox:logs": SandboxLogsCommand, "sandbox:policy-add": PolicyAddCommand, + "sandbox:policy-add:raw": PolicyAddRawCommand, "sandbox:policy-list": SandboxPolicyListCommand, "sandbox:policy-remove": PolicyRemoveCommand, "sandbox:rebuild": RebuildCliCommand, @@ -90,7 +96,9 @@ export default { "sandbox:shields:down": ShieldsDownCommand, "sandbox:shields:status": ShieldsStatusCommand, "sandbox:shields:up": ShieldsUpCommand, + "sandbox:skill": SkillCliCommand, "sandbox:skill:install": SkillInstallCliCommand, + "sandbox:snapshot": SnapshotCommand, "sandbox:snapshot:create": SnapshotCreateCommand, "sandbox:snapshot:list": SnapshotListCommand, "sandbox:snapshot:restore": SnapshotRestoreCommand, diff --git a/src/lib/policy-mutate-cli-commands.ts b/src/lib/policy-mutate-cli-commands.ts index 838987ec002..d37aca3a95d 100644 --- a/src/lib/policy-mutate-cli-commands.ts +++ b/src/lib/policy-mutate-cli-commands.ts @@ -76,6 +76,20 @@ export class PolicyAddCommand extends Command { } } +export class PolicyAddRawCommand extends Command { + static id = "sandbox:policy-add:raw"; + static strict = false; + static hidden = true; + + public async run(): Promise { + const [sandboxName, ...actionArgs] = this.argv; + if (!sandboxName || sandboxName.trim() === "") { + this.error("Missing required sandboxName for policy-add.", { exit: 2 }); + } + await getRuntimeBridge().sandboxPolicyAdd(sandboxName, actionArgs); + } +} + export class PolicyRemoveCommand extends Command { static id = "sandbox:policy-remove"; static strict = true; diff --git a/src/lib/skill-install-cli-command.ts b/src/lib/skill-install-cli-command.ts index 9b9f813d9f0..d982e1ff342 100644 --- a/src/lib/skill-install-cli-command.ts +++ b/src/lib/skill-install-cli-command.ts @@ -19,6 +19,22 @@ function getRuntimeBridge() { return runtimeBridgeFactory(); } +export class SkillCliCommand extends Command { + static id = "sandbox:skill"; + static strict = false; + static summary = "Show skill command usage"; + static description = "Show skill install usage or report unknown skill subcommands."; + static usage = [" skill install "]; + + public async run(): Promise { + const [sandboxName, ...actionArgs] = this.argv; + if (!sandboxName || sandboxName.trim() === "") { + this.error("Missing required sandboxName for skill.", { exit: 2 }); + } + await getRuntimeBridge().sandboxSkillInstall(sandboxName, actionArgs); + } +} + export default class SkillInstallCliCommand extends Command { static id = "sandbox:skill:install"; static strict = true; diff --git a/src/lib/snapshot-cli-commands.ts b/src/lib/snapshot-cli-commands.ts index ea0d32f25c7..a4c2629e812 100644 --- a/src/lib/snapshot-cli-commands.ts +++ b/src/lib/snapshot-cli-commands.ts @@ -25,6 +25,22 @@ const sandboxNameArg = Args.string({ required: true, }); +export class SnapshotCommand extends Command { + static id = "sandbox:snapshot"; + static strict = false; + static summary = "Show snapshot usage"; + static description = "Show snapshot usage or report unknown snapshot subcommands."; + static usage = [" snapshot "]; + + public async run(): Promise { + const [sandboxName, ...actionArgs] = this.argv; + if (!sandboxName || sandboxName.trim() === "") { + this.error("Missing required sandboxName for snapshot.", { exit: 2 }); + } + await getRuntimeBridge().sandboxSnapshot(sandboxName, actionArgs); + } +} + export class SnapshotListCommand extends Command { static id = "sandbox:snapshot:list"; static strict = true; diff --git a/src/nemoclaw.ts b/src/nemoclaw.ts index 3984e225af3..e8e762042dd 100644 --- a/src/nemoclaw.ts +++ b/src/nemoclaw.ts @@ -249,38 +249,6 @@ async function runDispatchResult( console.error(` Unknown action: ${result.action}`); console.error(` Valid actions: ${VALID_SANDBOX_ACTIONS}`); process.exit(1); - case "legacy": { - const sandboxName = opts.sandboxName; - const actionArgs = opts.actionArgs ?? []; - if (!sandboxName) { - throw new Error(`Missing sandbox name for legacy dispatch target ${result.target}`); - } - switch (result.target) { - case "policy-add": { - const { addSandboxPolicy } = require("./lib/policy-channel-actions") as { - addSandboxPolicy: (sandboxName: string, args?: string[]) => Promise; - }; - await addSandboxPolicy(sandboxName, actionArgs); - return; - } - case "skill": { - const { installSandboxSkill } = require("./lib/sandbox-skill-install-action") as { - installSandboxSkill: (sandboxName: string, args?: string[]) => Promise; - }; - await installSandboxSkill(sandboxName, actionArgs); - return; - } - case "snapshot": { - const { runSandboxSnapshot } = require("./lib/snapshot-action") as { - runSandboxSnapshot: (sandboxName: string, args: string[]) => Promise; - }; - await runSandboxSnapshot(sandboxName, actionArgs); - return; - } - default: - throw new Error(`Unhandled legacy dispatch target ${result.target}`); - } - } } }