@aui.io/apollo 0.1.7 → 0.1.9

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 (31) hide show
  1. package/node_modules/@aui.io/apollo-cli/package.json +3 -3
  2. package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.js +7 -1
  3. package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.js.map +1 -1
  4. package/node_modules/@aui.io/apollo-core/package.json +1 -1
  5. package/node_modules/@aui.io/apollo-tui/dist/src/index.d.ts +2 -1
  6. package/node_modules/@aui.io/apollo-tui/dist/src/index.js +2 -1
  7. package/node_modules/@aui.io/apollo-tui/dist/src/index.js.map +1 -1
  8. package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.d.ts +41 -0
  9. package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.js +927 -0
  10. package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.js.map +1 -0
  11. package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.d.ts +10 -4
  12. package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.js +402 -23
  13. package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.js.map +1 -1
  14. package/node_modules/@aui.io/apollo-tui/dist/src/lib/confirm.d.ts +10 -0
  15. package/node_modules/@aui.io/apollo-tui/dist/src/lib/confirm.js +12 -0
  16. package/node_modules/@aui.io/apollo-tui/dist/src/lib/confirm.js.map +1 -0
  17. package/node_modules/@aui.io/apollo-tui/dist/src/lib/picker.d.ts +28 -0
  18. package/node_modules/@aui.io/apollo-tui/dist/src/lib/picker.js +72 -0
  19. package/node_modules/@aui.io/apollo-tui/dist/src/lib/picker.js.map +1 -0
  20. package/node_modules/@aui.io/apollo-tui/dist/src/lib/place.d.ts +19 -0
  21. package/node_modules/@aui.io/apollo-tui/dist/src/lib/place.js +16 -0
  22. package/node_modules/@aui.io/apollo-tui/dist/src/lib/place.js.map +1 -0
  23. package/node_modules/@aui.io/apollo-tui/dist/src/lib/scope.d.ts +4 -0
  24. package/node_modules/@aui.io/apollo-tui/dist/src/lib/scope.js +9 -1
  25. package/node_modules/@aui.io/apollo-tui/dist/src/lib/scope.js.map +1 -1
  26. package/node_modules/@aui.io/apollo-tui/dist/src/lib/sheet.d.ts +2 -9
  27. package/node_modules/@aui.io/apollo-tui/dist/src/lib/sheet.js.map +1 -1
  28. package/node_modules/@aui.io/apollo-tui/dist/src/opentui-app.js +478 -182
  29. package/node_modules/@aui.io/apollo-tui/dist/src/opentui-app.js.map +1 -1
  30. package/node_modules/@aui.io/apollo-tui/package.json +2 -2
  31. package/package.json +4 -4
