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