@anna-ai/cli 0.1.29 → 0.1.32

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 (68) hide show
  1. package/dist/{_lifecycle-shared-BpSOfVCP.js → _lifecycle-shared-CUK2CE76.js} +1 -1
  2. package/dist/account-bqao7Gp5.js +30 -0
  3. package/dist/{agent-CaZVCPs6.js → agent-CzPryLi6.js} +1 -1
  4. package/dist/{app-bundle-upload-BhAYo6yj.js → app-bundle-upload-CqT9KUOl.js} +2 -2
  5. package/dist/{app-cache-Bl7cE5fm.js → app-cache-BqnTkxUw.js} +1 -0
  6. package/dist/{apps-CCdtLmxQ.js → apps-DlVsVHQ5.js} +46 -1
  7. package/dist/{apps-cut-BOhg9RHy.js → apps-cut-8870xpk-.js} +11 -11
  8. package/dist/{apps-destructive-DWF4eTHn.js → apps-destructive-B6RFjokU.js} +3 -3
  9. package/dist/{apps-discard-Dy3vzpCM.js → apps-discard-hVZxAQyj.js} +11 -11
  10. package/dist/{apps-3VcdHIBK.js → apps-dvknZYI2.js} +2 -2
  11. package/dist/{apps-grants-DgvymPBT.js → apps-grants-DnXCnkH5.js} +2 -2
  12. package/dist/{apps-publish-Dgi4lBlu.js → apps-publish-BKqVsS1W.js} +13 -7
  13. package/dist/apps-publish-Djpw5cfG.js +15 -0
  14. package/dist/{apps-push-DbUEFdCK.js → apps-push-B3wZr9B4.js} +18 -12
  15. package/dist/{apps-release-DJFG4BV3.js → apps-release-DZK30eF_.js} +4 -4
  16. package/dist/apps-rename-slug-D2BrWasG.js +62 -0
  17. package/dist/{apps-status-F6aVlzDW.js → apps-status-Ca6MdXZd.js} +2 -2
  18. package/dist/{apps-submit-review-BVmZlhmB.js → apps-submit-review-DMFQI8JZ.js} +3 -3
  19. package/dist/{apps-sync-meta-CQQC_Heb.js → apps-sync-meta-BOb6J_wz.js} +27 -18
  20. package/dist/{apps-versions-BIKsJzIT.js → apps-versions-D1lixrmM.js} +2 -2
  21. package/dist/{bridge-DAY7bsje.js → bridge-CbjqAm0H.js} +1 -1
  22. package/dist/bridge-CzHB_IsQ.js +3 -0
  23. package/dist/{bundled-executas-DeBhDjd8.js → bundled-executas-06keEPP_.js} +1 -1
  24. package/dist/cli.js +61 -40
  25. package/dist/dev-C3yWhzAp.js +4 -0
  26. package/dist/{dev-DXODERsf.js → dev-Re6NyNDS.js} +5 -5
  27. package/dist/{dev-app-cache-TSjL4D4n.js → dev-app-cache-Dv1dZ_bw.js} +1 -0
  28. package/dist/{doctor-DCsBgyRg.js → doctor-VrfEkedz.js} +1 -1
  29. package/dist/{executa-cache-Kx3rfQD-.js → executa-cache-B0O79Hpb.js} +1 -1
  30. package/dist/{executa-destructive-PL2ooHpZ.js → executa-destructive-Bz7vWxEO.js} +3 -3
  31. package/dist/{executa-dev-DEpBrEIH.js → executa-dev-B5Uz982M.js} +8 -8
  32. package/dist/{executa-install-BHQpOskj.js → executa-install-BW9jjY7u.js} +2 -2
  33. package/dist/executa-install-Cyid8R7r.js +7 -0
  34. package/dist/{executa-publish-CkPAB34b.js → executa-publish-Ah6J7jYd.js} +4 -4
  35. package/dist/executa-publish-DI_6M_ea.js +9 -0
  36. package/dist/{executa-reads-CjGZq1yP.js → executa-reads-BXDJWWw6.js} +2 -2
  37. package/dist/listing-meta-BlY7XMrw.js +117 -0
  38. package/dist/{manifest-Bljz8Y6T.js → manifest-DOMrcdCK.js} +16 -1
  39. package/dist/{publish-BYWuujP3.js → publish-GpvkaBIx.js} +11 -10
  40. package/dist/{runner-BuYbm-ex.js → runner-BxYjAmih.js} +9 -3
  41. package/dist/{server-F_VA-5tS.js → server-BMcGaUTy.js} +229 -5
  42. package/dist/{storage-CKTmE87u.js → storage-BkF4XyI0.js} +3 -3
  43. package/dist/{token-Cg7BZGp6.js → token-uedeaAy7.js} +1 -1
  44. package/dist/{working-orchestration-Pjm4YC_U.js → working-orchestration-1jzRuk34.js} +30 -13
  45. package/package.json +3 -3
  46. package/templates/executa/go/main.go +1 -1
  47. package/templates/executa/node/plugin.mjs +1 -1
  48. package/templates/executa/python/__SLUG_PY___plugin.py +1 -1
  49. package/templates/minimal/executas/__SLUG__/__SLUG_PY___plugin.py +1 -1
  50. package/dist/apps-publish-Do7M5je3.js +0 -14
  51. package/dist/bridge-6GIQG63S.js +0 -3
  52. package/dist/dev-CQkCFVXu.js +0 -4
  53. package/dist/executa-install-Bvf_Lvvg.js +0 -7
  54. package/dist/executa-publish-IXWSwva0.js +0 -9
  55. /package/dist/{bundled-executas-B6b8gIfp.js → bundled-executas-_H1A9M50.js} +0 -0
  56. /package/dist/{confirm-h_qMrx0I.js → confirm-BuT56E_B.js} +0 -0
  57. /package/dist/{dev-account-CGo8k9_2.js → dev-account-DqEFCmVg.js} +0 -0
  58. /package/dist/{executa-cache-CXiEgFZY.js → executa-cache-Do1D7xC0.js} +0 -0
  59. /package/dist/{executa-init-DXea7yRN.js → executa-init-D-kWINZ3.js} +0 -0
  60. /package/dist/{executa-register-BUiPzPIU.js → executa-register-D5kvSSz_.js} +0 -0
  61. /package/dist/{executas-CK3er6f9.js → executas-BhFed1B-.js} +0 -0
  62. /package/dist/{fixture-BvP5umlN.js → fixture-BUKQYR5e.js} +0 -0
  63. /package/dist/{host_upload-BXeHTgJs.js → host_upload-BVzAqvX1.js} +0 -0
  64. /package/dist/{image-CSEXEfD-.js → image-BLPyUbGQ.js} +0 -0
  65. /package/dist/{login-BGZjMAlh.js → login-DhgBgsfx.js} +0 -0
  66. /package/dist/{logout-mh2_QlyM.js → logout-VYDkLYAx.js} +0 -0
  67. /package/dist/{sampling-DwV7VPfT.js → sampling-XBIEYU4A.js} +0 -0
  68. /package/dist/{whoami-l_kIkfbI.js → whoami-C_x26b_k.js} +0 -0
