@anna-ai/cli 0.1.22 → 0.1.26

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 (67) hide show
  1. package/dist/{_lifecycle-shared-sbea9HtH.js → _lifecycle-shared-BpSOfVCP.js} +1 -1
  2. package/dist/{agent-Br6zY2qw.js → agent-CaZVCPs6.js} +1 -1
  3. package/dist/{app-bundle-upload-DuLalcSt.js → app-bundle-upload-BhAYo6yj.js} +3 -3
  4. package/dist/{apps-BTn9EN0x.js → apps-3VcdHIBK.js} +3 -3
  5. package/dist/{apps-cut-DtEkddIk.js → apps-cut-BCicQFRY.js} +13 -10
  6. package/dist/{apps-destructive-DSTrcFUP.js → apps-destructive-DWF4eTHn.js} +4 -4
  7. package/dist/{apps-discard-y3_IwcbQ.js → apps-discard-BoY4zMJw.js} +13 -10
  8. package/dist/{apps-grants-BGWlpee0.js → apps-grants-DgvymPBT.js} +3 -3
  9. package/dist/{apps-publish-CaTCanDu.js → apps-publish-Dgi4lBlu.js} +8 -8
  10. package/dist/apps-publish-Do7M5je3.js +14 -0
  11. package/dist/{apps-push-B9XT2uwF.js → apps-push-CixatuQG.js} +27 -12
  12. package/dist/{apps-release-BLH9XSxB.js → apps-release-DJFG4BV3.js} +5 -5
  13. package/dist/{apps-status-DQ9RvlME.js → apps-status-F6aVlzDW.js} +3 -3
  14. package/dist/{apps-submit-review-DLwCxeAs.js → apps-submit-review-BVmZlhmB.js} +4 -4
  15. package/dist/{apps-sync-meta-D9eKMMUp.js → apps-sync-meta-CQQC_Heb.js} +5 -5
  16. package/dist/{apps-versions-2Tmk0nsx.js → apps-versions-BIKsJzIT.js} +3 -3
  17. package/dist/{bridge-BuklhzeE.js → bridge-DxBd0Fl9.js} +1 -1
  18. package/dist/bridge-hzqNFm9-.js +3 -0
  19. package/dist/{bundled-executas-BNOKw4kv.js → bundled-executas-B6b8gIfp.js} +1 -1
  20. package/dist/{bundled-executas-CNaV2C_O.js → bundled-executas-DeBhDjd8.js} +2 -2
  21. package/dist/cli.js +70 -48
  22. package/dist/{confirm-DxHkk9Wn.js → confirm-h_qMrx0I.js} +1 -1
  23. package/dist/{dev-E7mqXj5S.js → dev-3okZmzNM.js} +19 -22
  24. package/dist/dev-C5r439wM.js +4 -0
  25. package/dist/{doctor-DKrt-Kda.js → doctor-giNqYnla.js} +10 -22
  26. package/dist/{executa-cache-WBkCLic7.js → executa-cache-Kx3rfQD-.js} +1 -1
  27. package/dist/{executa-destructive-COQE4Xqi.js → executa-destructive-PL2ooHpZ.js} +4 -4
  28. package/dist/{executa-dev-BvS9zTpO.js → executa-dev-Cr9Yepph.js} +10 -9
  29. package/dist/executa-install-DQIhVHPT.js +90 -0
  30. package/dist/executa-install-DjXE_-U-.js +7 -0
  31. package/dist/{executa-publish-Ca5V7MyA.js → executa-publish-CkPAB34b.js} +5 -5
  32. package/dist/executa-publish-IXWSwva0.js +9 -0
  33. package/dist/{executa-reads-CQ6S8gHY.js → executa-reads-CjGZq1yP.js} +3 -3
  34. package/dist/{manifest-DGwRap2i.js → manifest-Bljz8Y6T.js} +74 -15
  35. package/dist/nexus-root-BlPwOusj.js +49 -0
  36. package/dist/{publish-C1wcf-qI.js → publish-BYWuujP3.js} +11 -11
  37. package/dist/{server-_IG8Igje.js → server-Cp7mYV9t.js} +50 -8
  38. package/dist/{storage-CTkApNQ9.js → storage-CKTmE87u.js} +1 -1
  39. package/dist/test/index.js +3 -0
  40. package/dist/{token-B9JUPelx.js → token-Cg7BZGp6.js} +2 -2
  41. package/dist/{working-orchestration-Dw9u1Vq0.js → working-orchestration-Dd1ETQ3c.js} +54 -7
  42. package/package.json +5 -4
  43. package/templates/executa/go/main.go +4 -2
  44. package/templates/executa/node/plugin.mjs +4 -2
  45. package/templates/executa/python/__SLUG_PY___plugin.py +4 -2
  46. package/dist/apps-publish-DfZTOxBJ.js +0 -14
  47. package/dist/bridge-Id8K8gr-.js +0 -3
  48. package/dist/dev-BS_8yoSm.js +0 -3
  49. package/dist/executa-publish-B88_9gbp.js +0 -9
  50. /package/dist/{app-cache-BEM653Th.js → app-cache-Bl7cE5fm.js} +0 -0
  51. /package/dist/{apps-B1Nd8l_t.js → apps-CCdtLmxQ.js} +0 -0
  52. /package/dist/{client-Dn9zThOd.js → client-D-_z1ALk.js} +0 -0
  53. /package/dist/{credentials-DklPMD22.js → credentials-Chkoidh5.js} +0 -0
  54. /package/dist/{dev-account-qRaET1Cp.js → dev-account-CGo8k9_2.js} +0 -0
  55. /package/dist/{dev-app-cache-D-r6ZpEk.js → dev-app-cache-TSjL4D4n.js} +0 -0
  56. /package/dist/{executa-cache-BFoUtb4J.js → executa-cache-CXiEgFZY.js} +0 -0
  57. /package/dist/{executa-init-Jp-h9OI7.js → executa-init-DXea7yRN.js} +0 -0
  58. /package/dist/{executa-register-CulDtwYZ.js → executa-register-BUiPzPIU.js} +0 -0
  59. /package/dist/{executas-Cep6KEo0.js → executas-CK3er6f9.js} +0 -0
  60. /package/dist/{fixture-CYwxbiQD.js → fixture-BvP5umlN.js} +0 -0
  61. /package/dist/{host_upload-GXVkDM5M.js → host_upload-BXeHTgJs.js} +0 -0
  62. /package/dist/{image-DduR91n5.js → image-CSEXEfD-.js} +0 -0
  63. /package/dist/{login-BGqFjQwH.js → login-BGZjMAlh.js} +0 -0
  64. /package/dist/{logout-CGIRKH3y.js → logout-mh2_QlyM.js} +0 -0
  65. /package/dist/{runner-B-hIqx5L.js → runner-BuYbm-ex.js} +0 -0
  66. /package/dist/{sampling-CXke7hq1.js → sampling-BcML4teS.js} +0 -0
  67. /package/dist/{whoami-BoFLEUcp.js → whoami-l_kIkfbI.js} +0 -0
