@anna-ai/cli 0.1.40 → 0.1.42

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.
@@ -29,9 +29,16 @@ const CONTENT_TYPES = {
29
29
  md: "text/markdown"
30
30
  };
31
31
  const MAX_TOTAL_BYTES = 1024 * 1024 * 1024;
32
- const MAX_FILE_BYTES = 100 * 1024 * 1024;
32
+ const MAX_FILE_BYTES = 50 * 1024 * 1024;
33
33
  const MAX_FILES = 2e3;
34
34
  const UPLOAD_PARALLEL = 4;
35
+ const fmtMB = (n) => (n / 1024 / 1024).toFixed(2);
36
+ function totalSizeError(totalBytes) {
37
+ return new CliError(`bundle is ${fmtMB(totalBytes)} MB — exceeds the ${fmtMB(MAX_TOTAL_BYTES)} MB publish limit.\n Tip: preview/screenshot images usually shrink 5-10x when exported as WebP/AVIF\n thumbnails sized for their on-screen dimensions. Unchanged files are deduplicated\n server-side, so only new bytes upload on re-publish. Assets that must stay large\n belong in executa binary distribution, not the UI bundle.`, 4);
38
+ }
39
+ function fileSizeError(relativePath, byteSize) {
40
+ return new CliError(`bundle file exceeds the ${fmtMB(MAX_FILE_BYTES)} MB per-file limit: ${relativePath} (${fmtMB(byteSize)} MB).\n Compress/split the asset, or ship it via executa binary distribution.`, 4);
41
+ }
35
42
  function guessContentType(name) {
36
43
  const m = /\.([a-z0-9]+)$/i.exec(name);
37
44
  if (!m) return "application/octet-stream";
@@ -88,9 +95,9 @@ async function uploadAppBundle(params) {
88
95
  if (files.length === 0) throw new CliError(`bundle directory is empty: ${bundleDir}`, 4);
89
96
  if (files.length > MAX_FILES) throw new CliError(`too many bundle files (${files.length} > ${MAX_FILES})`, 4);
90
97
  const totalBytes = files.reduce((s, f) => s + f.entry.byte_size, 0);
91
- if (totalBytes > MAX_TOTAL_BYTES) throw new CliError(`bundle is ${(totalBytes / 1024 / 1024).toFixed(2)} MB — exceeds 50 MB limit`, 4);
98
+ if (totalBytes > MAX_TOTAL_BYTES) throw totalSizeError(totalBytes);
92
99
  const tooBig = files.find((f) => f.entry.byte_size > MAX_FILE_BYTES);
93
- if (tooBig) throw new CliError(`bundle file exceeds 10 MB: ${tooBig.relativePath} (${(tooBig.entry.byte_size / 1024 / 1024).toFixed(2)} MB)`, 4);
100
+ if (tooBig) throw fileSizeError(tooBig.relativePath, tooBig.entry.byte_size);
94
101
  if (!files.some((f) => f.relativePath === entryPath)) throw new CliError(`manifest entry "${entryPath}" not found in bundle dir ${bundleDir}`, 4);
95
102
  const fingerprint = manifestFingerprint(files);
96
103
  const existing = await getBundle(client, appId, versionId);
@@ -117,7 +124,10 @@ async function uploadAppBundle(params) {
117
124
  external_origins: externalOrigins
118
125
  });
119
126
  const proxyByPath = new Map(initResp.files.map((p) => [p.relative_path, p.proxy_upload_url]));
120
- const queue = [...files];
127
+ const skipSet = new Set(initResp.files.filter((p) => p.skip_upload).map((p) => p.relative_path));
128
+ const toUpload = files.filter((f) => !skipSet.has(f.relativePath));
129
+ if (skipSet.size > 0) log?.(` ♻ ${skipSet.size}/${files.length} files unchanged on server — skipped (dedup)`);
130
+ const queue = [...toUpload];
121
131
  const uploadOne = async () => {
122
132
  for (;;) {
123
133
  const f = queue.shift();
@@ -129,7 +139,7 @@ async function uploadAppBundle(params) {
129
139
  await uploadBundleFile(client, appId, versionId, f.relativePath, blob);
130
140
  }
131
141
  };
132
- await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, files.length) }, uploadOne));
142
+ await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, toUpload.length) }, uploadOne));
133
143
  const detail = await finalizeBundle(client, appId, versionId, false);
134
144
  log?.(` ✓ bundle finalized (${detail.file_count} files, status=${detail.status})`);