@@ -0,0 +1,117 @@
1
+ import { patchApp, uploadAppLogo, uploadAppScreenshot } from "./apps-DlVsVHQ5.js";
2
+ import { CliError } from "./client-D-_z1ALk.js";
3
+ import { extname, resolve } from "node:path";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+
6
+ //#region src/publish/listing-meta.ts
7
+ const IMAGE_CONTENT_TYPES = {
8
+ ".svg": "image/svg+xml",
9
+ ".png": "image/png",
10
+ ".jpg": "image/jpeg",
11
+ ".jpeg": "image/jpeg",
12
+ ".webp": "image/webp",
13
+ ".gif": "image/gif"
14
+ };
15
+ function imageContentType(path) {
16
+ return IMAGE_CONTENT_TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
17
+ }
18
+ /**
19
+ * A listing asset is "remote" (forwarded verbatim) when it's an absolute
20
+ * http(s) URL or an inline `data:` URI. Anything else is treated as a
21
+ * project-relative local file to be uploaded to the CDN — same split the logo
22
+ * uses between `logoUrl` and `logoFile`.
23
+ */
24
+ function isRemoteAsset(src) {
25
+ return /^https?:\/\//i.test(src) || src.startsWith("data:");
26
+ }
27
+ /**
28
+ * Build the metadata payload from the manifest (only defined fields). The
29
+ * logo is handled separately and never appears here when sourced from a file.
30
+ */
31
+ function buildMeta(manifest) {
32
+ const meta = {};
33
+ const add = (key, value) => {
34
+ if (value !== void 0 && value !== null && value !== "") meta[key] = value;
35
+ };
36
+ add("name", manifest.name);
37
+ add("category", manifest.category);
38
+ add("tagline", manifest.tagline);
39
+ add("description", manifest.description);
40
+ add("cover_url", manifest.coverUrl);
41
+ add("homepage_url", manifest.homepageUrl);
42
+ add("support_url", manifest.supportUrl);
43
+ add("privacy_url", manifest.privacyUrl);
44
+ add("pricing_model", manifest.pricingModel);
45
+ return meta;
46
+ }
47
+ /**
48
+ * Sync the app's store-listing metadata (and logo) from the local manifest.
49
+ *
50
+ * Returns a plan/result. When `dryRun` is set, no network calls are made and
51
+ * the returned object reflects what *would* be sent.
52
+ */
53
+ async function syncAppListingMeta(client, appId, manifest, opts) {
54
+ const meta = buildMeta(manifest);
55
+ const localLogo = opts.logoOverride ?? manifest.logoFile;
56
+ let logoUpload;
57
+ if (localLogo) {
58
+ const logoPath = resolve(opts.cwd, localLogo);
59
+ if (!existsSync(logoPath)) throw new CliError(`logo file not found: ${logoPath}`, 4);
60
+ logoUpload = { source: localLogo };
61
+ } else if (manifest.logoUrl) meta.logo_url = manifest.logoUrl;
62
+ const screenshotSources = manifest.screenshots ?? [];
63
+ const screenshotPlan = screenshotSources.map((src) => {
64
+ const remote = isRemoteAsset(src);
65
+ if (!remote && !existsSync(resolve(opts.cwd, src))) throw new CliError(`screenshot file not found: ${resolve(opts.cwd, src)}`, 4);
66
+ return {
67
+ source: src,
68
+ remote
69
+ };
70
+ });
71
+ const localScreenshots = screenshotPlan.filter((s) => !s.remote);
72
+ const noop = Object.keys(meta).length === 0 && !logoUpload && screenshotSources.length === 0;
73
+ if (opts.dryRun || noop) {
74
+ if (screenshotSources.length > 0) meta.screenshots = screenshotSources;
75
+ return {
76
+ meta,
77
+ logoUpload,
78
+ screenshotUploads: localScreenshots.length > 0 ? localScreenshots.map((s) => ({ source: s.source })) : void 0,
79
+ noop
80
+ };
81
+ }
82
+ const screenshotUploads = [];
83
+ const finalScreenshots = [];
84
+ for (const item of screenshotPlan) {
85
+ if (item.remote) {
86
+ finalScreenshots.push(item.source);
87
+ continue;
88
+ }
89
+ const shotPath = resolve(opts.cwd, item.source);
90
+ const bytes = readFileSync(shotPath);
91
+ const blob = new Blob([new Uint8Array(bytes)], { type: imageContentType(shotPath) });
92
+ const res = await uploadAppScreenshot(client, appId, blob);
93
+ finalScreenshots.push(res.screenshot_url);
94
+ screenshotUploads.push({
95
+ source: item.source,
96
+ screenshot_url: res.screenshot_url
97
+ });
98
+ }
99
+ if (finalScreenshots.length > 0) meta.screenshots = finalScreenshots;
100
+ if (Object.keys(meta).length > 0) await patchApp(client, appId, meta);
101
+ if (logoUpload) {
102
+ const logoPath = resolve(opts.cwd, logoUpload.source);
103
+ const bytes = readFileSync(logoPath);
104
+ const blob = new Blob([new Uint8Array(bytes)], { type: imageContentType(logoPath) });
105
+ const res = await uploadAppLogo(client, appId, blob);
106
+ logoUpload.logo_url = res.logo_url;
107
+ }
108
+ return {
109
+ meta,
110
+ logoUpload,
111
+ screenshotUploads: screenshotUploads.length > 0 ? screenshotUploads : void 0,
112
+ noop
113
+ };
114
+ }
115
+
116
+ //#endregion
117
+ export { syncAppListingMeta };
@@ -42,6 +42,7 @@ function loadAppManifest(cwd, manifestPath) {
42
42
  const metadataRaw = appJsonRaw ?? raw;
43
43
  const metadataLabel = hasAppJson ? "app.json" : "manifest.json";
44
44
  const pickMeta = (k) => (appJsonRaw && pickString(appJsonRaw, k)) ?? pickString(raw, k);
45
+ const pickMetaArray = (k) => (appJsonRaw && pickStringArray(appJsonRaw, k)) ?? pickStringArray(raw, k);
45
46
  const slug = pickMeta("slug");
46
47
  const name = pickMeta("name") ?? slug ?? "";
47
48
  const version = pickMeta("version") ?? "0.1.0";
@@ -63,7 +64,15 @@ function loadAppManifest(cwd, manifestPath) {
63
64
  category: pickMeta("category"),
64
65
  tagline: pickMeta("tagline"),
65
66
  description: pickMeta("description"),
66
- changelog: pickMeta("changelog")
67
+ changelog: pickMeta("changelog"),
68
+ logoUrl: pickMeta("logo_url"),
69
+ logoFile: pickMeta("logo_file"),
70
+ coverUrl: pickMeta("cover_url"),
71
+ screenshots: pickMetaArray("screenshots"),
72
+ homepageUrl: pickMeta("homepage_url"),
73
+ supportUrl: pickMeta("support_url"),
74
+ privacyUrl: pickMeta("privacy_url"),
75
+ pricingModel: pickMeta("pricing_model")
67
76
  };
68
77
  }
