@anna-ai/cli 0.1.19 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/_lifecycle-shared-sbea9HtH.js +65 -0
  2. package/dist/{agent-DUmINbo4.js → agent-Br6zY2qw.js} +1 -1
  3. package/dist/app-bundle-upload-DuLalcSt.js +213 -0
  4. package/dist/app-cache-BEM653Th.js +53 -0
  5. package/dist/apps-B1Nd8l_t.js +221 -0
  6. package/dist/apps-BTn9EN0x.js +53 -0
  7. package/dist/apps-cut-DtEkddIk.js +83 -0
  8. package/dist/apps-destructive-DSTrcFUP.js +104 -0
  9. package/dist/apps-discard-y3_IwcbQ.js +44 -0
  10. package/dist/apps-grants-BGWlpee0.js +34 -0
  11. package/dist/apps-publish-CaTCanDu.js +265 -0
  12. package/dist/apps-publish-DfZTOxBJ.js +14 -0
  13. package/dist/apps-push-B9XT2uwF.js +127 -0
  14. package/dist/apps-release-BLH9XSxB.js +135 -0
  15. package/dist/apps-status-DQ9RvlME.js +58 -0
  16. package/dist/apps-submit-review-DLwCxeAs.js +45 -0
  17. package/dist/apps-sync-meta-D9eKMMUp.js +72 -0
  18. package/dist/apps-versions-2Tmk0nsx.js +43 -0
  19. package/dist/bridge-Id8K8gr-.js +3 -0
  20. package/dist/bundled-executas-BNOKw4kv.js +161 -0
  21. package/dist/bundled-executas-CNaV2C_O.js +5 -0
  22. package/dist/cli.js +346 -23
  23. package/dist/client-Dn9zThOd.js +150 -0
  24. package/dist/confirm-DxHkk9Wn.js +37 -0
  25. package/dist/dev-BS_8yoSm.js +3 -0
  26. package/dist/{dev-b1j-dEM2.js → dev-E7mqXj5S.js} +95 -26
  27. package/dist/{dev-app-cache-3Pfesngr.js → dev-app-cache-D-r6ZpEk.js} +11 -2
  28. package/dist/{doctor-CgJYokiR.js → doctor-DKrt-Kda.js} +1 -1
  29. package/dist/executa-cache-BFoUtb4J.js +86 -0
  30. package/dist/executa-cache-WBkCLic7.js +4 -0
  31. package/dist/executa-destructive-COQE4Xqi.js +104 -0
  32. package/dist/{executa-dev-BeC6a8S8.js → executa-dev-BvS9zTpO.js} +11 -11
  33. package/dist/executa-publish-B88_9gbp.js +9 -0
  34. package/dist/executa-publish-Ca5V7MyA.js +258 -0
  35. package/dist/executa-reads-CQ6S8gHY.js +107 -0
  36. package/dist/executas-Cep6KEo0.js +109 -0
  37. package/dist/manifest-DGwRap2i.js +188 -0
  38. package/dist/publish-C1wcf-qI.js +58 -0
  39. package/dist/{server-BgJGmEpv.js → server-_IG8Igje.js} +200 -2
  40. package/dist/{storage-EQJA_0UW.js → storage-CTkApNQ9.js} +1 -1
  41. package/dist/token-B9JUPelx.js +87 -0
  42. package/dist/working-orchestration-Dw9u1Vq0.js +190 -0
  43. package/package.json +3 -3
  44. package/templates/executa/go/executa.json +5 -0
  45. package/templates/executa/node/executa.json +5 -0
  46. package/templates/executa/python/executa.json +5 -0
  47. package/dist/apps-ClgEOdKD.js +0 -44
  48. package/dist/bridge-B3Vwr4cg.js +0 -3
  49. package/dist/dev-D8o7xi0W.js +0 -3
  50. package/dist/dev-app-cache-CZ1UjMz0.js +0 -4
  51. /package/dist/{bridge-mkb_EM-y.js → bridge-BuklhzeE.js} +0 -0
  52. /package/dist/{credentials-DDqx6XMQ.js → credentials-DklPMD22.js} +0 -0
  53. /package/dist/{dev-account-DCyjamBa.js → dev-account-qRaET1Cp.js} +0 -0
  54. /package/dist/{executa-init-COEmKDOE.js → executa-init-Jp-h9OI7.js} +0 -0
  55. /package/dist/{executa-register-66WKIwQQ.js → executa-register-CulDtwYZ.js} +0 -0
  56. /package/dist/{fixture-CATHyLLI.js → fixture-CYwxbiQD.js} +0 -0
  57. /package/dist/{host_upload-C_pGOS6p.js → host_upload-GXVkDM5M.js} +0 -0
  58. /package/dist/{image-bwolX7pa.js → image-DduR91n5.js} +0 -0
  59. /package/dist/{login-CsIVbrmf.js → login-BGqFjQwH.js} +0 -0
  60. /package/dist/{logout-gfmKQxMj.js → logout-CGIRKH3y.js} +0 -0
  61. /package/dist/{runner-DmGLdat0.js → runner-B-hIqx5L.js} +0 -0
  62. /package/dist/{sampling-CJUDG-mf.js → sampling-CXke7hq1.js} +0 -0
  63. /package/dist/{whoami-BS5wy-Nh.js → whoami-BoFLEUcp.js} +0 -0