@@ -0,0 +1,927 @@
1
+ /**
2
+ * TUI action dispatcher — calls apollo-core services and returns UI effects.
3
+ */
4
+ import { mkdir, writeFile } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import path from "node:path";
7
+ import { readBinding, readCredentials, resolveContext, writeCredentials, } from "@aui.io/apollo-core/config/index.js";
8
+ import { ConfigError } from "@aui.io/apollo-core/errors/index.js";
9
+ import { importBundle, listVersions, localDiff, pullBundle, pushBundle, remoteDiff, validateLocalBundle, } from "@aui.io/apollo-core/services/bundles.js";
10
+ import { loginWithBrowser, loginWithOtp, loginWithToken, logout as logoutSession, requestOtp, } from "@aui.io/apollo-core/services/auth.js";
11
+ import { createClients } from "@aui.io/apollo-core/services/clients.js";
12
+ import { setEnvironment } from "@aui.io/apollo-core/services/env.service.js";
13
+ import { bindAgent, createAgent, createProject, listAgents, listOrganizations, listProjects, unlinkAgent, useAgent, useOrganization, useProject, } from "@aui.io/apollo-core/services/hierarchy.js";
14
+ import { createThread, debugInteraction, listMessages, listThreads, trace as fetchTrace, } from "@aui.io/apollo-core/services/messaging.js";
15
+ import { createCollection, createEndpoint, deleteEndpoint, deleteMockDb, describeMockDb, executeEndpoint, exportMockDb, getEndpoint, inspectSession, listCollections, listEndpoints, normalizeSchema, parseJsonFlag, provisionMockDb, resetSession, rotateMgmtKey, rotateRuntimeKey, seedRows, statusMockDb, storedKeys, updateCollection, updateEndpoint, wireMockDbConnection, } from "@aui.io/apollo-core/services/mockdb.js";
16
+ import { listProfiles, useProfile } from "@aui.io/apollo-core/services/profile.service.js";
17
+ import { getStatus } from "@aui.io/apollo-core/services/status.service.js";
18
+ import { upgradeCli } from "@aui.io/apollo-core/services/upgrade.js";
19
+ import { listWorkspaces } from "@aui.io/apollo-core/services/workspace.service.js";
20
+ import { installSkills } from "@aui.io/apollo-core/skills/install.js";
21
+ import { allocateAgentDirectory, clearActiveAgent, readWorkspace, registerAgent, resolveAgentPath, selectAgent, } from "@aui.io/apollo-core/workspace/store.js";
22
+ import { pickerDisplayName } from "./picker.js";
23
+ import { enterWorkspace, PLACE_CREATE_ID, PLACE_SKIP_ID, } from "./place.js";
24
+ function item(id, name, detail) {
25
+ const label = pickerDisplayName(name, id);
26
+ if (detail !== undefined) {
27
+ return detail && detail !== label
28
+ ? { id, label, detail }
29
+ : { id, label };
30
+ }
31
+ return label !== id ? { id, label, detail: id } : { id, label };
32
+ }
33
+ const PLAYGROUND_URLS = {
34
+ staging: "https://staging-playground-v3.aui.io",
35
+ production: "https://apollo-playground.aui.io",
36
+ eu: "https://playground-eu-v3.aui.io",
37
+ };
38
+ function diffSummary(diff) {
39
+ const total = diff.added.length + diff.changed.length + diff.removed.length;
40
+ if (total === 0)
41
+ return "no changes";
42
+ return `+${diff.added.length} ~${diff.changed.length} -${diff.removed.length}`;
43
+ }
44
+ async function persistToken(context, token, refreshToken) {
45
+ const credentials = await readCredentials(homedir());
46
+ const current = credentials[context.profile]?.[context.environment];
47
+ credentials[context.profile] ??= {};
48
+ credentials[context.profile][context.environment] = {
49
+ access_token: token,
50
+ ...(refreshToken
51
+ ? { refresh_token: refreshToken }
52
+ : current?.refresh_token
53
+ ? { refresh_token: current.refresh_token }
54
+ : {}),
55
+ ...(current?.user_id ? { user_id: current.user_id } : {}),
56
+ };
57
+ await writeCredentials(homedir(), credentials);
58
+ }
59
+ function orgIdOf(org) {
60
+ const id = org.id ?? org._id;
61
+ if (!id)
62
+ throw new Error("Organization is missing an id.");
63
+ return id;
64
+ }
65
+ export async function loadPickerCatalog(kind, context) {
66
+ const clients = createClients(context);
67
+ switch (kind) {
68
+ case "env":
69
+ return [
70
+ item("staging", "staging"),
71
+ item("production", "production"),
72
+ item("eu", "eu"),
73
+ ];
74
+ case "login-method":
75
+ return [
76
+ item("browser", "browser SSO"),
77
+ item("email", "email OTP"),
78
+ item("token", "paste JWT"),
79
+ ];
80
+ case "profile":
81
+ return (await listProfiles(context)).map((p) => item(p.name, p.name, p.active ? "active" : p.environments.join(",") || undefined));
82
+ case "workspace":
83
+ return (await listWorkspaces(context)).map((w) => item(w.path, w.name ?? path.basename(w.path), w.path));
84
+ case "place-onboard": {
85
+ const known = (await listWorkspaces(context)).map((w) => item(w.path, w.name ?? path.basename(w.path), w.path));
86
+ return [
87
+ ...known,
88
+ item(PLACE_CREATE_ID, "Create new workspace…", "path sheet"),
89
+ item(PLACE_SKIP_ID, "Continue without place", "account-only until /workspace"),
90
+ ];
91
+ }
92
+ case "org": {
93
+ const orgs = await listOrganizations(clients.identity);
94
+ return orgs.map((o) => item(orgIdOf(o), o.name));
95
+ }
96
+ case "project": {
97
+ const page = await listProjects(clients.management);
98
+ return page.results.map((p) => {
99
+ const raw = p;
100
+ return item(raw.id, raw.name || raw.title || raw.display_name);
101
+ });
102
+ }
103
+ case "agent-local": {
104
+ if (context.workspaceRoot) {
105
+ const manifest = await readWorkspace(context.workspaceRoot, false);
106
+ return (manifest?.agents ?? []).map((a) => item(a.agent_id, a.name, a.path));
107
+ }
108
+ const page = await listAgents(context, clients.management);
109
+ return page.results.map((a) => item(a.id, a.name));
110
+ }
111
+ case "agent-cloud": {
112
+ const page = await listAgents(context, clients.management);
113
+ const registered = new Set(context.workspaceRoot
114
+ ? ((await readWorkspace(context.workspaceRoot, false))?.agents ?? []).map((a) => a.agent_id)
115
+ : []);
116
+ return page.results.map((a) => item(a.id, a.name, registered.has(a.id) ? `${a.id} · in workspace` : a.id));
117
+ }
118
+ case "thread": {
119
+ const threads = await listThreads(context, clients.runtime);
120
+ return threads.map((t) => item(t.id, t.title || t.id.slice(0, 12), t.id));
121
+ }
122
+ case "version": {
123
+ const page = await listVersions(context, clients.management);
124
+ return [
125
+ item("latest", "latest (live)"),
126
+ ...page.results.map((v) => item(v.version_tag, v.version_tag, v.status)),
127
+ ];
128
+ }
129
+ default:
130
+ return [];
131
+ }
132
+ }
133
+ export async function refreshContext() {
134
+ return resolveContext({});
135
+ }
136
+ function relativeWorkspacePath(root, destination) {
137
+ const relative = path
138
+ .relative(path.resolve(root), destination)
139
+ .split(path.sep)
140
+ .join("/");
141
+ resolveAgentPath(root, relative);
142
+ return relative;
143
+ }
144
+ export async function runSimpleCommand(name, context, args) {
145
+ const clients = createClients(context);
146
+ switch (name) {
147
+ case "status": {
148
+ const status = await getStatus(context, { check: true });
149
+ return {
150
+ toast: `status · env=${status.environment} · agent=${status.agentId ?? "—"}`,
151
+ jumpTab: "scope",
152
+ paneTitle: "status",
153
+ paneLines: [
154
+ `env: ${status.environment}`,
155
+ `profile: ${status.profile}`,
156
+ `workspace: ${status.workspaceRoot ?? "—"}`,
157
+ `org: ${status.orgId ?? status.tokenOrgId ?? "—"}`,
158
+ `project: ${status.projectId ?? "—"}`,
159
+ `agent: ${status.agentId ?? "—"}`,
160
+ `version: ${status.versionTag ?? "—"}`,
161
+ `auth: ${status.authenticated ? "yes" : "no"}`,
162
+ ],
163
+ };
164
+ }
165
+ case "validate": {
166
+ const result = await validateLocalBundle(context, clients.runtime);
167
+ if (result.valid) {
168
+ return {
169
+ jumpTab: "bundle",
170
+ toast: result.program?.id
171
+ ? `Valid — ${result.program.id}@${result.program.version ?? "?"}`
172
+ : "Program is valid",
173
+ };
174
+ }
175
+ const errors = result.findings.filter((f) => f.level !== "warning");
176
+ return {
177
+ jumpTab: "bundle",
178
+ error: errors[0]?.message ?? "validation failed",
179
+ };
180
+ }
181
+ case "diff": {
182
+ const remote = args.trim().toLowerCase() === "remote";
183
+ const diff = remote
184
+ ? await remoteDiff(context, clients.management)
185
+ : await localDiff(context);
186
+ return {
187
+ jumpTab: "bundle",
188
+ toast: `diff vs ${remote ? "remote" : "baseline"} · ${diffSummary(diff)}`,
189
+ };
190
+ }
191
+ case "versions": {
192
+ const page = await listVersions(context, clients.management);
193
+ return {
194
+ jumpTab: "bundle",
195
+ paneTitle: "versions",
196
+ paneLines: page.results.length === 0
197
+ ? ["No versions."]
198
+ : page.results.map((v) => `${v.version_tag}\t${v.status}\t${v.label ?? ""}`),
199
+ toast: `${page.results.length} version(s)`,
200
+ };
201
+ }
202
+ case "schema-pull": {
203
+ const schemas = await clients.agentSettings.pullSchemas();
204
+ return {
205
+ jumpTab: "bundle",
206
+ toast: `Pulled ${Object.keys(schemas).length} schema(s)`,
207
+ };
208
+ }
209
+ case "wire": {
210
+ const wired = await wireMockDbConnection(context);
211
+ return { jumpTab: "mockdb", toast: `Wired ${wired.connectionId}` };
212
+ }
213
+ case "mdb-status": {
214
+ const status = await statusMockDb(context);
215
+ return {
216
+ jumpTab: "mockdb",
217
+ paneTitle: "mockdb status",
218
+ paneLines: [JSON.stringify(status, null, 2)],
219
+ toast: "mock DB status",
220
+ };
221
+ }
222
+ case "mdb-describe": {
223
+ const desc = await describeMockDb(context);
224
+ return {
225
+ jumpTab: "mockdb",
226
+ paneTitle: "mockdb describe",
227
+ paneLines: [JSON.stringify(desc, null, 2)],
228
+ };
229
+ }
230
+ case "mdb-keys": {
231
+ const keys = await storedKeys(context);
232
+ return {
233
+ jumpTab: "mockdb",
234
+ paneTitle: "mockdb keys",
235
+ paneLines: [JSON.stringify(keys, null, 2)],
236
+ };
237
+ }
238
+ case "mdb-export": {
239
+ const exported = await exportMockDb(context);
240
+ return {
241
+ jumpTab: "mockdb",
242
+ paneTitle: "mockdb export",
243
+ paneLines: [JSON.stringify(exported, null, 2)],
244
+ toast: "exported",
245
+ };
246
+ }
247
+ case "mdb-collections": {
248
+ const cols = await listCollections(context);
249
+ return {
250
+ jumpTab: "mockdb",
251
+ paneTitle: "collections",
252
+ paneLines: Array.isArray(cols)
253
+ ? cols.map((c) => JSON.stringify(c))
254
+ : [JSON.stringify(cols, null, 2)],
255
+ };
256
+ }
257
+ case "mdb-endpoints": {
258
+ const eps = await listEndpoints(context);
259
+ return {
260
+ jumpTab: "mockdb",
261
+ paneTitle: "endpoints",
262
+ paneLines: Array.isArray(eps)
263
+ ? eps.map((e) => JSON.stringify(e))
264
+ : [JSON.stringify(eps, null, 2)],
265
+ };
266
+ }
267
+ case "mdb-session": {
268
+ const key = args.trim();
269
+ if (!key) {
270
+ return { error: "Session key required (e.g. /mdb-session <key>)" };
271
+ }
272
+ const session = await inspectSession(context, key);
273
+ return {
274
+ jumpTab: "mockdb",
275
+ paneTitle: "session",
276
+ paneLines: [JSON.stringify(session, null, 2)],
277
+ };
278
+ }
279
+ case "mdb-session-reset": {
280
+ const key = args.trim();
281
+ if (!key) {
282
+ return { error: "Session key required (e.g. /mdb-session-reset <key>)" };
283
+ }
284
+ await resetSession(context, key);
285
+ return { jumpTab: "mockdb", toast: "Session reset" };
286
+ }
287
+ case "upgrade": {
288
+ const result = await upgradeCli("latest", {
289
+ packageName: "@aui.io/apollo",
290
+ currentVersion: process.env.npm_package_version ?? "0.0.0",
291
+ });
292
+ return {
293
+ toast: result.upgraded
294
+ ? `Upgraded to ${result.available}`
295
+ : `Already on ${result.current} (latest ${result.available})`,
296
+ };
297
+ }
298
+ case "new": {
299
+ const thread = await createThread(context, clients.runtime).catch(() => undefined);
300
+ return {
301
+ jumpTab: "chat",
302
+ chat: [],
303
+ toast: thread ? `New thread ${thread.id.slice(0, 12)}` : "New thread (local)",
304
+ ...(thread ? { threadId: thread.id } : {}),
305
+ };
306
+ }
307
+ default:
308
+ return { error: `Unhandled command /${name}` };
309
+ }
310
+ }
311
+ export async function applyPick(commandName, selectedId, context) {
312
+ const clients = createClients(context);
313
+ switch (commandName) {
314
+ case "env": {
315
+ await setEnvironment(selectedId, context.environment, context.profile);
316
+ return {
317
+ toast: `Using environment "${selectedId}" (cwd unchanged)`,
318
+ refreshScope: true,
319
+ jumpTab: "scope",
320
+ };
321
+ }
322
+ case "profile": {
323
+ await useProfile(selectedId, homedir());
324
+ return {
325
+ toast: `Using profile "${selectedId}"`,
326
+ refreshScope: true,
327
+ jumpTab: "scope",
328
+ };
329
+ }
330
+ case "workspace": {
331
+ const result = await enterWorkspace(context, selectedId);
332
+ return {
333
+ toast: `Entered ${result.path}`,
334
+ refreshScope: true,
335
+ jumpTab: "scope",
336
+ };
337
+ }
338
+ case "place-onboard": {
339
+ if (selectedId === PLACE_SKIP_ID) {
340
+ return {
341
+ toast: "Continuing without a workspace — /workspace when ready",
342
+ jumpTab: "scope",
343
+ };
344
+ }
345
+ if (selectedId === PLACE_CREATE_ID) {
346
+ return { openWorkspaceCreate: true };
347
+ }
348
+ const result = await enterWorkspace(context, selectedId);
349
+ return {
350
+ toast: `Entered ${result.path}`,
351
+ refreshScope: true,
352
+ jumpTab: "scope",
353
+ };
354
+ }
355
+ case "org": {
356
+ const switched = await useOrganization(context, clients.identity, selectedId);
357
+ await persistToken(context, switched.token, switched.refreshToken);
358
+ if (context.workspaceRoot &&
359
+ context.orgId &&
360
+ context.orgId !== selectedId) {
361
+ await clearActiveAgent(context.workspaceRoot);
362
+ }
363
+ return {
364
+ toast: `Using organization ${selectedId}`,
365
+ refreshScope: true,
366
+ jumpTab: "scope",
367
+ };
368
+ }
369
+ case "project": {
370
+ const result = await useProject(context, clients.management, selectedId, process.cwd());
371
+ return {
372
+ toast: `Using project ${result.project.name}`,
373
+ refreshScope: true,
374
+ jumpTab: "scope",
375
+ };
376
+ }
377
+ case "agent": {
378
+ if (context.workspaceRoot &&
379
+ (context.bindingFromWorkspace || !context.bindingPath)) {
380
+ const workspaceRoot = context.workspaceRoot;
381
+ const manifest = await readWorkspace(workspaceRoot);
382
+ const candidate = manifest?.agents.find((a) => a.agent_id === selectedId);
383
+ if (!candidate) {
384
+ return {
385
+ error: `Agent ${selectedId} not in workspace — /agent-import first`,
386
+ };
387
+ }
388
+ const candidateRoot = resolveAgentPath(workspaceRoot, candidate.path);
389
+ const local = await readBinding(candidateRoot);
390
+ if (local.path !== path.join(candidateRoot, ".apollorc") ||
391
+ local.binding?.agent_id !== selectedId) {
392
+ return {
393
+ error: `Registered artifacts for ${selectedId} have no matching .apollorc — re-import`,
394
+ };
395
+ }
396
+ if (local.binding.org_id &&
397
+ local.binding.org_id !== context.tokenOrgId) {
398
+ const switched = await clients.identity.switchOrganization(local.binding.org_id);
399
+ const token = switched.token ?? context.token;
400
+ if (!token) {
401
+ return {
402
+ error: `Could not authenticate with organization ${local.binding.org_id}`,
403
+ };
404
+ }
405
+ await persistToken(context, token, switched.refreshToken);
406
+ }
407
+ const selected = await selectAgent(workspaceRoot, selectedId);
408
+ return {
409
+ toast: `Using agent ${selected.name}`,
410
+ refreshScope: true,
411
+ jumpTab: "scope",
412
+ };
413
+ }
414
+ const result = await useAgent(context, clients.management, selectedId, process.cwd());
415
+ return {
416
+ toast: `Using agent ${result.agent.name}`,
417
+ refreshScope: true,
418
+ jumpTab: "scope",
419
+ };
420
+ }
421
+ case "thread": {
422
+ const messages = await listMessages(clients.runtime, selectedId);
423
+ const chat = messages.map((m) => {
424
+ const sender = m.sender?.type?.toLowerCase() ?? "";
425
+ const role = sender === "user" || sender === "human" ? "you" : "agent";
426
+ return {
427
+ role,
428
+ text: m.text ?? "(empty)",
429
+ messageId: m.id,
430
+ interactionId: m.id,
431
+ ...(m.trace_info ? { trace: m.trace_info } : {}),
432
+ };
433
+ });
434
+ return {
435
+ jumpTab: "chat",
436
+ chat,
437
+ threadId: selectedId,
438
+ toast: `Thread ${selectedId.slice(0, 12)} · ${chat.length} message(s)`,
439
+ };
440
+ }
441
+ case "version-publish": {
442
+ if (!context.agentId)
443
+ return { error: "Select an agent first" };
444
+ const selected = await clients.management.resolveVersion(context.agentId, selectedId);
445
+ const result = await clients.management.publishVersion(context.agentId, selected.id);
446
+ return {
447
+ jumpTab: "bundle",
448
+ toast: `Published ${result.version_tag}`,
449
+ refreshScope: true,
450
+ };
451
+ }
452
+ case "version-archive": {
453
+ if (!context.agentId)
454
+ return { error: "Select an agent first" };
455
+ const selected = await clients.management.resolveVersion(context.agentId, selectedId);
456
+ const result = await clients.management.archiveVersion(context.agentId, selected.id);
457
+ return {
458
+ jumpTab: "bundle",
459
+ toast: `Archived ${result.version_tag}`,
460
+ };
461
+ }
462
+ default:
463
+ return { error: `Unhandled pick /${commandName}` };
464
+ }
465
+ }
466
+ export async function runSheetAction(commandName, values, context) {
467
+ const text = (name) => {
468
+ const value = values[name];
469
+ return typeof value === "string" ? value.trim() : "";
470
+ };
471
+ const clients = createClients(context);
472
+ switch (commandName) {
473
+ case "login": {
474
+ const env = text("env") || "production";
475
+ const method = text("method") || "browser";
476
+ const loginContext = await resolveContext({
477
+ flags: {
478
+ env,
479
+ profile: context.profile,
480
+ timeout: String(context.timeoutMs),
481
+ },
482
+ });
483
+ const identity = createClients(loginContext).identity;
484
+ if (method === "token") {
485
+ const token = text("token");
486
+ if (!token)
487
+ return { error: "Token required (open advanced)" };
488
+ await loginWithToken(loginContext, token);
489
+ }
490
+ else if (method === "email") {
491
+ const email = text("email");
492
+ if (!email)
493
+ return { error: "Email required (open advanced)" };
494
+ const otp = text("otp");
495
+ if (!otp) {
496
+ await requestOtp(identity, email);
497
+ return {
498
+ error: "OTP sent — re-open /login, set method=email, fill email+otp in advanced",
499
+ };
500
+ }
501
+ await loginWithOtp(loginContext, identity, email, otp);
502
+ }
503
+ else {
504
+ await loginWithBrowser(loginContext, PLAYGROUND_URLS[loginContext.environment]);
505
+ }
506
+ return {
507
+ toast: `Logged in to ${loginContext.environment}`,
508
+ refreshScope: true,
509
+ jumpTab: "scope",
510
+ };
511
+ }
512
+ case "pull": {
513
+ const tag = text("tag");
514
+ const dir = text("dir");
515
+ const force = values.force === true;
516
+ try {
517
+ const result = await pullBundle(context, clients.management, {
518
+ ...(tag && tag !== "latest" ? { tag } : {}),
519
+ ...(dir ? { destination: dir } : {}),
520
+ force,
521
+ });
522
+ const removed = result.removed.length
523
+ ? ` · removed ${result.removed.length}`
524
+ : "";
525
+ return {
526
+ jumpTab: "bundle",
527
+ toast: `Pulled ${Object.keys(result.files).length} file(s)${removed}`,
528
+ refreshScope: true,
529
+ };
530
+ }
531
+ catch (error) {
532
+ if (!force &&
533
+ error instanceof ConfigError &&
534
+ (error.suggestion?.includes("--force") ||
535
+ /changed|baseline/i.test(error.message))) {
536
+ return {
537
+ jumpTab: "bundle",
538
+ needConfirm: {
539
+ title: "force pull (discard local changes)",
540
+ phrase: "force",
541
+ action: "pull-force",
542
+ args: {
543
+ ...(tag ? { tag } : {}),
544
+ ...(dir ? { dir } : {}),
545
+ },
546
+ },
547
+ toast: error.message.split("\n")[0],
548
+ };
549
+ }
550
+ throw error;
551
+ }
552
+ }
553
+ case "push": {
554
+ const message = text("message");
555
+ const dryRun = values.dryRun === true;
556
+ const result = await pushBundle(context, clients.management, clients.runtime, {
557
+ ...(message ? { commitMessage: message } : {}),
558
+ dryRun,
559
+ });
560
+ return {
561
+ jumpTab: "bundle",
562
+ toast: result.push
563
+ ? `Pushed ${result.push.new_version_tag} · ${diffSummary(result.diff)}`
564
+ : `Dry run · ${diffSummary(result.diff)}`,
565
+ refreshScope: true,
566
+ };
567
+ }
568
+ case "provision": {
569
+ const provisioned = await provisionMockDb(context);
570
+ let note = `Provisioned mock DB for ${provisioned.agentId ?? "agent"}`;
571
+ if (values.wire === true) {
572
+ const wired = await wireMockDbConnection(context);
573
+ note += ` · wired ${wired.connectionId}`;
574
+ }
575
+ return { jumpTab: "mockdb", toast: note };
576
+ }
577
+ case "seed": {
578
+ const collection = text("collection");
579
+ const rowsRaw = parseJsonFlag("--rows", text("rows"));
580
+ const rows = Array.isArray(rowsRaw) ? rowsRaw : [rowsRaw];
581
+ await seedRows(context, collection, rows);
582
+ return { jumpTab: "mockdb", toast: `Seeded ${collection}` };
583
+ }
584
+ case "mdb-collection-create": {
585
+ const schema = normalizeSchema(parseJsonFlag("--schema", text("schema")));
586
+ await createCollection(context, text("name"), schema);
587
+ return {
588
+ jumpTab: "mockdb",
589
+ toast: `Created collection ${text("name")}`,
590
+ };
591
+ }
592
+ case "mdb-collection-update": {
593
+ const raw = parseJsonFlag("--add-columns", text("addColumns"));
594
+ const addColumns = Array.isArray(raw)
595
+ ? raw
596
+ : (raw.columns ?? []);
597
+ if (!Array.isArray(addColumns) || addColumns.length === 0) {
598
+ return { error: "addColumns must be a non-empty JSON array" };
599
+ }
600
+ await updateCollection(context, text("name"), addColumns);
601
+ return {
602
+ jumpTab: "mockdb",
603
+ toast: `Updated collection ${text("name")}`,
604
+ };
605
+ }
606
+ case "mdb-endpoint-create": {
607
+ const body = parseJsonFlag("--body", text("body"));
608
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
609
+ return { error: "body must be a JSON object" };
610
+ }
611
+ await createEndpoint(context, body);
612
+ return { jumpTab: "mockdb", toast: "Created endpoint" };
613
+ }
614
+ case "mdb-endpoint-update": {
615
+ const body = parseJsonFlag("--body", text("body"));
616
+ await updateEndpoint(context, text("slug"), body);
617
+ return {
618
+ jumpTab: "mockdb",
619
+ toast: `Updated endpoint ${text("slug")}`,
620
+ };
621
+ }
622
+ case "mdb-endpoint-get": {
623
+ const ep = await getEndpoint(context, text("slug"));
624
+ return {
625
+ jumpTab: "mockdb",
626
+ paneTitle: `endpoint ${text("slug")}`,
627
+ paneLines: [JSON.stringify(ep, null, 2)],
628
+ };
629
+ }
630
+ case "mdb-endpoint-execute": {
631
+ const bodyRaw = text("body");
632
+ const body = bodyRaw
633
+ ? parseJsonFlag("--body", bodyRaw)
634
+ : undefined;
635
+ const result = await executeEndpoint(context, text("slug"), {
636
+ ...(text("session") ? { session: text("session") } : {}),
637
+ ...(body !== undefined ? { body } : {}),
638
+ });
639
+ return {
640
+ jumpTab: "mockdb",
641
+ paneTitle: `execute ${text("slug")}`,
642
+ paneLines: [JSON.stringify(result, null, 2)],
643
+ toast: `Executed ${text("slug")}`,
644
+ };
645
+ }
646
+ case "mdb-endpoint-delete": {
647
+ const slug = text("slug");
648
+ if (!slug)
649
+ return { error: "slug required" };
650
+ return {
651
+ jumpTab: "mockdb",
652
+ needConfirm: {
653
+ title: `delete endpoint ${slug}`,
654
+ phrase: slug,
655
+ action: "mdb-endpoint-delete",
656
+ args: { slug },
657
+ },
658
+ };
659
+ }
660
+ case "workspace-create": {
661
+ const root = text("path");
662
+ const name = text("name");
663
+ const result = await enterWorkspace(context, root, {
664
+ create: true,
665
+ ...(name ? { name } : {}),
666
+ });
667
+ return {
668
+ toast: `Created and entered ${result.path}`,
669
+ refreshScope: true,
670
+ jumpTab: "scope",
671
+ };
672
+ }
673
+ case "project-create": {
674
+ const result = await createProject(context, clients.management, text("name"), process.cwd());
675
+ return {
676
+ toast: `Created project ${result.project.name}`,
677
+ refreshScope: true,
678
+ jumpTab: "scope",
679
+ };
680
+ }
681
+ case "agent-create": {
682
+ if (!text("dir") && !context.workspaceRoot) {
683
+ return { error: "No workspace — pass dir or run from a workspace" };
684
+ }
685
+ const created = await createAgent(context, clients.management, text("name"));
686
+ let relative;
687
+ let directory;
688
+ if (text("dir")) {
689
+ directory = path.resolve(text("dir"));
690
+ relative = context.workspaceRoot
691
+ ? relativeWorkspacePath(context.workspaceRoot, directory)
692
+ : undefined;
693
+ }
694
+ else {
695
+ const allocated = await allocateAgentDirectory(context.workspaceRoot, created.name, created.id);
696
+ directory = allocated.absolute;
697
+ relative = allocated.relative;
698
+ }
699
+ const sourceDirectory = path.join(directory, "bundle", "src");
700
+ await mkdir(sourceDirectory, { recursive: true });
701
+ await bindAgent(context, created, directory);
702
+ const slug = text("name")
703
+ .toLowerCase()
704
+ .replace(/[^a-z0-9]+/g, "-")
705
+ .replace(/^-|-$/g, "") || "agent";
706
+ await writeFile(path.join(sourceDirectory, "program.yaml"), `id: ${slug}\nversion: 0.1.0\n`, { encoding: "utf8", flag: "wx" }).catch((error) => {
707
+ if (error.code !== "EEXIST")
708
+ throw error;
709
+ });
710
+ if (context.workspaceRoot && relative) {
711
+ await registerAgent(context.workspaceRoot, {
712
+ agent_id: created.id,
713
+ name: created.name,
714
+ path: relative,
715
+ });
716
+ }
717
+ return {
718
+ toast: `Created ${created.name} in ${directory}`,
719
+ refreshScope: true,
720
+ jumpTab: "scope",
721
+ };
722
+ }
723
+ case "agent-import": {
724
+ const agentId = text("agent");
725
+ if (!agentId)
726
+ return { error: "Agent required" };
727
+ if (!text("dir") && !context.workspaceRoot) {
728
+ return { error: "No workspace — pass dir or run from a workspace" };
729
+ }
730
+ const agent = await clients.management.getAgent(agentId);
731
+ let relative;
732
+ let destination;
733
+ if (text("dir")) {
734
+ destination = path.resolve(text("dir"));
735
+ relative = context.workspaceRoot
736
+ ? relativeWorkspacePath(context.workspaceRoot, destination)
737
+ : undefined;
738
+ }
739
+ else {
740
+ const allocated = await allocateAgentDirectory(context.workspaceRoot, agent.name, agent.id);
741
+ destination = allocated.absolute;
742
+ relative = allocated.relative;
743
+ }
744
+ const imported = await importBundle(context, clients.management, agentId, destination, text("tag") || undefined);
745
+ await installSkills(clients.runtime, destination, ["cursor", "claude"]);
746
+ if (context.workspaceRoot && relative) {
747
+ await registerAgent(context.workspaceRoot, {
748
+ agent_id: agent.id,
749
+ name: agent.name,
750
+ path: relative,
751
+ });
752
+ }
753
+ return {
754
+ toast: `Imported ${Object.keys(imported.files).length} files into ${destination}`,
755
+ refreshScope: true,
756
+ jumpTab: "scope",
757
+ };
758
+ }
759
+ case "var":
760
+ return { toast: "var handled by UI" };
761
+ default:
762
+ return { error: `Sheet /${commandName} not wired` };
763
+ }
764
+ }
765
+ export async function runDestructive(action, context, args = {}) {
766
+ const clients = createClients(context);
767
+ switch (action) {
768
+ case "logout": {
769
+ if (context.token) {
770
+ await clients.identity.logout().catch(() => undefined);
771
+ }
772
+ await logoutSession(context);
773
+ return {
774
+ toast: "Logged out",
775
+ refreshScope: true,
776
+ jumpTab: "scope",
777
+ };
778
+ }
779
+ case "agent-unlink": {
780
+ if (context.workspaceRoot &&
781
+ (context.bindingFromWorkspace || !context.bindingPath)) {
782
+ await clearActiveAgent(context.workspaceRoot);
783
+ return {
784
+ toast: "Cleared active workspace agent",
785
+ refreshScope: true,
786
+ jumpTab: "scope",
787
+ };
788
+ }
789
+ if (!context.bindingPath) {
790
+ return { error: "No .apollorc bound here" };
791
+ }
792
+ await unlinkAgent(context, process.cwd());
793
+ return {
794
+ toast: "Unlinked local agent binding",
795
+ refreshScope: true,
796
+ jumpTab: "scope",
797
+ };
798
+ }
799
+ case "mdb-rotate-mgmt": {
800
+ await rotateMgmtKey(context);
801
+ return { jumpTab: "mockdb", toast: "Rotated management key" };
802
+ }
803
+ case "mdb-rotate-runtime": {
804
+ await rotateRuntimeKey(context);
805
+ return { jumpTab: "mockdb", toast: "Rotated runtime key" };
806
+ }
807
+ case "mdb-delete": {
808
+ await deleteMockDb(context);
809
+ return { jumpTab: "mockdb", toast: "Deleted mock DB" };
810
+ }
811
+ case "pull-force": {
812
+ const tag = args.tag ?? "";
813
+ const dir = args.dir ?? "";
814
+ const result = await pullBundle(context, clients.management, {
815
+ ...(tag && tag !== "latest" ? { tag } : {}),
816
+ ...(dir ? { destination: dir } : {}),
817
+ force: true,
818
+ });
819
+ const removed = result.removed.length
820
+ ? ` · removed ${result.removed.length}`
821
+ : "";
822
+ return {
823
+ jumpTab: "bundle",
824
+ toast: `Force-pulled ${Object.keys(result.files).length} file(s)${removed}`,
825
+ refreshScope: true,
826
+ };
827
+ }
828
+ case "mdb-endpoint-delete": {
829
+ await deleteEndpoint(context, args.slug ?? "");
830
+ return {
831
+ jumpTab: "mockdb",
832
+ toast: `Deleted endpoint ${args.slug ?? ""}`,
833
+ };
834
+ }
835
+ case "version-publish":
836
+ case "version-archive":
837
+ return applyPick(action, args.id ?? "", context);
838
+ default:
839
+ return { error: `Unknown confirm action ${action}` };
840
+ }
841
+ }
842
+ export function destructiveSpec(commandName, context) {
843
+ switch (commandName) {
844
+ case "logout":
845
+ return { title: "logout", phrase: "logout", action: "logout" };
846
+ case "agent-unlink":
847
+ return {
848
+ title: "unlink agent",
849
+ phrase: "unlink",
850
+ action: "agent-unlink",
851
+ };
852
+ case "mdb-rotate-mgmt":
853
+ return {
854
+ title: "rotate mgmt key",
855
+ phrase: "rotate",
856
+ action: "mdb-rotate-mgmt",
857
+ };
858
+ case "mdb-rotate-runtime":
859
+ return {
860
+ title: "rotate runtime key",
861
+ phrase: "rotate",
862
+ action: "mdb-rotate-runtime",
863
+ };
864
+ case "mdb-delete":
865
+ return {
866
+ title: "delete mock DB",
867
+ phrase: context.agentId?.slice(0, 8) ?? "delete",
868
+ action: "mdb-delete",
869
+ };
870
+ default:
871
+ return undefined;
872
+ }
873
+ }
874
+ /** Resolve chat overlay for bare `t` / `d` / `t N` / `d N` on the Chat tab. */
875
+ export async function chatOverlay(kind, chat, threadId, context, indexArg) {
876
+ const agentTurns = chat
877
+ .map((turn, index) => ({ turn, index }))
878
+ .filter((entry) => entry.turn.role === "agent");
879
+ if (agentTurns.length === 0) {
880
+ return { error: "No agent replies yet — send a message first" };
881
+ }
882
+ let pick = agentTurns[agentTurns.length - 1];
883
+ if (indexArg?.trim()) {
884
+ const n = Number(indexArg.trim());
885
+ if (!Number.isInteger(n) || n < 1 || n > agentTurns.length) {
886
+ return {
887
+ error: `Pick agent reply 1–${agentTurns.length} (oldest→newest)`,
888
+ };
889
+ }
890
+ pick = agentTurns[n - 1];
891
+ }
892
+ const clients = createClients(context);
893
+ if (kind === "trace") {
894
+ if (pick.turn.trace) {
895
+ return {
896
+ jumpTab: "chat",
897
+ paneTitle: `trace · turn ${pick.index + 1}`,
898
+ paneLines: [JSON.stringify(pick.turn.trace, null, 2)],
899
+ toast: "Trace (from reply)",
900
+ };
901
+ }
902
+ if (threadId) {
903
+ const data = await fetchTrace(clients.runtime, threadId, pick.turn.interactionId);
904
+ return {
905
+ jumpTab: "chat",
906
+ paneTitle: `trace · ${pick.turn.interactionId ?? threadId.slice(0, 12)}`,
907
+ paneLines: [JSON.stringify(data, null, 2)],
908
+ toast: "Trace loaded",
909
+ };
910
+ }
911
+ return {
912
+ error: "No trace on this reply — enable /trace before sending, or open a /thread",
913
+ };
914
+ }
915
+ const interactionId = pick.turn.interactionId ?? pick.turn.messageId;
916
+ if (!interactionId) {
917
+ return { error: "No interaction id on this reply" };
918
+ }
919
+ const data = await debugInteraction(clients.runtime, interactionId);
920
+ return {
921
+ jumpTab: "chat",
922
+ paneTitle: `debug · ${interactionId.slice(0, 12)}`,
923
+ paneLines: [JSON.stringify(data, null, 2)],
924
+ toast: "Debug loaded",
925
+ };
926
+ }
927
+ //# sourceMappingURL=actions.js.map