@anna-ai/cli 0.1.12 → 0.1.16

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 (46) hide show
  1. package/dist/agent-DUmINbo4.js +372 -0
  2. package/dist/{apps-CDe6Fjq2.js → apps-Bt9CT5Sl.js} +1 -1
  3. package/dist/bridge-AJilXBw2.js +3 -0
  4. package/dist/{bridge-BQUo6ehX.js → bridge-nqQ3-j-t.js} +1 -1
  5. package/dist/cli.js +78 -9
  6. package/dist/dev-BRlFgo2I.js +3 -0
  7. package/dist/{dev-DoY58pBM.js → dev-C6v5yRV2.js} +4 -4
  8. package/dist/dev-account-DCyjamBa.js +44 -0
  9. package/dist/dev-app-cache-DAHcq46m.js +175 -0
  10. package/dist/dev-app-cache-DGF2Kuzd.js +4 -0
  11. package/dist/{doctor-DP2UB10l.js → doctor-C8MWfLt8.js} +1 -1
  12. package/dist/executa-dev-4FZ7AJHR.js +259 -0
  13. package/dist/executa-init-COEmKDOE.js +68 -0
  14. package/dist/executa-register-66WKIwQQ.js +47 -0
  15. package/dist/host_upload-C_pGOS6p.js +136 -0
  16. package/dist/image-bwolX7pa.js +131 -0
  17. package/dist/mascot-wlYTJqMs.js +218 -0
  18. package/dist/runner-DmGLdat0.js +322 -0
  19. package/dist/sampling-CJUDG-mf.js +155 -0
  20. package/dist/storage-EQJA_0UW.js +316 -0
  21. package/package.json +1 -1
  22. package/templates/executa/go/README.md +10 -0
  23. package/templates/executa/go/executa.json +4 -0
  24. package/templates/executa/go/go.mod +3 -0
  25. package/templates/executa/go/main.go +148 -0
  26. package/templates/executa/node/README.md +12 -0
  27. package/templates/executa/node/executa.json +4 -0
  28. package/templates/executa/node/package.json +12 -0
  29. package/templates/executa/node/plugin.mjs +126 -0
  30. package/templates/executa/node/sampling-fixture.jsonl +1 -0
  31. package/templates/executa/python/README.md +23 -0
  32. package/templates/executa/python/__SLUG_PY___plugin.py +146 -0
  33. package/templates/executa/python/executa.json +4 -0
  34. package/templates/executa/python/pyproject.toml +15 -0
  35. package/templates/executa/python/sampling-fixture.jsonl +4 -0
  36. package/templates/minimal/bundle/app.js +2 -0
  37. package/templates/minimal/bundle/index.html +1 -2
  38. package/dist/bridge-BEHyfpPI.js +0 -3
  39. package/dist/dev-app-cache-BMfOlTHd.js +0 -93
  40. package/dist/dev-app-cache-cXvO2XwQ.js +0 -4
  41. /package/dist/{credentials-CIOYq2Lm.js → credentials-DDqx6XMQ.js} +0 -0
  42. /package/dist/{fixture-BEu4LXLG.js → fixture-CATHyLLI.js} +0 -0
  43. /package/dist/{login-dl1Zfny8.js → login-CsIVbrmf.js} +0 -0
  44. /package/dist/{logout-DablvlFs.js → logout-gfmKQxMj.js} +0 -0
  45. /package/dist/{server-NXmiWJjX.js → server-D8R6ppOp.js} +0 -0
  46. /package/dist/{whoami-giXOY415.js → whoami-BS5wy-Nh.js} +0 -0
