@anna-ai/cli 0.1.19 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/_lifecycle-shared-sbea9HtH.js +65 -0
  2. package/dist/{agent-DUmINbo4.js → agent-Br6zY2qw.js} +1 -1
  3. package/dist/app-bundle-upload-DuLalcSt.js +213 -0
  4. package/dist/app-cache-BEM653Th.js +53 -0
  5. package/dist/apps-B1Nd8l_t.js +221 -0
  6. package/dist/apps-BTn9EN0x.js +53 -0
  7. package/dist/apps-cut-DtEkddIk.js +83 -0
  8. package/dist/apps-destructive-DSTrcFUP.js +104 -0
  9. package/dist/apps-discard-y3_IwcbQ.js +44 -0
  10. package/dist/apps-grants-BGWlpee0.js +34 -0
  11. package/dist/apps-publish-CaTCanDu.js +265 -0
  12. package/dist/apps-publish-DfZTOxBJ.js +14 -0
  13. package/dist/apps-push-B9XT2uwF.js +127 -0
  14. package/dist/apps-release-BLH9XSxB.js +135 -0
  15. package/dist/apps-status-DQ9RvlME.js +58 -0
  16. package/dist/apps-submit-review-DLwCxeAs.js +45 -0
  17. package/dist/apps-sync-meta-D9eKMMUp.js +72 -0
  18. package/dist/apps-versions-2Tmk0nsx.js +43 -0
  19. package/dist/bridge-Id8K8gr-.js +3 -0
  20. package/dist/bundled-executas-BNOKw4kv.js +161 -0
  21. package/dist/bundled-executas-CNaV2C_O.js +5 -0
  22. package/dist/cli.js +346 -23
  23. package/dist/client-Dn9zThOd.js +150 -0
  24. package/dist/confirm-DxHkk9Wn.js +37 -0
  25. package/dist/dev-BS_8yoSm.js +3 -0
  26. package/dist/{dev-b1j-dEM2.js → dev-E7mqXj5S.js} +95 -26
  27. package/dist/{dev-app-cache-3Pfesngr.js → dev-app-cache-D-r6ZpEk.js} +11 -2
  28. package/dist/{doctor-CgJYokiR.js → doctor-DKrt-Kda.js} +1 -1
  29. package/dist/executa-cache-BFoUtb4J.js +86 -0
  30. package/dist/executa-cache-WBkCLic7.js +4 -0
  31. package/dist/executa-destructive-COQE4Xqi.js +104 -0
  32. package/dist/{executa-dev-BeC6a8S8.js → executa-dev-BvS9zTpO.js} +11 -11
  33. package/dist/executa-publish-B88_9gbp.js +9 -0
  34. package/dist/executa-publish-Ca5V7MyA.js +258 -0
  35. package/dist/executa-reads-CQ6S8gHY.js +107 -0
  36. package/dist/executas-Cep6KEo0.js +109 -0
  37. package/dist/manifest-DGwRap2i.js +188 -0
  38. package/dist/publish-C1wcf-qI.js +58 -0
  39. package/dist/{server-BgJGmEpv.js → server-_IG8Igje.js} +200 -2
  40. package/dist/{storage-EQJA_0UW.js → storage-CTkApNQ9.js} +1 -1
  41. package/dist/token-B9JUPelx.js +87 -0
  42. package/dist/working-orchestration-Dw9u1Vq0.js +190 -0
  43. package/package.json +3 -3
  44. package/templates/executa/go/executa.json +5 -0
  45. package/templates/executa/node/executa.json +5 -0
  46. package/templates/executa/python/executa.json +5 -0
  47. package/dist/apps-ClgEOdKD.js +0 -44
  48. package/dist/bridge-B3Vwr4cg.js +0 -3
  49. package/dist/dev-D8o7xi0W.js +0 -3
  50. package/dist/dev-app-cache-CZ1UjMz0.js +0 -4
  51. /package/dist/{bridge-mkb_EM-y.js → bridge-BuklhzeE.js} +0 -0
  52. /package/dist/{credentials-DDqx6XMQ.js → credentials-DklPMD22.js} +0 -0
  53. /package/dist/{dev-account-DCyjamBa.js → dev-account-qRaET1Cp.js} +0 -0
  54. /package/dist/{executa-init-COEmKDOE.js → executa-init-Jp-h9OI7.js} +0 -0
  55. /package/dist/{executa-register-66WKIwQQ.js → executa-register-CulDtwYZ.js} +0 -0
  56. /package/dist/{fixture-CATHyLLI.js → fixture-CYwxbiQD.js} +0 -0
  57. /package/dist/{host_upload-C_pGOS6p.js → host_upload-GXVkDM5M.js} +0 -0
  58. /package/dist/{image-bwolX7pa.js → image-DduR91n5.js} +0 -0
  59. /package/dist/{login-CsIVbrmf.js → login-BGqFjQwH.js} +0 -0
  60. /package/dist/{logout-gfmKQxMj.js → logout-CGIRKH3y.js} +0 -0
  61. /package/dist/{runner-DmGLdat0.js → runner-B-hIqx5L.js} +0 -0
  62. /package/dist/{sampling-CJUDG-mf.js → sampling-CXke7hq1.js} +0 -0
  63. /package/dist/{whoami-BS5wy-Nh.js → whoami-BoFLEUcp.js} +0 -0
