@wordinweb/agent 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/README.md +296 -0
- package/dist/chunk-E7AFDS2O.js +34056 -0
- package/dist/chunk-E7AFDS2O.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +555 -0
- package/dist/cli.js.map +1 -0
- package/dist/dist-4GR64XSW.js +4326 -0
- package/dist/dist-4GR64XSW.js.map +1 -0
- package/dist/index.d.ts +3092 -0
- package/dist/index.js +124 -0
- package/dist/index.js.map +1 -0
- package/dist/wmf-KUCU4S75.js +193 -0
- package/dist/wmf-KUCU4S75.js.map +1 -0
- package/package.json +50 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type WakeTarget = {
|
|
2
|
+
provider: "codex" | "claude";
|
|
3
|
+
sessionId: string;
|
|
4
|
+
};
|
|
5
|
+
declare function detectWakeTarget(env?: NodeJS.ProcessEnv): WakeTarget | null;
|
|
6
|
+
declare function agentWakePrompt(agentSessionId: string, text: string): string;
|
|
7
|
+
type Emit = (value: unknown) => void;
|
|
8
|
+
declare function connectAgent(invitationUrl: string, inputStream?: NodeJS.ReadableStream, emit?: Emit): Promise<void>;
|
|
9
|
+
|
|
10
|
+
export { agentWakePrompt, connectAgent, detectWakeTarget };
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AgentDocument,
|
|
4
|
+
CollabConnection,
|
|
5
|
+
EncryptedCollabConnection,
|
|
6
|
+
createWebSocketTransport,
|
|
7
|
+
stretchShareCode
|
|
8
|
+
} from "./chunk-E7AFDS2O.js";
|
|
9
|
+
|
|
10
|
+
// src/cli.ts
|
|
11
|
+
import { webcrypto } from "crypto";
|
|
12
|
+
import { spawn } from "child_process";
|
|
13
|
+
import { chmodSync, realpathSync, rmSync } from "fs";
|
|
14
|
+
import { createConnection, createServer } from "net";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
17
|
+
import { createInterface } from "readline";
|
|
18
|
+
import { PassThrough } from "stream";
|
|
19
|
+
import WebSocket from "ws";
|
|
20
|
+
if (!globalThis.crypto) Object.defineProperty(globalThis, "crypto", { value: webcrypto });
|
|
21
|
+
var AGENT_MODE_CONTROL_PREFIX = "\0wordinweb-agent-mode:";
|
|
22
|
+
var AGENT_PACKAGE_URL = "https://collab.word-in-web.com/wordinweb-agent.tgz?v=short-invite-3";
|
|
23
|
+
function base64UrlToBytes(value) {
|
|
24
|
+
const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
25
|
+
return Uint8Array.from(Buffer.from(padded, "base64"));
|
|
26
|
+
}
|
|
27
|
+
function bytesToBase64Url(value) {
|
|
28
|
+
return Buffer.from(value).toString("base64url");
|
|
29
|
+
}
|
|
30
|
+
async function parseInvite(url) {
|
|
31
|
+
const parsed = new URL(url);
|
|
32
|
+
const encoded = parsed.hash.match(/(?:^#|&)invite=([A-Za-z0-9_-]+)/)?.[1];
|
|
33
|
+
let payload;
|
|
34
|
+
if (encoded) {
|
|
35
|
+
payload = JSON.parse(new TextDecoder().decode(base64UrlToBytes(encoded)));
|
|
36
|
+
} else {
|
|
37
|
+
const values = new URLSearchParams(parsed.hash.slice(1));
|
|
38
|
+
const inviteId = values.get("i");
|
|
39
|
+
const secret = values.get("k");
|
|
40
|
+
if (!inviteId || !/^[A-Za-z0-9_-]{16,64}$/.test(inviteId) || !secret) throw new Error("The URL has no AI invitation payload");
|
|
41
|
+
const response = await fetch(new URL(`/agent-invites/${inviteId}`, parsed.origin));
|
|
42
|
+
if (!response.ok) throw new Error(response.status === 404 ? "The AI invitation is unavailable or expired" : `The AI invitation could not be loaded (${response.status})`);
|
|
43
|
+
const stored = await response.json();
|
|
44
|
+
if (stored.expiresAt <= Date.now()) throw new Error("The AI invitation has expired");
|
|
45
|
+
const key = await crypto.subtle.importKey("raw", base64UrlToBytes(secret), "AES-GCM", false, ["decrypt"]);
|
|
46
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
47
|
+
{
|
|
48
|
+
name: "AES-GCM",
|
|
49
|
+
iv: base64UrlToBytes(stored.iv),
|
|
50
|
+
additionalData: new TextEncoder().encode(`wordinweb-agent-invite:${inviteId}`)
|
|
51
|
+
},
|
|
52
|
+
key,
|
|
53
|
+
base64UrlToBytes(stored.ciphertext)
|
|
54
|
+
);
|
|
55
|
+
payload = JSON.parse(new TextDecoder().decode(plaintext));
|
|
56
|
+
}
|
|
57
|
+
if (payload.version !== 1) throw new Error("The AI invitation version is unsupported");
|
|
58
|
+
if (payload.invite.expiresAt <= Date.now()) throw new Error("The AI invitation has expired");
|
|
59
|
+
return payload;
|
|
60
|
+
}
|
|
61
|
+
function parseAgentModeControl(text) {
|
|
62
|
+
if (!text.startsWith(AGENT_MODE_CONTROL_PREFIX)) return null;
|
|
63
|
+
const mode = text.slice(AGENT_MODE_CONTROL_PREFIX.length);
|
|
64
|
+
return mode === "suggest" || mode === "edit" ? mode : null;
|
|
65
|
+
}
|
|
66
|
+
function editRequestForMode(request, mode) {
|
|
67
|
+
if (mode === "edit" || !request) return request;
|
|
68
|
+
if (!Array.isArray(request.operations)) return request;
|
|
69
|
+
const operations = request.operations.map((value) => {
|
|
70
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Each edit operation must be an object");
|
|
71
|
+
const operation = value;
|
|
72
|
+
switch (operation.kind) {
|
|
73
|
+
case "insertText":
|
|
74
|
+
case "splitParagraph":
|
|
75
|
+
return { ...operation, suggest: true };
|
|
76
|
+
case "deleteText":
|
|
77
|
+
return {
|
|
78
|
+
kind: "suggestRevision",
|
|
79
|
+
ranges: [{ blockRef: operation.blockRef, runRef: operation.runRef, start: operation.start, end: operation.end }]
|
|
80
|
+
};
|
|
81
|
+
case "mergeParagraph":
|
|
82
|
+
return { kind: "suggestRevision", marks: [{ blockRef: operation.blockRef, glyph: "del" }] };
|
|
83
|
+
case "suggestRevision":
|
|
84
|
+
case "commentRun":
|
|
85
|
+
case "replyComment":
|
|
86
|
+
return operation;
|
|
87
|
+
default:
|
|
88
|
+
throw new Error(`${String(operation.kind)} is unavailable in suggestion mode. Ask the inviter to switch this agent to editing mode`);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return { ...request, operations };
|
|
92
|
+
}
|
|
93
|
+
function randomId(prefix) {
|
|
94
|
+
return `${prefix}_${bytesToBase64Url(crypto.getRandomValues(new Uint8Array(16)))}`;
|
|
95
|
+
}
|
|
96
|
+
function detectWakeTarget(env = process.env) {
|
|
97
|
+
const codexSessionId = env.CODEX_THREAD_ID?.trim();
|
|
98
|
+
if (codexSessionId && /^[A-Za-z0-9_-]{8,128}$/.test(codexSessionId)) {
|
|
99
|
+
return { provider: "codex", sessionId: codexSessionId };
|
|
100
|
+
}
|
|
101
|
+
const claudeSessionId = env.CLAUDE_CODE_SESSION_ID?.trim();
|
|
102
|
+
if (claudeSessionId && /^[A-Za-z0-9_-]{8,128}$/.test(claudeSessionId)) {
|
|
103
|
+
return { provider: "claude", sessionId: claudeSessionId };
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function agentWakePrompt(agentSessionId, text) {
|
|
108
|
+
return [
|
|
109
|
+
"An authenticated private WordInWeb message resumed this agent task.",
|
|
110
|
+
`The inviter's message is ${JSON.stringify(text)}.`,
|
|
111
|
+
`The detached document bridge session is ${agentSessionId}. Do not run the connect command again.`,
|
|
112
|
+
`Run document commands with: npx -y --package='${AGENT_PACKAGE_URL}' wordinweb-agent session '${agentSessionId}' '<JSON command>'`,
|
|
113
|
+
'Start with sync. For a broad text task, inspect all non-empty stories in one compact call: {"command":"inspect","request":{"kind":"context"}}. Use overview, read, object, or spatial only when the task needs their extra detail.',
|
|
114
|
+
"Inspect the relevant content before every edit. If an edit returns needs_sync, sync and inspect again.",
|
|
115
|
+
"Complete this task, then send the inviter a concise result with the chat command.",
|
|
116
|
+
"End this agent turn after the reply. Do not call wait. The detached bridge will resume this task for the next message."
|
|
117
|
+
].join("\n\n");
|
|
118
|
+
}
|
|
119
|
+
function write(value) {
|
|
120
|
+
process.stdout.write(`${JSON.stringify(value)}
|
|
121
|
+
`);
|
|
122
|
+
}
|
|
123
|
+
function sessionSocketPath(sessionId) {
|
|
124
|
+
if (!/^session_[A-Za-z0-9_-]{16,128}$/.test(sessionId)) throw new Error("The agent session ID is invalid");
|
|
125
|
+
return process.platform === "win32" ? `\\\\.\\pipe\\wordinweb-agent-${sessionId}` : join("/tmp", `wordinweb-agent-${sessionId}.sock`);
|
|
126
|
+
}
|
|
127
|
+
async function importChatKey(value) {
|
|
128
|
+
return crypto.subtle.importKey("raw", base64UrlToBytes(value), "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
129
|
+
}
|
|
130
|
+
function chatAad(agentClientId, messageId) {
|
|
131
|
+
return new TextEncoder().encode(`wordinweb-agent-chat:${agentClientId}:${messageId}`);
|
|
132
|
+
}
|
|
133
|
+
async function encryptChat(key, agentClientId, messageId, text) {
|
|
134
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
135
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
136
|
+
{ name: "AES-GCM", iv, additionalData: chatAad(agentClientId, messageId) },
|
|
137
|
+
key,
|
|
138
|
+
new TextEncoder().encode(text)
|
|
139
|
+
);
|
|
140
|
+
return { iv: bytesToBase64Url(iv), ciphertext: bytesToBase64Url(new Uint8Array(ciphertext)) };
|
|
141
|
+
}
|
|
142
|
+
async function decryptChat(key, agentClientId, messageId, iv, ciphertext) {
|
|
143
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
144
|
+
{ name: "AES-GCM", iv: base64UrlToBytes(iv), additionalData: chatAad(agentClientId, messageId) },
|
|
145
|
+
key,
|
|
146
|
+
base64UrlToBytes(ciphertext)
|
|
147
|
+
);
|
|
148
|
+
return new TextDecoder().decode(plaintext);
|
|
149
|
+
}
|
|
150
|
+
async function connectAgent(invitationUrl, inputStream = process.stdin, emit = write) {
|
|
151
|
+
const payload = await parseInvite(invitationUrl);
|
|
152
|
+
let mode = payload.agent.mode === "edit" ? "edit" : "suggest";
|
|
153
|
+
const clientId = randomId("agent");
|
|
154
|
+
const socket = new WebSocket(payload.room.wsUrl);
|
|
155
|
+
const transport = createWebSocketTransport(socket);
|
|
156
|
+
const chatKey = await importChatKey(payload.invite.chatKey);
|
|
157
|
+
let connection;
|
|
158
|
+
let revision = 0;
|
|
159
|
+
let ready = false;
|
|
160
|
+
const connectionState = { current: "reconnecting" };
|
|
161
|
+
let roster = [];
|
|
162
|
+
let lastActivityIndex = 0;
|
|
163
|
+
const events = [];
|
|
164
|
+
const waiters = [];
|
|
165
|
+
const publish = (event) => {
|
|
166
|
+
const waiter = waiters.shift();
|
|
167
|
+
if (waiter) waiter(event);
|
|
168
|
+
else events.push(event);
|
|
169
|
+
emit(event);
|
|
170
|
+
};
|
|
171
|
+
const heartbeat = setInterval(() => {
|
|
172
|
+
if (socket.readyState === WebSocket.OPEN) socket.ping();
|
|
173
|
+
}, 25e3);
|
|
174
|
+
socket.addEventListener("close", (event) => {
|
|
175
|
+
connectionState.current = "offline";
|
|
176
|
+
clearInterval(heartbeat);
|
|
177
|
+
publish({ event: "connection_closed", code: event.code, reason: event.reason || void 0 });
|
|
178
|
+
});
|
|
179
|
+
let readyResolve = null;
|
|
180
|
+
let readyReject = null;
|
|
181
|
+
const readyPromise = new Promise((resolve, reject) => {
|
|
182
|
+
readyResolve = resolve;
|
|
183
|
+
readyReject = reject;
|
|
184
|
+
});
|
|
185
|
+
const callbacks = {
|
|
186
|
+
onChange: () => {
|
|
187
|
+
revision++;
|
|
188
|
+
if (connection?.ready && !ready) {
|
|
189
|
+
ready = true;
|
|
190
|
+
connectionState.current = "live";
|
|
191
|
+
readyResolve?.();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (ready) publish({ event: "document_changed", revision: String(revision) });
|
|
195
|
+
},
|
|
196
|
+
onRoster: (next) => {
|
|
197
|
+
roster = next;
|
|
198
|
+
if (ready) publish({ event: "roster", roster });
|
|
199
|
+
},
|
|
200
|
+
onRefused: (reason) => {
|
|
201
|
+
const error = new Error(`Room connection refused: ${reason}`);
|
|
202
|
+
readyReject?.(error);
|
|
203
|
+
if (ready) publish({ event: "refused", reason });
|
|
204
|
+
},
|
|
205
|
+
onAgentChat: (message) => {
|
|
206
|
+
if (message.agentClientId !== clientId || message.sender !== "inviter") return;
|
|
207
|
+
void decryptChat(chatKey, clientId, message.messageId, message.iv, message.ciphertext).then(
|
|
208
|
+
(text) => {
|
|
209
|
+
const nextMode = parseAgentModeControl(text);
|
|
210
|
+
if (nextMode) {
|
|
211
|
+
mode = nextMode;
|
|
212
|
+
publish({ event: "mode_changed", mode });
|
|
213
|
+
} else publish({ event: "chat", messageId: message.messageId, text });
|
|
214
|
+
},
|
|
215
|
+
() => publish({ event: "chat_error", messageId: message.messageId })
|
|
216
|
+
);
|
|
217
|
+
},
|
|
218
|
+
onError: ({ where, error }) => publish({ event: "connection_error", where, message: error instanceof Error ? error.message : String(error) })
|
|
219
|
+
};
|
|
220
|
+
const stretched = payload.room.docKey && payload.room.shareCode ? await stretchShareCode(payload.room.shareCode, payload.room.docId) : void 0;
|
|
221
|
+
const codeProof = stretched ? Buffer.from(stretched).toString("base64") : void 0;
|
|
222
|
+
connection = payload.room.docKey ? new EncryptedCollabConnection(
|
|
223
|
+
transport,
|
|
224
|
+
clientId,
|
|
225
|
+
payload.room.docKey,
|
|
226
|
+
callbacks,
|
|
227
|
+
stretched,
|
|
228
|
+
void 0,
|
|
229
|
+
payload.room.httpBase ? { httpBase: payload.room.httpBase } : void 0
|
|
230
|
+
) : new CollabConnection(
|
|
231
|
+
transport,
|
|
232
|
+
clientId,
|
|
233
|
+
callbacks,
|
|
234
|
+
payload.room.httpBase ? { httpBase: payload.room.httpBase } : void 0
|
|
235
|
+
);
|
|
236
|
+
const profile = { name: payload.agent.name || "AI Agent", color: "" };
|
|
237
|
+
connection.join(payload.room.docId, void 0, {
|
|
238
|
+
profile,
|
|
239
|
+
codeProof,
|
|
240
|
+
agentInvite: { inviteId: payload.invite.inviteId, token: payload.invite.token }
|
|
241
|
+
});
|
|
242
|
+
const timeout = setTimeout(() => readyReject?.(new Error("The document did not become ready within 30 seconds")), 3e4);
|
|
243
|
+
await readyPromise.finally(() => clearTimeout(timeout));
|
|
244
|
+
const document = AgentDocument.connect({
|
|
245
|
+
getDocument: () => connection.doc,
|
|
246
|
+
getRevision: () => revision,
|
|
247
|
+
allocateIds: (count) => connection.allocIds(count),
|
|
248
|
+
submit: (operation) => connection.submit(operation),
|
|
249
|
+
setPresence: (position) => {
|
|
250
|
+
const deadline = Date.now() + 3e4;
|
|
251
|
+
const send = () => {
|
|
252
|
+
if (connection.pendingCount === 0) connection.setPresence(position);
|
|
253
|
+
else if (connectionState.current === "live" && Date.now() < deadline) setTimeout(send, 25);
|
|
254
|
+
};
|
|
255
|
+
send();
|
|
256
|
+
},
|
|
257
|
+
uploadMedia: (bytes) => connection.uploadMedia(bytes),
|
|
258
|
+
getConnectionState: () => connectionState.current
|
|
259
|
+
}, { provenance: { author: profile.name } });
|
|
260
|
+
emit({
|
|
261
|
+
event: "ready",
|
|
262
|
+
clientId,
|
|
263
|
+
revision: document.revision,
|
|
264
|
+
mode,
|
|
265
|
+
instructions: payload.agent.instructions,
|
|
266
|
+
commands: ["sync", "capabilities", "inspect", "edit", "chat", "wait", "close"]
|
|
267
|
+
});
|
|
268
|
+
const nextEvent = (timeoutMs) => {
|
|
269
|
+
if (events.length) return Promise.resolve(events.shift());
|
|
270
|
+
return new Promise((resolve) => {
|
|
271
|
+
const timer = setTimeout(() => {
|
|
272
|
+
const index = waiters.indexOf(done);
|
|
273
|
+
if (index >= 0) waiters.splice(index, 1);
|
|
274
|
+
resolve({ event: "wait_timeout" });
|
|
275
|
+
}, timeoutMs);
|
|
276
|
+
const done = (event) => {
|
|
277
|
+
clearTimeout(timer);
|
|
278
|
+
resolve(event);
|
|
279
|
+
};
|
|
280
|
+
waiters.push(done);
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
const respond = (id, result, error) => {
|
|
284
|
+
emit(error === void 0 ? { id, ok: true, result } : { id, ok: false, error: String(error) });
|
|
285
|
+
};
|
|
286
|
+
const input = createInterface({ input: inputStream, terminal: false });
|
|
287
|
+
for await (const line of input) {
|
|
288
|
+
if (!line.trim()) continue;
|
|
289
|
+
let command;
|
|
290
|
+
try {
|
|
291
|
+
command = JSON.parse(line);
|
|
292
|
+
} catch {
|
|
293
|
+
respond(void 0, void 0, "Each input line must be valid JSON");
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
switch (command.command) {
|
|
298
|
+
case "sync": {
|
|
299
|
+
const activity = connection.activity.slice(lastActivityIndex);
|
|
300
|
+
lastActivityIndex = connection.activity.length;
|
|
301
|
+
respond(command.id, { revision: document.revision, activity, roster, mode });
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case "capabilities":
|
|
305
|
+
respond(command.id, document.capabilities(command.category, command.kind));
|
|
306
|
+
break;
|
|
307
|
+
case "inspect":
|
|
308
|
+
respond(command.id, document.inspect(command.request));
|
|
309
|
+
break;
|
|
310
|
+
case "edit":
|
|
311
|
+
try {
|
|
312
|
+
if (connectionState.current !== "live") throw new Error("The collaboration connection is offline. Reconnect before editing");
|
|
313
|
+
respond(command.id, await document.edit(editRequestForMode(command.request, mode)));
|
|
314
|
+
} catch (error) {
|
|
315
|
+
if (error instanceof Error && error.message.includes("revision is stale")) {
|
|
316
|
+
respond(command.id, { status: "needs_sync", latestRevision: document.revision });
|
|
317
|
+
} else throw error;
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
case "chat": {
|
|
321
|
+
if (connectionState.current !== "live") throw new Error("The collaboration connection is offline. Reconnect before sending chat");
|
|
322
|
+
if (!command.text?.trim()) throw new Error("Chat text is required");
|
|
323
|
+
const messageId = randomId("message");
|
|
324
|
+
const sealed = await encryptChat(chatKey, clientId, messageId, command.text.trim());
|
|
325
|
+
connection.sendAgentChat(clientId, messageId, sealed.iv, sealed.ciphertext);
|
|
326
|
+
respond(command.id, { messageId });
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
case "wait":
|
|
330
|
+
respond(command.id, await nextEvent(Math.min(Math.max(command.timeoutMs ?? 3e4, 1), 55e3)));
|
|
331
|
+
break;
|
|
332
|
+
case "close":
|
|
333
|
+
respond(command.id, { closed: true });
|
|
334
|
+
clearInterval(heartbeat);
|
|
335
|
+
socket.close();
|
|
336
|
+
input.close();
|
|
337
|
+
return;
|
|
338
|
+
default:
|
|
339
|
+
throw new Error("Unknown command");
|
|
340
|
+
}
|
|
341
|
+
} catch (error) {
|
|
342
|
+
respond(command.id, void 0, error instanceof Error ? error.message : String(error));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
clearInterval(heartbeat);
|
|
346
|
+
socket.close();
|
|
347
|
+
}
|
|
348
|
+
function sendSocketValue(socket, value) {
|
|
349
|
+
socket.end(`${JSON.stringify(value)}
|
|
350
|
+
`);
|
|
351
|
+
}
|
|
352
|
+
async function listen(server, path) {
|
|
353
|
+
if (process.platform !== "win32") rmSync(path, { force: true });
|
|
354
|
+
await new Promise((resolve, reject) => {
|
|
355
|
+
server.once("error", reject);
|
|
356
|
+
server.listen(path, () => {
|
|
357
|
+
server.off("error", reject);
|
|
358
|
+
if (process.platform !== "win32") chmodSync(path, 384);
|
|
359
|
+
resolve();
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
async function runDaemon(invitationUrl, sessionId) {
|
|
364
|
+
const path = sessionSocketPath(sessionId);
|
|
365
|
+
const input = new PassThrough();
|
|
366
|
+
const wakeTarget = detectWakeTarget();
|
|
367
|
+
const wakeQueue = [];
|
|
368
|
+
const pending = /* @__PURE__ */ new Map();
|
|
369
|
+
let readyEvent = null;
|
|
370
|
+
let fatalError = null;
|
|
371
|
+
let closeTimer = null;
|
|
372
|
+
let wakeChild = null;
|
|
373
|
+
let requestCounter = 0;
|
|
374
|
+
let stopped = false;
|
|
375
|
+
const sendWakeFailure = () => {
|
|
376
|
+
input.write(`${JSON.stringify({ command: "chat", text: "I could not resume the agent task. Reopen the original Codex or Claude task and reconnect this AI." })}
|
|
377
|
+
`);
|
|
378
|
+
};
|
|
379
|
+
const startNextWake = () => {
|
|
380
|
+
if (!wakeTarget || wakeChild || wakeQueue.length === 0 || stopped) return;
|
|
381
|
+
const prompt = agentWakePrompt(sessionId, wakeQueue.shift());
|
|
382
|
+
const childEnv = { ...process.env };
|
|
383
|
+
delete childEnv.CODEX_THREAD_ID;
|
|
384
|
+
delete childEnv.CLAUDE_CODE_SESSION_ID;
|
|
385
|
+
delete childEnv.CLAUDECODE;
|
|
386
|
+
wakeChild = wakeTarget.provider === "codex" ? spawn("codex", ["exec", "resume", wakeTarget.sessionId, "-"], { env: childEnv, stdio: ["pipe", "ignore", "ignore"] }) : spawn("claude", ["-p", "--resume", wakeTarget.sessionId], { env: childEnv, stdio: ["pipe", "ignore", "ignore"] });
|
|
387
|
+
let finished = false;
|
|
388
|
+
const finish = (ok) => {
|
|
389
|
+
if (finished) return;
|
|
390
|
+
finished = true;
|
|
391
|
+
wakeChild = null;
|
|
392
|
+
if (!ok && !stopped) sendWakeFailure();
|
|
393
|
+
startNextWake();
|
|
394
|
+
};
|
|
395
|
+
wakeChild.once("error", () => finish(false));
|
|
396
|
+
wakeChild.once("exit", (code) => finish(code === 0));
|
|
397
|
+
wakeChild.stdin?.on("error", () => void 0);
|
|
398
|
+
wakeChild.stdin?.end(prompt);
|
|
399
|
+
};
|
|
400
|
+
const shutdown = () => {
|
|
401
|
+
if (stopped) return;
|
|
402
|
+
stopped = true;
|
|
403
|
+
if (closeTimer) clearTimeout(closeTimer);
|
|
404
|
+
wakeChild?.kill();
|
|
405
|
+
input.end();
|
|
406
|
+
server.close();
|
|
407
|
+
if (process.platform !== "win32") rmSync(path, { force: true });
|
|
408
|
+
};
|
|
409
|
+
const emit = (value) => {
|
|
410
|
+
const message = value;
|
|
411
|
+
if (message.event === "ready") readyEvent = message;
|
|
412
|
+
if (message.event === "connection_closed" && !closeTimer) {
|
|
413
|
+
closeTimer = setTimeout(shutdown, 6e4);
|
|
414
|
+
closeTimer.unref();
|
|
415
|
+
}
|
|
416
|
+
if (message.event === "chat" && typeof message.text === "string" && wakeTarget) {
|
|
417
|
+
wakeQueue.push(message.text);
|
|
418
|
+
startNextWake();
|
|
419
|
+
}
|
|
420
|
+
if (typeof message.id !== "string") return;
|
|
421
|
+
const command = pending.get(message.id);
|
|
422
|
+
if (!command) return;
|
|
423
|
+
pending.delete(message.id);
|
|
424
|
+
const response = { ...message };
|
|
425
|
+
if (command.originalId === void 0) delete response.id;
|
|
426
|
+
else response.id = command.originalId;
|
|
427
|
+
sendSocketValue(command.socket, response);
|
|
428
|
+
};
|
|
429
|
+
const server = createServer((socket) => {
|
|
430
|
+
socket.setEncoding("utf8");
|
|
431
|
+
let buffer = "";
|
|
432
|
+
socket.on("data", (chunk) => {
|
|
433
|
+
buffer += chunk;
|
|
434
|
+
const newline = buffer.indexOf("\n");
|
|
435
|
+
if (newline < 0) return;
|
|
436
|
+
socket.removeAllListeners("data");
|
|
437
|
+
try {
|
|
438
|
+
const message = JSON.parse(buffer.slice(0, newline));
|
|
439
|
+
if (message.daemon === "status") {
|
|
440
|
+
if (fatalError) sendSocketValue(socket, { ok: false, error: fatalError });
|
|
441
|
+
else if (readyEvent) sendSocketValue(socket, { ok: true, result: { state: "ready", ready: readyEvent } });
|
|
442
|
+
else sendSocketValue(socket, { ok: true, result: { state: "starting" } });
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (!readyEvent) {
|
|
446
|
+
sendSocketValue(socket, { ok: false, error: fatalError ?? "The agent bridge is still starting" });
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const id = `request_${++requestCounter}`;
|
|
450
|
+
pending.set(id, { socket, originalId: message.id });
|
|
451
|
+
input.write(`${JSON.stringify({ ...message, id })}
|
|
452
|
+
`);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
sendSocketValue(socket, { ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
await listen(server, path);
|
|
459
|
+
try {
|
|
460
|
+
await connectAgent(invitationUrl, input, emit);
|
|
461
|
+
} catch (error) {
|
|
462
|
+
fatalError = error instanceof Error ? error.message : String(error);
|
|
463
|
+
for (const command of pending.values()) sendSocketValue(command.socket, { ok: false, error: fatalError });
|
|
464
|
+
pending.clear();
|
|
465
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
466
|
+
} finally {
|
|
467
|
+
shutdown();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function daemonRequest(sessionId, request, timeoutMs = 6e4) {
|
|
471
|
+
return new Promise((resolve, reject) => {
|
|
472
|
+
const socket = createConnection(sessionSocketPath(sessionId));
|
|
473
|
+
let buffer = "";
|
|
474
|
+
const fail = (error) => {
|
|
475
|
+
socket.destroy();
|
|
476
|
+
reject(error);
|
|
477
|
+
};
|
|
478
|
+
socket.setEncoding("utf8");
|
|
479
|
+
socket.setTimeout(timeoutMs, () => fail(new Error("The agent bridge command timed out")));
|
|
480
|
+
socket.once("error", fail);
|
|
481
|
+
socket.once("connect", () => socket.write(`${JSON.stringify(request)}
|
|
482
|
+
`));
|
|
483
|
+
socket.on("data", (chunk) => {
|
|
484
|
+
buffer += chunk;
|
|
485
|
+
const newline = buffer.indexOf("\n");
|
|
486
|
+
if (newline < 0) return;
|
|
487
|
+
socket.destroy();
|
|
488
|
+
try {
|
|
489
|
+
resolve(JSON.parse(buffer.slice(0, newline)));
|
|
490
|
+
} catch (error) {
|
|
491
|
+
reject(error);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
async function startDaemon(invitationUrl) {
|
|
497
|
+
const sessionId = randomId("session");
|
|
498
|
+
const wakeTarget = detectWakeTarget();
|
|
499
|
+
const child = spawn(process.execPath, [fileURLToPath(import.meta.url), "daemon", invitationUrl, sessionId], {
|
|
500
|
+
detached: true,
|
|
501
|
+
stdio: "ignore"
|
|
502
|
+
});
|
|
503
|
+
child.unref();
|
|
504
|
+
const deadline = Date.now() + 35e3;
|
|
505
|
+
while (Date.now() < deadline) {
|
|
506
|
+
try {
|
|
507
|
+
const status = await daemonRequest(sessionId, { daemon: "status" }, 2e3);
|
|
508
|
+
if (!status.ok) throw new Error(String(status.error));
|
|
509
|
+
const result = status.result;
|
|
510
|
+
if (result?.state === "ready" && result.ready) {
|
|
511
|
+
write({
|
|
512
|
+
...result.ready,
|
|
513
|
+
sessionId,
|
|
514
|
+
mode: "detached",
|
|
515
|
+
wake: wakeTarget ? { state: "armed", provider: wakeTarget.provider } : { state: "unavailable", reason: "This command did not run inside a Codex or Claude task" },
|
|
516
|
+
next: `wordinweb-agent session '${sessionId}' '<JSON command>'`
|
|
517
|
+
});
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
const code = error.code;
|
|
522
|
+
if (code !== "ENOENT" && code !== "ECONNREFUSED") throw error;
|
|
523
|
+
}
|
|
524
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
525
|
+
}
|
|
526
|
+
throw new Error("The detached agent bridge did not become ready within 35 seconds");
|
|
527
|
+
}
|
|
528
|
+
async function main() {
|
|
529
|
+
const [command, first, second] = process.argv.slice(2);
|
|
530
|
+
if (command === "connect" && first) return startDaemon(first);
|
|
531
|
+
if (command === "session" && first && second) {
|
|
532
|
+
const request = JSON.parse(second);
|
|
533
|
+
const timeoutMs = request.command === "wait" ? Math.min(Math.max(request.timeoutMs ?? 3e4, 1), 55e3) + 5e3 : 6e4;
|
|
534
|
+
write(await daemonRequest(first, request, timeoutMs));
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (command === "daemon" && first && second) return runDaemon(first, second);
|
|
538
|
+
process.stderr.write(
|
|
539
|
+
"Usage:\n wordinweb-agent connect '<AI invitation URL>'\n wordinweb-agent session '<session ID>' '<JSON command>'\n"
|
|
540
|
+
);
|
|
541
|
+
process.exitCode = 1;
|
|
542
|
+
}
|
|
543
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === realpathSync(process.argv[1])) {
|
|
544
|
+
void main().catch((error) => {
|
|
545
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
546
|
+
`);
|
|
547
|
+
process.exitCode = 1;
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
export {
|
|
551
|
+
agentWakePrompt,
|
|
552
|
+
connectAgent,
|
|
553
|
+
detectWakeTarget
|
|
554
|
+
};
|
|
555
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { webcrypto } from \"node:crypto\";\nimport { spawn } from \"node:child_process\";\nimport { chmodSync, realpathSync, rmSync } from \"node:fs\";\nimport { createConnection, createServer, type Socket } from \"node:net\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createInterface } from \"node:readline\";\nimport { PassThrough } from \"node:stream\";\nimport WebSocket from \"ws\";\nimport {\n CollabConnection,\n EncryptedCollabConnection,\n createWebSocketTransport,\n stretchShareCode,\n type ConnectionCallbacks,\n type ParticipantProfile,\n type RosterEntry,\n} from \"@wordinweb/collab/client\";\nimport { AgentDocument } from \"./document.js\";\n\nif (!globalThis.crypto) Object.defineProperty(globalThis, \"crypto\", { value: webcrypto });\n\ninterface InvitePayload {\n version: 1;\n room: { wsUrl: string; httpBase: string; docId: string; docKey?: string; shareCode?: string };\n invite: { inviteId: string; token: string; chatKey: string; expiresAt: number };\n agent: { name: string; instructions: string; mode?: AgentMode };\n}\n\ntype AgentMode = \"suggest\" | \"edit\";\n\ntype WakeTarget = { provider: \"codex\" | \"claude\"; sessionId: string };\n\nconst AGENT_MODE_CONTROL_PREFIX = \"\\u0000wordinweb-agent-mode:\";\nconst AGENT_PACKAGE_URL = \"https://collab.word-in-web.com/wordinweb-agent.tgz?v=short-invite-3\";\n\ninterface Command {\n id?: string | number;\n command?: string;\n request?: Record<string, unknown>;\n category?: string;\n kind?: string;\n text?: string;\n timeoutMs?: number;\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const padded = value.replace(/-/g, \"+\").replace(/_/g, \"/\").padEnd(Math.ceil(value.length / 4) * 4, \"=\");\n return Uint8Array.from(Buffer.from(padded, \"base64\"));\n}\n\nfunction bytesToBase64Url(value: Uint8Array): string {\n return Buffer.from(value).toString(\"base64url\");\n}\n\nasync function parseInvite(url: string): Promise<InvitePayload> {\n const parsed = new URL(url);\n const encoded = parsed.hash.match(/(?:^#|&)invite=([A-Za-z0-9_-]+)/)?.[1];\n let payload: InvitePayload;\n if (encoded) {\n payload = JSON.parse(new TextDecoder().decode(base64UrlToBytes(encoded))) as InvitePayload;\n } else {\n const values = new URLSearchParams(parsed.hash.slice(1));\n const inviteId = values.get(\"i\");\n const secret = values.get(\"k\");\n if (!inviteId || !/^[A-Za-z0-9_-]{16,64}$/.test(inviteId) || !secret) throw new Error(\"The URL has no AI invitation payload\");\n const response = await fetch(new URL(`/agent-invites/${inviteId}`, parsed.origin));\n if (!response.ok) throw new Error(response.status === 404 ? \"The AI invitation is unavailable or expired\" : `The AI invitation could not be loaded (${response.status})`);\n const stored = await response.json() as { iv: string; ciphertext: string; expiresAt: number };\n if (stored.expiresAt <= Date.now()) throw new Error(\"The AI invitation has expired\");\n const key = await crypto.subtle.importKey(\"raw\", base64UrlToBytes(secret) as BufferSource, \"AES-GCM\", false, [\"decrypt\"]);\n const plaintext = await crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: base64UrlToBytes(stored.iv) as BufferSource,\n additionalData: new TextEncoder().encode(`wordinweb-agent-invite:${inviteId}`) as BufferSource,\n },\n key,\n base64UrlToBytes(stored.ciphertext) as BufferSource,\n );\n payload = JSON.parse(new TextDecoder().decode(plaintext)) as InvitePayload;\n }\n if (payload.version !== 1) throw new Error(\"The AI invitation version is unsupported\");\n if (payload.invite.expiresAt <= Date.now()) throw new Error(\"The AI invitation has expired\");\n return payload;\n}\n\nfunction parseAgentModeControl(text: string): AgentMode | null {\n if (!text.startsWith(AGENT_MODE_CONTROL_PREFIX)) return null;\n const mode = text.slice(AGENT_MODE_CONTROL_PREFIX.length);\n return mode === \"suggest\" || mode === \"edit\" ? mode : null;\n}\n\nfunction editRequestForMode(request: Record<string, unknown> | undefined, mode: AgentMode): Record<string, unknown> | undefined {\n if (mode === \"edit\" || !request) return request;\n if (!Array.isArray(request.operations)) return request;\n const operations = request.operations.map((value) => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(\"Each edit operation must be an object\");\n const operation = value as Record<string, unknown>;\n switch (operation.kind) {\n case \"insertText\":\n case \"splitParagraph\":\n return { ...operation, suggest: true };\n case \"deleteText\":\n return {\n kind: \"suggestRevision\",\n ranges: [{ blockRef: operation.blockRef, runRef: operation.runRef, start: operation.start, end: operation.end }],\n };\n case \"mergeParagraph\":\n return { kind: \"suggestRevision\", marks: [{ blockRef: operation.blockRef, glyph: \"del\" }] };\n case \"suggestRevision\":\n case \"commentRun\":\n case \"replyComment\":\n return operation;\n default:\n throw new Error(`${String(operation.kind)} is unavailable in suggestion mode. Ask the inviter to switch this agent to editing mode`);\n }\n });\n return { ...request, operations };\n}\n\nfunction randomId(prefix: string): string {\n return `${prefix}_${bytesToBase64Url(crypto.getRandomValues(new Uint8Array(16)))}`;\n}\n\nexport function detectWakeTarget(env: NodeJS.ProcessEnv = process.env): WakeTarget | null {\n const codexSessionId = env.CODEX_THREAD_ID?.trim();\n if (codexSessionId && /^[A-Za-z0-9_-]{8,128}$/.test(codexSessionId)) {\n return { provider: \"codex\", sessionId: codexSessionId };\n }\n const claudeSessionId = env.CLAUDE_CODE_SESSION_ID?.trim();\n if (claudeSessionId && /^[A-Za-z0-9_-]{8,128}$/.test(claudeSessionId)) {\n return { provider: \"claude\", sessionId: claudeSessionId };\n }\n return null;\n}\n\nexport function agentWakePrompt(agentSessionId: string, text: string): string {\n return [\n \"An authenticated private WordInWeb message resumed this agent task.\",\n `The inviter's message is ${JSON.stringify(text)}.`,\n `The detached document bridge session is ${agentSessionId}. Do not run the connect command again.`,\n `Run document commands with: npx -y --package='${AGENT_PACKAGE_URL}' wordinweb-agent session '${agentSessionId}' '<JSON command>'`,\n 'Start with sync. For a broad text task, inspect all non-empty stories in one compact call: {\"command\":\"inspect\",\"request\":{\"kind\":\"context\"}}. Use overview, read, object, or spatial only when the task needs their extra detail.',\n \"Inspect the relevant content before every edit. If an edit returns needs_sync, sync and inspect again.\",\n \"Complete this task, then send the inviter a concise result with the chat command.\",\n \"End this agent turn after the reply. Do not call wait. The detached bridge will resume this task for the next message.\",\n ].join(\"\\n\\n\");\n}\n\nfunction write(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n\ntype Emit = (value: unknown) => void;\n\nfunction sessionSocketPath(sessionId: string): string {\n if (!/^session_[A-Za-z0-9_-]{16,128}$/.test(sessionId)) throw new Error(\"The agent session ID is invalid\");\n return process.platform === \"win32\"\n ? `\\\\\\\\.\\\\pipe\\\\wordinweb-agent-${sessionId}`\n : join(\"/tmp\", `wordinweb-agent-${sessionId}.sock`);\n}\n\nasync function importChatKey(value: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\"raw\", base64UrlToBytes(value) as BufferSource, \"AES-GCM\", false, [\"encrypt\", \"decrypt\"]);\n}\n\nfunction chatAad(agentClientId: string, messageId: string): Uint8Array {\n return new TextEncoder().encode(`wordinweb-agent-chat:${agentClientId}:${messageId}`);\n}\n\nasync function encryptChat(key: CryptoKey, agentClientId: string, messageId: string, text: string) {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const ciphertext = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: iv as BufferSource, additionalData: chatAad(agentClientId, messageId) as BufferSource },\n key,\n new TextEncoder().encode(text),\n );\n return { iv: bytesToBase64Url(iv), ciphertext: bytesToBase64Url(new Uint8Array(ciphertext)) };\n}\n\nasync function decryptChat(\n key: CryptoKey,\n agentClientId: string,\n messageId: string,\n iv: string,\n ciphertext: string,\n): Promise<string> {\n const plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: base64UrlToBytes(iv) as BufferSource, additionalData: chatAad(agentClientId, messageId) as BufferSource },\n key,\n base64UrlToBytes(ciphertext) as BufferSource,\n );\n return new TextDecoder().decode(plaintext);\n}\n\nexport async function connectAgent(\n invitationUrl: string,\n inputStream: NodeJS.ReadableStream = process.stdin,\n emit: Emit = write,\n): Promise<void> {\n const payload = await parseInvite(invitationUrl);\n let mode: AgentMode = payload.agent.mode === \"edit\" ? \"edit\" : \"suggest\";\n const clientId = randomId(\"agent\");\n const socket = new WebSocket(payload.room.wsUrl);\n const transport = createWebSocketTransport(socket as never);\n const chatKey = await importChatKey(payload.invite.chatKey);\n let connection: CollabConnection;\n let revision = 0;\n let ready = false;\n const connectionState: { current: \"reconnecting\" | \"live\" | \"offline\" } = { current: \"reconnecting\" };\n let roster: RosterEntry[] = [];\n let lastActivityIndex = 0;\n const events: unknown[] = [];\n const waiters: Array<(event: unknown) => void> = [];\n\n const publish = (event: unknown): void => {\n const waiter = waiters.shift();\n if (waiter) waiter(event);\n else events.push(event);\n emit(event);\n };\n const heartbeat = setInterval(() => {\n if (socket.readyState === WebSocket.OPEN) socket.ping();\n }, 25_000);\n socket.addEventListener(\"close\", (event) => {\n connectionState.current = \"offline\";\n clearInterval(heartbeat);\n publish({ event: \"connection_closed\", code: event.code, reason: event.reason || undefined });\n });\n\n let readyResolve: (() => void) | null = null;\n let readyReject: ((error: Error) => void) | null = null;\n const readyPromise = new Promise<void>((resolve, reject) => {\n readyResolve = resolve;\n readyReject = reject;\n });\n const callbacks: ConnectionCallbacks = {\n onChange: () => {\n revision++;\n if (connection?.ready && !ready) {\n ready = true;\n connectionState.current = \"live\";\n readyResolve?.();\n return;\n }\n if (ready) publish({ event: \"document_changed\", revision: String(revision) });\n },\n onRoster: (next) => {\n roster = next;\n if (ready) publish({ event: \"roster\", roster });\n },\n onRefused: (reason) => {\n const error = new Error(`Room connection refused: ${reason}`);\n readyReject?.(error);\n if (ready) publish({ event: \"refused\", reason });\n },\n onAgentChat: (message) => {\n if (message.agentClientId !== clientId || message.sender !== \"inviter\") return;\n void decryptChat(chatKey, clientId, message.messageId, message.iv, message.ciphertext).then(\n (text) => {\n const nextMode = parseAgentModeControl(text);\n if (nextMode) {\n mode = nextMode;\n publish({ event: \"mode_changed\", mode });\n } else publish({ event: \"chat\", messageId: message.messageId, text });\n },\n () => publish({ event: \"chat_error\", messageId: message.messageId }),\n );\n },\n onError: ({ where, error }) => publish({ event: \"connection_error\", where, message: error instanceof Error ? error.message : String(error) }),\n };\n\n const stretched = payload.room.docKey && payload.room.shareCode\n ? await stretchShareCode(payload.room.shareCode, payload.room.docId)\n : undefined;\n const codeProof = stretched ? Buffer.from(stretched).toString(\"base64\") : undefined;\n connection = payload.room.docKey\n ? new EncryptedCollabConnection(\n transport,\n clientId,\n payload.room.docKey,\n callbacks,\n stretched,\n undefined,\n payload.room.httpBase ? { httpBase: payload.room.httpBase } : undefined,\n ) as unknown as CollabConnection\n : new CollabConnection(\n transport,\n clientId,\n callbacks,\n payload.room.httpBase ? { httpBase: payload.room.httpBase } : undefined,\n );\n const profile: ParticipantProfile = { name: payload.agent.name || \"AI Agent\", color: \"\" };\n connection.join(payload.room.docId, undefined, {\n profile,\n codeProof,\n agentInvite: { inviteId: payload.invite.inviteId, token: payload.invite.token },\n });\n\n const timeout = setTimeout(() => readyReject?.(new Error(\"The document did not become ready within 30 seconds\")), 30_000);\n await readyPromise.finally(() => clearTimeout(timeout));\n const document = AgentDocument.connect({\n getDocument: () => connection.doc,\n getRevision: () => revision,\n allocateIds: (count) => connection.allocIds(count),\n submit: (operation) => connection.submit(operation),\n setPresence: (position) => {\n const deadline = Date.now() + 30_000;\n const send = (): void => {\n if (connection.pendingCount === 0) connection.setPresence(position);\n else if (connectionState.current === \"live\" && Date.now() < deadline) setTimeout(send, 25);\n };\n send();\n },\n uploadMedia: (bytes) => connection.uploadMedia(bytes),\n getConnectionState: () => connectionState.current,\n }, { provenance: { author: profile.name } });\n\n emit({\n event: \"ready\",\n clientId,\n revision: document.revision,\n mode,\n instructions: payload.agent.instructions,\n commands: [\"sync\", \"capabilities\", \"inspect\", \"edit\", \"chat\", \"wait\", \"close\"],\n });\n\n const nextEvent = (timeoutMs: number): Promise<unknown> => {\n if (events.length) return Promise.resolve(events.shift());\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n const index = waiters.indexOf(done);\n if (index >= 0) waiters.splice(index, 1);\n resolve({ event: \"wait_timeout\" });\n }, timeoutMs);\n const done = (event: unknown) => {\n clearTimeout(timer);\n resolve(event);\n };\n waiters.push(done);\n });\n };\n\n const respond = (id: Command[\"id\"], result?: unknown, error?: unknown): void => {\n emit(error === undefined ? { id, ok: true, result } : { id, ok: false, error: String(error) });\n };\n const input = createInterface({ input: inputStream, terminal: false });\n for await (const line of input) {\n if (!line.trim()) continue;\n let command: Command;\n try {\n command = JSON.parse(line) as Command;\n } catch {\n respond(undefined, undefined, \"Each input line must be valid JSON\");\n continue;\n }\n try {\n switch (command.command) {\n case \"sync\": {\n const activity = connection.activity.slice(lastActivityIndex);\n lastActivityIndex = connection.activity.length;\n respond(command.id, { revision: document.revision, activity, roster, mode });\n break;\n }\n case \"capabilities\":\n respond(command.id, document.capabilities(command.category as never, command.kind as never));\n break;\n case \"inspect\":\n respond(command.id, document.inspect(command.request as never));\n break;\n case \"edit\":\n try {\n if (connectionState.current !== \"live\") throw new Error(\"The collaboration connection is offline. Reconnect before editing\");\n respond(command.id, await document.edit(editRequestForMode(command.request, mode) as never));\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"revision is stale\")) {\n respond(command.id, { status: \"needs_sync\", latestRevision: document.revision });\n } else throw error;\n }\n break;\n case \"chat\": {\n if (connectionState.current !== \"live\") throw new Error(\"The collaboration connection is offline. Reconnect before sending chat\");\n if (!command.text?.trim()) throw new Error(\"Chat text is required\");\n const messageId = randomId(\"message\");\n const sealed = await encryptChat(chatKey, clientId, messageId, command.text.trim());\n connection.sendAgentChat(clientId, messageId, sealed.iv, sealed.ciphertext);\n respond(command.id, { messageId });\n break;\n }\n case \"wait\":\n respond(command.id, await nextEvent(Math.min(Math.max(command.timeoutMs ?? 30_000, 1), 55_000)));\n break;\n case \"close\":\n respond(command.id, { closed: true });\n clearInterval(heartbeat);\n socket.close();\n input.close();\n return;\n default:\n throw new Error(\"Unknown command\");\n }\n } catch (error) {\n respond(command.id, undefined, error instanceof Error ? error.message : String(error));\n }\n }\n clearInterval(heartbeat);\n socket.close();\n}\n\ninterface PendingCommand {\n socket: Socket;\n originalId?: string | number;\n}\n\nfunction sendSocketValue(socket: Socket, value: unknown): void {\n socket.end(`${JSON.stringify(value)}\\n`);\n}\n\nasync function listen(server: ReturnType<typeof createServer>, path: string): Promise<void> {\n if (process.platform !== \"win32\") rmSync(path, { force: true });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(path, () => {\n server.off(\"error\", reject);\n if (process.platform !== \"win32\") chmodSync(path, 0o600);\n resolve();\n });\n });\n}\n\nasync function runDaemon(invitationUrl: string, sessionId: string): Promise<void> {\n const path = sessionSocketPath(sessionId);\n const input = new PassThrough();\n const wakeTarget = detectWakeTarget();\n const wakeQueue: string[] = [];\n const pending = new Map<string, PendingCommand>();\n let readyEvent: Record<string, unknown> | null = null;\n let fatalError: string | null = null;\n let closeTimer: ReturnType<typeof setTimeout> | null = null;\n let wakeChild: ReturnType<typeof spawn> | null = null;\n let requestCounter = 0;\n let stopped = false;\n\n const sendWakeFailure = (): void => {\n input.write(`${JSON.stringify({ command: \"chat\", text: \"I could not resume the agent task. Reopen the original Codex or Claude task and reconnect this AI.\" })}\\n`);\n };\n\n const startNextWake = (): void => {\n if (!wakeTarget || wakeChild || wakeQueue.length === 0 || stopped) return;\n const prompt = agentWakePrompt(sessionId, wakeQueue.shift()!);\n const childEnv = { ...process.env };\n delete childEnv.CODEX_THREAD_ID;\n delete childEnv.CLAUDE_CODE_SESSION_ID;\n delete childEnv.CLAUDECODE;\n wakeChild = wakeTarget.provider === \"codex\"\n ? spawn(\"codex\", [\"exec\", \"resume\", wakeTarget.sessionId, \"-\"], { env: childEnv, stdio: [\"pipe\", \"ignore\", \"ignore\"] })\n : spawn(\"claude\", [\"-p\", \"--resume\", wakeTarget.sessionId], { env: childEnv, stdio: [\"pipe\", \"ignore\", \"ignore\"] });\n let finished = false;\n const finish = (ok: boolean): void => {\n if (finished) return;\n finished = true;\n wakeChild = null;\n if (!ok && !stopped) sendWakeFailure();\n startNextWake();\n };\n wakeChild.once(\"error\", () => finish(false));\n wakeChild.once(\"exit\", (code) => finish(code === 0));\n wakeChild.stdin?.on(\"error\", () => undefined);\n wakeChild.stdin?.end(prompt);\n };\n\n const shutdown = (): void => {\n if (stopped) return;\n stopped = true;\n if (closeTimer) clearTimeout(closeTimer);\n wakeChild?.kill();\n input.end();\n server.close();\n if (process.platform !== \"win32\") rmSync(path, { force: true });\n };\n\n const emit: Emit = (value) => {\n const message = value as Record<string, unknown>;\n if (message.event === \"ready\") readyEvent = message;\n if (message.event === \"connection_closed\" && !closeTimer) {\n closeTimer = setTimeout(shutdown, 60_000);\n closeTimer.unref();\n }\n if (message.event === \"chat\" && typeof message.text === \"string\" && wakeTarget) {\n wakeQueue.push(message.text);\n startNextWake();\n }\n if (typeof message.id !== \"string\") return;\n const command = pending.get(message.id);\n if (!command) return;\n pending.delete(message.id);\n const response = { ...message };\n if (command.originalId === undefined) delete response.id;\n else response.id = command.originalId;\n sendSocketValue(command.socket, response);\n };\n\n const server = createServer((socket) => {\n socket.setEncoding(\"utf8\");\n let buffer = \"\";\n socket.on(\"data\", (chunk: string) => {\n buffer += chunk;\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) return;\n socket.removeAllListeners(\"data\");\n try {\n const message = JSON.parse(buffer.slice(0, newline)) as Record<string, unknown>;\n if (message.daemon === \"status\") {\n if (fatalError) sendSocketValue(socket, { ok: false, error: fatalError });\n else if (readyEvent) sendSocketValue(socket, { ok: true, result: { state: \"ready\", ready: readyEvent } });\n else sendSocketValue(socket, { ok: true, result: { state: \"starting\" } });\n return;\n }\n if (!readyEvent) {\n sendSocketValue(socket, { ok: false, error: fatalError ?? \"The agent bridge is still starting\" });\n return;\n }\n const id = `request_${++requestCounter}`;\n pending.set(id, { socket, originalId: message.id as string | number | undefined });\n input.write(`${JSON.stringify({ ...message, id })}\\n`);\n } catch (error) {\n sendSocketValue(socket, { ok: false, error: error instanceof Error ? error.message : String(error) });\n }\n });\n });\n\n await listen(server, path);\n try {\n await connectAgent(invitationUrl, input, emit);\n } catch (error) {\n fatalError = error instanceof Error ? error.message : String(error);\n for (const command of pending.values()) sendSocketValue(command.socket, { ok: false, error: fatalError });\n pending.clear();\n await new Promise<void>((resolve) => setTimeout(resolve, 1_000));\n } finally {\n shutdown();\n }\n}\n\nfunction daemonRequest(sessionId: string, request: unknown, timeoutMs = 60_000): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n const socket = createConnection(sessionSocketPath(sessionId));\n let buffer = \"\";\n const fail = (error: Error) => {\n socket.destroy();\n reject(error);\n };\n socket.setEncoding(\"utf8\");\n socket.setTimeout(timeoutMs, () => fail(new Error(\"The agent bridge command timed out\")));\n socket.once(\"error\", fail);\n socket.once(\"connect\", () => socket.write(`${JSON.stringify(request)}\\n`));\n socket.on(\"data\", (chunk: string) => {\n buffer += chunk;\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) return;\n socket.destroy();\n try {\n resolve(JSON.parse(buffer.slice(0, newline)) as Record<string, unknown>);\n } catch (error) {\n reject(error);\n }\n });\n });\n}\n\nasync function startDaemon(invitationUrl: string): Promise<void> {\n const sessionId = randomId(\"session\");\n const wakeTarget = detectWakeTarget();\n const child = spawn(process.execPath, [fileURLToPath(import.meta.url), \"daemon\", invitationUrl, sessionId], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n const deadline = Date.now() + 35_000;\n while (Date.now() < deadline) {\n try {\n const status = await daemonRequest(sessionId, { daemon: \"status\" }, 2_000);\n if (!status.ok) throw new Error(String(status.error));\n const result = status.result as { state?: string; ready?: Record<string, unknown> } | undefined;\n if (result?.state === \"ready\" && result.ready) {\n write({\n ...result.ready,\n sessionId,\n mode: \"detached\",\n wake: wakeTarget\n ? { state: \"armed\", provider: wakeTarget.provider }\n : { state: \"unavailable\", reason: \"This command did not run inside a Codex or Claude task\" },\n next: `wordinweb-agent session '${sessionId}' '<JSON command>'`,\n });\n return;\n }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\" && code !== \"ECONNREFUSED\") throw error;\n }\n await new Promise<void>((resolve) => setTimeout(resolve, 100));\n }\n throw new Error(\"The detached agent bridge did not become ready within 35 seconds\");\n}\n\nasync function main(): Promise<void> {\n const [command, first, second] = process.argv.slice(2);\n if (command === \"connect\" && first) return startDaemon(first);\n if (command === \"session\" && first && second) {\n const request = JSON.parse(second) as Command;\n const timeoutMs = request.command === \"wait\"\n ? Math.min(Math.max(request.timeoutMs ?? 30_000, 1), 55_000) + 5_000\n : 60_000;\n write(await daemonRequest(first, request, timeoutMs));\n return;\n }\n if (command === \"daemon\" && first && second) return runDaemon(first, second);\n process.stderr.write(\n \"Usage:\\n\" +\n \" wordinweb-agent connect '<AI invitation URL>'\\n\" +\n \" wordinweb-agent session '<session ID>' '<JSON command>'\\n\",\n );\n process.exitCode = 1;\n}\n\nif (process.argv[1] && fileURLToPath(import.meta.url) === realpathSync(process.argv[1])) {\n void main().catch((error) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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","names":[]}
|