@okrapdf/cli 0.2.13 → 0.2.16
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/commands/chat.d.ts +19 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +185 -195
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/chat.test.d.ts +17 -0
- package/dist/commands/chat.test.d.ts.map +1 -0
- package/dist/commands/chat.test.js +199 -0
- package/dist/commands/chat.test.js.map +1 -0
- package/dist/commands/entities.d.ts.map +1 -1
- package/dist/commands/entities.js +4 -3
- package/dist/commands/entities.js.map +1 -1
- package/dist/commands/jobs.d.ts.map +1 -1
- package/dist/commands/jobs.js +250 -54
- package/dist/commands/jobs.js.map +1 -1
- package/dist/commands/schema.test.js +18 -0
- package/dist/commands/schema.test.js.map +1 -1
- package/dist/lib/agent-renderer.d.ts +27 -0
- package/dist/lib/agent-renderer.d.ts.map +1 -0
- package/dist/lib/agent-renderer.js +71 -0
- package/dist/lib/agent-renderer.js.map +1 -0
- package/dist/lib/bootstrap.d.ts +13 -0
- package/dist/lib/bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap.js +13 -0
- package/dist/lib/bootstrap.js.map +1 -0
- package/dist/lib/bootstrap.test.d.ts +8 -0
- package/dist/lib/bootstrap.test.d.ts.map +1 -0
- package/dist/lib/bootstrap.test.js +53 -0
- package/dist/lib/bootstrap.test.js.map +1 -0
- package/dist/lib/pdfquery-adapter.d.ts +2 -1
- package/dist/lib/pdfquery-adapter.d.ts.map +1 -1
- package/dist/lib/pdfquery-adapter.js.map +1 -1
- package/dist/lib/system-prompt.d.ts +5 -0
- package/dist/lib/system-prompt.d.ts.map +1 -0
- package/dist/lib/system-prompt.js +58 -0
- package/dist/lib/system-prompt.js.map +1 -0
- package/dist/lib/validator.js +11 -11
- package/dist/lib/validator.js.map +1 -1
- package/dist/lib/ws-polyfill.d.ts +2 -0
- package/dist/lib/ws-polyfill.d.ts.map +1 -0
- package/dist/lib/ws-polyfill.js +9 -0
- package/dist/lib/ws-polyfill.js.map +1 -0
- package/dist/types.d.ts +64 -20
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/commands/chat.d.ts
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Agent-driven chat via WebSocket (same session as web UI)
|
|
3
|
+
*
|
|
4
|
+
* okra chat <jobId> -m "what tables are in this document?"
|
|
5
|
+
*
|
|
6
|
+
* Both the web UI and CLI are clients to the same Cloudflare Durable Object.
|
|
7
|
+
* On reconnect the DO replays persisted events (EVENTS_BATCH) containing old
|
|
8
|
+
* AGENT_MESSAGE, AGENT_DONE, etc. We must ignore those and only render events
|
|
9
|
+
* triggered by OUR new message.
|
|
10
|
+
*
|
|
11
|
+
* Server message order on reconnect:
|
|
12
|
+
* Frame 1: CONNECTED
|
|
13
|
+
* Frame 2: READY (with sandboxId if running)
|
|
14
|
+
* Frame 3: SANDBOX_STATUS (live, not replayed)
|
|
15
|
+
* Frame 4: EVENTS_BATCH (replayed history — arrives AFTER SANDBOX_STATUS)
|
|
16
|
+
*
|
|
17
|
+
* Because EVENTS_BATCH arrives after SANDBOX_STATUS resolves, a simple
|
|
18
|
+
* messageSent flag doesn't work. Instead we use a global listener to detect
|
|
19
|
+
* EVENTS_BATCH and wait for it before sending our message.
|
|
3
20
|
*/
|
|
21
|
+
import '../lib/ws-polyfill.js';
|
|
4
22
|
import { Command } from 'commander';
|
|
5
23
|
export declare function createChatCommand(): Command;
|
|
6
24
|
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,uBAAuB,CAAC;AAE/B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,iBAAiB,IAAI,OAAO,CA2L3C"}
|
package/dist/commands/chat.js
CHANGED
|
@@ -1,218 +1,208 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Agent-driven chat via WebSocket (same session as web UI)
|
|
3
|
+
*
|
|
4
|
+
* okra chat <jobId> -m "what tables are in this document?"
|
|
5
|
+
*
|
|
6
|
+
* Both the web UI and CLI are clients to the same Cloudflare Durable Object.
|
|
7
|
+
* On reconnect the DO replays persisted events (EVENTS_BATCH) containing old
|
|
8
|
+
* AGENT_MESSAGE, AGENT_DONE, etc. We must ignore those and only render events
|
|
9
|
+
* triggered by OUR new message.
|
|
10
|
+
*
|
|
11
|
+
* Server message order on reconnect:
|
|
12
|
+
* Frame 1: CONNECTED
|
|
13
|
+
* Frame 2: READY (with sandboxId if running)
|
|
14
|
+
* Frame 3: SANDBOX_STATUS (live, not replayed)
|
|
15
|
+
* Frame 4: EVENTS_BATCH (replayed history — arrives AFTER SANDBOX_STATUS)
|
|
16
|
+
*
|
|
17
|
+
* Because EVENTS_BATCH arrives after SANDBOX_STATUS resolves, a simple
|
|
18
|
+
* messageSent flag doesn't work. Instead we use a global listener to detect
|
|
19
|
+
* EVENTS_BATCH and wait for it before sending our message.
|
|
3
20
|
*/
|
|
21
|
+
import '../lib/ws-polyfill.js';
|
|
4
22
|
import { Command } from 'commander';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
23
|
+
import { AgentSessionClient } from '@steventsao/agent-session';
|
|
24
|
+
import { fetchBootstrap } from '../lib/bootstrap.js';
|
|
25
|
+
import { AgentRenderer } from '../lib/agent-renderer.js';
|
|
26
|
+
import { CLI_SYSTEM_PROMPT } from '../lib/system-prompt.js';
|
|
27
|
+
import { getApiKey } from '../lib/config.js';
|
|
28
|
+
import { error, info } from '../lib/output.js';
|
|
29
|
+
import { createSpinner } from '../lib/progress.js';
|
|
30
|
+
import { EXIT_CODES } from '../lib/client.js';
|
|
31
|
+
const WSS_URL = 'wss://agent-session.steventsao.workers.dev';
|
|
32
|
+
const SANDBOX_TEMPLATE = 'okra-claude-agent-sdk';
|
|
33
|
+
const SANDBOX_TIMEOUT_MS = 120_000;
|
|
10
34
|
export function createChatCommand() {
|
|
11
35
|
const chat = new Command('chat')
|
|
12
|
-
.description('Chat with
|
|
13
|
-
.argument('<
|
|
14
|
-
.
|
|
36
|
+
.description('Chat with a document via agent session')
|
|
37
|
+
.argument('<jobId>', 'OCR job ID (e.g. ocr-Bvx9ire6wFEHbsjecW3FM)')
|
|
38
|
+
.requiredOption('-m, --message <message>', 'Message to send')
|
|
15
39
|
.option('-o, --output <format>', 'Output format (text, json)', 'text')
|
|
16
|
-
.
|
|
17
|
-
|
|
18
|
-
|
|
40
|
+
.action(async (jobId, options) => {
|
|
41
|
+
const apiKey = getApiKey();
|
|
42
|
+
if (!apiKey) {
|
|
43
|
+
error('Not authenticated. Run `okra auth login` first.');
|
|
44
|
+
process.exit(EXIT_CODES.AUTH_ERROR);
|
|
45
|
+
}
|
|
46
|
+
// 1. Fetch bootstrap (archive URL + metadata)
|
|
47
|
+
const bootSpinner = createSpinner('Preparing document...');
|
|
48
|
+
bootSpinner.start();
|
|
49
|
+
let archiveUrl;
|
|
50
|
+
let totalPages;
|
|
19
51
|
try {
|
|
20
|
-
const
|
|
21
|
-
|
|
52
|
+
const bootstrap = await fetchBootstrap(jobId);
|
|
53
|
+
archiveUrl = bootstrap.archiveUrl;
|
|
54
|
+
totalPages = bootstrap.metadata.totalPages;
|
|
55
|
+
bootSpinner.succeed(`Document ready (${totalPages} pages)`);
|
|
22
56
|
}
|
|
23
57
|
catch (err) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
throw err;
|
|
58
|
+
bootSpinner.fail('Failed to prepare document');
|
|
59
|
+
error(err instanceof Error ? err.message : String(err));
|
|
60
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
29
61
|
}
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
62
|
+
// 2. Connect WebSocket
|
|
63
|
+
const renderer = new AgentRenderer();
|
|
64
|
+
const client = new AgentSessionClient({
|
|
65
|
+
url: WSS_URL,
|
|
66
|
+
sessionId: jobId,
|
|
67
|
+
autoConnect: false,
|
|
68
|
+
autoReconnect: false,
|
|
69
|
+
});
|
|
70
|
+
// Gate: ignore replayed agent events until we've sent our message.
|
|
71
|
+
// After SANDBOX_STATUS resolves we wait 100ms for EVENTS_BATCH to drain,
|
|
72
|
+
// then flip this flag and send. Events arriving before the flip are replay.
|
|
73
|
+
let messageSent = false;
|
|
74
|
+
// Promise that resolves when sandbox is ready.
|
|
75
|
+
// SANDBOX_STATUS is sent live by the server on connect (not from replay).
|
|
76
|
+
const sandboxReady = new Promise((resolve, reject) => {
|
|
77
|
+
const timeout = setTimeout(() => {
|
|
78
|
+
reject(new Error('Sandbox boot timed out'));
|
|
79
|
+
}, SANDBOX_TIMEOUT_MS);
|
|
80
|
+
client.on('SANDBOX_STATUS', (event) => {
|
|
81
|
+
if (event.status === 'ready') {
|
|
82
|
+
clearTimeout(timeout);
|
|
83
|
+
resolve();
|
|
84
|
+
}
|
|
85
|
+
else if (event.status === 'error') {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
reject(new Error(event.error || 'Sandbox boot failed'));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
// Wait for EVENTS_BATCH replay to finish (or timeout if no replay).
|
|
92
|
+
// The vanilla client logs "Replaying N events from batch" but doesn't
|
|
93
|
+
// emit EVENTS_BATCH to handlers — it recurses handleMessage for each
|
|
94
|
+
// sub-event. So we detect replay completion by waiting a tick after
|
|
95
|
+
// SANDBOX_STATUS resolves (EVENTS_BATCH arrives on the next frame).
|
|
96
|
+
const replayDone = new Promise((resolve) => {
|
|
97
|
+
sandboxReady.then(() => {
|
|
98
|
+
// Give one event-loop tick for EVENTS_BATCH frame to arrive and process.
|
|
99
|
+
// If there's no replay, this just adds ~50ms latency.
|
|
100
|
+
setTimeout(resolve, 100);
|
|
101
|
+
}).catch(() => resolve());
|
|
102
|
+
});
|
|
103
|
+
// Promise that resolves when agent is done
|
|
104
|
+
let resolveAgent;
|
|
105
|
+
let rejectAgent;
|
|
106
|
+
const agentDone = new Promise((resolve, reject) => {
|
|
107
|
+
resolveAgent = resolve;
|
|
108
|
+
rejectAgent = reject;
|
|
109
|
+
});
|
|
110
|
+
// 3. Register event handlers
|
|
111
|
+
const sandboxSpinner = createSpinner('Booting sandbox...');
|
|
112
|
+
client.on('READY', (event) => {
|
|
113
|
+
if (event.sandboxId) {
|
|
114
|
+
// Sandbox already running — server sends SANDBOX_STATUS ready separately
|
|
35
115
|
}
|
|
36
116
|
else {
|
|
37
|
-
|
|
117
|
+
sandboxSpinner.start();
|
|
118
|
+
client.send({
|
|
119
|
+
type: 'START_SANDBOX',
|
|
120
|
+
template: SANDBOX_TEMPLATE,
|
|
121
|
+
bootstrap: { archiveUrl },
|
|
122
|
+
});
|
|
38
123
|
}
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
// Interactive mode
|
|
42
|
-
await interactiveChat(documentUuid, options.system);
|
|
43
|
-
});
|
|
44
|
-
return chat;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Send a single message to the chat API
|
|
48
|
-
*/
|
|
49
|
-
async function sendMessage(documentUuid, message, systemPrompt) {
|
|
50
|
-
const spinner = createSpinner('Thinking...');
|
|
51
|
-
spinner.start();
|
|
52
|
-
try {
|
|
53
|
-
const response = await post('api/v1/messages', {
|
|
54
|
-
document_uuid: documentUuid,
|
|
55
|
-
message,
|
|
56
|
-
system_prompt: systemPrompt,
|
|
57
124
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
client.on('LIFECYCLE', (event) => {
|
|
126
|
+
if (sandboxSpinner.isSpinning) {
|
|
127
|
+
sandboxSpinner.text = event.message || event.phase;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// Agent events — gated on messageSent to skip replay
|
|
131
|
+
client.on('AGENT_STARTED', (_event) => {
|
|
132
|
+
if (!messageSent)
|
|
133
|
+
return;
|
|
134
|
+
renderer.onAgentStarted();
|
|
135
|
+
});
|
|
136
|
+
client.on('AGENT_MESSAGE', (event) => {
|
|
137
|
+
if (!messageSent)
|
|
138
|
+
return;
|
|
139
|
+
// Runner sends subtype:"result" with is_error:true on SDK failure
|
|
140
|
+
if (event.subtype === 'result' && event.is_error) {
|
|
141
|
+
const errMsg = event.result || 'Agent failed (no details)';
|
|
142
|
+
renderer.onAgentError(String(errMsg));
|
|
143
|
+
rejectAgent(new Error(String(errMsg)));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
renderer.onAgentMessage(event.message?.content);
|
|
147
|
+
});
|
|
148
|
+
client.on('AGENT_DONE', (event) => {
|
|
149
|
+
if (!messageSent)
|
|
150
|
+
return;
|
|
151
|
+
renderer.onAgentDone(event.total_cost_usd, event.duration_ms);
|
|
152
|
+
resolveAgent();
|
|
153
|
+
});
|
|
154
|
+
client.on('AGENT_ERROR', (event) => {
|
|
155
|
+
if (!messageSent)
|
|
156
|
+
return;
|
|
157
|
+
renderer.onAgentError(event.error);
|
|
158
|
+
rejectAgent(new Error(event.error));
|
|
159
|
+
});
|
|
160
|
+
client.on('ERROR', (event) => {
|
|
161
|
+
error(`Server error: ${event.msg}`);
|
|
162
|
+
rejectAgent(new Error(event.msg));
|
|
163
|
+
});
|
|
164
|
+
// 4. Connect
|
|
165
|
+
client.connect();
|
|
166
|
+
// 5. Wait for sandbox
|
|
167
|
+
try {
|
|
168
|
+
await sandboxReady;
|
|
169
|
+
if (sandboxSpinner.isSpinning) {
|
|
170
|
+
sandboxSpinner.succeed('Sandbox ready');
|
|
171
|
+
}
|
|
87
172
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
173
|
+
catch (err) {
|
|
174
|
+
sandboxSpinner.fail('Sandbox boot failed');
|
|
175
|
+
error(err instanceof Error ? err.message : String(err));
|
|
176
|
+
client.disconnect();
|
|
177
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
92
178
|
}
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
179
|
+
// 6. Wait for replay to drain before sending
|
|
180
|
+
await replayDone;
|
|
181
|
+
// 7. Send message — flip gate so subsequent events are rendered
|
|
182
|
+
messageSent = true;
|
|
183
|
+
info(`Sending: ${options.message}`);
|
|
184
|
+
// Send raw AGENT_MESSAGE so author metadata is preserved across
|
|
185
|
+
// older @steventsao/agent-session client versions used by the CLI.
|
|
186
|
+
client.send({
|
|
187
|
+
type: 'AGENT_MESSAGE',
|
|
188
|
+
content: options.message,
|
|
189
|
+
model: { id: 'claude-sonnet-4-20250514', provider: 'anthropic' },
|
|
190
|
+
systemPrompt: CLI_SYSTEM_PROMPT,
|
|
191
|
+
agentType: 'claude-code',
|
|
192
|
+
author: { name: 'okra-cli', clientType: 'cli' },
|
|
193
|
+
});
|
|
194
|
+
// 8. Wait for agent completion
|
|
100
195
|
try {
|
|
101
|
-
|
|
102
|
-
// Add assistant message to history
|
|
103
|
-
const assistantMessage = {
|
|
104
|
-
role: 'assistant',
|
|
105
|
-
content: response.message.content,
|
|
106
|
-
timestamp: new Date().toISOString(),
|
|
107
|
-
};
|
|
108
|
-
history.push(assistantMessage);
|
|
109
|
-
console.log();
|
|
110
|
-
console.log(chalk.green('Assistant:'), response.message.content);
|
|
111
|
-
// Show output files if any
|
|
112
|
-
if (response.output_files && response.output_files.length > 0) {
|
|
113
|
-
console.log();
|
|
114
|
-
console.log(chalk.dim('Generated files:'));
|
|
115
|
-
for (const file of response.output_files) {
|
|
116
|
-
console.log(chalk.dim(` - ${file.filename}`));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
console.log();
|
|
196
|
+
await agentDone;
|
|
120
197
|
}
|
|
121
198
|
catch (err) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
console.log(chalk.red('Error:'), 'Failed to get response');
|
|
127
|
-
}
|
|
128
|
-
console.log();
|
|
199
|
+
error(err instanceof Error ? err.message : String(err));
|
|
200
|
+
process.exitCode = EXIT_CODES.GENERAL_ERROR;
|
|
129
201
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
console.log();
|
|
134
|
-
console.log(chalk.dim('Chat session ended'));
|
|
135
|
-
process.exit(0);
|
|
202
|
+
// 9. Disconnect and exit
|
|
203
|
+
client.disconnect();
|
|
204
|
+
process.exit(process.exitCode ?? 0);
|
|
136
205
|
});
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Handle chat commands
|
|
140
|
-
*/
|
|
141
|
-
async function handleCommand(input, history, documentUuid, rl) {
|
|
142
|
-
const [command, ...args] = input.slice(1).split(' ');
|
|
143
|
-
switch (command.toLowerCase()) {
|
|
144
|
-
case 'quit':
|
|
145
|
-
case 'exit':
|
|
146
|
-
case 'q':
|
|
147
|
-
rl.close();
|
|
148
|
-
break;
|
|
149
|
-
case 'clear':
|
|
150
|
-
history.length = 0;
|
|
151
|
-
console.log(chalk.dim('Chat history cleared'));
|
|
152
|
-
console.log();
|
|
153
|
-
rl.prompt();
|
|
154
|
-
break;
|
|
155
|
-
case 'history':
|
|
156
|
-
if (history.length === 0) {
|
|
157
|
-
console.log(chalk.dim('No chat history'));
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
console.log();
|
|
161
|
-
console.log(chalk.bold('Chat History'));
|
|
162
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
163
|
-
for (const msg of history) {
|
|
164
|
-
const role = msg.role === 'user' ? chalk.cyan('You') : chalk.green('Assistant');
|
|
165
|
-
console.log(`${role}: ${msg.content.slice(0, 100)}${msg.content.length > 100 ? '...' : ''}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
console.log();
|
|
169
|
-
rl.prompt();
|
|
170
|
-
break;
|
|
171
|
-
case 'export':
|
|
172
|
-
const format = args[0] || 'json';
|
|
173
|
-
const filename = `chat-${documentUuid.slice(0, 8)}-${Date.now()}.${format}`;
|
|
174
|
-
try {
|
|
175
|
-
const { writeFileSync } = await import('fs');
|
|
176
|
-
if (format === 'json') {
|
|
177
|
-
writeFileSync(filename, JSON.stringify(history, null, 2));
|
|
178
|
-
}
|
|
179
|
-
else if (format === 'md' || format === 'markdown') {
|
|
180
|
-
const md = history.map(msg => {
|
|
181
|
-
const role = msg.role === 'user' ? '**You**' : '**Assistant**';
|
|
182
|
-
return `${role}: ${msg.content}`;
|
|
183
|
-
}).join('\n\n');
|
|
184
|
-
writeFileSync(filename.replace(/\.\w+$/, '.md'), md);
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
console.log(chalk.red('Unknown format. Use: json, md'));
|
|
188
|
-
rl.prompt();
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
console.log(chalk.green('✓'), `Exported to: ${filename}`);
|
|
192
|
-
}
|
|
193
|
-
catch (err) {
|
|
194
|
-
console.log(chalk.red('Failed to export:'), err instanceof Error ? err.message : 'Unknown error');
|
|
195
|
-
}
|
|
196
|
-
console.log();
|
|
197
|
-
rl.prompt();
|
|
198
|
-
break;
|
|
199
|
-
case 'help':
|
|
200
|
-
console.log();
|
|
201
|
-
console.log(chalk.bold('Available Commands'));
|
|
202
|
-
console.log(chalk.dim('─'.repeat(30)));
|
|
203
|
-
console.log('/quit, /exit, /q - Exit chat');
|
|
204
|
-
console.log('/clear - Clear chat history');
|
|
205
|
-
console.log('/history - Show chat history');
|
|
206
|
-
console.log('/export [format] - Export history (json, md)');
|
|
207
|
-
console.log('/help - Show this help');
|
|
208
|
-
console.log();
|
|
209
|
-
rl.prompt();
|
|
210
|
-
break;
|
|
211
|
-
default:
|
|
212
|
-
console.log(chalk.yellow('Unknown command:'), command);
|
|
213
|
-
console.log(chalk.dim('Use /help for available commands'));
|
|
214
|
-
console.log();
|
|
215
|
-
rl.prompt();
|
|
216
|
-
}
|
|
206
|
+
return chat;
|
|
217
207
|
}
|
|
218
208
|
//# sourceMappingURL=chat.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for chat command replay handling
|
|
3
|
+
*
|
|
4
|
+
* Both CLI and web UI are clients to the same CF Durable Object.
|
|
5
|
+
* On reconnect the DO replays EVENTS_BATCH with old events.
|
|
6
|
+
*
|
|
7
|
+
* Server message order on reconnect:
|
|
8
|
+
* Frame 1: CONNECTED
|
|
9
|
+
* Frame 2: READY (with sandboxId if running)
|
|
10
|
+
* Frame 3: SANDBOX_STATUS ready (live, not replayed)
|
|
11
|
+
* Frame 4: EVENTS_BATCH (replayed history — arrives AFTER SANDBOX_STATUS)
|
|
12
|
+
*
|
|
13
|
+
* The CLI adds a 100ms drain after SANDBOX_STATUS resolves to let
|
|
14
|
+
* EVENTS_BATCH arrive and process before flipping messageSent = true.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=chat.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.test.d.ts","sourceRoot":"","sources":["../../src/commands/chat.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|