@@ -0,0 +1,372 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { hostOf, mintAppSession, requireAccount, withCode } from "./dev-account-DCyjamBa.js";
3
+ import { resolve } from "node:path";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+
6
+ //#region src/executa/agent.ts
7
+ /** JSON-RPC error codes from matrix/src/executa/protocol.py. */
8
+ const AGENT_ERR_NOT_GRANTED = -32041;
9
+ const AGENT_ERR_SESSION_NOT_FOUND = -32042;
10
+ const AGENT_ERR_INVALID_REQUEST = -32043;
11
+ const AGENT_ERR_PROVIDER_ERROR = -32046;
12
+ const AGENT_ERR_RUN_TOO_LARGE = -32047;
13
+ const MAX_FRAMES_PER_RUN = 4096;
14
+ var AgentBridge = class {
15
+ mocks = [];
16
+ /** uuid → minted session token (one entry per `session.create`). */
17
+ sessions = new Map();
18
+ constructor(opts) {
19
+ this.opts = opts;
20
+ if (opts.mode === "mock" && opts.mockFile) {
21
+ const path = resolve(opts.mockFile);
22
+ if (existsSync(path)) for (const line of readFileSync(path, "utf8").split(/\r?\n/)) {
23
+ if (!line.trim() || line.startsWith("#")) continue;
24
+ try {
25
+ this.mocks.push(JSON.parse(line));
26
+ } catch {}
27
+ }
28
+ }
29
+ }
30
+ async call(method, params) {
31
+ if (this.opts.mode === "off") throw withCode(new Error("agent reverse-RPC disabled — rerun without `--no-agent`, or pass `--mock-agent <fixture>` / `--agent <real>`"), AGENT_ERR_NOT_GRANTED);
32
+ if (this.opts.mode === "mock") return this.mockCall(method, params);
33
+ return this.realCall(method, params);
34
+ }
35
+ mockCall(method, params) {
36
+ const shortName = method.startsWith("agent/") ? method.slice(6) : method;
37
+ const content = collectContent(params);
38
+ const matched = this.mocks.find((m) => m.ns === "agent" && m.method === shortName && (!m.match?.contentIncludes || content.includes(m.match.contentIncludes))) ?? this.mocks.find((m) => m.ns === "agent" && m.method === shortName);
39
+ if (matched && matched.result !== void 0) return matched.result;
40
+ if (shortName === "session.create") return {
41
+ app_session_uuid: `aps_mock_${Math.random().toString(16).slice(2, 10)}`,
42
+ expires_in: 600,
43
+ agent_submode: "auto",
44
+ granted_tools: []
45
+ };
46
+ if (shortName === "session.run") {
47
+ const text = String(params.content ?? "");
48
+ return {
49
+ run_id: "mock_run",
50
+ stream_id: "strm_mock",
51
+ frames: [{
52
+ event: "final",
53
+ text: `(mock) echoing: ${text}`,
54
+ seq: 0,
55
+ stream_id: "strm_mock"
56
+ }],
57
+ final: {
58
+ event: "final",
59
+ text: `(mock) echoing: ${text}`
60
+ },
61
+ frame_count: 1
62
+ };
63
+ }
64
+ if (shortName === "session.cancel") return { cancelled: true };
65
+ if (shortName === "session.history") return {
66
+ messages: [],
67
+ cursor: null
68
+ };
69
+ if (shortName === "session.delete") return { deleted: true };
70
+ if (shortName === "complete") return {
71
+ role: "assistant",
72
+ content: {
73
+ type: "text",
74
+ text: "(mock) no fixture matched"
75
+ },
76
+ model: "mock-model",
77
+ stopReason: "endTurn"
78
+ };
79
+ throw withCode(new Error(`unknown agent method: ${method}`), AGENT_ERR_INVALID_REQUEST);
80
+ }
81
+ requireAppSlug() {
82
+ if (!this.opts.appSlug) throw withCode(new Error("agent bridge has no app_slug — pass `--app-slug <slug>` so nexus can attribute the spend"), -32602);
83
+ return this.opts.appSlug;
84
+ }
85
+ async realCall(method, params) {
86
+ const shortName = method.startsWith("agent/") ? method.slice(6) : method;
87
+ switch (shortName) {
88
+ case "session.create": return this.sessionCreate(params);
89
+ case "session.run": return this.sessionRun(params);
90
+ case "session.cancel": return this.sessionCancel(params);
91
+ case "session.history": return this.sessionHistory(params);
92
+ case "session.delete": return this.sessionDelete(params);
93
+ case "complete": return this.complete(params);
94
+ default: throw withCode(new Error(`unknown agent method: ${method}`), AGENT_ERR_INVALID_REQUEST);
95
+ }
96
+ }
97
+ async sessionCreate(params) {
98
+ const acc = requireAccount(this.opts.account);
99
+ const submode = params.agent_submode ?? "auto";
100
+ if (submode !== "auto" && submode !== "fixed") throw withCode(new Error("agent_submode must be 'auto' or 'fixed'"), AGENT_ERR_INVALID_REQUEST);
101
+ const fixedClientId = params.fixed_client_id ?? params.fixedClientId ?? null;
102
+ if (submode === "fixed" && !fixedClientId) throw withCode(new Error("fixed_client_id required when agent_submode='fixed'"), AGENT_ERR_INVALID_REQUEST);
103
+ const minted = await mintAppSession(acc.host, {
104
+ pat: acc.pat,
105
+ kind: "agent",
106
+ app_slug: this.requireAppSlug(),
107
+ submode,
108
+ fixed_client_id: fixedClientId,
109
+ label: params.label ?? "anna-app executa dev",
110
+ quota_caps: params.quota_caps ?? null,
111
+ ttl_seconds: params.ttl_seconds ?? 600
112
+ });
113
+ this.sessions.set(minted.app_session_uuid, {
114
+ appSessionToken: minted.app_session_token,
115
+ appSessionUuid: minted.app_session_uuid,
116
+ expiresAt: Math.floor(Date.now() / 1e3) + (minted.expires_in || 600) - 30,
117
+ submode
118
+ });
119
+ return {
120
+ app_session_uuid: minted.app_session_uuid,
121
+ thread_id: minted.app_session_uuid,
122
+ agent_submode: submode,
123
+ fixed_client_id: fixedClientId,
124
+ expires_in: minted.expires_in,
125
+ granted_tools: []
126
+ };
127
+ }
128
+ getSession(params) {
129
+ const uuid = String(params.app_session_uuid ?? "");
130
+ if (!uuid.startsWith("aps_")) throw withCode(new Error("'app_session_uuid' must start with 'aps_'"), AGENT_ERR_INVALID_REQUEST);
131
+ const ms = this.sessions.get(uuid);
132
+ if (!ms) throw withCode(new Error(`app_session_uuid=${uuid} not in dev cache (expired or never created)`), AGENT_ERR_SESSION_NOT_FOUND);
133
+ if (ms.expiresAt < Math.floor(Date.now() / 1e3)) {
134
+ this.sessions.delete(uuid);
135
+ throw withCode(new Error(`app_session_uuid=${uuid} expired — call session.create again`), AGENT_ERR_SESSION_NOT_FOUND);
136
+ }
137
+ return ms;
138
+ }
139
+ async sessionRun(params) {
140
+ const acc = requireAccount(this.opts.account);
141
+ const ms = this.getSession(params);
142
+ const content = params.content;
143
+ if (typeof content !== "string" || !content.trim()) throw withCode(new Error("'content' must be a non-empty string"), AGENT_ERR_INVALID_REQUEST);
144
+ const runId = params.run_id ?? `run_${Math.random().toString(16).slice(2, 18)}`;
145
+ const streamId = `strm_${Math.random().toString(16).slice(2, 18)}`;
146
+ const body = {
147
+ app_session_uuid: ms.appSessionUuid,
148
+ content,
149
+ run_id: runId,
150
+ stream: true
151
+ };
152
+ if (params.attachments !== void 0) body.attachments = params.attachments;
153
+ if (params.recursion_limit !== void 0) body.recursion_limit = params.recursion_limit;
154
+ if (params.allowed_tools !== void 0) body.allowed_tools = params.allowed_tools;
155
+ if (params.system !== void 0) body.system = params.system;
156
+ const mp = params.modelPreferences ?? params.model_preferences;
157
+ if (mp !== void 0) body.model_preferences = mp;
158
+ const url = `${hostOf(acc)}/api/v1/copilot/app/agent`;
159
+ const res = await fetch(url, {
160
+ method: "POST",
161
+ headers: {
162
+ "content-type": "application/json",
163
+ authorization: `Bearer ${ms.appSessionToken}`,
164
+ accept: "text/event-stream"
165
+ },
166
+ body: JSON.stringify(body)
167
+ });
168
+ if (!res.ok || !res.body) {
169
+ const text = await res.text().catch(() => "");
170
+ throw withCode(new Error(`agent/session.run failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
171
+ }
172
+ const frames = [];
173
+ let final = null;
174
+ const textAcc = [];
175
+ const reader = res.body.getReader();
176
+ const decoder = new TextDecoder();
177
+ let buf = "";
178
+ while (true) {
179
+ const { value, done } = await reader.read();
180
+ if (done) break;
181
+ buf += decoder.decode(value, { stream: true });
182
+ let idx;
183
+ while ((idx = buf.indexOf("\n\n")) >= 0) {
184
+ const frameRaw = buf.slice(0, idx);
185
+ buf = buf.slice(idx + 2);
186
+ const payload = parseSseFrame(frameRaw);
187
+ if (payload && typeof payload === "object") {
188
+ const frame = payload;
189
+ if (frame.seq === void 0) frame.seq = frames.length;
190
+ if (frame.stream_id === void 0) frame.stream_id = streamId;
191
+ frames.push(frame);
192
+ if (frame.event === "delta" && typeof frame.text === "string") textAcc.push(frame.text);
193
+ else if (frame.event === "final") final = frame;
194
+ if (frames.length > MAX_FRAMES_PER_RUN) throw withCode(new Error(`agent run exceeded ${MAX_FRAMES_PER_RUN} frames`), AGENT_ERR_RUN_TOO_LARGE);
195
+ }
196
+ }
197
+ }
198
+ if (buf.trim().length > 0) {
199
+ const payload = parseSseFrame(buf);
200
+ if (payload && typeof payload === "object") {
201
+ const frame = payload;
202
+ if (frame.seq === void 0) frame.seq = frames.length;
203
+ if (frame.stream_id === void 0) frame.stream_id = streamId;
204
+ frames.push(frame);
205
+ if (frame.event === "final") final = frame;
206
+ }
207
+ }
208
+ if (final === null && textAcc.length > 0) {
209
+ final = {
210
+ event: "final",
211
+ text: textAcc.join(""),
212
+ stream_id: streamId,
213
+ seq: frames.length,
214
+ synthesized: true
215
+ };
216
+ frames.push(final);
217
+ }
218
+ return {
219
+ run_id: runId,
220
+ stream_id: streamId,
221
+ frames,
222
+ final,
223
+ frame_count: frames.length
224
+ };
225
+ }
226
+ async sessionCancel(params) {
227
+ const acc = requireAccount(this.opts.account);
228
+ const ms = this.getSession(params);
229
+ const runId = params.run_id;
230
+ if (typeof runId !== "string" || !runId.trim()) throw withCode(new Error("'run_id' required"), AGENT_ERR_INVALID_REQUEST);
231
+ const url = `${hostOf(acc)}/api/v1/copilot/app/agent/cancel`;
232
+ const res = await fetch(url, {
233
+ method: "POST",
234
+ headers: {
235
+ "content-type": "application/json",
236
+ authorization: `Bearer ${ms.appSessionToken}`
237
+ },
238
+ body: JSON.stringify({
239
+ app_session_uuid: ms.appSessionUuid,
240
+ run_id: runId
241
+ })
242
+ });
243
+ if (!res.ok) {
244
+ const text = await res.text().catch(() => "");
245
+ throw withCode(new Error(`agent/session.cancel failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
246
+ }
247
+ return await res.json().catch(() => ({}));
248
+ }
249
+ async sessionHistory(params) {
250
+ const acc = requireAccount(this.opts.account);
251
+ const ms = this.getSession(params);
252
+ const limit = Math.min(200, Math.max(1, Number(params.limit ?? 50)));
253
+ const cursor = params.cursor ?? null;
254
+ const url = new URL(`${hostOf(acc)}/api/v1/copilot/app/sessions/${encodeURIComponent(ms.appSessionUuid)}/history`);
255
+ url.searchParams.set("limit", String(limit));
256
+ if (cursor) url.searchParams.set("cursor", String(cursor));
257
+ const res = await fetch(url, {
258
+ method: "GET",
259
+ headers: { authorization: `Bearer ${ms.appSessionToken}` }
260
+ });
261
+ if (res.status === 404) return {
262
+ messages: [],
263
+ cursor: null
264
+ };
265
+ if (!res.ok) {
266
+ const text = await res.text().catch(() => "");
267
+ throw withCode(new Error(`agent/session.history failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
268
+ }
269
+ return await res.json();
270
+ }
271
+ async sessionDelete(params) {
272
+ const acc = requireAccount(this.opts.account);
273
+ const uuid = String(params.app_session_uuid ?? "");
274
+ if (!uuid.startsWith("aps_")) throw withCode(new Error("'app_session_uuid' must start with 'aps_'"), AGENT_ERR_INVALID_REQUEST);
275
+ const ms = this.sessions.get(uuid);
276
+ this.sessions.delete(uuid);
277
+ if (!ms) return {
278
+ deleted: true,
279
+ app_session_uuid: uuid,
280
+ already_gone: true
281
+ };
282
+ const url = `${hostOf(acc)}/api/v1/copilot/app/sessions/${encodeURIComponent(uuid)}`;
283
+ const res = await fetch(url, {
284
+ method: "DELETE",
285
+ headers: { authorization: `Bearer ${ms.appSessionToken}` }
286
+ });
287
+ if (!res.ok && res.status !== 404) {
288
+ const text = await res.text().catch(() => "");
289
+ throw withCode(new Error(`agent/session.delete failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
290
+ }
291
+ return {
292
+ deleted: true,
293
+ app_session_uuid: uuid
294
+ };
295
+ }
296
+ /**
297
+ * L1 `agent/complete` — single-turn completion convenience. Mints a
298
+ * transient kind=complete session, forwards to `/copilot/app/complete`,
299
+ * returns the assistant message.
300
+ */
301
+ async complete(params) {
302
+ const acc = requireAccount(this.opts.account);
303
+ const messages = params.messages;
304
+ if (!Array.isArray(messages) || messages.length === 0) throw withCode(new Error("'messages' must be a non-empty array"), AGENT_ERR_INVALID_REQUEST);
305
+ const minted = await mintAppSession(acc.host, {
306
+ pat: acc.pat,
307
+ kind: "complete",
308
+ app_slug: this.requireAppSlug()
309
+ });
310
+ const body = { messages };
311
+ if (params.system) body.system = params.system;
312
+ if (params.max_tokens != null) body.max_tokens = params.max_tokens;
313
+ if (params.maxTokens != null) body.max_tokens = params.maxTokens;
314
+ if (params.temperature != null) body.temperature = params.temperature;
315
+ if (params.stop) body.stop = params.stop;
316
+ if (params.model_hint) body.model_hint = params.model_hint;
317
+ if (params.metadata) body.metadata = params.metadata;
318
+ const res = await fetch(`${hostOf(acc)}/api/v1/copilot/app/complete`, {
319
+ method: "POST",
320
+ headers: {
321
+ "content-type": "application/json",
322
+ authorization: `Bearer ${minted.app_session_token}`
323
+ },
324
+ body: JSON.stringify(body)
325
+ });
326
+ if (!res.ok) {
327
+ const text = await res.text().catch(() => "");
328
+ throw withCode(new Error(`agent/complete failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
329
+ }
330
+ return await res.json();
331
+ }
332
+ };
333
+ function collectContent(params) {
334
+ const parts = [];
335
+ if (typeof params.content === "string") parts.push(params.content);
336
+ if (typeof params.system === "string") parts.push(params.system);
337
+ const msgs = params.messages;
338
+ if (Array.isArray(msgs)) for (const m of msgs) {
339
+ const c = m.content;
340
+ if (typeof c === "string") parts.push(c);
341
+ else if (c && typeof c === "object" && "text" in c) parts.push(String(c.text ?? ""));
342
+ }
343
+ return parts.join("\n");
344
+ }
345
+ function parseSseFrame(frame) {
346
+ const lines = frame.split(/\r?\n/);
347
+ const dataLines = [];
348
+ let eventName = null;
349
+ for (const raw of lines) {
350
+ if (!raw || raw.startsWith(":")) continue;
351
+ if (raw.startsWith("data:")) dataLines.push(raw.slice(5).trimStart());
352
+ else if (raw.startsWith("event:")) eventName = raw.slice(6).trim();
353
+ }
354
+ if (dataLines.length === 0) return null;
355
+ const data = dataLines.join("\n");
356
+ try {
357
+ const obj = JSON.parse(data);
358
+ if (eventName && obj && typeof obj === "object" && !("event" in obj)) return {
359
+ ...obj,
360
+ event: eventName
361
+ };
362
+ return obj;
363
+ } catch {
364
+ return {
365
+ event: eventName ?? "raw",
366
+ text: data
367
+ };
368
+ }
369
+ }
370
+
371
+ //#endregion
372
+ export { AgentBridge };
@@ -1,5 +1,5 @@
1
1
  import { getAccount } from "./credentials-BTv2IfUZ.js";
2
- import { listDevApps } from "./dev-app-cache-BMfOlTHd.js";
2
+ import { listDevApps } from "./dev-app-cache-DAHcq46m.js";
3
3
  import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
4
4
 
5
5
  //#region src/commands/apps.ts
@@ -0,0 +1,3 @@
1
+ import { PINNED_RUNTIME_VERSION, PythonBridge } from "./bridge-nqQ3-j-t.js";
2
+
3
+ export { PINNED_RUNTIME_VERSION, PythonBridge };
@@ -9,7 +9,7 @@ import { createInterface } from "node:readline";
9
9
  * `uvx <pkg>@<version>` so end users always run the dispatcher version
10
10
  * the CLI was tested against.
11
11
  */
12
- const PINNED_RUNTIME_VERSION = "0.2.0a2";
12
+ const PINNED_RUNTIME_VERSION = "0.2.0a3";
13
13
  var PythonBridge = class {
14
14
  proc = null;
15
15
  nextId = 1;
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { printMascot } from "./mascot-wlYTJqMs.js";
2
3
  import { dirname, extname, join, relative, resolve } from "node:path";
3
4
  import { createRequire } from "node:module";
4
5
  import { Command } from "commander";
@@ -53,6 +54,7 @@ function runInit(opts) {
53
54
  }
54
55
  const tplRoot = templateRoot(opts.template);
55
56
  copyDirWithSubst(tplRoot, target, opts.slug);
57
+ printMascot(`scaffolded a fresh app — happy hacking!`);
56
58
  console.log(kleur.green(`✓ scaffolded "${opts.slug}" at ${target}`));
57
59
  console.log(kleur.gray(` next: cd ${opts.targetDir} && anna-app validate`));
58
60
  return 0;
@@ -423,6 +425,10 @@ function printResult(result) {
423
425
  const pkg = createRequire(import.meta.url)("../package.json");
424
426
  const program = new Command();
425
427
  program.name("anna-app").description("Anna App developer CLI (scaffold, validate, harness)").version(pkg.version);
428
+ const argv = process.argv.slice(2);
429
+ const wantsTopLevelHelp = argv.length === 0 || argv[0] === "-h" || argv[0] === "--help" || argv[0] === "help";
430
+ const tagline = wantsTopLevelHelp ? `v${pkg.version} — run \`anna-app help <command>\` for details` : `v${pkg.version} — ${argv.join(" ")}`;
431
+ printMascot(tagline);
426
432
  program.command("init <dir>").description("Scaffold a new Anna App project").option("--slug <slug>", "App slug (lowercase, hyphens)", "").option("--template <name>", "Template to use", "minimal").option("--force", "Overwrite existing dir if non-empty", false).action((dir, opts) => {
427
433
  const slug = opts.slug || dir.split(/[\\/]/).pop() || "my-anna-app";
428
434
  const code = runInit({
@@ -445,7 +451,7 @@ program.command("validate").description("Run schema + ACL checks on a manifest+b
445
451
  process.exit(code);
446
452
  });
447
453
  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)").action(async (opts) => {
448
- const { runDev, parseExecutaSpec } = await import("./dev-DoY58pBM.js");
454
+ const { runDev, parseExecutaSpec } = await import("./dev-BRlFgo2I.js");
449
455
  const cwd = opts.cwd ?? process.cwd();
450
456
  let executas;
451
457
  if (opts.executa && opts.executa.length > 0) {
@@ -479,7 +485,7 @@ program.command("dev").description("Run a local harness (in-process dispatcher +
479
485
  });
480
486
  const fixture = program.command("fixture").description("Inspect / replay harness recordings (Phase 6)");
481
487
  fixture.command("verify <file>").description("Schema + invariant checks on a harness JSONL recording").option("--json", "Emit machine-readable JSON", false).action(async (file, opts) => {
482
- const { runFixtureVerify } = await import("./fixture-BEu4LXLG.js");
488
+ const { runFixtureVerify } = await import("./fixture-CATHyLLI.js");
483
489
  const code = await runFixtureVerify({
484
490
  file,
485
491
  json: opts.json
@@ -487,7 +493,7 @@ fixture.command("verify <file>").description("Schema + invariant checks on a har
487
493
  process.exit(code);
488
494
  });
489
495
  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) => {
490
- const { runFixtureSummarize } = await import("./fixture-BEu4LXLG.js");
496
+ const { runFixtureSummarize } = await import("./fixture-CATHyLLI.js");
491
497
  const code = await runFixtureSummarize({
492
498
  file,
493
499
  json: opts.json
@@ -495,7 +501,7 @@ fixture.command("summarize <file>").description("Print a human-readable digest o
495
501
  process.exit(code);
496
502
  });
497
503
  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) => {
498
- const { runFixtureReplay } = await import("./fixture-BEu4LXLG.js");
504
+ const { runFixtureReplay } = await import("./fixture-CATHyLLI.js");
499
505
  const code = await runFixtureReplay({
500
506
  file,
501
507
  manifest: opts.manifest
@@ -503,12 +509,12 @@ fixture.command("replay <file>").description("Dry-run replay of a harness record
503
509
  process.exit(code);
504
510
  });
505
511
  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) => {
506
- const { runDoctor } = await import("./doctor-DP2UB10l.js");
512
+ const { runDoctor } = await import("./doctor-C8MWfLt8.js");
507
513
  const code = await runDoctor({ matrixNexusRoot: opts.matrixNexusRoot });
508
514
  process.exit(code);
509
515
  });
510
516
  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) => {
511
- const { runLogin } = await import("./login-dl1Zfny8.js");
517
+ const { runLogin } = await import("./login-CsIVbrmf.js");
512
518
  const code = await runLogin({
513
519
  host: opts.host,
514
520
  noBrowser: opts.browser === false
@@ -516,7 +522,7 @@ program.command("login").description("Device-flow login against a nexus host; sa
516
522
  process.exit(code);
517
523
  });
518
524
  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) => {
519
- const { runLogout } = await import("./logout-DablvlFs.js");
525
+ const { runLogout } = await import("./logout-gfmKQxMj.js");
520
526
  const code = await runLogout({
521
527
  host: opts.host,
522
528
  all: opts.all
@@ -524,18 +530,81 @@ program.command("logout").description("Remove a saved PAT entry").option("--host
524
530
  process.exit(code);
525
531
  });
526
532
  program.command("whoami").description("Show the current account (and any others)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
527
- const { runWhoami } = await import("./whoami-giXOY415.js");
533
+ const { runWhoami } = await import("./whoami-BS5wy-Nh.js");
528
534
  const code = await runWhoami({ json: opts.json });
529
535
  process.exit(code);
530
536
  });
531
537
  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) => {
532
- const { runAppsList } = await import("./apps-CDe6Fjq2.js");
538
+ const { runAppsList } = await import("./apps-Bt9CT5Sl.js");
533
539
  const code = await runAppsList({
534
540
  account: opts.account,
535
541
  json: opts.json
536
542
  });
537
543
  process.exit(code);
538
544
  });
545
+ const executa = program.command("executa").description("Standalone Executa plugin scaffolding + local runner");
546
+ 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) => {
547
+ const slug = opts.slug || dir.split(/[\\/]/).pop() || "my-executa";
548
+ const tpl = opts.template;
549
+ if (![
550
+ "python",
551
+ "node",
552
+ "go"
553
+ ].includes(tpl)) {
554
+ console.error(`✗ unknown template "${opts.template}" — expected python | node | go`);
555
+ process.exit(2);
556
+ }
557
+ const { runExecutaInit } = await import("./executa-init-COEmKDOE.js");
558
+ const code = runExecutaInit({
559
+ targetDir: dir,
560
+ slug,
561
+ template: tpl,
562
+ toolId: opts.toolId,
563
+ force: opts.force
564
+ });
565
+ process.exit(code);
566
+ });
567
+ executa.command("register").description("Register a standalone executa with nexus so `executa dev --storage real` (and future agent / sampling real-mode flows) can mint tokens for it").requiredOption("--tool-id <id>", "Executa tool_id (matches manifest.name)").option("--slug <slug>", "App slug (default: executa-<tool_id>; must be lowercase + hyphens)").option("--name <name>", "Human-readable display name (default: <tool_id>)").option("--account <host>", "Saved account host (default: current)").action(async (opts) => {
568
+ const { runExecutaRegister } = await import("./executa-register-66WKIwQQ.js");
569
+ const code = await runExecutaRegister(opts);
570
+ process.exit(code);
571
+ });
572
+ 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) => {
573
+ const { runExecutaDev } = await import("./executa-dev-4FZ7AJHR.js");
574
+ const storageMode = opts.storage === void 0 ? void 0 : (() => {
575
+ const m = opts.storage;
576
+ if (m === "off" || m === "memory" || m === "mock" || m === "real") return m;
577
+ console.error(`✗ --storage must be one of off | memory | mock | real (got: ${m})`);
578
+ process.exit(2);
579
+ })();
580
+ const code = await runExecutaDev({
581
+ dir: opts.dir,
582
+ spec: opts.spec,
583
+ describe: opts.describe,
584
+ health: opts.health,
585
+ invoke: opts.invoke,
586
+ args: opts.args,
587
+ json: opts.json,
588
+ noSampling: opts.sampling === false,
589
+ mockSampling: opts.mockSampling,
590
+ appSlug: opts.appSlug,
591
+ samplingAccount: opts.samplingAccount,
592
+ noAgent: opts.agent === false,
593
+ mockAgent: opts.mockAgent,
594
+ agentAccount: opts.agentAccount,
595
+ storage: storageMode,
596
+ mockStorage: opts.mockStorage,
597
+ storageAccount: opts.storageAccount,
598
+ storageScopes: opts.storageScopes,
599
+ noImage: opts.image === false,
600
+ mockImage: opts.mockImage,
601
+ imageAccount: opts.imageAccount,
602
+ noUpload: opts.upload === false,
603
+ mockUpload: opts.mockUpload,
604
+ uploadAccount: opts.uploadAccount
605
+ });
606
+ process.exit(code);
607
+ });
539
608
  program.parseAsync(process.argv).catch((e) => {
540
609
  console.error(e);
541
610
  process.exit(2);
@@ -0,0 +1,3 @@
1
+ import { parseExecutaSpec, runDev } from "./dev-C6v5yRV2.js";
2
+
3
+ export { parseExecutaSpec, runDev };
@@ -31,8 +31,8 @@ async function runDev(opts) {
31
31
  }
32
32
  const matrixNexusRoot = await resolveMatrixNexusRoot(opts.matrixNexusRoot, cwd);
33
33
  const mode = matrixNexusRoot ? "nexus-source" : "uvx";
34
- const { PythonBridge, PINNED_RUNTIME_VERSION } = await import("./bridge-BEHyfpPI.js");
35
- const { HarnessServer } = await import("./server-NXmiWJjX.js");
34
+ const { PythonBridge, PINNED_RUNTIME_VERSION } = await import("./bridge-AJilXBw2.js");
35
+ const { HarnessServer } = await import("./server-D8R6ppOp.js");
36
36
  const bridge = new PythonBridge({
37
37
  mode,
38
38
  matrixNexusRoot: matrixNexusRoot ?? void 0,
@@ -122,8 +122,8 @@ async function runDev(opts) {
122
122
  * registered `appSlug`.
123
123
  */
124
124
  async function resolveRealLlm(args) {
125
- const { getAccount } = await import("./credentials-CIOYq2Lm.js");
126
- const { ensureDevAppRegistered } = await import("./dev-app-cache-cXvO2XwQ.js");
125
+ const { getAccount } = await import("./credentials-DDqx6XMQ.js");
126
+ const { ensureDevAppRegistered } = await import("./dev-app-cache-DGF2Kuzd.js");
127
127
  const acc = getAccount(args.account);
128
128
  if (!acc) {
129
129
  console.error(red("✗ no developer PAT on disk — run `anna-app login --host <nexus-url>` first.\n (or use `--no-llm` / `--mock-llm <fixture>` to develop offline.)"));
@@ -0,0 +1,44 @@
1
+ import { canonicalHost, getAccount } from "./credentials-BTv2IfUZ.js";
2
+
3
+ //#region src/executa/dev-account.ts
4
+ /** Embed a JSON-RPC error code on a thrown Error so the runner can forward it. */
5
+ function withCode(err, code) {
6
+ err.rpcCode = code;
7
+ return err;
8
+ }
9
+ /**
10
+ * Resolve a saved developer PAT for the given account host (or the
11
+ * "current" one when omitted).
12
+ *
13
+ * @throws JSON-RPC `-32001` when no PAT is on disk or it has expired.
14
+ */
15
+ function requireAccount(account) {
16
+ const acc = getAccount(account);
17
+ if (!acc) throw withCode(new Error("no PAT on disk — run `anna-app login --host <nexus-url>` first (or use a `mock`/`off`/`memory` mode for the affected bridge)"), -32001);
18
+ if (acc.expires_at && acc.expires_at < Math.floor(Date.now() / 1e3)) throw withCode(new Error("PAT expired — run `anna-app login` again"), -32001);
19
+ return acc;
20
+ }
21
+ /** Return the canonical `https://host[:port]` form of a saved account. */
22
+ function hostOf(acc) {
23
+ return canonicalHost(acc.host);
24
+ }
25
+ /**
26
+ * Call `POST /api/v1/anna-apps/dev/session/mint` and parse the response.
27
+ * Thin wrapper used by both sampling and agent bridges.
28
+ */
29
+ async function mintAppSession(host, body) {
30
+ const url = `${canonicalHost(host)}/api/v1/anna-apps/dev/session/mint`;
31
+ const res = await fetch(url, {
32
+ method: "POST",
33
+ headers: { "content-type": "application/json" },
34
+ body: JSON.stringify(body)
35
+ });
36
+ if (!res.ok) {
37
+ const text = await res.text().catch(() => "");
38
+ throw withCode(new Error(`session.mint failed: HTTP ${res.status} ${text}`), -32e3);
39
+ }
40
+ return await res.json();
41
+ }
42
+
43
+ //#endregion
44
+ export { hostOf, mintAppSession, requireAccount, withCode };