package/dist/cli.js CHANGED
@@ -160,14 +160,22 @@ function checkHostApiAllowance(usages, manifest, bundleMethods) {
160
160
  //#endregion
161
161
  //#region src/validate/ui-section.ts
162
162
  /**
163
- * Static validation of `manifest.ui` — direct port of
163
+ * Static validation of `manifest.ui` — port of
164
164
  * `src/services/anna_app_validator.py::validate_ui_section_static` from
165
165
  * matrix-nexus. Keep these algorithms byte-equivalent: any divergence here
166
166
  * means `anna-app validate` will lie about what production accepts.
167
+ *
168
+ * One intentional divergence: this CLI validates the **source** manifest
169
+ * *before* publish, so it must additionally tolerate the `bundled:<handle>`
170
+ * placeholder form in `host_api.tools` / `required_executas[].tool_id`.
171
+ * Those placeholders are resolved to real tool_ids at publish time by
172
+ * `substituteBundledRefs` (src/publish/bundled-executas.ts); production
173
+ * (the Python validator) only ever sees the substituted manifest, where
174
+ * the `bundled:` prefix is gone.
167
175
  */
168
176
  const VIEW_NAME_PATTERN = /^[a-z0-9_-]{1,40}$/;
169
177
  const BUNDLE_PATH_PATTERN = /^[A-Za-z0-9_./\-]+$/;
170
- const HOST_API_TOOL_REF_PATTERN = /^(?:required:\*|optional:\*|required:[A-Za-z0-9_.\-]+|optional:[A-Za-z0-9_.\-]+|[A-Za-z0-9_.\-]+)$/;
178
+ const HOST_API_TOOL_REF_PATTERN = /^(?:required:\*|optional:\*|(?:required:|optional:)?bundled:[A-Za-z0-9_.\-]+|required:[A-Za-z0-9_.\-]+|optional:[A-Za-z0-9_.\-]+|[A-Za-z0-9_.\-]+)$/;
171
179
  const CSP_OVERRIDABLE_DIRECTIVES = new Set([
172
180
  "connect-src",
173
181
  "img-src",
@@ -211,7 +219,8 @@ function validateUiSectionStatic(manifest) {
211
219
  continue;
212
220
  }
213
221
  if (ref === "required:*" || ref === "optional:*") continue;
214
- const bare = ref.includes(":") ? ref.split(":", 2)[1] : ref;
222
+ const colon = ref.indexOf(":");
223
+ const bare = ref.startsWith("required:") || ref.startsWith("optional:") ? ref.slice(colon + 1) : ref;
215
224
  if (!requiredIds.has(bare) && !optionalIds.has(bare)) errors.push(`host_api.tools 引用未在 manifest 中声明的 tool_id: ${ref}`);
216
225
  }
217
226
  const cspOverrides = ui.csp_overrides ?? {};
@@ -451,7 +460,7 @@ program.command("validate").description("Run schema + ACL checks on a manifest+b
451
460
  process.exit(code);
452
461
  });
453
462
  program.command("dev").description("Run a local harness (in-process dispatcher + iframe + SSE relay)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bundle <dir>", "bundle directory (default: ./bundle)").option("--slug <slug>", "App slug (overrides manifest.slug/name)").option("--view <name>", "View name to open (default: manifest default)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (auto-detected if omitted; can also use $ANNA_NEXUS_ROOT)").option("--port <number>", "HTTP port", "5180").option("--user-id <id>", "Harness user_id", "1").option("--cwd <dir>", "Project root (default: cwd)").option("--no-watch", "Disable bundle file watcher (default: enabled)").option("--executa <spec>", "Explicit executa registration; repeatable. Spec: comma-separated key=value (dir=<path>[,tool_id=<id>][,type=python|node|go|binary][,command=\"<argv>\"]). When only `dir=` is given, the executa is auto-detected from executa.json / pyproject.toml / package.json / go.mod. Overrides directory auto-discovery under <manifest-dir>/executas/.", (val, prev) => prev ? [...prev, val] : [val]).option("--no-llm", "Disable LLM bridge (anna.llm/agent return llm_disabled)").option("--mock-llm <fixture>", "Serve canned LLM responses from a JSONL fixture").option("--llm-account <host>", "Saved account host to use (default: current)").option("--llm-app-slug <slug>", "Override the manifest slug used to register / look up the dev AnnaApp (default: manifest.slug)").option("--storage <mode>", "Storage backend: \"legacy\" (in-memory runtime_state, default) or \"aps\" (real nexus APS via /api/v1/storage/* — requires `anna-app login`).", "legacy").action(async (opts) => {
454
- const { runDev, parseExecutaSpec } = await import("./dev-D8o7xi0W.js");
463
+ const { runDev, parseExecutaSpec } = await import("./dev-BS_8yoSm.js");
455
464
  const cwd = opts.cwd ?? process.cwd();
456
465
  let executas;
457
466
  if (opts.executa && opts.executa.length > 0) {
@@ -486,7 +495,7 @@ program.command("dev").description("Run a local harness (in-process dispatcher +
486
495
  });
487
496
  const fixture = program.command("fixture").description("Inspect / replay harness recordings (Phase 6)");
488
497
  fixture.command("verify <file>").description("Schema + invariant checks on a harness JSONL recording").option("--json", "Emit machine-readable JSON", false).action(async (file, opts) => {
489
- const { runFixtureVerify } = await import("./fixture-CATHyLLI.js");
498
+ const { runFixtureVerify } = await import("./fixture-CYwxbiQD.js");
490
499
  const code = await runFixtureVerify({
491
500
  file,
492
501
  json: opts.json
@@ -494,7 +503,7 @@ fixture.command("verify <file>").description("Schema + invariant checks on a har
494
503
  process.exit(code);
495
504
  });
496
505
  fixture.command("summarize <file>").description("Print a human-readable digest of a harness recording").option("--json", "Emit machine-readable JSON", false).action(async (file, opts) => {
497
- const { runFixtureSummarize } = await import("./fixture-CATHyLLI.js");
506
+ const { runFixtureSummarize } = await import("./fixture-CYwxbiQD.js");
498
507
  const code = await runFixtureSummarize({
499
508
  file,
500
509
  json: opts.json
@@ -502,7 +511,7 @@ fixture.command("summarize <file>").description("Print a human-readable digest o
502
511
  process.exit(code);
503
512
  });
504
513
  fixture.command("replay <file>").description("Dry-run replay of a harness recording (Phase 6 MVP)").option("--manifest <path>", "manifest.json path", "manifest.json").action(async (file, opts) => {
505
- const { runFixtureReplay } = await import("./fixture-CATHyLLI.js");
514
+ const { runFixtureReplay } = await import("./fixture-CYwxbiQD.js");
506
515
  const code = await runFixtureReplay({
507
516
  file,
508
517
  manifest: opts.manifest
@@ -510,12 +519,12 @@ fixture.command("replay <file>").description("Dry-run replay of a harness record
510
519
  process.exit(code);
511
520
  });
512
521
  program.command("doctor").description("Check environment for `anna-app dev` (uv, matrix-nexus, dev key)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (optional)").action(async (opts) => {
513
- const { runDoctor } = await import("./doctor-CgJYokiR.js");
522
+ const { runDoctor } = await import("./doctor-DKrt-Kda.js");
514
523
  const code = await runDoctor({ matrixNexusRoot: opts.matrixNexusRoot });
515
524
  process.exit(code);
516
525
  });
517
526
  program.command("login").description("Device-flow login against a nexus host; saves a PAT to ~/.config/anna/credentials.json").requiredOption("--host <url>", "nexus base URL, e.g. https://nexus.example.com").option("--no-browser", "Do not open a browser window automatically", false).action(async (opts) => {
518
- const { runLogin } = await import("./login-CsIVbrmf.js");
527
+ const { runLogin } = await import("./login-BGqFjQwH.js");
519
528
  const code = await runLogin({
520
529
  host: opts.host,
521
530
  noBrowser: opts.browser === false
@@ -523,7 +532,7 @@ program.command("login").description("Device-flow login against a nexus host; sa
523
532
  process.exit(code);
524
533
  });
525
534
  program.command("logout").description("Remove a saved PAT entry").option("--host <url>", "Account to remove (default: current)").option("--all", "Remove every saved account", false).action(async (opts) => {
526
- const { runLogout } = await import("./logout-gfmKQxMj.js");
535
+ const { runLogout } = await import("./logout-CGIRKH3y.js");
527
536
  const code = await runLogout({
528
537
  host: opts.host,
529
538
  all: opts.all
@@ -531,18 +540,10 @@ program.command("logout").description("Remove a saved PAT entry").option("--host
531
540
  process.exit(code);
532
541
  });
533
542
  program.command("whoami").description("Show the current account (and any others)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
534
- const { runWhoami } = await import("./whoami-BS5wy-Nh.js");
543
+ const { runWhoami } = await import("./whoami-BoFLEUcp.js");
535
544
  const code = await runWhoami({ json: opts.json });
536
545
  process.exit(code);
537
546
  });
538
- program.command("apps:list").description("List dev apps installed for the current PAT").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
539
- const { runAppsList } = await import("./apps-ClgEOdKD.js");
540
- const code = await runAppsList({
541
- account: opts.account,
542
- json: opts.json
543
- });
544
- process.exit(code);
545
- });
546
547
  const executa = program.command("executa").description("Standalone Executa plugin scaffolding + local runner");
547
548
  executa.command("init <dir>").description("Scaffold a standalone Executa plugin (python | node | go)").option("--slug <slug>", "Plugin slug (lowercase, hyphens)", "").option("--template <name>", "Language template: python | node | go", "python").option("--tool-id <id>", "Override the minted tool_id (default: tool-dev-<slug>)").option("--force", "Overwrite existing dir if non-empty", false).action(async (dir, opts) => {
548
549
  const slug = opts.slug || dir.split(/[\\/]/).pop() || "my-executa";
@@ -555,7 +556,7 @@ executa.command("init <dir>").description("Scaffold a standalone Executa plugin
555
556
  console.error(`✗ unknown template "${opts.template}" — expected python | node | go`);
556
557
  process.exit(2);
557
558
  }
558
- const { runExecutaInit } = await import("./executa-init-COEmKDOE.js");
559
+ const { runExecutaInit } = await import("./executa-init-Jp-h9OI7.js");
559
560
  const code = runExecutaInit({
560
561
  targetDir: dir,
561
562
  slug,
@@ -565,13 +566,13 @@ executa.command("init <dir>").description("Scaffold a standalone Executa plugin
565
566
  });
566
567
  process.exit(code);
567
568
  });
568
- executa.command("register").description("Register a standalone executa with nexus so `executa dev --storage real` (and future agent / sampling real-mode flows) can mint tokens for it").requiredOption("--tool-id <id>", "Executa tool_id (matches manifest.name)").option("--slug <slug>", "App slug (default: executa-<tool_id>; must be lowercase + hyphens)").option("--name <name>", "Human-readable display name (default: <tool_id>)").option("--account <host>", "Saved account host (default: current)").action(async (opts) => {
569
- const { runExecutaRegister } = await import("./executa-register-66WKIwQQ.js");
569
+ executa.command("register").description("Register a HARNESS AnnaApp(kind=executa) so `executa dev --storage real` (and agent / sampling / image / upload real-mode flows) can mint tokens for a local executa subprocess. Does NOT publish the executa itself — use `executa publish` for that. Lists/deletes via `apps list` / `apps delete`.").requiredOption("--tool-id <id>", "Executa tool_id (matches manifest.name)").option("--slug <slug>", "App slug (default: executa-<tool_id>; must be lowercase + hyphens)").option("--name <name>", "Human-readable display name (default: <tool_id>)").option("--account <host>", "Saved account host (default: current)").action(async (opts) => {
570
+ const { runExecutaRegister } = await import("./executa-register-CulDtwYZ.js");
570
571
  const code = await runExecutaRegister(opts);
571
572
  process.exit(code);
572
573
  });
573
574
  executa.command("dev").description("Run one Executa plugin in isolation (REPL or one-shot describe/invoke)").option("--dir <path>", "Executa project dir (default: CWD)").option("--spec <spec>", "Override discovery: comma-separated key=value (tool_id=...,type=...,command=\"...\")").option("--describe", "Print MANIFEST and exit", false).option("--health", "Print health and exit", false).option("--invoke <tool>", "Invoke one tool and exit").option("--args <json>", "JSON object passed as tool arguments", "{}").option("--json", "One-shot: emit compact JSON (no banners)", false).option("--no-sampling", "Hard-disable sampling reverse RPC (returns sampling_disabled)").option("--mock-sampling <fixture>", "Serve canned sampling responses from a JSONL fixture (offline)").option("--app-slug <slug>", "Forward sampling to nexus on behalf of this dev AnnaApp slug").option("--sampling-account <host>", "Saved account host for nexus sampling (default: current)").option("--no-agent", "Hard-disable agent reverse RPC (returns agent_not_granted)").option("--mock-agent <fixture>", "Serve canned agent/* responses from a JSONL fixture (offline)").option("--agent-account <host>", "Saved account host for nexus agent (default: --sampling-account or current)").option("--storage <mode>", "Storage backend: off | memory | mock | real (default: memory)").option("--mock-storage <fixture>", "Serve canned storage/* + files/* responses from a JSONL fixture").option("--storage-account <host>", "Saved account host for nexus storage (default: --sampling-account or current)").option("--storage-scopes <list>", "Comma-separated scopes for real storage tokens (default: user,app,tool)").option("--no-image", "Hard-disable image reverse RPC (returns image_not_granted)").option("--mock-image <fixture>", "Serve canned image/generate + image/edit responses from a JSONL fixture").option("--image-account <host>", "Saved account host for nexus image (default: --sampling-account or current)").option("--no-upload", "Hard-disable host/uploadFile reverse RPC (returns upload_not_granted)").option("--mock-upload <fixture>", "Serve canned host/uploadFile responses from a JSONL fixture").option("--upload-account <host>", "Saved account host for nexus uploads (default: --sampling-account or current)").action(async (opts) => {
574
- const { runExecutaDev } = await import("./executa-dev-BeC6a8S8.js");
575
+ const { runExecutaDev } = await import("./executa-dev-BvS9zTpO.js");
575
576
  const storageMode = opts.storage === void 0 ? void 0 : (() => {
576
577
  const m = opts.storage;
577
578
  if (m === "off" || m === "memory" || m === "mock" || m === "real") return m;
@@ -606,6 +607,328 @@ executa.command("dev").description("Run one Executa plugin in isolation (REPL or
606
607
  });
607
608
  process.exit(code);
608
609
  });
610
+ const apps = program.command("apps").description("Anna App publish lifecycle commands");
611
+ apps.command("list").description("List the Anna Apps you authored, with lifecycle status").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
612
+ const { runAppsList } = await import("./apps-BTn9EN0x.js");
613
+ process.exit(await runAppsList({
614
+ account: opts.account,
615
+ json: opts.json
616
+ }));
617
+ });
618
+ apps.command("publish").description("Publish or update an Anna App (manifest.json in cwd)").option("--cwd <dir>", "Project root (default: cwd)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bump <kind>", "Bump version: patch|minor|major").option("--no-write", "Skip writing the bumped version back to disk", false).option("--dry-run", "Resolve identity + diff but don't upload", false).option("--executa-id <handle=id>", "Override a bundled executa handle with a real tool_id (repeatable)", (val, prev) => [...prev, val], []).option("--skip-executa-publish", "Use cached/overridden tool_ids; don't recursively publish bundled executas", false).option("--no-bundled-executas", "Disable bundled-executa orchestration (legacy behaviour)").option("--no-bundle", "Skip UI bundle upload even if the manifest declares a UI").option("--bundle-dir <dir>", "UI bundle directory (default: <cwd>/bundle)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
619
+ if (opts.bump && ![
620
+ "patch",
621
+ "minor",
622
+ "major"
623
+ ].includes(opts.bump)) {
624
+ console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
625
+ process.exit(2);
626
+ }
627
+ const { runAppsPublish } = await import("./apps-publish-DfZTOxBJ.js");
628
+ process.exit(await runAppsPublish({
629
+ cwd: opts.cwd,
630
+ manifest: opts.manifest,
631
+ bump: opts.bump,
632
+ noWrite: opts.write === false,
633
+ dryRun: opts.dryRun,
634
+ executaId: opts.executaId,
635
+ skipExecutaPublish: opts.skipExecutaPublish,
636
+ noBundledExecutas: opts.bundledExecutas === false,
637
+ noBundle: opts.bundle === false,
638
+ bundleDir: opts.bundleDir,
639
+ account: opts.account,
640
+ json: opts.json
641
+ }));
642
+ });
643
+ apps.command("push").description("Upsert the mutable working draft (manifest + bundle, no freeze)").option("--cwd <dir>", "Project root (default: cwd)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bump <kind>", "Bump version: patch|minor|major").option("--no-write", "Skip writing the bumped version back to disk", false).option("--dry-run", "Resolve identity + diff but don't upsert", false).option("--executa-id <handle=id>", "Override a bundled executa handle with a real tool_id (repeatable)", (val, prev) => [...prev, val], []).option("--skip-executa-publish", "Use cached/overridden tool_ids; don't recursively register bundled executas", false).option("--no-bundled-executas", "Disable bundled-executa orchestration (legacy behaviour)").option("--no-bundle", "Skip working UI bundle upload even if the manifest declares a UI").option("--bundle-dir <dir>", "UI bundle directory (default: <cwd>/bundle)").option("--if-match <revision>", "Optimistic-lock: require this working revision").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
644
+ if (opts.bump && ![
645
+ "patch",
646
+ "minor",
647
+ "major"
648
+ ].includes(opts.bump)) {
649
+ console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
650
+ process.exit(2);
651
+ }
652
+ let ifMatch;
653
+ if (opts.ifMatch !== void 0) {
654
+ ifMatch = Number.parseInt(opts.ifMatch, 10);
655
+ if (!Number.isFinite(ifMatch)) {
656
+ console.error(`✗ --if-match must be an integer revision (got: ${opts.ifMatch})`);
657
+ process.exit(2);
658
+ }
659
+ }
660
+ const { runAppsPush } = await import("./apps-push-B9XT2uwF.js");
661
+ process.exit(await runAppsPush({
662
+ cwd: opts.cwd,
663
+ manifest: opts.manifest,
664
+ bump: opts.bump,
665
+ noWrite: opts.write === false,
666
+ dryRun: opts.dryRun,
667
+ executaId: opts.executaId,
668
+ skipExecutaPublish: opts.skipExecutaPublish,
669
+ noBundledExecutas: opts.bundledExecutas === false,
670
+ noBundle: opts.bundle === false,
671
+ bundleDir: opts.bundleDir,
672
+ ifMatch,
673
+ account: opts.account,
674
+ json: opts.json
675
+ }));
676
+ });
677
+ apps.command("cut <version>").description("Snapshot the working draft into an immutable version (freeze deps)").option("--changelog <text>", "Override the working draft changelog for this version").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--dry-run", "Resolve target but don't cut", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (version, opts) => {
678
+ const { runAppsCut } = await import("./apps-cut-DtEkddIk.js");
679
+ process.exit(await runAppsCut({
680
+ version,
681
+ changelog: opts.changelog,
682
+ slug: opts.slug,
683
+ cwd: opts.cwd,
684
+ dryRun: opts.dryRun,
685
+ account: opts.account,
686
+ json: opts.json
687
+ }));
688
+ });
689
+ apps.command("discard").description("Drop the mutable working draft (leaves cut versions intact)").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--dry-run", "Resolve target but don't discard", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
690
+ const { runAppsDiscard } = await import("./apps-discard-y3_IwcbQ.js");
691
+ process.exit(await runAppsDiscard({
692
+ slug: opts.slug,
693
+ cwd: opts.cwd,
694
+ dryRun: opts.dryRun,
695
+ account: opts.account,
696
+ json: opts.json
697
+ }));
698
+ });
699
+ apps.command("release <version>").description("Freeze & publish an existing remote version (go live)").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--allow-create", "If the version isn't on the server, run 'apps publish' first, then release", false).option("--dry-run", "Resolve target + pre-flight but don't publish", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (version, opts) => {
700
+ const { runAppsRelease } = await import("./apps-release-BLH9XSxB.js");
701
+ process.exit(await runAppsRelease({
702
+ version,
703
+ slug: opts.slug,
704
+ cwd: opts.cwd,
705
+ allowCreate: opts.allowCreate,
706
+ dryRun: opts.dryRun,
707
+ account: opts.account,
708
+ json: opts.json
709
+ }));
710
+ });
711
+ apps.command("sync-meta").description("Push mutable store metadata (name/tagline/…) from the manifest").option("--cwd <dir>", "Project root (default: cwd)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--dry-run", "Print the metadata diff but don't PATCH", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
712
+ const { runAppsSyncMeta } = await import("./apps-sync-meta-D9eKMMUp.js");
713
+ process.exit(await runAppsSyncMeta({
714
+ cwd: opts.cwd,
715
+ manifest: opts.manifest,
716
+ dryRun: opts.dryRun,
717
+ account: opts.account,
718
+ json: opts.json
719
+ }));
720
+ });
721
+ apps.command("submit-review [slug]").description("Move an app into review (DRAFT/REJECTED → PENDING_REVIEW)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
722
+ const { runAppsSubmitReview } = await import("./apps-submit-review-DLwCxeAs.js");
723
+ process.exit(await runAppsSubmitReview({
724
+ slug,
725
+ cwd: opts.cwd,
726
+ account: opts.account,
727
+ json: opts.json
728
+ }));
729
+ });
730
+ apps.command("status <slug>").description("Show server-known state for one Anna App").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
731
+ const { runAppsStatus } = await import("./apps-status-DQ9RvlME.js");
732
+ process.exit(await runAppsStatus({
733
+ slug,
734
+ account: opts.account,
735
+ json: opts.json
736
+ }));
737
+ });
738
+ apps.command("versions <slug>").description("List all server versions of one Anna App").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
739
+ const { runAppsVersions } = await import("./apps-versions-2Tmk0nsx.js");
740
+ process.exit(await runAppsVersions({
741
+ slug,
742
+ account: opts.account,
743
+ json: opts.json
744
+ }));
745
+ });
746
+ apps.command("grants <slug>").description("Show currently granted scopes/quota for one Anna App").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
747
+ const { runAppsGrants } = await import("./apps-grants-BGWlpee0.js");
748
+ process.exit(await runAppsGrants({
749
+ slug,
750
+ account: opts.account,
751
+ json: opts.json
752
+ }));
753
+ });
754
+ apps.command("unpublish <slug>").description("Flip a PUBLISHED app back to APPROVED (private)").option("--yes", "Acknowledge the destructive operation", false).option("--confirm <slug>", "Slug confirmation (must match target)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
755
+ const { runAppsUnpublish } = await import("./apps-destructive-DSTrcFUP.js");
756
+ process.exit(await runAppsUnpublish({
757
+ slug,
758
+ yes: opts.yes,
759
+ confirm: opts.confirm,
760
+ account: opts.account,
761
+ json: opts.json
762
+ }));
763
+ });
764
+ apps.command("archive <slug>").description("Archive an Anna App (hides it from end users)").option("--yes", "Acknowledge the destructive operation", false).option("--confirm <slug>", "Slug confirmation (must match target)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
765
+ const { runAppsArchive } = await import("./apps-destructive-DSTrcFUP.js");
766
+ process.exit(await runAppsArchive({
767
+ slug,
768
+ yes: opts.yes,
769
+ confirm: opts.confirm,
770
+ account: opts.account,
771
+ json: opts.json
772
+ }));
773
+ });
774
+ apps.command("unarchive <slug>").description("Restore an archived Anna App back to APPROVED").option("--yes", "Acknowledge the operation", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
775
+ const { runAppsUnarchive } = await import("./apps-destructive-DSTrcFUP.js");
776
+ process.exit(await runAppsUnarchive({
777
+ slug,
778
+ yes: opts.yes,
779
+ account: opts.account,
780
+ json: opts.json
781
+ }));
782
+ });
783
+ apps.command("delete <slug>").description("Hard-delete an Anna App (refused by server if installs exist)").option("--yes", "Acknowledge the destructive operation", false).option("--confirm <slug>", "Slug confirmation (must match target)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
784
+ const { runAppsDelete } = await import("./apps-destructive-DSTrcFUP.js");
785
+ process.exit(await runAppsDelete({
786
+ slug,
787
+ yes: opts.yes,
788
+ confirm: opts.confirm,
789
+ account: opts.account,
790
+ json: opts.json
791
+ }));
792
+ });
793
+ executa.command("list").description("List Executas owned by the current PAT").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
794
+ const { runExecutaList } = await import("./executa-reads-CQ6S8gHY.js");
795
+ process.exit(await runExecutaList({
796
+ account: opts.account,
797
+ json: opts.json
798
+ }));
799
+ });
800
+ executa.command("status <ref>").description("Show server-known state for one Executa (slug or tool_id)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (ref, opts) => {
801
+ const { runExecutaStatus } = await import("./executa-reads-CQ6S8gHY.js");
802
+ process.exit(await runExecutaStatus({
803
+ ref,
804
+ account: opts.account,
805
+ json: opts.json
806
+ }));
807
+ });
808
+ executa.command("versions <ref>").description("List immutable version snapshots for one Executa").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (ref, opts) => {
809
+ const { runExecutaVersions } = await import("./executa-reads-CQ6S8gHY.js");
810
+ process.exit(await runExecutaVersions({
811
+ ref,
812
+ account: opts.account,
813
+ json: opts.json
814
+ }));
815
+ });
816
+ executa.command("publish").description("Mint or update an Executa (executa.json in cwd)").option("--cwd <dir>", "Project root (default: cwd)").option("--manifest <path>", "executa.json path", "executa.json").option("--bump <kind>", "Bump version: patch|minor|major").option("--no-write", "Skip writing the bumped version back to disk", false).option("--publish", "After freezing the version, flip visibility to PUBLIC", false).option("--dry-run", "Resolve identity + diff but don't upload", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
817
+ if (opts.bump && ![
818
+ "patch",
819
+ "minor",
820
+ "major"
821
+ ].includes(opts.bump)) {
822
+ console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
823
+ process.exit(2);
824
+ }
825
+ const { runExecutaPublish } = await import("./executa-publish-B88_9gbp.js");
826
+ process.exit(await runExecutaPublish({
827
+ cwd: opts.cwd,
828
+ manifest: opts.manifest,
829
+ bump: opts.bump,
830
+ noWrite: opts.write === false,
831
+ publish: opts.publish,
832
+ dryRun: opts.dryRun,
833
+ account: opts.account,
834
+ json: opts.json
835
+ }));
836
+ });
837
+ executa.command("unpublish <slug>").description("Flip an Executa's visibility back to private").option("--yes", "Acknowledge the destructive operation", false).option("--confirm <slug>", "Slug confirmation (must match target)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
838
+ const { runExecutaUnpublish } = await import("./executa-destructive-COQE4Xqi.js");
839
+ process.exit(await runExecutaUnpublish({
840
+ slug,
841
+ yes: opts.yes,
842
+ confirm: opts.confirm,
843
+ account: opts.account,
844
+ json: opts.json
845
+ }));
846
+ });
847
+ executa.command("yank <ref>").description("Hard-delete one Executa version (<slug>@<version>)").option("--yes", "Acknowledge the destructive operation", false).option("--confirm <slug>", "Slug confirmation (must match target)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (ref, opts) => {
848
+ const { runExecutaYank } = await import("./executa-destructive-COQE4Xqi.js");
849
+ process.exit(await runExecutaYank({
850
+ ref,
851
+ yes: opts.yes,
852
+ confirm: opts.confirm,
853
+ account: opts.account,
854
+ json: opts.json
855
+ }));
856
+ });
857
+ executa.command("delete <slug>").description("Hard-delete an Executa").option("--yes", "Acknowledge the destructive operation", false).option("--confirm <slug>", "Slug confirmation (must match target)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (slug, opts) => {
858
+ const { runExecutaDelete } = await import("./executa-destructive-COQE4Xqi.js");
859
+ process.exit(await runExecutaDelete({
860
+ slug,
861
+ yes: opts.yes,
862
+ confirm: opts.confirm,
863
+ account: opts.account,
864
+ json: opts.json
865
+ }));
866
+ });
867
+ executa.command("cache-clear").description("Delete the local `.anna/executa.json` identity cache. Use after the server-side Executa has been deleted/recreated, or when switching host. Next `executa publish` will re-mint via the idempotency key.").option("--cwd <dir>", "Project root (default: cwd)").action(async (opts) => {
868
+ const { resolve: resolve$1 } = await import("node:path");
869
+ const { existsSync: existsSync$1 } = await import("node:fs");
870
+ const { executaCachePath, invalidateExecutaCache } = await import("./executa-cache-WBkCLic7.js");
871
+ const cwd = resolve$1(opts.cwd ?? process.cwd());
872
+ const p = executaCachePath(cwd);
873
+ if (!existsSync$1(p)) {
874
+ console.log(`(no cache at ${p})`);
875
+ process.exit(0);
876
+ }
877
+ invalidateExecutaCache(cwd);
878
+ console.log(`✓ removed ${p}`);
879
+ process.exit(0);
880
+ });
881
+ const token = program.command("token").description("Manage developer PATs (list / revoke / scopes)");
882
+ token.command("list").description("List the caller's PATs (most-recent first)").option("--include-revoked", "Also show revoked PATs", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
883
+ const { runTokenList } = await import("./token-B9JUPelx.js");
884
+ process.exit(await runTokenList({
885
+ includeRevoked: opts.includeRevoked,
886
+ account: opts.account,
887
+ json: opts.json
888
+ }));
889
+ });
890
+ token.command("revoke <id>").description("Revoke one of the caller's PATs by id").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (id, opts) => {
891
+ const idNum = Number.parseInt(id, 10);
892
+ if (!Number.isFinite(idNum) || idNum <= 0) {
893
+ console.error(`✗ token id must be a positive integer (got: ${id})`);
894
+ process.exit(2);
895
+ }
896
+ const { runTokenRevoke } = await import("./token-B9JUPelx.js");
897
+ process.exit(await runTokenRevoke({
898
+ id: idNum,
899
+ account: opts.account,
900
+ json: opts.json
901
+ }));
902
+ });
903
+ token.command("scopes").description("Print the available PAT scope catalogue").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
904
+ const { runTokenScopes } = await import("./token-B9JUPelx.js");
905
+ process.exit(await runTokenScopes({
906
+ account: opts.account,
907
+ json: opts.json
908
+ }));
909
+ });
910
+ program.command("publish").description("Auto-detect cwd and publish (apps publish OR executa publish)").option("--cwd <dir>", "Project root (default: cwd)").option("--bump <kind>", "Bump version: patch|minor|major").option("--no-write", "Skip writing the bumped version back to disk", false).option("--publish", "(executa) flip visibility to PUBLIC after upload", false).option("--dry-run", "Resolve identity + diff but don't upload", false).option("--no-bundle", "(apps) skip UI bundle upload").option("--bundle-dir <dir>", "(apps) UI bundle directory (default: <cwd>/bundle)").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
911
+ if (opts.bump && ![
912
+ "patch",
913
+ "minor",
914
+ "major"
915
+ ].includes(opts.bump)) {
916
+ console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
917
+ process.exit(2);
918
+ }
919
+ const { runTopLevelPublish } = await import("./publish-C1wcf-qI.js");
920
+ process.exit(await runTopLevelPublish({
921
+ cwd: opts.cwd,
922
+ bump: opts.bump,
923
+ noWrite: opts.write === false,
924
+ publish: opts.publish,
925
+ dryRun: opts.dryRun,
926
+ noBundle: opts.bundle === false,
927
+ bundleDir: opts.bundleDir,
928
+ account: opts.account,
929
+ json: opts.json
930
+ }));
931
+ });
609
932
  program.parseAsync(process.argv).catch((e) => {
610
933
  console.error(e);
611
934
  process.exit(2);
@@ -0,0 +1,150 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+
3
+ //#region src/api/client.ts
4
+ var CliError = class extends Error {
5
+ exitCode;
6
+ status;
7
+ body;
8
+ constructor(message, exitCode, opts) {
9
+ super(message);
10
+ this.exitCode = exitCode;
11
+ this.status = opts?.status;
12
+ this.body = opts?.body;
13
+ }
14
+ };
15
+ var ScopeError = class extends CliError {
16
+ needed;
17
+ missing;
18
+ have;
19
+ constructor(opts) {
20
+ super(`missing PAT scope(s): ${opts.missing.join(", ") || "(unknown)"}`, 3, {
21
+ status: 403,
22
+ body: opts.body
23
+ });
24
+ this.needed = opts.needed;
25
+ this.missing = opts.missing;
26
+ this.have = opts.have;
27
+ }
28
+ };
29
+ var NexusClient = class {
30
+ host;
31
+ pat;
32
+ fetchImpl;
33
+ userAgent;
34
+ constructor(opts) {
35
+ this.host = canonicalHost(opts.host);
36
+ this.pat = opts.pat;
37
+ this.fetchImpl = opts.fetchImpl ?? fetch;
38
+ this.userAgent = opts.userAgent ?? "anna-app-cli";
39
+ }
40
+ async request(opts) {
41
+ const url = new URL(this.host + opts.path);
42
+ if (opts.query) for (const [k, v] of Object.entries(opts.query)) {
43
+ if (v === void 0 || v === null) continue;
44
+ url.searchParams.set(k, String(v));
45
+ }
46
+ const headers = {
47
+ authorization: `Bearer ${this.pat}`,
48
+ "user-agent": this.userAgent,
49
+ accept: "application/json",
50
+ ...opts.headers ?? {}
51
+ };
52
+ let body;
53
+ if (opts.multipart) {
54
+ const fd = new FormData();
55
+ for (const [k, v] of Object.entries(opts.multipart.fields)) fd.append(k, v);
56
+ body = fd;
57
+ } else if (opts.body !== void 0) {
58
+ headers["content-type"] = "application/json";
59
+ body = JSON.stringify(opts.body);
60
+ }
61
+ const method = opts.method ?? "GET";
62
+ const allowed = new Set(opts.allowStatuses ?? []);
63
+ let res;
64
+ try {
65
+ res = await this.fetchImpl(url, {
66
+ method,
67
+ headers,
68
+ body
69
+ });
70
+ } catch (e) {
71
+ throw new CliError(`network error contacting ${this.host}: ${e.message}`, 6);
72
+ }
73
+ if (res.status >= 500 && !opts.noRetry && !allowed.has(res.status) && method === "GET") try {
74
+ res = await this.fetchImpl(url, {
75
+ method,
76
+ headers,
77
+ body
78
+ });
79
+ } catch (e) {
80
+ throw new CliError(`network error contacting ${this.host}: ${e.message}`, 6);
81
+ }
82
+ const idempotentHit = res.headers.get("x-anna-idempotent") === "hit";
83
+ let parsed = null;
84
+ const text = await res.text();
85
+ if (text.length > 0) try {
86
+ parsed = JSON.parse(text);
87
+ } catch {
88
+ parsed = text;
89
+ }
90
+ if (!res.ok && !allowed.has(res.status)) this.throwForStatus(res.status, parsed);
91
+ return {
92
+ data: parsed,
93
+ status: res.status,
94
+ headers: res.headers,
95
+ idempotentHit
96
+ };
97
+ }
98
+ throwForStatus(status, body) {
99
+ if (status === 401) throw new CliError("PAT rejected by server (401). Run `anna-app login --host …` to refresh.", 2, {
100
+ status,
101
+ body
102
+ });
103
+ if (status === 403) {
104
+ const det = extractDetail(body);
105
+ if (det && typeof det === "object" && det.detail === "missing_scope") {
106
+ const dd = det;
107
+ throw new ScopeError({
108
+ needed: dd.needed ?? [],
109
+ missing: dd.missing ?? [],
110
+ have: dd.have ?? [],
111
+ body
112
+ });
113
+ }
114
+ throw new CliError(`forbidden (403): ${describeBody(body)}`, 3, {
115
+ status,
116
+ body
117
+ });
118
+ }
119
+ if (status >= 500) throw new CliError(`server error (${status}): ${describeBody(body)}`, 6, {
120
+ status,
121
+ body
122
+ });
123
+ throw new CliError(`request failed (${status}): ${describeBody(body)}`, 5, {
124
+ status,
125
+ body
126
+ });
127
+ }
128
+ };
129
+ function extractDetail(body) {
130
+ if (body && typeof body === "object" && "detail" in body) {
131
+ const d = body.detail;
132
+ if (typeof d === "string") return { detail: d };
133
+ if (d && typeof d === "object") return d;
134
+ }
135
+ return null;
136
+ }
137
+ function describeBody(body) {
138
+ if (typeof body === "string") return body.slice(0, 240);
139
+ if (body && typeof body === "object") {
140
+ const det = body.detail;
141
+ if (typeof det === "string") return det;
142
+ try {
143
+ return JSON.stringify(body).slice(0, 240);
144
+ } catch {}
145
+ }
146
+ return String(body ?? "(empty body)");
147
+ }
148
+
149
+ //#endregion
150
+ export { CliError, NexusClient, ScopeError };