135
145
  return {
@@ -158,9 +168,9 @@ async function uploadWorkingBundle(params) {
158
168
  if (files.length === 0) throw new CliError(`bundle directory is empty: ${bundleDir}`, 4);
159
169
  if (files.length > MAX_FILES) throw new CliError(`too many bundle files (${files.length} > ${MAX_FILES})`, 4);
160
170
  const totalBytes = files.reduce((s, f) => s + f.entry.byte_size, 0);
161
- if (totalBytes > MAX_TOTAL_BYTES) throw new CliError(`bundle is ${(totalBytes / 1024 / 1024).toFixed(2)} MB — exceeds 50 MB limit`, 4);
171
+ if (totalBytes > MAX_TOTAL_BYTES) throw totalSizeError(totalBytes);
162
172
  const tooBig = files.find((f) => f.entry.byte_size > MAX_FILE_BYTES);
163
- if (tooBig) throw new CliError(`bundle file exceeds 10 MB: ${tooBig.relativePath} (${(tooBig.entry.byte_size / 1024 / 1024).toFixed(2)} MB)`, 4);
173
+ if (tooBig) throw fileSizeError(tooBig.relativePath, tooBig.entry.byte_size);
164
174
  if (!files.some((f) => f.relativePath === entryPath)) throw new CliError(`manifest entry "${entryPath}" not found in bundle dir ${bundleDir}`, 4);
165
175
  const fingerprint = manifestFingerprint(files);
166
176
  if (knownFingerprint && knownFingerprint === fingerprint) {
@@ -185,7 +195,10 @@ async function uploadWorkingBundle(params) {
185
195
  external_origins: externalOrigins
186
196
  });
187
197
  const proxyByPath = new Map(initResp.files.map((p) => [p.relative_path, p.proxy_upload_url]));
188
- const queue = [...files];
198
+ const skipSet = new Set(initResp.files.filter((p) => p.skip_upload).map((p) => p.relative_path));
199
+ const toUpload = files.filter((f) => !skipSet.has(f.relativePath));
200
+ if (skipSet.size > 0) log?.(` ♻ ${skipSet.size}/${files.length} files unchanged on server — skipped (dedup)`);
201
+ const queue = [...toUpload];
189
202
  const uploadOne = async () => {
190
203
  for (;;) {
191
204
  const f = queue.shift();
@@ -197,7 +210,7 @@ async function uploadWorkingBundle(params) {
197
210
  await uploadWorkingBundleFile(client, appId, f.relativePath, blob);
198
211
  }
199
212
  };
200
- await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, files.length) }, uploadOne));
213
+ await Promise.all(Array.from({ length: Math.min(UPLOAD_PARALLEL, toUpload.length) }, uploadOne));
201
214
  const detail = await finalizeWorkingBundle(client, appId, false);
202
215
  log?.(` ✓ working bundle staged (${detail.file_count} files, status=${detail.bundle_status})`);
