@agent-api/cli 0.2.1 → 0.3.0

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 (61) hide show
  1. package/README.md +11 -5
  2. package/dist/index.js +8 -6
  3. package/dist/runtime.d.ts +4 -0
  4. package/dist/runtime.js +4 -0
  5. package/dist/tui/ink/app.d.ts +1 -1
  6. package/dist/tui/ink/app.js +27 -130
  7. package/dist/tui/ink/components.d.ts +1 -2
  8. package/dist/tui/ink/components.js +1 -3
  9. package/package.json +9 -6
  10. package/dist/agent/runner.d.ts +0 -117
  11. package/dist/agent/runner.js +0 -486
  12. package/dist/agent.d.ts +0 -2
  13. package/dist/agent.js +0 -2
  14. package/dist/chat-options.d.ts +0 -37
  15. package/dist/chat-options.js +0 -42
  16. package/dist/config.d.ts +0 -66
  17. package/dist/config.js +0 -201
  18. package/dist/conversation/index.d.ts +0 -17
  19. package/dist/conversation/index.js +0 -54
  20. package/dist/profile.d.ts +0 -57
  21. package/dist/profile.js +0 -211
  22. package/dist/runtime/index.d.ts +0 -5
  23. package/dist/runtime/index.js +0 -152
  24. package/dist/tui/workbench.d.ts +0 -187
  25. package/dist/tui/workbench.js +0 -392
  26. package/dist/update.d.ts +0 -16
  27. package/dist/update.js +0 -74
  28. package/dist/workbench/auth-controller.d.ts +0 -43
  29. package/dist/workbench/auth-controller.js +0 -84
  30. package/dist/workbench/auth-gate-controller.d.ts +0 -62
  31. package/dist/workbench/auth-gate-controller.js +0 -231
  32. package/dist/workbench/command-controller.d.ts +0 -29
  33. package/dist/workbench/command-controller.js +0 -426
  34. package/dist/workbench/conversation-controller.d.ts +0 -32
  35. package/dist/workbench/conversation-controller.js +0 -53
  36. package/dist/workbench/engine.d.ts +0 -66
  37. package/dist/workbench/engine.js +0 -291
  38. package/dist/workbench/input-controller.d.ts +0 -44
  39. package/dist/workbench/input-controller.js +0 -71
  40. package/dist/workbench/isolator-installer.d.ts +0 -29
  41. package/dist/workbench/isolator-installer.js +0 -208
  42. package/dist/workbench/lifecycle-controller.d.ts +0 -30
  43. package/dist/workbench/lifecycle-controller.js +0 -75
  44. package/dist/workbench/local-controller.d.ts +0 -21
  45. package/dist/workbench/local-controller.js +0 -94
  46. package/dist/workbench/render-model.d.ts +0 -46
  47. package/dist/workbench/render-model.js +0 -61
  48. package/dist/workbench/runtime-controller.d.ts +0 -12
  49. package/dist/workbench/runtime-controller.js +0 -57
  50. package/dist/workbench/session.d.ts +0 -27
  51. package/dist/workbench/session.js +0 -42
  52. package/dist/workbench/settings-controller.d.ts +0 -80
  53. package/dist/workbench/settings-controller.js +0 -309
  54. package/dist/workbench/shell-isolation.d.ts +0 -20
  55. package/dist/workbench/shell-isolation.js +0 -13
  56. package/dist/workbench/turn-controller.d.ts +0 -25
  57. package/dist/workbench/turn-controller.js +0 -164
  58. package/dist/workbench/view-model.d.ts +0 -34
  59. package/dist/workbench/view-model.js +0 -121
  60. package/dist/workdir/index.d.ts +0 -22
  61. package/dist/workdir/index.js +0 -46
