@oh-my-pi/pi-coding-agent 5.6.77 → 5.7.68
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/CHANGELOG.md +15 -0
- package/package.json +8 -8
- package/src/migrations.ts +1 -34
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +2 -2
- package/src/vendor/photon/LICENSE.md +201 -0
- package/src/vendor/photon/README.md +158 -0
- package/src/vendor/photon/index.d.ts +3013 -0
- package/src/vendor/photon/index.js +4461 -0
- package/src/vendor/photon/photon_rs_bg.wasm +0 -0
- package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
- package/src/vendor/photon/photon_rs_bg.wasm.d.ts +193 -0
- package/src/core/python-executor-display.test.ts +0 -42
- package/src/core/python-executor-lifecycle.test.ts +0 -99
- package/src/core/python-executor-mapping.test.ts +0 -41
- package/src/core/python-executor-per-call.test.ts +0 -49
- package/src/core/python-executor-session.test.ts +0 -103
- package/src/core/python-executor-streaming.test.ts +0 -77
- package/src/core/python-executor-timeout.test.ts +0 -35
- package/src/core/python-executor.lifecycle.test.ts +0 -139
- package/src/core/python-executor.result.test.ts +0 -49
- package/src/core/python-executor.test.ts +0 -180
- package/src/core/python-kernel-display.test.ts +0 -54
- package/src/core/python-kernel-env.test.ts +0 -138
- package/src/core/python-kernel-session.test.ts +0 -87
- package/src/core/python-kernel-ws.test.ts +0 -104
- package/src/core/python-kernel.lifecycle.test.ts +0 -249
- package/src/core/python-kernel.test.ts +0 -461
- package/src/core/python-modules.test.ts +0 -102
- package/src/core/python-prelude.test.ts +0 -140
- package/src/core/settings-manager-python.test.ts +0 -23
- package/src/core/streaming-output.test.ts +0 -26
- package/src/core/system-prompt.python.test.ts +0 -17
- package/src/core/tools/index.test.ts +0 -212
- package/src/core/tools/python-execution.test.ts +0 -68
- package/src/core/tools/python-fallback.test.ts +0 -72
- package/src/core/tools/python-renderer.test.ts +0 -36
- package/src/core/tools/python-tool-mode.test.ts +0 -43
- package/src/core/tools/python.test.ts +0 -121
- package/src/core/tools/schema-validation.test.ts +0 -530
- package/src/core/tools/web-scrapers/academic.test.ts +0 -239
- package/src/core/tools/web-scrapers/business.test.ts +0 -82
- package/src/core/tools/web-scrapers/dev-platforms.test.ts +0 -254
- package/src/core/tools/web-scrapers/documentation.test.ts +0 -85
- package/src/core/tools/web-scrapers/finance-media.test.ts +0 -144
- package/src/core/tools/web-scrapers/git-hosting.test.ts +0 -272
- package/src/core/tools/web-scrapers/media.test.ts +0 -138
- package/src/core/tools/web-scrapers/package-managers-2.test.ts +0 -199
- package/src/core/tools/web-scrapers/package-managers.test.ts +0 -171
- package/src/core/tools/web-scrapers/package-registries.test.ts +0 -259
- package/src/core/tools/web-scrapers/research.test.ts +0 -107
- package/src/core/tools/web-scrapers/security.test.ts +0 -103
- package/src/core/tools/web-scrapers/social-extended.test.ts +0 -192
- package/src/core/tools/web-scrapers/social.test.ts +0 -259
- package/src/core/tools/web-scrapers/stackexchange.test.ts +0 -120
- package/src/core/tools/web-scrapers/standards.test.ts +0 -122
- package/src/core/tools/web-scrapers/wikipedia.test.ts +0 -73
- package/src/core/tools/web-scrapers/youtube.test.ts +0 -198
- package/src/discovery/helpers.test.ts +0 -131
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { deserializeWebSocketMessage, type JupyterMessage, serializeWebSocketMessage } from "./python-kernel";
|
|
3
|
-
|
|
4
|
-
const encoder = new TextEncoder();
|
|
5
|
-
|
|
6
|
-
function buildFrame(message: Omit<JupyterMessage, "buffers">, buffers: Uint8Array[] = []): ArrayBuffer {
|
|
7
|
-
const msgBytes = encoder.encode(JSON.stringify(message));
|
|
8
|
-
const offsetCount = 1 + buffers.length;
|
|
9
|
-
const headerSize = 4 + offsetCount * 4;
|
|
10
|
-
|
|
11
|
-
let totalSize = headerSize + msgBytes.length;
|
|
12
|
-
for (const buffer of buffers) {
|
|
13
|
-
totalSize += buffer.length;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const frame = new ArrayBuffer(totalSize);
|
|
17
|
-
const view = new DataView(frame);
|
|
18
|
-
const bytes = new Uint8Array(frame);
|
|
19
|
-
|
|
20
|
-
view.setUint32(0, offsetCount, true);
|
|
21
|
-
view.setUint32(4, headerSize, true);
|
|
22
|
-
bytes.set(msgBytes, headerSize);
|
|
23
|
-
|
|
24
|
-
let offset = headerSize + msgBytes.length;
|
|
25
|
-
for (let i = 0; i < buffers.length; i++) {
|
|
26
|
-
view.setUint32(4 + (i + 1) * 4, offset, true);
|
|
27
|
-
bytes.set(buffers[i], offset);
|
|
28
|
-
offset += buffers[i].length;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return frame;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
describe("deserializeWebSocketMessage", () => {
|
|
35
|
-
it("parses offset tables and buffers", () => {
|
|
36
|
-
const message = {
|
|
37
|
-
channel: "iopub",
|
|
38
|
-
header: {
|
|
39
|
-
msg_id: "msg-1",
|
|
40
|
-
session: "session-1",
|
|
41
|
-
username: "omp",
|
|
42
|
-
date: "2024-01-01T00:00:00Z",
|
|
43
|
-
msg_type: "stream",
|
|
44
|
-
version: "5.5",
|
|
45
|
-
},
|
|
46
|
-
parent_header: {},
|
|
47
|
-
metadata: {},
|
|
48
|
-
content: { text: "hello" },
|
|
49
|
-
};
|
|
50
|
-
const buffer = new Uint8Array([1, 2, 3]);
|
|
51
|
-
const frame = buildFrame(message, [buffer]);
|
|
52
|
-
|
|
53
|
-
const parsed = deserializeWebSocketMessage(frame);
|
|
54
|
-
|
|
55
|
-
expect(parsed).not.toBeNull();
|
|
56
|
-
expect(parsed?.header.msg_id).toBe("msg-1");
|
|
57
|
-
expect(parsed?.content).toEqual({ text: "hello" });
|
|
58
|
-
expect(parsed?.buffers?.[0]).toEqual(buffer);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("returns null for invalid frames", () => {
|
|
62
|
-
const headerSize = 8;
|
|
63
|
-
const bytes = encoder.encode("not-json");
|
|
64
|
-
const frame = new ArrayBuffer(headerSize + bytes.length);
|
|
65
|
-
const view = new DataView(frame);
|
|
66
|
-
const data = new Uint8Array(frame);
|
|
67
|
-
view.setUint32(0, 1, true);
|
|
68
|
-
view.setUint32(4, headerSize, true);
|
|
69
|
-
data.set(bytes, headerSize);
|
|
70
|
-
|
|
71
|
-
expect(deserializeWebSocketMessage(frame)).toBeNull();
|
|
72
|
-
const emptyFrame = new ArrayBuffer(4);
|
|
73
|
-
new DataView(emptyFrame).setUint32(0, 0, true);
|
|
74
|
-
expect(deserializeWebSocketMessage(emptyFrame)).toBeNull();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("serializeWebSocketMessage", () => {
|
|
79
|
-
it("round trips message payloads", () => {
|
|
80
|
-
const message: JupyterMessage = {
|
|
81
|
-
channel: "shell",
|
|
82
|
-
header: {
|
|
83
|
-
msg_id: "msg-2",
|
|
84
|
-
session: "session-2",
|
|
85
|
-
username: "omp",
|
|
86
|
-
date: "2024-02-01T00:00:00Z",
|
|
87
|
-
msg_type: "execute_request",
|
|
88
|
-
version: "5.5",
|
|
89
|
-
},
|
|
90
|
-
parent_header: { parent: "root" },
|
|
91
|
-
metadata: { tag: "meta" },
|
|
92
|
-
content: { code: "print('hi')" },
|
|
93
|
-
buffers: [new Uint8Array([9, 8, 7])],
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const frame = serializeWebSocketMessage(message);
|
|
97
|
-
const parsed = deserializeWebSocketMessage(frame);
|
|
98
|
-
|
|
99
|
-
expect(parsed).not.toBeNull();
|
|
100
|
-
expect(parsed?.header.msg_type).toBe("execute_request");
|
|
101
|
-
expect(parsed?.content).toEqual({ code: "print('hi')" });
|
|
102
|
-
expect(parsed?.buffers?.[0]).toEqual(new Uint8Array([9, 8, 7]));
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import type { Subprocess } from "bun";
|
|
6
|
-
import { PythonKernel } from "./python-kernel";
|
|
7
|
-
|
|
8
|
-
type SpawnOptions = Parameters<typeof Bun.spawn>[1];
|
|
9
|
-
|
|
10
|
-
type FetchCall = { url: string; init?: RequestInit };
|
|
11
|
-
|
|
12
|
-
type FetchResponse = {
|
|
13
|
-
ok: boolean;
|
|
14
|
-
status: number;
|
|
15
|
-
json: () => Promise<unknown>;
|
|
16
|
-
text: () => Promise<string>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
type MockEnvironment = {
|
|
20
|
-
fetchCalls: FetchCall[];
|
|
21
|
-
spawnCalls: { cmd: string[]; options: SpawnOptions }[];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
type MessageEventPayload = { data: ArrayBuffer };
|
|
25
|
-
|
|
26
|
-
type WebSocketHandler = (event: unknown) => void;
|
|
27
|
-
|
|
28
|
-
type WebSocketMessageHandler = (event: MessageEventPayload) => void;
|
|
29
|
-
|
|
30
|
-
class FakeWebSocket {
|
|
31
|
-
static OPEN = 1;
|
|
32
|
-
static CLOSED = 3;
|
|
33
|
-
static instances: FakeWebSocket[] = [];
|
|
34
|
-
|
|
35
|
-
readyState = FakeWebSocket.OPEN;
|
|
36
|
-
binaryType = "arraybuffer";
|
|
37
|
-
url: string;
|
|
38
|
-
sent: ArrayBuffer[] = [];
|
|
39
|
-
|
|
40
|
-
onopen: WebSocketHandler | null = null;
|
|
41
|
-
onerror: WebSocketHandler | null = null;
|
|
42
|
-
onclose: WebSocketHandler | null = null;
|
|
43
|
-
onmessage: WebSocketMessageHandler | null = null;
|
|
44
|
-
|
|
45
|
-
constructor(url: string) {
|
|
46
|
-
this.url = url;
|
|
47
|
-
FakeWebSocket.instances.push(this);
|
|
48
|
-
queueMicrotask(() => {
|
|
49
|
-
this.onopen?.(undefined);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
send(data: ArrayBuffer): void {
|
|
54
|
-
this.sent.push(data);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
close(): void {
|
|
58
|
-
this.readyState = FakeWebSocket.CLOSED;
|
|
59
|
-
this.onclose?.(undefined);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const createResponse = (options: { ok: boolean; status?: number; json?: unknown; text?: string }): FetchResponse => {
|
|
64
|
-
return {
|
|
65
|
-
ok: options.ok,
|
|
66
|
-
status: options.status ?? (options.ok ? 200 : 500),
|
|
67
|
-
json: async () => options.json ?? {},
|
|
68
|
-
text: async () => options.text ?? "",
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const createTempDir = () => mkdtempSync(join(tmpdir(), "omp-python-kernel-"));
|
|
73
|
-
|
|
74
|
-
const createFakeProcess = (): Subprocess => {
|
|
75
|
-
const exited = new Promise<number>(() => undefined);
|
|
76
|
-
return { pid: 999999, exited } as Subprocess;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
describe("PythonKernel gateway lifecycle", () => {
|
|
80
|
-
const originalFetch = globalThis.fetch;
|
|
81
|
-
const originalWebSocket = globalThis.WebSocket;
|
|
82
|
-
const originalSpawn = Bun.spawn;
|
|
83
|
-
const originalSleep = Bun.sleep;
|
|
84
|
-
const originalWhich = Bun.which;
|
|
85
|
-
const originalExecute = PythonKernel.prototype.execute;
|
|
86
|
-
const originalGatewayUrl = process.env.OMP_PYTHON_GATEWAY_URL;
|
|
87
|
-
const originalGatewayToken = process.env.OMP_PYTHON_GATEWAY_TOKEN;
|
|
88
|
-
const originalBunEnv = process.env.BUN_ENV;
|
|
89
|
-
|
|
90
|
-
let tempDir: string;
|
|
91
|
-
let env: MockEnvironment;
|
|
92
|
-
|
|
93
|
-
beforeEach(() => {
|
|
94
|
-
tempDir = createTempDir();
|
|
95
|
-
env = { fetchCalls: [], spawnCalls: [] };
|
|
96
|
-
|
|
97
|
-
process.env.BUN_ENV = "test";
|
|
98
|
-
delete process.env.OMP_PYTHON_GATEWAY_URL;
|
|
99
|
-
delete process.env.OMP_PYTHON_GATEWAY_TOKEN;
|
|
100
|
-
|
|
101
|
-
FakeWebSocket.instances = [];
|
|
102
|
-
globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket;
|
|
103
|
-
|
|
104
|
-
Bun.spawn = ((cmd: string[] | string, options?: SpawnOptions) => {
|
|
105
|
-
const normalized = Array.isArray(cmd) ? cmd : [cmd];
|
|
106
|
-
env.spawnCalls.push({ cmd: normalized, options: options ?? {} });
|
|
107
|
-
return createFakeProcess();
|
|
108
|
-
}) as typeof Bun.spawn;
|
|
109
|
-
|
|
110
|
-
Bun.sleep = (async () => undefined) as typeof Bun.sleep;
|
|
111
|
-
|
|
112
|
-
Bun.which = (() => "/usr/bin/python") as typeof Bun.which;
|
|
113
|
-
|
|
114
|
-
Object.defineProperty(PythonKernel.prototype, "execute", {
|
|
115
|
-
value: (async () => ({
|
|
116
|
-
status: "ok",
|
|
117
|
-
cancelled: false,
|
|
118
|
-
timedOut: false,
|
|
119
|
-
stdinRequested: false,
|
|
120
|
-
})) as typeof PythonKernel.prototype.execute,
|
|
121
|
-
configurable: true,
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
afterEach(() => {
|
|
126
|
-
if (tempDir) {
|
|
127
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (originalBunEnv === undefined) {
|
|
131
|
-
delete process.env.BUN_ENV;
|
|
132
|
-
} else {
|
|
133
|
-
process.env.BUN_ENV = originalBunEnv;
|
|
134
|
-
}
|
|
135
|
-
if (originalGatewayUrl === undefined) {
|
|
136
|
-
delete process.env.OMP_PYTHON_GATEWAY_URL;
|
|
137
|
-
} else {
|
|
138
|
-
process.env.OMP_PYTHON_GATEWAY_URL = originalGatewayUrl;
|
|
139
|
-
}
|
|
140
|
-
if (originalGatewayToken === undefined) {
|
|
141
|
-
delete process.env.OMP_PYTHON_GATEWAY_TOKEN;
|
|
142
|
-
} else {
|
|
143
|
-
process.env.OMP_PYTHON_GATEWAY_TOKEN = originalGatewayToken;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
globalThis.fetch = originalFetch;
|
|
147
|
-
globalThis.WebSocket = originalWebSocket;
|
|
148
|
-
|
|
149
|
-
Bun.spawn = originalSpawn;
|
|
150
|
-
Bun.sleep = originalSleep;
|
|
151
|
-
Bun.which = originalWhich;
|
|
152
|
-
Object.defineProperty(PythonKernel.prototype, "execute", { value: originalExecute, configurable: true });
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("starts local gateway, polls readiness, interrupts, and shuts down", async () => {
|
|
156
|
-
let kernelspecAttempts = 0;
|
|
157
|
-
globalThis.fetch = (async (input: string | URL, init?: RequestInit) => {
|
|
158
|
-
const url = String(input);
|
|
159
|
-
env.fetchCalls.push({ url, init });
|
|
160
|
-
|
|
161
|
-
if (url.endsWith("/api/kernelspecs")) {
|
|
162
|
-
kernelspecAttempts += 1;
|
|
163
|
-
const ok = kernelspecAttempts >= 2;
|
|
164
|
-
return createResponse({ ok }) as unknown as Response;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (url.endsWith("/api/kernels") && init?.method === "POST") {
|
|
168
|
-
return createResponse({ ok: true, json: { id: "kernel-123" } }) as unknown as Response;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return createResponse({ ok: true }) as unknown as Response;
|
|
172
|
-
}) as typeof fetch;
|
|
173
|
-
|
|
174
|
-
const kernel = await PythonKernel.start({ cwd: tempDir, useSharedGateway: false });
|
|
175
|
-
|
|
176
|
-
expect(env.spawnCalls).toHaveLength(1);
|
|
177
|
-
expect(env.spawnCalls[0].cmd).toEqual(
|
|
178
|
-
expect.arrayContaining([
|
|
179
|
-
"-m",
|
|
180
|
-
"kernel_gateway",
|
|
181
|
-
"--KernelGatewayApp.allow_origin=*",
|
|
182
|
-
"--JupyterApp.answer_yes=true",
|
|
183
|
-
]),
|
|
184
|
-
);
|
|
185
|
-
expect(env.fetchCalls.filter((call) => call.url.endsWith("/api/kernelspecs"))).toHaveLength(2);
|
|
186
|
-
expect(env.fetchCalls.some((call) => call.url.endsWith("/api/kernels") && call.init?.method === "POST")).toBe(
|
|
187
|
-
true,
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
await kernel.interrupt();
|
|
191
|
-
expect(env.fetchCalls.some((call) => call.url.includes("/interrupt") && call.init?.method === "POST")).toBe(true);
|
|
192
|
-
expect(FakeWebSocket.instances[0]?.sent.length).toBe(1);
|
|
193
|
-
|
|
194
|
-
await kernel.shutdown();
|
|
195
|
-
expect(env.fetchCalls.some((call) => call.init?.method === "DELETE")).toBe(true);
|
|
196
|
-
expect(kernel.isAlive()).toBe(false);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("throws when gateway readiness never succeeds", async () => {
|
|
200
|
-
const originalNow = Date.now;
|
|
201
|
-
let now = 0;
|
|
202
|
-
Date.now = () => {
|
|
203
|
-
now += 1000;
|
|
204
|
-
return now;
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
globalThis.fetch = (async (input: string | URL, init?: RequestInit) => {
|
|
209
|
-
const url = String(input);
|
|
210
|
-
env.fetchCalls.push({ url, init });
|
|
211
|
-
if (url.endsWith("/api/kernelspecs")) {
|
|
212
|
-
return createResponse({ ok: false, status: 503 }) as unknown as Response;
|
|
213
|
-
}
|
|
214
|
-
return createResponse({ ok: true }) as unknown as Response;
|
|
215
|
-
}) as typeof fetch;
|
|
216
|
-
|
|
217
|
-
await expect(PythonKernel.start({ cwd: tempDir, useSharedGateway: false })).rejects.toThrow(
|
|
218
|
-
"Kernel gateway failed to start",
|
|
219
|
-
);
|
|
220
|
-
expect(env.spawnCalls).toHaveLength(3);
|
|
221
|
-
} finally {
|
|
222
|
-
Date.now = originalNow;
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it("does not throw when shutdown API fails", async () => {
|
|
227
|
-
let kernelspecAttempts = 0;
|
|
228
|
-
globalThis.fetch = (async (input: string | URL, init?: RequestInit) => {
|
|
229
|
-
const url = String(input);
|
|
230
|
-
env.fetchCalls.push({ url, init });
|
|
231
|
-
if (url.endsWith("/api/kernelspecs")) {
|
|
232
|
-
kernelspecAttempts += 1;
|
|
233
|
-
const ok = kernelspecAttempts >= 1;
|
|
234
|
-
return createResponse({ ok }) as unknown as Response;
|
|
235
|
-
}
|
|
236
|
-
if (url.endsWith("/api/kernels") && init?.method === "POST") {
|
|
237
|
-
return createResponse({ ok: true, json: { id: "kernel-456" } }) as unknown as Response;
|
|
238
|
-
}
|
|
239
|
-
if (init?.method === "DELETE") {
|
|
240
|
-
throw new Error("delete failed");
|
|
241
|
-
}
|
|
242
|
-
return createResponse({ ok: true }) as unknown as Response;
|
|
243
|
-
}) as typeof fetch;
|
|
244
|
-
|
|
245
|
-
const kernel = await PythonKernel.start({ cwd: tempDir });
|
|
246
|
-
|
|
247
|
-
await expect(kernel.shutdown()).resolves.toBeUndefined();
|
|
248
|
-
});
|
|
249
|
-
});
|