@@ -0,0 +1,258 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { CliError } from "./client-Dn9zThOd.js";
3
+ import { commitDraft, createDraft, getMySkill, getMyTool, listToolVersions, publishToolVersion, setSkillVisibility, setToolVisibility, updateMySkill, updateMyTool } from "./executas-Cep6KEo0.js";
4
+ import { executaCacheMatches, invalidateExecutaCache, mintIdempotencyKey, readExecutaIdentity, writeExecutaIdentity } from "./executa-cache-BFoUtb4J.js";
5
+ import { loadExecutaManifest } from "./manifest-DGwRap2i.js";
6
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
7
+ import { join, relative, resolve, sep } from "node:path";
8
+ import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
9
+ import { bold, cyan, dim, green, yellow } from "kleur/colors";
10
+ import { createHash } from "node:crypto";
11
+
12
+ //#region src/publish/bundle.ts
13
+ function manifestHash(manifest) {
14
+ const canon = canonicalize(manifest);
15
+ return createHash("sha256").update(canon).digest("hex");
16
+ }
17
+ function bundleHash(manifest, bundleDir) {
18
+ const h = createHash("sha256");
19
+ h.update(canonicalize(manifest));
20
+ if (bundleDir) {
21
+ const files = walk(bundleDir);
22
+ files.sort();
23
+ for (const rel of files) {
24
+ const abs = join(bundleDir, rel);
25
+ const data = readFileSync(abs);
26
+ h.update("\n");
27
+ h.update(rel.split(sep).join("/"));
28
+ h.update("\0");
29
+ h.update(createHash("sha256").update(data).digest("hex"));
30
+ }
31
+ }
32
+ return h.digest("hex");
33
+ }
34
+ /** RFC-8785-ish JSON canonicalization, sufficient for our hash use. */
35
+ function canonicalize(value) {
36
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
37
+ if (Array.isArray(value)) return "[" + value.map(canonicalize).join(",") + "]";
38
+ const obj = value;
39
+ const keys = Object.keys(obj).sort();
40
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
41
+ }
42
+ function walk(root, base = root) {
43
+ const out = [];
44
+ for (const ent of readdirSync(base, { withFileTypes: true })) {
45
+ const abs = join(base, ent.name);
46
+ if (ent.isDirectory()) out.push(...walk(root, abs));
47
+ else if (ent.isFile()) out.push(relative(root, abs));
48
+ }
49
+ return out;
50
+ }
51
+
52
+ //#endregion
53
+ //#region src/publish/bump.ts
54
+ function bumpVersion(current, kind) {
55
+ const m = current.match(/^(\d+)\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]+)?$/);
56
+ if (!m || !m[1] || !m[2] || !m[3]) throw new CliError(`cannot bump non-semver version "${current}"`, 4);
57
+ let [, maj, min, pat] = m;
58
+ if (kind === "major") {
59
+ maj = String(Number(maj) + 1);
60
+ min = "0";
61
+ pat = "0";
62
+ } else if (kind === "minor") {
63
+ min = String(Number(min) + 1);
64
+ pat = "0";
65
+ } else pat = String(Number(pat) + 1);
66
+ return `${maj}.${min}.${pat}`;
67
+ }
68
+ /**
69
+ * Rewrite a manifest's `version` field in place.
70
+ *
71
+ * Preserves on-disk formatting as much as possible (re-serialising
72
+ * with 2-space JSON.stringify) — `manifest.json` and `executa.json`
73
+ * are both expected to be machine-managed, so cosmetic formatting
74
+ * differences are acceptable.
75
+ */
76
+ function rewriteVersion(manifestPath, manifest, newVersion) {
77
+ const next = {
78
+ ...manifest,
79
+ version: newVersion
80
+ };
81
+ writeFileSync(manifestPath, JSON.stringify(next, null, 2) + "\n", "utf-8");
82
+ }
83
+
84
+ //#endregion
85
+ //#region src/commands/executa-publish.ts
86
+ /**
87
+ * Flatten the parsed `executa.json` `distribution` block into the snake_case
88
+ * field set the server expects on both the draft-commit payload and the
89
+ * `PUT /my/tools/{id}` metadata sync. Returns an empty object when the
90
+ * Executa declares no distribution (dev-only) so the spread is a no-op.
91
+ *
92
+ * Note: per-asset `sha256` is folded into each `binary_urls` asset dict
93
+ * (`{ url, sha256, … }`) rather than sent as a separate top-level
94
+ * `binary_sha256` map — the `PUT /my/tools/{id}` schema
95
+ * (`UserToolUpdateRequest`) has no `binary_sha256` field, whereas
96
+ * `binary_urls` accepts the asset-dict form on both the create and update
97
+ * paths.
98
+ */
99
+ function distributionPayload(d) {
100
+ if (!d) return {};
101
+ const out = { distribution_type: d.type };
102
+ if (d.packageName !== void 0) out.package_name = d.packageName;
103
+ if (d.executableName !== void 0) out.executable_name = d.executableName;
104
+ if (d.supportsProtocol !== void 0) out.supports_protocol = d.supportsProtocol;
105
+ if (d.binaryUrls !== void 0) out.binary_urls = d.binaryUrls;
106
+ if (d.capabilities !== void 0) out.capabilities = d.capabilities;
107
+ return out;
108
+ }
109
+ async function runExecutaPublish(opts) {
110
+ return withErrorHandling(async () => {
111
+ const cwd = resolve(opts.cwd ?? process.cwd());
112
+ const manifest = loadExecutaManifest(cwd, opts.manifest);
113
+ const isSkill = manifest.executa_type === "skill";
114
+ const { client, host, source } = resolveClient({ account: opts.account });
115
+ if (!opts.json && !opts.quiet) console.log(dim(`using PAT from ${source} (host=${host})`));
116
+ if (opts.bump) {
117
+ const next = bumpVersion(manifest.version, opts.bump);
118
+ if (!opts.json && !opts.quiet) console.log(yellow(`↑ bumping executa.json version: ${manifest.version} → ${next}`));
119
+ if (!opts.noWrite && !opts.dryRun) rewriteVersion(manifest.path, manifest.raw, next);
120
+ manifest.version = next;
121
+ manifest.raw.version = next;
122
+ }
123
+ let cached = readExecutaIdentity(cwd);
124
+ if (cached && cached.host !== canonicalHost(host)) cached = null;
125
+ let executaId;
126
+ let toolId;
127
+ let serverSlug;
128
+ let firstPublish = false;
129
+ let cacheValid = false;
130
+ if (cached && executaCacheMatches(cached, host, manifest.slug)) try {
131
+ if (isSkill) await getMySkill(client, cached.executa_id);
132
+ else await getMyTool(client, cached.executa_id);
133
+ cacheValid = true;
134
+ } catch (e) {
135
+ if (e instanceof CliError && e.status === 404) {
136
+ if (!opts.json && !opts.quiet) console.log(yellow(`! cached executa_id=${cached.executa_id} no longer exists on ${host}; clearing .anna/executa.json and re-minting via draft+commit`));
137
+ invalidateExecutaCache(cwd);
138
+ cached = null;
139
+ } else throw e;
140
+ }
141
+ if (cacheValid && cached) {
142
+ executaId = cached.executa_id;
143
+ toolId = cached.tool_id;
144
+ serverSlug = cached.slug;
145
+ } else {
146
+ if (opts.dryRun) {
147
+ if (!opts.json) console.log(yellow(`[dry-run] would POST /my/drafts (slug=${manifest.slug}, type=${manifest.executa_type}) and commit`));
148
+ return 0;
149
+ }
150
+ const idemKey = mintIdempotencyKey(host, manifest.slug, manifest.executa_type);
151
+ const draftRes = await createDraft(client, {
152
+ name: manifest.name,
153
+ executa_type: manifest.executa_type,
154
+ slug_override: manifest.slug,
155
+ idempotency_key: idemKey
156
+ });
157
+ if (draftRes.committedExecutaId !== null) {
158
+ executaId = draftRes.committedExecutaId;
159
+ toolId = draftRes.draft.tool_id;
160
+ serverSlug = manifest.slug;
161
+ if (!opts.json && !opts.quiet) console.log(dim(`(server idempotent hit on already-committed Executa id=${executaId})`));
162
+ } else {
163
+ const draft = draftRes.draft;
164
+ const finalPayload = {
165
+ ...typeof draft.payload === "object" && draft.payload !== null ? draft.payload : {},
166
+ name: manifest.name,
167
+ description: manifest.description,
168
+ executa_type: manifest.executa_type,
169
+ slug: manifest.slug,
170
+ version: manifest.version,
171
+ ...distributionPayload(manifest.distribution)
172
+ };
173
+ if (manifest.executa_type === "skill") {
174
+ finalPayload.skill_content = manifest.skillContent;
175
+ if (manifest.executionMode) finalPayload.execution_mode = manifest.executionMode;
176
+ }
177
+ const committed = draftRes.idempotentHit ? await commitDraft(client, draft.id, finalPayload) : await commitDraft(client, draft.id, finalPayload);
178
+ executaId = committed.executa_id;
179
+ toolId = committed.tool_id;
180
+ serverSlug = manifest.slug;
181
+ firstPublish = true;
182
+ }
183
+ writeExecutaIdentity(cwd, {
184
+ $schema: "anna-executa-identity/v1",
185
+ host: canonicalHost(host),
186
+ executa_id: executaId,
187
+ tool_id: toolId,
188
+ slug: serverSlug,
189
+ executa_type: manifest.executa_type,
190
+ first_published_at: cached?.first_published_at ?? new Date().toISOString()
191
+ });
192
+ }
193
+ if (opts.dryRun) {
194
+ if (!opts.json) console.log(yellow(`[dry-run] resolved executa_id=${executaId} tool_id=${toolId}; would PUT metadata + POST version ${manifest.version}`));
195
+ return 0;
196
+ }
197
+ if (isSkill) await updateMySkill(client, executaId, {
198
+ name: manifest.name,
199
+ description: manifest.description,
200
+ skill_content: manifest.skillContent,
201
+ version: manifest.version,
202
+ ...manifest.executionMode ? { skill_execution_mode: manifest.executionMode } : {}
203
+ });
204
+ else await updateMyTool(client, executaId, {
205
+ name: manifest.name,
206
+ description: manifest.description,
207
+ ...distributionPayload(manifest.distribution)
208
+ });
209
+ const preFreeze = isSkill ? await getMySkill(client, executaId) : await getMyTool(client, executaId);
210
+ const currentVis = preFreeze.visibility ?? "private";
211
+ const targetVis = opts.publish ? "public" : currentVis === "public" ? "public" : "app_bundled";
212
+ const visibilityChanged = currentVis !== targetVis;
213
+ if (visibilityChanged) if (isSkill) await setSkillVisibility(client, executaId, targetVis);
214
+ else await setToolVisibility(client, executaId, targetVis);
215
+ const hash = manifestHash(manifest.raw);
216
+ const freeze = opts.freeze !== false;
217
+ let frozenIdempotentHit;
218
+ if (!freeze) frozenIdempotentHit = true;
219
+ else if (isSkill) frozenIdempotentHit = !visibilityChanged;
220
+ else {
221
+ const verRes = await publishToolVersion(client, executaId, { version: manifest.version }, hash);
222
+ frozenIdempotentHit = verRes.idempotentHit;
223
+ }
224
+ let publishedToPublic = false;
225
+ if (opts.publish && targetVis === "public") publishedToPublic = true;
226
+ const finalExecuta = isSkill ? await getMySkill(client, executaId) : await getMyTool(client, executaId);
227
+ const versions = await listToolVersions(client, executaId);
228
+ const latest = versions[0] ?? null;
229
+ const result = {
230
+ host,
231
+ executa_id: executaId,
232
+ tool_id: toolId,
233
+ slug: serverSlug,
234
+ version: manifest.version,
235
+ content_hash: hash,
236
+ idempotent_hit: frozenIdempotentHit,
237
+ first_publish: firstPublish,
238
+ published_public: publishedToPublic,
239
+ visibility: finalExecuta.visibility ?? null,
240
+ latest_version_id: latest?.id ?? null
241
+ };
242
+ opts.capture?.(result);
243
+ if (opts.quiet) return 0;
244
+ if (opts.json) {
245
+ console.log(JSON.stringify(result, null, 2));
246
+ return 0;
247
+ }
248
+ if (frozenIdempotentHit) console.log(green(`✓ executas/${serverSlug}: version ${manifest.version} already frozen (content hash unchanged)`));
249
+ else console.log(green(`✓ executas/${serverSlug}: version ${manifest.version} frozen`));
250
+ if (firstPublish) console.log(dim(` first publish — wrote .anna/executa.json (tool_id=${toolId})`));
251
+ if (publishedToPublic) console.log(green(` visibility set to PUBLIC`));
252
+ console.log(bold(cyan(`tool_id: ${toolId}`)));
253
+ return 0;
254
+ });
255
+ }
256
+
257
+ //#endregion
258
+ export { bumpVersion, bundleHash, canonicalize, manifestHash, rewriteVersion, runExecutaPublish };
@@ -0,0 +1,107 @@
1
+ import "./credentials-BTv2IfUZ.js";
2
+ import { CliError } from "./client-Dn9zThOd.js";
3
+ import { getMyTool, listMyTools, listToolVersions } from "./executas-Cep6KEo0.js";
4
+ import { resolveClient, withErrorHandling } from "./_lifecycle-shared-sbea9HtH.js";
5
+ import { bold, cyan, dim, green } from "kleur/colors";
6
+
7
+ //#region src/commands/executa-reads.ts
8
+ async function runExecutaList(opts) {
9
+ return withErrorHandling(async () => {
10
+ const { client, host } = resolveClient({ account: opts.account });
11
+ const published = await listMyTools(client);
12
+ if (opts.json) {
13
+ console.log(JSON.stringify({
14
+ host,
15
+ executas: published
16
+ }, null, 2));
17
+ return 0;
18
+ }
19
+ console.log(bold(cyan(`my executas`)) + " " + dim(`(${host})`));
20
+ if (published.length === 0) {
21
+ console.log(dim(" (none — run `anna-app executa publish` from a project to mint one; dev harness placeholders show up under `anna-app apps list`)"));
22
+ return 0;
23
+ }
24
+ for (const r of published) {
25
+ const vis = r.visibility ? `[${r.visibility}]` : "";
26
+ console.log(` ${green("[published]")} ${r.tool_id} ` + dim(`(id=${r.id}, type=${r.executa_type}) ${vis}`));
27
+ }
28
+ return 0;
29
+ });
30
+ }
31
+ async function findExecutaByRef(client, ref) {
32
+ const rows = await listMyTools(client);
33
+ const exact = rows.find((r) => r.tool_id === ref);
34
+ if (exact) return exact;
35
+ const slug = rows.find((r) => {
36
+ const parts = r.tool_id.split("-");
37
+ return parts.includes(ref);
38
+ });
39
+ return slug ?? null;
40
+ }
41
+ async function runExecutaStatus(opts) {
42
+ return withErrorHandling(async () => {
43
+ const { client, host } = resolveClient({ account: opts.account });
44
+ const ex = await findExecutaByRef(client, opts.ref);
45
+ if (!ex) throw new CliError(`no executa matching "${opts.ref}" for current user on ${host}`, 5);
46
+ const full = await getMyTool(client, ex.id);
47
+ const versions = await listToolVersions(client, ex.id);
48
+ const latest = versions[0] ?? null;
49
+ if (opts.json) {
50
+ console.log(JSON.stringify({
51
+ host,
52
+ executa_id: full.id,
53
+ tool_id: full.tool_id,
54
+ name: full.name,
55
+ executa_type: full.executa_type,
56
+ visibility: full.visibility ?? null,
57
+ is_published: full.is_published ?? false,
58
+ version_count: versions.length,
59
+ latest_version: latest ? {
60
+ id: latest.id,
61
+ version: latest.version,
62
+ content_hash: latest.content_hash,
63
+ in_published_app: latest.in_published_app ?? false
64
+ } : null
65
+ }, null, 2));
66
+ return 0;
67
+ }
68
+ console.log(bold(cyan(full.tool_id)) + " " + dim(`(${host})`));
69
+ console.log(` ${dim("name :")} ${full.name}`);
70
+ console.log(` ${dim("type :")} ${full.executa_type}`);
71
+ console.log(` ${dim("visibility:")} ${full.visibility ?? "(unknown)"}`);
72
+ console.log(` ${dim("published :")} ${full.is_published ? green("yes") : "no"}`);
73
+ if (latest) console.log(` ${dim("latest :")} v${latest.version} (id=${latest.id})`);
74
+ else console.log(` ${dim("latest :")} ${dim("(no versions)")}`);
75
+ return 0;
76
+ });
77
+ }
78
+ async function runExecutaVersions(opts) {
79
+ return withErrorHandling(async () => {
80
+ const { client, host } = resolveClient({ account: opts.account });
81
+ const ex = await findExecutaByRef(client, opts.ref);
82
+ if (!ex) throw new CliError(`no executa matching "${opts.ref}" for current user on ${host}`, 5);
83
+ const versions = await listToolVersions(client, ex.id);
84
+ if (opts.json) {
85
+ console.log(JSON.stringify({
86
+ host,
87
+ executa_id: ex.id,
88
+ tool_id: ex.tool_id,
89
+ versions
90
+ }, null, 2));
91
+ return 0;
92
+ }
93
+ console.log(bold(cyan(ex.tool_id)) + " " + dim(`versions (${versions.length})`));
94
+ if (versions.length === 0) {
95
+ console.log(dim(" (none)"));
96
+ return 0;
97
+ }
98
+ for (const v of versions) {
99
+ const inApp = v.in_published_app ? green("[anchored]") : " ";
100
+ console.log(` ${inApp} v${v.version} ${dim(`(id=${v.id}, hash=${v.content_hash.slice(0, 12)}…)`)}`);
101
+ }
102
+ return 0;
103
+ });
104
+ }
105
+
106
+ //#endregion
107
+ export { runExecutaList, runExecutaStatus, runExecutaVersions };
@@ -0,0 +1,109 @@
1
+ //#region src/api/executas.ts
2
+ async function createDraft(client, body) {
3
+ const r = await client.request({
4
+ method: "POST",
5
+ path: "/api/v1/executas/my/drafts",
6
+ body
7
+ });
8
+ const committed = r.idempotentHit && r.data.id < 0 && r.data.payload && typeof r.data.payload === "object" && typeof r.data.payload.committed_executa_id === "number" ? r.data.payload.committed_executa_id : null;
9
+ return {
10
+ draft: r.data,
11
+ idempotentHit: r.idempotentHit,
12
+ committedExecutaId: committed
13
+ };
14
+ }
15
+ async function commitDraft(client, draftId, finalPayload) {
16
+ const r = await client.request({
17
+ method: "POST",
18
+ path: `/api/v1/executas/my/drafts/${draftId}/commit`,
19
+ body: finalPayload ? { payload: finalPayload } : void 0
20
+ });
21
+ return r.data;
22
+ }
23
+ async function listMyTools(client) {
24
+ const r = await client.request({ path: "/api/v1/executas/my/tools" });
25
+ return Array.isArray(r.data) ? r.data : [];
26
+ }
27
+ async function getMyTool(client, executaId) {
28
+ const r = await client.request({ path: `/api/v1/executas/my/tools/${executaId}/detail` });
29
+ return r.data;
30
+ }
31
+ async function updateMyTool(client, executaId, body) {
32
+ const r = await client.request({
33
+ method: "PUT",
34
+ path: `/api/v1/executas/my/tools/${executaId}`,
35
+ body
36
+ });
37
+ return r.data;
38
+ }
39
+ async function listToolVersions(client, executaId) {
40
+ const r = await client.request({
41
+ path: `/api/v1/executas/my/tools/${executaId}/versions`,
42
+ allowStatuses: [404]
43
+ });
44
+ if (r.status === 404) return [];
45
+ return Array.isArray(r.data) ? r.data : [];
46
+ }
47
+ async function publishToolVersion(client, executaId, body, contentHash) {
48
+ const r = await client.request({
49
+ method: "POST",
50
+ path: `/api/v1/executas/my/tools/${executaId}/versions`,
51
+ query: contentHash ? { content_hash: contentHash } : void 0,
52
+ body: body ?? void 0
53
+ });
54
+ return {
55
+ executa: r.data,
56
+ idempotentHit: r.idempotentHit
57
+ };
58
+ }
59
+ async function setToolVisibility(client, executaId, visibility) {
60
+ const r = await client.request({
61
+ method: "POST",
62
+ path: `/api/v1/executas/my/tools/${executaId}/visibility`,
63
+ body: { visibility }
64
+ });
65
+ return r.data;
66
+ }
67
+ async function unpublishTool(client, executaId) {
68
+ const r = await client.request({
69
+ method: "POST",
70
+ path: `/api/v1/executas/my/tools/${executaId}/unpublish`
71
+ });
72
+ return r.data;
73
+ }
74
+ async function getMySkill(client, executaId) {
75
+ const r = await client.request({ path: `/api/v1/executas/my/skills/${executaId}/detail` });
76
+ return r.data;
77
+ }
78
+ async function updateMySkill(client, executaId, body) {
79
+ const r = await client.request({
80
+ method: "PUT",
81
+ path: `/api/v1/executas/my/skills/${executaId}`,
82
+ body
83
+ });
84
+ return r.data;
85
+ }
86
+ async function setSkillVisibility(client, executaId, visibility) {
87
+ const r = await client.request({
88
+ method: "POST",
89
+ path: `/api/v1/executas/my/skills/${executaId}/visibility`,
90
+ body: { visibility }
91
+ });
92
+ return r.data;
93
+ }
94
+ async function deleteMyTool(client, executaId) {
95
+ await client.request({
96
+ method: "DELETE",
97
+ path: `/api/v1/executas/my/tools/${executaId}`,
98
+ allowStatuses: [200, 204]
99
+ });
100
+ }
101
+ async function yankToolVersion(client, executaId, versionId) {
102
+ await client.request({
103
+ method: "DELETE",
104
+ path: `/api/v1/executas/my/tools/${executaId}/versions/${versionId}`
105
+ });
106
+ }
107
+
108
+ //#endregion
109
+ export { commitDraft, createDraft, deleteMyTool, getMySkill, getMyTool, listMyTools, listToolVersions, publishToolVersion, setSkillVisibility, setToolVisibility, unpublishTool, updateMySkill, updateMyTool, yankToolVersion };
@@ -0,0 +1,188 @@
1
+ import { CliError } from "./client-Dn9zThOd.js";
2
+ import { resolve } from "node:path";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+
5
+ //#region src/publish/manifest.ts
6
+ const DISTRIBUTION_TYPES = new Set([
7
+ "uv",
8
+ "npm",
9
+ "homebrew",
10
+ "binary",
11
+ "pipx",
12
+ "local"
13
+ ]);
14
+ /** Distribution types that install a named package from an index. */
15
+ const PACKAGE_BACKED = new Set([
16
+ "uv",
17
+ "npm",
18
+ "homebrew",
19
+ "pipx"
20
+ ]);
21
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]{1,78}[a-z0-9]$/;
22
+ const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
23
+ function readJson(path) {
24
+ const raw = readFileSync(path, "utf-8");
25
+ try {
26
+ const parsed = JSON.parse(raw);
27
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(`manifest at ${path} must be a JSON object`, 4);
28
+ return parsed;
29
+ } catch (e) {
30
+ if (e instanceof CliError) throw e;
31
+ throw new CliError(`failed to parse ${path}: ${e.message}`, 4);
32
+ }
33
+ }
34
+ function loadAppManifest(cwd, manifestPath) {
35
+ const path = resolve(cwd, manifestPath ?? "manifest.json");
36
+ if (!existsSync(path)) throw new CliError(`no manifest.json at ${path}`, 4);
37
+ const raw = readJson(path);
38
+ const appJsonPath = resolve(cwd, "app.json");
39
+ const hasAppJson = existsSync(appJsonPath);
40
+ const appJsonRaw = hasAppJson ? readJson(appJsonPath) : null;
41
+ const metadataPath = hasAppJson ? appJsonPath : path;
42
+ const metadataRaw = appJsonRaw ?? raw;
43
+ const metadataLabel = hasAppJson ? "app.json" : "manifest.json";
44
+ const pickMeta = (k) => (appJsonRaw && pickString(appJsonRaw, k)) ?? pickString(raw, k);
45
+ const slug = pickMeta("slug");
46
+ const name = pickMeta("name") ?? slug ?? "";
47
+ const version = pickMeta("version") ?? "0.1.0";
48
+ if (!slug) throw new CliError(`${metadataLabel} missing required \`slug\` (3-80 chars, kebab-case)`, 4);
49
+ if (!SLUG_RE.test(slug)) throw new CliError(`slug "${slug}" is not valid — must match ${SLUG_RE.source}`, 4);
50
+ if (!SEMVER_RE.test(version)) throw new CliError(`${metadataLabel} \`version\` "${version}" is not semver`, 4);
51
+ const bundledExecutas = parseBundledExecutas((appJsonRaw && appJsonRaw["bundled_executas"]) ?? raw["bundled_executas"], metadataLabel);
52
+ return {
53
+ kind: "app",
54
+ path,
55
+ raw,
56
+ metadataPath,
57
+ metadataRaw,
58
+ cwd: resolve(cwd),
59
+ bundledExecutas,
60
+ slug,
61
+ name,
62
+ version,
63
+ category: pickMeta("category"),
64
+ tagline: pickMeta("tagline"),
65
+ description: pickMeta("description"),
66
+ changelog: pickMeta("changelog")
67
+ };
68
+ }
69
+ const HANDLE_RE = /^[a-z0-9][a-z0-9-]{0,62}$/;
70
+ function parseBundledExecutas(value, label) {
71
+ if (value === void 0 || value === null) return [];
72
+ if (typeof value !== "object" || Array.isArray(value)) throw new CliError(`${label} \`bundled_executas\` must be an object mapping handle → { path }`, 4);
73
+ const out = [];
74
+ for (const [handle, decl] of Object.entries(value)) {
75
+ if (!HANDLE_RE.test(handle)) throw new CliError(`bundled_executas handle "${handle}" is invalid — must be kebab-case, ≤63 chars`, 4);
76
+ if (decl === null || typeof decl !== "object" || Array.isArray(decl)) throw new CliError(`bundled_executas["${handle}"] must be an object with a \`path\` field`, 4);
77
+ const p = pickString(decl, "path");
78
+ if (!p) throw new CliError(`bundled_executas["${handle}"] missing required \`path\``, 4);
79
+ out.push({
80
+ handle,
81
+ path: p
82
+ });
83
+ }
84
+ return out;
85
+ }
86
+ function loadExecutaManifest(cwd, manifestPath) {
87
+ const path = resolve(cwd, manifestPath ?? "executa.json");
88
+ if (!existsSync(path)) throw new CliError(`no executa.json at ${path}`, 4);
89
+ const raw = readJson(path);
90
+ const slug = pickString(raw, "slug");
91
+ const name = pickString(raw, "name") ?? slug ?? "";
92
+ const version = pickString(raw, "version") ?? "0.1.0";
93
+ const t = pickString(raw, "executa_type") ?? "tool";
94
+ if (t !== "tool" && t !== "skill") throw new CliError(`executa.json \`executa_type\` must be "tool" or "skill" (got "${t}")`, 4);
95
+ if (!slug) throw new CliError(`executa.json missing required \`slug\` (3-80 chars, kebab-case)`, 4);
96
+ if (!SLUG_RE.test(slug)) throw new CliError(`slug "${slug}" is not valid — must match ${SLUG_RE.source}`, 4);
97
+ if (!SEMVER_RE.test(version)) throw new CliError(`executa.json \`version\` "${version}" is not semver`, 4);
98
+ let skillContent;
99
+ if (t === "skill") {
100
+ const contentFile = pickString(raw, "content_file") ?? "SKILL.md";
101
+ const contentPath = resolve(cwd, contentFile);
102
+ if (!existsSync(contentPath)) throw new CliError(`skill executa.json references content file "${contentFile}" but ${contentPath} does not exist`, 4);
103
+ skillContent = readFileSync(contentPath, "utf-8");
104
+ if (!skillContent.trim()) throw new CliError(`skill content file "${contentFile}" is empty`, 4);
105
+ }
106
+ return {
107
+ kind: "executa",
108
+ path,
109
+ raw,
110
+ slug,
111
+ name,
112
+ version,
113
+ executa_type: t,
114
+ description: pickString(raw, "description") ?? void 0,
115
+ changelog: pickString(raw, "changelog") ?? void 0,
116
+ skillContent,
117
+ executionMode: pickString(raw, "execution_mode") ?? void 0,
118
+ distribution: parseExecutaDistribution(raw["distribution"], t)
119
+ };
120
+ }
121
+ /**
122
+ * Parse + validate the optional `executa.json` `distribution` object.
123
+ *
124
+ * Returns `undefined` when absent (a dev-only Executa). Skills never carry
125
+ * distribution config — they are declarative Markdown, not installable
126
+ * processes — so a `distribution` block on a skill is rejected.
127
+ */
128
+ function parseExecutaDistribution(value, executaType) {
129
+ if (value === void 0 || value === null) return void 0;
130
+ if (typeof value !== "object" || Array.isArray(value)) throw new CliError("executa.json `distribution` must be an object with a `type` field", 4);
131
+ if (executaType === "skill") throw new CliError("executa.json `distribution` is only valid for `executa_type: \"tool\"` — skills are declarative Markdown and are not installed as a process", 4);
132
+ const d = value;
133
+ const type = pickString(d, "type");
134
+ if (!type) throw new CliError("executa.json `distribution.type` is required", 4);
135
+ if (!DISTRIBUTION_TYPES.has(type)) throw new CliError(`executa.json \`distribution.type\` "${type}" is invalid — expected one of ` + [...DISTRIBUTION_TYPES].join(" | "), 4);
136
+ const distType = type;
137
+ const packageName = pickString(d, "package_name");
138
+ const executableName = pickString(d, "executable_name");
139
+ const supportsProtocol = pickBool(d, "supports_protocol");
140
+ const binaryUrls = parseBinaryUrls(d["binary_urls"]);
141
+ const capabilities = parseCapabilities(d["capabilities"]);
142
+ if (PACKAGE_BACKED.has(distType) && !packageName) throw new CliError(`executa.json \`distribution.package_name\` is required for \`distribution.type: "${distType}"\``, 4);
143
+ if (distType === "binary" && (!binaryUrls || Object.keys(binaryUrls).length === 0)) throw new CliError("executa.json `distribution.binary_urls` is required (and non-empty) for `distribution.type: \"binary\"`", 4);
144
+ return {
145
+ type: distType,
146
+ packageName,
147
+ executableName,
148
+ supportsProtocol,
149
+ binaryUrls,
150
+ capabilities
151
+ };
152
+ }
153
+ function parseBinaryUrls(value) {
154
+ if (value === void 0 || value === null) return void 0;
155
+ if (typeof value !== "object" || Array.isArray(value)) throw new CliError("executa.json `distribution.binary_urls` must be an object mapping platform → URL string or asset dict", 4);
156
+ const out = {};
157
+ for (const [platform, asset] of Object.entries(value)) {
158
+ if (typeof asset === "string") {
159
+ if (!asset.trim()) throw new CliError(`executa.json \`distribution.binary_urls["${platform}"]\` is empty`, 4);
160
+ out[platform] = asset;
161
+ continue;
162
+ }
163
+ if (asset && typeof asset === "object" && !Array.isArray(asset)) {
164
+ const url = pickString(asset, "url");
165
+ if (!url) throw new CliError(`executa.json \`distribution.binary_urls["${platform}"]\` asset dict missing required \`url\``, 4);
166
+ out[platform] = asset;
167
+ continue;
168
+ }
169
+ throw new CliError(`executa.json \`distribution.binary_urls["${platform}"]\` must be a URL string or an asset dict \`{ url, sha256?, size?, entrypoint?, format? }\``, 4);
170
+ }
171
+ return out;
172
+ }
173
+ function parseCapabilities(value) {
174
+ if (value === void 0 || value === null) return void 0;
175
+ if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) throw new CliError("executa.json `distribution.capabilities` must be an array of strings", 4);
176
+ return value;
177
+ }
178
+ function pickBool(o, k) {
179
+ const v = o[k];
180
+ return typeof v === "boolean" ? v : void 0;
181
+ }
182
+ function pickString(o, k) {
183
+ const v = o[k];
184
+ return typeof v === "string" ? v : void 0;
185
+ }
186
+
187
+ //#endregion
188
+ export { loadAppManifest, loadExecutaManifest };