@@ -1,392 +0,0 @@
1
- export function createInitialWorkbenchState(options) {
2
- const accessMode = options.accessMode ?? (options.contextEnabled ? "approval" : "off");
3
- return {
4
- messages: [
5
- newMessage("system", "Agent API Workbench is ready. Type /help for commands."),
6
- ],
7
- activities: [
8
- newActivity("info", "Workbench started"),
9
- ],
10
- busy: false,
11
- contextEnabled: options.contextEnabled || accessMode === "approval" || accessMode === "full",
12
- workdir: null,
13
- activeAssistantMessageId: null,
14
- pendingLocalTool: null,
15
- accessMode,
16
- currentConversation: options.conversation || "default",
17
- runPreset: options.preset,
18
- runModel: options.model,
19
- renderMode: options.renderMode ?? "markdown",
20
- defaultPreset: options.defaultPreset,
21
- shellIsolation: options.shellIsolation,
22
- };
23
- }
24
- export function createInputHistory(limit = 100) {
25
- const entries = [];
26
- let cursor = null;
27
- let draftBeforeBrowse = "";
28
- return {
29
- record(value) {
30
- const trimmed = value.trim();
31
- if (!trimmed)
32
- return;
33
- if (entries.at(-1) !== trimmed) {
34
- entries.push(trimmed);
35
- if (entries.length > limit)
36
- entries.splice(0, entries.length - limit);
37
- }
38
- cursor = null;
39
- draftBeforeBrowse = "";
40
- },
41
- previous(currentDraft) {
42
- if (entries.length === 0)
43
- return currentDraft;
44
- if (cursor === null) {
45
- draftBeforeBrowse = currentDraft;
46
- cursor = entries.length - 1;
47
- }
48
- else {
49
- cursor = Math.max(0, cursor - 1);
50
- }
51
- return entries[cursor] ?? currentDraft;
52
- },
53
- next(currentDraft) {
54
- if (entries.length === 0 || cursor === null)
55
- return currentDraft;
56
- if (cursor < entries.length - 1) {
57
- cursor += 1;
58
- return entries[cursor] ?? currentDraft;
59
- }
60
- cursor = null;
61
- const restored = draftBeforeBrowse;
62
- draftBeforeBrowse = "";
63
- return restored;
64
- },
65
- reset() {
66
- cursor = null;
67
- draftBeforeBrowse = "";
68
- },
69
- values() {
70
- return [...entries];
71
- },
72
- };
73
- }
74
- export function workbenchReducer(state, action) {
75
- switch (action.type) {
76
- case "message.add":
77
- return {
78
- ...state,
79
- messages: [...state.messages, newMessage(action.role, action.text, action.id)],
80
- };
81
- case "message.append":
82
- return {
83
- ...state,
84
- messages: state.messages.map((message) => message.id === action.id ? { ...message, text: message.text + action.delta } : message),
85
- };
86
- case "messages.clear":
87
- return {
88
- ...state,
89
- messages: [newMessage("system", "Cleared. Type /help for commands.")],
90
- };
91
- case "activity.add":
92
- return {
93
- ...state,
94
- activities: [...state.activities, newActivity(action.level ?? "info", action.text)].slice(-20),
95
- };
96
- case "busy.set":
97
- return { ...state, busy: action.busy };
98
- case "context.toggle":
99
- return setLocalAccess(state, state.contextEnabled ? "off" : "approval");
100
- case "context.set":
101
- return setLocalAccess(state, action.enabled ? "approval" : "off");
102
- case "workdir.set":
103
- return {
104
- ...state,
105
- workdir: action.workdir,
106
- activities: [...state.activities, newActivity("success", `Workdir loaded: ${action.workdir.name}`)].slice(-20),
107
- };
108
- case "assistant.active":
109
- return { ...state, activeAssistantMessageId: action.id };
110
- case "local_tool.pending.set": {
111
- const pending = {
112
- ...action.approval,
113
- id: `local-${Date.now()}`,
114
- createdAt: Date.now(),
115
- };
116
- return {
117
- ...state,
118
- pendingLocalTool: pending,
119
- activities: [
120
- ...state.activities,
121
- newActivity("warning", `Local approval ready: ${pending.name}${pending.action ? `.${pending.action}` : ""}`),
122
- ].slice(-20),
123
- };
124
- }
125
- case "local_tool.pending.clear":
126
- return {
127
- ...state,
128
- pendingLocalTool: null,
129
- };
130
- case "access.set":
131
- return setLocalAccess(state, action.mode);
132
- case "conversation.set":
133
- return {
134
- ...state,
135
- currentConversation: action.name,
136
- activities: [...state.activities, newActivity("info", `Conversation: ${action.name}`)].slice(-20),
137
- };
138
- case "settings.set":
139
- return {
140
- ...state,
141
- ...action.settings,
142
- };
143
- default:
144
- return state;
145
- }
146
- }
147
- function setLocalAccess(state, mode) {
148
- return {
149
- ...state,
150
- accessMode: mode,
151
- contextEnabled: mode !== "off",
152
- pendingLocalTool: mode === "off" ? null : state.pendingLocalTool,
153
- activities: [...state.activities, newActivity(mode === "off" ? "warning" : "success", `Local access: ${mode}`)].slice(-20),
154
- };
155
- }
156
- export function parseWorkbenchCommand(input) {
157
- const trimmed = input.trim();
158
- if (!trimmed.startsWith("/"))
159
- return null;
160
- const [name = "", ...rest] = trimmed.slice(1).split(/\s+/);
161
- switch (name) {
162
- case "abort":
163
- case "cancel":
164
- return { kind: "abort" };
165
- case "quit":
166
- return { kind: "quit" };
167
- case "exit":
168
- return { kind: "quit" };
169
- case "help":
170
- return { kind: "help" };
171
- case "clear":
172
- return { kind: "clear" };
173
- case "login":
174
- case "signin":
175
- return { kind: "login" };
176
- case "logout":
177
- case "signout":
178
- return { kind: "logout" };
179
- case "delete-profile":
180
- case "delete_profile":
181
- return { kind: "delete_profile" };
182
- case "switch-profile":
183
- case "switch_profile":
184
- return { kind: "switch_profile", name: rest.join(" ").trim() || undefined };
185
- case "auth":
186
- return { kind: "auth_status" };
187
- case "config":
188
- case "settings": {
189
- const [field, ...valueParts] = rest;
190
- if (!field)
191
- return { kind: "config" };
192
- if (field === "preset" || field === "isolation" || field === "isolator") {
193
- return { kind: "config", field, value: valueParts.join(" ").trim() || undefined };
194
- }
195
- return { kind: "invalid", command: `${name} ${field}` };
196
- }
197
- case "render":
198
- case "display":
199
- case "view": {
200
- const mode = rest[0];
201
- if (mode === "raw" || mode === "markdown")
202
- return { kind: "render", mode };
203
- return { kind: "render" };
204
- }
205
- case "transcript":
206
- return { kind: "transcript" };
207
- case "export":
208
- return { kind: "export", path: rest.join(" ").trim() || undefined };
209
- case "context":
210
- return { kind: "context", enabled: parseOnOff(rest[0]) };
211
- case "access": {
212
- const mode = rest[0];
213
- if (mode === "off" || mode === "approval" || mode === "full")
214
- return { kind: "access", mode };
215
- return { kind: "access" };
216
- }
217
- case "preset": {
218
- const value = rest.join(" ").trim();
219
- return { kind: "preset", value: value || undefined };
220
- }
221
- case "model": {
222
- const value = rest.join(" ").trim();
223
- return { kind: "model", value: value || undefined };
224
- }
225
- case "workdir":
226
- case "local":
227
- return { kind: "workdir", enabled: parseOnOff(rest[0]) };
228
- case "summary":
229
- return { kind: "summary" };
230
- case "new":
231
- case "thread":
232
- return { kind: "new_conversation", name: rest.join(" ").trim() || undefined };
233
- case "conversation":
234
- case "switch":
235
- case "use":
236
- if (rest.length === 0)
237
- return { kind: "list_conversations" };
238
- return { kind: "switch_conversation", name: rest.join(" ").trim() };
239
- case "conversations":
240
- case "threads":
241
- return { kind: "list_conversations" };
242
- case "refresh":
243
- case "reload":
244
- case "refresh-catalog":
245
- return { kind: "refresh_catalog" };
246
- case "search":
247
- case "grep":
248
- return { kind: "search", query: rest.join(" ").trim() };
249
- case "preview":
250
- return { kind: "preview" };
251
- case "apply":
252
- return { kind: "apply" };
253
- case "apply-all":
254
- case "yes-all":
255
- return { kind: "apply_all" };
256
- case "reject":
257
- return { kind: "reject" };
258
- default:
259
- return { kind: "invalid", command: name };
260
- }
261
- }
262
- export function parsePendingApprovalCommand(input) {
263
- const trimmed = input.trim().toLowerCase();
264
- if (!trimmed.startsWith("/"))
265
- return null;
266
- const [name = ""] = trimmed.slice(1).split(/\s+/);
267
- switch (name) {
268
- case "apply":
269
- case "yes":
270
- return { kind: "apply" };
271
- case "apply-all":
272
- case "yes-all":
273
- return { kind: "apply_all" };
274
- case "reject":
275
- case "no":
276
- return { kind: "reject" };
277
- default:
278
- return null;
279
- }
280
- }
281
- export function helpText() {
282
- return [
283
- "Commands:",
284
- "/auth show current auth profile",
285
- "/login return to auth gate without deleting profiles",
286
- "/logout leave current session and return to auth gate",
287
- "/switch-profile switch/sign in with a different profile",
288
- "/delete-profile delete current saved profile and return to auth",
289
- "/config show current run configuration and saved defaults",
290
- "/render [mode] show or set output rendering: markdown or raw",
291
- "/transcript show a plain-text transcript preview",
292
- "/export [file] save the plain-text transcript to a file",
293
- "/config preset save default preset; use none/off for no preset, reset for built-in",
294
- "/config isolation save shell isolation mode: none, auto, or required",
295
- "/config isolator save agent-isolator path; use none/off to clear",
296
- "/preset [name] show or set preset; use none/off to clear",
297
- "/model [name] show or set explicit model; use auto/none/off to clear",
298
- "/access [mode] show or set local tool access: off, approval, or full",
299
- "/workdir show local workdir status",
300
- "/workdir on shortcut for /access approval; /workdir off hides local tools",
301
- "/new [name] start a fresh conversation in this workbench",
302
- "/switch <name> switch to an existing/new conversation handle",
303
- "/conversations list saved local conversation handles",
304
- "/summary show local workdir summary previews",
305
- "/search <query> search text in the local workdir",
306
- "/preview show pending local action preview",
307
- "/apply apply pending local action",
308
- "/apply-all apply pending action and allow future local actions",
309
- "/reject reject pending local action",
310
- "/abort cancel the in-flight agent turn",
311
- "/context toggle local context packaging for each agent turn",
312
- "/clear clear the visible terminal transcript",
313
- "/quit leave the workbench",
314
- ].join("\n");
315
- }
316
- export function formatTranscript(messages) {
317
- return messages
318
- .map((message) => {
319
- const body = message.text.trimEnd();
320
- return body ? `${roleLabel(message.role)}:\n${body}` : `${roleLabel(message.role)}:`;
321
- })
322
- .join("\n\n")
323
- .trimEnd() + "\n";
324
- }
325
- export function formatTranscriptPreview(messages, maxLines = 80) {
326
- const lines = formatTranscript(messages).trimEnd().split(/\r?\n/);
327
- if (lines.length <= maxLines) {
328
- return ["Transcript preview:", "", ...lines].join("\n");
329
- }
330
- return [
331
- `Transcript preview: showing last ${maxLines} lines of ${lines.length}. Use /export [file] for the full transcript.`,
332
- "",
333
- ...lines.slice(-maxLines),
334
- ].join("\n");
335
- }
336
- function parseOnOff(value) {
337
- if (!value)
338
- return undefined;
339
- const normalized = value.toLowerCase();
340
- if (["on", "enable", "enabled", "yes", "true"].includes(normalized))
341
- return true;
342
- if (["off", "disable", "disabled", "no", "false"].includes(normalized))
343
- return false;
344
- return undefined;
345
- }
346
- export function workdirText(status) {
347
- if (!status)
348
- return "Workdir summary is still loading.";
349
- return [
350
- `Workdir: ${status.root}`,
351
- `Files: ${status.fileCount}`,
352
- `Size: ${formatBytes(status.totalBytes)}`,
353
- `Scan truncated: ${status.scanTruncated ? "yes" : "no"}`,
354
- ].join("\n");
355
- }
356
- export function formatBytes(bytes) {
357
- if (bytes < 1024)
358
- return `${bytes} B`;
359
- if (bytes < 1024 * 1024)
360
- return `${(bytes / 1024).toFixed(1)} KB`;
361
- return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
362
- }
363
- export function activityColor(level) {
364
- if (level === "success")
365
- return "green";
366
- if (level === "warning")
367
- return "yellow";
368
- if (level === "error")
369
- return "red";
370
- return "gray";
371
- }
372
- function newMessage(role, text, id = randomId()) {
373
- return { id, role, text };
374
- }
375
- function roleLabel(role) {
376
- if (role === "user")
377
- return "You";
378
- if (role === "assistant")
379
- return "Agent";
380
- return "System";
381
- }
382
- function newActivity(level, text) {
383
- return {
384
- id: randomId(),
385
- level,
386
- text,
387
- timestamp: Date.now(),
388
- };
389
- }
390
- function randomId() {
391
- return Math.random().toString(36).slice(2);
392
- }
package/dist/update.d.ts DELETED
@@ -1,16 +0,0 @@
1
- export interface UpdateCheckResult {
2
- current: string;
3
- latest: string;
4
- packageName: string;
5
- updateAvailable: boolean;
6
- }
7
- export interface UpdateCheckOptions {
8
- currentVersion?: string;
9
- packageName?: string;
10
- registryURL?: string;
11
- signal?: AbortSignal;
12
- timeoutMs?: number;
13
- }
14
- export declare function checkForUpdate(options?: UpdateCheckOptions): Promise<UpdateCheckResult | null>;
15
- export declare function formatUpdateNotice(result: UpdateCheckResult): string;
16
- export declare function compareVersions(a: string, b: string): number;
package/dist/update.js DELETED
@@ -1,74 +0,0 @@
1
- import { cliVersion } from "./runtime/index.js";
2
- const defaultPackageName = "@agent-api/cli";
3
- const defaultRegistryURL = "https://registry.npmjs.org";
4
- export async function checkForUpdate(options = {}) {
5
- const packageName = options.packageName || process.env.AGENT_TUI_UPDATE_PACKAGE || defaultPackageName;
6
- const current = options.currentVersion || cliVersion;
7
- const registryURL = (options.registryURL || process.env.AGENT_TUI_NPM_REGISTRY || defaultRegistryURL).replace(/\/+$/, "");
8
- const timeoutMs = options.timeoutMs ?? 1500;
9
- const controller = new AbortController();
10
- const timer = setTimeout(() => controller.abort(), timeoutMs);
11
- const signal = combineSignals(options.signal, controller.signal);
12
- try {
13
- const response = await fetch(`${registryURL}/${encodeURIComponent(packageName).replace(/^%40/, "@")}/latest`, {
14
- headers: { Accept: "application/json" },
15
- signal,
16
- });
17
- if (!response.ok)
18
- return null;
19
- const payload = await response.json().catch(() => undefined);
20
- const latest = typeof payload?.version === "string" ? payload.version : "";
21
- if (!latest)
22
- return null;
23
- return {
24
- current,
25
- latest,
26
- packageName,
27
- updateAvailable: compareVersions(latest, current) > 0,
28
- };
29
- }
30
- catch {
31
- return null;
32
- }
33
- finally {
34
- clearTimeout(timer);
35
- }
36
- }
37
- export function formatUpdateNotice(result) {
38
- return `Update available: ${result.packageName} ${result.current} -> ${result.latest}. Run: npm install -g ${result.packageName}@latest`;
39
- }
40
- export function compareVersions(a, b) {
41
- const left = parseVersion(a);
42
- const right = parseVersion(b);
43
- for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
44
- const delta = (left[index] ?? 0) - (right[index] ?? 0);
45
- if (delta !== 0)
46
- return delta > 0 ? 1 : -1;
47
- }
48
- return 0;
49
- }
50
- function parseVersion(version) {
51
- return version
52
- .replace(/^[^\d]*/, "")
53
- .split(/[.-]/)
54
- .slice(0, 3)
55
- .map((part) => {
56
- const parsed = Number.parseInt(part, 10);
57
- return Number.isFinite(parsed) ? parsed : 0;
58
- });
59
- }
60
- function combineSignals(...signals) {
61
- const active = signals.filter((signal) => Boolean(signal));
62
- if (active.length === 1)
63
- return active[0];
64
- const controller = new AbortController();
65
- const abort = () => controller.abort();
66
- for (const signal of active) {
67
- if (signal.aborted) {
68
- controller.abort();
69
- break;
70
- }
71
- signal.addEventListener("abort", abort, { once: true });
72
- }
73
- return controller.signal;
74
- }
@@ -1,43 +0,0 @@
1
- import { deleteProfile, getAuthStatus, loginWithAPIKey, openBrowserURL, refreshActiveProfileIfNeeded, saveBrowserProfile, startBrowserAuthChallenge, waitForBrowserAuthChallenge, type AuthStatus } from "../profile.js";
2
- export interface WorkbenchAuthController {
3
- check(profile?: string, refreshWindowMs?: number): Promise<{
4
- profileName: string;
5
- refreshed: boolean;
6
- }>;
7
- loginAPIKey(input: {
8
- profile: string;
9
- baseURL: string;
10
- apiKey: string;
11
- }): Promise<{
12
- profileName: string;
13
- }>;
14
- loginBrowser(input: {
15
- profile: string;
16
- baseURL: string;
17
- clientName?: string;
18
- onChallenge?: (challenge: {
19
- url: string;
20
- code: string;
21
- }) => void;
22
- onStatus?: (status: string) => void;
23
- }): Promise<{
24
- profileName: string;
25
- }>;
26
- deleteProfile(name: string): Promise<void>;
27
- statusText(profile?: string): Promise<string>;
28
- refresh(profile?: string, refreshWindowMs?: number): Promise<{
29
- refreshed: boolean;
30
- }>;
31
- }
32
- export interface WorkbenchAuthControllerOptions {
33
- refreshActiveProfileIfNeededImpl?: typeof refreshActiveProfileIfNeeded;
34
- loginWithAPIKeyImpl?: typeof loginWithAPIKey;
35
- startBrowserAuthChallengeImpl?: typeof startBrowserAuthChallenge;
36
- openBrowserURLImpl?: typeof openBrowserURL;
37
- waitForBrowserAuthChallengeImpl?: typeof waitForBrowserAuthChallenge;
38
- saveBrowserProfileImpl?: typeof saveBrowserProfile;
39
- deleteProfileImpl?: typeof deleteProfile;
40
- getAuthStatusImpl?: typeof getAuthStatus;
41
- }
42
- export declare function createWorkbenchAuthController(options?: WorkbenchAuthControllerOptions): WorkbenchAuthController;
43
- export declare function authStatusText(status: AuthStatus): string;
@@ -1,84 +0,0 @@
1
- import { deleteProfile, formatDeviceUserCode, getAuthStatus, loginWithAPIKey, openBrowserURL, refreshActiveProfileIfNeeded, saveBrowserProfile, startBrowserAuthChallenge, waitForBrowserAuthChallenge, } from "../profile.js";
2
- export function createWorkbenchAuthController(options = {}) {
3
- const refreshActiveProfileIfNeededImpl = options.refreshActiveProfileIfNeededImpl ?? refreshActiveProfileIfNeeded;
4
- const loginWithAPIKeyImpl = options.loginWithAPIKeyImpl ?? loginWithAPIKey;
5
- const startBrowserAuthChallengeImpl = options.startBrowserAuthChallengeImpl ?? startBrowserAuthChallenge;
6
- const openBrowserURLImpl = options.openBrowserURLImpl ?? openBrowserURL;
7
- const waitForBrowserAuthChallengeImpl = options.waitForBrowserAuthChallengeImpl ?? waitForBrowserAuthChallenge;
8
- const saveBrowserProfileImpl = options.saveBrowserProfileImpl ?? saveBrowserProfile;
9
- const deleteProfileImpl = options.deleteProfileImpl ?? deleteProfile;
10
- const getAuthStatusImpl = options.getAuthStatusImpl ?? getAuthStatus;
11
- return {
12
- async check(profile, refreshWindowMs = 5 * 60_000) {
13
- const result = await refreshActiveProfileIfNeededImpl(profile, refreshWindowMs);
14
- return { profileName: result.profile.name, refreshed: result.refreshed };
15
- },
16
- async loginAPIKey(input) {
17
- const saved = await loginWithAPIKeyImpl(input);
18
- return { profileName: saved.name };
19
- },
20
- async loginBrowser(input) {
21
- const challenge = await startBrowserAuthChallengeImpl({
22
- baseURL: input.baseURL,
23
- clientName: input.clientName || "Agent API TUI",
24
- });
25
- input.onChallenge?.({
26
- url: challenge.verification_uri_complete,
27
- code: formatDeviceUserCode(challenge.user_code),
28
- });
29
- await openBrowserURLImpl(challenge.verification_uri_complete).catch(() => undefined);
30
- const session = await waitForBrowserAuthChallengeImpl({
31
- baseURL: input.baseURL,
32
- challenge,
33
- on_poll(result) {
34
- if (result.status && result.status !== "pending") {
35
- input.onStatus?.(result.status);
36
- }
37
- },
38
- });
39
- const saved = await saveBrowserProfileImpl(input.profile, input.baseURL, session);
40
- return { profileName: saved.name };
41
- },
42
- async deleteProfile(name) {
43
- await deleteProfileImpl(name);
44
- },
45
- async statusText(profile) {
46
- return authStatusText(await getAuthStatusImpl(profile));
47
- },
48
- async refresh(profile, refreshWindowMs = 5 * 60_000) {
49
- const result = await refreshActiveProfileIfNeededImpl(profile, refreshWindowMs);
50
- return { refreshed: result.refreshed };
51
- },
52
- };
53
- }
54
- export function authStatusText(status) {
55
- return [
56
- `Profile: ${status.profile}`,
57
- `Endpoint: ${status.baseURL}`,
58
- `Auth: ${status.authType === "api_key" ? "API key" : "Browser session"}`,
59
- `Account: ${summarizeMe(status.me)}`,
60
- ].join("\n");
61
- }
62
- function summarizeMe(me) {
63
- if (!me || typeof me !== "object")
64
- return "available";
65
- const obj = me;
66
- const candidates = [
67
- obj.email,
68
- obj.name,
69
- obj.username,
70
- obj.user_id,
71
- obj.id,
72
- nestedString(obj.user, "email"),
73
- nestedString(obj.user, "name"),
74
- nestedString(obj.user, "id"),
75
- ];
76
- const picked = candidates.find((value) => typeof value === "string" && value.trim() !== "");
77
- return picked || "available";
78
- }
79
- function nestedString(value, key) {
80
- if (!value || typeof value !== "object")
81
- return undefined;
82
- const nested = value[key];
83
- return typeof nested === "string" ? nested : undefined;
84
- }
@@ -1,62 +0,0 @@
1
- import { type WorkbenchAuthController } from "./auth-controller.js";
2
- export type AuthMethod = "browser" | "api_key" | "exit";
3
- export type AuthGateState = {
4
- status: "checking" | "select" | "api_profile" | "api_base_url" | "api_key" | "browser_profile" | "browser_base_url" | "browser_waiting" | "ready";
5
- selectedMethod: number;
6
- profile: string;
7
- baseURL: string;
8
- apiKey: string;
9
- message: string;
10
- error: string;
11
- browserURL: string;
12
- browserCode: string;
13
- };
14
- export interface AuthGateInputKey {
15
- backspace?: boolean;
16
- ctrl?: boolean;
17
- delete?: boolean;
18
- downArrow?: boolean;
19
- meta?: boolean;
20
- return?: boolean;
21
- upArrow?: boolean;
22
- }
23
- export type AuthGateInputEffect = {
24
- type: "exit";
25
- } | {
26
- type: "submit";
27
- };
28
- export interface AuthGateInputResult {
29
- state: AuthGateState;
30
- effects: AuthGateInputEffect[];
31
- }
32
- export interface AuthGateSubmitResult {
33
- state: AuthGateState;
34
- profileName?: string;
35
- }
36
- export interface WorkbenchAuthGateController {
37
- authMethods: typeof authMethods;
38
- initialState(input: {
39
- profile?: string;
40
- baseURL?: string;
41
- apiKey?: string;
42
- }): AuthGateState;
43
- check(profile?: string): Promise<AuthGateSubmitResult>;
44
- handleInput(input: string, key: AuthGateInputKey, state: AuthGateState): AuthGateInputResult;
45
- submit(state: AuthGateState, options?: {
46
- onState?: (state: AuthGateState) => void;
47
- }): Promise<AuthGateSubmitResult>;
48
- requestLogin(state: AuthGateState, profileName: string): AuthGateState;
49
- requestLogout(state: AuthGateState, profileName: string): AuthGateState;
50
- requestSwitchProfile(state: AuthGateState, currentProfile: string, nextProfile?: string): AuthGateState;
51
- deletedProfile(state: AuthGateState, profileName: string): AuthGateState;
52
- }
53
- export interface WorkbenchAuthGateControllerOptions {
54
- authController?: WorkbenchAuthController;
55
- formatError?: (error: unknown) => string;
56
- }
57
- export declare const authMethods: Array<{
58
- method: AuthMethod;
59
- label: string;
60
- description: string;
61
- }>;
62
- export declare function createWorkbenchAuthGateController(options?: WorkbenchAuthGateControllerOptions): WorkbenchAuthGateController;