@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,135 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { findAppBySlug, getApp, getBundle, listVersions, publishVersion } from "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
5
+ import { readAppIdentity } from "./app-cache-BEM653Th.js";
6
+ import { resolve } from "node:path";
7
+ import { bold, cyan, dim, green, yellow } from "kleur/colors";
8
+
9
+ //#region src/commands/apps-release.ts
10
+ /** Resolve the target app via `--slug` or the local `.anna/app.json` cache. */
11
+ async function resolveApp(client, host, opts) {
12
+ if (opts.slug) {
13
+ const app = await findAppBySlug(client, opts.slug);
14
+ if (!app) throw new CliError(`no app with slug "${opts.slug}" on ${host}`, 5);
15
+ return getApp(client, app.id);
16
+ }
17
+ const cwd = resolve(opts.cwd ?? process.cwd());
18
+ const cached = readAppIdentity(cwd);
19
+ if (cached && cached.host === canonicalHost(host)) try {
20
+ return await getApp(client, cached.app_id);
21
+ } catch (e) {
22
+ if (e instanceof CliError && e.status === 404) {
23
+ const app = await findAppBySlug(client, cached.slug);
24
+ if (app) return getApp(client, app.id);
25
+ } else throw e;
26
+ }
27
+ 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 publish' first)", 5);
28
+ }
29
+ /** Does this version declare a UI bundle in its frozen manifest? */
30
+ function declaresUiBundle(version) {
31
+ const entry = version.manifest?.ui?.bundle?.entry;
32
+ return typeof entry === "string" && entry.length > 0;
33
+ }
34
+ function statusGuard(app) {
35
+ if (app.status === "APPROVED" || app.status === "PUBLISHED") return;
36
+ let hint;
37
+ switch (app.status) {
38
+ case "DRAFT":
39
+ case "REJECTED":
40
+ hint = "submit for review first: `anna-app apps submit-review`";
41
+ break;
42
+ case "PENDING_REVIEW":
43
+ hint = "awaiting admin approval — cannot release until APPROVED";
44
+ break;
45
+ case "ARCHIVED":
46
+ hint = "app is archived; restore it first: `anna-app apps unarchive`";
47
+ break;
48
+ default: hint = "app must be APPROVED or PUBLISHED to release";
49
+ }
50
+ throw new CliError(`app status is ${app.status}; release not permitted — ${hint}`, 6);
51
+ }
52
+ async function runAppsRelease(opts) {
53
+ return withErrorHandling(async () => {
54
+ const { client, host } = resolveClient({ account: opts.account });
55
+ let app = await resolveApp(client, host, opts);
56
+ let versions = await listVersions(client, app.id);
57
+ let target = versions.find((v) => v.version === opts.version);
58
+ if (!target) {
59
+ if (opts.allowCreate) {
60
+ if (!opts.json) console.log(yellow(`version ${opts.version} not found remotely; running 'apps publish' first (--allow-create)…`));
61
+ const { runAppsPublish } = await import("./apps-publish-DfZTOxBJ.js");
62
+ const code = await runAppsPublish({
63
+ cwd: opts.cwd,
64
+ account: opts.account,
65
+ json: opts.json
66
+ });
67
+ if (code !== 0) return code;
68
+ app = await getApp(client, app.id);
69
+ versions = await listVersions(client, app.id);
70
+ target = versions.find((v) => v.version === opts.version);
71
+ }
72
+ if (!target) throw new CliError(`no version ${opts.version} for app "${app.slug}" on ${host} — run 'anna-app apps publish' first`, 5);
73
+ }
74
+ if (declaresUiBundle(target)) {
75
+ const bundle = await getBundle(client, app.id, target.id);
76
+ if (!bundle || bundle.status !== "bundle_ready") throw new CliError(`version ${opts.version} declares a UI bundle but it is not finalized (status=${bundle?.status ?? "missing"}) — re-run 'anna-app apps publish'`, 4);
77
+ }
78
+ statusGuard(app);
79
+ if (target.is_latest && app.status === "PUBLISHED") {
80
+ if (opts.json) {
81
+ console.log(JSON.stringify({
82
+ host,
83
+ app_id: app.id,
84
+ slug: app.slug,
85
+ version: target.version,
86
+ version_id: target.id,
87
+ is_latest: true,
88
+ published_at: target.published_at ?? null,
89
+ status: app.status,
90
+ already_live: true
91
+ }, null, 2));
92
+ return 0;
93
+ }
94
+ console.log(green(`✓ apps/${app.slug}: version ${target.version} already live`) + dim(` (no-op)`));
95
+ return 0;
96
+ }
97
+ if (opts.dryRun) {
98
+ if (opts.json) {
99
+ console.log(JSON.stringify({
100
+ host,
101
+ app_id: app.id,
102
+ slug: app.slug,
103
+ version: target.version,
104
+ version_id: target.id,
105
+ current_status: app.status,
106
+ dry_run: true
107
+ }, null, 2));
108
+ return 0;
109
+ }
110
+ console.log(yellow(`[dry-run] would release apps/${app.slug} version ${target.version} (id=${target.id}, current status=${app.status})`));
111
+ return 0;
112
+ }
113
+ const released = await publishVersion(client, app.id, target.id);
114
+ const finalApp = await getApp(client, app.id);
115
+ if (opts.json) {
116
+ console.log(JSON.stringify({
117
+ host,
118
+ app_id: app.id,
119
+ slug: app.slug,
120
+ version: released.version,
121
+ version_id: released.id,
122
+ is_latest: released.is_latest ?? true,
123
+ published_at: released.published_at ?? null,
124
+ status: finalApp.status
125
+ }, null, 2));
126
+ return 0;
127
+ }
128
+ console.log(green(`✓ apps/${app.slug}: released version ${released.version}`) + dim(` (id=${released.id})`));
129
+ console.log(bold(cyan(`status: ${finalApp.status}`)) + " — now live in the store");
130
+ return 0;
131
+ });
132
+ }
133
+
134
+ //#endregion
135
+ export { runAppsRelease };
@@ -0,0 +1,58 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import { findAppBySlug, getApp, listVersions } from "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
5
+ import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
6
+
7
+ //#region src/commands/apps-status.ts
8
+ async function runAppsStatus(opts) {
9
+ return withErrorHandling(async () => {
10
+ const { client, host } = resolveClient({ account: opts.account });
11
+ const app = await findAppBySlug(client, opts.slug);
12
+ if (!app) throw new CliError(`no app with slug "${opts.slug}" on ${host}`, 5);
13
+ const full = await getApp(client, app.id);
14
+ const versions = await listVersions(client, app.id);
15
+ const latest = versions.find((v) => v.is_latest) ?? versions[0] ?? null;
16
+ if (opts.json) {
17
+ console.log(JSON.stringify({
18
+ host,
19
+ app_id: full.id,
20
+ slug: full.slug,
21
+ name: full.name,
22
+ status: full.status,
23
+ visibility: full.visibility ?? null,
24
+ is_published: full.is_published ?? false,
25
+ review_message: full.review_message ?? null,
26
+ latest_version: latest ? {
27
+ id: latest.id,
28
+ version: latest.version,
29
+ published_at: latest.published_at,
30
+ is_latest: latest.is_latest ?? null
31
+ } : null,
32
+ version_count: versions.length
33
+ }, null, 2));
34
+ return 0;
35
+ }
36
+ console.log(bold(cyan(`apps/${full.slug}`)) + " " + dim(`(${host})`));
37
+ console.log(` ${dim("name :")} ${full.name}`);
38
+ console.log(` ${dim("app_id :")} ${full.id}`);
39
+ console.log(` ${dim("status :")} ${colorStatus(full.status)}` + (full.is_published ? " " + green("[published]") : ""));
40
+ if (latest) console.log(` ${dim("latest :")} v${latest.version} (id=${latest.id}, published=${latest.published_at ?? "—"})`);
41
+ else console.log(` ${dim("latest :")} ${dim("(no versions yet)")}`);
42
+ if (full.review_message) console.log(` ${dim("reviewer :")} ${full.review_message}`);
43
+ console.log(` ${dim("versions :")} ${versions.length} total`);
44
+ return 0;
45
+ });
46
+ }
47
+ function colorStatus(s) {
48
+ switch (s) {
49
+ case "PUBLISHED": return green(s);
50
+ case "PENDING_REVIEW": return yellow(s);
51
+ case "REJECTED": return red(s);
52
+ case "ARCHIVED": return dim(s);
53
+ default: return s;
54
+ }
55
+ }
56
+
57
+ //#endregion
58
+ export { runAppsStatus };
@@ -0,0 +1,45 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { findAppBySlug, getApp, submitForReview } from "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
5
+ import { readAppIdentity } from "./app-cache-BEM653Th.js";
6
+ import { resolve } from "node:path";
7
+ import { bold, cyan, green } from "kleur/colors";
8
+
9
+ //#region src/commands/apps-submit-review.ts
10
+ async function runAppsSubmitReview(opts) {
11
+ return withErrorHandling(async () => {
12
+ const { client, host } = resolveClient({ account: opts.account });
13
+ let appId;
14
+ let serverSlug;
15
+ if (opts.slug) {
16
+ const found = await findAppBySlug(client, opts.slug);
17
+ if (!found) throw new CliError(`no app with slug "${opts.slug}" on ${host}`, 5);
18
+ appId = found.id;
19
+ serverSlug = found.slug;
20
+ } else {
21
+ const cwd = resolve(opts.cwd ?? process.cwd());
22
+ const cached = readAppIdentity(cwd);
23
+ if (!cached || cached.host !== canonicalHost(host)) throw new CliError("cannot resolve app — pass a <slug> or run from a project dir with a .anna/app.json cache", 5);
24
+ appId = cached.app_id;
25
+ serverSlug = cached.slug;
26
+ }
27
+ const app = await submitForReview(client, appId);
28
+ const finalApp = await getApp(client, appId);
29
+ if (opts.json) {
30
+ console.log(JSON.stringify({
31
+ host,
32
+ app_id: appId,
33
+ slug: serverSlug,
34
+ status: finalApp.status
35
+ }, null, 2));
36
+ return 0;
37
+ }
38
+ console.log(green(`✓ apps/${serverSlug}: submitted for review`));
39
+ console.log(bold(cyan(`status: ${app.status ?? finalApp.status}`)));
40
+ return 0;
41
+ });
42
+ }
43
+
44
+ //#endregion
45
+ export { runAppsSubmitReview };
@@ -0,0 +1,72 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { findAppBySlug, getApp, patchApp } from "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import { loadAppManifest } from "./manifest-DGwRap2i.js";
5
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
6
+ import { readAppIdentity } from "./app-cache-BEM653Th.js";
7
+ import { resolve } from "node:path";
8
+ import { bold, cyan, dim, green } from "kleur/colors";
9
+
10
+ //#region src/commands/apps-sync-meta.ts
11
+ async function runAppsSyncMeta(opts) {
12
+ return withErrorHandling(async () => {
13
+ const cwd = resolve(opts.cwd ?? process.cwd());
14
+ const manifest = loadAppManifest(cwd, opts.manifest);
15
+ const { client, host } = resolveClient({ account: opts.account });
16
+ let appId = null;
17
+ let serverSlug = manifest.slug;
18
+ const cached = readAppIdentity(cwd);
19
+ if (cached && cached.host === canonicalHost(host) && cached.slug === manifest.slug) {
20
+ appId = cached.app_id;
21
+ serverSlug = cached.slug;
22
+ } else {
23
+ const found = await findAppBySlug(client, manifest.slug);
24
+ if (found) {
25
+ appId = found.id;
26
+ serverSlug = found.slug;
27
+ }
28
+ }
29
+ if (appId === null) throw new CliError(`no app with slug "${manifest.slug}" on ${host} — run 'anna-app apps publish' first`, 5);
30
+ const meta = {
31
+ name: manifest.name,
32
+ category: manifest.category,
33
+ tagline: manifest.tagline,
34
+ description: manifest.description
35
+ };
36
+ if (opts.dryRun) {
37
+ if (opts.json) {
38
+ console.log(JSON.stringify({
39
+ host,
40
+ app_id: appId,
41
+ slug: serverSlug,
42
+ meta,
43
+ dry_run: true
44
+ }, null, 2));
45
+ return 0;
46
+ }
47
+ console.log(`[dry-run] would PATCH apps/${serverSlug} metadata: ` + JSON.stringify(meta));
48
+ return 0;
49
+ }
50
+ await patchApp(client, appId, meta);
51
+ const finalApp = await getApp(client, appId);
52
+ if (opts.json) {
53
+ console.log(JSON.stringify({
54
+ host,
55
+ app_id: appId,
56
+ slug: serverSlug,
57
+ meta,
58
+ status: finalApp.status
59
+ }, null, 2));
60
+ return 0;
61
+ }
62
+ console.log(green(`✓ apps/${serverSlug}: metadata synced`));
63
+ console.log(` ${dim("name :")} ${manifest.name}`);
64
+ if (manifest.category) console.log(` ${dim("category :")} ${manifest.category}`);
65
+ if (manifest.tagline) console.log(` ${dim("tagline :")} ${manifest.tagline}`);
66
+ console.log(bold(cyan(`status: ${finalApp.status}`)));
67
+ return 0;
68
+ });
69
+ }
70
+
71
+ //#endregion
72
+ export { runAppsSyncMeta };
@@ -0,0 +1,43 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import { findAppBySlug, listVersions } from "./apps-B1Nd8l_t.js";
3
+ import { CliError } from "./client-Dn9zThOd.js";
4
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
5
+ import { bold, cyan, dim, green } from "kleur/colors";
6
+
7
+ //#region src/commands/apps-versions.ts
8
+ async function runAppsVersions(opts) {
9
+ return withErrorHandling(async () => {
10
+ const { client, host } = resolveClient({ account: opts.account });
11
+ const app = await findAppBySlug(client, opts.slug);
12
+ if (!app) throw new CliError(`no app with slug "${opts.slug}" on ${host}`, 5);
13
+ const versions = await listVersions(client, app.id);
14
+ if (opts.json) {
15
+ console.log(JSON.stringify({
16
+ host,
17
+ app_id: app.id,
18
+ slug: app.slug,
19
+ versions: versions.map((v) => ({
20
+ id: v.id,
21
+ version: v.version,
22
+ is_latest: v.is_latest ?? false,
23
+ published_at: v.published_at ?? null,
24
+ created_at: v.created_at ?? null
25
+ }))
26
+ }, null, 2));
27
+ return 0;
28
+ }
29
+ console.log(bold(cyan(`apps/${app.slug}`)) + " " + dim(`versions (${versions.length})`));
30
+ if (versions.length === 0) {
31
+ console.log(dim(" (none)"));
32
+ return 0;
33
+ }
34
+ for (const v of versions) {
35
+ const tag = v.is_latest ? green("[latest]") : dim(" ");
36
+ console.log(` ${tag} v${v.version} ${dim(`(id=${v.id}, published=${v.published_at ?? "—"})`)}`);
37
+ }
38
+ return 0;
39
+ });
40
+ }
41
+
42
+ //#endregion
43
+ export { runAppsVersions };
@@ -0,0 +1,3 @@
1
+ import { BridgeRequestError, PINNED_RUNTIME_VERSION, PythonBridge } from "./bridge-BuklhzeE.js";
2
+
3
+ export { PINNED_RUNTIME_VERSION, PythonBridge };
@@ -0,0 +1,161 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { CliError } from "./client-Dn9zThOd.js";
3
+ import { dirname, resolve } from "node:path";
4
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
5
+
6
+ //#region src/publish/bundled-executas.ts
7
+ const BUNDLED_PREFIX = "bundled:";
8
+ const LOCK_DIR = ".anna";
9
+ const LOCK_FILE = "executas.lock.json";
10
+ /** Parse repeated `--executa-id handle=tool-id` flags. */
11
+ function parseExecutaIdOverrides(raw) {
12
+ if (!raw || raw.length === 0) return [];
13
+ return raw.map((entry) => {
14
+ const eq = entry.indexOf("=");
15
+ if (eq <= 0) throw new CliError(`--executa-id must be in the form handle=tool-id (got "${entry}")`, 2);
16
+ const handle = entry.slice(0, eq).trim();
17
+ const toolId = entry.slice(eq + 1).trim();
18
+ if (!handle || !toolId) throw new CliError(`--executa-id must be in the form handle=tool-id (got "${entry}")`, 2);
19
+ return {
20
+ handle,
21
+ toolId
22
+ };
23
+ });
24
+ }
25
+ function lockPath(cwd) {
26
+ return resolve(cwd, LOCK_DIR, LOCK_FILE);
27
+ }
28
+ function readExecutasLock(cwd, host) {
29
+ const p = lockPath(cwd);
30
+ if (!existsSync(p)) return null;
31
+ try {
32
+ const raw = JSON.parse(readFileSync(p, "utf-8"));
33
+ if (raw.executas && typeof raw.executas === "object" && typeof raw.host === "string" && canonicalHost(raw.host) === canonicalHost(host)) return {
34
+ $schema: "anna-executas-lock/v1",
35
+ host: canonicalHost(raw.host),
36
+ executas: raw.executas
37
+ };
38
+ } catch {}
39
+ return null;
40
+ }
41
+ function writeExecutasLock(cwd, lock) {
42
+ const p = lockPath(cwd);
43
+ mkdirSync(dirname(p), { recursive: true });
44
+ const tmp = `${p}.tmp.${process.pid}`;
45
+ writeFileSync(tmp, JSON.stringify({
46
+ ...lock,
47
+ host: canonicalHost(lock.host)
48
+ }, null, 2) + "\n", "utf-8");
49
+ try {
50
+ renameSync(tmp, p);
51
+ } catch (e) {
52
+ try {
53
+ unlinkSync(tmp);
54
+ } catch {}
55
+ throw e;
56
+ }
57
+ }
58
+ /**
59
+ * Walk a JSON value and collect every `bundled:<handle>` token found in
60
+ * a string. Tokens can be bare (`bundled:foo`) or prefixed
61
+ * (`required:bundled:foo`, `optional:bundled:foo`).
62
+ */
63
+ function collectBundledHandles(value, out) {
64
+ if (typeof value === "string") {
65
+ const h = extractHandle(value);
66
+ if (h) out.add(h);
67
+ return;
68
+ }
69
+ if (Array.isArray(value)) {
70
+ for (const v of value) collectBundledHandles(v, out);
71
+ return;
72
+ }
73
+ if (value && typeof value === "object") for (const v of Object.values(value)) collectBundledHandles(v, out);
74
+ }
75
+ function extractHandle(s) {
76
+ let body = s;
77
+ if (body.startsWith("required:")) body = body.slice(9);
78
+ else if (body.startsWith("optional:")) body = body.slice(9);
79
+ if (body.startsWith(BUNDLED_PREFIX)) return body.slice(BUNDLED_PREFIX.length);
80
+ return null;
81
+ }
82
+ /**
83
+ * Return a deep copy of `value` with every `bundled:<handle>` token
84
+ * replaced by the resolved tool_id (preserving any required:/optional:
85
+ * prefix).
86
+ */
87
+ function substituteBundledRefs(value, resolved) {
88
+ if (typeof value === "string") return substituteString(value, resolved);
89
+ if (Array.isArray(value)) return value.map((v) => substituteBundledRefs(v, resolved));
90
+ if (value && typeof value === "object") {
91
+ const out = {};
92
+ for (const [k, v] of Object.entries(value)) out[k] = substituteBundledRefs(v, resolved);
93
+ return out;
94
+ }
95
+ return value;
96
+ }
97
+ function substituteString(s, resolved) {
98
+ let prefix = "";
99
+ let body = s;
100
+ if (body.startsWith("required:")) {
101
+ prefix = "required:";
102
+ body = body.slice(prefix.length);
103
+ } else if (body.startsWith("optional:")) {
104
+ prefix = "optional:";
105
+ body = body.slice(prefix.length);
106
+ }
107
+ if (body.startsWith(BUNDLED_PREFIX)) {
108
+ const handle = body.slice(BUNDLED_PREFIX.length);
109
+ const id = resolved[handle];
110
+ if (!id) throw new CliError(`unresolved bundled handle "${handle}"`, 4);
111
+ return prefix + id;
112
+ }
113
+ return s;
114
+ }
115
+ /**
116
+ * Write the Option-A sidecar `<bundleDir>/anna-tool-ids.js`.
117
+ *
118
+ * The bundle can read it via:
119
+ *
120
+ * <script src="anna-tool-ids.js"></script>
121
+ * const TOOL_ID = window.__ANNA_TOOL_IDS__["focus-session"];
122
+ */
123
+ function writeBundleToolIdSidecar(bundleDir, resolved) {
124
+ const file = resolve(bundleDir, "anna-tool-ids.js");
125
+ const body = "/* AUTO-GENERATED by `anna-app apps publish`. Do not edit. */\n/* Maps local bundled_executas handles → server-minted tool_ids. */\nwindow.__ANNA_TOOL_IDS__ = " + JSON.stringify(resolved, null, 2) + ";\n";
126
+ mkdirSync(bundleDir, { recursive: true });
127
+ const tmp = `${file}.tmp.${process.pid}`;
128
+ writeFileSync(tmp, body, "utf-8");
129
+ try {
130
+ renameSync(tmp, file);
131
+ } catch (e) {
132
+ try {
133
+ unlinkSync(tmp);
134
+ } catch {}
135
+ throw e;
136
+ }
137
+ return file;
138
+ }
139
+ /**
140
+ * Validate that the declared `bundled_executas` map, the `bundled:`
141
+ * tokens used in the manifest, and any `--executa-id` overrides are
142
+ * mutually consistent.
143
+ *
144
+ * - Every `bundled:<handle>` token referenced in the manifest must have
145
+ * a matching entry in `bundled_executas` OR a `--executa-id` override.
146
+ * - Every override handle must be referenced or declared (typo guard).
147
+ */
148
+ function validateBundledHandles(manifestRaw, declared, overrides) {
149
+ const referenced = new Set();
150
+ collectBundledHandles(manifestRaw, referenced);
151
+ const declaredHandles = new Set(declared.map((d) => d.handle));
152
+ const overrideHandles = new Set(overrides.map((o) => o.handle));
153
+ const unresolvable = [];
154
+ for (const h of referenced) if (!declaredHandles.has(h) && !overrideHandles.has(h)) unresolvable.push(h);
155
+ if (unresolvable.length > 0) throw new CliError(`manifest references bundled:${unresolvable.sort().join(", bundled:")} but no matching app.json bundled_executas entry or --executa-id override was found`, 4);
156
+ for (const h of overrideHandles) if (!referenced.has(h) && !declaredHandles.has(h)) throw new CliError(`--executa-id "${h}=…" does not match any bundled:${h} reference or app.json bundled_executas entry (typo?)`, 2);
157
+ return { referenced };
158
+ }
159
+
160
+ //#endregion
161
+ export { collectBundledHandles, lockPath, parseExecutaIdOverrides, readExecutasLock, substituteBundledRefs, validateBundledHandles, writeBundleToolIdSidecar, writeExecutasLock };
@@ -0,0 +1,5 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import "./client-Dn9zThOd.js";
3
+ import { collectBundledHandles, lockPath, parseExecutaIdOverrides, readExecutasLock, substituteBundledRefs, validateBundledHandles, writeBundleToolIdSidecar, writeExecutasLock } from "./bundled-executas-BNOKw4kv.js";
4
+
5
+ export { substituteBundledRefs, writeBundleToolIdSidecar };