@@ -1,4 +1,5 @@
1
- import { parseExecutaSpec } from "./dev-E7mqXj5S.js";
1
+ import "./nexus-root-BlPwOusj.js";
2
+ import { parseExecutaSpec } from "./dev-3okZmzNM.js";
2
3
  import { isAbsolute, resolve } from "node:path";
3
4
  import { existsSync } from "node:fs";
4
5
  import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
@@ -33,8 +34,8 @@ async function runExecutaDev(opts) {
33
34
  }
34
35
  const oneShot = !!(opts.describe || opts.health || opts.invoke);
35
36
  const quiet = oneShot && (opts.json ?? false);
36
- const { getAccount } = await import("./credentials-DklPMD22.js");
37
- const { ensureDevExecutaRegistered } = await import("./dev-app-cache-D-r6ZpEk.js");
37
+ const { getAccount } = await import("./credentials-Chkoidh5.js");
38
+ const { ensureDevExecutaRegistered } = await import("./dev-app-cache-TSjL4D4n.js");
38
39
  const needsRealMint = !opts.noSampling && !opts.mockSampling || !opts.noAgent && !opts.mockAgent || !opts.noImage && !opts.mockImage || !opts.noUpload && !opts.mockUpload || opts.storage === "real";
39
40
  let effectiveAppSlug = opts.appSlug;
40
41
  let autoRegistered = false;
@@ -56,7 +57,7 @@ async function runExecutaDev(opts) {
56
57
  }
57
58
  }
58
59
  }
