@arbidocs/tui 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,899 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var commander = require('commander');
5
+ var core = require('@arbidocs/core');
6
+ var piTui = require('@mariozechner/pi-tui');
7
+ var chalk = require('chalk');
8
+ require('fake-indexeddb/auto');
9
+ var prompts = require('@inquirer/prompts');
10
+ var sdk = require('@arbidocs/sdk');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var chalk__default = /*#__PURE__*/_interopDefault(chalk);
15
+
16
+ var colors = {
17
+ /** Primary accent — teal/cyan used for headings, prompts, highlights */
18
+ accent: chalk__default.default.cyan,
19
+ accentBold: chalk__default.default.bold.cyan,
20
+ /** Secondary accent — blue for links and info */
21
+ secondary: chalk__default.default.blue,
22
+ /** Muted text — dim gray for borders, metadata */
23
+ muted: chalk__default.default.gray,
24
+ mutedDim: chalk__default.default.dim.gray,
25
+ /** Success — green for completed states */
26
+ success: chalk__default.default.green,
27
+ /** Warning — yellow for pending states, code */
28
+ warning: chalk__default.default.yellow,
29
+ /** Error — red for error messages */
30
+ error: chalk__default.default.red,
31
+ errorBold: chalk__default.default.bold.red,
32
+ /** Text — default and bold */
33
+ text: chalk__default.default.white,
34
+ textBold: chalk__default.default.bold.white,
35
+ /** User message styling */
36
+ userLabel: chalk__default.default.bold.green,
37
+ userText: chalk__default.default.white,
38
+ /** Assistant message styling */
39
+ assistantLabel: chalk__default.default.bold.cyan,
40
+ /** System message styling */
41
+ systemText: chalk__default.default.dim,
42
+ systemInfo: chalk__default.default.dim.cyan,
43
+ systemError: chalk__default.default.dim.red,
44
+ systemWarning: chalk__default.default.dim.yellow,
45
+ /** Agent step styling */
46
+ stepPending: chalk__default.default.dim.yellow,
47
+ stepComplete: chalk__default.default.dim.green
48
+ };
49
+ var selectListTheme = {
50
+ selectedPrefix: (s) => colors.accent(s),
51
+ selectedText: (s) => colors.text(s),
52
+ description: (s) => colors.muted(s),
53
+ scrollInfo: (s) => colors.muted(s),
54
+ noMatch: (s) => colors.muted(s)
55
+ };
56
+ var editorTheme = {
57
+ borderColor: (s) => colors.accent(s),
58
+ selectList: selectListTheme
59
+ };
60
+ var markdownTheme = {
61
+ heading: (s) => colors.accentBold(s),
62
+ link: (s) => chalk__default.default.underline.blue(s),
63
+ linkUrl: (s) => colors.muted(s),
64
+ code: (s) => colors.warning(s),
65
+ codeBlock: (s) => colors.text(s),
66
+ codeBlockBorder: (s) => colors.muted(s),
67
+ quote: (s) => chalk__default.default.italic.gray(s),
68
+ quoteBorder: (s) => colors.muted(s),
69
+ hr: (s) => colors.muted(s),
70
+ listBullet: (s) => colors.accent(s),
71
+ bold: (s) => chalk__default.default.bold(s),
72
+ italic: (s) => chalk__default.default.italic(s),
73
+ strikethrough: (s) => chalk__default.default.strikethrough(s),
74
+ underline: (s) => chalk__default.default.underline(s)
75
+ };
76
+ function formatHeader(workspaceName, status) {
77
+ const app = colors.accentBold("ARBI");
78
+ const ws = workspaceName ? colors.muted(` | ${workspaceName}`) : "";
79
+ const st = colors.muted(` | ${status}`);
80
+ return `${app}${ws}${st}`;
81
+ }
82
+ function highlightCode(code, lang) {
83
+ const lines = code.split("\n");
84
+ return lines.map((line) => {
85
+ let styled = line.replace(/(["'])(?:(?=(\\?))\2.)*?\1/g, (m) => chalk__default.default.green(m)).replace(/\b(\d+\.?\d*)\b/g, (m) => chalk__default.default.yellow(m)).replace(/(\/\/.*|#.*)$/g, (m) => chalk__default.default.gray(m));
86
+ const keywords = /\b(const|let|var|function|return|if|else|for|while|import|export|from|class|extends|new|async|await|try|catch|throw|type|interface|enum|def|self|None|True|False|print)\b/g;
87
+ styled = styled.replace(keywords, (m) => chalk__default.default.cyan(m));
88
+ return styled;
89
+ });
90
+ }
91
+
92
+ // src/components/assistant-message.ts
93
+ var assistantMarkdownTheme = {
94
+ ...markdownTheme,
95
+ highlightCode
96
+ };
97
+ var AssistantMessage = class extends piTui.Container {
98
+ markdown;
99
+ label;
100
+ constructor() {
101
+ super();
102
+ this.label = new piTui.Text(colors.assistantLabel("Assistant"), 1, 0);
103
+ this.markdown = new piTui.Markdown("", 1, 0, assistantMarkdownTheme);
104
+ this.addChild(this.label);
105
+ this.addChild(this.markdown);
106
+ }
107
+ /** Update the response text (called as tokens stream in). */
108
+ setText(text) {
109
+ this.markdown.setText(text);
110
+ }
111
+ };
112
+ var UserMessage = class extends piTui.Container {
113
+ constructor(text) {
114
+ super();
115
+ const label = new piTui.Text(colors.userLabel("You"), 1, 0);
116
+ const content = new piTui.Markdown(text, 1, 0, markdownTheme);
117
+ this.addChild(label);
118
+ this.addChild(content);
119
+ }
120
+ };
121
+ var SystemMessage = class extends piTui.Text {
122
+ constructor(message, level = "info") {
123
+ const styleFn = level === "error" ? colors.systemError : level === "warning" ? colors.systemWarning : colors.systemInfo;
124
+ super(styleFn(message), 1, 0);
125
+ }
126
+ };
127
+ var AgentStep = class extends piTui.Text {
128
+ completed = false;
129
+ constructor(focus) {
130
+ const styled = `${colors.stepPending(">")} ${colors.muted(focus)}`;
131
+ super(styled, 2, 0);
132
+ }
133
+ /** Mark this step as completed. */
134
+ complete() {
135
+ if (this.completed) return;
136
+ this.completed = true;
137
+ }
138
+ };
139
+
140
+ // src/components/chat-log.ts
141
+ var ChatLog = class extends piTui.Container {
142
+ /** Currently streaming assistant message (null when idle). */
143
+ activeAssistant = null;
144
+ /** Agent steps associated with the current streaming run. */
145
+ activeSteps = [];
146
+ /** Add a user message to the log. */
147
+ addUser(text) {
148
+ this.addChild(new UserMessage(text));
149
+ this.addChild(new piTui.Spacer(1));
150
+ }
151
+ /** Add a system/info/error message. */
152
+ addSystem(message, level = "info") {
153
+ this.addChild(new SystemMessage(message, level));
154
+ this.addChild(new piTui.Spacer(1));
155
+ }
156
+ /** Start a new assistant streaming response. */
157
+ startAssistant() {
158
+ this.activeAssistant = new AssistantMessage();
159
+ this.activeSteps = [];
160
+ this.addChild(this.activeAssistant);
161
+ }
162
+ /** Update the active assistant message with accumulated text. */
163
+ updateAssistant(text) {
164
+ this.activeAssistant?.setText(text);
165
+ }
166
+ /** Add an agent step to the current streaming run. */
167
+ addAgentStep(focus) {
168
+ if (!this.activeAssistant) return;
169
+ const step = new AgentStep(focus);
170
+ this.activeSteps.push(step);
171
+ this.addChild(step);
172
+ }
173
+ /** Finalize the current assistant response. */
174
+ finalizeAssistant() {
175
+ for (const step of this.activeSteps) {
176
+ step.complete();
177
+ }
178
+ this.activeAssistant = null;
179
+ this.activeSteps = [];
180
+ this.addChild(new piTui.Spacer(1));
181
+ }
182
+ };
183
+ var ArbiEditor = class extends piTui.Editor {
184
+ /** Callback when Escape is pressed (abort streaming). */
185
+ onEscape;
186
+ /** Callback when Ctrl+C is pressed (clear or exit). */
187
+ onCtrlC;
188
+ /** Callback when Ctrl+D is pressed (exit). */
189
+ onCtrlD;
190
+ /** Callback when Ctrl+W is pressed (workspace selector). */
191
+ onCtrlW;
192
+ /** Callback when Ctrl+N is pressed (new conversation). */
193
+ onCtrlN;
194
+ /** Track Ctrl+C presses for double-tap exit. */
195
+ lastCtrlCTime = 0;
196
+ constructor(tui) {
197
+ super(tui, editorTheme, { paddingX: 1 });
198
+ }
199
+ handleInput(data) {
200
+ if (piTui.matchesKey(data, piTui.Key.escape)) {
201
+ this.onEscape?.();
202
+ return;
203
+ }
204
+ if (piTui.matchesKey(data, piTui.Key.ctrl("c"))) {
205
+ const now = Date.now();
206
+ if (this.getText().trim()) {
207
+ this.setText("");
208
+ this.lastCtrlCTime = now;
209
+ } else if (now - this.lastCtrlCTime < 1e3) {
210
+ this.onCtrlC?.();
211
+ } else {
212
+ this.lastCtrlCTime = now;
213
+ this.onCtrlC?.();
214
+ }
215
+ return;
216
+ }
217
+ if (piTui.matchesKey(data, piTui.Key.ctrl("d"))) {
218
+ this.onCtrlD?.();
219
+ return;
220
+ }
221
+ if (piTui.matchesKey(data, piTui.Key.ctrl("w"))) {
222
+ this.onCtrlW?.();
223
+ return;
224
+ }
225
+ if (piTui.matchesKey(data, piTui.Key.ctrl("n"))) {
226
+ this.onCtrlN?.();
227
+ return;
228
+ }
229
+ super.handleInput(data);
230
+ }
231
+ };
232
+
233
+ // src/commands.ts
234
+ var commands = [
235
+ { name: "help", description: "Show available commands" },
236
+ { name: "login", description: "Log in (re-authenticate)" },
237
+ { name: "register", description: "Register a new account" },
238
+ { name: "workspace", description: "Switch workspace (or show selector)" },
239
+ { name: "workspaces", description: "List all workspaces" },
240
+ { name: "docs", description: "List documents in current workspace" },
241
+ { name: "conversations", description: "List conversations" },
242
+ { name: "new", description: "Start fresh conversation (clear threading)" },
243
+ { name: "status", description: "Show auth/workspace/connection status" },
244
+ { name: "exit", description: "Exit TUI" },
245
+ { name: "quit", description: "Exit TUI" }
246
+ ];
247
+ function formatHelpText() {
248
+ const lines = commands.filter((c) => c.name !== "quit").map((c) => ` /${c.name} \u2014 ${c.description}`);
249
+ return [
250
+ "Available commands:",
251
+ "",
252
+ ...lines,
253
+ "",
254
+ "Keyboard shortcuts:",
255
+ " Ctrl+N \u2014 New conversation",
256
+ " Ctrl+W \u2014 Switch workspace",
257
+ " Ctrl+D \u2014 Exit",
258
+ " Escape \u2014 Abort streaming"
259
+ ].join("\n");
260
+ }
261
+ async function interactiveLogin(store) {
262
+ const config = store.requireConfig();
263
+ const email = await prompts.input({ message: "Email", validate: (v) => v.trim() ? true : "Required" });
264
+ const pw = await prompts.password({
265
+ message: "Password",
266
+ mask: "*",
267
+ validate: (v) => v ? true : "Required"
268
+ });
269
+ const arbi = sdk.createArbiClient({
270
+ baseUrl: config.baseUrl,
271
+ deploymentDomain: config.deploymentDomain,
272
+ credentials: "omit"
273
+ });
274
+ await arbi.crypto.initSodium();
275
+ const loginResult = await arbi.auth.login({ email, password: pw });
276
+ store.saveCredentials({
277
+ email,
278
+ signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),
279
+ serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey)
280
+ });
281
+ console.info(`Logged in as ${email}`);
282
+ const { data: workspaces3 } = await arbi.fetch.GET("/api/user/workspaces");
283
+ const wsList = workspaces3 || [];
284
+ let selectedWorkspaceId;
285
+ let selectedWorkspaceName;
286
+ if (wsList.length === 1) {
287
+ selectedWorkspaceId = wsList[0].external_id;
288
+ selectedWorkspaceName = wsList[0].name;
289
+ store.updateConfig({ selectedWorkspaceId });
290
+ console.info(`Workspace: ${wsList[0].name}`);
291
+ } else if (wsList.length > 1) {
292
+ const choices = wsList.map((ws2) => {
293
+ const totalDocs = ws2.shared_document_count + ws2.private_document_count;
294
+ return {
295
+ name: `${ws2.name} (${totalDocs} docs)`,
296
+ value: ws2.external_id,
297
+ description: ws2.external_id
298
+ };
299
+ });
300
+ selectedWorkspaceId = await prompts.select({ message: "Select workspace", choices });
301
+ const ws = wsList.find((w) => w.external_id === selectedWorkspaceId);
302
+ selectedWorkspaceName = ws?.name;
303
+ store.updateConfig({ selectedWorkspaceId });
304
+ console.info(`Workspace: ${selectedWorkspaceName}`);
305
+ } else {
306
+ console.info("No workspaces found.");
307
+ }
308
+ const authContext = await core.resolveAuth(store);
309
+ return { authContext, selectedWorkspaceId, selectedWorkspaceName };
310
+ }
311
+ async function interactiveRegister(store) {
312
+ const config = store.requireConfig();
313
+ const email = await prompts.input({ message: "Email", validate: (v) => v.trim() ? true : "Required" });
314
+ const arbi = sdk.createArbiClient({
315
+ baseUrl: config.baseUrl,
316
+ deploymentDomain: config.deploymentDomain,
317
+ credentials: "omit"
318
+ });
319
+ await arbi.crypto.initSodium();
320
+ const codeMethod = await prompts.select({
321
+ message: "Verification method",
322
+ choices: [
323
+ { name: "I have an invitation code", value: "code" },
324
+ { name: "Send me a verification email", value: "email" }
325
+ ]
326
+ });
327
+ let verificationCode;
328
+ if (codeMethod === "code") {
329
+ verificationCode = await prompts.input({
330
+ message: "Invitation code",
331
+ validate: (v) => v.trim() ? true : "Required"
332
+ });
333
+ } else {
334
+ console.info("Sending verification email...");
335
+ const verifyResponse = await arbi.fetch.POST("/api/user/verify-email", {
336
+ body: { email }
337
+ });
338
+ if (verifyResponse.error) {
339
+ throw new Error(`Failed to send verification email: ${JSON.stringify(verifyResponse.error)}`);
340
+ }
341
+ console.info("Verification email sent. Check your inbox.");
342
+ verificationCode = await prompts.input({
343
+ message: "Verification code",
344
+ validate: (v) => v.trim() ? true : "Required"
345
+ });
346
+ }
347
+ const pw = await prompts.password({
348
+ message: "Password",
349
+ mask: "*",
350
+ validate: (v) => v ? true : "Required"
351
+ });
352
+ const confirmPw = await prompts.password({
353
+ message: "Confirm password",
354
+ mask: "*",
355
+ validate: (v) => v ? true : "Required"
356
+ });
357
+ if (pw !== confirmPw) {
358
+ throw new Error("Passwords do not match.");
359
+ }
360
+ const firstName = await prompts.input({ message: "First name (optional)" }) || "User";
361
+ const lastName = await prompts.input({ message: "Last name (optional)" }) || "";
362
+ await arbi.auth.register({
363
+ email,
364
+ password: pw,
365
+ verificationCode,
366
+ firstName,
367
+ lastName
368
+ });
369
+ console.info(`
370
+ Registered successfully as ${email}`);
371
+ const doLogin = await prompts.confirm({ message: "Log in now?", default: true });
372
+ if (doLogin) {
373
+ return interactiveLogin(store);
374
+ }
375
+ return null;
376
+ }
377
+ async function ensureAuthenticated(store) {
378
+ try {
379
+ const authContext = await core.resolveAuth(store);
380
+ const config = store.requireConfig();
381
+ return {
382
+ authContext,
383
+ selectedWorkspaceId: config.selectedWorkspaceId
384
+ };
385
+ } catch {
386
+ console.info("Not authenticated.\n");
387
+ const action = await prompts.select({
388
+ message: "What would you like to do?",
389
+ choices: [
390
+ { name: "Log in", value: "login" },
391
+ { name: "Register a new account", value: "register" },
392
+ { name: "Exit", value: "exit" }
393
+ ]
394
+ });
395
+ if (action === "exit") {
396
+ process.exit(0);
397
+ }
398
+ if (action === "register") {
399
+ const result = await interactiveRegister(store);
400
+ if (!result) {
401
+ console.info("Registration complete. Please restart to log in.");
402
+ process.exit(0);
403
+ }
404
+ return result;
405
+ }
406
+ return interactiveLogin(store);
407
+ }
408
+ }
409
+
410
+ // src/command-handlers.ts
411
+ async function handleCommand(tui, input2) {
412
+ const trimmed = input2.trim();
413
+ if (!trimmed.startsWith("/")) return false;
414
+ const parts = trimmed.slice(1).split(/\s+/);
415
+ const cmd = parts[0]?.toLowerCase();
416
+ const args = parts.slice(1);
417
+ switch (cmd) {
418
+ case "help":
419
+ tui.chatLog.addSystem(formatHelpText());
420
+ tui.requestRender();
421
+ return true;
422
+ case "login":
423
+ await handleLogin(tui);
424
+ return true;
425
+ case "register":
426
+ await handleRegister(tui);
427
+ return true;
428
+ case "workspace":
429
+ await handleWorkspaceSwitch(tui, args[0]);
430
+ return true;
431
+ case "workspaces":
432
+ await handleListWorkspaces(tui);
433
+ return true;
434
+ case "docs":
435
+ await handleListDocs(tui);
436
+ return true;
437
+ case "conversations":
438
+ await handleListConversations(tui);
439
+ return true;
440
+ case "new":
441
+ handleNewConversation(tui);
442
+ return true;
443
+ case "status":
444
+ handleStatus(tui);
445
+ return true;
446
+ case "exit":
447
+ case "quit":
448
+ tui.shutdown();
449
+ return true;
450
+ default:
451
+ tui.chatLog.addSystem(
452
+ `Unknown command: /${cmd}. Type /help for available commands.`,
453
+ "warning"
454
+ );
455
+ tui.requestRender();
456
+ return true;
457
+ }
458
+ }
459
+ async function handleLogin(tui) {
460
+ tui.chatLog.addSystem("Pausing TUI for login...");
461
+ tui.requestRender();
462
+ tui.stopTui();
463
+ try {
464
+ const result = await interactiveLogin(tui.store);
465
+ tui.setAuthContext(result.authContext);
466
+ if (result.selectedWorkspaceId) {
467
+ const wsCtx = await core.resolveWorkspace(tui.store, result.selectedWorkspaceId);
468
+ tui.setWorkspaceContext(wsCtx);
469
+ tui.state.workspaceName = result.selectedWorkspaceName ?? null;
470
+ }
471
+ tui.restartTui();
472
+ tui.chatLog.addSystem("Logged in successfully.");
473
+ tui.requestRender();
474
+ } catch (err) {
475
+ tui.restartTui();
476
+ const msg = err instanceof Error ? err.message : String(err);
477
+ tui.chatLog.addSystem(`Login failed: ${msg}`, "error");
478
+ tui.requestRender();
479
+ }
480
+ }
481
+ async function handleRegister(tui) {
482
+ tui.chatLog.addSystem("Pausing TUI for registration...");
483
+ tui.requestRender();
484
+ tui.stopTui();
485
+ try {
486
+ const result = await interactiveRegister(tui.store);
487
+ if (result) {
488
+ tui.setAuthContext(result.authContext);
489
+ if (result.selectedWorkspaceId) {
490
+ const wsCtx = await core.resolveWorkspace(tui.store, result.selectedWorkspaceId);
491
+ tui.setWorkspaceContext(wsCtx);
492
+ tui.state.workspaceName = result.selectedWorkspaceName ?? null;
493
+ }
494
+ }
495
+ tui.restartTui();
496
+ tui.chatLog.addSystem(
497
+ result ? "Registered and logged in." : "Registered. Use /login to log in."
498
+ );
499
+ tui.requestRender();
500
+ } catch (err) {
501
+ tui.restartTui();
502
+ const msg = err instanceof Error ? err.message : String(err);
503
+ tui.chatLog.addSystem(`Registration failed: ${msg}`, "error");
504
+ tui.requestRender();
505
+ }
506
+ }
507
+ async function handleWorkspaceSwitch(tui, workspaceId) {
508
+ if (!tui.authContext) {
509
+ tui.chatLog.addSystem("Not authenticated. Please restart and log in.", "error");
510
+ tui.requestRender();
511
+ return;
512
+ }
513
+ if (!workspaceId) {
514
+ await handleListWorkspaces(tui);
515
+ tui.chatLog.addSystem("Use /workspace <id> to switch.");
516
+ tui.requestRender();
517
+ return;
518
+ }
519
+ try {
520
+ tui.chatLog.addSystem(`Switching to workspace ${workspaceId}...`);
521
+ tui.requestRender();
522
+ const { selectWorkspaceById } = await import('@arbidocs/core');
523
+ const ws = await selectWorkspaceById(
524
+ tui.authContext.arbi,
525
+ workspaceId,
526
+ tui.authContext.loginResult.serverSessionKey,
527
+ tui.store.requireCredentials().signingPrivateKeyBase64
528
+ );
529
+ tui.state.workspaceId = ws.external_id;
530
+ tui.state.workspaceName = ws.name;
531
+ tui.state.conversationMessageId = null;
532
+ tui.store.updateConfig({ selectedWorkspaceId: ws.external_id });
533
+ await tui.refreshWorkspaceContext();
534
+ tui.chatLog.addSystem(`Switched to workspace: ${colors.accentBold(ws.name)}`);
535
+ tui.requestRender();
536
+ } catch (err) {
537
+ const msg = err instanceof Error ? err.message : String(err);
538
+ tui.chatLog.addSystem(`Failed to switch workspace: ${msg}`, "error");
539
+ tui.requestRender();
540
+ }
541
+ }
542
+ async function handleListWorkspaces(tui) {
543
+ if (!tui.authContext) {
544
+ tui.chatLog.addSystem("Not authenticated.", "error");
545
+ tui.requestRender();
546
+ return;
547
+ }
548
+ try {
549
+ const wsList = await core.workspaces.listWorkspaces(tui.authContext.arbi);
550
+ const lines = wsList.map((ws) => {
551
+ const current = ws.external_id === tui.state.workspaceId ? colors.accent(" (current)") : "";
552
+ return ` ${ws.external_id} ${ws.name}${current}`;
553
+ });
554
+ tui.chatLog.addSystem(["Workspaces:", "", ...lines].join("\n"));
555
+ tui.requestRender();
556
+ } catch (err) {
557
+ const msg = err instanceof Error ? err.message : String(err);
558
+ tui.chatLog.addSystem(`Failed to list workspaces: ${msg}`, "error");
559
+ tui.requestRender();
560
+ }
561
+ }
562
+ async function handleListDocs(tui) {
563
+ if (!tui.authContext || !tui.state.workspaceId) {
564
+ tui.chatLog.addSystem("No workspace selected. Use /workspace <id> first.", "warning");
565
+ tui.requestRender();
566
+ return;
567
+ }
568
+ try {
569
+ const docs = await core.documents.listDocuments(tui.authContext.arbi, tui.state.workspaceId);
570
+ if (docs.length === 0) {
571
+ tui.chatLog.addSystem("No documents in this workspace.");
572
+ } else {
573
+ const lines = docs.map((d) => ` ${d.external_id} ${d.file_name ?? "(unnamed)"}`);
574
+ tui.chatLog.addSystem([`Documents (${docs.length}):`, "", ...lines].join("\n"));
575
+ }
576
+ tui.requestRender();
577
+ } catch (err) {
578
+ const msg = err instanceof Error ? err.message : String(err);
579
+ tui.chatLog.addSystem(`Failed to list documents: ${msg}`, "error");
580
+ tui.requestRender();
581
+ }
582
+ }
583
+ async function handleListConversations(tui) {
584
+ if (!tui.authContext || !tui.state.workspaceId) {
585
+ tui.chatLog.addSystem("No workspace selected. Use /workspace <id> first.", "warning");
586
+ tui.requestRender();
587
+ return;
588
+ }
589
+ try {
590
+ const convs = await core.conversations.listConversations(tui.authContext.arbi, tui.state.workspaceId);
591
+ if (convs.length === 0) {
592
+ tui.chatLog.addSystem("No conversations in this workspace.");
593
+ } else {
594
+ const lines = convs.map((c) => ` ${c.external_id} ${c.title ?? "(untitled)"}`);
595
+ tui.chatLog.addSystem([`Conversations (${convs.length}):`, "", ...lines].join("\n"));
596
+ }
597
+ tui.requestRender();
598
+ } catch (err) {
599
+ const msg = err instanceof Error ? err.message : String(err);
600
+ tui.chatLog.addSystem(`Failed to list conversations: ${msg}`, "error");
601
+ tui.requestRender();
602
+ }
603
+ }
604
+ function handleNewConversation(tui) {
605
+ tui.state.conversationMessageId = null;
606
+ tui.store.clearChatSession();
607
+ tui.chatLog.addSystem("Started new conversation.");
608
+ tui.requestRender();
609
+ }
610
+ function handleStatus(tui) {
611
+ const { state } = tui;
612
+ const lines = [
613
+ `Authenticated: ${state.isAuthenticated ? colors.success("yes") : colors.error("no")}`,
614
+ `Workspace: ${state.workspaceName ? colors.accent(state.workspaceName) : colors.muted("none")}`,
615
+ `Workspace ID: ${state.workspaceId ?? colors.muted("none")}`,
616
+ `Conversation: ${state.conversationMessageId ? colors.muted(state.conversationMessageId) : colors.muted("new")}`,
617
+ `Status: ${state.activityStatus}`
618
+ ];
619
+ tui.chatLog.addSystem(lines.join("\n"));
620
+ tui.requestRender();
621
+ }
622
+ async function streamResponse(tui, response) {
623
+ tui.chatLog.startAssistant();
624
+ tui.state.activityStatus = "streaming";
625
+ tui.requestRender();
626
+ let accumulated = "";
627
+ const callbacks = {
628
+ onStreamStart: (data) => {
629
+ if (data.assistant_message_ext_id) {
630
+ tui.state.conversationMessageId = data.assistant_message_ext_id;
631
+ }
632
+ },
633
+ onToken: (content) => {
634
+ accumulated += content;
635
+ tui.chatLog.updateAssistant(accumulated);
636
+ tui.requestRender();
637
+ },
638
+ onAgentStep: (data) => {
639
+ const focus = data.focus || data.status || "";
640
+ if (focus) {
641
+ tui.chatLog.addAgentStep(focus);
642
+ tui.requestRender();
643
+ }
644
+ },
645
+ onError: (message) => {
646
+ tui.chatLog.addSystem(`Stream error: ${message}`, "error");
647
+ tui.requestRender();
648
+ }
649
+ };
650
+ try {
651
+ const result = await core.streamSSE(response, callbacks);
652
+ tui.chatLog.finalizeAssistant();
653
+ tui.state.activityStatus = "idle";
654
+ tui.requestRender();
655
+ return result;
656
+ } catch (err) {
657
+ tui.chatLog.finalizeAssistant();
658
+ tui.state.activityStatus = "error";
659
+ const msg = err instanceof Error ? err.message : String(err);
660
+ tui.chatLog.addSystem(`Streaming failed: ${msg}`, "error");
661
+ tui.requestRender();
662
+ throw err;
663
+ }
664
+ }
665
+
666
+ // src/tui.ts
667
+ var ArbiTui = class {
668
+ tui;
669
+ header;
670
+ /** The chat message log — public so command handlers can add messages. */
671
+ chatLog;
672
+ /** The input editor. */
673
+ editor;
674
+ /** Application state — public so handlers can read/write it. */
675
+ state;
676
+ /** Auth context from @arbidocs/core — set during init. */
677
+ authContext = null;
678
+ /** Workspace context with tokens/headers — set when workspace is selected. */
679
+ workspaceContext = null;
680
+ /** Config store for persistence. */
681
+ store;
682
+ constructor(store) {
683
+ this.store = store;
684
+ this.state = {
685
+ workspaceId: null,
686
+ workspaceName: null,
687
+ conversationMessageId: null,
688
+ activityStatus: "idle",
689
+ isAuthenticated: false
690
+ };
691
+ this.tui = new piTui.TUI(new piTui.ProcessTerminal());
692
+ this.header = new piTui.Text(formatHeader(null, "starting..."), 1, 0);
693
+ this.chatLog = new ChatLog();
694
+ this.editor = new ArbiEditor(this.tui);
695
+ this.editor.setAutocompleteProvider(new piTui.CombinedAutocompleteProvider(commands));
696
+ this.editor.onSubmit = (text) => this.handleSubmit(text);
697
+ this.editor.onEscape = () => this.handleAbort();
698
+ this.editor.onCtrlC = () => this.shutdown();
699
+ this.editor.onCtrlD = () => this.shutdown();
700
+ this.editor.onCtrlW = () => this.handleSubmit("/workspaces");
701
+ this.editor.onCtrlN = () => this.handleSubmit("/new");
702
+ this.tui.addChild(this.header);
703
+ this.tui.addChild(new piTui.Spacer(1));
704
+ this.tui.addChild(this.chatLog);
705
+ this.tui.addChild(this.editor);
706
+ this.tui.setFocus(this.editor);
707
+ }
708
+ // ── Lifecycle ──────────────────────────────────────────────────────────
709
+ /** Start the TUI event loop. */
710
+ start() {
711
+ this.tui.start();
712
+ this.updateHeader();
713
+ this.tui.requestRender();
714
+ }
715
+ /** Gracefully shut down the TUI and exit. */
716
+ shutdown() {
717
+ this.tui.stop();
718
+ process.exit(0);
719
+ }
720
+ /**
721
+ * Temporarily stop the TUI (releases terminal raw mode).
722
+ * Used before interactive prompts that need stdin (login, register).
723
+ */
724
+ stopTui() {
725
+ this.tui.stop();
726
+ }
727
+ /**
728
+ * Restart the TUI after a stopTui() call.
729
+ * Rebuilds the component tree with a fresh TUI instance since
730
+ * pi-tui doesn't support stop/start cycling on the same instance.
731
+ */
732
+ restartTui() {
733
+ this.tui = new piTui.TUI(new piTui.ProcessTerminal());
734
+ this.tui.addChild(this.header);
735
+ this.tui.addChild(new piTui.Spacer(1));
736
+ this.tui.addChild(this.chatLog);
737
+ this.editor = new ArbiEditor(this.tui);
738
+ this.editor.setAutocompleteProvider(new piTui.CombinedAutocompleteProvider(commands));
739
+ this.editor.onSubmit = (text) => this.handleSubmit(text);
740
+ this.editor.onEscape = () => this.handleAbort();
741
+ this.editor.onCtrlC = () => this.shutdown();
742
+ this.editor.onCtrlD = () => this.shutdown();
743
+ this.editor.onCtrlW = () => this.handleSubmit("/workspaces");
744
+ this.editor.onCtrlN = () => this.handleSubmit("/new");
745
+ this.tui.addChild(this.editor);
746
+ this.tui.setFocus(this.editor);
747
+ this.tui.start();
748
+ this.updateHeader();
749
+ this.tui.requestRender();
750
+ }
751
+ /** Request a render update. */
752
+ requestRender() {
753
+ this.tui.requestRender();
754
+ }
755
+ // ── Auth / Workspace ───────────────────────────────────────────────────
756
+ /** Set authentication context after login. */
757
+ setAuthContext(ctx) {
758
+ this.authContext = ctx;
759
+ this.state.isAuthenticated = true;
760
+ this.updateHeader();
761
+ }
762
+ /** Set workspace context after workspace selection. */
763
+ setWorkspaceContext(ctx) {
764
+ this.workspaceContext = ctx;
765
+ this.state.workspaceId = ctx.workspaceId;
766
+ this.updateHeader();
767
+ }
768
+ /** Refresh workspace context (after switching workspaces). */
769
+ async refreshWorkspaceContext() {
770
+ if (!this.state.workspaceId || !this.authContext) return;
771
+ const { resolveWorkspace: resolveWorkspace3 } = await import('@arbidocs/core');
772
+ const ctx = await resolveWorkspace3(this.store, this.state.workspaceId);
773
+ this.workspaceContext = ctx;
774
+ this.updateHeader();
775
+ }
776
+ // ── Input handling ─────────────────────────────────────────────────────
777
+ async handleSubmit(text) {
778
+ const trimmed = text.trim();
779
+ if (!trimmed) return;
780
+ if (trimmed.startsWith("/")) {
781
+ const handled = await handleCommand(this, trimmed);
782
+ if (handled) return;
783
+ }
784
+ await this.sendMessage(trimmed);
785
+ }
786
+ handleAbort() {
787
+ if (this.state.activityStatus === "streaming") {
788
+ this.chatLog.addSystem("Abort requested (stream will complete current chunk).", "warning");
789
+ this.requestRender();
790
+ }
791
+ }
792
+ // ── Messaging ──────────────────────────────────────────────────────────
793
+ async sendMessage(question) {
794
+ if (!this.workspaceContext) {
795
+ this.chatLog.addSystem(
796
+ "No workspace selected. Use /workspace <id> or /workspaces to choose one.",
797
+ "warning"
798
+ );
799
+ this.requestRender();
800
+ return;
801
+ }
802
+ this.chatLog.addUser(question);
803
+ this.state.activityStatus = "sending";
804
+ this.updateHeader();
805
+ this.requestRender();
806
+ try {
807
+ const docs = await core.documents.listDocuments(
808
+ this.workspaceContext.arbi,
809
+ this.workspaceContext.workspaceId
810
+ );
811
+ const docIds = docs.map((d) => d.external_id);
812
+ const session = this.store.getChatSession();
813
+ const parentMessageExtId = this.state.conversationMessageId ?? session.lastMessageExtId;
814
+ const response = await core.assistant.queryAssistant({
815
+ baseUrl: this.workspaceContext.config.baseUrl,
816
+ accessToken: this.workspaceContext.accessToken,
817
+ workspaceKeyHeader: this.workspaceContext.workspaceKeyHeader,
818
+ workspaceId: this.workspaceContext.workspaceId,
819
+ question,
820
+ docIds,
821
+ parentMessageExtId
822
+ });
823
+ const result = await streamResponse(this, response);
824
+ if (result.assistantMessageExtId) {
825
+ this.state.conversationMessageId = result.assistantMessageExtId;
826
+ this.store.updateChatSession({ lastMessageExtId: result.assistantMessageExtId });
827
+ }
828
+ } catch (err) {
829
+ this.state.activityStatus = "error";
830
+ const msg = err instanceof Error ? err.message : String(err);
831
+ this.chatLog.addSystem(`Error: ${msg}`, "error");
832
+ }
833
+ this.state.activityStatus = "idle";
834
+ this.updateHeader();
835
+ this.requestRender();
836
+ }
837
+ // ── UI Updates ─────────────────────────────────────────────────────────
838
+ updateHeader() {
839
+ const statusText = this.state.activityStatus === "idle" ? colors.success("ready") : this.state.activityStatus === "streaming" ? colors.warning("streaming...") : this.state.activityStatus === "sending" ? colors.warning("sending...") : colors.error("error");
840
+ this.header.setText(formatHeader(this.state.workspaceName, statusText));
841
+ }
842
+ };
843
+
844
+ // src/index.ts
845
+ console.debug = () => {
846
+ };
847
+ var _origInfo = console.info;
848
+ console.info = (...args) => {
849
+ if (typeof args[0] === "string" && args[0].startsWith("[API]")) return;
850
+ _origInfo(...args);
851
+ };
852
+ var program = new commander.Command();
853
+ program.name("arbi-tui").description("Interactive terminal UI for ARBI \u2014 chat with the RAG assistant").version("0.1.0").option("-w, --workspace <id>", "Workspace ID to use").action(async (opts) => {
854
+ const store = new core.FileConfigStore();
855
+ try {
856
+ store.requireConfig();
857
+ } catch {
858
+ console.error("Not configured. Run `arbi config set-url <url>` first.");
859
+ process.exit(1);
860
+ }
861
+ const { authContext, selectedWorkspaceId, selectedWorkspaceName } = await ensureAuthenticated(store);
862
+ const workspaceId = opts.workspace || selectedWorkspaceId;
863
+ const tui = new ArbiTui(store);
864
+ tui.setAuthContext(authContext);
865
+ if (workspaceId) {
866
+ try {
867
+ const wsCtx = await core.resolveWorkspace(store, workspaceId);
868
+ tui.setWorkspaceContext(wsCtx);
869
+ if (selectedWorkspaceName && workspaceId === selectedWorkspaceId) {
870
+ tui.state.workspaceName = selectedWorkspaceName;
871
+ } else {
872
+ const wsList = await core.workspaces.listWorkspaces(authContext.arbi);
873
+ const ws = wsList.find((w) => w.external_id === workspaceId);
874
+ if (ws) {
875
+ tui.state.workspaceName = ws.name;
876
+ }
877
+ }
878
+ } catch (err) {
879
+ const msg = err instanceof Error ? err.message : String(err);
880
+ tui.chatLog.addSystem(`Failed to load workspace: ${msg}`, "warning");
881
+ tui.chatLog.addSystem("Use /workspaces to list and /workspace <id> to select.");
882
+ }
883
+ } else {
884
+ tui.chatLog.addSystem(
885
+ "No workspace selected. Use /workspaces to list and /workspace <id> to select."
886
+ );
887
+ }
888
+ const session = store.getChatSession();
889
+ if (session.lastMessageExtId) {
890
+ tui.state.conversationMessageId = session.lastMessageExtId;
891
+ }
892
+ tui.chatLog.addSystem(
893
+ "Type a question to chat with the ARBI assistant. Use /help for commands."
894
+ );
895
+ tui.start();
896
+ });
897
+ program.parse();
898
+ //# sourceMappingURL=index.cjs.map
899
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theme/theme.ts","../src/theme/syntax-theme.ts","../src/components/assistant-message.ts","../src/components/user-message.ts","../src/components/system-message.ts","../src/components/agent-step.ts","../src/components/chat-log.ts","../src/components/arbi-editor.ts","../src/commands.ts","../src/auth.ts","../src/command-handlers.ts","../src/event-handlers.ts","../src/tui.ts","../src/index.ts"],"names":["chalk","Container","Text","Markdown","Spacer","Editor","matchesKey","Key","input","password","createArbiClient","workspaces","ws","select","resolveAuth","confirm","resolveWorkspace","documents","conversations","streamSSE","TUI","ProcessTerminal","CombinedAutocompleteProvider","assistant","Command","FileConfigStore"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * ARBI TUI theme — color palette and component styling functions.\n *\n * Uses chalk for ANSI color output. All theme values are functions\n * that wrap text in the appropriate escape sequences.\n */\n\nimport chalk from 'chalk'\nimport type { MarkdownTheme, EditorTheme, SelectListTheme } from '@mariozechner/pi-tui'\n\n// ── Color palette ──────────────────────────────────────────────────────────\n\nexport const colors = {\n /** Primary accent — teal/cyan used for headings, prompts, highlights */\n accent: chalk.cyan,\n accentBold: chalk.bold.cyan,\n\n /** Secondary accent — blue for links and info */\n secondary: chalk.blue,\n\n /** Muted text — dim gray for borders, metadata */\n muted: chalk.gray,\n mutedDim: chalk.dim.gray,\n\n /** Success — green for completed states */\n success: chalk.green,\n\n /** Warning — yellow for pending states, code */\n warning: chalk.yellow,\n\n /** Error — red for error messages */\n error: chalk.red,\n errorBold: chalk.bold.red,\n\n /** Text — default and bold */\n text: chalk.white,\n textBold: chalk.bold.white,\n\n /** User message styling */\n userLabel: chalk.bold.green,\n userText: chalk.white,\n\n /** Assistant message styling */\n assistantLabel: chalk.bold.cyan,\n\n /** System message styling */\n systemText: chalk.dim,\n systemInfo: chalk.dim.cyan,\n systemError: chalk.dim.red,\n systemWarning: chalk.dim.yellow,\n\n /** Agent step styling */\n stepPending: chalk.dim.yellow,\n stepComplete: chalk.dim.green,\n} as const\n\n// ── Background functions ───────────────────────────────────────────────────\n\nexport const bgFn = {\n /** Subtle background for user messages */\n user: (text: string) => chalk.bgGray(text),\n\n /** Background for agent steps */\n agentStep: (text: string) => text,\n} as const\n\n// ── Component themes ───────────────────────────────────────────────────────\n\nexport const selectListTheme: SelectListTheme = {\n selectedPrefix: (s: string) => colors.accent(s),\n selectedText: (s: string) => colors.text(s),\n description: (s: string) => colors.muted(s),\n scrollInfo: (s: string) => colors.muted(s),\n noMatch: (s: string) => colors.muted(s),\n}\n\nexport const editorTheme: EditorTheme = {\n borderColor: (s: string) => colors.accent(s),\n selectList: selectListTheme,\n}\n\nexport const markdownTheme: MarkdownTheme = {\n heading: (s: string) => colors.accentBold(s),\n link: (s: string) => chalk.underline.blue(s),\n linkUrl: (s: string) => colors.muted(s),\n code: (s: string) => colors.warning(s),\n codeBlock: (s: string) => colors.text(s),\n codeBlockBorder: (s: string) => colors.muted(s),\n quote: (s: string) => chalk.italic.gray(s),\n quoteBorder: (s: string) => colors.muted(s),\n hr: (s: string) => colors.muted(s),\n listBullet: (s: string) => colors.accent(s),\n bold: (s: string) => chalk.bold(s),\n italic: (s: string) => chalk.italic(s),\n strikethrough: (s: string) => chalk.strikethrough(s),\n underline: (s: string) => chalk.underline(s),\n}\n\n// ── Header / Footer helpers ────────────────────────────────────────────────\n\nexport function formatHeader(workspaceName: string | null, status: string): string {\n const app = colors.accentBold('ARBI')\n const ws = workspaceName ? colors.muted(` | ${workspaceName}`) : ''\n const st = colors.muted(` | ${status}`)\n return `${app}${ws}${st}`\n}\n\nexport function formatPrompt(): string {\n return colors.accent('> ')\n}\n","/**\n * Syntax highlighting theme for code blocks in Markdown.\n *\n * Provides a highlightCode function compatible with pi-tui's\n * MarkdownTheme.highlightCode option.\n */\n\nimport chalk from 'chalk'\n\n/**\n * Simple keyword-based syntax highlighting for code blocks.\n * Returns an array of styled lines.\n *\n * For a first version, we apply basic keyword coloring. This can be\n * upgraded to use a proper highlighter (e.g., cli-highlight) later.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function highlightCode(code: string, lang?: string): string[] {\n const lines = code.split('\\n')\n\n return lines.map((line) => {\n // Highlight common keywords\n let styled = line\n // String literals (double and single quoted)\n .replace(/([\"'])(?:(?=(\\\\?))\\2.)*?\\1/g, (m) => chalk.green(m))\n // Numbers\n .replace(/\\b(\\d+\\.?\\d*)\\b/g, (m) => chalk.yellow(m))\n // Comments (// and #)\n .replace(/(\\/\\/.*|#.*)$/g, (m) => chalk.gray(m))\n\n // Keywords (applied after strings/comments to avoid conflicts in simple cases)\n const keywords =\n /\\b(const|let|var|function|return|if|else|for|while|import|export|from|class|extends|new|async|await|try|catch|throw|type|interface|enum|def|self|None|True|False|print)\\b/g\n styled = styled.replace(keywords, (m) => chalk.cyan(m))\n\n return styled\n })\n}\n","/**\n * AssistantMessage — streaming markdown response from the RAG assistant.\n *\n * Wraps pi-tui's Markdown component. Call setText() as tokens arrive\n * to update the rendered content incrementally.\n */\n\nimport { Container, Markdown, Text } from '@mariozechner/pi-tui'\nimport { markdownTheme, colors } from '../theme/theme.js'\nimport { highlightCode } from '../theme/syntax-theme.js'\n\nconst assistantMarkdownTheme = {\n ...markdownTheme,\n highlightCode,\n}\n\nexport class AssistantMessage extends Container {\n private markdown: Markdown\n private label: Text\n\n constructor() {\n super()\n this.label = new Text(colors.assistantLabel('Assistant'), 1, 0)\n this.markdown = new Markdown('', 1, 0, assistantMarkdownTheme)\n this.addChild(this.label)\n this.addChild(this.markdown)\n }\n\n /** Update the response text (called as tokens stream in). */\n setText(text: string): void {\n this.markdown.setText(text)\n }\n}\n","/**\n * UserMessage — displays a user's submitted question.\n *\n * Wraps pi-tui's Markdown component with user-specific styling.\n */\n\nimport { Container, Markdown, Text } from '@mariozechner/pi-tui'\nimport { markdownTheme, colors } from '../theme/theme.js'\n\nexport class UserMessage extends Container {\n constructor(text: string) {\n super()\n const label = new Text(colors.userLabel('You'), 1, 0)\n const content = new Markdown(text, 1, 0, markdownTheme)\n this.addChild(label)\n this.addChild(content)\n }\n}\n","/**\n * SystemMessage — info, warning, and error messages.\n *\n * Wraps pi-tui's Text component with dim/accent styling.\n */\n\nimport { Text } from '@mariozechner/pi-tui'\nimport { colors } from '../theme/theme.js'\n\nexport type SystemMessageLevel = 'info' | 'warning' | 'error'\n\nexport class SystemMessage extends Text {\n constructor(message: string, level: SystemMessageLevel = 'info') {\n const styleFn =\n level === 'error'\n ? colors.systemError\n : level === 'warning'\n ? colors.systemWarning\n : colors.systemInfo\n super(styleFn(message), 1, 0)\n }\n}\n","/**\n * AgentStep — displays a single agent processing step.\n *\n * Shows the step focus/description with a status indicator.\n * Background changes from pending (yellow) to complete (green).\n */\n\nimport { Text } from '@mariozechner/pi-tui'\nimport { colors } from '../theme/theme.js'\n\nexport class AgentStep extends Text {\n private completed = false\n\n constructor(focus: string) {\n const styled = `${colors.stepPending('>')} ${colors.muted(focus)}`\n super(styled, 2, 0)\n }\n\n /** Mark this step as completed. */\n complete(): void {\n if (this.completed) return\n this.completed = true\n // Re-render would require knowing the original text — for now we leave styling as-is.\n // The step stays visible as context for the user.\n }\n}\n","/**\n * ChatLog — container managing the message list and streaming runs.\n *\n * Provides methods for adding user/system messages and managing\n * assistant streaming lifecycle (start → update → finalize).\n */\n\nimport { Container, Spacer } from '@mariozechner/pi-tui'\nimport { AssistantMessage } from './assistant-message.js'\nimport { UserMessage } from './user-message.js'\nimport { SystemMessage, type SystemMessageLevel } from './system-message.js'\nimport { AgentStep } from './agent-step.js'\n\nexport class ChatLog extends Container {\n /** Currently streaming assistant message (null when idle). */\n private activeAssistant: AssistantMessage | null = null\n\n /** Agent steps associated with the current streaming run. */\n private activeSteps: AgentStep[] = []\n\n /** Add a user message to the log. */\n addUser(text: string): void {\n this.addChild(new UserMessage(text))\n this.addChild(new Spacer(1))\n }\n\n /** Add a system/info/error message. */\n addSystem(message: string, level: SystemMessageLevel = 'info'): void {\n this.addChild(new SystemMessage(message, level))\n this.addChild(new Spacer(1))\n }\n\n /** Start a new assistant streaming response. */\n startAssistant(): void {\n this.activeAssistant = new AssistantMessage()\n this.activeSteps = []\n this.addChild(this.activeAssistant)\n }\n\n /** Update the active assistant message with accumulated text. */\n updateAssistant(text: string): void {\n this.activeAssistant?.setText(text)\n }\n\n /** Add an agent step to the current streaming run. */\n addAgentStep(focus: string): void {\n if (!this.activeAssistant) return\n const step = new AgentStep(focus)\n this.activeSteps.push(step)\n // Insert step before the assistant message content\n this.addChild(step)\n }\n\n /** Finalize the current assistant response. */\n finalizeAssistant(): void {\n for (const step of this.activeSteps) {\n step.complete()\n }\n this.activeAssistant = null\n this.activeSteps = []\n this.addChild(new Spacer(1))\n }\n}\n","/**\n * ArbiEditor — custom Editor with ARBI-specific keybindings.\n *\n * Extends pi-tui's Editor to add:\n * - Escape → abort active stream\n * - Ctrl+C → clear input / double-tap to exit\n * - Ctrl+D → exit\n * - Ctrl+W → workspace selector\n * - Ctrl+N → new conversation\n */\n\nimport { Editor, matchesKey, Key, type TUI } from '@mariozechner/pi-tui'\nimport { editorTheme } from '../theme/theme.js'\n\nexport class ArbiEditor extends Editor {\n /** Callback when Escape is pressed (abort streaming). */\n onEscape?: () => void\n\n /** Callback when Ctrl+C is pressed (clear or exit). */\n onCtrlC?: () => void\n\n /** Callback when Ctrl+D is pressed (exit). */\n onCtrlD?: () => void\n\n /** Callback when Ctrl+W is pressed (workspace selector). */\n onCtrlW?: () => void\n\n /** Callback when Ctrl+N is pressed (new conversation). */\n onCtrlN?: () => void\n\n /** Track Ctrl+C presses for double-tap exit. */\n private lastCtrlCTime = 0\n\n constructor(tui: TUI) {\n super(tui, editorTheme, { paddingX: 1 })\n }\n\n override handleInput(data: string): void {\n if (matchesKey(data, Key.escape)) {\n this.onEscape?.()\n return\n }\n\n if (matchesKey(data, Key.ctrl('c'))) {\n const now = Date.now()\n if (this.getText().trim()) {\n // First Ctrl+C with content: clear input\n this.setText('')\n this.lastCtrlCTime = now\n } else if (now - this.lastCtrlCTime < 1000) {\n // Double Ctrl+C within 1s: exit\n this.onCtrlC?.()\n } else {\n this.lastCtrlCTime = now\n this.onCtrlC?.()\n }\n return\n }\n\n if (matchesKey(data, Key.ctrl('d'))) {\n this.onCtrlD?.()\n return\n }\n\n if (matchesKey(data, Key.ctrl('w'))) {\n this.onCtrlW?.()\n return\n }\n\n if (matchesKey(data, Key.ctrl('n'))) {\n this.onCtrlN?.()\n return\n }\n\n // Pass all other input to the base Editor\n super.handleInput(data)\n }\n}\n","/**\n * Command registry — slash commands available in the TUI.\n *\n * Each command has a name, description, and optional argument hint.\n * Used for autocomplete and help display.\n */\n\nimport type { SlashCommand } from '@mariozechner/pi-tui'\n\nexport interface TuiCommand extends SlashCommand {\n name: string\n description: string\n}\n\nexport const commands: TuiCommand[] = [\n { name: 'help', description: 'Show available commands' },\n { name: 'login', description: 'Log in (re-authenticate)' },\n { name: 'register', description: 'Register a new account' },\n { name: 'workspace', description: 'Switch workspace (or show selector)' },\n { name: 'workspaces', description: 'List all workspaces' },\n { name: 'docs', description: 'List documents in current workspace' },\n { name: 'conversations', description: 'List conversations' },\n { name: 'new', description: 'Start fresh conversation (clear threading)' },\n { name: 'status', description: 'Show auth/workspace/connection status' },\n { name: 'exit', description: 'Exit TUI' },\n { name: 'quit', description: 'Exit TUI' },\n]\n\n/** Format all commands as a help string for display. */\nexport function formatHelpText(): string {\n const lines = commands\n .filter((c) => c.name !== 'quit') // Don't show duplicate exit/quit\n .map((c) => ` /${c.name} — ${c.description}`)\n return [\n 'Available commands:',\n '',\n ...lines,\n '',\n 'Keyboard shortcuts:',\n ' Ctrl+N — New conversation',\n ' Ctrl+W — Switch workspace',\n ' Ctrl+D — Exit',\n ' Escape — Abort streaming',\n ].join('\\n')\n}\n","/**\n * Authentication flows for the TUI.\n *\n * Handles login and registration using @inquirer/prompts for interactive input.\n * These flows run BEFORE the TUI takes over the terminal (pi-tui raw mode),\n * or after temporarily stopping the TUI.\n */\n\nimport 'fake-indexeddb/auto'\nimport { input, password, select, confirm } from '@inquirer/prompts'\nimport { createArbiClient } from '@arbidocs/sdk'\nimport type { ConfigStore, AuthContext } from '@arbidocs/core'\nimport { resolveAuth } from '@arbidocs/core'\n\n// ── Login ──────────────────────────────────────────────────────────────────\n\nexport interface LoginResult {\n authContext: AuthContext\n selectedWorkspaceId?: string\n selectedWorkspaceName?: string\n}\n\n/**\n * Interactive login flow. Prompts for email/password, authenticates,\n * and offers workspace selection.\n *\n * Must be called before the TUI starts (or after stopping it).\n */\nexport async function interactiveLogin(store: ConfigStore): Promise<LoginResult> {\n const config = store.requireConfig()\n\n const email = await input({ message: 'Email', validate: (v) => (v.trim() ? true : 'Required') })\n const pw = await password({\n message: 'Password',\n mask: '*',\n validate: (v) => (v ? true : 'Required'),\n })\n\n const arbi = createArbiClient({\n baseUrl: config.baseUrl,\n deploymentDomain: config.deploymentDomain,\n credentials: 'omit',\n })\n\n await arbi.crypto.initSodium()\n\n const loginResult = await arbi.auth.login({ email, password: pw })\n\n store.saveCredentials({\n email,\n signingPrivateKeyBase64: arbi.crypto.bytesToBase64(loginResult.signingPrivateKey),\n serverSessionKeyBase64: arbi.crypto.bytesToBase64(loginResult.serverSessionKey),\n })\n\n console.info(`Logged in as ${email}`)\n\n // Workspace selection\n const { data: workspaces } = await arbi.fetch.GET('/api/user/workspaces')\n const wsList = workspaces || []\n\n let selectedWorkspaceId: string | undefined\n let selectedWorkspaceName: string | undefined\n\n if (wsList.length === 1) {\n selectedWorkspaceId = wsList[0].external_id\n selectedWorkspaceName = wsList[0].name\n store.updateConfig({ selectedWorkspaceId })\n console.info(`Workspace: ${wsList[0].name}`)\n } else if (wsList.length > 1) {\n const choices = wsList.map((ws) => {\n const totalDocs = ws.shared_document_count + ws.private_document_count\n return {\n name: `${ws.name} (${totalDocs} docs)`,\n value: ws.external_id,\n description: ws.external_id,\n }\n })\n\n selectedWorkspaceId = await select({ message: 'Select workspace', choices })\n const ws = wsList.find((w) => w.external_id === selectedWorkspaceId)\n selectedWorkspaceName = ws?.name\n store.updateConfig({ selectedWorkspaceId })\n console.info(`Workspace: ${selectedWorkspaceName}`)\n } else {\n console.info('No workspaces found.')\n }\n\n // Re-resolve auth with saved credentials\n const authContext = await resolveAuth(store)\n\n return { authContext, selectedWorkspaceId, selectedWorkspaceName }\n}\n\n// ── Registration ───────────────────────────────────────────────────────────\n\n/**\n * Interactive registration flow. Prompts for email, verification code,\n * password, and name. Optionally logs in afterward.\n *\n * Must be called before the TUI starts (or after stopping it).\n */\nexport async function interactiveRegister(store: ConfigStore): Promise<LoginResult | null> {\n const config = store.requireConfig()\n\n const email = await input({ message: 'Email', validate: (v) => (v.trim() ? true : 'Required') })\n\n const arbi = createArbiClient({\n baseUrl: config.baseUrl,\n deploymentDomain: config.deploymentDomain,\n credentials: 'omit',\n })\n await arbi.crypto.initSodium()\n\n // Verification\n const codeMethod = await select({\n message: 'Verification method',\n choices: [\n { name: 'I have an invitation code', value: 'code' as const },\n { name: 'Send me a verification email', value: 'email' as const },\n ],\n })\n\n let verificationCode: string\n if (codeMethod === 'code') {\n verificationCode = await input({\n message: 'Invitation code',\n validate: (v) => (v.trim() ? true : 'Required'),\n })\n } else {\n console.info('Sending verification email...')\n const verifyResponse = await arbi.fetch.POST('/api/user/verify-email', {\n body: { email },\n })\n if (verifyResponse.error) {\n throw new Error(`Failed to send verification email: ${JSON.stringify(verifyResponse.error)}`)\n }\n console.info('Verification email sent. Check your inbox.')\n verificationCode = await input({\n message: 'Verification code',\n validate: (v) => (v.trim() ? true : 'Required'),\n })\n }\n\n // Password\n const pw = await password({\n message: 'Password',\n mask: '*',\n validate: (v) => (v ? true : 'Required'),\n })\n const confirmPw = await password({\n message: 'Confirm password',\n mask: '*',\n validate: (v) => (v ? true : 'Required'),\n })\n if (pw !== confirmPw) {\n throw new Error('Passwords do not match.')\n }\n\n // Name\n const firstName = (await input({ message: 'First name (optional)' })) || 'User'\n const lastName = (await input({ message: 'Last name (optional)' })) || ''\n\n // Register\n await arbi.auth.register({\n email,\n password: pw,\n verificationCode,\n firstName,\n lastName,\n })\n\n console.info(`\\nRegistered successfully as ${email}`)\n\n // Offer to log in\n const doLogin = await confirm({ message: 'Log in now?', default: true })\n if (doLogin) {\n return interactiveLogin(store)\n }\n\n return null\n}\n\n// ── Pre-flight auth check ──────────────────────────────────────────────────\n\n/**\n * Check if the user is authenticated. If not, offer to login or register.\n * Returns the auth context if successful, or exits the process.\n *\n * Called at startup before the TUI launches.\n */\nexport async function ensureAuthenticated(store: ConfigStore): Promise<LoginResult> {\n // Try existing credentials first\n try {\n const authContext = await resolveAuth(store)\n const config = store.requireConfig()\n return {\n authContext,\n selectedWorkspaceId: config.selectedWorkspaceId,\n }\n } catch {\n // Not authenticated — offer login/register\n console.info('Not authenticated.\\n')\n\n const action = await select({\n message: 'What would you like to do?',\n choices: [\n { name: 'Log in', value: 'login' as const },\n { name: 'Register a new account', value: 'register' as const },\n { name: 'Exit', value: 'exit' as const },\n ],\n })\n\n if (action === 'exit') {\n process.exit(0)\n }\n\n if (action === 'register') {\n const result = await interactiveRegister(store)\n if (!result) {\n console.info('Registration complete. Please restart to log in.')\n process.exit(0)\n }\n return result\n }\n\n // Login\n return interactiveLogin(store)\n }\n}\n","/**\n * Command handlers — dispatch logic for slash commands.\n *\n * Each handler receives the TUI context and performs its action,\n * typically displaying results in the chat log.\n */\n\nimport { workspaces, documents, conversations, resolveWorkspace } from '@arbidocs/core'\nimport type { ArbiTui } from './tui.js'\nimport { formatHelpText } from './commands.js'\nimport { interactiveLogin, interactiveRegister } from './auth.js'\nimport { colors } from './theme/theme.js'\n\n/** Dispatch a slash command. Returns true if handled. */\nexport async function handleCommand(tui: ArbiTui, input: string): Promise<boolean> {\n const trimmed = input.trim()\n if (!trimmed.startsWith('/')) return false\n\n const parts = trimmed.slice(1).split(/\\s+/)\n const cmd = parts[0]?.toLowerCase()\n const args = parts.slice(1)\n\n switch (cmd) {\n case 'help':\n tui.chatLog.addSystem(formatHelpText())\n tui.requestRender()\n return true\n\n case 'login':\n await handleLogin(tui)\n return true\n\n case 'register':\n await handleRegister(tui)\n return true\n\n case 'workspace':\n await handleWorkspaceSwitch(tui, args[0])\n return true\n\n case 'workspaces':\n await handleListWorkspaces(tui)\n return true\n\n case 'docs':\n await handleListDocs(tui)\n return true\n\n case 'conversations':\n await handleListConversations(tui)\n return true\n\n case 'new':\n handleNewConversation(tui)\n return true\n\n case 'status':\n handleStatus(tui)\n return true\n\n case 'exit':\n case 'quit':\n tui.shutdown()\n return true\n\n default:\n tui.chatLog.addSystem(\n `Unknown command: /${cmd}. Type /help for available commands.`,\n 'warning'\n )\n tui.requestRender()\n return true\n }\n}\n\nasync function handleLogin(tui: ArbiTui): Promise<void> {\n tui.chatLog.addSystem('Pausing TUI for login...')\n tui.requestRender()\n\n // Stop the TUI so @inquirer/prompts can use stdin\n tui.stopTui()\n\n try {\n const result = await interactiveLogin(tui.store)\n tui.setAuthContext(result.authContext)\n\n if (result.selectedWorkspaceId) {\n const wsCtx = await resolveWorkspace(tui.store, result.selectedWorkspaceId)\n tui.setWorkspaceContext(wsCtx)\n tui.state.workspaceName = result.selectedWorkspaceName ?? null\n }\n\n // Restart TUI\n tui.restartTui()\n tui.chatLog.addSystem('Logged in successfully.')\n tui.requestRender()\n } catch (err) {\n // Restart TUI even on error\n tui.restartTui()\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Login failed: ${msg}`, 'error')\n tui.requestRender()\n }\n}\n\nasync function handleRegister(tui: ArbiTui): Promise<void> {\n tui.chatLog.addSystem('Pausing TUI for registration...')\n tui.requestRender()\n\n // Stop the TUI so @inquirer/prompts can use stdin\n tui.stopTui()\n\n try {\n const result = await interactiveRegister(tui.store)\n\n if (result) {\n tui.setAuthContext(result.authContext)\n\n if (result.selectedWorkspaceId) {\n const wsCtx = await resolveWorkspace(tui.store, result.selectedWorkspaceId)\n tui.setWorkspaceContext(wsCtx)\n tui.state.workspaceName = result.selectedWorkspaceName ?? null\n }\n }\n\n // Restart TUI\n tui.restartTui()\n tui.chatLog.addSystem(\n result ? 'Registered and logged in.' : 'Registered. Use /login to log in.'\n )\n tui.requestRender()\n } catch (err) {\n // Restart TUI even on error\n tui.restartTui()\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Registration failed: ${msg}`, 'error')\n tui.requestRender()\n }\n}\n\nasync function handleWorkspaceSwitch(tui: ArbiTui, workspaceId?: string): Promise<void> {\n if (!tui.authContext) {\n tui.chatLog.addSystem('Not authenticated. Please restart and log in.', 'error')\n tui.requestRender()\n return\n }\n\n if (!workspaceId) {\n // Show workspace list for selection\n await handleListWorkspaces(tui)\n tui.chatLog.addSystem('Use /workspace <id> to switch.')\n tui.requestRender()\n return\n }\n\n try {\n tui.chatLog.addSystem(`Switching to workspace ${workspaceId}...`)\n tui.requestRender()\n\n const { selectWorkspaceById } = await import('@arbidocs/core')\n const ws = await selectWorkspaceById(\n tui.authContext.arbi,\n workspaceId,\n tui.authContext.loginResult.serverSessionKey,\n tui.store.requireCredentials().signingPrivateKeyBase64\n )\n\n // Update state\n tui.state.workspaceId = ws.external_id\n tui.state.workspaceName = ws.name\n tui.state.conversationMessageId = null\n\n // Update config\n tui.store.updateConfig({ selectedWorkspaceId: ws.external_id })\n\n // Refresh workspace context\n await tui.refreshWorkspaceContext()\n\n tui.chatLog.addSystem(`Switched to workspace: ${colors.accentBold(ws.name)}`)\n tui.requestRender()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Failed to switch workspace: ${msg}`, 'error')\n tui.requestRender()\n }\n}\n\nasync function handleListWorkspaces(tui: ArbiTui): Promise<void> {\n if (!tui.authContext) {\n tui.chatLog.addSystem('Not authenticated.', 'error')\n tui.requestRender()\n return\n }\n\n try {\n const wsList = await workspaces.listWorkspaces(tui.authContext.arbi)\n const lines = wsList.map((ws) => {\n const current = ws.external_id === tui.state.workspaceId ? colors.accent(' (current)') : ''\n return ` ${ws.external_id} ${ws.name}${current}`\n })\n tui.chatLog.addSystem(['Workspaces:', '', ...lines].join('\\n'))\n tui.requestRender()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Failed to list workspaces: ${msg}`, 'error')\n tui.requestRender()\n }\n}\n\nasync function handleListDocs(tui: ArbiTui): Promise<void> {\n if (!tui.authContext || !tui.state.workspaceId) {\n tui.chatLog.addSystem('No workspace selected. Use /workspace <id> first.', 'warning')\n tui.requestRender()\n return\n }\n\n try {\n const docs = await documents.listDocuments(tui.authContext.arbi, tui.state.workspaceId)\n if (docs.length === 0) {\n tui.chatLog.addSystem('No documents in this workspace.')\n } else {\n const lines = docs.map((d) => ` ${d.external_id} ${d.file_name ?? '(unnamed)'}`)\n tui.chatLog.addSystem([`Documents (${docs.length}):`, '', ...lines].join('\\n'))\n }\n tui.requestRender()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Failed to list documents: ${msg}`, 'error')\n tui.requestRender()\n }\n}\n\nasync function handleListConversations(tui: ArbiTui): Promise<void> {\n if (!tui.authContext || !tui.state.workspaceId) {\n tui.chatLog.addSystem('No workspace selected. Use /workspace <id> first.', 'warning')\n tui.requestRender()\n return\n }\n\n try {\n const convs = await conversations.listConversations(tui.authContext.arbi, tui.state.workspaceId)\n if (convs.length === 0) {\n tui.chatLog.addSystem('No conversations in this workspace.')\n } else {\n const lines = convs.map((c) => ` ${c.external_id} ${c.title ?? '(untitled)'}`)\n tui.chatLog.addSystem([`Conversations (${convs.length}):`, '', ...lines].join('\\n'))\n }\n tui.requestRender()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Failed to list conversations: ${msg}`, 'error')\n tui.requestRender()\n }\n}\n\nfunction handleNewConversation(tui: ArbiTui): void {\n tui.state.conversationMessageId = null\n tui.store.clearChatSession()\n tui.chatLog.addSystem('Started new conversation.')\n tui.requestRender()\n}\n\nfunction handleStatus(tui: ArbiTui): void {\n const { state } = tui\n const lines = [\n `Authenticated: ${state.isAuthenticated ? colors.success('yes') : colors.error('no')}`,\n `Workspace: ${state.workspaceName ? colors.accent(state.workspaceName) : colors.muted('none')}`,\n `Workspace ID: ${state.workspaceId ?? colors.muted('none')}`,\n `Conversation: ${state.conversationMessageId ? colors.muted(state.conversationMessageId) : colors.muted('new')}`,\n `Status: ${state.activityStatus}`,\n ]\n tui.chatLog.addSystem(lines.join('\\n'))\n tui.requestRender()\n}\n","/**\n * SSE streaming event handler — wires @arbidocs/core streaming\n * into TUI component updates.\n */\n\nimport { streamSSE, type SSEStreamCallbacks, type SSEStreamResult } from '@arbidocs/core'\nimport type { ArbiTui } from './tui.js'\n\n/**\n * Stream an assistant response from the API into the TUI.\n *\n * 1. Creates an assistant message in the chat log\n * 2. Streams tokens into it via SSE callbacks\n * 3. Displays agent steps as they arrive\n * 4. Finalizes when complete\n *\n * Returns the full SSE result for caller to extract metadata.\n */\nexport async function streamResponse(tui: ArbiTui, response: Response): Promise<SSEStreamResult> {\n tui.chatLog.startAssistant()\n tui.state.activityStatus = 'streaming'\n tui.requestRender()\n\n let accumulated = ''\n\n const callbacks: SSEStreamCallbacks = {\n onStreamStart: (data) => {\n if (data.assistant_message_ext_id) {\n tui.state.conversationMessageId = data.assistant_message_ext_id as string\n }\n },\n\n onToken: (content) => {\n accumulated += content\n tui.chatLog.updateAssistant(accumulated)\n tui.requestRender()\n },\n\n onAgentStep: (data) => {\n const focus = data.focus || data.status || ''\n if (focus) {\n tui.chatLog.addAgentStep(focus)\n tui.requestRender()\n }\n },\n\n onError: (message) => {\n tui.chatLog.addSystem(`Stream error: ${message}`, 'error')\n tui.requestRender()\n },\n }\n\n try {\n const result = await streamSSE(response, callbacks)\n tui.chatLog.finalizeAssistant()\n tui.state.activityStatus = 'idle'\n tui.requestRender()\n return result\n } catch (err) {\n tui.chatLog.finalizeAssistant()\n tui.state.activityStatus = 'error'\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Streaming failed: ${msg}`, 'error')\n tui.requestRender()\n throw err\n }\n}\n","/**\n * ArbiTui — main orchestrator for the ARBI terminal UI.\n *\n * Manages state, component wiring, and lifecycle. Coordinates between\n * the editor input, chat log display, command dispatch, and SSE streaming.\n */\n\nimport {\n TUI,\n ProcessTerminal,\n Text,\n Spacer,\n CombinedAutocompleteProvider,\n} from '@mariozechner/pi-tui'\nimport {\n type AuthContext,\n type WorkspaceContext,\n type ConfigStore,\n assistant,\n documents,\n} from '@arbidocs/core'\nimport { ChatLog } from './components/chat-log.js'\nimport { ArbiEditor } from './components/arbi-editor.js'\nimport { handleCommand } from './command-handlers.js'\nimport { streamResponse } from './event-handlers.js'\nimport { commands } from './commands.js'\nimport { colors, formatHeader } from './theme/theme.js'\n\n// ── State ──────────────────────────────────────────────────────────────────\n\nexport interface TuiState {\n workspaceId: string | null\n workspaceName: string | null\n conversationMessageId: string | null\n activityStatus: 'idle' | 'sending' | 'streaming' | 'error'\n isAuthenticated: boolean\n}\n\n// ── Main TUI class ─────────────────────────────────────────────────────────\n\nexport class ArbiTui {\n private tui: TUI\n private header: Text\n\n /** The chat message log — public so command handlers can add messages. */\n readonly chatLog: ChatLog\n\n /** The input editor. */\n private editor: ArbiEditor\n\n /** Application state — public so handlers can read/write it. */\n state: TuiState\n\n /** Auth context from @arbidocs/core — set during init. */\n authContext: AuthContext | null = null\n\n /** Workspace context with tokens/headers — set when workspace is selected. */\n private workspaceContext: WorkspaceContext | null = null\n\n /** Config store for persistence. */\n readonly store: ConfigStore\n\n constructor(store: ConfigStore) {\n this.store = store\n\n this.state = {\n workspaceId: null,\n workspaceName: null,\n conversationMessageId: null,\n activityStatus: 'idle',\n isAuthenticated: false,\n }\n\n // Build TUI component tree\n this.tui = new TUI(new ProcessTerminal())\n\n // Header\n this.header = new Text(formatHeader(null, 'starting...'), 1, 0)\n\n // Chat log\n this.chatLog = new ChatLog()\n\n // Editor\n this.editor = new ArbiEditor(this.tui)\n this.editor.setAutocompleteProvider(new CombinedAutocompleteProvider(commands))\n\n // Wire editor callbacks\n this.editor.onSubmit = (text: string) => this.handleSubmit(text)\n this.editor.onEscape = () => this.handleAbort()\n this.editor.onCtrlC = () => this.shutdown()\n this.editor.onCtrlD = () => this.shutdown()\n this.editor.onCtrlW = () => this.handleSubmit('/workspaces')\n this.editor.onCtrlN = () => this.handleSubmit('/new')\n\n // Compose layout\n this.tui.addChild(this.header)\n this.tui.addChild(new Spacer(1))\n this.tui.addChild(this.chatLog)\n this.tui.addChild(this.editor)\n\n this.tui.setFocus(this.editor)\n }\n\n // ── Lifecycle ──────────────────────────────────────────────────────────\n\n /** Start the TUI event loop. */\n start(): void {\n this.tui.start()\n this.updateHeader()\n this.tui.requestRender()\n }\n\n /** Gracefully shut down the TUI and exit. */\n shutdown(): void {\n this.tui.stop()\n process.exit(0)\n }\n\n /**\n * Temporarily stop the TUI (releases terminal raw mode).\n * Used before interactive prompts that need stdin (login, register).\n */\n stopTui(): void {\n this.tui.stop()\n }\n\n /**\n * Restart the TUI after a stopTui() call.\n * Rebuilds the component tree with a fresh TUI instance since\n * pi-tui doesn't support stop/start cycling on the same instance.\n */\n restartTui(): void {\n this.tui = new TUI(new ProcessTerminal())\n\n // Re-compose layout\n this.tui.addChild(this.header)\n this.tui.addChild(new Spacer(1))\n this.tui.addChild(this.chatLog)\n\n // Create new editor (needs fresh TUI reference)\n this.editor = new ArbiEditor(this.tui)\n this.editor.setAutocompleteProvider(new CombinedAutocompleteProvider(commands))\n this.editor.onSubmit = (text: string) => this.handleSubmit(text)\n this.editor.onEscape = () => this.handleAbort()\n this.editor.onCtrlC = () => this.shutdown()\n this.editor.onCtrlD = () => this.shutdown()\n this.editor.onCtrlW = () => this.handleSubmit('/workspaces')\n this.editor.onCtrlN = () => this.handleSubmit('/new')\n\n this.tui.addChild(this.editor)\n this.tui.setFocus(this.editor)\n\n this.tui.start()\n this.updateHeader()\n this.tui.requestRender()\n }\n\n /** Request a render update. */\n requestRender(): void {\n this.tui.requestRender()\n }\n\n // ── Auth / Workspace ───────────────────────────────────────────────────\n\n /** Set authentication context after login. */\n setAuthContext(ctx: AuthContext): void {\n this.authContext = ctx\n this.state.isAuthenticated = true\n this.updateHeader()\n }\n\n /** Set workspace context after workspace selection. */\n setWorkspaceContext(ctx: WorkspaceContext): void {\n this.workspaceContext = ctx\n this.state.workspaceId = ctx.workspaceId\n // Workspace name will be set separately since WorkspaceContext doesn't include it\n this.updateHeader()\n }\n\n /** Refresh workspace context (after switching workspaces). */\n async refreshWorkspaceContext(): Promise<void> {\n if (!this.state.workspaceId || !this.authContext) return\n\n const { resolveWorkspace } = await import('@arbidocs/core')\n const ctx = await resolveWorkspace(this.store, this.state.workspaceId)\n this.workspaceContext = ctx\n this.updateHeader()\n }\n\n // ── Input handling ─────────────────────────────────────────────────────\n\n private async handleSubmit(text: string): Promise<void> {\n const trimmed = text.trim()\n if (!trimmed) return\n\n // Check for slash commands\n if (trimmed.startsWith('/')) {\n const handled = await handleCommand(this, trimmed)\n if (handled) return\n }\n\n // Regular message — send to assistant\n await this.sendMessage(trimmed)\n }\n\n private handleAbort(): void {\n if (this.state.activityStatus === 'streaming') {\n // TODO: implement AbortController for stream cancellation\n this.chatLog.addSystem('Abort requested (stream will complete current chunk).', 'warning')\n this.requestRender()\n }\n }\n\n // ── Messaging ──────────────────────────────────────────────────────────\n\n private async sendMessage(question: string): Promise<void> {\n if (!this.workspaceContext) {\n this.chatLog.addSystem(\n 'No workspace selected. Use /workspace <id> or /workspaces to choose one.',\n 'warning'\n )\n this.requestRender()\n return\n }\n\n // Display user message\n this.chatLog.addUser(question)\n this.state.activityStatus = 'sending'\n this.updateHeader()\n this.requestRender()\n\n try {\n // Get all docs in workspace for retrieval context\n const docs = await documents.listDocuments(\n this.workspaceContext.arbi,\n this.workspaceContext.workspaceId\n )\n const docIds = docs.map((d) => d.external_id as string)\n\n // Load chat session for conversation threading\n const session = this.store.getChatSession()\n const parentMessageExtId = this.state.conversationMessageId ?? session.lastMessageExtId\n\n // Query the assistant\n const response = await assistant.queryAssistant({\n baseUrl: this.workspaceContext.config.baseUrl,\n accessToken: this.workspaceContext.accessToken,\n workspaceKeyHeader: this.workspaceContext.workspaceKeyHeader,\n workspaceId: this.workspaceContext.workspaceId,\n question,\n docIds,\n parentMessageExtId,\n })\n\n // Stream the response into the chat log\n const result = await streamResponse(this, response)\n\n // Save conversation state for threading\n if (result.assistantMessageExtId) {\n this.state.conversationMessageId = result.assistantMessageExtId\n this.store.updateChatSession({ lastMessageExtId: result.assistantMessageExtId })\n }\n } catch (err) {\n this.state.activityStatus = 'error'\n const msg = err instanceof Error ? err.message : String(err)\n this.chatLog.addSystem(`Error: ${msg}`, 'error')\n }\n\n this.state.activityStatus = 'idle'\n this.updateHeader()\n this.requestRender()\n }\n\n // ── UI Updates ─────────────────────────────────────────────────────────\n\n private updateHeader(): void {\n const statusText =\n this.state.activityStatus === 'idle'\n ? colors.success('ready')\n : this.state.activityStatus === 'streaming'\n ? colors.warning('streaming...')\n : this.state.activityStatus === 'sending'\n ? colors.warning('sending...')\n : colors.error('error')\n\n this.header.setText(formatHeader(this.state.workspaceName, statusText))\n }\n}\n","/**\n * ARBI TUI — entry point\n *\n * Handles CLI argument parsing, authentication, and TUI launch.\n * Uses @arbidocs/core for auth and config, pi-tui for rendering.\n *\n * If not authenticated, offers interactive login/register before launching.\n *\n * Usage:\n * arbi-tui # Launch with default workspace\n * arbi-tui -w <workspace-id> # Launch with specific workspace\n */\n\n// Suppress SDK middleware logging\nconsole.debug = () => {}\nconst _origInfo = console.info\nconsole.info = (...args: unknown[]) => {\n if (typeof args[0] === 'string' && args[0].startsWith('[API]')) return\n _origInfo(...args)\n}\n\nimport { Command } from 'commander'\nimport { FileConfigStore, resolveWorkspace, workspaces } from '@arbidocs/core'\nimport { ArbiTui } from './tui.js'\nimport { ensureAuthenticated } from './auth.js'\n\nconst program = new Command()\n\nprogram\n .name('arbi-tui')\n .description('Interactive terminal UI for ARBI — chat with the RAG assistant')\n .version('0.1.0')\n .option('-w, --workspace <id>', 'Workspace ID to use')\n .action(async (opts: { workspace?: string }) => {\n const store = new FileConfigStore()\n\n // Ensure config exists\n try {\n store.requireConfig()\n } catch {\n console.error('Not configured. Run `arbi config set-url <url>` first.')\n process.exit(1)\n }\n\n // Authenticate — offers login/register if not already authenticated\n const { authContext, selectedWorkspaceId, selectedWorkspaceName } =\n await ensureAuthenticated(store)\n\n // Resolve workspace\n const workspaceId = opts.workspace || selectedWorkspaceId\n\n // Create and start TUI\n const tui = new ArbiTui(store)\n tui.setAuthContext(authContext)\n\n if (workspaceId) {\n try {\n const wsCtx = await resolveWorkspace(store, workspaceId)\n tui.setWorkspaceContext(wsCtx)\n\n // Get workspace name\n if (selectedWorkspaceName && workspaceId === selectedWorkspaceId) {\n tui.state.workspaceName = selectedWorkspaceName\n } else {\n const wsList = await workspaces.listWorkspaces(authContext.arbi)\n const ws = wsList.find((w) => w.external_id === workspaceId)\n if (ws) {\n tui.state.workspaceName = ws.name\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n tui.chatLog.addSystem(`Failed to load workspace: ${msg}`, 'warning')\n tui.chatLog.addSystem('Use /workspaces to list and /workspace <id> to select.')\n }\n } else {\n tui.chatLog.addSystem(\n 'No workspace selected. Use /workspaces to list and /workspace <id> to select.'\n )\n }\n\n // Load chat session for conversation continuity\n const session = store.getChatSession()\n if (session.lastMessageExtId) {\n tui.state.conversationMessageId = session.lastMessageExtId\n }\n\n tui.chatLog.addSystem(\n 'Type a question to chat with the ARBI assistant. Use /help for commands.'\n )\n tui.start()\n })\n\nprogram.parse()\n"]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@arbidocs/tui",
3
+ "version": "0.1.0",
4
+ "description": "Interactive terminal UI for ARBI — chat-style RAG assistant with streaming responses",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "bin": {
8
+ "arbi-tui": "dist/index.cjs"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@arbidocs/core": "^0.1.0",
19
+ "@arbidocs/sdk": "^0.1.0",
20
+ "@inquirer/prompts": "^8.2.0",
21
+ "@mariozechner/pi-tui": "^0.52.6",
22
+ "chalk": "^5.4.1",
23
+ "commander": "^13.1.0",
24
+ "fake-indexeddb": "^6.2.5"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^24.9.2",
28
+ "tsup": "^8.4.0",
29
+ "typescript": "^5.9.3"
30
+ },
31
+ "license": "MIT",
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/arbitrationcity/ARBI-frontend.git",
38
+ "directory": "packages/arbi-tui"
39
+ },
40
+ "keywords": [
41
+ "arbi",
42
+ "tui",
43
+ "terminal",
44
+ "rag",
45
+ "chat",
46
+ "streaming"
47
+ ]
48
+ }