@usenaive-sdk/cli 0.12.5 → 0.13.1

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 (164) hide show
  1. package/README.md +69 -6
  2. package/dist/client.d.ts +48 -0
  3. package/dist/client.js +199 -27
  4. package/dist/client.js.map +1 -1
  5. package/dist/commands/aeo.js +58 -23
  6. package/dist/commands/aeo.js.map +1 -1
  7. package/dist/commands/app-data.js +29 -11
  8. package/dist/commands/app-data.js.map +1 -1
  9. package/dist/commands/approvals.js +142 -9
  10. package/dist/commands/approvals.js.map +1 -1
  11. package/dist/commands/apps.d.ts +7 -0
  12. package/dist/commands/apps.js +255 -18
  13. package/dist/commands/apps.js.map +1 -1
  14. package/dist/commands/audio.d.ts +9 -0
  15. package/dist/commands/audio.js +391 -0
  16. package/dist/commands/audio.js.map +1 -0
  17. package/dist/commands/auth.d.ts +1 -0
  18. package/dist/commands/auth.js +47 -19
  19. package/dist/commands/auth.js.map +1 -1
  20. package/dist/commands/billing.js +9 -9
  21. package/dist/commands/billing.js.map +1 -1
  22. package/dist/commands/brain.js +813 -54
  23. package/dist/commands/brain.js.map +1 -1
  24. package/dist/commands/browser.js +8 -8
  25. package/dist/commands/browser.js.map +1 -1
  26. package/dist/commands/business.js +117 -16
  27. package/dist/commands/business.js.map +1 -1
  28. package/dist/commands/clone.js +8 -4
  29. package/dist/commands/clone.js.map +1 -1
  30. package/dist/commands/companies.d.ts +8 -0
  31. package/dist/commands/companies.js +137 -47
  32. package/dist/commands/companies.js.map +1 -1
  33. package/dist/commands/company-data.d.ts +2 -0
  34. package/dist/commands/company-data.js +168 -0
  35. package/dist/commands/company-data.js.map +1 -0
  36. package/dist/commands/compute.js +295 -18
  37. package/dist/commands/compute.js.map +1 -1
  38. package/dist/commands/connections.d.ts +6 -0
  39. package/dist/commands/connections.js +42 -2
  40. package/dist/commands/connections.js.map +1 -1
  41. package/dist/commands/domains.js +63 -19
  42. package/dist/commands/domains.js.map +1 -1
  43. package/dist/commands/ecommerce.js +19 -9
  44. package/dist/commands/ecommerce.js.map +1 -1
  45. package/dist/commands/email.js +56 -9
  46. package/dist/commands/email.js.map +1 -1
  47. package/dist/commands/formation.js +5 -5
  48. package/dist/commands/formation.js.map +1 -1
  49. package/dist/commands/identity.js +4 -3
  50. package/dist/commands/identity.js.map +1 -1
  51. package/dist/commands/images.js +86 -12
  52. package/dist/commands/images.js.map +1 -1
  53. package/dist/commands/init.d.ts +3 -0
  54. package/dist/commands/init.js +44 -36
  55. package/dist/commands/init.js.map +1 -1
  56. package/dist/commands/jobs.js +1 -0
  57. package/dist/commands/jobs.js.map +1 -1
  58. package/dist/commands/legacy-auth.d.ts +27 -0
  59. package/dist/commands/legacy-auth.js +64 -0
  60. package/dist/commands/legacy-auth.js.map +1 -0
  61. package/dist/commands/llm.js +14 -1
  62. package/dist/commands/llm.js.map +1 -1
  63. package/dist/commands/login.js +2 -0
  64. package/dist/commands/login.js.map +1 -1
  65. package/dist/commands/logout.d.ts +7 -0
  66. package/dist/commands/logout.js +55 -0
  67. package/dist/commands/logout.js.map +1 -0
  68. package/dist/commands/media.js +3 -3
  69. package/dist/commands/media.js.map +1 -1
  70. package/dist/commands/mobile.d.ts +2 -0
  71. package/dist/commands/mobile.js +42 -1
  72. package/dist/commands/mobile.js.map +1 -1
  73. package/dist/commands/payments.js +1 -1
  74. package/dist/commands/people.d.ts +2 -0
  75. package/dist/commands/people.js +197 -0
  76. package/dist/commands/people.js.map +1 -0
  77. package/dist/commands/phone.js +40 -83
  78. package/dist/commands/phone.js.map +1 -1
  79. package/dist/commands/projects.d.ts +13 -0
  80. package/dist/commands/projects.js +220 -0
  81. package/dist/commands/projects.js.map +1 -0
  82. package/dist/commands/register.js +13 -5
  83. package/dist/commands/register.js.map +1 -1
  84. package/dist/commands/sandbox.d.ts +2 -0
  85. package/dist/commands/sandbox.js +558 -0
  86. package/dist/commands/sandbox.js.map +1 -0
  87. package/dist/commands/search.js +27 -13
  88. package/dist/commands/search.js.map +1 -1
  89. package/dist/commands/seo.js +16 -8
  90. package/dist/commands/seo.js.map +1 -1
  91. package/dist/commands/sessions.d.ts +6 -0
  92. package/dist/commands/sessions.js +109 -0
  93. package/dist/commands/sessions.js.map +1 -0
  94. package/dist/commands/social-data.d.ts +2 -0
  95. package/dist/commands/social-data.js +245 -0
  96. package/dist/commands/social-data.js.map +1 -0
  97. package/dist/commands/social.js +9 -7
  98. package/dist/commands/social.js.map +1 -1
  99. package/dist/commands/tasks.js +9 -0
  100. package/dist/commands/tasks.js.map +1 -1
  101. package/dist/commands/teams.d.ts +69 -0
  102. package/dist/commands/teams.js +503 -0
  103. package/dist/commands/teams.js.map +1 -0
  104. package/dist/commands/up.d.ts +150 -0
  105. package/dist/commands/up.js +465 -15
  106. package/dist/commands/up.js.map +1 -1
  107. package/dist/commands/usage.js +15 -4
  108. package/dist/commands/usage.js.map +1 -1
  109. package/dist/commands/use.d.ts +9 -0
  110. package/dist/commands/use.js +76 -11
  111. package/dist/commands/use.js.map +1 -1
  112. package/dist/commands/users.d.ts +8 -0
  113. package/dist/commands/users.js +28 -9
  114. package/dist/commands/users.js.map +1 -1
  115. package/dist/commands/vault.js +31 -1
  116. package/dist/commands/vault.js.map +1 -1
  117. package/dist/commands/verification.js +1 -1
  118. package/dist/commands/verification.js.map +1 -1
  119. package/dist/commands/video.js +118 -19
  120. package/dist/commands/video.js.map +1 -1
  121. package/dist/commands/wallet.js +3 -3
  122. package/dist/commands/wallet.js.map +1 -1
  123. package/dist/commands/whoami.js +17 -1
  124. package/dist/commands/whoami.js.map +1 -1
  125. package/dist/config.d.ts +16 -1
  126. package/dist/config.js +15 -1
  127. package/dist/config.js.map +1 -1
  128. package/dist/index.d.ts +2 -1
  129. package/dist/index.js +250 -24
  130. package/dist/index.js.map +1 -1
  131. package/dist/lib/brain-select.d.ts +193 -0
  132. package/dist/lib/brain-select.js +403 -0
  133. package/dist/lib/brain-select.js.map +1 -0
  134. package/dist/lib/cli-validation.d.ts +12 -0
  135. package/dist/lib/cli-validation.js +46 -0
  136. package/dist/lib/cli-validation.js.map +1 -0
  137. package/dist/lib/deprecation.d.ts +158 -0
  138. package/dist/lib/deprecation.js +140 -0
  139. package/dist/lib/deprecation.js.map +1 -0
  140. package/dist/lib/duration.d.ts +8 -0
  141. package/dist/lib/duration.js +11 -0
  142. package/dist/lib/duration.js.map +1 -0
  143. package/dist/lib/http-response.d.ts +11 -0
  144. package/dist/lib/http-response.js +96 -0
  145. package/dist/lib/http-response.js.map +1 -0
  146. package/dist/lib/pricing.d.ts +132 -0
  147. package/dist/lib/pricing.js +148 -0
  148. package/dist/lib/pricing.js.map +1 -0
  149. package/dist/lib/stream-frames.d.ts +114 -0
  150. package/dist/lib/stream-frames.js +169 -0
  151. package/dist/lib/stream-frames.js.map +1 -0
  152. package/dist/lib/teams-catalog.d.ts +141 -0
  153. package/dist/lib/teams-catalog.js +765 -0
  154. package/dist/lib/teams-catalog.js.map +1 -0
  155. package/dist/lib/unknown-command.d.ts +55 -1
  156. package/dist/lib/unknown-command.js +113 -0
  157. package/dist/lib/unknown-command.js.map +1 -1
  158. package/dist/output.d.ts +127 -5
  159. package/dist/output.js +189 -12
  160. package/dist/output.js.map +1 -1
  161. package/package.json +3 -3
  162. package/dist/commands/link.d.ts +0 -3
  163. package/dist/commands/link.js +0 -91
  164. package/dist/commands/link.js.map +0 -1
