pi-repl-py 0.7.1 → 0.8.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/bridge.py +495 -0
- package/docs/ARCHITECTURE.md +71 -51
- package/index.ts +56 -44
- package/package.json +2 -1
- package/scripts/setup-venv.mjs +22 -9
- package/src/engine/index.ts +55 -136
- package/src/engine/kernel.ts +272 -496
- package/src/extension/prompt.ts +12 -17
- package/src/extension/session-engine.ts +1 -3
- package/src/engine/session.ts +0 -146
- package/src/engine/zmtp.ts +0 -239
- package/src/extension/tool-meta.ts +0 -8
package/src/extension/prompt.ts
CHANGED
|
@@ -1,26 +1,21 @@
|
|
|
1
|
+
import { DEFAULT_MAX_OUTPUT_CHARS } from "../engine/index.js";
|
|
2
|
+
|
|
1
3
|
export const executeToolDescription =
|
|
2
|
-
"Execute Python in a Jupyter notebook. Your workspace is
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
"Output is truncated to 45K with an explicit marker";
|
|
4
|
+
"Execute Python in a Jupyter notebook. Your workspace is one persistent live session: every " +
|
|
5
|
+
"cell runs in the same namespace, so variables, functions, classes, imports, and data defined " +
|
|
6
|
+
`in one cell stay available to every later cell. Output is truncated to ${DEFAULT_MAX_OUTPUT_CHARS} chars with an explicit marker`;
|
|
6
7
|
|
|
7
8
|
export const executePromptSnippet = "Execute Python in a Jupyter notebook (read, write, run, search, and more)";
|
|
8
9
|
|
|
9
|
-
export function buildPromptGuidelines(
|
|
10
|
+
export function buildPromptGuidelines(): string[] {
|
|
10
11
|
return [
|
|
11
12
|
"Write idiomatic Python.",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
[
|
|
19
|
-
"Preloaded helpers, use them as any loaded function or variable:",
|
|
20
|
-
...preloaded.map((line) => ` - ${line.replace(/\n/g, "\n ")}`),
|
|
21
|
-
].join("\n"),
|
|
22
|
-
]
|
|
23
|
-
: []),
|
|
13
|
+
"REPL-driven development: the live session's namespace is the single source of truth; reuse and extend existing objects rather than recreating or recomputing them.",
|
|
14
|
+
"Exploratory data analysis: look before acting (head, shape, slice, sample); print the minimal view that answers the question.",
|
|
15
|
+
"Quit thinking and look: read the exact lines/values before changing anything.",
|
|
16
|
+
"Minimal diff: change one thing at a time; if you didn't verify it, it ain't fixed.",
|
|
17
|
+
"Idempotent cells: safe to re-run.",
|
|
18
|
+
"If output begins with <repl_engine_reset>, the runtime rebuilt from a snapshot; trust but verify surviving state.",
|
|
24
19
|
"Be concise.",
|
|
25
20
|
];
|
|
26
21
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Lifecycle: boot, session binding, reset announcements. Recovery is a background quiet-gap job; the notice lands on the first cell after the restore.
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { DEFAULT_BOOT_TIMEOUT_MS, type HelperLoadResult, type RestoreResult } from "../engine/index.js";
|
|
4
4
|
|
|
5
5
|
/** Show enough names to orient, then count the rest (a revive can carry hundreds). */
|
|
6
6
|
function summarizeNames(names: readonly string[], limit: number): string {
|
|
@@ -34,8 +34,6 @@ export interface EngineLifecycleDeps<E extends RevivableEngine> {
|
|
|
34
34
|
/** startup: announce when the conversation has a saved past; cell: a mid-session rebuild announces immediately. */
|
|
35
35
|
export type AcquireOrigin = "startup" | "cell";
|
|
36
36
|
|
|
37
|
-
const DEFAULT_BOOT_TIMEOUT_MS = 90_000;
|
|
38
|
-
|
|
39
37
|
function revivedNoticeBody(origin: AcquireOrigin): string {
|
|
40
38
|
const resumed = origin === "startup";
|
|
41
39
|
return resumed
|
package/src/engine/session.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// --- Jupyter over ZMTP: [<IDS|MSG>] sig h p m c; sig = hex(HMAC-SHA256(key, h||p||m||c)); ids are empty for client channels ---
|
|
2
|
-
|
|
3
|
-
import { createHmac, randomUUID } from "node:crypto";
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
5
|
-
|
|
6
|
-
export interface ConnectionFile {
|
|
7
|
-
ip: string;
|
|
8
|
-
transport: "tcp" | "ipc";
|
|
9
|
-
shell_port: number;
|
|
10
|
-
iopub_port: number;
|
|
11
|
-
stdin_port: number;
|
|
12
|
-
control_port: number;
|
|
13
|
-
hb_port: number;
|
|
14
|
-
key: string;
|
|
15
|
-
signature_scheme: string;
|
|
16
|
-
kernel_name?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function readConnectionFile(path: string): ConnectionFile {
|
|
20
|
-
return JSON.parse(readFileSync(path, "utf8")) as ConnectionFile;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const DELIM = Buffer.from("<IDS|MSG>");
|
|
24
|
-
/** The protocol version ipykernel 7 advertises; we send the same on our own headers. */
|
|
25
|
-
const PROTOCOL_VERSION = "5.3";
|
|
26
|
-
|
|
27
|
-
export interface JupyterHeader {
|
|
28
|
-
msg_id: string;
|
|
29
|
-
msg_type: string;
|
|
30
|
-
username: string;
|
|
31
|
-
session: string;
|
|
32
|
-
date: string;
|
|
33
|
-
version: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface ParsedMessage {
|
|
37
|
-
msg_id: string;
|
|
38
|
-
msg_type: string;
|
|
39
|
-
header: JupyterHeader;
|
|
40
|
-
parent: Record<string, unknown>;
|
|
41
|
-
metadata: Record<string, unknown>;
|
|
42
|
-
content: Record<string, unknown>;
|
|
43
|
-
signatureOk: boolean;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function pack(obj: unknown): Buffer {
|
|
47
|
-
return Buffer.from(JSON.stringify(obj));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class JupyterSession {
|
|
51
|
-
readonly sessionId: string;
|
|
52
|
-
readonly username: string;
|
|
53
|
-
private counter = 0;
|
|
54
|
-
private readonly key: Buffer;
|
|
55
|
-
|
|
56
|
-
constructor(opts: { key: string; sessionId?: string; username?: string }) {
|
|
57
|
-
this.key = Buffer.from(opts.key, "utf8");
|
|
58
|
-
this.sessionId = opts.sessionId ?? randomUUID();
|
|
59
|
-
this.username = opts.username ?? "pi-repl";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
nextMsgId(): string {
|
|
63
|
-
// --- ids only need uniqueness; a monotone counter over a session id keeps them short ---
|
|
64
|
-
return `${this.sessionId}_${process.pid}_${this.counter++}`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private sign(parts: Buffer[]): Buffer {
|
|
68
|
-
if (this.key.length === 0) return Buffer.alloc(0);
|
|
69
|
-
const hmac = createHmac("sha256", this.key);
|
|
70
|
-
for (const part of parts) hmac.update(part);
|
|
71
|
-
return Buffer.from(hmac.digest("hex"), "ascii");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
buildFrames(
|
|
75
|
-
msgType: string,
|
|
76
|
-
content: Record<string, unknown>,
|
|
77
|
-
parent?: JupyterHeader | null,
|
|
78
|
-
msgId?: string,
|
|
79
|
-
): Buffer[] {
|
|
80
|
-
const header: JupyterHeader = {
|
|
81
|
-
msg_id: msgId ?? this.nextMsgId(),
|
|
82
|
-
msg_type: msgType,
|
|
83
|
-
username: this.username,
|
|
84
|
-
session: this.sessionId,
|
|
85
|
-
date: new Date().toISOString(),
|
|
86
|
-
version: PROTOCOL_VERSION,
|
|
87
|
-
};
|
|
88
|
-
const h = pack(header);
|
|
89
|
-
const p = pack(parent ?? {});
|
|
90
|
-
const m = pack({});
|
|
91
|
-
const c = pack(content);
|
|
92
|
-
const signature = this.sign([h, p, m, c]);
|
|
93
|
-
return [DELIM, signature, h, p, m, c];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
parseMessage(frames: Buffer[]): ParsedMessage | null {
|
|
97
|
-
// --- indexOf uses ===; frames are distinct Buffers, so match by value ---
|
|
98
|
-
const delimIdx = frames.findIndex((f) => f.equals(DELIM));
|
|
99
|
-
if (delimIdx < 0) return null;
|
|
100
|
-
const rest = frames.slice(delimIdx + 1);
|
|
101
|
-
if (rest.length < 5) return null;
|
|
102
|
-
const [signature, h, p, m, c] = rest;
|
|
103
|
-
const expected = this.sign([h, p, m, c]);
|
|
104
|
-
const signatureOk = this.key.length === 0 || signature.equals(expected);
|
|
105
|
-
try {
|
|
106
|
-
const header = JSON.parse(h.toString("utf8")) as JupyterHeader;
|
|
107
|
-
const content = JSON.parse(c.toString("utf8")) as Record<string, unknown>;
|
|
108
|
-
const metadata = JSON.parse(m.toString("utf8")) as Record<string, unknown>;
|
|
109
|
-
const parent = JSON.parse(p.toString("utf8")) as Record<string, unknown>;
|
|
110
|
-
return { msg_id: header.msg_id, msg_type: header.msg_type, header, parent, metadata, content, signatureOk };
|
|
111
|
-
} catch {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function executeRequest(code: string, silent: boolean): Record<string, unknown> {
|
|
118
|
-
// --- store_history off: IPython's In/Out pins every result and can't be reclaimed from cells (62MB → 400+MB); the display hook still publishes results ---
|
|
119
|
-
return {
|
|
120
|
-
code,
|
|
121
|
-
silent,
|
|
122
|
-
store_history: false,
|
|
123
|
-
user_expressions: {},
|
|
124
|
-
allow_stdin: false,
|
|
125
|
-
stop_on_error: true,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** A payload the kernel publishes back to us with a private MIME key. */
|
|
130
|
-
export const SNAPSHOT_MIME = "application/vnd.pi-repl.snapshot+json";
|
|
131
|
-
export const RESTORE_MIME = "application/vnd.pi-repl.restore+json";
|
|
132
|
-
export const NAMES_MIME = "application/vnd.pi-repl.names+json";
|
|
133
|
-
|
|
134
|
-
/** Route-gate: drop malformed and unsigned traffic — anything that fails the kernel's HMAC is not the kernel. */
|
|
135
|
-
export function isTrustedMessage(msg: ParsedMessage | null): msg is ParsedMessage {
|
|
136
|
-
return msg !== null && msg.signatureOk;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function readPayload(content: Record<string, unknown>, mime: string): string | null {
|
|
140
|
-
const data = content.data;
|
|
141
|
-
if (data && typeof data === "object") {
|
|
142
|
-
const value = (data as Record<string, unknown>)[mime];
|
|
143
|
-
if (typeof value === "string") return value;
|
|
144
|
-
}
|
|
145
|
-
return null;
|
|
146
|
-
}
|
package/src/engine/zmtp.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
// --- ZMTP 3.0 by hand (bun can't load libzmq's bindings); DEALER shell/control, SUB iopub; greeting 0xff..0x7f + READY each side ---
|
|
2
|
-
|
|
3
|
-
import { connect, type Socket } from "node:net";
|
|
4
|
-
|
|
5
|
-
const GREETING_SIGNATURE = Buffer.from([0xff, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x7f]);
|
|
6
|
-
const NULL_MECHANISM = Buffer.concat([Buffer.from("NULL"), Buffer.alloc(16)]);
|
|
7
|
-
|
|
8
|
-
function buildGreeting(): Buffer {
|
|
9
|
-
return Buffer.concat([
|
|
10
|
-
GREETING_SIGNATURE,
|
|
11
|
-
Buffer.from([3, 0]), // version 3.0
|
|
12
|
-
NULL_MECHANISM,
|
|
13
|
-
Buffer.from([0]), // as-server: we are the connecting socket
|
|
14
|
-
Buffer.alloc(31), // filler
|
|
15
|
-
]);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const FRAME_MORE = 0x01;
|
|
19
|
-
const FRAME_LONG = 0x02;
|
|
20
|
-
const GREETING_LENGTH = 64;
|
|
21
|
-
|
|
22
|
-
export function encodeFrame(body: Uint8Array, more: boolean): Buffer {
|
|
23
|
-
const flags = more ? FRAME_MORE : 0;
|
|
24
|
-
if (body.length <= 255) {
|
|
25
|
-
const out = Buffer.allocUnsafe(2 + body.length);
|
|
26
|
-
out[0] = flags;
|
|
27
|
-
out[1] = body.length;
|
|
28
|
-
Buffer.from(body).copy(out, 2);
|
|
29
|
-
return out;
|
|
30
|
-
}
|
|
31
|
-
const out = Buffer.allocUnsafe(9 + body.length);
|
|
32
|
-
out[0] = flags | FRAME_LONG;
|
|
33
|
-
out.writeUInt32BE(0, 1); // length is 64-bit; we never exceed 2^32
|
|
34
|
-
out.writeUInt32BE(body.length, 5);
|
|
35
|
-
Buffer.from(body).copy(out, 9);
|
|
36
|
-
return out;
|
|
37
|
-
}
|
|
38
|
-
/** Incremental parser: `current` persists across feed() and accumulation is once-per-frame (avoid O(n²)). */
|
|
39
|
-
export class ZmtpFrameParser {
|
|
40
|
-
private chunks: Buffer[] = [];
|
|
41
|
-
private total = 0;
|
|
42
|
-
private current: Buffer[] = []; // frames of the in-progress message
|
|
43
|
-
|
|
44
|
-
feed(chunk: Uint8Array): Buffer[][] {
|
|
45
|
-
if (chunk.length > 0) {
|
|
46
|
-
this.chunks.push(Buffer.from(chunk));
|
|
47
|
-
this.total += chunk.length;
|
|
48
|
-
}
|
|
49
|
-
const messages: Buffer[][] = [];
|
|
50
|
-
for (;;) {
|
|
51
|
-
if (this.total < 1) break;
|
|
52
|
-
const flags = this.peekBytes(1)[0];
|
|
53
|
-
const long = (flags & FRAME_LONG) !== 0;
|
|
54
|
-
const headerLen = long ? 9 : 2;
|
|
55
|
-
if (this.total < headerLen) break;
|
|
56
|
-
const header = this.peekBytes(headerLen);
|
|
57
|
-
const length = long ? header.readUInt32BE(5) : header[1];
|
|
58
|
-
if (this.total < headerLen + length) break;
|
|
59
|
-
const frame = this.take(headerLen + length);
|
|
60
|
-
// `take` returns a subarray (or a fresh concat for multi-chunk frames); never mutated, so no copy
|
|
61
|
-
this.current.push(frame.subarray(headerLen));
|
|
62
|
-
if ((flags & FRAME_MORE) === 0) {
|
|
63
|
-
messages.push(this.current);
|
|
64
|
-
this.current = [];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return messages;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private peekBytes(n: number): Buffer {
|
|
71
|
-
if (this.chunks[0].length >= n) return this.chunks[0].subarray(0, n);
|
|
72
|
-
const parts: Buffer[] = [];
|
|
73
|
-
let need = n;
|
|
74
|
-
for (const c of this.chunks) {
|
|
75
|
-
const t = Math.min(c.length, need);
|
|
76
|
-
parts.push(c.subarray(0, t));
|
|
77
|
-
need -= t;
|
|
78
|
-
if (need === 0) break;
|
|
79
|
-
}
|
|
80
|
-
return Buffer.concat(parts);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private take(n: number): Buffer {
|
|
84
|
-
const first = this.chunks[0];
|
|
85
|
-
if (first.length >= n) {
|
|
86
|
-
const out = first.subarray(0, n);
|
|
87
|
-
if (first.length === n) this.chunks.shift();
|
|
88
|
-
else this.chunks[0] = first.subarray(n);
|
|
89
|
-
this.total -= n;
|
|
90
|
-
return out;
|
|
91
|
-
}
|
|
92
|
-
const parts: Buffer[] = [];
|
|
93
|
-
let need = n;
|
|
94
|
-
for (const c of this.chunks) {
|
|
95
|
-
const t = Math.min(c.length, need);
|
|
96
|
-
parts.push(c.subarray(0, t));
|
|
97
|
-
need -= t;
|
|
98
|
-
if (need === 0) break;
|
|
99
|
-
}
|
|
100
|
-
let left = n;
|
|
101
|
-
while (left > 0) {
|
|
102
|
-
const c = this.chunks[0];
|
|
103
|
-
if (c.length <= left) {
|
|
104
|
-
this.chunks.shift();
|
|
105
|
-
left -= c.length;
|
|
106
|
-
} else {
|
|
107
|
-
this.chunks[0] = c.subarray(left);
|
|
108
|
-
left = 0;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
this.total -= n;
|
|
112
|
-
return Buffer.concat(parts);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export type ZmtpSocketType = "DEALER" | "SUB";
|
|
117
|
-
|
|
118
|
-
interface ReadReady {
|
|
119
|
-
resolve(): void;
|
|
120
|
-
reject(error: Error): void;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export class ZmtpSocket {
|
|
124
|
-
private socket?: Socket;
|
|
125
|
-
private parser = new ZmtpFrameParser();
|
|
126
|
-
private readyResolve?: ReadReady;
|
|
127
|
-
private closed = false;
|
|
128
|
-
onMessage?: (frames: Buffer[]) => void;
|
|
129
|
-
onClose?: () => void;
|
|
130
|
-
|
|
131
|
-
private constructor(socket: Socket) {
|
|
132
|
-
this.socket = socket;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
static connect(opts: { host: string; port: number; socketType: ZmtpSocketType }): Promise<ZmtpSocket> {
|
|
136
|
-
const socket = connect({ host: opts.host, port: opts.port });
|
|
137
|
-
const z = new ZmtpSocket(socket);
|
|
138
|
-
// --- the peer's 64-byte greeting is not frame-formatted; collect it before the parser sees bytes ---
|
|
139
|
-
let greeting = Buffer.alloc(0);
|
|
140
|
-
|
|
141
|
-
socket.on("data", (chunk) => {
|
|
142
|
-
if (greeting.length < GREETING_LENGTH) {
|
|
143
|
-
const take = Math.min(chunk.length, GREETING_LENGTH - greeting.length);
|
|
144
|
-
greeting = Buffer.concat([greeting, chunk.subarray(0, take)]);
|
|
145
|
-
chunk = chunk.subarray(take);
|
|
146
|
-
if (greeting.length === GREETING_LENGTH) {
|
|
147
|
-
const sig = greeting.subarray(0, GREETING_SIGNATURE.length);
|
|
148
|
-
if (!sig.equals(GREETING_SIGNATURE)) {
|
|
149
|
-
z.failHandshake(
|
|
150
|
-
new Error(`ZMTP peer at ${opts.host}:${opts.port} sent an unexpected signature (${sig.toString("hex")})`),
|
|
151
|
-
);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
z.send([buildReadyMetadata(opts.socketType)]);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (greeting.length === GREETING_LENGTH) {
|
|
158
|
-
for (const message of z.parser.feed(chunk)) {
|
|
159
|
-
const ready = z.readyResolve;
|
|
160
|
-
if (ready) {
|
|
161
|
-
// --- the first frame after the greeting is the peer's READY ---
|
|
162
|
-
z.readyResolve = undefined;
|
|
163
|
-
ready.resolve();
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
z.deliver(message);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
socket.on("error", (error) => {
|
|
171
|
-
if (z.readyResolve) {
|
|
172
|
-
z.failHandshake(new Error(`ZMTP connection to ${opts.host}:${opts.port} failed: ${error.message}`));
|
|
173
|
-
}
|
|
174
|
-
// --- node always follows an error with 'close', which handles teardown ---
|
|
175
|
-
});
|
|
176
|
-
socket.on("close", () => {
|
|
177
|
-
if (z.closed) return;
|
|
178
|
-
z.closed = true;
|
|
179
|
-
if (z.readyResolve) {
|
|
180
|
-
z.failHandshake(new Error(`ZMTP connection to ${opts.host}:${opts.port} closed during handshake`));
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
z.onClose?.();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
return new Promise<ZmtpSocket>((resolve, reject) => {
|
|
187
|
-
z.readyResolve = { resolve: () => resolve(z), reject };
|
|
188
|
-
socket.on("connect", () => {
|
|
189
|
-
// --- full greeting in one write; the peer may split its reply ---
|
|
190
|
-
socket.write(buildGreeting());
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private failHandshake(error: Error): void {
|
|
196
|
-
const ready = this.readyResolve;
|
|
197
|
-
if (!ready) return;
|
|
198
|
-
this.readyResolve = undefined;
|
|
199
|
-
ready.reject(error);
|
|
200
|
-
this.close();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private deliver(message: Buffer[]): void {
|
|
204
|
-
this.onMessage?.(message);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
send(frames: Uint8Array[]): void {
|
|
208
|
-
for (let i = 0; i < frames.length; i++) {
|
|
209
|
-
this.socket?.write(encodeFrame(frames[i], i < frames.length - 1));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
subscribe(topic: Uint8Array): void {
|
|
214
|
-
this.send([Buffer.concat([Buffer.from([0x01]), topic])]);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
close(): void {
|
|
218
|
-
this.closed = true;
|
|
219
|
-
this.socket?.destroy();
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
get isClosed(): boolean {
|
|
223
|
-
return this.closed;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** The READY metadata frame: `\x05READY` + Socket-Type + Identity properties. */
|
|
228
|
-
function buildReadyMetadata(socketType: ZmtpSocketType): Buffer {
|
|
229
|
-
const type = Buffer.from(socketType);
|
|
230
|
-
const body = Buffer.concat([
|
|
231
|
-
Buffer.from("\x05READY"),
|
|
232
|
-
Buffer.from("\x0bSocket-Type"),
|
|
233
|
-
Buffer.from([0, 0, 0, type.length]),
|
|
234
|
-
type,
|
|
235
|
-
Buffer.from("\x08Identity"),
|
|
236
|
-
Buffer.from([0, 0, 0, 0]), // empty identity: the routing id lives in ZMTP, not Jupyter
|
|
237
|
-
]);
|
|
238
|
-
return body;
|
|
239
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { buildPromptGuidelines, executePromptSnippet, executeToolDescription } from "./prompt.js";
|
|
2
|
-
|
|
3
|
-
export const EXECUTE_DESCRIPTION = executeToolDescription;
|
|
4
|
-
export const EXECUTE_PROMPT_SNIPPET = executePromptSnippet;
|
|
5
|
-
|
|
6
|
-
export function buildExecutePromptGuidelines(): string[] {
|
|
7
|
-
return buildPromptGuidelines([]);
|
|
8
|
-
}
|