@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.
- package/dist/_lifecycle-shared-sbea9HtH.js +65 -0
- package/dist/{agent-DUmINbo4.js → agent-Br6zY2qw.js} +1 -1
- package/dist/app-bundle-upload-DuLalcSt.js +213 -0
- package/dist/app-cache-BEM653Th.js +53 -0
- package/dist/apps-B1Nd8l_t.js +221 -0
- package/dist/apps-BTn9EN0x.js +53 -0
- package/dist/apps-cut-DtEkddIk.js +83 -0
- package/dist/apps-destructive-DSTrcFUP.js +104 -0
- package/dist/apps-discard-y3_IwcbQ.js +44 -0
- package/dist/apps-grants-BGWlpee0.js +34 -0
- package/dist/apps-publish-CaTCanDu.js +265 -0
- package/dist/apps-publish-DfZTOxBJ.js +14 -0
- package/dist/apps-push-B9XT2uwF.js +127 -0
- package/dist/apps-release-BLH9XSxB.js +135 -0
- package/dist/apps-status-DQ9RvlME.js +58 -0
- package/dist/apps-submit-review-DLwCxeAs.js +45 -0
- package/dist/apps-sync-meta-D9eKMMUp.js +72 -0
- package/dist/apps-versions-2Tmk0nsx.js +43 -0
- package/dist/bridge-Id8K8gr-.js +3 -0
- package/dist/bundled-executas-BNOKw4kv.js +161 -0
- package/dist/bundled-executas-CNaV2C_O.js +5 -0
- package/dist/cli.js +346 -23
- package/dist/client-Dn9zThOd.js +150 -0
- package/dist/confirm-DxHkk9Wn.js +37 -0
- package/dist/dev-BS_8yoSm.js +3 -0
- package/dist/{dev-b1j-dEM2.js → dev-E7mqXj5S.js} +95 -26
- package/dist/{dev-app-cache-3Pfesngr.js → dev-app-cache-D-r6ZpEk.js} +11 -2
- package/dist/{doctor-CgJYokiR.js → doctor-DKrt-Kda.js} +1 -1
- package/dist/executa-cache-BFoUtb4J.js +86 -0
- package/dist/executa-cache-WBkCLic7.js +4 -0
- package/dist/executa-destructive-COQE4Xqi.js +104 -0
- package/dist/{executa-dev-BeC6a8S8.js → executa-dev-BvS9zTpO.js} +11 -11
- package/dist/executa-publish-B88_9gbp.js +9 -0
- package/dist/executa-publish-Ca5V7MyA.js +258 -0
- package/dist/executa-reads-CQ6S8gHY.js +107 -0
- package/dist/executas-Cep6KEo0.js +109 -0
- package/dist/manifest-DGwRap2i.js +188 -0
- package/dist/publish-C1wcf-qI.js +58 -0
- package/dist/{server-BgJGmEpv.js → server-_IG8Igje.js} +200 -2
- package/dist/{storage-EQJA_0UW.js → storage-CTkApNQ9.js} +1 -1
- package/dist/token-B9JUPelx.js +87 -0
- package/dist/working-orchestration-Dw9u1Vq0.js +190 -0
- package/package.json +3 -3
- package/templates/executa/go/executa.json +5 -0
- package/templates/executa/node/executa.json +5 -0
- package/templates/executa/python/executa.json +5 -0
- package/dist/apps-ClgEOdKD.js +0 -44
- package/dist/bridge-B3Vwr4cg.js +0 -3
- package/dist/dev-D8o7xi0W.js +0 -3
- package/dist/dev-app-cache-CZ1UjMz0.js +0 -4
- /package/dist/{bridge-mkb_EM-y.js → bridge-BuklhzeE.js} +0 -0
- /package/dist/{credentials-DDqx6XMQ.js → credentials-DklPMD22.js} +0 -0
- /package/dist/{dev-account-DCyjamBa.js → dev-account-qRaET1Cp.js} +0 -0
- /package/dist/{executa-init-COEmKDOE.js → executa-init-Jp-h9OI7.js} +0 -0
- /package/dist/{executa-register-66WKIwQQ.js → executa-register-CulDtwYZ.js} +0 -0
- /package/dist/{fixture-CATHyLLI.js → fixture-CYwxbiQD.js} +0 -0
- /package/dist/{host_upload-C_pGOS6p.js → host_upload-GXVkDM5M.js} +0 -0
- /package/dist/{image-bwolX7pa.js → image-DduR91n5.js} +0 -0
- /package/dist/{login-CsIVbrmf.js → login-BGqFjQwH.js} +0 -0
- /package/dist/{logout-gfmKQxMj.js → logout-CGIRKH3y.js} +0 -0
- /package/dist/{runner-DmGLdat0.js → runner-B-hIqx5L.js} +0 -0
- /package/dist/{sampling-CJUDG-mf.js → sampling-CXke7hq1.js} +0 -0
- /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` —
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
569
|
-
const { runExecutaRegister } = await import("./executa-register-
|
|
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-
|
|
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 };
|