@runtypelabs/cli 2.59.0 → 2.59.2

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 (84) hide show
  1. package/dist/chunk-0-T9NWjH.js +3899 -0
  2. package/dist/chunk-0x7UKC1U.js +17 -0
  3. package/dist/chunk-2ySxXtcg.js +418 -0
  4. package/dist/chunk-3An_BRHv.js +501 -0
  5. package/dist/chunk-6uTxgdiY.js +16218 -0
  6. package/dist/chunk-87nRnNRG.js +1363 -0
  7. package/dist/chunk-B3wo8uRx.js +322 -0
  8. package/dist/chunk-BGp3ZdBy.js +560 -0
  9. package/dist/chunk-BJQv9Elp.js +107 -0
  10. package/dist/chunk-BOpO6xE-.js +6053 -0
  11. package/dist/chunk-BQXXCL8B.js +102 -0
  12. package/dist/chunk-BV-8isOd.js +34 -0
  13. package/dist/chunk-BVMhjGVe.js +826 -0
  14. package/dist/chunk-BW2fg46T.js +141 -0
  15. package/dist/chunk-BWJdY5Ag.js +36 -0
  16. package/dist/chunk-BYJOrLhh.js +302 -0
  17. package/dist/chunk-BYab8EVL.js +127 -0
  18. package/dist/chunk-BfEKvp7h.js +82 -0
  19. package/dist/chunk-BhgLSfH6.js +1123 -0
  20. package/dist/chunk-BirUxTtH.js +296 -0
  21. package/dist/chunk-BmB6UVZ9.js +1559 -0
  22. package/dist/chunk-BnWhIqW1.js +109 -0
  23. package/dist/chunk-Bpfnfk0t.js +123 -0
  24. package/dist/chunk-BrhgdnM3.js +108 -0
  25. package/dist/chunk-Bt0XAgX5.js +327 -0
  26. package/dist/chunk-Bu0FJr6S.js +84 -0
  27. package/dist/chunk-BvEoKpN3.js +255 -0
  28. package/dist/chunk-BxNJgYbE.js +28 -0
  29. package/dist/chunk-C1OFhd7F.js +6131 -0
  30. package/dist/chunk-C7e7WwbU.js +331 -0
  31. package/dist/chunk-CDTYP6bN.js +558 -0
  32. package/dist/chunk-CGDQoqDC.js +24 -0
  33. package/dist/chunk-CJraEBVd.js +127 -0
  34. package/dist/chunk-CQrNhOtv.js +40 -0
  35. package/dist/chunk-CW6AzBH6.js +128 -0
  36. package/dist/chunk-CXgL4w-9.js +317 -0
  37. package/dist/chunk-CZrqoPyf.js +233 -0
  38. package/dist/chunk-CcFCyqbB.js +83 -0
  39. package/dist/chunk-CdaIvO-y.js +485 -0
  40. package/dist/chunk-CehSXtgK.js +624 -0
  41. package/dist/chunk-Ciqkop65.js +218 -0
  42. package/dist/chunk-CoAWdSiE.js +43 -0
  43. package/dist/chunk-Crb4y7t4.js +830 -0
  44. package/dist/chunk-CrlqIfmb.js +337 -0
  45. package/dist/chunk-Cs4pwCQ-.js +324 -0
  46. package/dist/chunk-CwM_9y01.js +519 -0
  47. package/dist/chunk-CyRbIJg8.js +440 -0
  48. package/dist/chunk-D0OHsyMy.js +212 -0
  49. package/dist/chunk-D3y2bURs.js +1347 -0
  50. package/dist/chunk-D4_mJk03.js +2338 -0
  51. package/dist/chunk-D6C8rned.js +578 -0
  52. package/dist/chunk-D9ZbUnks.js +11 -0
  53. package/dist/chunk-DEPmLshX.js +834 -0
  54. package/dist/chunk-DFj1UPB2.js +330 -0
  55. package/dist/chunk-DGdFdZVf.js +197 -0
  56. package/dist/chunk-DGv7X9S_.js +63 -0
  57. package/dist/chunk-DJQ47WYZ.js +664 -0
  58. package/dist/chunk-DJw3O61j.js +1974 -0
  59. package/dist/chunk-DK4FbWVK.js +100 -0
  60. package/dist/chunk-DSRYhUus.js +993 -0
  61. package/dist/chunk-DajZ9Eg4.js +299 -0
  62. package/dist/chunk-Df4dunvU.js +195 -0
  63. package/dist/chunk-DjNTPhfO.js +481 -0
  64. package/dist/chunk-Dn8eaJuu.js +1042 -0
  65. package/dist/chunk-DwDWqSxM.js +637 -0
  66. package/dist/chunk-Dz7cEtxl.js +84 -0
  67. package/dist/chunk-E_0bgjrS.js +359 -0
  68. package/dist/chunk-KPYN2vUp.js +776 -0
  69. package/dist/chunk-KcqPQJBi.js +86 -0
  70. package/dist/chunk-N9H0JnoG.js +349 -0
  71. package/dist/chunk-NONFcXkn.js +728 -0
  72. package/dist/chunk-PrFjcpmH.js +256 -0
  73. package/dist/chunk-QSfNYiw9.js +11 -0
  74. package/dist/chunk-R265bzj9.js +226 -0
  75. package/dist/chunk-X9V_r6jM.js +166 -0
  76. package/dist/chunk-ZBtPUtyA.js +55 -0
  77. package/dist/chunk-ZCTfmOs7.js +4939 -0
  78. package/dist/chunk-h6oavj0t.js +97 -0
  79. package/dist/chunk-i7DwkvvD.js +42 -0
  80. package/dist/chunk-mG-4eZ6S.js +203 -0
  81. package/dist/chunk-o0ygUbCf.js +313 -0
  82. package/dist/chunk-ucu5lrDZ.js +5715 -0
  83. package/dist/index.js +11367 -73764
  84. package/package.json +6 -6
