beatrina 0.8.6
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 +92 -0
- package/NOTICES +72 -0
- package/README.md +124 -0
- package/bin/beatrina.mjs +223 -0
- package/bin/cli.mjs +80 -0
- package/bin/failsafe.mjs +74 -0
- package/bin/identity.mjs +45 -0
- package/bin/prova-post.mjs +51 -0
- package/bin/sessions.mjs +95 -0
- package/bin/shortcut.mjs +151 -0
- package/bin/update-check.mjs +55 -0
- package/bin/upgrade.mjs +76 -0
- package/build-info.json +1 -0
- package/carmar_V0.8.6.html +1310 -0
- package/check/acceptance.mjs +278 -0
- package/check/session.mjs +215 -0
- package/engines/js/document-exec.mjs +82 -0
- package/engines/js/persist.mjs +214 -0
- package/engines/js/worker.mjs +424 -0
- package/engines/python/adapter.py +577 -0
- package/engines/python/analyze.py +814 -0
- package/engines/python/bootstrap.py +309 -0
- package/engines/python/dataview.py +735 -0
- package/engines/python/debugger.py +346 -0
- package/engines/python/document_exec.py +158 -0
- package/engines/python/engine.json +28 -0
- package/engines/python/handoff.py +118 -0
- package/engines/python/worker.py +564 -0
- package/engines/r/engine.json +25 -0
- package/engines/r/handoff.R +92 -0
- package/failsafe/ai-policy.R +255 -0
- package/failsafe/ai-store.R +373 -0
- package/failsafe/cite.R +418 -0
- package/failsafe/journal.R +684 -0
- package/failsafe/plugins.R +809 -0
- package/failsafe/serve.R +5500 -0
- package/host/ai-policy.mjs +218 -0
- package/host/deployment.mjs +160 -0
- package/host/engine-js.mjs +98 -0
- package/host/engine-pool.mjs +383 -0
- package/host/engine-python.mjs +228 -0
- package/host/engine-r.mjs +206 -0
- package/host/engine-stdio.mjs +401 -0
- package/host/journal-store.mjs +749 -0
- package/host/main.mjs +503 -0
- package/host/planes/README.md +41 -0
- package/host/planes/ai-store.mjs +327 -0
- package/host/planes/ai.mjs +467 -0
- package/host/planes/analyze.mjs +397 -0
- package/host/planes/cite.mjs +517 -0
- package/host/planes/files.mjs +0 -0
- package/host/planes/jobs.mjs +704 -0
- package/host/planes/journal.mjs +53 -0
- package/host/planes/latex.mjs +201 -0
- package/host/planes/mcp.mjs +493 -0
- package/host/planes/pair.mjs +325 -0
- package/host/planes/pipe-term.mjs +122 -0
- package/host/planes/plugins.mjs +112 -0
- package/host/planes/proc-tree.mjs +76 -0
- package/host/planes/sessions.mjs +434 -0
- package/host/planes/settings.mjs +164 -0
- package/host/planes/terminal.mjs +286 -0
- package/host/planes/test-file.mjs +80 -0
- package/host/planes/update.mjs +214 -0
- package/host/plugin-store.mjs +838 -0
- package/host/server.mjs +441 -0
- package/host/settings.mjs +379 -0
- package/host/update-record.mjs +59 -0
- package/host/user-dirs.mjs +117 -0
- package/host/windows-runtime.mjs +145 -0
- package/host/worker-plane.mjs +713 -0
- package/host/ws.mjs +190 -0
- package/kernel/analyze.R +668 -0
- package/kernel/deployment.R +165 -0
- package/kernel/examples/NOTICE.md +38 -0
- package/kernel/examples/tna-complete-tutorial.Rmd +210 -0
- package/kernel/fileio.R +656 -0
- package/kernel/index.html +96 -0
- package/kernel/job-run.R +391 -0
- package/kernel/jobs.R +276 -0
- package/kernel/kernel-protocol +1 -0
- package/kernel/kernel-version +1 -0
- package/kernel/kernel.R +671 -0
- package/kernel/knitr-run.R +245 -0
- package/kernel/latex.R +609 -0
- package/kernel/mcp/carmar-mcp.mjs +516 -0
- package/kernel/notebook-page.R +67 -0
- package/kernel/plugins/csl/apa/apa.csl +2273 -0
- package/kernel/plugins/csl/apa/plugin.json +19 -0
- package/kernel/plugins/csl/chicago-author-date/chicago-author-date.csl +4216 -0
- package/kernel/plugins/csl/chicago-author-date/plugin.json +19 -0
- package/kernel/plugins/csl/harvard-cite-them-right/harvard-cite-them-right.csl +316 -0
- package/kernel/plugins/csl/harvard-cite-them-right/plugin.json +19 -0
- package/kernel/plugins/csl/ieee/ieee.csl +519 -0
- package/kernel/plugins/csl/ieee/plugin.json +19 -0
- package/kernel/plugins/csl/modern-language-association/modern-language-association.csl +1184 -0
- package/kernel/plugins/csl/modern-language-association/plugin.json +19 -0
- package/kernel/plugins/csl/nature/nature.csl +189 -0
- package/kernel/plugins/csl/nature/plugin.json +19 -0
- package/kernel/plugins/latex/apa7/apa7.json +14 -0
- package/kernel/plugins/latex/apa7/plugin.json +19 -0
- package/kernel/plugins/latex/elsarticle/elsarticle.json +14 -0
- package/kernel/plugins/latex/elsarticle/plugin.json +19 -0
- package/kernel/plugins/latex/ieeetran/ieeetran.json +10 -0
- package/kernel/plugins/latex/ieeetran/plugin.json +19 -0
- package/kernel/project.R +131 -0
- package/kernel/settings.R +410 -0
- package/kernel/sniff.R +769 -0
- package/kernel/worker-boot.R +22 -0
- package/kernel/worker.R +3496 -0
- package/lib/agent-authoring-contract.js +547 -0
- package/lib/cell-kinds.js +108 -0
- package/lib/engine-labels.js +324 -0
- package/package.json +32 -0
package/host/ws.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// ws.mjs — a WebSocket SERVER, RFC 6455, over Node's http upgrade event.
|
|
2
|
+
//
|
|
3
|
+
// Node ships a WebSocket client (>= 22) and no server, and the host has no
|
|
4
|
+
// dependencies by decision (tools/mcp/carmar-mcp.mjs and test/fixtures/
|
|
5
|
+
// ws-origin.mjs set the precedent: a few hundred lines of protocol beat a
|
|
6
|
+
// package the security review must then read). What this implements is the
|
|
7
|
+
// subset a notebook page uses: text frames, masked client → unmasked server,
|
|
8
|
+
// ping answered, close handshake, continuation reassembled. No extensions, no
|
|
9
|
+
// permessage-deflate (the client never negotiates one; a request for it is
|
|
10
|
+
// simply not granted).
|
|
11
|
+
//
|
|
12
|
+
// The gate is the caller's. `attachWebSocket` hands every upgrade request to
|
|
13
|
+
// `gate(req)` first and refuses with the returned status/text when it answers
|
|
14
|
+
// anything but null — that is where Origin and Host are decided, and it is
|
|
15
|
+
// deliberately outside this file so the protocol code never grows an opinion
|
|
16
|
+
// about trust.
|
|
17
|
+
|
|
18
|
+
import crypto from "node:crypto";
|
|
19
|
+
|
|
20
|
+
const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
21
|
+
/** A single message larger than this closes the socket (1009). serve.R's
|
|
22
|
+
* 8 MB refusal is applied by the frame handler above; this is the ceiling
|
|
23
|
+
* under which a message is even assembled. */
|
|
24
|
+
const MAX_MESSAGE_BYTES = 16 * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} WsSocket
|
|
28
|
+
* @property {(text: string) => boolean} send false when the socket is gone
|
|
29
|
+
* @property {(code?: number, reason?: string) => void} close
|
|
30
|
+
* @property {(fn: (text: string) => void) => void} onMessage
|
|
31
|
+
* @property {(fn: () => void) => void} onClose
|
|
32
|
+
* @property {import("node:http").IncomingMessage} request the upgrade request, kept —
|
|
33
|
+
* the gate's reasoning is recomputed from it (socket_class in serve.R)
|
|
34
|
+
* @property {boolean} open
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Listen for upgrades on an http.Server.
|
|
39
|
+
*
|
|
40
|
+
* @param {import("node:http").Server} server
|
|
41
|
+
* @param {Object} opts
|
|
42
|
+
* @param {(req: import("node:http").IncomingMessage) => (null | {status:number, body:string})} opts.gate
|
|
43
|
+
* @param {(socket: WsSocket) => void} opts.onOpen
|
|
44
|
+
*/
|
|
45
|
+
export function attachWebSocket(server, { gate, onOpen }) {
|
|
46
|
+
server.on("upgrade", (req, raw, head) => {
|
|
47
|
+
const refusal = gate(req);
|
|
48
|
+
if (refusal) {
|
|
49
|
+
raw.write(`HTTP/1.1 ${refusal.status} ${statusText(refusal.status)}\r\n`
|
|
50
|
+
+ "Content-Type: text/plain\r\nCache-Control: no-store\r\nConnection: close\r\n"
|
|
51
|
+
+ `Content-Length: ${Buffer.byteLength(refusal.body)}\r\n\r\n${refusal.body}`);
|
|
52
|
+
raw.destroy();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const key = req.headers["sec-websocket-key"];
|
|
56
|
+
const version = req.headers["sec-websocket-version"];
|
|
57
|
+
if (String(req.headers.upgrade || "").toLowerCase() !== "websocket" || !key || version !== "13") {
|
|
58
|
+
raw.write("HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
|
|
59
|
+
raw.destroy();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const accept = crypto.createHash("sha1").update(key + GUID).digest("base64");
|
|
63
|
+
raw.write("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n"
|
|
64
|
+
+ `Sec-WebSocket-Accept: ${accept}\r\n\r\n`);
|
|
65
|
+
raw.setNoDelay(true);
|
|
66
|
+
const socket = wrap(raw, req, head);
|
|
67
|
+
onOpen(socket);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function wrap(raw, request, head) {
|
|
72
|
+
let buf = head && head.length ? Buffer.from(head) : Buffer.alloc(0);
|
|
73
|
+
let partial = null; // continuation in progress: {opcode, chunks, bytes}
|
|
74
|
+
let open = true;
|
|
75
|
+
let closeSent = false;
|
|
76
|
+
const messageHandlers = [];
|
|
77
|
+
const closeHandlers = [];
|
|
78
|
+
|
|
79
|
+
const finish = () => {
|
|
80
|
+
if (!open) return;
|
|
81
|
+
open = false;
|
|
82
|
+
closeHandlers.forEach((fn) => { try { fn(); } catch { /* a listener must not break teardown */ } });
|
|
83
|
+
};
|
|
84
|
+
const sendFrame = (opcode, payload) => {
|
|
85
|
+
if (!open || raw.destroyed) return false;
|
|
86
|
+
try { raw.write(frame(opcode, payload)); return true; } catch { return false; }
|
|
87
|
+
};
|
|
88
|
+
const closeWith = (code, reason = "") => {
|
|
89
|
+
if (!closeSent) {
|
|
90
|
+
closeSent = true;
|
|
91
|
+
const body = Buffer.concat([Buffer.from([code >> 8, code & 0xff]), Buffer.from(reason, "utf8")]);
|
|
92
|
+
sendFrame(0x8, body);
|
|
93
|
+
}
|
|
94
|
+
// Give the peer's close frame a moment, then drop the transport either way.
|
|
95
|
+
setTimeout(() => { try { raw.destroy(); } catch { /* gone */ } }, 200).unref();
|
|
96
|
+
finish();
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
raw.on("data", (chunk) => {
|
|
100
|
+
buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
|
|
101
|
+
drain();
|
|
102
|
+
});
|
|
103
|
+
raw.on("close", finish);
|
|
104
|
+
raw.on("error", finish);
|
|
105
|
+
raw.on("end", () => { try { raw.end(); } catch { /* gone */ } finish(); });
|
|
106
|
+
|
|
107
|
+
function drain() {
|
|
108
|
+
for (;;) {
|
|
109
|
+
if (buf.length < 2) return;
|
|
110
|
+
const fin = (buf[0] & 0x80) !== 0;
|
|
111
|
+
const rsv = buf[0] & 0x70;
|
|
112
|
+
const opcode = buf[0] & 0x0f;
|
|
113
|
+
const masked = (buf[1] & 0x80) !== 0;
|
|
114
|
+
let len = buf[1] & 0x7f;
|
|
115
|
+
let off = 2;
|
|
116
|
+
if (len === 126) {
|
|
117
|
+
if (buf.length < 4) return;
|
|
118
|
+
len = buf.readUInt16BE(2); off = 4;
|
|
119
|
+
} else if (len === 127) {
|
|
120
|
+
if (buf.length < 10) return;
|
|
121
|
+
const big = buf.readBigUInt64BE(2);
|
|
122
|
+
if (big > BigInt(MAX_MESSAGE_BYTES)) return closeWith(1009, "message too big");
|
|
123
|
+
len = Number(big); off = 10;
|
|
124
|
+
}
|
|
125
|
+
// A client MUST mask (RFC 6455 §5.1) and no extension was negotiated, so
|
|
126
|
+
// a set RSV bit is a protocol error, not a feature.
|
|
127
|
+
if (!masked || rsv !== 0) return closeWith(1002, "protocol error");
|
|
128
|
+
if (buf.length < off + 4 + len) return;
|
|
129
|
+
const mask = buf.subarray(off, off + 4);
|
|
130
|
+
const payload = Buffer.from(buf.subarray(off + 4, off + 4 + len));
|
|
131
|
+
buf = buf.subarray(off + 4 + len);
|
|
132
|
+
for (let i = 0; i < payload.length; i += 1) payload[i] ^= mask[i & 3];
|
|
133
|
+
|
|
134
|
+
if (opcode === 0x8) { // close: echo, then drop
|
|
135
|
+
if (!closeSent) { closeSent = true; sendFrame(0x8, payload.subarray(0, 2)); }
|
|
136
|
+
try { raw.end(); } catch { /* gone */ }
|
|
137
|
+
finish();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (opcode === 0x9) { sendFrame(0xa, payload); continue; } // ping → pong
|
|
141
|
+
if (opcode === 0xa) continue; // pong: a reply to nothing we sent
|
|
142
|
+
if (opcode === 0x1 || opcode === 0x2) {
|
|
143
|
+
if (partial) return closeWith(1002, "interleaved message");
|
|
144
|
+
partial = { opcode, chunks: [payload], bytes: payload.length };
|
|
145
|
+
} else if (opcode === 0x0) {
|
|
146
|
+
if (!partial) return closeWith(1002, "continuation without a start");
|
|
147
|
+
partial.chunks.push(payload);
|
|
148
|
+
partial.bytes += payload.length;
|
|
149
|
+
} else {
|
|
150
|
+
return closeWith(1002, "unknown opcode");
|
|
151
|
+
}
|
|
152
|
+
if (partial.bytes > MAX_MESSAGE_BYTES) return closeWith(1009, "message too big");
|
|
153
|
+
if (!fin) continue;
|
|
154
|
+
const message = partial.chunks.length === 1 ? partial.chunks[0] : Buffer.concat(partial.chunks);
|
|
155
|
+
const isText = partial.opcode === 0x1;
|
|
156
|
+
partial = null;
|
|
157
|
+
// Binary is not the protocol (serve.R: `if (is.raw(message)) return`).
|
|
158
|
+
// Dropped without error, exactly as there.
|
|
159
|
+
if (!isText) continue;
|
|
160
|
+
const text = message.toString("utf8");
|
|
161
|
+
messageHandlers.forEach((fn) => { try { fn(text); } catch { /* a handler must not break the read loop */ } });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
request,
|
|
167
|
+
get open() { return open; },
|
|
168
|
+
send: (text) => sendFrame(0x1, Buffer.from(String(text), "utf8")),
|
|
169
|
+
close: (code = 1000, reason = "") => closeWith(code, reason),
|
|
170
|
+
onMessage: (fn) => { messageHandlers.push(fn); },
|
|
171
|
+
onClose: (fn) => { closeHandlers.push(fn); },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** A server frame: never masked. */
|
|
176
|
+
function frame(opcode, payload) {
|
|
177
|
+
const body = Buffer.isBuffer(payload) ? payload : Buffer.from(String(payload), "utf8");
|
|
178
|
+
let head;
|
|
179
|
+
if (body.length < 126) head = Buffer.from([0x80 | opcode, body.length]);
|
|
180
|
+
else if (body.length < 65536) {
|
|
181
|
+
head = Buffer.alloc(4); head[0] = 0x80 | opcode; head[1] = 126; head.writeUInt16BE(body.length, 2);
|
|
182
|
+
} else {
|
|
183
|
+
head = Buffer.alloc(10); head[0] = 0x80 | opcode; head[1] = 127; head.writeBigUInt64BE(BigInt(body.length), 2);
|
|
184
|
+
}
|
|
185
|
+
return Buffer.concat([head, body]);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function statusText(status) {
|
|
189
|
+
return { 400: "Bad Request", 403: "Forbidden", 404: "Not Found", 429: "Too Many Requests" }[status] || "Error";
|
|
190
|
+
}
|