69
78
  const HANDLE_RE = /^[a-z0-9][a-z0-9-]{0,62}$/;
@@ -242,6 +251,12 @@ function pickString(o, k) {
242
251
  const v = o[k];
243
252
  return typeof v === "string" ? v : void 0;
244
253
  }
254
+ function pickStringArray(o, k) {
255
+ const v = o[k];
256
+ if (!Array.isArray(v)) return void 0;
257
+ const out = v.filter((x) => typeof x === "string");
258
+ return out.length > 0 ? out : void 0;
259
+ }
245
260
 
246
261
  //#endregion
247
262
  export { loadAppManifest, loadExecutaManifest };
@@ -1,15 +1,16 @@
1
1
  import "./credentials-BTv2IfUZ.js";
2
- import "./apps-CCdtLmxQ.js";
2
+ import "./apps-DlVsVHQ5.js";
3
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";
4
+ import { withErrorHandling } from "./_lifecycle-shared-CUK2CE76.js";
5
+ import "./bundled-executas-_H1A9M50.js";
6
+ import "./executas-BhFed1B-.js";
7
+ import "./executa-cache-Do1D7xC0.js";
8
+ import { runExecutaPublish } from "./executa-publish-Ah6J7jYd.js";
9
+ import "./manifest-DOMrcdCK.js";
10
+ import "./app-cache-BqnTkxUw.js";
11
+ import "./listing-meta-BlY7XMrw.js";
12
+ import "./app-bundle-upload-CqT9KUOl.js";
13
+ import { runAppsPublish } from "./apps-publish-BKqVsS1W.js";
13
14
  import { resolve } from "node:path";