@@ -0,0 +1,993 @@
1
+ #!/usr/bin/env node
2
+ import { t as isTTY } from "./index.js";
3
+ import { ConfirmPrompt, DataList, EntityCard, MutationResult, React, render, useEffect, useState } from "./chunk-CoAWdSiE.js";
4
+ import "./chunk-6uTxgdiY.js";
5
+ import { r as createCliClient } from "./chunk-BW2fg46T.js";
6
+ import { n as ensureAuth } from "./chunk-CJraEBVd.js";
7
+ import { a as printJson, i as printDetail, n as buildListParams, r as getTotalCount, s as printNextCursorHint } from "./chunk-CcFCyqbB.js";
8
+ import { taskCommand } from "./chunk-ZCTfmOs7.js";
9
+ import { t as readJsonFile } from "./chunk-DGv7X9S_.js";
10
+ import { n as addExecutionInputOptions, r as resolveExecutionInput, t as runExecution } from "./chunk-Cs4pwCQ-.js";
11
+ import { f as writePulledDefinition, o as agentSkillsCommand } from "./chunk-BirUxTtH.js";
12
+ import { r as describeAgentExportFailure, t as agentReleaseSelectorParams } from "./chunk-87nRnNRG.js";
13
+ import { t as buildAgentVersionSelector } from "./chunk-BxNJgYbE.js";
14
+ import { Command } from "commander";
15
+ import chalk from "chalk";
16
+ import * as fs$1 from "node:fs";
17
+ import { writeFileSync } from "node:fs";
18
+ import * as path$1 from "node:path";
19
+ import { ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE, RuntypeApiError, activateAgentPromotion, prepareAgentPromotion, validateAgentPromotion } from "@runtypelabs/sdk";
20
+ // src/commands/agent-aliases.ts
21
+ /** Print a failure the way every other CLI command does, then exit non-zero. */
22
+ function fail$1(headline, error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ console.error(chalk.red(headline));
25
+ console.error(chalk.red(message));
26
+ process.exit(1);
27
+ }
28
+ function describeAlias(row) {
29
+ const version = row.versionNumber !== null ? `v${row.versionNumber}` : row.versionId;
30
+ const label = row.label ? ` ${row.label}` : "";
31
+ const archived = row.archivedAt ? chalk.gray(" [archived]") : "";
32
+ const expires = row.expiresAt ? chalk.gray(` expires ${row.expiresAt}`) : "";
33
+ return ` ${chalk.green(row.alias)} ${version}${label} ${chalk.gray(`rev ${row.revision}`)}${archived}${expires}`;
34
+ }
35
+ const agentAliasesCommand = new Command("aliases").description("Manage the release aliases that select which agent version runs");
36
+ agentAliasesCommand.command("list <agentId>").description("List release aliases for an agent").option("--include-archived", "Include archived aliases, which never resolve for execution").option("--limit <n>", "Maximum aliases to return").option("--cursor <cursor>", "Pagination cursor from a previous page").option("--json", "Output as JSON").action(async (agentId, options) => {
37
+ const apiKey = await ensureAuth();
38
+ if (!apiKey) return;
39
+ const client = createCliClient(apiKey);
40
+ try {
41
+ const result = await client.agents.aliases.list(agentId, {
42
+ ...options.includeArchived ? { includeArchived: true } : {},
43
+ ...options.limit ? { limit: Number(options.limit) } : {},
44
+ ...options.cursor ? { cursor: options.cursor } : {}
45
+ });
46
+ if (options.json) {
47
+ printJson(result);
48
+ return;
49
+ }
50
+ const rows = result.data ?? [];
51
+ if (rows.length === 0) {
52
+ console.log(chalk.gray("No release aliases found"));
53
+ return;
54
+ }
55
+ console.log(chalk.cyan(`Release aliases for agent ${agentId}:`));
56
+ for (const row of rows) console.log(describeAlias(row));
57
+ if (result.nextCursor) console.log(chalk.gray(`Next page: --cursor ${result.nextCursor}`));
58
+ } catch (error) {
59
+ fail$1("Failed to list release aliases", error);
60
+ }
61
+ });
62
+ agentAliasesCommand.command("get <agentId> <alias>").description("Get one release alias").option("--include-archived", "Resolve the alias even when it is archived").option("--json", "Output as JSON").action(async (agentId, alias, options) => {
63
+ const apiKey = await ensureAuth();
64
+ if (!apiKey) return;
65
+ const client = createCliClient(apiKey);
66
+ try {
67
+ const row = await client.agents.aliases.get(agentId, alias, { ...options.includeArchived ? { includeArchived: true } : {} });
68
+ if (options.json) {
69
+ printJson(row);
70
+ return;
71
+ }
72
+ printDetail("Release alias", [
73
+ {
74
+ label: "Alias",
75
+ value: row.alias
76
+ },
77
+ {
78
+ label: "Version",
79
+ value: row.versionId
80
+ },
81
+ {
82
+ label: "Version number",
83
+ value: row.versionNumber
84
+ },
85
+ {
86
+ label: "Label",
87
+ value: row.label
88
+ },
89
+ {
90
+ label: "Revision",
91
+ value: row.revision
92
+ },
93
+ {
94
+ label: "Content hash",
95
+ value: row.contentHash
96
+ },
97
+ {
98
+ label: "Expires",
99
+ value: row.expiresAt
100
+ },
101
+ {
102
+ label: "Archived",
103
+ value: row.archivedAt
104
+ }
105
+ ]);
106
+ } catch (error) {
107
+ fail$1("Failed to fetch the release alias", error);
108
+ }
109
+ });
110
+ agentAliasesCommand.command("activate <agentId> <alias>").description("Activate a version at a release alias").requiredOption("--version-id <id>", "The exact version to aim the alias at").option("--revision <n>", "The alias revision you read, sent as If-Match. Required for live").option("--idempotency-key <key>", "Replay key so a retry does not deploy twice").option("--reason <text>", "Why this deployment happened, recorded on the receipt").option("--json", "Output as JSON").action(async (agentId, alias, options) => {
111
+ const apiKey = await ensureAuth();
112
+ if (!apiKey) return;
113
+ const client = createCliClient(apiKey);
114
+ try {
115
+ const result = await client.agents.aliases.activate(agentId, alias, {
116
+ versionId: options.versionId,
117
+ ...options.revision ? { revision: Number(options.revision) } : {},
118
+ ...options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
119
+ ...options.reason ? { reason: options.reason } : {}
120
+ });
121
+ if (options.json) {
122
+ printJson(result);
123
+ return;
124
+ }
125
+ console.log(chalk.green(`${result.changed ? "Deployed" : "Already deployed"} ${result.versionId} at "${result.alias}" (revision ${result.revision})`));
126
+ console.log(chalk.gray(`Receipt ${result.receiptId}`));
127
+ } catch (error) {
128
+ fail$1("Failed to activate the release alias", error);
129
+ }
130
+ });
131
+ /**
132
+ * `archive <agentId> <alias>` archives one pointer; `archive <alias> --all`
133
+ * archives that name on every agent in the organization. The first positional
134
+ * changes meaning with `--all`, which is what keeps the PR-close call a one-liner.
135
+ */
136
+ async function archiveAliasEverywhere(alias, options) {
137
+ const apiKey = await ensureAuth();
138
+ if (!apiKey) return;
139
+ const client = createCliClient(apiKey);
140
+ try {
141
+ if (options.dryRun) {
142
+ const rows = (await client.agents.aliases.listByName(alias)).data ?? [];
143
+ if (options.json) {
144
+ printJson({
145
+ alias,
146
+ dryRun: true,
147
+ wouldArchive: rows
148
+ });
149
+ return;
150
+ }
151
+ if (rows.length === 0) {
152
+ console.log(chalk.gray(`No active "${alias}" aliases to archive`));
153
+ return;
154
+ }
155
+ console.log(chalk.cyan(`Would archive "${alias}" on ${rows.length} agent(s):`));
156
+ for (const row of rows) console.log(` ${chalk.green(row.agentId)} ${row.agentName} ${chalk.gray(`rev ${row.revision}`)}`);
157
+ return;
158
+ }
159
+ const result = await client.agents.aliases.archiveEverywhere(alias);
160
+ if (options.json) printJson(result);
161
+ else {
162
+ for (const row of result.archived) console.log(chalk.green(`Archived "${alias}" on ${row.agentId} (${row.agentName})`));
163
+ for (const row of result.failed) console.error(chalk.red(`Failed on ${row.agentId} (${row.agentName}): ${row.error}`));
164
+ if (result.archived.length === 0 && result.failed.length === 0) console.log(chalk.gray(`No active "${alias}" aliases to archive`));
165
+ }
166
+ if (result.failed.length > 0) process.exit(1);
167
+ } catch (error) {
168
+ fail$1("Failed to archive the release alias", error);
169
+ }
170
+ }
171
+ agentAliasesCommand.command("archive <agentIdOrAlias> [alias]").description("Archive a preview alias so it stops resolving for new executions. With --all, the first argument is the alias name and it is archived on every agent in the organization.").option("--all", "Archive this alias name on every agent in the organization").option("--dry-run", "Report what would be archived and archive nothing").option("--revision <n>", "The alias revision you read, sent as If-Match").option("--json", "Output as JSON").action(async (agentIdOrAlias, alias, options) => {
172
+ if (options.all) {
173
+ if (alias !== void 0) {
174
+ console.error(chalk.red("With --all, pass only the alias name: runtype agents aliases archive <alias> --all"));
175
+ process.exit(1);
176
+ }
177
+ await archiveAliasEverywhere(agentIdOrAlias, options);
178
+ return;
179
+ }
180
+ if (alias === void 0) {
181
+ console.error(chalk.red("Pass an agent id and an alias, or an alias name with --all."));
182
+ process.exit(1);
183
+ }
184
+ const apiKey = await ensureAuth();
185
+ if (!apiKey) return;
186
+ const client = createCliClient(apiKey);
187
+ try {
188
+ if (options.dryRun) {
189
+ const pointer = await client.agents.aliases.get(agentIdOrAlias, alias);
190
+ if (options.json) {
191
+ printJson({
192
+ agentId: agentIdOrAlias,
193
+ alias,
194
+ dryRun: true,
195
+ wouldArchive: pointer
196
+ });
197
+ return;
198
+ }
199
+ console.log(chalk.cyan(`Would archive "${pointer.alias}" on ${agentIdOrAlias} (revision ${pointer.revision})`));
200
+ return;
201
+ }
202
+ const result = await client.agents.aliases.archive(agentIdOrAlias, alias, { ...options.revision ? { revision: Number(options.revision) } : {} });
203
+ if (options.json) {
204
+ printJson(result);
205
+ return;
206
+ }
207
+ console.log(chalk.green(`Archived "${result.alias}" (revision ${result.revision})`));
208
+ } catch (error) {
209
+ fail$1("Failed to archive the release alias", error);
210
+ }
211
+ });
212
+ agentAliasesCommand.command("rollback <agentId> <alias>").description("Roll a release alias back to its previous version").option("--steps <n>", "How many pointer-moving receipts to walk back", "1").option("--revision <n>", "The alias revision you read, sent as If-Match. Required for live").option("--reason <text>", "Why this rollback happened, recorded on the receipt").option("--json", "Output as JSON").action(async (agentId, alias, options) => {
213
+ const apiKey = await ensureAuth();
214
+ if (!apiKey) return;
215
+ const client = createCliClient(apiKey);
216
+ try {
217
+ const result = await client.agents.aliases.rollback(agentId, alias, {
218
+ ...options.steps ? { steps: Number(options.steps) } : {},
219
+ ...options.revision ? { revision: Number(options.revision) } : {},
220
+ ...options.reason ? { reason: options.reason } : {}
221
+ });
222
+ if (options.json) {
223
+ printJson(result);
224
+ return;
225
+ }
226
+ console.log(chalk.green(`Rolled "${result.alias}" back to ${result.versionId} (revision ${result.revision})`));
227
+ console.log(chalk.gray(`Receipt ${result.receiptId}`));
228
+ } catch (error) {
229
+ fail$1("Failed to roll the release alias back", error);
230
+ }
231
+ });
232
+ const agentDeploymentsCommand = new Command("deployments").description("Read the append-only deployment history of an agent");
233
+ agentDeploymentsCommand.command("list <agentId>").description("List deployment receipts for an agent, newest first").option("--alias <name>", "Only receipts for this release alias").option("--limit <n>", "Maximum receipts to return").option("--cursor <cursor>", "Pagination cursor from a previous page").option("--json", "Output as JSON").action(async (agentId, options) => {
234
+ const apiKey = await ensureAuth();
235
+ if (!apiKey) return;
236
+ const client = createCliClient(apiKey);
237
+ try {
238
+ const result = await client.agents.deployments.list(agentId, {
239
+ ...options.alias ? { alias: options.alias } : {},
240
+ ...options.limit ? { limit: Number(options.limit) } : {},
241
+ ...options.cursor ? { cursor: options.cursor } : {}
242
+ });
243
+ if (options.json) {
244
+ printJson(result);
245
+ return;
246
+ }
247
+ const rows = result.data ?? [];
248
+ if (rows.length === 0) {
249
+ console.log(chalk.gray("No deployment receipts found"));
250
+ return;
251
+ }
252
+ console.log(chalk.cyan(`Deployment history for agent ${agentId}:`));
253
+ for (const row of rows) {
254
+ const from = row.previousVersionId ? `${row.previousVersionId} -> ` : "";
255
+ console.log(` ${chalk.green(row.alias)} ${row.action} ${from}${row.versionId} ${chalk.gray(`rev ${row.aliasRevision} ${row.createdAt}`)}`);
256
+ }
257
+ } catch (error) {
258
+ fail$1("Failed to list deployment receipts", error);
259
+ }
260
+ });
261
+ // src/commands/agent-ensure.ts
262
+ /**
263
+ * The activation the flags ask for. `release` and `deploy` are two spellings of
264
+ * one thing, so supplying both is refused before any request is sent.
265
+ */
266
+ function buildEnsureActivation(flags, bindings) {
267
+ if (flags.release !== void 0 && flags.deploy !== void 0) throw new Error(ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE);
268
+ if (bindings !== void 0 && flags.deploy === void 0) throw new Error("--bindings-file sets per-alias secret bindings, so it needs --deploy <alias>.");
269
+ if (flags.deploy !== void 0) return { deploy: {
270
+ alias: flags.deploy,
271
+ ...bindings !== void 0 ? { bindings } : {}
272
+ } };
273
+ if (flags.release !== void 0) return { release: flags.release };
274
+ return {};
275
+ }
276
+ /** The API refuses expectedRemoteHash on a preview deploy, since it guards live-row writes only. */
277
+ function acceptsExpectedRemoteHash(deploy) {
278
+ return deploy === void 0 || deploy === "live";
279
+ }
280
+ /**
281
+ * Parse a `--bindings-file` object of `{ SECRET_NAME: value }`. The whole object
282
+ * replaces the alias's bindings, so `{}` clears them.
283
+ */
284
+ function parseAliasBindings(file) {
285
+ const bindings = {};
286
+ for (const [name, value] of Object.entries(file)) {
287
+ if (typeof value !== "string") throw new Error(`Binding "${name}" must be a string secret value.`);
288
+ bindings[name] = value;
289
+ }
290
+ return bindings;
291
+ }
292
+ /** The file `agents ensure` reads back: the definition with its skills and capabilities at the top level. */
293
+ function agentPullToDefinitionFile(pulled) {
294
+ return {
295
+ ...pulled.definition,
296
+ ...pulled.skills !== void 0 ? { skills: pulled.skills } : {},
297
+ ...pulled.capabilities !== void 0 ? { capabilities: pulled.capabilities } : {}
298
+ };
299
+ }
300
+ /**
301
+ * Lift a definition file's top-level `skills` (the agent's complete binding set)
302
+ * out of the definition, which ensure hashes, and into the request.
303
+ */
304
+ function splitDefinitionSkills(file) {
305
+ if (!("skills" in file)) return { definition: file };
306
+ const { skills, ...definition } = file;
307
+ if (!Array.isArray(skills)) throw new Error("\"skills\" must be an array of { name, version?, enabled? } bindings.");
308
+ return {
309
+ definition,
310
+ skills
311
+ };
312
+ }
313
+ /**
314
+ * Lift a definition file's top-level `skills` and `capabilities` (each the agent's
315
+ * complete set) out of the definition and into the request.
316
+ */
317
+ function splitDefinitionAgentState(file) {
318
+ const { definition: withCapabilities, skills } = splitDefinitionSkills(file);
319
+ if (!("capabilities" in withCapabilities)) return {
320
+ definition: withCapabilities,
321
+ ...skills !== void 0 ? { skills } : {}
322
+ };
323
+ const { capabilities, ...definition } = withCapabilities;
324
+ if (!Array.isArray(capabilities)) throw new Error("\"capabilities\" must be an array of { toolName, flow | agent | tool, description?, parametersSchema?, enabled? } entries.");
325
+ return {
326
+ definition,
327
+ ...skills !== void 0 ? { skills } : {},
328
+ capabilities
329
+ };
330
+ }
331
+ function formatCapabilityChange(change) {
332
+ const target = change.kind && change.source ? ` (${change.kind}:${change.source})` : "";
333
+ return ` capability:${change.toolName} ${change.action}${target}`;
334
+ }
335
+ /**
336
+ * Whether a dry-run plan drifts from the definition: content, skill bindings, capabilities, or,
337
+ * when the flags ask for a deploy, the release pointer or its binding names (#9371).
338
+ */
339
+ function ensurePlanHasDrift(result, activation) {
340
+ if (result.result !== "plan") return false;
341
+ if (result.changes !== "none") return true;
342
+ if ((result.skillBindings ?? []).some((b) => b.action !== "unchanged")) return true;
343
+ if ((result.capabilityChanges ?? []).some((c) => c.action !== "unchanged")) return true;
344
+ return (activation.deploy !== void 0 || activation.release === "publish") && (result.deployment?.changed === true || result.deployment?.bindingsChanged === true);
345
+ }
346
+ function formatSkillBinding(binding) {
347
+ const pin = binding.version === null ? "published" : `v${binding.version}`;
348
+ return ` skill:${binding.name} ${binding.action} (${pin})`;
349
+ }
350
+ const agentEnsureCommand = new Command("ensure").description("Converge an agent definition from a JSON file, optionally deploying it").argument("<file>", "Agent definition JSON file, or - to read from stdin").option("--deploy <alias>", "Activate the converged version at this release alias").option("--release <mode>", "Compatibility spelling of deploy: none or publish").option("--dry-run", "Plan without writing").option("--expect-no-changes", "Plan and exit 1 on any drift (the CI gate)").option("--overwrite-managed", "Converge even when the last write came from the dashboard").option("--label <text>", "Version label recorded on the version this converge appends").option("--notes <text>", "Version notes recorded on the version this converge appends").option("--expected-remote-hash <hash>", "Apply only if the remote still hashes to this value (the remoteHash a dry run printed)").option("--bindings-file <file>", "JSON object of { SECRET_NAME: value } bound to the --deploy alias (replaces the whole set)").option("--json", "Output as JSON").action(async (file, options) => {
351
+ if (options.release !== void 0 && !["none", "publish"].includes(options.release)) {
352
+ console.error(chalk.red("--release must be none or publish"));
353
+ process.exit(1);
354
+ }
355
+ let activation;
356
+ try {
357
+ activation = buildEnsureActivation(options, options.bindingsFile !== void 0 ? parseAliasBindings(readJsonFile(options.bindingsFile, { label: "alias bindings" })) : void 0);
358
+ } catch (error) {
359
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
360
+ process.exit(1);
361
+ }
362
+ let parsed;
363
+ try {
364
+ parsed = splitDefinitionAgentState(readJsonFile(file, {
365
+ label: "agent definition",
366
+ unwrapKey: "agent"
367
+ }));
368
+ } catch (error) {
369
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
370
+ process.exit(1);
371
+ }
372
+ const { definition, skills, capabilities } = parsed;
373
+ const name = definition.name;
374
+ if (typeof name !== "string" || name.length === 0) {
375
+ console.error(chalk.red("The agent definition needs a non-empty \"name\"."));
376
+ process.exit(1);
377
+ }
378
+ const apiKey = await ensureAuth();
379
+ if (!apiKey) return;
380
+ const client = createCliClient(apiKey);
381
+ const dryRun = options.dryRun || options.expectNoChanges;
382
+ const version = options.label || options.notes ? {
383
+ ...options.label ? { label: options.label } : {},
384
+ ...options.notes ? { notes: options.notes } : {}
385
+ } : void 0;
386
+ try {
387
+ const result = await client.post("/agents/ensure", {
388
+ name,
389
+ definition,
390
+ ...dryRun ? { dryRun: true } : {},
391
+ ...options.overwriteManaged ? { onConflict: "overwrite" } : {},
392
+ ...version ? { version } : {},
393
+ ...options.expectedRemoteHash ? { expectedRemoteHash: options.expectedRemoteHash } : {},
394
+ ...skills !== void 0 ? { skills } : {},
395
+ ...capabilities !== void 0 ? { capabilities } : {},
396
+ ...activation
397
+ });
398
+ if (options.json) printJson(result);
399
+ else if (result.result === "plan") {
400
+ console.log(chalk.cyan(`Plan for "${name}": ${result.changes}`));
401
+ for (const key of result.changedKeys ?? []) console.log(` ${key}`);
402
+ if (result.remoteHash && acceptsExpectedRemoteHash(options.deploy)) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
403
+ } else {
404
+ console.log(chalk.green(`Agent "${name}" ${result.result} (${result.agentId})`));
405
+ if (result.versionId) console.log(chalk.gray(`Version ${result.versionId}`));
406
+ }
407
+ const skillChanges = (result.skillBindings ?? []).filter((b) => b.action !== "unchanged");
408
+ const capabilityChanges = (result.capabilityChanges ?? []).filter((change) => change.action !== "unchanged");
409
+ if (!options.json) {
410
+ for (const binding of skillChanges) console.log(formatSkillBinding(binding));
411
+ for (const change of capabilityChanges) console.log(formatCapabilityChange(change));
412
+ }
413
+ if (result.deployment && !options.json) {
414
+ const { alias, versionId, revision, changed, bindingsChanged } = result.deployment;
415
+ const planned = result.result === "plan";
416
+ const verb = changed ? planned ? "Would deploy over" : "Deployed" : "Already deployed";
417
+ console.log(chalk.green(`${verb} ${versionId} at "${alias}" (revision ${revision})`));
418
+ if (planned && bindingsChanged) console.log(` deploy:${alias} (bindings)`);
419
+ if (alias !== "live") console.log(chalk.gray("Live unchanged."));
420
+ }
421
+ if (!options.json) for (const warning of result._warnings ?? []) console.warn(chalk.yellow(`Warning: ${warning}`));
422
+ if (options.expectNoChanges && ensurePlanHasDrift(result, activation)) process.exit(1);
423
+ } catch (error) {
424
+ const message = error instanceof Error ? error.message : String(error);
425
+ console.error(chalk.red("Failed to converge the agent"));
426
+ console.error(chalk.red(message));
427
+ process.exit(1);
428
+ }
429
+ });
430
+ const agentPullCommand = new Command("pull").description("Print an agent as the definition file `agents ensure` reads (dashboard wins)").argument("<name>", "Agent name").option("-o, --out <file>", "Write the definition file here instead of stdout").option("--json", "Print the full pull response (definition, contentHash, provenance)").action(async (name, options) => {
431
+ const apiKey = await ensureAuth();
432
+ if (!apiKey) return;
433
+ const client = createCliClient(apiKey);
434
+ try {
435
+ const pulled = await client.get("/agents/pull", { name });
436
+ writePulledDefinition(options.json ? pulled : agentPullToDefinitionFile(pulled), {
437
+ out: options.out,
438
+ contentHash: pulled.contentHash,
439
+ warnings: pulled.warnings
440
+ });
441
+ } catch (error) {
442
+ console.error(chalk.red(`Failed to pull agent "${name}"`));
443
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
444
+ process.exit(1);
445
+ }
446
+ });
447
+ // src/commands/agent-promote.ts
448
+ const DEFAULT_SOURCE_KEY_ENV = "RUNTYPE_SOURCE_API_KEY";
449
+ const DEFAULT_TARGET_KEY_ENV = "RUNTYPE_TARGET_API_KEY";
450
+ const DEFAULT_MANIFEST_PATH = "promotion.json";
451
+ /** Print a failure the way every other CLI command does, then exit non-zero. */
452
+ function fail(headline, error) {
453
+ const message = error instanceof Error ? error.message : String(error);
454
+ console.error(chalk.red(headline));
455
+ console.error(chalk.red(message));
456
+ process.exit(1);
457
+ }
458
+ /**
459
+ * Read one organization's key out of the named environment variable. The key
460
+ * itself is never a command-line argument, so it cannot leak into shell history
461
+ * or a CI log of the invoked command.
462
+ */
463
+ function readKeyFromEnv(variable, role, env = process.env) {
464
+ const value = env[variable];
465
+ if (!value) throw new Error(`No ${role} API key: set ${variable} in the environment, or point --${role}-key-env at the variable that holds it. Never pass a key on the command line.`);
466
+ return value;
467
+ }
468
+ function transports(options, env = process.env) {
469
+ const sourceKey = readKeyFromEnv(options.sourceKeyEnv ?? DEFAULT_SOURCE_KEY_ENV, "source", env);
470
+ const targetKey = readKeyFromEnv(options.targetKeyEnv ?? DEFAULT_TARGET_KEY_ENV, "target", env);
471
+ return {
472
+ source: createCliClient(sourceKey, options.sourceUrl),
473
+ target: createCliClient(targetKey, options.targetUrl)
474
+ };
475
+ }
476
+ /** The commit this promotion ran from, taken from CI rather than by shelling out. */
477
+ function resolveCommit(explicit, env = process.env) {
478
+ return explicit || env.RUNTYPE_COMMIT || env.GITHUB_SHA || void 0;
479
+ }
480
+ function readManifest(path) {
481
+ return readJsonFile(path, { label: "promotion manifest" });
482
+ }
483
+ function addCredentialOptions(command) {
484
+ return command.option("--source-key-env <var>", "Environment variable holding the source organization API key", DEFAULT_SOURCE_KEY_ENV).option("--target-key-env <var>", "Environment variable holding the target organization API key", DEFAULT_TARGET_KEY_ENV).option("--source-url <url>", "API base URL for the source organization").option("--target-url <url>", "API base URL for the target organization");
485
+ }
486
+ const agentPromoteCommand = new Command("promote").description("Promote an agent between organizations: prepare, validate, evaluate, activate");
487
+ addCredentialOptions(agentPromoteCommand.command("prepare <name>").description("Pull an agent definition from the source organization and stage it at a preview alias in the target. Live is never touched and credentials never move.").requiredOption("--alias <name>", "Preview alias to stage the candidate at. Never live").option("--commit <sha>", "Source-control commit, defaulting to RUNTYPE_COMMIT or GITHUB_SHA").option("--manifest <path>", "Where to write the promotion manifest", DEFAULT_MANIFEST_PATH).option("--json", "Output the manifest as JSON")).action(async (name, options) => {
488
+ try {
489
+ const { source, target } = transports(options);
490
+ const commit = resolveCommit(options.commit);
491
+ const manifest = await prepareAgentPromotion({
492
+ source,
493
+ target,
494
+ name,
495
+ alias: options.alias,
496
+ ...commit ? { commit } : {}
497
+ });
498
+ writeFileSync(options.manifest, `${JSON.stringify(manifest, null, 2)}\n`);
499
+ if (options.json) {
500
+ printJson(manifest);
501
+ return;
502
+ }
503
+ console.log(chalk.green(`Staged ${manifest.target.versionId} at "${manifest.target.alias}" on agent ${manifest.target.agentId}`));
504
+ console.log(chalk.gray(`Live is unchanged. Manifest written to ${options.manifest}`));
505
+ } catch (error) {
506
+ fail("Failed to prepare the promotion", error);
507
+ }
508
+ });
509
+ addCredentialOptions(agentPromoteCommand.command("validate <manifest>").description("Re-plan the staged candidate in the target organization and report every named reference it could not resolve. Exits 1 when any reference is unresolved.").option("--json", "Output the validation result as JSON")).action(async (manifestPath, options) => {
510
+ try {
511
+ const manifest = readManifest(manifestPath);
512
+ const { source, target } = transports(options);
513
+ const validation = await validateAgentPromotion({
514
+ source,
515
+ target,
516
+ manifest
517
+ });
518
+ if (options.json) printJson(validation);
519
+ else {
520
+ console.log(chalk.cyan(`Plan for "${manifest.name}" in the target organization: ${validation.plan.changes}`));
521
+ for (const ref of validation.refs) {
522
+ const mark = ref.resolvedId ? chalk.green("resolved") : chalk.red("unresolved");
523
+ console.log(` ${mark} ${ref.ref}${ref.resolvedId ? ` -> ${ref.resolvedId}` : ""}`);
524
+ }
525
+ if (validation.ok) console.log(chalk.green("Every named reference resolves in the target organization."));
526
+ else console.error(chalk.red(`Unresolved in the target organization: ${validation.unresolvedRefs.join(", ")}`));
527
+ }
528
+ if (!validation.ok) process.exit(1);
529
+ } catch (error) {
530
+ fail("Failed to validate the promotion", error);
531
+ }
532
+ });
533
+ addCredentialOptions(agentPromoteCommand.command("evaluate <manifest>").description("Run an eval suite in the target organization pinned to the staged version. Reports the result; nothing gates activation on it.").requiredOption("--suite <id>", "Eval suite in the target organization to run").option("--json", "Output the run summary as JSON")).action(async (manifestPath, options) => {
534
+ let passed = true;
535
+ try {
536
+ const manifest = readManifest(manifestPath);
537
+ const targetKey = readKeyFromEnv(options.targetKeyEnv ?? DEFAULT_TARGET_KEY_ENV, "target");
538
+ const result = await createCliClient(targetKey, options.targetUrl).post(`/eval/suites/${options.suite}/run`, { agent: { versionId: manifest.target.versionId } });
539
+ passed = result.passed !== false;
540
+ if (options.json) printJson(result);
541
+ else {
542
+ console.log(chalk.cyan(`Suite ${options.suite} ran against ${manifest.target.versionId}: ${passed ? "passed" : "failed"}`));
543
+ if (result.runId) console.log(chalk.gray(`Run ${result.runId}`));
544
+ console.log(chalk.gray("Reporting only: activation is never gated on this result (ADR 0025)."));
545
+ }
546
+ } catch (error) {
547
+ fail("Failed to evaluate the promotion", error);
548
+ }
549
+ if (!passed) process.exit(1);
550
+ });
551
+ addCredentialOptions(agentPromoteCommand.command("activate <manifest>").description("Deploy the exact staged version at the target organization pointer, quoting its revision and linking the source ids on the receipt.").option("--alias <name>", "Pointer to deploy to in the target organization", "live").option("--reason <text>", "Why this deployment happened, recorded on the receipt").option("--idempotency-key <key>", "Replay key, defaulting to one derived from the manifest").option("--json", "Output the activation result as JSON")).action(async (manifestPath, options) => {
552
+ try {
553
+ const manifest = readManifest(manifestPath);
554
+ const targetKey = readKeyFromEnv(options.targetKeyEnv ?? DEFAULT_TARGET_KEY_ENV, "target");
555
+ const target = createCliClient(targetKey, options.targetUrl);
556
+ const result = await activateAgentPromotion({
557
+ target,
558
+ manifest,
559
+ alias: options.alias,
560
+ ...options.reason ? { reason: options.reason } : {},
561
+ ...options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
562
+ });
563
+ if (options.json) {
564
+ printJson(result);
565
+ return;
566
+ }
567
+ console.log(chalk.green(`${result.changed ? "Deployed" : "Already deployed"} ${result.versionId} at "${result.alias}" (revision ${result.revision})`));
568
+ console.log(chalk.gray(`Receipt ${result.receiptId}`));
569
+ } catch (error) {
570
+ fail("Failed to activate the promotion", error);
571
+ }
572
+ });
573
+ // src/commands/agent-export-runtime.ts
574
+ const agentExportRuntimeCommand = new Command("export-runtime").description("Export an agent for @runtypelabs/runtime, as JSON or as a vendorable bundle zip").argument("<id>", "Agent ID").option("--alias <name>", "Export the version the agent's release alias points at").option("--version-id <id>", "Export this published agent version").option("--format <format>", "json (the definition) or bundle (definition + prebuilt runtime zip)", "json").option("--output <path>", "Write to this file (bundle default: the server-provided file name)").action(async (id, options) => {
575
+ if (options.format !== "json" && options.format !== "bundle") {
576
+ console.error(chalk.red(`Error: unknown --format "${options.format}". Must be json or bundle.`));
577
+ process.exit(1);
578
+ }
579
+ const selector = agentReleaseSelectorParams(options, 1);
580
+ if ("error" in selector) {
581
+ console.error(chalk.red(`Error: ${selector.error}`));
582
+ process.exit(1);
583
+ }
584
+ const apiKey = await ensureAuth();
585
+ if (!apiKey) return;
586
+ const client = createCliClient(apiKey);
587
+ try {
588
+ if (options.format === "json") {
589
+ const exported = await client.agents.exportRuntime(id, selector.params);
590
+ const body = `${JSON.stringify(exported, null, 2)}\n`;
591
+ if (options.output) fs$1.writeFileSync(path$1.resolve(options.output), body);
592
+ else process.stdout.write(body);
593
+ return;
594
+ }
595
+ const bundle = await client.agents.exportRuntimeBundle(id, selector.params);
596
+ const outPath = path$1.resolve(options.output ?? bundle.fileName);
597
+ fs$1.writeFileSync(outPath, bundle.bytes);
598
+ console.log(JSON.stringify({
599
+ path: outPath,
600
+ bytes: bundle.bytes.byteLength,
601
+ contentHash: bundle.contentHash,
602
+ runtimeVersion: bundle.runtimeVersion,
603
+ runtimeCommit: bundle.runtimeCommit
604
+ }, null, 2));
605
+ } catch (err) {
606
+ const message = err instanceof RuntypeApiError ? err.message : String(err);
607
+ console.error(chalk.red(`Failed to export agent ${id}: ${message}`));
608
+ for (const hint of describeAgentExportFailure(err)) console.error(chalk.yellow(` ${hint}`));
609
+ process.exit(1);
610
+ }
611
+ });
612
+ // src/commands/agents.ts
613
+ const agentsCommand = new Command("agents").description("Manage agents");
614
+ agentsCommand.addCommand(taskCommand);
615
+ agentsCommand.addCommand(agentAliasesCommand);
616
+ agentsCommand.addCommand(agentDeploymentsCommand);
617
+ agentsCommand.addCommand(agentEnsureCommand);
618
+ agentsCommand.addCommand(agentPullCommand);
619
+ agentsCommand.addCommand(agentSkillsCommand);
620
+ agentsCommand.addCommand(agentPromoteCommand);
621
+ agentsCommand.addCommand(agentExportRuntimeCommand);
622
+ agentsCommand.command("list").description("List all agents").option("--json", "Output as JSON").option("--limit <n>", "Limit results", "20").option("--cursor <cursor>", "Pagination cursor for the next page").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
623
+ const apiKey = await ensureAuth();
624
+ if (!apiKey) return;
625
+ const client = createCliClient(apiKey);
626
+ const params = buildListParams(options);
627
+ if (!isTTY(options) || options.json) {
628
+ try {
629
+ const data = await client.get("/agents", params);
630
+ if (options.json) {
631
+ printJson(data);
632
+ return;
633
+ }
634
+ const agents = data.data ?? [];
635
+ if (agents.length === 0) {
636
+ console.log("No agents found");
637
+ return;
638
+ }
639
+ console.log("Your Agents:");
640
+ for (const agent of agents) {
641
+ const desc = agent.description ? ` - ${agent.description}` : "";
642
+ console.log(` ${agent.id} ${agent.name}${desc}`);
643
+ }
644
+ const total = getTotalCount(data.pagination);
645
+ if (total !== void 0) console.log(`\n Total: ${total} agents`);
646
+ printNextCursorHint("runtype agents list", data.pagination);
647
+ } catch (error) {
648
+ const message = error instanceof Error ? error.message : "Unknown error";
649
+ console.error(chalk.red(`Failed to fetch agents: ${message}`));
650
+ process.exit(1);
651
+ }
652
+ return;
653
+ }
654
+ const App = () => {
655
+ const [items, setItems] = useState(null);
656
+ const [error, setError] = useState(null);
657
+ const [total, setTotal] = useState();
658
+ useEffect(() => {
659
+ client.get("/agents", params).then((res) => {
660
+ setItems(res.data ?? []);
661
+ setTotal(getTotalCount(res.pagination));
662
+ }).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
663
+ }, []);
664
+ return React.createElement(DataList, {
665
+ title: "Your Agents",
666
+ items,
667
+ error,
668
+ loading: items === null && error === null,
669
+ total,
670
+ renderCard: (item) => {
671
+ const agent = item;
672
+ return React.createElement(EntityCard, { fields: [
673
+ {
674
+ label: "ID",
675
+ value: agent.id,
676
+ color: "green"
677
+ },
678
+ {
679
+ label: "Name",
680
+ value: agent.name
681
+ },
682
+ {
683
+ label: "Description",
684
+ value: agent.description,
685
+ color: "gray"
686
+ },
687
+ {
688
+ label: "Model",
689
+ value: agent.model,
690
+ color: "blue"
691
+ }
692
+ ] });
693
+ },
694
+ emptyMessage: "No agents found"
695
+ });
696
+ };
697
+ const { waitUntilExit } = render(React.createElement(App));
698
+ await waitUntilExit();
699
+ });
700
+ agentsCommand.command("get <id>").description("Get agent details").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
701
+ const apiKey = await ensureAuth();
702
+ if (!apiKey) return;
703
+ const client = createCliClient(apiKey);
704
+ if (!isTTY(options) || options.json) {
705
+ try {
706
+ const data = await client.get(`/agents/${id}`);
707
+ if (options.json) {
708
+ printJson(data);
709
+ return;
710
+ }
711
+ printDetail("Agent", [
712
+ {
713
+ label: "ID",
714
+ value: data.id
715
+ },
716
+ {
717
+ label: "Name",
718
+ value: data.name
719
+ },
720
+ {
721
+ label: "Description",
722
+ value: data.description
723
+ },
724
+ {
725
+ label: "Status",
726
+ value: data.status
727
+ },
728
+ {
729
+ label: "Model",
730
+ value: data.model
731
+ }
732
+ ]);
733
+ } catch (error) {
734
+ const message = error instanceof Error ? error.message : "Unknown error";
735
+ console.error(chalk.red(`Failed to fetch agent: ${message}`));
736
+ process.exit(1);
737
+ }
738
+ return;
739
+ }
740
+ const App = () => {
741
+ const [items, setItems] = useState(null);
742
+ const [error, setError] = useState(null);
743
+ useEffect(() => {
744
+ client.get(`/agents/${id}`).then((res) => setItems([res])).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
745
+ }, []);
746
+ return React.createElement(DataList, {
747
+ title: "Agent",
748
+ items,
749
+ error,
750
+ loading: items === null && error === null,
751
+ renderCard: (item) => {
752
+ const agent = item;
753
+ return React.createElement(EntityCard, { fields: [
754
+ {
755
+ label: "ID",
756
+ value: agent.id,
757
+ color: "green"
758
+ },
759
+ {
760
+ label: "Name",
761
+ value: agent.name
762
+ },
763
+ {
764
+ label: "Description",
765
+ value: agent.description
766
+ },
767
+ {
768
+ label: "Model",
769
+ value: agent.model,
770
+ color: "blue"
771
+ },
772
+ {
773
+ label: "Capabilities",
774
+ value: agent.capabilities?.length
775
+ }
776
+ ] });
777
+ }
778
+ });
779
+ };
780
+ const { waitUntilExit } = render(React.createElement(App));
781
+ await waitUntilExit();
782
+ });
783
+ agentsCommand.command("create").description("Create a new agent").requiredOption("-n, --name <name>", "Agent name").option("-d, --description <desc>", "Agent description").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
784
+ const apiKey = await ensureAuth();
785
+ if (!apiKey) return;
786
+ const client = createCliClient(apiKey);
787
+ if (!isTTY(options) || options.json) {
788
+ try {
789
+ const data = await client.post("/agents", {
790
+ name: options.name,
791
+ description: options.description
792
+ });
793
+ if (options.json) {
794
+ printJson(data);
795
+ return;
796
+ }
797
+ console.log(` ID: ${data.id}`);
798
+ console.log(` Name: ${data.name}`);
799
+ } catch (error) {
800
+ const message = error instanceof Error ? error.message : "Unknown error";
801
+ console.error(chalk.red(`Failed to create agent: ${message}`));
802
+ process.exit(1);
803
+ }
804
+ return;
805
+ }
806
+ const App = () => {
807
+ const [loading, setLoading] = useState(true);
808
+ const [success, setSuccess] = useState(null);
809
+ const [error, setError] = useState(null);
810
+ const [result, setResult] = useState(null);
811
+ useEffect(() => {
812
+ client.post("/agents", {
813
+ name: options.name,
814
+ description: options.description
815
+ }).then((data) => {
816
+ setResult(data);
817
+ setSuccess(true);
818
+ setLoading(false);
819
+ }).catch((err) => {
820
+ setError(err instanceof Error ? err : new Error(String(err)));
821
+ setSuccess(false);
822
+ setLoading(false);
823
+ });
824
+ }, []);
825
+ return React.createElement(MutationResult, {
826
+ loading,
827
+ loadingLabel: "Creating agent...",
828
+ success,
829
+ successMessage: "Agent created",
830
+ error,
831
+ result: result ? React.createElement(EntityCard, { fields: [{
832
+ label: "ID",
833
+ value: result.id,
834
+ color: "green"
835
+ }, {
836
+ label: "Name",
837
+ value: result.name
838
+ }] }) : void 0
839
+ });
840
+ };
841
+ const { waitUntilExit } = render(React.createElement(App));
842
+ await waitUntilExit();
843
+ });
844
+ agentsCommand.command("update <id>").description("Update an agent").option("-n, --name <name>", "New agent name").option("-d, --description <desc>", "New agent description").option("--from-file <file>", "Update agent from JSON file (full body)").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
845
+ const apiKey = await ensureAuth();
846
+ if (!apiKey) return;
847
+ const client = createCliClient(apiKey);
848
+ let body = {};
849
+ if (options.fromFile) body = readJsonFile(options.fromFile, {
850
+ label: "agent file",
851
+ unwrapKey: "agent"
852
+ });
853
+ if (options.name !== void 0) body.name = options.name;
854
+ if (options.description !== void 0) body.description = options.description;
855
+ if (Object.keys(body).length === 0) {
856
+ console.error(chalk.red("Error: provide at least one of --name, --description, or --from-file"));
857
+ process.exit(1);
858
+ }
859
+ if (!isTTY(options) || options.json) {
860
+ try {
861
+ const data = await client.put(`/agents/${id}`, body);
862
+ if (options.json) {
863
+ printJson(data);
864
+ return;
865
+ }
866
+ console.log(chalk.green("Agent updated"));
867
+ console.log(` ID: ${chalk.green(data.id)}`);
868
+ console.log(` Name: ${data.name}`);
869
+ } catch (error) {
870
+ const message = error instanceof Error ? error.message : "Unknown error";
871
+ console.error(chalk.red(`Failed to update agent: ${message}`));
872
+ process.exit(1);
873
+ }
874
+ return;
875
+ }
876
+ const App = () => {
877
+ const [loading, setLoading] = useState(true);
878
+ const [success, setSuccess] = useState(null);
879
+ const [error, setError] = useState(null);
880
+ const [result, setResult] = useState(null);
881
+ useEffect(() => {
882
+ client.put(`/agents/${id}`, body).then((data) => {
883
+ setResult(data);
884
+ setSuccess(true);
885
+ setLoading(false);
886
+ }).catch((err) => {
887
+ setError(err instanceof Error ? err : new Error(String(err)));
888
+ setSuccess(false);
889
+ setLoading(false);
890
+ });
891
+ }, []);
892
+ return React.createElement(MutationResult, {
893
+ loading,
894
+ loadingLabel: "Updating agent...",
895
+ success,
896
+ successMessage: "Agent updated",
897
+ error,
898
+ result: result ? React.createElement(EntityCard, { fields: [{
899
+ label: "ID",
900
+ value: result.id,
901
+ color: "green"
902
+ }, {
903
+ label: "Name",
904
+ value: result.name
905
+ }] }) : void 0
906
+ });
907
+ };
908
+ const { waitUntilExit } = render(React.createElement(App));
909
+ await waitUntilExit();
910
+ });
911
+ agentsCommand.command("delete <id>").description("Delete an agent").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
912
+ const apiKey = await ensureAuth();
913
+ if (!apiKey) return;
914
+ const client = createCliClient(apiKey);
915
+ if (!isTTY(options)) {
916
+ try {
917
+ await client.agents.delete(id);
918
+ console.log(JSON.stringify({
919
+ status: "deleted",
920
+ id
921
+ }, null, 2));
922
+ } catch (error) {
923
+ const message = error instanceof Error ? error.message : "Unknown error";
924
+ console.error(chalk.red(`Failed to delete agent: ${message}`));
925
+ process.exit(1);
926
+ }
927
+ return;
928
+ }
929
+ const App = () => {
930
+ const [confirmed, setConfirmed] = useState(null);
931
+ const [loading, setLoading] = useState(false);
932
+ const [success, setSuccess] = useState(null);
933
+ const [error, setError] = useState(null);
934
+ useEffect(() => {
935
+ if (confirmed !== true) return void 0;
936
+ setLoading(true);
937
+ client.delete(`/agents/${id}`).then(() => {
938
+ setSuccess(true);
939
+ setLoading(false);
940
+ }).catch((err) => {
941
+ setError(err instanceof Error ? err : new Error(String(err)));
942
+ setSuccess(false);
943
+ setLoading(false);
944
+ });
945
+ }, [confirmed]);
946
+ if (confirmed === null) return React.createElement(ConfirmPrompt, {
947
+ message: `Delete agent ${id}?`,
948
+ onConfirm: (val) => setConfirmed(val)
949
+ });
950
+ if (confirmed === false) return React.createElement(MutationResult, {
951
+ loading: false,
952
+ success: true,
953
+ successMessage: "Cancelled",
954
+ error: null
955
+ });
956
+ return React.createElement(MutationResult, {
957
+ loading,
958
+ loadingLabel: "Deleting agent...",
959
+ success,
960
+ successMessage: "Agent deleted",
961
+ error
962
+ });
963
+ };
964
+ const { waitUntilExit } = render(React.createElement(App));
965
+ await waitUntilExit();
966
+ });
967
+ addExecutionInputOptions(agentsCommand.command("execute <id>").description("Execute an agent").option("-m, --message <text>", "Message to send").option("--stream", "Stream the response (default)", true).option("--no-stream", "Wait for complete response").option("--alias <name>", "Run whichever version this release alias points at").option("--version-id <id>", "Run this exact version snapshot").option("--json", "Output one final JSON result").option("--jsonl", "Output stream events and a terminal execution_summary as JSON lines")).action(async (id, options) => {
968
+ const hasInputs = options.inputs !== void 0 || options.inputsFile !== void 0 || !!options.variable?.length;
969
+ await runExecution(options, () => {
970
+ if (hasInputs) {
971
+ const input = resolveExecutionInput(options);
972
+ if (!input.messages?.length) throw new Error("Either --message or --messages-file is required");
973
+ const selector = buildAgentVersionSelector(options);
974
+ return {
975
+ ...input,
976
+ agent: {
977
+ agentId: id,
978
+ ...selector ?? {}
979
+ }
980
+ };
981
+ }
982
+ const { messages } = resolveExecutionInput(options, true);
983
+ return {
984
+ messages,
985
+ ...buildAgentVersionSelector(options) ?? {}
986
+ };
987
+ }, (client, payload) => {
988
+ if (hasInputs) return options.stream ? client.dispatch.executeStream(payload) : client.dispatch.execute(payload);
989
+ return options.stream ? client.agents.executeStream(id, payload) : client.agents.execute(id, payload);
990
+ }, "agent");
991
+ });
992
+ export { agentsCommand };
993
+