@fnndsc/calypso 0.2.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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/daemon/engine.d.ts +44 -0
- package/dist/daemon/engine.js +3 -0
- package/dist/daemon/engine.js.map +1 -0
- package/dist/daemon/server.d.ts +217 -0
- package/dist/daemon/server.js +466 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/token.d.ts +24 -0
- package/dist/daemon/token.js +56 -0
- package/dist/daemon/token.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/envelope.d.ts +136 -0
- package/dist/protocol/envelope.js +49 -0
- package/dist/protocol/envelope.js.map +1 -0
- package/dist/protocol/messages.d.ts +1032 -0
- package/dist/protocol/messages.js +189 -0
- package/dist/protocol/messages.js.map +1 -0
- package/dist/protocol/validate.d.ts +57 -0
- package/dist/protocol/validate.js +94 -0
- package/dist/protocol/validate.js.map +1 -0
- package/dist/protocol/version.d.ts +25 -0
- package/dist/protocol/version.js +32 -0
- package/dist/protocol/version.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CalypsoDaemon = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @file The CALYPSO daemon: a WebSocket host over one engine, with a session
|
|
6
|
+
* bus.
|
|
7
|
+
*
|
|
8
|
+
* The daemon binds the loopback interface only and hosts a single hosted
|
|
9
|
+
* engine. A surface attaches with the contract version and the attach token
|
|
10
|
+
* (compared in constant time); once attached it drives the engine with
|
|
11
|
+
* `execute` and `complete` messages and receives `result` and completion
|
|
12
|
+
* replies. Command execution is serialized per connection — shell semantics,
|
|
13
|
+
* and what keeps each command's error boundary correct.
|
|
14
|
+
*
|
|
15
|
+
* All attached surfaces share one session. A **session bus** broadcasts every
|
|
16
|
+
* result envelope to the *other* attached surfaces (tagged with the surface
|
|
17
|
+
* that produced it), so a command issued in one surface is immediately
|
|
18
|
+
* visible in the rest. A bounded **scrollback** ring buffer of recent
|
|
19
|
+
* envelopes is replayed to an attaching surface so it does not join blind;
|
|
20
|
+
* scrollback is presentation, not truth — a daemon restart loses it, which is
|
|
21
|
+
* correct, and the durable record is an opt-in transcript materialized to
|
|
22
|
+
* CUBE.
|
|
23
|
+
*
|
|
24
|
+
* CUBE credentials never cross the wire: the engine the daemon hosts holds its
|
|
25
|
+
* own CUBE session, established by the launcher exactly as the CLI does;
|
|
26
|
+
* surfaces authenticate to the daemon, not to CUBE.
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
const node_crypto_1 = require("node:crypto");
|
|
31
|
+
const ws_1 = require("ws");
|
|
32
|
+
const token_js_1 = require("./token.js");
|
|
33
|
+
const version_js_1 = require("../protocol/version.js");
|
|
34
|
+
const validate_js_1 = require("../protocol/validate.js");
|
|
35
|
+
/** The default number of envelopes retained for scrollback replay. */
|
|
36
|
+
const SCROLLBACK_DEFAULT = 200;
|
|
37
|
+
/**
|
|
38
|
+
* A WebSocket daemon hosting one engine for attached surfaces, with a session
|
|
39
|
+
* bus broadcasting activity across them.
|
|
40
|
+
*/
|
|
41
|
+
class CalypsoDaemon {
|
|
42
|
+
/**
|
|
43
|
+
* @param options - The engine to host, the attach token, the bind address,
|
|
44
|
+
* and the scrollback size.
|
|
45
|
+
*/
|
|
46
|
+
constructor(options) {
|
|
47
|
+
/** The one session all surfaces share; returned in each attach ack. */
|
|
48
|
+
this.sessionId = (0, node_crypto_1.randomBytes)(8).toString('hex');
|
|
49
|
+
this.surfaces = new Set();
|
|
50
|
+
this.scrollback = [];
|
|
51
|
+
this.wss = null;
|
|
52
|
+
/**
|
|
53
|
+
* Execution is serialized across the whole session (one foreground command
|
|
54
|
+
* at a time), so a prompt raised mid-command has one unambiguous surface to
|
|
55
|
+
* ask — the one running the current command.
|
|
56
|
+
*/
|
|
57
|
+
this.queue = Promise.resolve();
|
|
58
|
+
this.currentOrigin = null;
|
|
59
|
+
this.currentId = null;
|
|
60
|
+
this.pendingPrompts = new Map();
|
|
61
|
+
this.promptSeq = 0;
|
|
62
|
+
this.pendingPipes = new Map();
|
|
63
|
+
this.pipeSeq = 0;
|
|
64
|
+
this.pendingEdits = new Map();
|
|
65
|
+
this.editSeq = 0;
|
|
66
|
+
this.engine = options.engine;
|
|
67
|
+
this.token = options.token;
|
|
68
|
+
this.port = options.port ?? 0;
|
|
69
|
+
this.host = options.host ?? '127.0.0.1';
|
|
70
|
+
this.scrollbackSize = options.scrollbackSize ?? SCROLLBACK_DEFAULT;
|
|
71
|
+
this.promptProvider = options.promptProvider;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Starts listening.
|
|
75
|
+
*
|
|
76
|
+
* @returns The bound port (useful when an ephemeral port was requested).
|
|
77
|
+
*/
|
|
78
|
+
start() {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const wss = new ws_1.WebSocketServer({ host: this.host, port: this.port });
|
|
81
|
+
wss.on('listening', () => resolve(wss.address().port));
|
|
82
|
+
wss.on('error', reject);
|
|
83
|
+
wss.on('connection', (socket) => this.connection_handle(socket));
|
|
84
|
+
this.wss = wss;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Stops listening and closes all connections.
|
|
89
|
+
*
|
|
90
|
+
* @returns A promise resolving when the server has closed.
|
|
91
|
+
*/
|
|
92
|
+
stop() {
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
if (!this.wss) {
|
|
95
|
+
resolve();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const client of this.wss.clients) {
|
|
99
|
+
client.terminate();
|
|
100
|
+
}
|
|
101
|
+
this.wss.close(() => resolve());
|
|
102
|
+
this.wss = null;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Handles one surface connection: an attach handshake, then serialized
|
|
107
|
+
* command dispatch, with the surface removed from the bus on close.
|
|
108
|
+
*
|
|
109
|
+
* @param socket - The connected surface.
|
|
110
|
+
*/
|
|
111
|
+
connection_handle(socket) {
|
|
112
|
+
let surface = null;
|
|
113
|
+
socket.on('message', (data) => {
|
|
114
|
+
let parsed;
|
|
115
|
+
try {
|
|
116
|
+
parsed = JSON.parse(data.toString());
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
120
|
+
this.send(socket, { type: 'error', reason: `malformed JSON: ${message}` });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!surface) {
|
|
124
|
+
surface = this.attach_handle(socket, parsed);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const message = (0, validate_js_1.clientMessage_parse)(parsed);
|
|
128
|
+
if (!message.ok || message.value === undefined) {
|
|
129
|
+
this.send(socket, { type: 'error', reason: message.error ?? 'invalid message' });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const value = message.value;
|
|
133
|
+
const attached = surface;
|
|
134
|
+
if (value.type === 'execute') {
|
|
135
|
+
// One shared queue: commands from every surface run one at a time.
|
|
136
|
+
this.queue = this.queue.then(() => this.execute_run(attached, value));
|
|
137
|
+
}
|
|
138
|
+
else if (value.type === 'complete') {
|
|
139
|
+
void this.complete_run(socket, value);
|
|
140
|
+
}
|
|
141
|
+
else if (value.type === 'promptAnswer') {
|
|
142
|
+
this.promptAnswer_settle(value.promptId, value.answer);
|
|
143
|
+
}
|
|
144
|
+
else if (value.type === 'pipeResult') {
|
|
145
|
+
this.pipeResult_settle(value.pipeId, value.output);
|
|
146
|
+
}
|
|
147
|
+
else if (value.type === 'editResult') {
|
|
148
|
+
this.editResult_settle(value.editId, { content: value.content, changed: value.changed });
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.send(socket, { type: 'error', reason: 'already attached' });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
socket.on('close', () => {
|
|
155
|
+
if (surface) {
|
|
156
|
+
this.surfaces.delete(surface);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Validates an attach handshake: contract version, then constant-time
|
|
162
|
+
* token check. On success the surface is acknowledged, registered on the
|
|
163
|
+
* bus, and replayed the scrollback so it does not join blind; on failure it
|
|
164
|
+
* is told why and disconnected.
|
|
165
|
+
*
|
|
166
|
+
* @param socket - The connecting surface.
|
|
167
|
+
* @param raw - The first message received.
|
|
168
|
+
* @returns The registered surface, or null when the attach was refused.
|
|
169
|
+
*/
|
|
170
|
+
attach_handle(socket, raw) {
|
|
171
|
+
const attach = (0, validate_js_1.attach_parse)(raw);
|
|
172
|
+
if (!attach.ok || attach.value === undefined) {
|
|
173
|
+
this.send(socket, { type: 'error', reason: attach.error ?? 'invalid attach' });
|
|
174
|
+
socket.close();
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
if (!(0, token_js_1.token_matches)(this.token, attach.value.token)) {
|
|
178
|
+
this.send(socket, { type: 'error', reason: 'invalid token' });
|
|
179
|
+
socket.close();
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
// Each connection is a distinct surface (its own bus tag); all attach to
|
|
183
|
+
// the one shared session returned in the ack.
|
|
184
|
+
const surface = { socket, id: (0, node_crypto_1.randomBytes)(8).toString('hex') };
|
|
185
|
+
this.surfaces.add(surface);
|
|
186
|
+
this.send(socket, { type: 'attached', session: this.sessionId, protocolVersion: version_js_1.CONTRACT_VERSION });
|
|
187
|
+
this.scrollback_replay(socket);
|
|
188
|
+
// The newcomer shows the right prompt immediately, before any command.
|
|
189
|
+
void this.promptline_push(surface);
|
|
190
|
+
return surface;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Renders the current prompt (when the host supplies a provider) and pushes
|
|
194
|
+
* it to a surface, or to all surfaces when none is given. Called after each
|
|
195
|
+
* command — the context may have changed — and on attach.
|
|
196
|
+
*
|
|
197
|
+
* @param target - The surface to push to; omitted to push to all.
|
|
198
|
+
*/
|
|
199
|
+
async promptline_push(target) {
|
|
200
|
+
if (!this.promptProvider) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const text = await this.promptProvider();
|
|
204
|
+
if (target) {
|
|
205
|
+
this.send(target.socket, { type: 'promptline', text });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
for (const surface of this.surfaces) {
|
|
209
|
+
this.send(surface.socket, { type: 'promptline', text });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Replays the retained scrollback to a freshly attached surface as session
|
|
214
|
+
* events, so it arrives seeing recent activity rather than blank.
|
|
215
|
+
*
|
|
216
|
+
* @param socket - The surface to replay to.
|
|
217
|
+
*/
|
|
218
|
+
scrollback_replay(socket) {
|
|
219
|
+
for (const entry of this.scrollback) {
|
|
220
|
+
this.send(socket, { type: 'session', surface: entry.surface, envelope: entry.envelope });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Records an envelope in scrollback (trimming to the retention bound) and
|
|
225
|
+
* broadcasts it to every attached surface except the one that produced it,
|
|
226
|
+
* which already received it as the correlated `result`.
|
|
227
|
+
*
|
|
228
|
+
* @param origin - The surface that produced the envelope.
|
|
229
|
+
* @param envelope - The envelope to publish.
|
|
230
|
+
*/
|
|
231
|
+
bus_publish(origin, envelope) {
|
|
232
|
+
this.scrollback.push({ surface: origin.id, envelope });
|
|
233
|
+
if (this.scrollback.length > this.scrollbackSize) {
|
|
234
|
+
this.scrollback.splice(0, this.scrollback.length - this.scrollbackSize);
|
|
235
|
+
}
|
|
236
|
+
for (const surface of this.surfaces) {
|
|
237
|
+
if (surface !== origin) {
|
|
238
|
+
this.send(surface.socket, { type: 'session', surface: origin.id, envelope });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Runs one execute request: replies to the requester with the result, and
|
|
244
|
+
* publishes each envelope to the session bus.
|
|
245
|
+
*
|
|
246
|
+
* @param origin - The surface that submitted the request.
|
|
247
|
+
* @param message - The execute request.
|
|
248
|
+
*/
|
|
249
|
+
async execute_run(origin, message) {
|
|
250
|
+
// The command runs with this surface as the prompt target, so any prompt
|
|
251
|
+
// the engine raises is asked of the surface that submitted the command.
|
|
252
|
+
this.currentOrigin = origin;
|
|
253
|
+
this.currentId = message.id;
|
|
254
|
+
try {
|
|
255
|
+
const envelopes = await this.engine.line_execute(message.line);
|
|
256
|
+
this.send(origin.socket, { type: 'result', id: message.id, envelopes });
|
|
257
|
+
for (const envelope of envelopes) {
|
|
258
|
+
this.bus_publish(origin, envelope);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
263
|
+
this.send(origin.socket, { type: 'error', id: message.id, reason });
|
|
264
|
+
}
|
|
265
|
+
finally {
|
|
266
|
+
this.currentOrigin = null;
|
|
267
|
+
this.currentId = null;
|
|
268
|
+
}
|
|
269
|
+
// The command may have changed session context (cwd, connection); push the
|
|
270
|
+
// refreshed prompt to every surface.
|
|
271
|
+
await this.promptline_push();
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Raises a prompt on the surface running the current command, returning its
|
|
275
|
+
* answer. The host installs an engine-side input broker that calls this; the
|
|
276
|
+
* engine therefore prompts through the wire without knowing the transport.
|
|
277
|
+
*
|
|
278
|
+
* @param message - The prompt text to show.
|
|
279
|
+
* @param hidden - Whether to request no-echo entry (a password).
|
|
280
|
+
* @returns The surface's answer.
|
|
281
|
+
* @throws {Error} When no command is executing (nothing to prompt for) or the
|
|
282
|
+
* surface disconnects before answering.
|
|
283
|
+
*/
|
|
284
|
+
prompt_current(message, hidden) {
|
|
285
|
+
const origin = this.currentOrigin;
|
|
286
|
+
if (!origin) {
|
|
287
|
+
return Promise.reject(new Error('no active command to prompt for'));
|
|
288
|
+
}
|
|
289
|
+
const promptId = `p${this.promptSeq++}`;
|
|
290
|
+
return new Promise((resolve, reject) => {
|
|
291
|
+
this.pendingPrompts.set(promptId, resolve);
|
|
292
|
+
const onClose = () => {
|
|
293
|
+
if (this.pendingPrompts.delete(promptId)) {
|
|
294
|
+
reject(new Error('surface disconnected before answering'));
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
origin.socket.once('close', onClose);
|
|
298
|
+
this.send(origin.socket, { type: 'prompt', promptId, message, hidden });
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Streams structured progress from the executing command to its origin
|
|
303
|
+
* surface. Progress is live-only; when no command is active or the surface is
|
|
304
|
+
* gone, the event is dropped.
|
|
305
|
+
*
|
|
306
|
+
* @param event - The structured progress facts.
|
|
307
|
+
* @returns True when the event was sent to a surface.
|
|
308
|
+
*/
|
|
309
|
+
progress_current(event) {
|
|
310
|
+
const origin = this.currentOrigin;
|
|
311
|
+
const id = this.currentId;
|
|
312
|
+
if (!origin || !id || origin.socket.readyState !== ws_1.WebSocket.OPEN) {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
this.send(origin.socket, { type: 'progress', id, ...event });
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Streams an opaque live output chunk from the executing command to its
|
|
320
|
+
* origin surface. Output is live telemetry, distinct from final envelopes;
|
|
321
|
+
* when no command is active or the origin surface is gone, it is dropped.
|
|
322
|
+
*
|
|
323
|
+
* @param channel - The output channel that produced the chunk.
|
|
324
|
+
* @param chunk - The text chunk to forward.
|
|
325
|
+
* @returns True when the chunk was sent to a surface.
|
|
326
|
+
*/
|
|
327
|
+
output_current(channel, chunk) {
|
|
328
|
+
const origin = this.currentOrigin;
|
|
329
|
+
const id = this.currentId;
|
|
330
|
+
if (!origin || !id || origin.socket.readyState !== ws_1.WebSocket.OPEN) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
this.send(origin.socket, { type: 'output', id, channel, chunk });
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Resolves a pending prompt with the surface's answer.
|
|
338
|
+
*
|
|
339
|
+
* @param promptId - The prompt correlation id.
|
|
340
|
+
* @param answer - The answer the surface supplied.
|
|
341
|
+
*/
|
|
342
|
+
promptAnswer_settle(promptId, answer) {
|
|
343
|
+
const resolve = this.pendingPrompts.get(promptId);
|
|
344
|
+
if (resolve) {
|
|
345
|
+
this.pendingPrompts.delete(promptId);
|
|
346
|
+
resolve(answer);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Runs a pipeline segment on the surface running the current command,
|
|
351
|
+
* returning its output. The host installs a `pipeSegment` on its engine-side
|
|
352
|
+
* surface that calls this, so a pipeline's segments run on the client and
|
|
353
|
+
* never on the daemon host. Data crosses the wire base64-encoded.
|
|
354
|
+
*
|
|
355
|
+
* @param command - The segment command line.
|
|
356
|
+
* @param input - The bytes to feed the segment.
|
|
357
|
+
* @returns The segment's output bytes.
|
|
358
|
+
* @throws {Error} When no command is executing or the surface disconnects.
|
|
359
|
+
*/
|
|
360
|
+
pipe_current(command, input) {
|
|
361
|
+
const origin = this.currentOrigin;
|
|
362
|
+
if (!origin) {
|
|
363
|
+
return Promise.reject(new Error('no active command to run a pipe segment for'));
|
|
364
|
+
}
|
|
365
|
+
const pipeId = `x${this.pipeSeq++}`;
|
|
366
|
+
return new Promise((resolve, reject) => {
|
|
367
|
+
this.pendingPipes.set(pipeId, resolve);
|
|
368
|
+
const onClose = () => {
|
|
369
|
+
if (this.pendingPipes.delete(pipeId)) {
|
|
370
|
+
reject(new Error('surface disconnected before returning pipe output'));
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
origin.socket.once('close', onClose);
|
|
374
|
+
this.send(origin.socket, { type: 'pipe', pipeId, command, input: input.toString('base64') });
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Resolves a pending pipe segment with the surface's output.
|
|
379
|
+
*
|
|
380
|
+
* @param pipeId - The pipe correlation id.
|
|
381
|
+
* @param output - The base64-encoded segment output.
|
|
382
|
+
*/
|
|
383
|
+
pipeResult_settle(pipeId, output) {
|
|
384
|
+
const resolve = this.pendingPipes.get(pipeId);
|
|
385
|
+
if (resolve) {
|
|
386
|
+
this.pendingPipes.delete(pipeId);
|
|
387
|
+
resolve(Buffer.from(output, 'base64'));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Opens content in the editor of the surface running the current command
|
|
392
|
+
* and returns the edited result. The host installs a `localEdit` on its
|
|
393
|
+
* engine-side surface that calls this, so `edit` opens the operator's own
|
|
394
|
+
* editor and never one on the daemon host.
|
|
395
|
+
*
|
|
396
|
+
* @param content - The content to edit.
|
|
397
|
+
* @param extension - An optional filename extension for syntax mode.
|
|
398
|
+
* @returns The edited content and whether it changed.
|
|
399
|
+
* @throws {Error} When no command is executing or the surface disconnects.
|
|
400
|
+
*/
|
|
401
|
+
edit_current(content, extension) {
|
|
402
|
+
const origin = this.currentOrigin;
|
|
403
|
+
if (!origin) {
|
|
404
|
+
return Promise.reject(new Error('no active command to edit for'));
|
|
405
|
+
}
|
|
406
|
+
const editId = `e${this.editSeq++}`;
|
|
407
|
+
return new Promise((resolve, reject) => {
|
|
408
|
+
this.pendingEdits.set(editId, resolve);
|
|
409
|
+
const onClose = () => {
|
|
410
|
+
if (this.pendingEdits.delete(editId)) {
|
|
411
|
+
reject(new Error('surface disconnected before returning the edit'));
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
origin.socket.once('close', onClose);
|
|
415
|
+
this.send(origin.socket, { type: 'edit', editId, content, extension });
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Resolves a pending edit with the surface's result.
|
|
420
|
+
*
|
|
421
|
+
* @param editId - The edit correlation id.
|
|
422
|
+
* @param outcome - The edited content and changed flag.
|
|
423
|
+
*/
|
|
424
|
+
editResult_settle(editId, outcome) {
|
|
425
|
+
const resolve = this.pendingEdits.get(editId);
|
|
426
|
+
if (resolve) {
|
|
427
|
+
this.pendingEdits.delete(editId);
|
|
428
|
+
resolve(outcome);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Runs one completion request and sends its reply, or an error. Completion
|
|
433
|
+
* is a read and is not broadcast.
|
|
434
|
+
*
|
|
435
|
+
* @param socket - The surface to reply to.
|
|
436
|
+
* @param message - The completion request.
|
|
437
|
+
*/
|
|
438
|
+
async complete_run(socket, message) {
|
|
439
|
+
try {
|
|
440
|
+
const result = await this.engine.line_complete(message.prefix);
|
|
441
|
+
this.send(socket, {
|
|
442
|
+
type: 'complete',
|
|
443
|
+
id: message.id,
|
|
444
|
+
prefix: result.prefix,
|
|
445
|
+
candidates: result.candidates,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
450
|
+
this.send(socket, { type: 'error', id: message.id, reason });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Sends a message to a surface if the socket is still open.
|
|
455
|
+
*
|
|
456
|
+
* @param socket - The destination surface.
|
|
457
|
+
* @param message - The message to send.
|
|
458
|
+
*/
|
|
459
|
+
send(socket, message) {
|
|
460
|
+
if (socket.readyState === ws_1.WebSocket.OPEN) {
|
|
461
|
+
socket.send(JSON.stringify(message));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
exports.CalypsoDaemon = CalypsoDaemon;
|
|
466
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a fresh attach token: 256 bits of randomness, hex-encoded.
|
|
3
|
+
*
|
|
4
|
+
* @returns The token string.
|
|
5
|
+
*/
|
|
6
|
+
export declare function token_generate(): string;
|
|
7
|
+
/**
|
|
8
|
+
* Writes the token to a file readable only by the current user (mode 0600),
|
|
9
|
+
* for same-user discovery.
|
|
10
|
+
*
|
|
11
|
+
* @param token - The token to write.
|
|
12
|
+
* @param path - The destination file path.
|
|
13
|
+
*/
|
|
14
|
+
export declare function token_writeFile(token: string, path: string): void;
|
|
15
|
+
/**
|
|
16
|
+
* Compares a provided token against the expected one in constant time, so a
|
|
17
|
+
* mismatch reveals nothing through timing. Unequal lengths fail fast (and are
|
|
18
|
+
* not themselves secret).
|
|
19
|
+
*
|
|
20
|
+
* @param expected - The daemon's token.
|
|
21
|
+
* @param provided - The token a surface presented at attach.
|
|
22
|
+
* @returns True when the tokens match exactly.
|
|
23
|
+
*/
|
|
24
|
+
export declare function token_matches(expected: string, provided: string): boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.token_generate = token_generate;
|
|
4
|
+
exports.token_writeFile = token_writeFile;
|
|
5
|
+
exports.token_matches = token_matches;
|
|
6
|
+
/**
|
|
7
|
+
* @file The attach token: generation, discovery file, and constant-time
|
|
8
|
+
* comparison.
|
|
9
|
+
*
|
|
10
|
+
* The daemon's attach perimeter is the proven local-daemon model: a random
|
|
11
|
+
* token generated at startup, required in every attach handshake, compared in
|
|
12
|
+
* constant time. The token is printed and written to a user-readable file so
|
|
13
|
+
* a same-user surface can discover it without it crossing an untrusted
|
|
14
|
+
* channel. Remote access is an operator concern (an SSH tunnel or TLS proxy
|
|
15
|
+
* in front, with the token still gating attach inside it).
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
const node_crypto_1 = require("node:crypto");
|
|
20
|
+
const node_fs_1 = require("node:fs");
|
|
21
|
+
/**
|
|
22
|
+
* Generates a fresh attach token: 256 bits of randomness, hex-encoded.
|
|
23
|
+
*
|
|
24
|
+
* @returns The token string.
|
|
25
|
+
*/
|
|
26
|
+
function token_generate() {
|
|
27
|
+
return (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Writes the token to a file readable only by the current user (mode 0600),
|
|
31
|
+
* for same-user discovery.
|
|
32
|
+
*
|
|
33
|
+
* @param token - The token to write.
|
|
34
|
+
* @param path - The destination file path.
|
|
35
|
+
*/
|
|
36
|
+
function token_writeFile(token, path) {
|
|
37
|
+
(0, node_fs_1.writeFileSync)(path, `${token}\n`, { mode: 0o600 });
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compares a provided token against the expected one in constant time, so a
|
|
41
|
+
* mismatch reveals nothing through timing. Unequal lengths fail fast (and are
|
|
42
|
+
* not themselves secret).
|
|
43
|
+
*
|
|
44
|
+
* @param expected - The daemon's token.
|
|
45
|
+
* @param provided - The token a surface presented at attach.
|
|
46
|
+
* @returns True when the tokens match exactly.
|
|
47
|
+
*/
|
|
48
|
+
function token_matches(expected, provided) {
|
|
49
|
+
const expectedBytes = Buffer.from(expected, 'utf-8');
|
|
50
|
+
const providedBytes = Buffer.from(provided, 'utf-8');
|
|
51
|
+
if (expectedBytes.length !== providedBytes.length) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return (0, node_crypto_1.timingSafeEqual)(expectedBytes, providedBytes);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/daemon/token.ts"],"names":[],"mappings":";;AAqBA,wCAEC;AASD,0CAEC;AAWD,sCAOC;AApDD;;;;;;;;;;;;GAYG;AACH,6CAA2D;AAC3D,qCAAwC;AAExC;;;;GAIG;AACH,SAAgB,cAAc;IAC5B,OAAO,IAAA,yBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,KAAa,EAAE,IAAY;IACzD,IAAA,uBAAa,EAAC,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,QAAgB,EAAE,QAAgB;IAC9D,MAAM,aAAa,GAAW,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAW,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7D,IAAI,aAAa,CAAC,MAAM,KAAK,aAAa,CAAC,MAAM,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAA,6BAAe,EAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AACvD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The CALYPSO wire contract: public surface.
|
|
3
|
+
*
|
|
4
|
+
* calypso is the fifth mise package: the session daemon that hosts a chell
|
|
5
|
+
* engine and serves it to surfaces over a WebSocket. This first slice is the
|
|
6
|
+
* wire contract — the typed protocol schemas and the boundary validation
|
|
7
|
+
* that every message crosses. The daemon, session bus, and remote client
|
|
8
|
+
* build on it.
|
|
9
|
+
*
|
|
10
|
+
* @see docs/calypso.adoc for the governing design.
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
export { CONTRACT_VERSION, version_isCompatible } from './protocol/version.js';
|
|
14
|
+
export { envelopeStatusSchema, stackMessageSchema, envelopeModelSchema, resolutionTraceSchema, commandEnvelopeSchema, type WireEnvelope, } from './protocol/envelope.js';
|
|
15
|
+
export { channelSchema, attachMessageSchema, executeMessageSchema, completeRequestSchema, clientMessageSchema, attachedMessageSchema, resultMessageSchema, completeReplySchema, outputMessageSchema, progressOperationSchema, progressKindSchema, progressPhaseSchema, progressUnitSchema, progressStatusSchema, progressMessageSchema, sessionMessageSchema, errorMessageSchema, serverMessageSchema, type ClientMessage, type ProgressEvent, type ServerMessage, } from './protocol/messages.js';
|
|
16
|
+
export { clientMessage_parse, serverMessage_parse, clientMessage_fromJson, attach_parse, type ParseResult, } from './protocol/validate.js';
|
|
17
|
+
export { type HostedEngine, type CompletionResult } from './daemon/engine.js';
|
|
18
|
+
export { token_generate, token_writeFile, token_matches } from './daemon/token.js';
|
|
19
|
+
export { CalypsoDaemon, type DaemonOptions, type EditOutcome } from './daemon/server.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CalypsoDaemon = exports.token_matches = exports.token_writeFile = exports.token_generate = exports.attach_parse = exports.clientMessage_fromJson = exports.serverMessage_parse = exports.clientMessage_parse = exports.serverMessageSchema = exports.errorMessageSchema = exports.sessionMessageSchema = exports.progressMessageSchema = exports.progressStatusSchema = exports.progressUnitSchema = exports.progressPhaseSchema = exports.progressKindSchema = exports.progressOperationSchema = exports.outputMessageSchema = exports.completeReplySchema = exports.resultMessageSchema = exports.attachedMessageSchema = exports.clientMessageSchema = exports.completeRequestSchema = exports.executeMessageSchema = exports.attachMessageSchema = exports.channelSchema = exports.commandEnvelopeSchema = exports.resolutionTraceSchema = exports.envelopeModelSchema = exports.stackMessageSchema = exports.envelopeStatusSchema = exports.version_isCompatible = exports.CONTRACT_VERSION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @file The CALYPSO wire contract: public surface.
|
|
6
|
+
*
|
|
7
|
+
* calypso is the fifth mise package: the session daemon that hosts a chell
|
|
8
|
+
* engine and serves it to surfaces over a WebSocket. This first slice is the
|
|
9
|
+
* wire contract — the typed protocol schemas and the boundary validation
|
|
10
|
+
* that every message crosses. The daemon, session bus, and remote client
|
|
11
|
+
* build on it.
|
|
12
|
+
*
|
|
13
|
+
* @see docs/calypso.adoc for the governing design.
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
var version_js_1 = require("./protocol/version.js");
|
|
17
|
+
Object.defineProperty(exports, "CONTRACT_VERSION", { enumerable: true, get: function () { return version_js_1.CONTRACT_VERSION; } });
|
|
18
|
+
Object.defineProperty(exports, "version_isCompatible", { enumerable: true, get: function () { return version_js_1.version_isCompatible; } });
|
|
19
|
+
var envelope_js_1 = require("./protocol/envelope.js");
|
|
20
|
+
Object.defineProperty(exports, "envelopeStatusSchema", { enumerable: true, get: function () { return envelope_js_1.envelopeStatusSchema; } });
|
|
21
|
+
Object.defineProperty(exports, "stackMessageSchema", { enumerable: true, get: function () { return envelope_js_1.stackMessageSchema; } });
|
|
22
|
+
Object.defineProperty(exports, "envelopeModelSchema", { enumerable: true, get: function () { return envelope_js_1.envelopeModelSchema; } });
|
|
23
|
+
Object.defineProperty(exports, "resolutionTraceSchema", { enumerable: true, get: function () { return envelope_js_1.resolutionTraceSchema; } });
|
|
24
|
+
Object.defineProperty(exports, "commandEnvelopeSchema", { enumerable: true, get: function () { return envelope_js_1.commandEnvelopeSchema; } });
|
|
25
|
+
var messages_js_1 = require("./protocol/messages.js");
|
|
26
|
+
Object.defineProperty(exports, "channelSchema", { enumerable: true, get: function () { return messages_js_1.channelSchema; } });
|
|
27
|
+
Object.defineProperty(exports, "attachMessageSchema", { enumerable: true, get: function () { return messages_js_1.attachMessageSchema; } });
|
|
28
|
+
Object.defineProperty(exports, "executeMessageSchema", { enumerable: true, get: function () { return messages_js_1.executeMessageSchema; } });
|
|
29
|
+
Object.defineProperty(exports, "completeRequestSchema", { enumerable: true, get: function () { return messages_js_1.completeRequestSchema; } });
|
|
30
|
+
Object.defineProperty(exports, "clientMessageSchema", { enumerable: true, get: function () { return messages_js_1.clientMessageSchema; } });
|
|
31
|
+
Object.defineProperty(exports, "attachedMessageSchema", { enumerable: true, get: function () { return messages_js_1.attachedMessageSchema; } });
|
|
32
|
+
Object.defineProperty(exports, "resultMessageSchema", { enumerable: true, get: function () { return messages_js_1.resultMessageSchema; } });
|
|
33
|
+
Object.defineProperty(exports, "completeReplySchema", { enumerable: true, get: function () { return messages_js_1.completeReplySchema; } });
|
|
34
|
+
Object.defineProperty(exports, "outputMessageSchema", { enumerable: true, get: function () { return messages_js_1.outputMessageSchema; } });
|
|
35
|
+
Object.defineProperty(exports, "progressOperationSchema", { enumerable: true, get: function () { return messages_js_1.progressOperationSchema; } });
|
|
36
|
+
Object.defineProperty(exports, "progressKindSchema", { enumerable: true, get: function () { return messages_js_1.progressKindSchema; } });
|
|
37
|
+
Object.defineProperty(exports, "progressPhaseSchema", { enumerable: true, get: function () { return messages_js_1.progressPhaseSchema; } });
|
|
38
|
+
Object.defineProperty(exports, "progressUnitSchema", { enumerable: true, get: function () { return messages_js_1.progressUnitSchema; } });
|
|
39
|
+
Object.defineProperty(exports, "progressStatusSchema", { enumerable: true, get: function () { return messages_js_1.progressStatusSchema; } });
|
|
40
|
+
Object.defineProperty(exports, "progressMessageSchema", { enumerable: true, get: function () { return messages_js_1.progressMessageSchema; } });
|
|
41
|
+
Object.defineProperty(exports, "sessionMessageSchema", { enumerable: true, get: function () { return messages_js_1.sessionMessageSchema; } });
|
|
42
|
+
Object.defineProperty(exports, "errorMessageSchema", { enumerable: true, get: function () { return messages_js_1.errorMessageSchema; } });
|
|
43
|
+
Object.defineProperty(exports, "serverMessageSchema", { enumerable: true, get: function () { return messages_js_1.serverMessageSchema; } });
|
|
44
|
+
var validate_js_1 = require("./protocol/validate.js");
|
|
45
|
+
Object.defineProperty(exports, "clientMessage_parse", { enumerable: true, get: function () { return validate_js_1.clientMessage_parse; } });
|
|
46
|
+
Object.defineProperty(exports, "serverMessage_parse", { enumerable: true, get: function () { return validate_js_1.serverMessage_parse; } });
|
|
47
|
+
Object.defineProperty(exports, "clientMessage_fromJson", { enumerable: true, get: function () { return validate_js_1.clientMessage_fromJson; } });
|
|
48
|
+
Object.defineProperty(exports, "attach_parse", { enumerable: true, get: function () { return validate_js_1.attach_parse; } });
|
|
49
|
+
var token_js_1 = require("./daemon/token.js");
|
|
50
|
+
Object.defineProperty(exports, "token_generate", { enumerable: true, get: function () { return token_js_1.token_generate; } });
|
|
51
|
+
Object.defineProperty(exports, "token_writeFile", { enumerable: true, get: function () { return token_js_1.token_writeFile; } });
|
|
52
|
+
Object.defineProperty(exports, "token_matches", { enumerable: true, get: function () { return token_js_1.token_matches; } });
|
|
53
|
+
var server_js_1 = require("./daemon/server.js");
|
|
54
|
+
Object.defineProperty(exports, "CalypsoDaemon", { enumerable: true, get: function () { return server_js_1.CalypsoDaemon; } });
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,oDAA+E;AAAtE,8GAAA,gBAAgB,OAAA;AAAE,kHAAA,oBAAoB,OAAA;AAC/C,sDAOgC;AAN9B,mHAAA,oBAAoB,OAAA;AACpB,iHAAA,kBAAkB,OAAA;AAClB,kHAAA,mBAAmB,OAAA;AACnB,oHAAA,qBAAqB,OAAA;AACrB,oHAAA,qBAAqB,OAAA;AAGvB,sDAsBgC;AArB9B,4GAAA,aAAa,OAAA;AACb,kHAAA,mBAAmB,OAAA;AACnB,mHAAA,oBAAoB,OAAA;AACpB,oHAAA,qBAAqB,OAAA;AACrB,kHAAA,mBAAmB,OAAA;AACnB,oHAAA,qBAAqB,OAAA;AACrB,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,sHAAA,uBAAuB,OAAA;AACvB,iHAAA,kBAAkB,OAAA;AAClB,kHAAA,mBAAmB,OAAA;AACnB,iHAAA,kBAAkB,OAAA;AAClB,mHAAA,oBAAoB,OAAA;AACpB,oHAAA,qBAAqB,OAAA;AACrB,mHAAA,oBAAoB,OAAA;AACpB,iHAAA,kBAAkB,OAAA;AAClB,kHAAA,mBAAmB,OAAA;AAKrB,sDAMgC;AAL9B,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,qHAAA,sBAAsB,OAAA;AACtB,2GAAA,YAAY,OAAA;AAId,8CAAmF;AAA1E,0GAAA,cAAc,OAAA;AAAE,2GAAA,eAAe,OAAA;AAAE,yGAAA,aAAa,OAAA;AACvD,gDAAyF;AAAhF,0GAAA,aAAa,OAAA"}
|