14
15
  import { existsSync } from "node:fs";
15
16
  import { red, yellow } from "kleur/colors";
@@ -2,10 +2,16 @@ import { spawn } from "node:child_process";
2
2
  import { createInterface } from "node:readline";
3
3
 
4
4
  //#region src/executa/runner.ts
5
- /** Spec describing where the host advertises its capabilities. */
5
+ /**
6
+ * Host `initialize` request params. Mirrors the production matrix Agent
7
+ * (`matrix/src/executa/loader.py::negotiate`), which sends camelCase
8
+ * `clientInfo` + `capabilities`. The CLI advertises a richer capability
9
+ * set because the dev runner also brokers agent/storage/image/upload
10
+ * reverse RPCs.
11
+ */
6
12
  const HOST_INITIALIZE_PARAMS = {
7
13
  protocolVersion: "2.0",
8
- client_capabilities: {
14
+ capabilities: {
9
15
  sampling: {},
10
16
  agent: {},
11
17
  storage: {},
@@ -13,7 +19,7 @@ const HOST_INITIALIZE_PARAMS = {
13
19
  "image.edit": {},
14
20
  upload: {}
15
21
  },
16
- client_info: {
22
+ clientInfo: {
17
23
  name: "anna-app-cli",
18
24
  version: "executa-dev"
19
25
  }
@@ -1,5 +1,5 @@
1
1
  import { canonicalHost, getAccount } from "./credentials-BTv2IfUZ.js";
2
- import { BridgeRequestError } from "./bridge-DAY7bsje.js";
2
+ import { BridgeRequestError } from "./bridge-CbjqAm0H.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";
@@ -128,7 +128,7 @@ var LlmBridge = class {
128
128
  * - ``aps``: the bridge claims storage and forwards to real nexus APS
129
129
  * via ``/api/v1/storage/*`` with a Bearer ``storage_token``. */
130
130
  handles(ns, method) {
131
- if (ns === "llm" && (method === "complete" || method === "embed")) return true;
131
+ if (ns === "llm" && (method === "complete" || method === "embed" || method === "stream")) return true;
132
132
  if (ns === "agent" && method.startsWith("session.")) return true;
133
133
  if (ns === "image" && (method === "generate" || method === "edit")) return true;
134
134
  if (ns === "upload" && (method === "inline" || method === "negotiate" || method === "confirm")) return true;
@@ -140,7 +140,7 @@ var LlmBridge = class {
140
140
  * ``LlmBridge.handles(ns, method)`` without an instance. Returns the
141
141
  * legacy (no-storage) decision since options aren't available here. */
142
142
  static handles(ns, method) {
143
- if (ns === "llm" && (method === "complete" || method === "embed")) return true;
143
+ if (ns === "llm" && (method === "complete" || method === "embed" || method === "stream")) return true;
144
144
  if (ns === "agent" && method.startsWith("session.")) return true;
145
145
  if (ns === "image" && (method === "generate" || method === "edit")) return true;
146
146
  if (ns === "upload" && (method === "inline" || method === "negotiate" || method === "confirm")) return true;
@@ -453,6 +453,25 @@ var LlmBridge = class {
453
453
  result
454
454
  };
455
455
  }
456
+ if (args.ns === "llm" && args.method === "stream") {
457
+ const ms = await this.mintComplete(args.windowUuid);
458
+ const sid = `dev-${++this.streamCounter}`;
459
+ this.pumpCompleteStream({
460
+ host: acc.host,
461
+ token: ms.appSessionToken,
462
+ body: args.args,
463
+ streamId: sid,
464
+ windowUuid: args.windowUuid,
465
+ onEvent: args.onEvent
466
+ });
467
+ return {
468
+ ok: true,
469
+ result: {
470
+ stream_id: sid,
471
+ run_id: String(args.args.run_id ?? sid)
472
+ }
473
+ };
474
+ }
456
475
  if (args.ns === "agent") switch (args.method) {
457
476
  case "session.create": {
458
477
  const ms = await this.mintAgent(args.args);
@@ -846,7 +865,24 @@ var LlmBridge = class {
846
865
  }
847
866
  /** Consume an SSE response and forward each frame as `rpc.stream`. */
848
867
  async pumpAgentRun(args) {
849
- const url = `${canonicalHost(args.host)}/api/v1/copilot/app/agent`;
868
+ await this.pumpSse({
869
+ ...args,
870
+ url: `${canonicalHost(args.host)}/api/v1/copilot/app/agent`
871
+ });
872
+ }
873
+ /** L1 streaming: consume ``/app/complete/stream`` SSE and forward each
874
+ * ``{event:"model_token"|"complete"}`` frame as ``rpc.stream`` (mirrors
875
+ * ``pumpAgentRun``). */
876
+ async pumpCompleteStream(args) {
877
+ await this.pumpSse({
878
+ ...args,
879
+ url: `${canonicalHost(args.host)}/api/v1/copilot/app/complete/stream`
880
+ });
881
+ }
882
+ /** Shared SSE→rpc.stream pump used by both L1 (`llm.stream`) and L2
883
+ * (`agent.session.run`) — POSTs the body, reassembles `data:` frames,
884
+ * and re-emits each as an ordered `rpc.stream` event. */
885
+ async pumpSse(args) {
850
886
  let seq = 0;
851
887
  const emit = (payload, done) => {
852
888
  seq += 1;
@@ -859,7 +895,7 @@ var LlmBridge = class {
859
895
  });
860
896
  };
861
897
  try {
862
- const res = await fetch(url, {
898
+ const res = await fetch(args.url, {
863
899
  method: "POST",
864
900
  headers: {
865
901
  "content-type": "application/json",
@@ -951,6 +987,187 @@ const MIME = {
951
987
  ".woff2": "font/woff2",
952
988
  ".map": "application/json"
953
989
  };
990
+ function makeAppsStub() {
991
+ const catalog = [
992
+ {
993
+ id: 1,
994
+ slug: "finder",
995
+ qualified_ref: "@anna/finder",
996
+ name: "Finder",
997
+ tagline: "Browse, preview and curate APS storage.",
998
+ logo_url: null,
999
+ category: "utilities",
1000
+ is_enabled: true,
1001
+ installed_version: "0.0.1",
1002
+ latest_version: "0.0.1",
1003
+ update_available: false,
1004
+ deck_pinned: true,
1005
+ deck_order: 0
1006
+ },
1007
+ {
1008
+ id: 2,
1009
+ slug: "avalon",
1010
+ qualified_ref: "@anna/avalon",
1011
+ name: "Avalon",
1012
+ tagline: "A 5-player social-deduction game.",
1013
+ logo_url: null,
1014
+ category: "game",
1015
+ is_enabled: true,
1016
+ installed_version: "0.0.1",
1017
+ latest_version: "0.0.1",
1018
+ update_available: false,
1019
+ deck_pinned: false,
1020
+ deck_order: null
1021
+ },
1022
+ {
1023
+ id: 3,
1024
+ slug: "apps",
1025
+ qualified_ref: "@anna/apps",
1026
+ name: "Apps",
1027
+ tagline: "A Launchpad-style launcher for your installed apps.",
1028
+ logo_url: null,
1029
+ category: "utilities",
1030
+ is_enabled: true,
1031
+ installed_version: "0.0.1",
1032
+ latest_version: "0.0.1",
1033
+ update_available: false,
1034
+ deck_pinned: true,
1035
+ deck_order: 1
1036
+ }
1037
+ ];
1038
+ const resolve$1 = (ref) => {
1039
+ if (typeof ref === "number") return catalog.find((a) => a.id === ref);
1040
+ const s = String(ref ?? "").trim();
1041
+ if (/^\d+$/.test(s)) return catalog.find((a) => a.id === Number(s));
1042
+ return catalog.find((a) => a.qualified_ref === s || a.slug === s);
1043
+ };
1044
+ const deck = () => catalog.filter((a) => a.deck_pinned).sort((a, b) => (a.deck_order ?? 0) - (b.deck_order ?? 0));
1045
+ const nextOrder = () => deck().reduce((m, a) => Math.max(m, a.deck_order ?? 0), -1) + 1;
1046
+ const handle = (method, args) => {
1047
+ switch (method) {
1048
+ case "list": {
1049
+ const q = String(args.search ?? args.query ?? "").trim().toLowerCase();
1050
+ const items = q ? catalog.filter((a) => a.name.toLowerCase().includes(q) || a.slug.toLowerCase().includes(q) || a.tagline.toLowerCase().includes(q) || a.category.toLowerCase().includes(q)) : catalog;
1051
+ return {
1052
+ ok: true,
1053
+ result: { items }
1054
+ };
1055
+ }
1056
+ case "search": {
1057
+ const q = String(args.query ?? args.search ?? "").trim().toLowerCase();
1058
+ const items = catalog.filter((a) => a.name.toLowerCase().includes(q) || a.slug.toLowerCase().includes(q) || a.tagline.toLowerCase().includes(q));
1059
+ return {
1060
+ ok: true,
1061
+ result: { items }
1062
+ };
1063
+ }
1064
+ case "get": {
1065
+ const hit = resolve$1(args.app_ref ?? args.app_id);
1066
+ return hit ? {
1067
+ ok: true,
1068
+ result: hit
1069
+ } : {
1070
+ ok: false,
1071
+ error: {
1072
+ code: "app_not_installed",
1073
+ message: "not installed"
1074
+ }
1075
+ };
1076
+ }
1077
+ case "launch": {
1078
+ const hit = resolve$1(args.app_ref ?? args.app_id);
1079
+ if (!hit) return {
1080
+ ok: false,
1081
+ error: {
1082
+ code: "app_not_installed",
1083
+ message: "not installed"
1084
+ }
1085
+ };
1086
+ return {
1087
+ ok: true,
1088
+ result: {
1089
+ window_uuid: `dev-win-${hit.slug}`,
1090
+ app: {
1091
+ id: hit.id,
1092
+ slug: hit.slug,
1093
+ name: hit.name
1094
+ },
1095
+ opened_new: true
1096
+ }
1097
+ };
1098
+ }
1099
+ case "deck.list": return {
1100
+ ok: true,
1101
+ result: { items: deck() }
1102
+ };
1103
+ case "deck.add": {
1104
+ const hit = resolve$1(args.app_ref ?? args.app_id);
1105
+ if (!hit) return {
1106
+ ok: false,
1107
+ error: {
1108
+ code: "app_not_installed",
1109
+ message: "not installed"
1110
+ }
1111
+ };
1112
+ if (!hit.deck_pinned) {
1113
+ hit.deck_pinned = true;
1114
+ hit.deck_order = nextOrder();
1115
+ }
1116
+ return {
1117
+ ok: true,
1118
+ result: { items: deck() }
1119
+ };
1120
+ }
1121
+ case "deck.remove": {
1122
+ const hit = resolve$1(args.app_ref ?? args.app_id);
1123
+ if (!hit) return {
1124
+ ok: false,
1125
+ error: {
1126
+ code: "app_not_installed",
1127
+ message: "not installed"
1128
+ }
1129
+ };
1130
+ if (hit.deck_pinned) {
1131
+ hit.deck_pinned = false;
1132
+ hit.deck_order = null;
1133
+ }
1134
+ return {
1135
+ ok: true,
1136
+ result: { items: deck() }
1137
+ };
1138
+ }
1139
+ case "deck.reorder": {
1140
+ const ordered = args.ordered ?? args.app_refs;
1141
+ if (!Array.isArray(ordered)) return {
1142
+ ok: false,
1143
+ error: {
1144
+ code: "invalid_arg",
1145
+ message: "ordered must be a list"
1146
+ }
1147
+ };
1148
+ ordered.forEach((ref, i) => {
1149
+ const hit = resolve$1(ref);
1150
+ if (hit) {
1151
+ hit.deck_pinned = true;
1152
+ hit.deck_order = i;
1153
+ }
1154
+ });
1155
+ return {
1156
+ ok: true,
1157
+ result: { items: deck() }
1158
+ };
1159
+ }
1160
+ default: return {
1161
+ ok: false,
1162
+ error: {
1163
+ code: "not_implemented",
1164
+ message: `apps.${method} stub missing`
1165
+ }
1166
+ };
1167
+ }
1168
+ };
1169
+ return { handle };
1170
+ }
954
1171
  var HarnessServer = class {
955
1172
  server = createServer((req, res) => this.handle(req, res));
956
1173
  wss = null;
@@ -961,6 +1178,8 @@ var HarnessServer = class {
961
1178
  /** Pending events queued by the LLM bridge — drained alongside Python events. */
962
1179
  llmEventQueue = [];
963
1180
  llmBridge;
1181
+ /** In-memory `apps.*` dev stub (Apps launcher). */
1182
+ appsStub = makeAppsStub();
964
1183
  constructor(cfg, bridge) {
965
1184
  this.cfg = cfg;
966
1185
  this.bridge = bridge;
@@ -1242,6 +1461,11 @@ var HarnessServer = class {
1242
1461
  }
1243
1462
  });
1244
1463
  }
1464
+ if (parsed.ns === "apps") {
1465
+ const out = this.appsStub.handle(parsed.method, parsed.args ?? {});
1466
+ this.json(res, 200, out);
1467
+ return;
1468
+ }
1245
1469
  if (this.llmBridge != null && this.llmBridge.handles(parsed.ns, parsed.method)) {
1246
1470
  const out = await this.llmBridge.dispatch({
1247
1471
  windowUuid: this.sessionId ?? "harness",
@@ -1,15 +1,15 @@
1
1
  import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
- import { hostOf, requireAccount, withCode } from "./dev-account-CGo8k9_2.js";
2
+ import { hostOf, requireAccount, withCode } from "./dev-account-DqEFCmVg.js";
3
3
  import { resolve } from "node:path";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
 
6
6
  //#region src/executa/storage.ts
7
7
  /** JSON-RPC error codes from matrix/src/executa/protocol.py. */
8
- const STORAGE_ERR_INVALID_REQUEST = -32020;
9
8
  const STORAGE_ERR_NOT_GRANTED = -32021;
10
9
  const STORAGE_ERR_NOT_FOUND = -32022;
11
10
  const STORAGE_ERR_PRECONDITION_FAILED = -32023;
12
- const STORAGE_ERR_UPSTREAM = -32026;
11
+ const STORAGE_ERR_INVALID_REQUEST = -32028;
12
+ const STORAGE_ERR_UPSTREAM = -32029;
13
13
  /** Method → (http_method, sub_path, payload_kind). Mirrors matrix/storage.py. */
14
14
  const ROUTING = {
15
15
  "storage/get": [
@@ -1,6 +1,6 @@
1
1
  import "./credentials-BTv2IfUZ.js";
2
2
  import "./client-D-_z1ALk.js";
3
- import { resolveClient, withErrorHandling } from "./_lifecycle-shared-BpSOfVCP.js";
3
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-CUK2CE76.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,11 @@
1
1
  import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
- import { createApp, findAppBySlug, getApp } from "./apps-CCdtLmxQ.js";
2
+ import { createApp, findAppBySlug, getApp } from "./apps-DlVsVHQ5.js";
3
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-BHQpOskj.js";
8
- import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-Bl7cE5fm.js";
4
+ import { parseExecutaIdOverrides, readExecutasLock, substituteBundledRefs, validateBundledHandles, writeBundleToolIdSidecar, writeExecutasLock } from "./bundled-executas-_H1A9M50.js";
5
+ import { runExecutaPublish } from "./executa-publish-Ah6J7jYd.js";
6
+ import { loadExecutaManifest } from "./manifest-DOMrcdCK.js";
7
+ import { runExecutaInstall } from "./executa-install-BW9jjY7u.js";
8
+ import { appCacheMatches, readAppIdentity, writeAppIdentity } from "./app-cache-BqnTkxUw.js";
9
9
  import { join, resolve } from "node:path";
10
10
  import { dim, green, yellow } from "kleur/colors";
11
11
  import { homedir } from "node:os";
@@ -174,24 +174,40 @@ async function resolveAppIdentity(params) {
174
174
  const found = await findAppBySlug(client, manifest.slug);
175
175
  let appId;
176
176
  let serverSlug;
177
+ let serverHandle;
177
178
  let firstPublish = false;
178
179
  if (found) {
179
180
  appId = found.id;
180
181
  serverSlug = found.slug;
182
+ serverHandle = typeof found.handle === "string" ? found.handle : void 0;
181
183
  } else {
182
184
  if (dryRun) {
183
185
  if (!json) console.log(yellow(`[dry-run] would create AnnaApp slug=${manifest.slug}`));
184
186
  return null;
185
187
  }
186
- const created = await createApp(client, {
187
- slug: manifest.slug,
188
- name: manifest.name,
189
- category: manifest.category,
190
- tagline: manifest.tagline,
191
- description: manifest.description
192
- });
188
+ let created;
189
+ try {
190
+ created = await createApp(client, {
191
+ slug: manifest.slug,
192
+ name: manifest.name,
193
+ category: manifest.category,
194
+ tagline: manifest.tagline,
195
+ description: manifest.description
196
+ });
197
+ } catch (e) {
198
+ if (e instanceof CliError && e.status === 409) {
199
+ const body = e.body;
200
+ const code = body?.detail?.code;
201
+ if (code === "HANDLE_REQUIRED") throw new CliError("you must set a developer handle before creating an app.\n Run: anna-app account set-handle <handle>\n Your apps will then publish as @<handle>/" + manifest.slug + ".", 5, {
202
+ status: 409,
203
+ body
204
+ });
205
+ }
206
+ throw e;
207
+ }
193
208
  appId = created.id;
194
209
  serverSlug = created.slug;
210
+ serverHandle = typeof created.handle === "string" ? created.handle : void 0;
195
211
  firstPublish = true;
196
212
  }
197
213
  writeAppIdentity(cwd, {
@@ -199,6 +215,7 @@ async function resolveAppIdentity(params) {
199
215
  host: canonicalHost(host),
200
216
  app_id: appId,
201
217
  slug: serverSlug,
218
+ handle: serverHandle,
202
219
  first_published_at: cached?.first_published_at ?? new Date().toISOString()
203
220
  });
204
221
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anna-ai/cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.32",
4
4
  "description": "Anna App developer CLI: scaffold, validate, harness (Phase 2 MVP: init + validate).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,8 +33,8 @@
33
33
  "prepublishOnly": "pnpm lint && pnpm test && pnpm build && node scripts/check-runtime-pin.mjs && node scripts/check-sdk-pin.mjs"
34
34
  },
35
35
  "dependencies": {
36
- "@anna-ai/app-runtime": "^0.8.0",
37
- "@anna-ai/app-schema": "^0.10.0",
36
+ "@anna-ai/app-runtime": "^0.10.0",
37
+ "@anna-ai/app-schema": "^0.12.0",
38
38
  "ajv": "^8.17.1",
39
39
  "ajv-formats": "^3.0.1",
40
40
  "commander": "^12.1.0",
@@ -93,7 +93,7 @@ func dispatch(e envelope) {
93
93
  case "describe":
94
94
  result = manifest
95
95
  case "health":
96
- result = map[string]any{"status": "ok"}
96
+ result = map[string]any{"status": "ready"}
97
97
  case "invoke":
98
98
  var p struct {
99
99
  Tool string `json:"tool"`
@@ -85,7 +85,7 @@ async function dispatch(env) {
85
85
  } else if (method === "describe") {
86
86
  result = MANIFEST;
87
87
  } else if (method === "health") {
88
- result = { status: "ok" };
88
+ result = { status: "ready" };
89
89
  } else if (method === "invoke") {
90
90
  result = await invoke(params.tool, params.arguments ?? {});
91
91
  } else {
@@ -119,7 +119,7 @@ def _dispatch(env: dict[str, Any]) -> None:
119
119
  elif method == "describe":
120
120
  result = MANIFEST
121
121
  elif method == "health":
122
- result = {"status": "ok"}
122
+ result = {"status": "ready"}
123
123
  elif method == "invoke":
124
124
  params = env.get("params", {})
125
125
  result = invoke(params.get("tool", ""), params.get("arguments", {}))
@@ -40,7 +40,7 @@ def main() -> None:
40
40
  if req.get("method") == "describe":
41
41
  result = MANIFEST
42
42
  elif req.get("method") == "health":
43
- result = {"status": "ok"}
43
+ result = {"status": "ready"}
44
44
  elif req.get("method") == "invoke":
45
45
  result = invoke(req["params"]["tool"], req["params"].get("arguments", {}))
46
46
  else: