@anna-ai/cli 0.1.19 → 0.1.21

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 +334 -20
  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
@@ -0,0 +1,58 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import "./bundled-executas-BNOKw4kv.js";
5
+ import "./executas-Cep6KEo0.js";
6
+ import "./executa-cache-BFoUtb4J.js";
7
+ import { runExecutaPublish } from "./executa-publish-Ca5V7MyA.js";
8
+ import "./manifest-DGwRap2i.js";
9
+ import { withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
10
+ import "./app-cache-BEM653Th.js";
11
+ import "./app-bundle-upload-DuLalcSt.js";
12
+ import { runAppsPublish } from "./apps-publish-CaTCanDu.js";
13
+ import { resolve } from "node:path";
14
+ import { existsSync } from "node:fs";
15
+ import { red, yellow } from "kleur/colors";
16
+
17
+ //#region src/publish/detector.ts
18
+ function detectProjectKind(cwd) {
19
+ const hasManifest = existsSync(resolve(cwd, "manifest.json"));
20
+ const hasExecuta = existsSync(resolve(cwd, "executa.json"));
21
+ if (hasManifest && hasExecuta) return "ambiguous";
22
+ if (hasManifest) return "app";
23
+ if (hasExecuta) return "executa";
24
+ return "unknown";
25
+ }
26
+
27
+ //#endregion
28
+ //#region src/commands/publish.ts
29
+ async function runTopLevelPublish(opts) {
30
+ return withErrorHandling(async () => {
31
+ const cwd = resolve(opts.cwd ?? process.cwd());
32
+ const kind = detectProjectKind(cwd);
33
+ if (kind === "ambiguous") throw new CliError(`cannot auto-detect: ${cwd} has BOTH manifest.json and executa.json. Run \`anna-app apps publish\` or \`anna-app executa publish\` explicitly.`, 4);
34
+ if (kind === "unknown") throw new CliError(`no manifest.json or executa.json found in ${cwd}`, 4);
35
+ if (kind === "app") return runAppsPublish({
36
+ cwd,
37
+ bump: opts.bump,
38
+ noWrite: opts.noWrite,
39
+ dryRun: opts.dryRun,
40
+ account: opts.account,
41
+ json: opts.json,
42
+ noBundle: opts.noBundle,
43
+ bundleDir: opts.bundleDir
44
+ });
45
+ return runExecutaPublish({
46
+ cwd,
47
+ bump: opts.bump,
48
+ noWrite: opts.noWrite,
49
+ publish: opts.publish,
50
+ dryRun: opts.dryRun,
51
+ account: opts.account,
52
+ json: opts.json
53
+ });
54
+ });
55
+ }
56
+
57
+ //#endregion
58
+ export { runTopLevelPublish };
@@ -1,5 +1,5 @@
1
1
  import { canonicalHost, getAccount } from "./credentials-BTv2IfUZ.js";
2
- import { BridgeRequestError } from "./bridge-mkb_EM-y.js";
2
+ import { BridgeRequestError } from "./bridge-BuklhzeE.js";
3
3
  import { dirname, join, normalize, resolve } from "node:path";
4
4
  import { createRequire } from "node:module";
5
5
  import { createReadStream, existsSync, readFileSync, statSync, watch } from "node:fs";
@@ -10,6 +10,90 @@ import { WebSocketServer } from "ws";
10
10
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
11
11
 
12
12
  //#region src/harness/llm-bridge.ts
13
+ /** APS-mode ``storage`` methods the bridge forwards to ``/api/v1/storage/*``.
14
+ * ``get/set/delete/list`` are the KV family; ``files_*`` are the object/blob
15
+ * (APS Files) family, forwarded from an executa's ``files/*`` reverse-RPC. */
16
+ const STORAGE_APS_METHODS = new Set([
17
+ "get",
18
+ "set",
19
+ "delete",
20
+ "list",
21
+ "files_init",
22
+ "files_finalize",
23
+ "files_url",
24
+ "files_list",
25
+ "files_delete"
26
+ ]);
27
+ /** APS Files method → ``[httpMethod, subPath, payloadKind]``. Mirrors
28
+ * `anna-app-cli/src/executa/storage.ts` ROUTING and matrix `storage.py`
29
+ * so the app-harness path round-trips identically to `executa dev`. */
30
+ const FILES_ROUTES = {
31
+ files_init: [
32
+ "POST",
33
+ "/files/init",
34
+ "body"
35
+ ],
36
+ files_finalize: [
37
+ "POST",
38
+ "/files/finalize",
39
+ "body"
40
+ ],
41
+ files_url: [
42
+ "GET",
43
+ "/files/url",
44
+ "query"
45
+ ],
46
+ files_list: [
47
+ "GET",
48
+ "/files",
49
+ "query"
50
+ ],
51
+ files_delete: [
52
+ "DELETE",
53
+ "/files",
54
+ "query"
55
+ ]
56
+ };
57
+ /** App-direct ``anna.files.*`` method → ``[httpMethod, subPath, payloadKind]``.
58
+ * These are the iframe-facing method names (ns=``"files"``) the browser SDK
59
+ * emits via ``rt.call("files", method, args)``. Production nexus serves them
60
+ * through ``anna_app_rpc_handlers/files.py`` with ``scope="app"`` /
61
+ * ``owner_id=app_id`` (the handler ignores ``args.scope``); we force the same
62
+ * scope here so the harness round-trips identically. Distinct from
63
+ * ``FILES_ROUTES`` above, which is the executa reverse-RPC method naming
64
+ * (``files_init`` …) forwarded with the executa's own (default ``user``) scope. */
65
+ const FILES_APP_ROUTES = {
66
+ upload_init: [
67
+ "POST",
68
+ "/files/init",
69
+ "body"
70
+ ],
71
+ upload_finalize: [
72
+ "POST",
73
+ "/files/finalize",
74
+ "body"
75
+ ],
76
+ download_url: [
77
+ "GET",
78
+ "/files/url",
79
+ "query"
80
+ ],
81
+ list: [
82
+ "GET",
83
+ "/files",
84
+ "query"
85
+ ],
86
+ delete: [
87
+ "DELETE",
88
+ "/files",
89
+ "query"
90
+ ]
91
+ };
92
+ const VALID_STORAGE_SCOPES = new Set([
93
+ "user",
94
+ "app",
95
+ "tool"
96
+ ]);
13
97
  var LlmBridge = class {
14
98
  mintedAuto = new Map();
15
99
  mintedAgent = new Map();
@@ -48,7 +132,8 @@ var LlmBridge = class {
48
132
  if (ns === "agent" && method.startsWith("session.")) return true;
49
133
  if (ns === "image" && (method === "generate" || method === "edit")) return true;
50
134
  if (ns === "upload" && (method === "inline" || method === "negotiate" || method === "confirm")) return true;
51
- if (ns === "storage" && this.opts.storageMode === "aps" && this.opts.mode === "real" && (method === "get" || method === "set" || method === "delete" || method === "list")) return true;
135
+ if (ns === "storage" && this.opts.storageMode === "aps" && this.opts.mode === "real" && STORAGE_APS_METHODS.has(method)) return true;
136
+ if (ns === "files" && this.opts.storageMode === "aps" && this.opts.mode === "real" && Object.prototype.hasOwnProperty.call(FILES_APP_ROUTES, method)) return true;
52
137
  return false;
53
138
  }
54
139
  /** Back-compat static alias — older code paths still call
@@ -503,6 +588,94 @@ var LlmBridge = class {
503
588
  result: await res.json()
504
589
  };
505
590
  }
591
+ const froute = FILES_ROUTES[args.method];
592
+ if (froute) {
593
+ const [httpMethod, subPath, payloadKind] = froute;
594
+ const scope = typeof a.scope === "string" && VALID_STORAGE_SCOPES.has(a.scope) ? a.scope : "user";
595
+ const url = new URL(`${base}${subPath}`);
596
+ url.searchParams.set("scope", scope);
597
+ let jsonBody;
598
+ if (payloadKind === "query") for (const [k, v] of Object.entries(a)) {
599
+ if (k === "scope" || v === void 0 || v === null) continue;
600
+ url.searchParams.set(k, typeof v === "string" ? v : JSON.stringify(v));
601
+ }
602
+ else {
603
+ const inner = {};
604
+ for (const [k, v] of Object.entries(a)) {
605
+ if (k === "scope" || v === void 0) continue;
606
+ inner[k] = v;
607
+ }
608
+ jsonBody = JSON.stringify(inner);
609
+ }
610
+ const headers = { authorization: `Bearer ${sm.storageToken}` };
611
+ if (jsonBody !== void 0) headers["content-type"] = "application/json";
612
+ const res = await fetch(url, {
613
+ method: httpMethod,
614
+ headers,
615
+ body: jsonBody
616
+ });
617
+ if (!res.ok) {
618
+ const text = await res.text().catch(() => "");
619
+ throw new Error(`storage.${args.method} HTTP ${res.status}: ${text}`);
620
+ }
621
+ const ct = res.headers.get("content-type") ?? "";
622
+ if (args.method === "files_delete" && (res.status === 204 || !ct.includes("json"))) return {
623
+ ok: true,
624
+ result: { deleted: true }
625
+ };
626
+ return {
627
+ ok: true,
628
+ result: await res.json()
629
+ };
630
+ }
631
+ }
632
+ if (args.ns === "files") {
633
+ const froute = FILES_APP_ROUTES[args.method];
634
+ if (froute) {
635
+ const sm = await this.mintStorage();
636
+ const a = { ...args.args };
637
+ if (args.method === "upload_init" && a.size !== void 0 && a.size_bytes === void 0) {
638
+ a.size_bytes = a.size;
639
+ delete a.size;
640
+ }
641
+ const base = `${canonicalHost(this.account().host)}/api/v1/storage`;
642
+ const [httpMethod, subPath, payloadKind] = froute;
643
+ const url = new URL(`${base}${subPath}`);
644
+ url.searchParams.set("scope", "app");
645
+ let jsonBody;
646
+ if (payloadKind === "query") for (const [k, v] of Object.entries(a)) {
647
+ if (k === "scope" || v === void 0 || v === null) continue;
648
+ url.searchParams.set(k, typeof v === "string" ? v : JSON.stringify(v));
649
+ }
650
+ else {
651
+ const inner = {};
652
+ for (const [k, v] of Object.entries(a)) {
653
+ if (k === "scope" || v === void 0) continue;
654
+ inner[k] = v;
655
+ }
656
+ jsonBody = JSON.stringify(inner);
657
+ }
658
+ const headers = { authorization: `Bearer ${sm.storageToken}` };
659
+ if (jsonBody !== void 0) headers["content-type"] = "application/json";
660
+ const res = await fetch(url, {
661
+ method: httpMethod,
662
+ headers,
663
+ body: jsonBody
664
+ });
665
+ if (!res.ok) {
666
+ const text = await res.text().catch(() => "");
667
+ throw new Error(`files.${args.method} HTTP ${res.status}: ${text}`);
668
+ }
669
+ const ct = res.headers.get("content-type") ?? "";
670
+ if (args.method === "delete" && (res.status === 204 || !ct.includes("json"))) return {
671
+ ok: true,
672
+ result: { deleted: true }
673
+ };
674
+ return {
675
+ ok: true,
676
+ result: await res.json()
677
+ };
678
+ }
506
679
  }
507
680
  return {
508
681
  ok: false,
@@ -728,6 +901,31 @@ var HarnessServer = class {
728
901
  "host.storage.list",
729
902
  "storage",
730
903
  "list"
904
+ ],
905
+ [
906
+ "host.storage.files_init",
907
+ "storage",
908
+ "files_init"
909
+ ],
910
+ [
911
+ "host.storage.files_finalize",
912
+ "storage",
913
+ "files_finalize"
914
+ ],
915
+ [
916
+ "host.storage.files_url",
917
+ "storage",
918
+ "files_url"
919
+ ],
920
+ [
921
+ "host.storage.files_list",
922
+ "storage",
923
+ "files_list"
924
+ ],
925
+ [
926
+ "host.storage.files_delete",
927
+ "storage",
928
+ "files_delete"
731
929
  ]
732
930
  ];
733
931
  for (const [hostMethod, ns, dispatchMethod] of HOST_OUTBOUND_ROUTES) this.bridge.onRequest(hostMethod, async (params) => {
@@ -1,5 +1,5 @@
1
1
  import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
- import { hostOf, requireAccount, withCode } from "./dev-account-DCyjamBa.js";
2
+ import { hostOf, requireAccount, withCode } from "./dev-account-qRaET1Cp.js";
3
3
  import { resolve } from "node:path";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
 
@@ -0,0 +1,87 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import "./client-Dn9zThOd.js";
3
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
4
+ import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
5
+
6
+ //#region src/api/tokens.ts
7
+ async function listTokens(client, includeRevoked = false) {
8
+ const r = await client.request({
9
+ path: "/api/v1/anna-apps/dev/tokens",
10
+ query: { include_revoked: includeRevoked ? "true" : void 0 }
11
+ });
12
+ return r.data?.tokens ?? [];
13
+ }
14
+ async function revokeToken(client, id) {
15
+ await client.request({
16
+ method: "POST",
17
+ path: `/api/v1/anna-apps/dev/tokens/${id}/revoke`,
18
+ allowStatuses: [204]
19
+ });
20
+ }
21
+ async function listScopes(client) {
22
+ const r = await client.request({ path: "/api/v1/anna-apps/dev/tokens/scopes" });
23
+ return r.data?.scopes ?? [];
24
+ }
25
+
26
+ //#endregion
27
+ //#region src/commands/token.ts
28
+ async function runTokenList(opts) {
29
+ return withErrorHandling(async () => {
30
+ const { client, host } = resolveClient({ account: opts.account });
31
+ const tokens = await listTokens(client, opts.includeRevoked ?? false);
32
+ if (opts.json) {
33
+ console.log(JSON.stringify({
34
+ host,
35
+ tokens
36
+ }, null, 2));
37
+ return 0;
38
+ }
39
+ console.log(bold(cyan(`PATs for ${host}`)));
40
+ if (tokens.length === 0) {
41
+ console.log(dim(` (no tokens — run \`anna-app login\` to mint one)`));
42
+ return 0;
43
+ }
44
+ for (const t of tokens) {
45
+ const scopes = Array.isArray(t.scopes) ? t.scopes.join(",") : t.scopes ?? "";
46
+ const tag = t.revoked_at ? red("[revoked]") : green("[active]");
47
+ const label = t.label ? ` "${t.label}"` : "";
48
+ console.log(` ${tag} #${t.id}${label} ${dim(`(scopes=[${scopes}], jti=${t.jti})`)}`);
49
+ }
50
+ return 0;
51
+ });
52
+ }
53
+ async function runTokenRevoke(opts) {
54
+ return withErrorHandling(async () => {
55
+ const { client, host } = resolveClient({ account: opts.account });
56
+ await revokeToken(client, opts.id);
57
+ if (opts.json) {
58
+ console.log(JSON.stringify({
59
+ host,
60
+ token_id: opts.id,
61
+ revoked: true
62
+ }, null, 2));
63
+ return 0;
64
+ }
65
+ console.log(yellow(`✓ token #${opts.id} revoked on ${host}`));
66
+ return 0;
67
+ });
68
+ }
69
+ async function runTokenScopes(opts) {
70
+ return withErrorHandling(async () => {
71
+ const { client, host } = resolveClient({ account: opts.account });
72
+ const scopes = await listScopes(client);
73
+ if (opts.json) {
74
+ console.log(JSON.stringify({
75
+ host,
76
+ scopes
77
+ }, null, 2));
78
+ return 0;
79
+ }
80
+ console.log(bold(cyan(`available PAT scopes`)) + " " + dim(`(${host})`));
81
+ for (const s of scopes) console.log(` ${green(s.name.padEnd(18))} ${dim(s.description)}`);
82
+ return 0;
83
+ });
84
+ }
85
+
86
+ //#endregion
87
+ export { runTokenList, runTokenRevoke, runTokenScopes };
@@ -0,0 +1,190 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { createApp, findAppBySlug, getApp } from "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import { parseExecutaIdOverrides, readExecutasLock, substituteBundledRefs, validateBundledHandles, writeBundleToolIdSidecar, writeExecutasLock } from "./bundled-executas-BNOKw4kv.js";
5
+ import { runExecutaPublish } from "./executa-publish-Ca5V7MyA.js";
6
+ import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-BEM653Th.js";
7
+ import { resolve } from "node:path";
8
+ import { dim, green, yellow } from "kleur/colors";
9
+
10
+ //#region src/publish/working-orchestration.ts
11
+ async function resolveBundledExecutas(params) {
12
+ const { manifest, cwd, host, dryRun = false, freeze, json = false } = params;
13
+ const overrides = parseExecutaIdOverrides(params.executaId);
14
+ const active = !params.noBundledExecutas && (manifest.bundledExecutas.length > 0 || overrides.length > 0);
15
+ if (!active) return {
16
+ publishManifestRaw: manifest.raw,
17
+ resolved: {}
18
+ };
19
+ const { referenced } = validateBundledHandles(manifest.raw, manifest.bundledExecutas, overrides);
20
+ const overrideMap = new Map(overrides.map((o) => [o.handle, o.toolId]));
21
+ const lock = readExecutasLock(cwd, host);
22
+ const resolved = {};
23
+ const lockEntries = {};
24
+ for (const decl of manifest.bundledExecutas) {
25
+ if (overrideMap.has(decl.handle)) {
26
+ const id = overrideMap.get(decl.handle);
27
+ resolved[decl.handle] = id;
28
+ lockEntries[decl.handle] = {
29
+ tool_id: id,
30
+ path: decl.path,
31
+ source: "override"
32
+ };
33
+ if (!json) console.log(dim(` bundled:${decl.handle} → ${id} (override)`));
34
+ continue;
35
+ }
36
+ if (params.skipExecutaPublish) {
37
+ const cachedId = lock?.executas[decl.handle]?.tool_id;
38
+ if (!cachedId) throw new CliError(`--skip-executa-publish set but no cached tool_id for "${decl.handle}" in .anna/executas.lock.json (run without --skip-executa-publish first, or pass --executa-id ${decl.handle}=<tool-id>)`, 4);
39
+ resolved[decl.handle] = cachedId;
40
+ lockEntries[decl.handle] = {
41
+ tool_id: cachedId,
42
+ path: decl.path,
43
+ source: "cache"
44
+ };
45
+ if (!json) console.log(dim(` bundled:${decl.handle} → ${cachedId} (cache)`));
46
+ continue;
47
+ }
48
+ const execCwd = resolve(cwd, decl.path);
49
+ if (dryRun) {
50
+ if (!json) console.log(yellow(` [dry-run] would publish bundled executa "${decl.handle}" from ${decl.path}`));
51
+ continue;
52
+ }
53
+ if (!json) console.log(dim(` ${freeze ? "publishing" : "registering"} bundled executa "${decl.handle}" (${decl.path})${freeze ? "" : " [no-freeze]"}…`));
54
+ let captured;
55
+ const code = await runExecutaPublish({
56
+ cwd: execCwd,
57
+ account: params.account,
58
+ quiet: true,
59
+ freeze,
60
+ capture: (r) => {
61
+ captured = r;
62
+ }
63
+ });
64
+ if (code !== 0 || !captured) throw new CliError(`failed to ${freeze ? "publish" : "register"} bundled executa "${decl.handle}" (${decl.path})`, code === 0 ? 4 : code);
65
+ resolved[decl.handle] = captured.tool_id;
66
+ lockEntries[decl.handle] = {
67
+ tool_id: captured.tool_id,
68
+ path: decl.path,
69
+ source: params.lockSource ?? (freeze ? "published" : "working")
70
+ };
71
+ if (!json) console.log(green(` ✓ bundled:${decl.handle} → ${captured.tool_id}`) + dim(` (v${captured.version}${captured.idempotent_hit ? ", unchanged" : ""})`));
72
+ }
73
+ for (const o of overrides) if (!(o.handle in resolved)) {
74
+ resolved[o.handle] = o.toolId;
75
+ lockEntries[o.handle] = {
76
+ tool_id: o.toolId,
77
+ source: "override"
78
+ };
79
+ if (!json) console.log(dim(` bundled:${o.handle} → ${o.toolId} (override)`));
80
+ }
81
+ const missing = [...referenced].filter((h) => !(h in resolved));
82
+ if (missing.length > 0 && !dryRun) throw new CliError(`could not resolve bundled handle(s): ${missing.sort().join(", ")}`, 4);
83
+ let publishManifestRaw = manifest.raw;
84
+ if (!dryRun) {
85
+ const merged = {
86
+ $schema: "anna-executas-lock/v1",
87
+ host: canonicalHost(host),
88
+ executas: {
89
+ ...lock?.executas ?? {},
90
+ ...lockEntries
91
+ }
92
+ };
93
+ writeExecutasLock(cwd, merged);
94
+ publishManifestRaw = substituteBundledRefs(manifest.raw, resolved);
95
+ const bundleEntry = manifest.raw.ui?.bundle?.entry;
96
+ if (bundleEntry && Object.keys(resolved).length > 0) {
97
+ const sidecar = writeBundleToolIdSidecar(resolve(cwd, "bundle"), resolved);
98
+ if (!json) console.log(dim(` wrote ${sidecar.replace(cwd + "/", "")}`));
99
+ }
100
+ }
101
+ return {
102
+ publishManifestRaw,
103
+ resolved
104
+ };
105
+ }
106
+ /**
107
+ * Resolve (creating if absent) the `AnnaApp` row, persisting the identity
108
+ * cache before any subsequent network call. Returns `null` only in dry-run
109
+ * mode when the app does not exist yet (nothing to create).
110
+ */
111
+ async function resolveAppIdentity(params) {
112
+ const { client, cwd, host, manifest, dryRun = false, json = false } = params;
113
+ let cached = readAppIdentity(cwd);
114
+ if (cached && cached.host !== canonicalHost(host)) cached = null;
115
+ if (cached && appCacheMatches(cached, host, manifest.slug)) try {
116
+ const live = await getApp(client, cached.app_id);
117
+ return {
118
+ appId: live.id,
119
+ serverSlug: live.slug,
120
+ firstPublish: false,
121
+ fromCache: true
122
+ };
123
+ } catch (e) {
124
+ if (!(e instanceof CliError && e.status === 404)) throw e;
125
+ if (!json) console.log(yellow(`↻ cached app_id=${cached.app_id} (slug=${cached.slug}) no longer exists on ${host}; re-resolving by slug…`));
126
+ }
127
+ const found = await findAppBySlug(client, manifest.slug);
128
+ let appId;
129
+ let serverSlug;
130
+ let firstPublish = false;
131
+ if (found) {
132
+ appId = found.id;
133
+ serverSlug = found.slug;
134
+ } else {
135
+ if (dryRun) {
136
+ if (!json) console.log(yellow(`[dry-run] would create AnnaApp slug=${manifest.slug}`));
137
+ return null;
138
+ }
139
+ const created = await createApp(client, {
140
+ slug: manifest.slug,
141
+ name: manifest.name,
142
+ category: manifest.category,
143
+ tagline: manifest.tagline,
144
+ description: manifest.description
145
+ });
146
+ appId = created.id;
147
+ serverSlug = created.slug;
148
+ firstPublish = true;
149
+ }
150
+ writeAppIdentity(cwd, {
151
+ $schema: "anna-app-identity/v1",
152
+ host: canonicalHost(host),
153
+ app_id: appId,
154
+ slug: serverSlug,
155
+ first_published_at: cached?.first_published_at ?? new Date().toISOString()
156
+ });
157
+ return {
158
+ appId,
159
+ serverSlug,
160
+ firstPublish,
161
+ fromCache: false
162
+ };
163
+ }
164
+ /**
165
+ * Resolve an EXISTING app via an explicit `slug` or the local
166
+ * `.anna/app.json` identity cache (no create). Used by verbs that operate on
167
+ * an already-pushed app (`cut`, `discard`). Throws `CliError(…, 5)` when the
168
+ * app cannot be resolved.
169
+ */
170
+ async function resolveAppBySlugOrCache(client, host, opts) {
171
+ if (opts.slug) {
172
+ const app = await findAppBySlug(client, opts.slug);
173
+ if (!app) throw new CliError(`no app with slug "${opts.slug}" on ${host}`, 5);
174
+ return getApp(client, app.id);
175
+ }
176
+ const cwd = resolve(opts.cwd ?? process.cwd());
177
+ const cached = readAppIdentity(cwd);
178
+ if (cached && cached.host === canonicalHost(host)) try {
179
+ return await getApp(client, cached.app_id);
180
+ } catch (e) {
181
+ if (e instanceof CliError && e.status === 404) {
182
+ const app = await findAppBySlug(client, cached.slug);
183
+ if (app) return getApp(client, app.id);
184
+ } else throw e;
185
+ }
186
+ throw new CliError("cannot resolve app — pass --slug <slug> or run from a project dir with a .anna/app.json cache (run `anna-app apps push` first)", 5);
187
+ }
188
+
189
+ //#endregion
190
+ export { resolveAppBySlugOrCache, resolveAppIdentity, resolveBundledExecutas };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anna-ai/cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,8 +32,8 @@
32
32
  "prepublishOnly": "pnpm lint && pnpm test && pnpm build"
33
33
  },
34
34
  "dependencies": {
35
- "@anna-ai/app-runtime": "^0.2.0",
36
- "@anna-ai/app-schema": "^0.4.0",
35
+ "@anna-ai/app-runtime": "^0.5.0",
36
+ "@anna-ai/app-schema": "^0.8.0",
37
37
  "ajv": "^8.17.1",
38
38
  "ajv-formats": "^3.0.1",
39
39
  "commander": "^12.1.0",
@@ -1,4 +1,9 @@
1
1
  {
2
+ "slug": "__SLUG__",
3
+ "name": "__SLUG__",
4
+ "version": "0.1.0",
5
+ "executa_type": "tool",
6
+ "description": "",
2
7
  "tool_id": "__TOOL_ID__",
3
8
  "type": "go"
4
9
  }
@@ -1,4 +1,9 @@
1
1
  {
2
+ "slug": "__SLUG__",
3
+ "name": "__SLUG__",
4
+ "version": "0.1.0",
5
+ "executa_type": "tool",
6
+ "description": "",
2
7
  "tool_id": "__TOOL_ID__",
3
8
  "type": "node"
4
9
  }
@@ -1,4 +1,9 @@
1
1
  {
2
+ "slug": "__SLUG__",
3
+ "name": "__SLUG__",
4
+ "version": "0.1.0",
5
+ "executa_type": "tool",
6
+ "description": "",
2
7
  "tool_id": "__TOOL_ID__",
3
8
  "type": "python"
4
9
  }
@@ -1,44 +0,0 @@
1
- import { getAccount } from "./credentials-BTv2IfUZ.js";
2
- import { listDevApps } from "./dev-app-cache-3Pfesngr.js";
3
- import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
4
-
5
- //#region src/commands/apps.ts
6
- async function runAppsList(opts) {
7
- const acc = getAccount(opts.account);
8
- if (!acc) {
9
- console.error(red("✗ no PAT on disk — run `anna-app login --host <nexus-url>` first."));
10
- return 2;
11
- }
12
- let apps;
13
- try {
14
- apps = await listDevApps({
15
- host: acc.host,
16
- pat: acc.pat
17
- });
18
- } catch (e) {
19
- console.error(red(`✗ ${e.message}`));
20
- return 2;
21
- }
22
- if (opts.json) {
23
- console.log(JSON.stringify({
24
- host: acc.host,
25
- apps
26
- }, null, 2));
27
- return 0;
28
- }
29
- console.log(bold(cyan("dev apps installed for")) + " " + cyan(acc.host));
30
- if (apps.length === 0) {
31
- console.log(dim(" (none — run `anna-app dev` in a project to register one)"));
32
- return 0;
33
- }
34
- for (const a of apps) {
35
- const tag = a.is_dev ? yellow("[dev]") : green("[prod]");
36
- const enabled = a.is_enabled ? green("✓") : red("✗");
37
- console.log(` ${tag} ${enabled} ${bold(a.slug)} ${dim(`(app_id=${a.app_id}, v=${a.installed_version})`)}`);
38
- if (a.name && a.name !== a.slug) console.log(` ${dim(a.name)}`);
39
- }
40
- return 0;
41
- }
42
-
43
- //#endregion
44
- export { runAppsList };
@@ -1,3 +0,0 @@
1
- import { BridgeRequestError, PINNED_RUNTIME_VERSION, PythonBridge } from "./bridge-mkb_EM-y.js";
2
-
3
- export { PINNED_RUNTIME_VERSION, PythonBridge };
@@ -1,3 +0,0 @@
1
- import { parseExecutaSpec, runDev } from "./dev-b1j-dEM2.js";
2
-
3
- export { parseExecutaSpec, runDev };
@@ -1,4 +0,0 @@
1
- import "./credentials-BTv2IfUZ.js";
2
- import { ensureDevAppRegistered, ensureDevExecutaRegistered, invalidateDevAppCache, listDevApps, readDevAppCache, readDevExecutaCache, registerDevApp, registerDevExecuta, writeDevAppCache, writeDevExecutaCache } from "./dev-app-cache-3Pfesngr.js";
3
-
4
- export { ensureDevAppRegistered, ensureDevExecutaRegistered, invalidateDevAppCache };