59
- const { SamplingBridge } = await import("./sampling-CXke7hq1.js");
60
+ const { SamplingBridge } = await import("./sampling-BcML4teS.js");
60
61
  const sampling = opts.noSampling ? new SamplingBridge({ mode: "off" }) : opts.mockSampling ? new SamplingBridge({
61
62
  mode: "mock",
62
63
  mockFile: opts.mockSampling
@@ -65,7 +66,7 @@ async function runExecutaDev(opts) {
65
66
  account: opts.samplingAccount,
66
67
  appSlug: effectiveAppSlug
67
68
  }) : new SamplingBridge({ mode: "off" });
68
- const { AgentBridge } = await import("./agent-Br6zY2qw.js");
69
+ const { AgentBridge } = await import("./agent-CaZVCPs6.js");
69
70
  const agent = opts.noAgent ? new AgentBridge({ mode: "off" }) : opts.mockAgent ? new AgentBridge({
70
71
  mode: "mock",
71
72
  mockFile: opts.mockAgent
@@ -74,7 +75,7 @@ async function runExecutaDev(opts) {
74
75
  account: opts.agentAccount ?? opts.samplingAccount,
75
76
  appSlug: effectiveAppSlug
76
77
  }) : new AgentBridge({ mode: "off" });
77
- const { StorageBridge } = await import("./storage-CTkApNQ9.js");
78
+ const { StorageBridge } = await import("./storage-CKTmE87u.js");
78
79
  const storageMode = opts.storage ?? (opts.mockStorage ? "mock" : "memory");
79
80
  const storage = new StorageBridge({
80
81
  mode: storageMode,
@@ -84,8 +85,8 @@ async function runExecutaDev(opts) {
84
85
  scopes: opts.storageScopes ? opts.storageScopes.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
85
86
  pluginName: parsed.tool_id
86
87
  });
87
- const { ExecutaRunner } = await import("./runner-B-hIqx5L.js");
88
- const { ImageBridge } = await import("./image-DduR91n5.js");
88
+ const { ExecutaRunner } = await import("./runner-BuYbm-ex.js");
89
+ const { ImageBridge } = await import("./image-CSEXEfD-.js");
89
90
  const image = opts.noImage ? new ImageBridge({ mode: "off" }) : opts.mockImage ? new ImageBridge({
90
91
  mode: "mock",
91
92
  mockFile: opts.mockImage
@@ -94,7 +95,7 @@ async function runExecutaDev(opts) {
94
95
  account: opts.imageAccount ?? opts.samplingAccount,
95
96
  appSlug: effectiveAppSlug
96
97
  }) : new ImageBridge({ mode: "off" });
97
- const { HostUploadBridge } = await import("./host_upload-GXVkDM5M.js");
98
+ const { HostUploadBridge } = await import("./host_upload-BXeHTgJs.js");
98
99
  const hostUpload = opts.noUpload ? new HostUploadBridge({ mode: "off" }) : opts.mockUpload ? new HostUploadBridge({
99
100
  mode: "mock",
100
101
  mockFile: opts.mockUpload
@@ -0,0 +1,90 @@
1
+ import { readExecutaIdentity } from "./executa-cache-CXiEgFZY.js";
2
+ import { parseExecutaSpec } from "./dev-3okZmzNM.js";
3
+ import { isAbsolute, join, resolve } from "node:path";
4
+ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
5
+ import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
6
+ import { homedir } from "node:os";
7
+
8
+ //#region src/commands/executa-install.ts
9
+ /** POSIX single-quote one argument for safe embedding in the shim. */
10
+ function shQuote(arg) {
11
+ return `'${arg.replace(/'/g, "'\\''")}'`;
12
+ }
13
+ function buildShim(projectDir, command) {
14
+ const cd = `cd ${shQuote(projectDir)} || exit 1`;
15
+ const exec = `exec ${command.map(shQuote).join(" ")} "$@"`;
16
+ return [
17
+ "#!/bin/sh",
18
+ "# Auto-generated by `anna-app executa install`.",
19
+ "# Local-dev launcher shim — runs the Executa from source so the Agent",
20
+ "# can discover it via 'Rediscover Local'. Safe to delete; regenerate",
21
+ "# with `anna-app executa install`.",
22
+ cd,
23
+ exec,
24
+ ""
25
+ ].join("\n");
26
+ }
27
+ async function runExecutaInstall(opts) {
28
+ const json = opts.json ?? false;
29
+ const cwd = opts.dir ? isAbsolute(opts.dir) ? opts.dir : resolve(process.cwd(), opts.dir) : process.cwd();
30
+ if (!existsSync(cwd)) {
31
+ console.error(red(`✗ executa dir not found: ${cwd}`));
32
+ return 2;
33
+ }
34
+ const parsed = parseExecutaSpec(`dir=${cwd}`, process.cwd());
35
+ if (parsed instanceof Error) {
36
+ console.error(red(`✗ ${parsed.message}`));
37
+ return 2;
38
+ }
39
+ if (!parsed.command || parsed.command.length === 0) {
40
+ console.error(red(`✗ could not derive a launch command for ${cwd}`));
41
+ return 2;
42
+ }
43
+ const identity = readExecutaIdentity(cwd);
44
+ const targetId = opts.toolId ?? identity?.tool_id ?? parsed.tool_id;
45
+ if (!targetId) {
46
+ console.error(red("✗ could not resolve a tool_id — pass --tool-id <id>, or run `anna-app executa publish` / `anna-app apps push` first to mint one."));
47
+ return 2;
48
+ }
49
+ if (/[/\\]/.test(targetId) || targetId.startsWith(".")) {
50
+ console.error(red(`✗ invalid tool_id "${targetId}" (no path separators or leading dot)`));
51
+ return 2;
52
+ }
53
+ const binDir = opts.binDir ? isAbsolute(opts.binDir) ? opts.binDir : resolve(process.cwd(), opts.binDir) : join(homedir(), ".anna", "executa", "bin");
54
+ const shimPath = join(binDir, targetId);
55
+ if (existsSync(shimPath) && !opts.force) {
56
+ console.error(red(`✗ shim already exists: ${shimPath}`) + dim(" (pass --force to overwrite)"));
57
+ return 1;
58
+ }
59
+ try {
60
+ mkdirSync(binDir, { recursive: true });
61
+ writeFileSync(shimPath, buildShim(parsed.project_dir, parsed.command), "utf-8");
62
+ chmodSync(shimPath, 493);
63
+ } catch (e) {
64
+ console.error(red(`✗ failed to write shim: ${e.message}`));
65
+ return 1;
66
+ }
67
+ if (json) {
68
+ console.log(JSON.stringify({
69
+ tool_id: targetId,
70
+ shim_path: shimPath,
71
+ project_dir: parsed.project_dir,
72
+ command: parsed.command,
73
+ source: opts.toolId ? "flag" : identity?.tool_id ? "identity-cache" : "discovered"
74
+ }));
75
+ return 0;
76
+ }
77
+ if (opts.quiet) return 0;
78
+ console.log(bold(cyan("anna-app executa install")));
79
+ console.log(` tool_id ${green(targetId)}`);
80
+ console.log(` shim ${dim(shimPath)}`);
81
+ console.log(` dir ${dim(parsed.project_dir)}`);
82
+ console.log(` command ${dim(parsed.command.join(" "))}`);
83
+ if (!opts.toolId && identity?.tool_id) console.log(` source ${dim("minted tool_id from .anna/executa.json")}`);
84
+ else if (!opts.toolId) console.log(yellow(" ! using the placeholder tool_id from executa.json — run `executa publish`/`apps push` to mint the real one, then re-run with --force (or pass --tool-id)."));
85
+ console.log(dim("\n→ open the Agent's Plugin Details modal and click " + bold("Rediscover Local") + dim(" to load it under this tool_id.")));
86
+ return 0;
87
+ }
88
+
89
+ //#endregion
90
+ export { runExecutaInstall };
@@ -0,0 +1,7 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import "./nexus-root-BlPwOusj.js";
3
+ import "./executa-cache-CXiEgFZY.js";
4
+ import "./dev-3okZmzNM.js";
5
+ import { runExecutaInstall } from "./executa-install-DQIhVHPT.js";
6
+
7
+ export { runExecutaInstall };
@@ -1,9 +1,9 @@
1
1
  import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
- import { CliError } from "./client-Dn9zThOd.js";
3
- import { commitDraft, createDraft, getMySkill, getMyTool, listToolVersions, publishToolVersion, setSkillVisibility, setToolVisibility, updateMySkill, updateMyTool } from "./executas-Cep6KEo0.js";
4
- import { executaCacheMatches, invalidateExecutaCache, mintIdempotencyKey, readExecutaIdentity, writeExecutaIdentity } from "./executa-cache-BFoUtb4J.js";
5
- import { loadExecutaManifest } from "./manifest-DGwRap2i.js";
6
- import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
2
+ import { CliError } from "./client-D-_z1ALk.js";
3
+ import { commitDraft, createDraft, getMySkill, getMyTool, listToolVersions, publishToolVersion, setSkillVisibility, setToolVisibility, updateMySkill, updateMyTool } from "./executas-CK3er6f9.js";
4
+ import { executaCacheMatches, invalidateExecutaCache, mintIdempotencyKey, readExecutaIdentity, writeExecutaIdentity } from "./executa-cache-CXiEgFZY.js";
5
+ import { loadExecutaManifest } from "./manifest-Bljz8Y6T.js";
6
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-BpSOfVCP.js";
7
7
  import { join, relative, resolve, sep } from "node:path";
8
8
  import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
9
9
  import { bold, cyan, dim, green, yellow } from "kleur/colors";
@@ -0,0 +1,9 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import "./client-D-_z1ALk.js";
3
+ import "./executas-CK3er6f9.js";
4
+ import "./executa-cache-CXiEgFZY.js";
5
+ import { runExecutaPublish } from "./executa-publish-CkPAB34b.js";
6
+ import "./manifest-Bljz8Y6T.js";
7
+ import "./_lifecycle-shared-BpSOfVCP.js";
8
+
9
+ export { runExecutaPublish };
@@ -1,7 +1,7 @@
1
1
  import "./credentials-BTv2IfUZ.js";
2
- import { CliError } from "./client-Dn9zThOd.js";
3
- import { getMyTool, listMyTools, listToolVersions } from "./executas-Cep6KEo0.js";
4
- import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
2
+ import { CliError } from "./client-D-_z1ALk.js";
3
+ import { getMyTool, listMyTools, listToolVersions } from "./executas-CK3er6f9.js";
4
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-BpSOfVCP.js";
5
5
  import { bold, cyan, dim, green } from "kleur/colors";
6
6
 
7
7
  //#region src/commands/executa-reads.ts
@@ -1,4 +1,4 @@
1
- import { CliError } from "./client-Dn9zThOd.js";
1
+ import { CliError } from "./client-D-_z1ALk.js";
2
2
  import { resolve } from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
 
@@ -124,23 +124,82 @@ function loadExecutaManifest(cwd, manifestPath) {
124
124
  * Returns `undefined` when absent (a dev-only Executa). Skills never carry
125
125
  * distribution config — they are declarative Markdown, not installable
126
126
  * processes — so a `distribution` block on a skill is rejected.
127
+ *
128
+ * Two shapes are accepted:
129
+ *
130
+ * 1. Flat (single mode) — the historical form:
131
+ *
132
+ * "distribution": { "type": "binary", "binary_urls": { … } }
133
+ *
134
+ * 2. Multi-profile (switchable) — keep several modes side by side and flip
135
+ * `active` to choose which one is published:
136
+ *
137
+ * "distribution": {
138
+ * "active": "binary",
139
+ * "profiles": {
140
+ * "local": { "type": "local", "executable_name": "…" },
141
+ * "binary": { "type": "binary", "binary_urls": { … } }
142
+ * }
143
+ * }
144
+ *
145
+ * Every profile is validated (so a typo in an *inactive* profile is
146
+ * still caught), but the returned `ExecutaDistribution` carries the
147
+ * fields of the *active* profile plus `active`/`profiles` for
148
+ * introspection. Downstream publish code stays profile-agnostic.
127
149
  */
128
150
  function parseExecutaDistribution(value, executaType) {
129
151
  if (value === void 0 || value === null) return void 0;
130
- if (typeof value !== "object" || Array.isArray(value)) throw new CliError("executa.json `distribution` must be an object with a `type` field", 4);
152
+ if (typeof value !== "object" || Array.isArray(value)) throw new CliError("executa.json `distribution` must be an object with a `type` field (or `profiles` for the switchable multi-mode form)", 4);
131
153
  if (executaType === "skill") throw new CliError("executa.json `distribution` is only valid for `executa_type: \"tool\"` — skills are declarative Markdown and are not installed as a process", 4);
132
154
  const d = value;
155
+ if (d["profiles"] !== void 0) return parseProfiledDistribution(d);
156
+ return parseDistributionProfile(d, "distribution");
157
+ }
158
+ /**
159
+ * Resolve the multi-profile `distribution` form into the selected profile.
160
+ * Validates *all* profiles and records them under `profiles`.
161
+ */
162
+ function parseProfiledDistribution(d) {
163
+ if (d["type"] !== void 0) throw new CliError("executa.json `distribution` cannot set both `type` (flat form) and `profiles` (switchable form) — pick one", 4);
164
+ const profilesRaw = d["profiles"];
165
+ if (profilesRaw === null || typeof profilesRaw !== "object" || Array.isArray(profilesRaw)) throw new CliError("executa.json `distribution.profiles` must be an object mapping a profile name → distribution config", 4);
166
+ const profileEntries = Object.entries(profilesRaw);
167
+ if (profileEntries.length === 0) throw new CliError("executa.json `distribution.profiles` must declare at least one profile", 4);
168
+ const profiles = {};
169
+ for (const [pname, pval] of profileEntries) {
170
+ if (pval === null || typeof pval !== "object" || Array.isArray(pval)) throw new CliError(`executa.json \`distribution.profiles["${pname}"]\` must be an object`, 4);
171
+ profiles[pname] = parseDistributionProfile(pval, `distribution.profiles["${pname}"]`);
172
+ }
173
+ const active = pickString(d, "active");
174
+ let selected;
175
+ if (active !== void 0) {
176
+ if (!(active in profiles)) throw new CliError(`executa.json \`distribution.active\` "${active}" is not a declared profile — expected one of ${Object.keys(profiles).join(" | ")}`, 4);
177
+ selected = active;
178
+ } else if (profileEntries.length === 1) selected = profileEntries[0][0];
179
+ else throw new CliError(`executa.json \`distribution.active\` is required when more than one profile is declared — expected one of ${Object.keys(profiles).join(" | ")}`, 4);
180
+ const resolved = profiles[selected];
181
+ return {
182
+ ...resolved,
183
+ active: selected,
184
+ profiles
185
+ };
186
+ }
187
+ /**
188
+ * Parse + validate a single distribution config (one flat block or one entry
189
+ * of a `profiles` map). `ctx` is the JSON path label used in error messages.
190
+ */
191
+ function parseDistributionProfile(d, ctx) {
133
192
  const type = pickString(d, "type");
134
- if (!type) throw new CliError("executa.json `distribution.type` is required", 4);
135
- if (!DISTRIBUTION_TYPES.has(type)) throw new CliError(`executa.json \`distribution.type\` "${type}" is invalid — expected one of ` + [...DISTRIBUTION_TYPES].join(" | "), 4);
193
+ if (!type) throw new CliError(`executa.json \`${ctx}.type\` is required`, 4);
194
+ if (!DISTRIBUTION_TYPES.has(type)) throw new CliError(`executa.json \`${ctx}.type\` "${type}" is invalid — expected one of ` + [...DISTRIBUTION_TYPES].join(" | "), 4);
136
195
  const distType = type;
137
196
  const packageName = pickString(d, "package_name");
138
197
  const executableName = pickString(d, "executable_name");
139
198
  const supportsProtocol = pickBool(d, "supports_protocol");
140
- const binaryUrls = parseBinaryUrls(d["binary_urls"]);
141
- const capabilities = parseCapabilities(d["capabilities"]);
142
- if (PACKAGE_BACKED.has(distType) && !packageName) throw new CliError(`executa.json \`distribution.package_name\` is required for \`distribution.type: "${distType}"\``, 4);
143
- if (distType === "binary" && (!binaryUrls || Object.keys(binaryUrls).length === 0)) throw new CliError("executa.json `distribution.binary_urls` is required (and non-empty) for `distribution.type: \"binary\"`", 4);
199
+ const binaryUrls = parseBinaryUrls(d["binary_urls"], ctx);
200
+ const capabilities = parseCapabilities(d["capabilities"], ctx);
201
+ if (PACKAGE_BACKED.has(distType) && !packageName) throw new CliError(`executa.json \`${ctx}.package_name\` is required for \`${ctx}.type: "${distType}"\``, 4);
202
+ if (distType === "binary" && (!binaryUrls || Object.keys(binaryUrls).length === 0)) throw new CliError(`executa.json \`${ctx}.binary_urls\` is required (and non-empty) for \`${ctx}.type: "binary"\``, 4);
144
203
  return {
145
204
  type: distType,
146
205
  packageName,
@@ -150,29 +209,29 @@ function parseExecutaDistribution(value, executaType) {
150
209
  capabilities
151
210
  };
152
211
  }
153
- function parseBinaryUrls(value) {
212
+ function parseBinaryUrls(value, ctx = "distribution") {
154
213
  if (value === void 0 || value === null) return void 0;
155
- if (typeof value !== "object" || Array.isArray(value)) throw new CliError("executa.json `distribution.binary_urls` must be an object mapping platform → URL string or asset dict", 4);
214
+ if (typeof value !== "object" || Array.isArray(value)) throw new CliError(`executa.json \`${ctx}.binary_urls\` must be an object mapping platform → URL string or asset dict`, 4);
156
215
  const out = {};
157
216
  for (const [platform, asset] of Object.entries(value)) {
158
217
  if (typeof asset === "string") {
159
- if (!asset.trim()) throw new CliError(`executa.json \`distribution.binary_urls["${platform}"]\` is empty`, 4);
218
+ if (!asset.trim()) throw new CliError(`executa.json \`${ctx}.binary_urls["${platform}"]\` is empty`, 4);
160
219
  out[platform] = asset;
161
220
  continue;
162
221
  }
163
222
  if (asset && typeof asset === "object" && !Array.isArray(asset)) {
164
223
  const url = pickString(asset, "url");
165
- if (!url) throw new CliError(`executa.json \`distribution.binary_urls["${platform}"]\` asset dict missing required \`url\``, 4);
224
+ if (!url) throw new CliError(`executa.json \`${ctx}.binary_urls["${platform}"]\` asset dict missing required \`url\``, 4);
166
225
  out[platform] = asset;
167
226
  continue;
168
227
  }
169
- throw new CliError(`executa.json \`distribution.binary_urls["${platform}"]\` must be a URL string or an asset dict \`{ url, sha256?, size?, entrypoint?, format? }\``, 4);
228
+ throw new CliError(`executa.json \`${ctx}.binary_urls["${platform}"]\` must be a URL string or an asset dict \`{ url, sha256?, size?, entrypoint?, format? }\``, 4);
170
229
  }
171
230
  return out;
172
231
  }
173
- function parseCapabilities(value) {
232
+ function parseCapabilities(value, ctx = "distribution") {
174
233
  if (value === void 0 || value === null) return void 0;
175
- if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) throw new CliError("executa.json `distribution.capabilities` must be an array of strings", 4);
234
+ if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) throw new CliError(`executa.json \`${ctx}.capabilities\` must be an array of strings`, 4);
176
235
  return value;
177
236
  }
178
237
  function pickBool(o, k) {
@@ -0,0 +1,49 @@
1
+ import { dirname, isAbsolute, resolve } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+
4
+ //#region src/nexus-root.ts
5
+ /**
6
+ * Resolve an absolute matrix-nexus root, or null if none is found.
7
+ *
8
+ * Search order (first hit wins):
9
+ * 1. `explicit` (the `--matrix-nexus-root` flag).
10
+ * 2. `$ANNA_NEXUS_ROOT`.
11
+ * 3. Walk up from `cwd`.
12
+ * 4. Sibling `../matrix-nexus`.
13
+ *
14
+ * A candidate qualifies only if it contains
15
+ * `packages/anna-app-runtime-local/pyproject.toml` (the marker that also
16
+ * gates `nexus-source` runtime mode), so the three escape hatches can
17
+ * never disagree on what counts as "a matrix-nexus checkout".
18
+ */
19
+ function findMatrixNexusRoot(explicit, cwd) {
20
+ const candidates = [explicit, process.env.ANNA_NEXUS_ROOT];
21
+ let dir = cwd;
22
+ while (true) {
23
+ candidates.push(dir);
24
+ const parent = dirname(dir);
25
+ if (parent === dir) break;
26
+ dir = parent;
27
+ }
28
+ candidates.push(resolve(cwd, "..", "matrix-nexus"));
29
+ for (const c of candidates) {
30
+ if (!c) continue;
31
+ const abs = isAbsolute(c) ? c : resolve(cwd, c);
32
+ if (existsSync(resolve(abs, "packages/anna-app-runtime-local/pyproject.toml"))) return abs;
33
+ }
34
+ return null;
35
+ }
36
+ /**
37
+ * Locate the in-tree `@anna-ai/app-schema` bundle inside a matrix-nexus
38
+ * root, or null if it is absent / not built. The returned dir is suitable
39
+ * for `ANNA_APP_SCHEMA_DIR` / `loadSchemaBundle`'s override so contributors
40
+ * can `validate` against an UNPUBLISHED schema bump without waiting for the
41
+ * npm push — parity with the runtime/SDK nexus-source hatches.
42
+ */
43
+ function nexusSchemaDir(root) {
44
+ const dir = resolve(root, "packages", "anna-app-schema");
45
+ return existsSync(resolve(dir, "dispatcher_version.txt")) ? dir : null;
46
+ }
47
+
48
+ //#endregion
49
+ export { findMatrixNexusRoot, nexusSchemaDir };
@@ -1,15 +1,15 @@
1
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";
2
+ import "./apps-CCdtLmxQ.js";
3
+ import { CliError } from "./client-D-_z1ALk.js";
4
+ import "./bundled-executas-B6b8gIfp.js";
5
+ import "./executas-CK3er6f9.js";
6
+ import "./executa-cache-CXiEgFZY.js";
7
+ import { runExecutaPublish } from "./executa-publish-CkPAB34b.js";
8
+ import "./manifest-Bljz8Y6T.js";
9
+ import { withErrorHandling } from "./_lifecycle-shared-BpSOfVCP.js";
10
+ import "./app-cache-Bl7cE5fm.js";
11
+ import "./app-bundle-upload-BhAYo6yj.js";
12
+ import { runAppsPublish } from "./apps-publish-Dgi4lBlu.js";
13
13
  import { resolve } from "node:path";
14
14
  import { existsSync } from "node:fs";
15
15
  import { red, yellow } from "kleur/colors";
@@ -1,5 +1,5 @@
1
1
  import { canonicalHost, getAccount } from "./credentials-BTv2IfUZ.js";
2
- import { BridgeRequestError } from "./bridge-BuklhzeE.js";
2
+ import { BridgeRequestError } from "./bridge-DxBd0Fl9.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";
@@ -445,6 +445,26 @@ var LlmBridge = class {
445
445
  message: "agent.session.history is not exposed over HTTP; available only via in-process store"
446
446
  }
447
447
  };
448
+ case "session.list": {
449
+ const includeExpired = Boolean(args.args?.include_expired);
450
+ const rawLimit = Number(args.args?.limit ?? 50);
451
+ const limit = Math.max(1, Math.min(100, Number.isFinite(rawLimit) ? rawLimit : 50));
452
+ const now = Math.floor(Date.now() / 1e3);
453
+ const sessions = [...this.mintedAgent.values()].filter((ms) => includeExpired || ms.expiresAt > now).slice(0, limit).map((ms) => ({
454
+ app_session_uuid: ms.appSessionUuid,
455
+ kind: "agent",
456
+ submode: ms.submode,
457
+ fixed_client_id: null,
458
+ label: "anna-app dev",
459
+ created_at: null,
460
+ last_active_at: null,
461
+ expires_at: new Date(ms.expiresAt * 1e3).toISOString()
462
+ }));
463
+ return {
464
+ ok: true,
465
+ result: { sessions }
466
+ };
467
+ }
448
468
  case "session.delete": {
449
469
  const apsUuid = String(args.args.app_session_uuid ?? "");
450
470
  const ms = this.mintedAgent.get(apsUuid);
@@ -857,6 +877,11 @@ var HarnessServer = class {
857
877
  "agent",
858
878
  "session.delete"
859
879
  ],
880
+ [
881
+ "host.agent.session.list",
882
+ "agent",
883
+ "session.list"
884
+ ],
860
885
  [
861
886
  "host.image.generate",
862
887
  "image",
@@ -1133,17 +1158,34 @@ var HarnessServer = class {
1133
1158
  }
1134
1159
  async serveSdk(pathname, res) {
1135
1160
  const sdkRel = pathname.replace(/^\/static\/anna-apps\/_sdk\/[^/]+\//, "");
1136
- let distRoot;
1137
- try {
1138
- const req = createRequire(import.meta.url);
1139
- distRoot = dirname(req.resolve("@anna-ai/app-runtime"));
1140
- } catch (e) {
1141
- return this.text(res, 500, `@anna-ai/app-runtime is not installed: ${e.message}`);
1142
- }
1161
+ const distRoot = this.resolveSdkDistRoot();
1162
+ if (!distRoot) return this.text(res, 500, "@anna-ai/app-runtime is not installed and no in-tree packages/anna-app-runtime/dist was found.");
1143
1163
  const abs = resolve(distRoot, sdkRel);
1144
1164
  if (!abs.startsWith(distRoot)) return this.text(res, 403, "forbidden");
1145
1165
  return this.serveFile(abs, res);
1146
1166
  }
1167
+ /**
1168
+ * Resolve the directory the browser SDK (`index.js` + friends) is served
1169
+ * from. When `--matrix-nexus-root` is set we prefer the in-tree
1170
+ * `packages/anna-app-runtime/dist/` so contributors run byte-identical
1171
+ * with the unpublished workspace SDK (parity with the Python bridge, which
1172
+ * already runs in-tree in `nexus-source` mode). Falls back to the
1173
+ * published npm package resolved from anna-app-cli's own node_modules.
1174
+ * Returns null if neither is available.
1175
+ */
1176
+ resolveSdkDistRoot() {
1177
+ const root = this.cfg.matrixNexusRoot;
1178
+ if (root) {
1179
+ const inTree = resolve(root, "packages", "anna-app-runtime", "dist");
1180
+ if (existsSync(join(inTree, "index.js"))) return inTree;
1181
+ }
1182
+ try {
1183
+ const req = createRequire(import.meta.url);
1184
+ return dirname(req.resolve("@anna-ai/app-runtime"));
1185
+ } catch {
1186
+ return null;
1187
+ }
1188
+ }
1147
1189
  async serveBundleAsset(rel, res) {
1148
1190
  const abs = resolve(this.cfg.bundleDir, normalize(rel));
1149
1191
  if (!abs.startsWith(resolve(this.cfg.bundleDir))) return this.text(res, 403, "forbidden");
@@ -1,5 +1,5 @@
1
1
  import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
- import { hostOf, requireAccount, withCode } from "./dev-account-qRaET1Cp.js";
2
+ import { hostOf, requireAccount, withCode } from "./dev-account-CGo8k9_2.js";
3
3
  import { resolve } from "node:path";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
 
@@ -22,6 +22,9 @@ function deriveAcl(manifest) {
22
22
  const bare = ref.includes(":") ? ref.split(":", 2)[1] : ref;
23
23
  allowedTools.add(bare);
24
24
  }
25
+ if (allowedTools.size === 0 && !toolsWildcard) {
26
+ for (const r of [...manifest.required_executas ?? [], ...manifest.optional_executas ?? []]) if (r.tool_id) allowedTools.add(r.tool_id);
27
+ }
25
28
  if (allowedTools.size > 0 || toolsWildcard) allowed.add("tools.invoke");
26
29
  for (const ns of Object.keys(NAMESPACED)) {
27
30
  if (ns === "tools") continue;
@@ -1,6 +1,6 @@
1
1
  import "./credentials-BTv2IfUZ.js";
2
- import "./client-Dn9zThOd.js";
3
- import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
2
+ import "./client-D-_z1ALk.js";
3
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-BpSOfVCP.js";
4
4
  import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
5
5
 
6
6
  //#region src/api/tokens.ts
@@ -1,11 +1,14 @@
1
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";
2
+ import { createApp, findAppBySlug, getApp } from "./apps-CCdtLmxQ.js";
3
+ import { CliError } from "./client-D-_z1ALk.js";
4
+ import { parseExecutaIdOverrides, readExecutasLock, substituteBundledRefs, validateBundledHandles, writeBundleToolIdSidecar, writeExecutasLock } from "./bundled-executas-B6b8gIfp.js";
5
+ import { runExecutaPublish } from "./executa-publish-CkPAB34b.js";
6
+ import { loadExecutaManifest } from "./manifest-Bljz8Y6T.js";
7
+ import { runExecutaInstall } from "./executa-install-DQIhVHPT.js";
8
+ import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-Bl7cE5fm.js";
9
+ import { join, resolve } from "node:path";
8
10
  import { dim, green, yellow } from "kleur/colors";
11
+ import { homedir } from "node:os";
9
12
 
10
13
  //#region src/publish/working-orchestration.ts
11
14
  async function resolveBundledExecutas(params) {
@@ -104,6 +107,50 @@ async function resolveBundledExecutas(params) {
104
107
  };
105
108
  }
106
109
  /**
110
+ * Install a local-dev launcher shim for every bundled executa whose *active*
111
+ * distribution profile is `local`, under its minted `tool_id`. This closes
112
+ * the manual gap after `apps push`: a `local` executa never triggers a server
113
+ * `install_plugin` RPC, so the only way it turns "Running" on an Agent is the
114
+ * **Rediscover Local** flow — which needs an on-disk shim named exactly the
115
+ * minted `tool_id`. Binary/uv/npm/etc. profiles are skipped (the Agent
116
+ * installs those itself), as are skills (no runtime process) and dev-only
117
+ * executas with no distribution block.
118
+ *
119
+ * Failures are non-fatal: a shim that can't be written warns but does not
120
+ * fail the surrounding `apps push`.
121
+ */
122
+ async function installLocalBundledShims(params) {
123
+ const { manifest, cwd, resolved, json = false } = params;
124
+ const binDir = params.binDir ?? join(homedir(), ".anna", "executa", "bin");
125
+ const installed = [];
126
+ for (const decl of manifest.bundledExecutas) {
127
+ const toolId = resolved[decl.handle];
128
+ if (!toolId) continue;
129
+ const dir = resolve(cwd, decl.path);
130
+ let exManifest;
131
+ try {
132
+ exManifest = loadExecutaManifest(dir);
133
+ } catch {
134
+ continue;
135
+ }
136
+ if (exManifest.distribution?.type !== "local") continue;
137
+ const code = await runExecutaInstall({
138
+ dir,
139
+ toolId,
140
+ binDir: params.binDir,
141
+ force: true,
142
+ quiet: true
143
+ });
144
+ if (code === 0) installed.push({
145
+ handle: decl.handle,
146
+ tool_id: toolId,
147
+ shim_path: join(binDir, toolId)
148
+ });
149
+ else if (!json) console.log(yellow(` ! could not install local dev shim for "${decl.handle}" (exit ${code})`));
150
+ }
151
+ return installed;
152
+ }
153
+ /**
107
154
  * Resolve (creating if absent) the `AnnaApp` row, persisting the identity
108
155
  * cache before any subsequent network call. Returns `null` only in dry-run
109
156
  * mode when the app does not exist yet (nothing to create).
@@ -187,4 +234,4 @@ async function resolveAppBySlugOrCache(client, host, opts) {
187
234
  }
188
235
 
189
236
  //#endregion
190
- export { resolveAppBySlugOrCache, resolveAppIdentity, resolveBundledExecutas };
237
+ export { installLocalBundledShims, resolveAppBySlugOrCache, resolveAppIdentity, resolveBundledExecutas };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anna-ai/cli",
3
- "version": "0.1.22",
3
+ "version": "0.1.26",
4
4
  "description": "Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,11 +29,12 @@
29
29
  "test:watch": "vitest",
30
30
  "lint": "tsc --noEmit",
31
31
  "check:runtime-pin": "node scripts/check-runtime-pin.mjs",
32
- "prepublishOnly": "pnpm lint && pnpm test && pnpm build"
32
+ "check:sdk-pin": "node scripts/check-sdk-pin.mjs",
33
+ "prepublishOnly": "pnpm lint && pnpm test && pnpm build && node scripts/check-runtime-pin.mjs && node scripts/check-sdk-pin.mjs"
33
34
  },
34
35
  "dependencies": {
35
- "@anna-ai/app-runtime": "^0.5.0",
36
- "@anna-ai/app-schema": "^0.8.0",
36
+ "@anna-ai/app-runtime": "^0.7.0",
37
+ "@anna-ai/app-schema": "^0.9.0",
37
38
  "ajv": "^8.17.1",
38
39
  "ajv-formats": "^3.0.1",
39
40
  "commander": "^12.1.0",