@@ -0,0 +1,403 @@
1
+ /**
2
+ * MULTI-BRAIN ADDRESSING FOR THE CLI.
3
+ *
4
+ * ── the model this reconciles toward ────────────────────────────────────────
5
+ * `brain_knowledge_bases` (packages/db/src/product/brain.ts:50-92) is
6
+ * `(company_id, name, is_default, …)` with a PARTIAL unique index on ONE default
7
+ * per company. The database has always supported N brains per company; so has
8
+ * the API (`GET /v1/brain` lists, `POST /v1/brain` creates, and every content
9
+ * verb takes `knowledge_base_id`). What neither supports is addressing a brain
10
+ * by NAME: `resolveKnowledgeBase` (platform/brain/src/brain.ts:318-330) rejects
11
+ * a non-UUID with `resource_not_found` before it ever looks at the row.
12
+ *
13
+ * So the naming layer lives here, in the client, where it costs no route and no
14
+ * governance decision: `--brain <name|id>` is resolved against `GET /v1/brain`
15
+ * and only ever a UUID reaches the wire.
16
+ *
17
+ * ── what does NOT change ────────────────────────────────────────────────────
18
+ * `--kb <uuid>` is the shipped spelling and stays byte-identical: a value that
19
+ * already looks like a UUID is passed straight through with ZERO extra
20
+ * requests, exactly as before. The list is fetched only for a value that could
21
+ * not have worked before (a non-UUID, which the API refused) or for `--brain`,
22
+ * which is new. See __tests__/brain-multi.test.ts.
23
+ */
24
+ import { apiRequest, handleApiError } from "../client.js";
25
+ import { agentError } from "../output.js";
26
+ /**
27
+ * THE BINDING KEY, and it is deliberately NOT a new word.
28
+ *
29
+ * `brain_knowledge_base_id` is the exact spelling `POST /v1/runs` already
30
+ * accepts to point a run's reflexive-brain attach at a knowledge base
31
+ * (platform/brain/src/contracts/brain-ops.ts:98, packages/api/src/routes/runs.ts:104).
32
+ * Binding an AGENT to a brain records the value that surface already consumes
33
+ * under the name it already uses, rather than minting a second one.
34
+ */
35
+ export const BRAIN_BINDING_KEY = "brain_knowledge_base_id";
36
+ /**
37
+ * THE COMPANION KEY, written by the brain surface and by nothing else.
38
+ *
39
+ * `brain_connected_at` is stamped by `connectBrainToAgent`
40
+ * (packages/api/src/routes/brain.ts:1121-1131) in the same atomic jsonb merge as
41
+ * the binding, and removed by `disconnectBrainFromAgent`. It is what
42
+ * `GET /v1/brain/connections` reports as `connected_at`, so a binding written
43
+ * without it reads back as `connected_at: null` — or, worse, carries the
44
+ * timestamp of an EARLIER binding to a DIFFERENT brain, which is what the roster
45
+ * PATCH used to leave behind here.
46
+ */
47
+ export const BRAIN_CONNECTED_AT_KEY = "brain_connected_at";
48
+ /**
49
+ * THE BRAIN BINDING SURFACE — the routes that carry the brain gate.
50
+ *
51
+ * 🔴 THESE REPLACED A ROSTER PATCH, AND THAT IS THE WHOLE POINT. This CLI used
52
+ * to write the binding with `PATCH {@link ROSTER_PATH}/:id {metadata:{…}}`, which
53
+ * is mounted on the ROSTER's own primitive gate rather than the brain's.
54
+ * `packages/api/src/routes/brain.ts:17-22`
55
+ * says exactly why that must not exist — "a second mount is how a route reaches
56
+ * the same data without the same gate" — and it was measured live on one tenant,
57
+ * one agent, one field: `POST /v1/users/{tu}/brain/connect` → 403
58
+ * `subprocessor_consent_required`, while the roster PATCH of
59
+ * `metadata.brain_knowledge_base_id` on the same agent → 200. The shipped
60
+ * `naive brain connect` was the caller of the ungated door.
61
+ *
62
+ * Three things came back with the move, none of which the roster PATCH did:
63
+ * 1. the `brain` primitive gate, so a company with brain disabled is refused
64
+ * here the way it is refused everywhere else;
65
+ * 2. the `brain.agent.connected` `activity_events` row — the ledger entry that
66
+ * makes a binding auditable;
67
+ * 3. `brain_connected_at`, stamped in the same statement as the binding.
68
+ * And one hazard went away: the roster PATCH read-modify-wrote the whole
69
+ * metadata bag, so two concurrent writers lost each other's keys. `connect` is a
70
+ * single `COALESCE(metadata,'{}') || {…}` merge in Postgres.
71
+ */
72
+ export const BRAIN_CONNECT_PATH = "/v1/brain/connect";
73
+ export const BRAIN_CONNECTIONS_PATH = "/v1/brain/connections";
74
+ /**
75
+ * THE ROSTER SURFACE, NAMED ONCE — READ-ONLY FROM HERE.
76
+ *
77
+ * This is the only place in the multi-brain code that spells the retired noun,
78
+ * and it is spelled because it is a WIRE NAME: the route and the key of the
79
+ * array it answers with. `ci/vocabulary.test.ts` §3.1 retires the word for
80
+ * every other use — one governed worker identity is an AGENT — so everything
81
+ * downstream of these two constants says `agent`, including the errors a user
82
+ * reads. The surface is also DEPRECATED (its router carries a deprecation id).
83
+ *
84
+ * 🔴 IT IS NOW ONLY EVER READ. Nothing in this CLI PATCHes it: the one write
85
+ * that used to go here — the brain binding — moved to
86
+ * {@link BRAIN_CONNECT_PATH}. `__tests__/brain-multi.test.ts` asserts the roster
87
+ * sees no PATCH, so a future edit cannot quietly put the write back.
88
+ *
89
+ * TWO constants, not three. There was a third, `ROSTER_ITEM_KEY`, naming the
90
+ * envelope key of the SINGLE-agent response — and the only thing that ever read
91
+ * it was the PATCH's own reply. With the write gone it had no reader, and a dead
92
+ * constant spelling a retired noun is the shape a future edit reaches for when it
93
+ * wants to write here again. Deleted with the write. The list read below uses
94
+ * {@link ROSTER_LIST_KEY}; nothing in this CLI reads a single roster row.
95
+ */
96
+ export const ROSTER_PATH = "/v1/employees";
97
+ export const ROSTER_LIST_KEY = "employees";
98
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
99
+ /** True when a reference is already the id the API wants — no lookup needed. */
100
+ export function looksLikeId(ref) {
101
+ return UUID_RE.test(ref.trim());
102
+ }
103
+ /** `GET /v1/brain`. Exits via handleApiError on a non-2xx, like every other command. */
104
+ export async function listBrains(action) {
105
+ const resp = await apiRequest("GET", "/v1/brain");
106
+ handleApiError(action, resp);
107
+ const data = resp.data;
108
+ return data.knowledge_bases ?? [];
109
+ }
110
+ /** A one-line rendering of a brain for an error message's recovery steps. */
111
+ function brainLabel(b) {
112
+ return `${b.name}${b.is_default ? " (default)" : ""} — ${b.id}`;
113
+ }
114
+ function shellQuote(arg) {
115
+ return /[\s"'$`\\]/.test(arg) ? `"${arg.replace(/(["\\$`])/g, "\\$1")}"` : arg;
116
+ }
117
+ /**
118
+ * THE RECOVERY STEP IS THE USER'S OWN COMMAND, REPAIRED.
119
+ *
120
+ * A recovery step whose `command` is a bare fragment like `--brain "Support"`
121
+ * is not runnable, and the whole point of the envelope is that a model can
122
+ * execute the next step without reconstructing anything. So the failing
123
+ * argument is substituted back into the argv the caller actually typed.
124
+ */
125
+ function rerunWith(wrong, right) {
126
+ const argv = process.argv.slice(2);
127
+ const i = argv.lastIndexOf(wrong);
128
+ const repaired = i >= 0 ? [...argv.slice(0, i), right, ...argv.slice(i + 1)] : [...argv, right];
129
+ return `naive ${repaired.map(shellQuote).join(" ")}`;
130
+ }
131
+ /**
132
+ * Name (or id) -> id. THE HELPFUL-ERROR PATH: naming a brain that does not
133
+ * exist lists the ones that do, in the envelope AND in the recovery steps, so
134
+ * an agent recovers from the response instead of guessing a second time.
135
+ */
136
+ export async function resolveBrainId(action, ref) {
137
+ if (looksLikeId(ref))
138
+ return ref.trim();
139
+ const brains = await listBrains(action);
140
+ const wanted = ref.trim().toLowerCase();
141
+ const matches = brains.filter((b) => (b.name ?? "").trim().toLowerCase() === wanted);
142
+ if (matches.length === 1)
143
+ return matches[0].id;
144
+ if (matches.length > 1) {
145
+ // Names are NOT unique in the schema — `brain_knowledge_bases` has a unique
146
+ // index on the DEFAULT flag and on the provider db, never on the name. Two
147
+ // brains may legitimately share one. Refusing with both ids is the only
148
+ // answer that cannot pick the wrong brain on the user's behalf.
149
+ agentError({
150
+ action,
151
+ error: {
152
+ code: "invalid_input",
153
+ message: `${matches.length} knowledge bases are named "${ref}". Name is not unique — pass an id.`,
154
+ available_brains: matches.map((b) => ({ id: b.id, name: b.name, is_default: b.is_default ?? false })),
155
+ },
156
+ recovery_steps: matches.map((b) => ({
157
+ command: rerunWith(ref, b.id),
158
+ description: brainLabel(b),
159
+ })),
160
+ });
161
+ }
162
+ agentError({
163
+ action,
164
+ error: {
165
+ code: "resource_not_found",
166
+ message: brains.length === 0
167
+ ? `No knowledge base named "${ref}" — this company has none yet.`
168
+ : `No knowledge base named "${ref}". This company has ${brains.length}: ${brains.map((b) => b.name).join(", ")}.`,
169
+ hint: "Brains are addressed by name or id. `naive brain list` shows both.",
170
+ available_brains: brains.map((b) => ({ id: b.id, name: b.name, is_default: b.is_default ?? false })),
171
+ },
172
+ recovery_steps: [
173
+ { command: "naive brain list", description: "List this company's knowledge bases" },
174
+ ...brains.slice(0, 5).map((b) => ({ command: rerunWith(ref, b.name), description: brainLabel(b) })),
175
+ { command: `naive brain create "${ref}"`, description: "Create a knowledge base with this name" },
176
+ ],
177
+ });
178
+ throw new Error("unreachable"); // agentError exits; this keeps the return type honest.
179
+ }
180
+ /**
181
+ * Resolve a command's brain selection to a `knowledge_base_id`, or `undefined`
182
+ * to mean "say nothing and let the server use the company default" — which is
183
+ * exactly what every one of these commands did before when no flag was passed.
184
+ *
185
+ * THE DEFAULT PATH IS UNTOUCHED: no selector -> `undefined` -> no
186
+ * `knowledge_base_id` in the body -> `resolveKnowledgeBase(companyId)` ->
187
+ * `ensureDefaultKnowledgeBase`. No extra request is made.
188
+ */
189
+ export async function selectBrainId(action, sel) {
190
+ const given = [
191
+ sel.brain !== undefined && "--brain",
192
+ sel.kb !== undefined && "--kb",
193
+ sel.agent !== undefined && "--agent",
194
+ ].filter(Boolean);
195
+ if (given.length > 1) {
196
+ agentError({
197
+ action,
198
+ error: {
199
+ code: "invalid_input",
200
+ message: `A call reads ONE brain. Pass one of ${given.join(", ")}, not ${given.length}.`,
201
+ hint: "`--kb` is the id-only spelling of `--brain`; `--agent` means 'the brain that agent is bound to'.",
202
+ },
203
+ recovery_steps: [
204
+ { command: "naive brain list", description: "See the brains and what is bound to each" },
205
+ ],
206
+ });
207
+ }
208
+ if (sel.brain !== undefined)
209
+ return resolveBrainId(action, sel.brain);
210
+ if (sel.kb !== undefined)
211
+ return resolveBrainId(action, sel.kb);
212
+ if (sel.agent !== undefined) {
213
+ const agent = await resolveAgent(action, sel.agent);
214
+ const bound = boundBrainId(agent);
215
+ if (!bound) {
216
+ agentError({
217
+ action,
218
+ error: {
219
+ code: "resource_not_found",
220
+ message: `Agent "${agent.name}" is not connected to a brain, so there is no brain to read from.`,
221
+ agent: { id: agent.id, name: agent.name },
222
+ },
223
+ recovery_steps: [
224
+ { command: `naive brain connect <brain> --agent ${agent.id}`, description: "Connect this agent to a brain" },
225
+ { command: "naive brain list", description: "See the brains and what is bound to each" },
226
+ ],
227
+ });
228
+ }
229
+ // A BINDING CAN OUTLIVE ITS BRAIN. `DELETE /v1/brain/:id` cannot reach into
230
+ // the roster, so deleting a knowledge base leaves every agent connected to
231
+ // it pointing at a row that is gone. Sending the stale id would come back as
232
+ // a bare `Knowledge base not found` naming an id the caller never typed;
233
+ // resolving it here costs one GET on a NEW flag and names the actual fault.
234
+ const brains = await listBrains(action);
235
+ if (!brains.some((b) => b.id === bound)) {
236
+ agentError({
237
+ action,
238
+ error: {
239
+ code: "resource_not_found",
240
+ message: `Agent "${agent.name}" is connected to knowledge base ${bound}, which no longer exists.`,
241
+ hint: "The brain was deleted after the agent was connected to it. Connect the agent to a brain that exists.",
242
+ agent: { id: agent.id, name: agent.name },
243
+ knowledge_base_id: bound,
244
+ available_brains: brains.map((b) => ({ id: b.id, name: b.name, is_default: b.is_default ?? false })),
245
+ },
246
+ recovery_steps: [
247
+ ...brains.slice(0, 5).map((b) => ({
248
+ command: `naive brain connect "${b.name}" --agent ${agent.id}`,
249
+ description: brainLabel(b),
250
+ })),
251
+ { command: `naive brain disconnect --agent ${agent.id}`, description: "Clear the stale binding and fall back to the company default" },
252
+ ],
253
+ });
254
+ }
255
+ return bound;
256
+ }
257
+ return undefined;
258
+ }
259
+ /* ══════════════════════════════════════════════════════════════════════════
260
+ * THE AGENT SIDE
261
+ * ════════════════════════════════════════════════════════════════════════ */
262
+ /**
263
+ * The roster, WITHOUT exiting on failure.
264
+ *
265
+ * `naive brain list` reports bindings as an ENRICHMENT of the brain list. The
266
+ * roster lives behind a different primitive, and a deprecated one, so a company
267
+ * that has it switched off must still get its brains
268
+ * listed — with the reason the bindings column is absent, not a silent empty
269
+ * one. An empty list and an unreadable roster are different facts.
270
+ */
271
+ export async function tryListAgents() {
272
+ const resp = await apiRequest("GET", ROSTER_PATH);
273
+ if (resp.status >= 400) {
274
+ const err = resp.data?.error;
275
+ return {
276
+ ok: false,
277
+ because: `the agent roster could not be read (GET ${ROSTER_PATH} -> ${resp.status}${err?.code ? ` ${err.code}` : ""}${err?.message ? `: ${err.message}` : ""})`,
278
+ };
279
+ }
280
+ const data = (resp.data ?? {});
281
+ return { ok: true, agents: data[ROSTER_LIST_KEY] ?? [] };
282
+ }
283
+ /** The roster, exiting on failure — for the commands that cannot proceed without it. */
284
+ export async function listAgents(action) {
285
+ const roster = await tryListAgents();
286
+ if (!roster.ok) {
287
+ agentError({
288
+ action,
289
+ error: { code: "upstream_error", message: `Cannot resolve an agent because ${roster.because}.` },
290
+ recovery_steps: [{ command: "naive brain list", description: "See the brains and what is bound to each" }],
291
+ });
292
+ }
293
+ return roster.agents;
294
+ }
295
+ /** The brain an agent is connected to, or undefined. */
296
+ export function boundBrainId(agent) {
297
+ const raw = (agent.metadata ?? {})[BRAIN_BINDING_KEY];
298
+ return typeof raw === "string" && raw.length > 0 ? raw : undefined;
299
+ }
300
+ function agentLabel(a) {
301
+ return `${a.name}${a.role ? ` (${a.role})` : ""} — ${a.id}`;
302
+ }
303
+ /**
304
+ * Reference (id | display name | hermes profile slug) -> the roster row. Same
305
+ * helpful-error contract as `resolveBrainId`: an unknown agent lists the known
306
+ * ones.
307
+ */
308
+ export async function resolveAgent(action, ref) {
309
+ const agents = await listAgents(action);
310
+ const wanted = ref.trim().toLowerCase();
311
+ const byId = agents.find((a) => a.id === ref.trim());
312
+ if (byId)
313
+ return byId;
314
+ const matches = agents.filter((a) => (a.name ?? "").trim().toLowerCase() === wanted ||
315
+ (a.hermesProfileName ?? "").trim().toLowerCase() === wanted);
316
+ if (matches.length === 1)
317
+ return matches[0];
318
+ if (matches.length > 1) {
319
+ agentError({
320
+ action,
321
+ error: {
322
+ code: "invalid_input",
323
+ message: `${matches.length} agents answer to "${ref}". Pass an id.`,
324
+ available_agents: matches.map((a) => ({ id: a.id, name: a.name })),
325
+ },
326
+ recovery_steps: matches.map((a) => ({ command: rerunWith(ref, a.id), description: agentLabel(a) })),
327
+ });
328
+ }
329
+ agentError({
330
+ action,
331
+ error: {
332
+ code: "resource_not_found",
333
+ message: agents.length === 0
334
+ ? `No agent named "${ref}" — this company's roster is empty.`
335
+ : `No agent named "${ref}". This company has ${agents.length}: ${agents.map((a) => a.name).join(", ")}.`,
336
+ hint: "An agent is addressed by id, display name, or runtime profile name. All three are listed below.",
337
+ available_agents: agents.map((a) => ({ id: a.id, name: a.name })),
338
+ },
339
+ recovery_steps: [
340
+ ...agents.slice(0, 5).map((a) => ({ command: rerunWith(ref, a.id), description: agentLabel(a) })),
341
+ ],
342
+ });
343
+ throw new Error("unreachable");
344
+ }
345
+ /**
346
+ * Write (or clear) an agent's brain binding, THROUGH THE BRAIN SURFACE.
347
+ *
348
+ * 🔴 NOT THE ROSTER PATCH. See {@link BRAIN_CONNECT_PATH} for the measurement:
349
+ * `PATCH {@link ROSTER_PATH}/:id` reaches `metadata.brain_knowledge_base_id`
350
+ * behind the ROSTER's primitive gate rather than the `brain` gate, so it wrote a
351
+ * binding on a company whose brain primitive was OFF. It also read-modify-wrote the whole
352
+ * metadata bag (lost-update racy), skipped the `brain.agent.connected` ledger
353
+ * event, and never stamped `brain_connected_at` — which is why
354
+ * `GET /v1/brain/connections` reported a `connected_at` belonging to an earlier
355
+ * binding to a different brain.
356
+ *
357
+ * `POST /v1/brain/connect` and `DELETE /v1/brain/connections/:agentId` do all
358
+ * four correctly and are the routes `naive brain list` already reads back. This
359
+ * function no longer sends `metadata` at all: it sends two ids, and the merge
360
+ * happens in one Postgres statement on the server.
361
+ *
362
+ * The returned `agent` carries the metadata as it now stands — the caller's row
363
+ * with the two brain-owned keys applied — so callers that print `metadata` keep
364
+ * printing something true without a second round trip. The authority is
365
+ * `connection`.
366
+ */
367
+ export async function setAgentBrain(action, agent, knowledgeBaseId) {
368
+ const before = { ...(agent.metadata ?? {}) };
369
+ if (knowledgeBaseId === null) {
370
+ const resp = await apiRequest("DELETE", `${BRAIN_CONNECTIONS_PATH}/${encodeURIComponent(agent.id)}`);
371
+ handleApiError(action, resp);
372
+ delete before[BRAIN_BINDING_KEY];
373
+ delete before[BRAIN_CONNECTED_AT_KEY];
374
+ return { agent: { ...agent, metadata: before }, connection: null };
375
+ }
376
+ const resp = await apiRequest("POST", BRAIN_CONNECT_PATH, {
377
+ agent_id: agent.id,
378
+ knowledge_base_id: knowledgeBaseId,
379
+ });
380
+ handleApiError(action, resp);
381
+ const connection = (resp.data ?? {});
382
+ before[BRAIN_BINDING_KEY] = knowledgeBaseId;
383
+ // Only when the route said so. A CLI that invented a timestamp the server did
384
+ // not write would reintroduce, in the client, the exact drift the roster PATCH
385
+ // caused on the server.
386
+ if (typeof connection.connected_at === "string") {
387
+ before[BRAIN_CONNECTED_AT_KEY] = connection.connected_at;
388
+ }
389
+ return { agent: { ...agent, metadata: before }, connection };
390
+ }
391
+ /** The `next_steps` every binding command ends with. */
392
+ export function bindingNextSteps(brain, agentId) {
393
+ return [
394
+ { command: "naive brain list", description: "See every brain and what is bound to each" },
395
+ {
396
+ command: `naive brain attach "<goal>" --agent ${agentId}`,
397
+ description: brain
398
+ ? `Read a capsule from "${brain.name}" as this agent`
399
+ : "Read a capsule from this agent's brain",
400
+ },
401
+ ];
402
+ }
403
+ //# sourceMappingURL=brain-select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-select.js","sourceRoot":"","sources":["../../src/lib/brain-select.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type Command } from "commander";
2
+ export declare function isTenantUserRef(value: string): boolean;
3
+ /**
4
+ * Fail-closed for malformed `--user` before any HTTP call.
5
+ * Mirrors API subject-resolver: non-uuid ids never match → not_found (never a DB 500 / rate-limit burn).
6
+ */
7
+ export declare function assertTenantUserRef(user: string | undefined): void;
8
+ export declare function configureStructuredValidation(command: Command): void;
9
+ export declare function parseIntegerOption(value: string, name: string, min: number, max?: number): number;
10
+ export declare function commandAction(args: readonly string[]): string;
11
+ export declare function cleanCommanderMessage(message: string): string;
12
+ export declare function helpCommand(args: readonly string[]): string;
@@ -0,0 +1,46 @@
1
+ import { InvalidArgumentError } from "commander";
2
+ /** Same shape the API accepts for `:user_id` / `--user` (uuid or default/me aliases). */
3
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
+ const USER_ALIASES = new Set(["default", "me"]);
5
+ export function isTenantUserRef(value) {
6
+ return USER_ALIASES.has(value) || UUID_RE.test(value);
7
+ }
8
+ /**
9
+ * Fail-closed for malformed `--user` before any HTTP call.
10
+ * Mirrors API subject-resolver: non-uuid ids never match → not_found (never a DB 500 / rate-limit burn).
11
+ */
12
+ export function assertTenantUserRef(user) {
13
+ if (user === undefined || user === "")
14
+ return;
15
+ if (isTenantUserRef(user))
16
+ return;
17
+ throw Object.assign(new Error("User not found"), {
18
+ code: "not_found",
19
+ hint: "The user_id does not exist in your workspace.",
20
+ });
21
+ }
22
+ export function configureStructuredValidation(command) {
23
+ command.exitOverride();
24
+ command.configureOutput({ writeErr: () => { } });
25
+ for (const child of command.commands)
26
+ configureStructuredValidation(child);
27
+ }
28
+ export function parseIntegerOption(value, name, min, max = Number.MAX_SAFE_INTEGER) {
29
+ const parsed = Number(value);
30
+ if (!Number.isSafeInteger(parsed) || parsed < min || parsed > max) {
31
+ throw new InvalidArgumentError(`${name} must be an integer between ${min} and ${max}`);
32
+ }
33
+ return parsed;
34
+ }
35
+ export function commandAction(args) {
36
+ const words = args.filter((arg) => !arg.startsWith("-")).slice(0, 2);
37
+ return words.length > 0 ? words.join(".") : "cli.validation";
38
+ }
39
+ export function cleanCommanderMessage(message) {
40
+ return message.replace(/^error:\s*/i, "").trim() || "Invalid command options";
41
+ }
42
+ export function helpCommand(args) {
43
+ const command = args.filter((arg) => !arg.startsWith("-")).slice(0, 2).join(" ");
44
+ return command ? `naive ${command} --help` : "naive --help";
45
+ }
46
+ //# sourceMappingURL=cli-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-validation.js","sourceRoot":"","sources":["../../src/lib/cli-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAgB,MAAM,WAAW,CAAC;AAE/D,yFAAyF;AACzF,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAClF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;AAEhD,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAwB;IAC1D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO;IAC9C,IAAI,eAAe,CAAC,IAAI,CAAC;QAAE,OAAO;IAClC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,EAAE;QAC/C,IAAI,EAAE,WAAoB;QAC1B,IAAI,EAAE,+CAA+C;KACtD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,OAAgB;IAC5D,OAAO,CAAC,YAAY,EAAE,CAAC;IACvB,OAAO,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,CAAC;IAChD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ;QAAE,6BAA6B,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,IAAY,EACZ,GAAW,EACX,GAAG,GAAG,MAAM,CAAC,gBAAgB;IAE7B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;QAClE,MAAM,IAAI,oBAAoB,CAAC,GAAG,IAAI,+BAA+B,GAAG,QAAQ,GAAG,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,yBAAyB,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAuB;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjF,OAAO,OAAO,CAAC,CAAC,CAAC,SAAS,OAAO,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The one deprecation experience for the CLI (06 §1.9 / §1.10.1).
3
+ *
4
+ * THE RULE THIS FILE EXISTS TO KEEP: a deprecated command KEEPS WORKING. Nothing
5
+ * here refuses, gates, or short-circuits — `markDeprecated` installs a commander
6
+ * `preAction` hook that writes a banner to STDERR and then returns, and the
7
+ * action runs exactly as it did before. Existing users must not break.
8
+ *
9
+ * WHY A preAction HOOK AND NOT A CALL INSIDE EACH HANDLER. A hook on the GROUP
10
+ * fires for every subcommand of that group, including subcommands added later by
11
+ * someone who has never read this file. A per-handler call is a convention, and a
12
+ * convention is a thing that is true until the next PR.
13
+ *
14
+ * WHY STDERR. The CLI's contract is that stdout is `JSON.parse`-able. The banner
15
+ * is for a human; the machine half is `deprecation` on the stdout envelope, set
16
+ * here through `setPendingDeprecation` and consumed by `agentOutput` exactly the
17
+ * way `update` already is.
18
+ *
19
+ * ── THE THREE LOAD-BEARING DETAILS OF THE TEXT ──────────────────────────────
20
+ *
21
+ * 1. THE `[dep.…]` ID is printed because it is the one word an operator can grep
22
+ * across every rendering of the same fact — the CLI banner, the JSON
23
+ * envelope, and (when they land) the openapi extension, the MCP tool
24
+ * description and the dashboard notice. Renderings drift; an id does not.
25
+ *
26
+ * 2. THE "also called" LINE is not decoration. The legacy orchestration runtime
27
+ * has EIGHT in-tree names. A notice that names one leaves seven ways to keep
28
+ * talking about the thing it is retiring.
29
+ *
30
+ * 3. "frozen, no sunset date" is rendered from a CLOSED two-member `Sunset`
31
+ * union. There is no "removal TBD", because a date nobody can keep is how a
32
+ * banner becomes furniture.
33
+ *
34
+ * And `Replacement` is closed for the same reason: a capability that is simply
35
+ * GONE renders as `{kind:"gap", because}`. It cannot be rendered as an empty
36
+ * string, and the reason is required by the type rather than by review. A notice
37
+ * that invents a replacement for a capability that no longer exists is worse than
38
+ * no notice at all.
39
+ */
40
+ import type { Command } from "commander";
41
+ import { type DeprecationNotice, type NextStep } from "../output.js";
42
+ /** A replacement is a real symbol or an admitted gap. Closed on purpose (R-7). */
43
+ export type Replacement = {
44
+ readonly kind: "use";
45
+ readonly what: string;
46
+ } | {
47
+ readonly kind: "gap";
48
+ readonly because: string;
49
+ };
50
+ /** Frozen, or a date. There is no third member and no "TBD". */
51
+ export type Sunset = {
52
+ readonly kind: "frozen";
53
+ } | {
54
+ readonly kind: "on";
55
+ readonly date: string;
56
+ };
57
+ /** One subcommand of a deprecated group whose capability has no replacement. */
58
+ export interface SubcommandGap {
59
+ readonly sub: string;
60
+ readonly because: string;
61
+ }
62
+ export interface DeprecationSpec {
63
+ /**
64
+ * THE REGISTER'S id, not a derived one.
65
+ *
66
+ * This used to be computed as `dep.${cmd.name()}` — `dep.tasks`, `dep.ceo` —
67
+ * while `packages/api/src/routes/deprecations.ts` mints `dep.primitive.tasks`
68
+ * and emits it in the `X-Naive-Deprecation-Id` response header, whose own
69
+ * comment calls it "the grep handle … the SAME id the openapi operation
70
+ * carries". The dashboard uses the API's spelling too. So the one thing the
71
+ * machine channel exists for — correlating a CLI warning with the HTTP header
72
+ * for the same retirement — matched nothing.
73
+ *
74
+ * Declared rather than derived because the mapping is not a function of the
75
+ * command name: a future group may retire to a `dep.surface.*` row.
76
+ *
77
+ * TWO NAMESPACES, and the difference is real:
78
+ * `dep.primitive.*` / `dep.surface.*` the REST register's own ids. These
79
+ * MUST exist there — `ci/docs-deprecation-parity.mjs` asserts it — so a
80
+ * CLI warning and an `X-Naive-Deprecation-Id` header correlate.
81
+ * `dep.cli.*` a retirement with no REST twin, because the endpoints are
82
+ * gone rather than deprecated (`naive verify`). There is no operation to
83
+ * mark and no header to match, and saying so in the id is better than
84
+ * pointing at a register row that cannot exist.
85
+ */
86
+ readonly id: string;
87
+ /** One sentence: what this command drives. Rendered after the header. */
88
+ readonly drives: string;
89
+ /** Every other in-tree name for the thing being retired (detail 2 above). */
90
+ readonly alsoCalled: readonly string[];
91
+ readonly replacement: Replacement;
92
+ /** Why the replacement is better. Rendered on the "Why:" line. */
93
+ readonly why: string;
94
+ /** `YYYY-MM` — when the deprecation was declared. */
95
+ readonly since: string;
96
+ readonly sunset: Sunset;
97
+ readonly guide: string;
98
+ /** Subcommands of this group whose capability is gone, with the reason. */
99
+ readonly gaps?: readonly SubcommandGap[];
100
+ }
101
+ export interface DeprecationRecord extends DeprecationSpec {
102
+ /** `naive tasks` — derived from the commander command, never retyped. */
103
+ readonly subject: string;
104
+ }
105
+ /**
106
+ * The eight names the legacy orchestration runtime answers to in this tree.
107
+ * Exported so a staleness check can hold it against the source rather than
108
+ * against memory — a list like this becomes folklore the moment nothing reads it.
109
+ */
110
+ export declare const LEGACY_RUNTIME_ALIASES: readonly ["orchestration", "warm pool", "instance pool", "Hermes", "sidecar", "container runtime", "hosted runtime", "runtime.pool"];
111
+ /** Build the full record from the commander command plus its spec. */
112
+ export declare function deprecationRecord(cmd: Command, spec: DeprecationSpec): DeprecationRecord;
113
+ /** The machine half — what rides the stdout envelope. */
114
+ export declare function deprecationNotice(rec: DeprecationRecord, status: "deprecated" | "retired"): DeprecationNotice;
115
+ /**
116
+ * The banner, as a string. Pure, so the wording is a test rather than a habit.
117
+ * The first line carries the two facts an operator needs before anything else:
118
+ * it is deprecated, and it KEEPS WORKING.
119
+ */
120
+ export declare function renderDeprecationBanner(rec: DeprecationRecord): string;
121
+ export declare function _resetDeprecationBannersForTest(): void;
122
+ /**
123
+ * Mark a command group deprecated. The group keeps working; every invocation
124
+ * writes the banner to stderr (once per process) and attaches the machine notice
125
+ * to the next stdout envelope.
126
+ *
127
+ * Returns the record so the caller can assert on it — the registry in index.ts is
128
+ * built from the return values, which is what makes the set of deprecations a
129
+ * value a test can read instead of a side effect it has to observe.
130
+ */
131
+ export declare function markDeprecated(cmd: Command, spec: DeprecationSpec): DeprecationRecord;
132
+ /**
133
+ * A RETIRED command: it no longer does anything, and saying so is the whole
134
+ * behaviour. Distinct from `markDeprecated`, which never refuses.
135
+ *
136
+ * ── WHY THE CODE IS `deprecated_command` AND WHY IT IS NOT IN THE SERVER UNION ──
137
+ *
138
+ * `platform/kernel/src/errors.ts` holds a 25-member `ErrorCode` union typed as
139
+ * `Record<ErrorCode, number>` in `STATUS_MAP` — it is the set of codes an HTTP
140
+ * RESPONSE can carry, and every member owes a status. `deprecated_command` is
141
+ * minted here, by the CLI, for a command that makes no request at all: there is
142
+ * no response and therefore no status to map. Adding it to that union would put
143
+ * a code the server can never emit into the type that says what the server emits.
144
+ *
145
+ * It is instead the fourth member of the CLI-LOCAL code namespace that already
146
+ * exists in this package and is already relied on by callers:
147
+ * `cli_outdated` and `unknown_subcommand` (lib/unknown-command.ts:8) and
148
+ * `upstream_error` (client.ts:97). All three are minted client-side and none is
149
+ * in the server union. `CLI_LOCAL_ERROR_CODES` below names the set so it stays a
150
+ * declared four rather than a habit.
151
+ */
152
+ export declare const CLI_LOCAL_ERROR_CODES: readonly ["cli_outdated", "unknown_subcommand", "upstream_error", "deprecated_command"];
153
+ export declare function retiredCommand(opts: {
154
+ action: string;
155
+ message: string;
156
+ recovery_steps: NextStep[];
157
+ notice?: DeprecationNotice;
158
+ }): void;