@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +8 -8
  3. package/src/migrations.ts +1 -34
  4. package/src/utils/image-convert.ts +1 -1
  5. package/src/utils/image-resize.ts +2 -2
  6. package/src/vendor/photon/LICENSE.md +201 -0
  7. package/src/vendor/photon/README.md +158 -0
  8. package/src/vendor/photon/index.d.ts +3013 -0
  9. package/src/vendor/photon/index.js +4461 -0
  10. package/src/vendor/photon/photon_rs_bg.wasm +0 -0
  11. package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
  12. package/src/vendor/photon/photon_rs_bg.wasm.d.ts +193 -0
  13. package/src/core/python-executor-display.test.ts +0 -42
  14. package/src/core/python-executor-lifecycle.test.ts +0 -99
  15. package/src/core/python-executor-mapping.test.ts +0 -41
  16. package/src/core/python-executor-per-call.test.ts +0 -49
  17. package/src/core/python-executor-session.test.ts +0 -103
  18. package/src/core/python-executor-streaming.test.ts +0 -77
  19. package/src/core/python-executor-timeout.test.ts +0 -35
  20. package/src/core/python-executor.lifecycle.test.ts +0 -139
  21. package/src/core/python-executor.result.test.ts +0 -49
  22. package/src/core/python-executor.test.ts +0 -180
  23. package/src/core/python-kernel-display.test.ts +0 -54
  24. package/src/core/python-kernel-env.test.ts +0 -138
  25. package/src/core/python-kernel-session.test.ts +0 -87
  26. package/src/core/python-kernel-ws.test.ts +0 -104
  27. package/src/core/python-kernel.lifecycle.test.ts +0 -249
  28. package/src/core/python-kernel.test.ts +0 -461
  29. package/src/core/python-modules.test.ts +0 -102
  30. package/src/core/python-prelude.test.ts +0 -140
  31. package/src/core/settings-manager-python.test.ts +0 -23
  32. package/src/core/streaming-output.test.ts +0 -26
  33. package/src/core/system-prompt.python.test.ts +0 -17
  34. package/src/core/tools/index.test.ts +0 -212
  35. package/src/core/tools/python-execution.test.ts +0 -68
  36. package/src/core/tools/python-fallback.test.ts +0 -72
  37. package/src/core/tools/python-renderer.test.ts +0 -36
  38. package/src/core/tools/python-tool-mode.test.ts +0 -43
  39. package/src/core/tools/python.test.ts +0 -121
  40. package/src/core/tools/schema-validation.test.ts +0 -530
  41. package/src/core/tools/web-scrapers/academic.test.ts +0 -239
  42. package/src/core/tools/web-scrapers/business.test.ts +0 -82
  43. package/src/core/tools/web-scrapers/dev-platforms.test.ts +0 -254
  44. package/src/core/tools/web-scrapers/documentation.test.ts +0 -85
  45. package/src/core/tools/web-scrapers/finance-media.test.ts +0 -144
  46. package/src/core/tools/web-scrapers/git-hosting.test.ts +0 -272
  47. package/src/core/tools/web-scrapers/media.test.ts +0 -138
  48. package/src/core/tools/web-scrapers/package-managers-2.test.ts +0 -199
  49. package/src/core/tools/web-scrapers/package-managers.test.ts +0 -171
  50. package/src/core/tools/web-scrapers/package-registries.test.ts +0 -259
  51. package/src/core/tools/web-scrapers/research.test.ts +0 -107
  52. package/src/core/tools/web-scrapers/security.test.ts +0 -103
  53. package/src/core/tools/web-scrapers/social-extended.test.ts +0 -192
  54. package/src/core/tools/web-scrapers/social.test.ts +0 -259
  55. package/src/core/tools/web-scrapers/stackexchange.test.ts +0 -120
  56. package/src/core/tools/web-scrapers/standards.test.ts +0 -122
  57. package/src/core/tools/web-scrapers/wikipedia.test.ts +0 -73
  58. package/src/core/tools/web-scrapers/youtube.test.ts +0 -198
  59. 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
- });