203
216
  return {
@@ -10,10 +10,10 @@ import "./executas-CvQY_w2U.js";
10
10
  import "./executa-publish-DueD_0hh.js";
11
11
  import "./binary-upload-CKMU5Rfw.js";
12
12
  import { uploadExecutaBinaries } from "./executa-upload-binaries-DURJTXRI.js";
13
- import "./dev-CseybCH2.js";
14
- import "./executa-install-IP1xB9nw.js";
13
+ import "./dev-DEGmXqc_.js";
14
+ import "./executa-install-jSVVx6oh.js";
15
15
  import "./app-cache-Cr3tKbux.js";
16
- import { resolveAppBySlugOrCache } from "./working-orchestration-YUC3pKQx.js";
16
+ import { resolveAppBySlugOrCache } from "./working-orchestration-oFZwsWqQ.js";
17
17
  import { resolve } from "node:path";
18
18
  import { existsSync } from "node:fs";
19
19
  import { bold, cyan, dim, green, yellow } from "kleur/colors";
@@ -10,10 +10,10 @@ import "./executas-CvQY_w2U.js";
10
10
  import "./executa-publish-DueD_0hh.js";
11
11
  import "./binary-upload-CKMU5Rfw.js";
12
12
  import "./executa-upload-binaries-DURJTXRI.js";
13
- import "./dev-CseybCH2.js";
14
- import "./executa-install-IP1xB9nw.js";
13
+ import "./dev-DEGmXqc_.js";
14
+ import "./executa-install-jSVVx6oh.js";
15
15
  import "./app-cache-Cr3tKbux.js";
16
- import { resolveAppBySlugOrCache } from "./working-orchestration-YUC3pKQx.js";
16
+ import { resolveAppBySlugOrCache } from "./working-orchestration-oFZwsWqQ.js";
17
17
  import { dim, green, yellow } from "kleur/colors";
18
18
 
19
19
  //#region src/commands/apps-discard.ts
@@ -7,7 +7,7 @@ import { loadAppManifest } from "./manifest-CAF3_r1T.js";
7
7
  import { bumpVersion, bundleHash, manifestHash, rewriteVersion, runExecutaPublish } from "./executa-publish-DueD_0hh.js";
8
8
  import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-Cr3tKbux.js";
9
9
  import { syncAppListingMeta } from "./listing-meta-CtVtyaWu.js";
10
- import { isExistingDir, uploadAppBundle } from "./app-bundle-upload-B6TJeyOq.js";
10
+ import { isExistingDir, uploadAppBundle } from "./app-bundle-upload-DgoZIhQG.js";
11
11
  import { resolve } from "node:path";
12
12
  import { bold, cyan, dim, green, yellow } from "kleur/colors";
13
13
 
@@ -11,7 +11,7 @@ import "./binary-upload-CKMU5Rfw.js";
11
11
  import "./executa-upload-binaries-DURJTXRI.js";
12
12
  import "./app-cache-Cr3tKbux.js";
13
13
  import "./listing-meta-CtVtyaWu.js";
14
- import "./app-bundle-upload-B6TJeyOq.js";
15
- import { runAppsPublish } from "./apps-publish-bcqO88S0.js";
14
+ import "./app-bundle-upload-DgoZIhQG.js";
15
+ import { runAppsPublish } from "./apps-publish-l4itT8Dk.js";
16
16
 
17
17
  export { runAppsPublish };
@@ -10,12 +10,12 @@ import "./executas-CvQY_w2U.js";
10
10
  import { bumpVersion, bundleHash, manifestHash, rewriteVersion } from "./executa-publish-DueD_0hh.js";
11
11
  import "./binary-upload-CKMU5Rfw.js";
12
12
  import "./executa-upload-binaries-DURJTXRI.js";
13
- import "./dev-CseybCH2.js";
14
- import "./executa-install-IP1xB9nw.js";
13
+ import "./dev-DEGmXqc_.js";
14
+ import "./executa-install-jSVVx6oh.js";
15
15
  import "./app-cache-Cr3tKbux.js";
16
- import { installLocalBundledShims, resolveAppIdentity, resolveBundledExecutas } from "./working-orchestration-YUC3pKQx.js";
16
+ import { installLocalBundledShims, resolveAppIdentity, resolveBundledExecutas } from "./working-orchestration-oFZwsWqQ.js";
17
17
  import { syncAppListingMeta } from "./listing-meta-CtVtyaWu.js";
18
- import { isExistingDir, uploadWorkingBundle } from "./app-bundle-upload-B6TJeyOq.js";
18
+ import { isExistingDir, uploadWorkingBundle } from "./app-bundle-upload-DgoZIhQG.js";
19
19
  import { resolve } from "node:path";
20
20
  import { bold, cyan, dim, green, yellow } from "kleur/colors";
21
21
 
@@ -59,7 +59,7 @@ async function runAppsRelease(opts) {
59
59
  if (!target) {
60
60
  if (opts.allowCreate) {
61
61
  if (!opts.json) console.log(yellow(`version ${opts.version} not found remotely; running 'apps publish' first (--allow-create)…`));
62
- const { runAppsPublish } = await import("./apps-publish-DnU-B7YF.js");
62
+ const { runAppsPublish } = await import("./apps-publish-z0YSOUL0.js");
63
63
  const code = await runAppsPublish({
64
64
  cwd: opts.cwd,
65
65
  account: opts.account,
@@ -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.0a17";
12
+ const PINNED_RUNTIME_VERSION = "0.2.0a18";
13
13
  /**
14
14
  * Throwable from a {@link RequestHandler} to send a structured JSON-RPC
15
15
  * error back to the python bridge with a stable string ``code`` (e.g.
@@ -0,0 +1,3 @@
1
+ import { BridgeRequestError, PINNED_RUNTIME_VERSION, PythonBridge } from "./bridge-Bnpq2I_T.js";
2
+
3
+ export { PINNED_RUNTIME_VERSION, PythonBridge };
package/dist/cli.js CHANGED
@@ -473,8 +473,8 @@ program.command("validate").description("Run schema + ACL checks on a manifest+b
473
473
  const code = printResult(result);
474
474
  process.exit(code);
475
475
  });
476
- program.command("dev").description("Run a local harness (in-process dispatcher + iframe + SSE relay)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bundle <dir>", "bundle directory (default: ./bundle)").option("--slug <slug>", "App slug (overrides manifest.slug/name)").option("--view <name>", "View name to open (default: manifest default)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (auto-detected if omitted; can also use $ANNA_NEXUS_ROOT)").option("--port <number>", "HTTP port", "5180").option("--user-id <id>", "Harness user_id", "1").option("--cwd <dir>", "Project root (default: cwd)").option("--no-watch", "Disable bundle file watcher (default: enabled)").option("--executa <spec>", "Explicit executa registration; repeatable. Spec: comma-separated key=value (dir=<path>[,tool_id=<id>][,type=python|node|go|binary][,command=\"<argv>\"]). When only `dir=` is given, the executa is auto-detected from executa.json / pyproject.toml / package.json / go.mod. Overrides directory auto-discovery under <manifest-dir>/executas/.", (val, prev) => prev ? [...prev, val] : [val]).option("--no-llm", "Disable LLM bridge (anna.llm/agent return llm_disabled)").option("--mock-llm <fixture>", "Serve canned LLM responses from a JSONL fixture").option("--llm-account <host>", "Saved account host to use (default: current)").option("--llm-app-slug <slug>", "Override the manifest slug used to register / look up the dev AnnaApp (default: manifest.slug)").option("--storage <mode>", "Storage backend: \"legacy\" (in-memory runtime_state, default) or \"aps\" (real nexus APS via /api/v1/storage/* — requires `anna-app login`).", "legacy").action(async (opts) => {
477
- const { runDev, parseExecutaSpec } = await import("./dev-CnZO3d6s.js");
476
+ program.command("dev").description("Run a local harness (in-process dispatcher + iframe + SSE relay)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bundle <dir>", "bundle directory (default: ./bundle)").option("--slug <slug>", "App slug (overrides manifest.slug/name)").option("--view <name>", "View name to open (default: manifest default)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (auto-detected if omitted; can also use $ANNA_NEXUS_ROOT)").option("--port <number>", "HTTP port", "5180").option("--user-id <id>", "Harness user_id", "1").option("--cwd <dir>", "Project root (default: cwd)").option("--no-watch", "Disable bundle file watcher (default: enabled)").option("--executa <spec>", "Explicit executa registration; repeatable. Spec: comma-separated key=value (dir=<path>[,tool_id=<id>][,type=python|node|go|binary][,command=\"<argv>\"]). When only `dir=` is given, the executa is auto-detected from executa.json / pyproject.toml / package.json / go.mod. Overrides directory auto-discovery under <manifest-dir>/executas/.", (val, prev) => prev ? [...prev, val] : [val]).option("--no-llm", "Disable LLM bridge (anna.llm/agent return llm_disabled)").option("--mock-llm <fixture>", "Serve canned LLM responses from a JSONL fixture").option("--llm-account <host>", "Saved account host to use (default: current)").option("--llm-app-slug <slug>", "Override the manifest slug used to register / look up the dev AnnaApp (default: manifest.slug)").option("--storage <mode>", "Storage backend: \"legacy\" (in-memory runtime_state, default) or \"aps\" (real nexus APS via /api/v1/storage/* — requires `anna-app login`).", "legacy").option("--web <mode>", "Web backend: \"local\" (keyless ddgs + stdlib fetcher, free, default) or \"real\" (platform web service via /api/v1/copilot/app/web/* — production provider chain + normal CU billing; requires `anna-app login`).", "local").action(async (opts) => {
477
+ const { runDev, parseExecutaSpec } = await import("./dev-C0sHLKn_.js");
478
478
  const cwd = opts.cwd ?? process.cwd();
479
479
  let executas;
480
480
  if (opts.executa && opts.executa.length > 0) {
@@ -503,7 +503,8 @@ program.command("dev").description("Run a local harness (in-process dispatcher +
503
503
  mockLlm: opts.mockLlm,
504
504
  llmAccount: opts.llmAccount,
505
505
  llmAppSlug: opts.llmAppSlug,
506
- storageMode: opts.storage
506
+ storageMode: opts.storage,
507
+ webMode: opts.web
507
508
  });
508
509
  process.exit(code);
509
510
  });
@@ -533,7 +534,7 @@ fixture.command("replay <file>").description("Dry-run replay of a harness record
533
534
  process.exit(code);
534
535
  });
535
536
  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) => {
536
- const { runDoctor } = await import("./doctor-7XhZ7A4o.js");
537
+ const { runDoctor } = await import("./doctor-Dm6TNsq6.js");
537
538
  const code = await runDoctor({ matrixNexusRoot: opts.matrixNexusRoot });
538
539
  process.exit(code);
539
540
  });
@@ -596,7 +597,7 @@ executa.command("register").description("Register a HARNESS AnnaApp(kind=executa
596
597
  process.exit(code);
597
598
  });
598
599
  executa.command("install").description("Install a local-dev shim for an Executa under its minted tool_id so the Agent can discover it via 'Rediscover Local' (for distribution_type: local). Resolves the tool_id from .anna/executa.json (written by `executa publish` / `apps push`) unless --tool-id is given.").option("--dir <path>", "Executa project dir (default: CWD)").option("--tool-id <id>", "Install the shim under this exact id (default: minted id from .anna/executa.json, else executa.json tool_id)").option("--bin-dir <path>", "Install dir for the shim (default: ~/.anna/executa/bin)").option("--force", "Overwrite an existing shim of the same name", false).option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
599
- const { runExecutaInstall } = await import("./executa-install-B-mkxkKd.js");
600
+ const { runExecutaInstall } = await import("./executa-install-Q8zSREKQ.js");
600
601
  const code = await runExecutaInstall({
601
602
  dir: opts.dir,
602
603
  toolId: opts.toolId,
@@ -607,7 +608,7 @@ executa.command("install").description("Install a local-dev shim for an Executa
607
608
  process.exit(code);
608
609
  });
609
610
  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("--sampling-unsupported-format", "Simulate a model without json_schema support — exercises responseFormat onUnsupported branches (-32010 / downgrade)").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) => {
610
- const { runExecutaDev } = await import("./executa-dev-AODQxG02.js");
611
+ const { runExecutaDev } = await import("./executa-dev-CnWCz6Of.js");
611
612
  const storageMode = opts.storage === void 0 ? void 0 : (() => {
612
613
  const m = opts.storage;
613
614
  if (m === "off" || m === "memory" || m === "mock" || m === "real") return m;
@@ -670,7 +671,7 @@ apps.command("publish").description("Publish or update an Anna App (manifest.jso
670
671
  console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
671
672
  process.exit(2);
672
673
  }
673
- const { runAppsPublish } = await import("./apps-publish-DnU-B7YF.js");
674
+ const { runAppsPublish } = await import("./apps-publish-z0YSOUL0.js");
674
675
  process.exit(await runAppsPublish({
675
676
  cwd: opts.cwd,
676
677
  manifest: opts.manifest,
@@ -704,7 +705,7 @@ apps.command("push").description("Upsert the mutable working draft (manifest + b
704
705
  process.exit(2);
705
706
  }
706
707
  }
707
- const { runAppsPush } = await import("./apps-push-XmXxOTDb.js");
708
+ const { runAppsPush } = await import("./apps-push-CDLYRDt8.js");
708
709
  process.exit(await runAppsPush({
709
710
  cwd: opts.cwd,
710
711
  manifest: opts.manifest,
@@ -724,7 +725,7 @@ apps.command("push").description("Upsert the mutable working draft (manifest + b
724
725
  }));
725
726
  });
726
727
  apps.command("cut <version>").description("Snapshot the working draft into an immutable version (freeze deps)").option("--changelog <text>", "Override the working draft changelog for this version").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--dry-run", "Resolve target but don't cut", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (version, opts) => {
727
- const { runAppsCut } = await import("./apps-cut-CK5eBabo.js");
728
+ const { runAppsCut } = await import("./apps-cut-Crx0WrpZ.js");
728
729
  process.exit(await runAppsCut({
729
730
  version,
730
731
  changelog: opts.changelog,
@@ -736,7 +737,7 @@ apps.command("cut <version>").description("Snapshot the working draft into an im
736
737
  }));
737
738
  });
738
739
  apps.command("discard").description("Drop the mutable working draft (leaves cut versions intact)").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--dry-run", "Resolve target but don't discard", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
739
- const { runAppsDiscard } = await import("./apps-discard-BpkNZTjG.js");
740
+ const { runAppsDiscard } = await import("./apps-discard-Bmj0-j_T.js");
740
741
  process.exit(await runAppsDiscard({
741
742
  slug: opts.slug,
742
743
  cwd: opts.cwd,
@@ -746,7 +747,7 @@ apps.command("discard").description("Drop the mutable working draft (leaves cut
746
747
  }));
747
748
  });
748
749
  apps.command("release <version>").description("Freeze & publish an existing remote version (go live)").option("--slug <slug>", "App slug (default: resolve from .anna/app.json cache)").option("--cwd <dir>", "Project root for identity cache (default: cwd)").option("--allow-create", "If the version isn't on the server, run 'apps publish' first, then release", false).option("--dry-run", "Resolve target + pre-flight but don't publish", false).option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (version, opts) => {
749
- const { runAppsRelease } = await import("./apps-release-CX7FtoOH.js");
750
+ const { runAppsRelease } = await import("./apps-release-DlXBYdbb.js");
750
751
  process.exit(await runAppsRelease({
751
752
  version,
752
753
  slug: opts.slug,
@@ -1000,7 +1001,7 @@ program.command("publish").description("Auto-detect cwd and publish (apps publis
1000
1001
  console.error(`✗ --bump must be patch|minor|major (got: ${opts.bump})`);
1001
1002
  process.exit(2);
1002
1003
  }
1003
- const { runTopLevelPublish } = await import("./publish-BQL5NDnd.js");
1004
+ const { runTopLevelPublish } = await import("./publish-BC5dFF_P.js");
1004
1005
  process.exit(await runTopLevelPublish({
1005
1006
  cwd: opts.cwd,
1006
1007
  bump: opts.bump,
@@ -0,0 +1,4 @@
1
+ import "./nexus-root-BlPwOusj.js";
2
+ import { parseExecutaSpec, runDev } from "./dev-DEGmXqc_.js";
3
+
4
+ export { parseExecutaSpec, runDev };
@@ -49,8 +49,20 @@ async function runDev(opts) {
49
49
  }
50
50
  process.env.ANNA_APP_RUNTIME_STORAGE_MODE = "aps";
51
51
  }
52
- const { PythonBridge, PINNED_RUNTIME_VERSION } = await import("./bridge-BsQ-pos9.js");
53
- const { HarnessServer } = await import("./server-DqTkDxEM.js");
52
+ const webMode = opts.webMode === "real" ? "real" : "local";
53
+ if (opts.webMode && opts.webMode !== "local" && opts.webMode !== "real") {
54
+ console.error(red(`✗ --web must be "local" or "real", got "${opts.webMode}"`));
55
+ return 2;
56
+ }
57
+ if (webMode === "real") {
58
+ if (opts.noLlm || opts.mockLlm) {
59
+ console.error(red("✗ --web real requires a real LLM bridge (PAT on disk); drop --no-llm / --mock-llm or switch back to --web local."));
60
+ return 2;
61
+ }
62
+ process.env.ANNA_APP_RUNTIME_WEB_MODE = "real";
63
+ }
64
+ const { PythonBridge, PINNED_RUNTIME_VERSION } = await import("./bridge-CL8HLo-I.js");
65
+ const { HarnessServer } = await import("./server-3aUfxWXU.js");
54
66
  const bridge = new PythonBridge({
55
67
  mode,
56
68
  matrixNexusRoot: matrixNexusRoot ?? void 0,
@@ -112,8 +124,10 @@ async function runDev(opts) {
112
124
  });
113
125
  if (llm === null) return 2;
114
126
  llm.storageMode = storageMode;
127
+ llm.webMode = webMode;
115
128
  console.log(` llm bridge ${dim(llm.mode === "off" ? "disabled (--no-llm)" : llm.mode === "mock" ? `mock (${opts.mockLlm})` : `real${opts.llmAccount ? ` [${opts.llmAccount}]` : ""} → app_slug=${llm.appSlug}`)}`);
116
129
  console.log(` storage backend ${dim(storageMode === "aps" ? "aps (real nexus APS via /api/v1/storage/*)" : "legacy (in-memory runtime_state)")}`);
130
+ console.log(` web backend ${dim(webMode === "real" ? "real (platform web service via /api/v1/copilot/app/web/* — normal CU billing)" : "local (keyless ddgs + stdlib SSRF fetcher, unbilled)")}`);
117
131
  const server = new HarnessServer({
118
132
  slug,
119
133
  manifest,
@@ -1,5 +1,5 @@
1
1
  import { findMatrixNexusRoot, nexusSchemaDir } from "./nexus-root-BlPwOusj.js";
2
- import { PINNED_RUNTIME_VERSION } from "./bridge-CpntGdy3.js";
2
+ import { PINNED_RUNTIME_VERSION } from "./bridge-Bnpq2I_T.js";
3
3
  import { resolve } from "node:path";
4
4
  import { existsSync, statSync } from "node:fs";
5
5
  import { spawnSync } from "node:child_process";
@@ -1,5 +1,5 @@
1
1
  import "./nexus-root-BlPwOusj.js";
2
- import { parseExecutaSpec } from "./dev-CseybCH2.js";
2
+ import { parseExecutaSpec } from "./dev-DEGmXqc_.js";
3
3
  import { isAbsolute, resolve } from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
  import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
@@ -88,7 +88,7 @@ async function runExecutaDev(opts) {
88
88
  scopes: opts.storageScopes ? opts.storageScopes.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
89
89
  pluginName: parsed.tool_id
90
90
  });
91
- const { ExecutaRunner } = await import("./runner-Bka_coxb.js");
91
+ const { ExecutaRunner } = await import("./runner-Dq3cluPM.js");
92
92
  const { ImageBridge } = await import("./image-B4UrufYH.js");
93
93
  const image = opts.noImage ? new ImageBridge({ mode: "off" }) : opts.mockImage ? new ImageBridge({
94
94
  mode: "mock",
@@ -0,0 +1,7 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import "./nexus-root-BlPwOusj.js";
3
+ import "./executa-cache-BFoUtb4J.js";
4
+ import "./dev-DEGmXqc_.js";
5
+ import { runExecutaInstall } from "./executa-install-jSVVx6oh.js";
6
+
7
+ export { runExecutaInstall };
@@ -1,5 +1,5 @@
1
1
  import { readExecutaIdentity } from "./executa-cache-BFoUtb4J.js";
2
- import { parseExecutaSpec } from "./dev-CseybCH2.js";
2
+ import { parseExecutaSpec } from "./dev-DEGmXqc_.js";
3
3
  import { isAbsolute, join, resolve } from "node:path";
4
4
  import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
5
5
  import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
@@ -11,8 +11,8 @@ import "./binary-upload-CKMU5Rfw.js";
11
11
  import "./executa-upload-binaries-DURJTXRI.js";
12
12
  import "./app-cache-Cr3tKbux.js";
13
13
  import "./listing-meta-CtVtyaWu.js";
14
- import "./app-bundle-upload-B6TJeyOq.js";
15
- import { runAppsPublish } from "./apps-publish-bcqO88S0.js";
14
+ import "./app-bundle-upload-DgoZIhQG.js";
15
+ import { runAppsPublish } from "./apps-publish-l4itT8Dk.js";
16
16
  import { resolve } from "node:path";
17
17
  import { existsSync } from "node:fs";
18
18
  import { red, yellow } from "kleur/colors";
@@ -204,7 +204,11 @@ var ExecutaRunner = class {
204
204
  async handleReverse(env) {
205
205
  const id = env.id;
206
206
  const method = env.method;
207
- const params = env.params ?? {};
207
+ let params = env.params ?? {};
208
+ if (params.context !== void 0) {
209
+ const { context: _context,...rest } = params;
210
+ params = rest;
211
+ }
208
212
  const respond = (body) => {
209
213
  if (id === void 0 || id === null) return;
210
214
  if (!this.proc) return;
@@ -1,5 +1,5 @@
1
1
  import { canonicalHost, getAccount } from "./credentials-BTv2IfUZ.js";
2
- import { BridgeRequestError } from "./bridge-CpntGdy3.js";
2
+ import { BridgeRequestError } from "./bridge-Bnpq2I_T.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";
@@ -99,6 +99,15 @@ const VALID_STORAGE_SCOPES = new Set([
99
99
  "app",
100
100
  "tool"
101
101
  ]);
102
+ /** ``anna.web.*`` methods forwarded to ``/api/v1/copilot/app/web/*`` when
103
+ * ``webMode === "real"``. Mirrors matrix-nexus ``copilot_app.py`` routes;
104
+ * wire shapes are identical to the iframe HOST API (app-web-search §2). */
105
+ const WEB_REAL_METHODS = new Set([
106
+ "search",
107
+ "fetch",
108
+ "image_search",
109
+ "image_fetch"
110
+ ]);
102
111
  var LlmBridge = class {
103
112
  mintedAuto = new Map();
104
113
  mintedAgent = new Map();
@@ -139,6 +148,7 @@ var LlmBridge = class {
139
148
  if (ns === "upload" && (method === "inline" || method === "negotiate" || method === "confirm")) return true;
140
149
  if (ns === "storage" && this.opts.storageMode === "aps" && this.opts.mode === "real" && STORAGE_APS_METHODS.has(method)) return true;
141
150
  if (ns === "files" && this.opts.storageMode === "aps" && this.opts.mode === "real" && Object.prototype.hasOwnProperty.call(FILES_APP_ROUTES, method)) return true;
151
+ if (ns === "web" && this.opts.webMode === "real" && this.opts.mode === "real" && WEB_REAL_METHODS.has(method)) return true;
142
152
  return false;
143
153
  }
144
154
  /** Back-compat static alias — older code paths still call
@@ -694,6 +704,14 @@ var LlmBridge = class {
694
704
  };
695
705
  }
696
706
  }
707
+ if (args.ns === "web" && WEB_REAL_METHODS.has(args.method)) {
708
+ const ms = await this.mintComplete(args.windowUuid);
709
+ const result = await this.postJson(`${canonicalHost(acc.host)}/api/v1/copilot/app/web/${args.method}`, ms.appSessionToken, args.args);
710
+ return {
711
+ ok: true,
712
+ result
713
+ };
714
+ }
697
715
  if (args.ns === "storage") {
698
716
  const sm = await this.mintStorage();
699
717
  const a = args.args;
@@ -1355,6 +1373,26 @@ var HarnessServer = class {
1355
1373
  "host.storage.files_delete",
1356
1374
  "storage",
1357
1375
  "files_delete"
1376
+ ],
1377
+ [
1378
+ "host.web.search",
1379
+ "web",
1380
+ "search"
1381
+ ],
1382
+ [
1383
+ "host.web.fetch",
1384
+ "web",
1385
+ "fetch"
1386
+ ],
1387
+ [
1388
+ "host.web.image_search",
1389
+ "web",
1390
+ "image_search"
1391
+ ],
1392
+ [
1393
+ "host.web.image_fetch",
1394
+ "web",
1395
+ "image_fetch"
1358
1396
  ]
1359
1397
  ];
1360
1398
  for (const [hostMethod, ns, dispatchMethod] of HOST_OUTBOUND_ROUTES) this.bridge.onRequest(hostMethod, async (params) => {
@@ -4,7 +4,7 @@ import { CliError } from "./client-D-_z1ALk.js";
4
4
  import { parseExecutaIdOverrides, readExecutasLock, substituteBundledRefs, validateBundledHandles, writeBundleToolIdSidecar, writeExecutasLock } from "./bundled-executas-rSIkaA6-.js";
5
5
  import { loadExecutaManifest } from "./manifest-CAF3_r1T.js";
6
6
  import { runExecutaPublish } from "./executa-publish-DueD_0hh.js";
7
- import { runExecutaInstall } from "./executa-install-IP1xB9nw.js";
7
+ import { runExecutaInstall } from "./executa-install-jSVVx6oh.js";
8
8
  import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-Cr3tKbux.js";
9
9
  import { join, resolve } from "node:path";
10
10
  import { dim, green, yellow } from "kleur/colors";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anna-ai/cli",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,3 +0,0 @@
1
- import { BridgeRequestError, PINNED_RUNTIME_VERSION, PythonBridge } from "./bridge-CpntGdy3.js";
2
-
3
- export { PINNED_RUNTIME_VERSION, PythonBridge };
@@ -1,4 +0,0 @@
1
- import "./nexus-root-BlPwOusj.js";
2
- import { parseExecutaSpec, runDev } from "./dev-CseybCH2.js";
3
-
4
- export { parseExecutaSpec, runDev };
@@ -1,7 +0,0 @@
1
- import "./credentials-BTv2IfUZ.js";
2
- import "./nexus-root-BlPwOusj.js";
3
- import "./executa-cache-BFoUtb4J.js";
4
- import "./dev-CseybCH2.js";
5
- import { runExecutaInstall } from "./executa-install-IP1xB9nw.js";
6
-
7
- export { runExecutaInstall };