@xiaoso/dsh-tool-plus 0.1.16 → 0.1.17-beta.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/lib/{abort-DZ54CaKU.mjs → abort-BTblp0Rt.mjs} +2 -2
- package/lib/{alibaba-coding-plan-DODpkcqi.mjs → alibaba-coding-plan-t3R4YKCB.mjs} +3 -3
- package/lib/{alibaba-token-plan-C6lpKCda.mjs → alibaba-token-plan-CO4djG1y.mjs} +4 -4
- package/lib/{alibaba-token-plan-CHGyWOfX.mjs → alibaba-token-plan-CUjYJX0-.mjs} +1 -1
- package/lib/{anthropic-Dv_GGX6g.mjs → anthropic-nf7w6so1.mjs} +48 -21
- package/lib/{api-key-validation-MnMf_ihX.mjs → api-key-validation-C_tZ7Y9a.mjs} +2 -2
- package/lib/behavior-BwvTUtag.mjs +111 -0
- package/lib/{browser-render-BZXt548F.mjs → browser-render-BQaEVK_n.mjs} +4 -4
- package/lib/{reference-CtvM4zLU.mjs → bundled-references-CrQ4dQcT.mjs} +57 -4
- package/lib/cascade-BWjRTD1q.mjs +30265 -0
- package/lib/{cline-pass-BT9CBEOj.mjs → cline-pass-Bjm-O8GG.mjs} +2 -2
- package/lib/cloudflare-CKXRJ0l9.mjs +37 -0
- package/lib/{cloudflare-ai-gateway-DReN6ohJ.mjs → cloudflare-ai-gateway-BpnBSjwV.mjs} +8 -4
- package/lib/cloudflare-workers-ai-odj9XswL.mjs +32 -0
- package/lib/collapse-_nkb2dn7.mjs +1945 -0
- package/lib/{coreweave-C82HrLpb.mjs → coreweave-CGDZOD8-.mjs} +1 -1
- package/lib/{coreweave-C68UgRQO.mjs → coreweave-DwffcgW5.mjs} +4 -4
- package/lib/cursor-BgOuV7Qf.mjs +794 -0
- package/lib/{cursor-_gHHq-Dq.mjs → cursor-CF1iHHbv.mjs} +68 -19
- package/lib/{cursor-proto-Cr_WEMuv.mjs → cursor-proto-BuNTjX3L.mjs} +823 -23
- package/lib/{devin-QpfTSl9H.mjs → devin-7OEeEb80.mjs} +112 -31
- package/lib/{devin-proto-DYhp4Ty6.mjs → devin-proto-C-QreeAv.mjs} +71 -5
- package/lib/{dom-DG3sPESc.mjs → dom-BmbZv4v9.mjs} +598 -182
- package/lib/{error-O63pC8zg.mjs → error-8LVo7tug.mjs} +725 -104
- package/lib/factory-droid-ChhmsGWb.mjs +71 -0
- package/lib/{fs-promises-shim-B2YFWkcd.mjs → fs-promises-shim-CANAxFWh.mjs} +10 -1
- package/lib/{github-copilot-BMEcw8N1.mjs → github-copilot-QwevdT30.mjs} +4 -4
- package/lib/{github-copilot-headers-DnjGEbko.mjs → github-copilot-headers-CAUZ0rUD.mjs} +138 -26
- package/lib/{gitlab-duo-DnHqJjFY.mjs → gitlab-duo-DigV7k_o.mjs} +2 -2
- package/lib/{google-antigravity-B1mSckrK.mjs → google-antigravity-D-oe0r8A.mjs} +6 -7
- package/lib/{google-gemini-cli-DJwThCCn.mjs → google-gemini-cli-CQBHgGJT.mjs} +5 -6
- package/lib/{google-oauth-shared-BctLpVUN.mjs → google-oauth-shared-CvgVIUwJ.mjs} +2 -2
- package/lib/google-validation-CdvRj4ec.mjs +126 -0
- package/lib/id-EJwVLDHE.mjs +2548 -0
- package/lib/index.mjs +248 -258
- package/lib/{kilo-BEgEwMJQ.mjs → kilo-5r6OffCa.mjs} +2 -2
- package/lib/{kimi-T9NldEIJ.mjs → kimi-DOOtbXmm.mjs} +9 -10
- package/lib/{launch-BIWNyrBB.mjs → launch-DkAjib7h.mjs} +1 -1
- package/lib/{markit-Dvp2JPdT.mjs → markit-CCcyeAds.mjs} +359 -140
- package/lib/{models-DnXMYGcJ.mjs → models-DCpDT_im.mjs} +199203 -112275
- package/lib/{muse-code-CNvqqAp6.mjs → muse-code-CYf8zj_B.mjs} +5 -5
- package/lib/{openai-codex-D5-CyTxa.mjs → openai-codex-Cj-TvSFh.mjs} +13 -10
- package/lib/{perplexity-A2PEoGaC.mjs → perplexity-Dh_Qgsmx.mjs} +68 -25
- package/lib/{pkce-BYjmugmw.mjs → pkce-DO-jBJLV.mjs} +1 -1
- package/lib/pricing-DusBEhwm.mjs +593 -0
- package/lib/{readability-uGhqb4GC.mjs → readability-XHK0o3bv.mjs} +287 -27
- package/lib/{scrapers-C1ujjV8k.mjs → scrapers-B6PADOVm.mjs} +13 -13
- package/lib/slugify-ynYWBIA5.mjs +11 -0
- package/lib/snowflake-B_jap6lz.mjs +666 -0
- package/lib/snowflake-Cu1ZyOp6.mjs +151 -0
- package/lib/{src-AELUUb5U.mjs → src-BhinCSpi.mjs} +1172 -458
- package/lib/{stream-AVOoe5Hj.mjs → stream-D2ivg3GH.mjs} +3740 -5311
- package/lib/{turndown-D6fof1OX.mjs → turndown-Vya8SavL.mjs} +82 -30
- package/lib/types/tools/hashline/native/index.d.ts +2 -2
- package/lib/types/tools/hashline/native/policy.d.ts +19 -21
- package/lib/types/tools/omp/session/agent-storage.d.ts +3 -3
- package/lib/types/tools/shared/bun-named-shim.d.ts +5 -0
- package/lib/{types-BLrLohwK.mjs → types-tH1v9uRm.mjs} +2 -2
- package/lib/{utils-cZX3f_Ls.mjs → utils-C_Ncya1r.mjs} +1737 -509
- package/lib/utils-D2tK38xq.mjs +34006 -0
- package/lib/{utils-I4I8azHN.mjs → utils-DrDwmNtu.mjs} +8 -3
- package/lib/{xai-oauth-CbeldUSV.mjs → xai-oauth-DLLzX6my.mjs} +4 -4
- package/lib/{xiaomi-Dl3VSs04.mjs → xiaomi-CqK-KivK.mjs} +2 -2
- package/lib/{zai-FyjGSypr.mjs → zai-DoJ2xPIH.mjs} +2 -2
- package/lib/{zip-DRrycScC.mjs → zip-DG1mUV6X.mjs} +1 -1
- package/package.json +8 -8
- package/lib/amazon-bedrock-Dx0rr9dY.mjs +0 -996
- package/lib/anthropic-ClCw0PV_.mjs +0 -3624
- package/lib/anthropic-wire-lbGAFzYb.mjs +0 -927
- package/lib/azure-openai-responses-WE_bgFWp.mjs +0 -285
- package/lib/bundled-references-DY3EJxZV.mjs +0 -56
- package/lib/cloudflare-ai-gateway-CrMa97Ap.mjs +0 -44
- package/lib/collapse-CkMNRNr7.mjs +0 -2304
- package/lib/cursor-C6jaIJDg.mjs +0 -283
- package/lib/cursor-D-XZYDf_.mjs +0 -4544
- package/lib/deterministic-id-CUEIMSqR.mjs +0 -17
- package/lib/devin-ExGIghmx.mjs +0 -69
- package/lib/devin-u0ljw5P6.mjs +0 -628
- package/lib/effort-BdtQMN-_.mjs +0 -23
- package/lib/event-stream-BKEtb45Y.mjs +0 -167
- package/lib/gemini-headers-Co9cVvex.mjs +0 -54
- package/lib/google-auth-qAN-6DMW.mjs +0 -232
- package/lib/google-gemini-cli-B2Aw1S5w.mjs +0 -844
- package/lib/google-lTxuq28n.mjs +0 -38
- package/lib/google-shared-Y01j9xOr.mjs +0 -856
- package/lib/google-validation-BEkNs-Bx.mjs +0 -18
- package/lib/google-vertex-DcM-FRC5.mjs +0 -99
- package/lib/hosts-00rjRzUD.mjs +0 -203
- package/lib/id-AL5obQzR.mjs +0 -71
- package/lib/idle-iterator-D0ZXEcUq.mjs +0 -366
- package/lib/ollama-6R0oXZks.mjs +0 -574
- package/lib/openai-codex-responses-C8bnBWOe.mjs +0 -3232
- package/lib/openai-completions-DNr3mUsn.mjs +0 -1430
- package/lib/openai-configuration-update-xMYUjaK_.mjs +0 -150
- package/lib/openai-reasoning-fallback-Ju-5pSR8.mjs +0 -380
- package/lib/openai-responses-CjpafAEr.mjs +0 -861
- package/lib/protobuf-BMgDYu_U.mjs +0 -717
- package/lib/provider-response-SqKSkkUc.mjs +0 -22
- package/lib/proxy-DxvCQOCA.mjs +0 -230
- package/lib/request-debug-BwPOqN_j.mjs +0 -183
- package/lib/sanitize-text-BCf5KmCf.mjs +0 -33
- package/lib/sse-debug-B-tkLCPW.mjs +0 -11
- package/lib/stream-markup-healing-CN3RsqG9.mjs +0 -267
- package/lib/strict-tool-validation-CBTz1dAA.mjs +0 -105
- package/lib/taxonomy-DSIM99rS.mjs +0 -15392
- package/lib/thinking-ngMwf_1Y.mjs +0 -336
- package/lib/type-guards-CttAANEx.mjs +0 -11
- package/lib/utils-CE3DSebh.mjs +0 -11763
- package/lib/vision-guard-BCpXWUj3.mjs +0 -77
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
2
|
__installBunShim();
|
|
3
|
-
import { n as fs_promises_shim_exports } from "./fs-promises-shim-
|
|
3
|
+
import { n as fs_promises_shim_exports } from "./fs-promises-shim-CANAxFWh.mjs";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import { Process } from "@oh-my-pi/pi-natives";
|
|
6
6
|
import * as path$1 from "node:path";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
|
+
import "node:timers/promises";
|
|
10
|
+
import { expandWindowsLongPath } from "@oh-my-pi/pi-natives/path";
|
|
11
|
+
import { parseEnv } from "node:util";
|
|
12
|
+
import JSON5 from "json5";
|
|
9
13
|
import { parse, stringify } from "yaml";
|
|
10
14
|
import * as fsp from "fs/promises";
|
|
11
|
-
import * as crypto$1 from "node:crypto";
|
|
12
15
|
import { createHash, randomUUID } from "node:crypto";
|
|
13
16
|
import { gzipSync } from "node:zlib";
|
|
14
|
-
import JSON5 from "json5";
|
|
15
17
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
18
|
import { isPromise } from "node:util/types";
|
|
17
|
-
import inspector from "node:inspector";
|
|
18
|
-
import { isMainThread } from "node:worker_threads";
|
|
19
19
|
import * as tls from "node:tls";
|
|
20
20
|
//#region src/tools/bash/which.ts
|
|
21
21
|
/**
|
|
@@ -61,7 +61,7 @@ function findOnPath(command, env = process.env) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
//#endregion
|
|
64
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
64
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/abortable.ts
|
|
65
65
|
var AbortError$1 = class extends Error {
|
|
66
66
|
constructor(signal) {
|
|
67
67
|
assert(signal.aborted, "Abort signal must be aborted");
|
|
@@ -154,54 +154,19 @@ function once(fn) {
|
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
156
|
//#endregion
|
|
157
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
157
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/peek-file.ts
|
|
158
158
|
/**
|
|
159
159
|
* Read the first `maxBytes` of a file (offset 0) and pass that slice to `op`.
|
|
160
160
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* queue, falling back to a fresh allocation when the pool and queue are saturated
|
|
164
|
-
* or when `maxBytes` exceeds the pool slot size.
|
|
161
|
+
* Synchronous peeks reuse one growable `Uint8Array`; asynchronous peeks allocate
|
|
162
|
+
* their result window so callback-retained slices cannot be changed by a later read.
|
|
165
163
|
*/
|
|
166
|
-
/** Async pool slot size; larger peeks allocate ad hoc. */
|
|
167
|
-
const POOLED_BUFFER_SIZE = 512;
|
|
168
|
-
const ASYNC_POOL_SIZE = 10;
|
|
169
|
-
/** Cap waiter queue so heavy concurrency does not queue unbounded; overflow uses alloc. */
|
|
170
|
-
const MAX_ASYNC_WAITERS = 4;
|
|
171
164
|
const INITIAL_SYNC_BUFFER_SIZE = 1024;
|
|
172
|
-
const EMPTY_BUFFER =
|
|
173
|
-
const asyncPool = Array.from({ length: ASYNC_POOL_SIZE }, () => Buffer.allocUnsafe(POOLED_BUFFER_SIZE));
|
|
174
|
-
const availableAsyncPoolIndexes = Array.from({ length: ASYNC_POOL_SIZE }, (_, index) => index);
|
|
175
|
-
const asyncPoolWaiters = [];
|
|
165
|
+
const EMPTY_BUFFER = /* @__PURE__ */ new Uint8Array(0);
|
|
176
166
|
new Uint8Array(INITIAL_SYNC_BUFFER_SIZE);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (index !== void 0) return index;
|
|
181
|
-
if (asyncPoolWaiters.length >= MAX_ASYNC_WAITERS) return -1;
|
|
182
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
183
|
-
asyncPoolWaiters.push(resolve);
|
|
184
|
-
return promise;
|
|
185
|
-
}
|
|
186
|
-
function releaseAsyncPoolIndex(index) {
|
|
187
|
-
if (index < 0) return;
|
|
188
|
-
const waiter = asyncPoolWaiters.shift();
|
|
189
|
-
if (waiter) {
|
|
190
|
-
waiter(index);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
availableAsyncPoolIndexes.push(index);
|
|
194
|
-
}
|
|
195
|
-
async function withAsyncPoolBuffer(maxBytes, op) {
|
|
196
|
-
if (maxBytes <= 0) return op(EMPTY_BUFFER);
|
|
197
|
-
if (maxBytes > POOLED_BUFFER_SIZE) return op(Buffer.allocUnsafe(maxBytes));
|
|
198
|
-
const poolIndex = await acquireAsyncPoolIndex();
|
|
199
|
-
const buffer = poolIndex >= 0 ? asyncPool[poolIndex] : Buffer.allocUnsafe(maxBytes);
|
|
200
|
-
try {
|
|
201
|
-
return await op(buffer.subarray(0, maxBytes));
|
|
202
|
-
} finally {
|
|
203
|
-
releaseAsyncPoolIndex(poolIndex);
|
|
204
|
-
}
|
|
167
|
+
function allocateWindow(length) {
|
|
168
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
169
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
205
170
|
}
|
|
206
171
|
/**
|
|
207
172
|
* Like {@link peekFileSync} but uses async I/O.
|
|
@@ -210,16 +175,15 @@ async function peekFile(filePath, maxBytes, op) {
|
|
|
210
175
|
if (maxBytes <= 0) return op(EMPTY_BUFFER);
|
|
211
176
|
const fileHandle = await fs.promises.open(filePath, "r");
|
|
212
177
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
});
|
|
178
|
+
const buffer = allocateWindow(maxBytes);
|
|
179
|
+
const { bytesRead } = await fileHandle.read(buffer, 0, buffer.byteLength, 0);
|
|
180
|
+
return op(buffer.subarray(0, bytesRead));
|
|
217
181
|
} finally {
|
|
218
182
|
await fileHandle.close();
|
|
219
183
|
}
|
|
220
184
|
}
|
|
221
185
|
//#endregion
|
|
222
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
186
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/binary.ts
|
|
223
187
|
/**
|
|
224
188
|
* Content-based binary/text classification for files that are about to be
|
|
225
189
|
* decoded as UTF-8 text and shown to a model or user.
|
|
@@ -266,11 +230,11 @@ function isProbablyBinary(filePath, maxBytes = BINARY_SNIFF_BYTES) {
|
|
|
266
230
|
return peekFile(filePath, maxBytes, isProbablyBinaryHeader);
|
|
267
231
|
}
|
|
268
232
|
//#endregion
|
|
269
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
270
|
-
var version = "18.
|
|
233
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/package.json
|
|
234
|
+
var version = "18.12.6";
|
|
271
235
|
var engines = { "bun": ">=1.3.14" };
|
|
272
236
|
//#endregion
|
|
273
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
237
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/fs-error.ts
|
|
274
238
|
function isFsError(err) {
|
|
275
239
|
return err instanceof Error && "code" in err && typeof err.code === "string";
|
|
276
240
|
}
|
|
@@ -283,6 +247,16 @@ function isEnotdir(err) {
|
|
|
283
247
|
function hasFsCode(err, code) {
|
|
284
248
|
return isFsError(err) && err.code === code;
|
|
285
249
|
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/test-runtime.ts
|
|
252
|
+
const BUN_TEST_ENTRY_PATTERN = /[._](?:test|spec)\.[cm]?[jt]sx?$/;
|
|
253
|
+
/** True when the process is an explicitly marked test child or Bun is running a test entrypoint. */
|
|
254
|
+
function isBunTestRuntime() {
|
|
255
|
+
if (Bun.env.PI_TEST_RUNTIME === "1") return true;
|
|
256
|
+
return (Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test") && BUN_TEST_ENTRY_PATTERN.test(Bun.main);
|
|
257
|
+
}
|
|
258
|
+
/** Public homepage that inference gateways (OpenRouter, Vercel AI Gateway) credit omp traffic to. */
|
|
259
|
+
const APP_URL = "https://omp.sh/";
|
|
286
260
|
/** Version (e.g. "1.0.0") */
|
|
287
261
|
const VERSION = version;
|
|
288
262
|
/** Default User-Agent header string (e.g. "omp/17.2.12") */
|
|
@@ -341,6 +315,7 @@ function readProfileFromEnvSafe() {
|
|
|
341
315
|
return;
|
|
342
316
|
}
|
|
343
317
|
}
|
|
318
|
+
/** Profile-independent config root (~/.omp), shared by every omp profile. */
|
|
344
319
|
function getBaseConfigRoot() {
|
|
345
320
|
return path$1.join(os.homedir(), getConfigDirName());
|
|
346
321
|
}
|
|
@@ -373,26 +348,34 @@ function standardizeMacOSPath(p) {
|
|
|
373
348
|
} catch {}
|
|
374
349
|
return p;
|
|
375
350
|
}
|
|
351
|
+
/** Keep the current directory's spelling while expanding Windows 8.3 aliases. */
|
|
352
|
+
function standardizeProjectPath(p) {
|
|
353
|
+
return process.platform === "win32" ? expandWindowsLongPath(p) : standardizeMacOSPath(p);
|
|
354
|
+
}
|
|
376
355
|
let projectDir;
|
|
377
356
|
/** Get the project directory. */
|
|
378
357
|
function getProjectDir() {
|
|
379
|
-
if (projectDir === void 0)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
358
|
+
if (projectDir === void 0) {
|
|
359
|
+
let cwd;
|
|
360
|
+
try {
|
|
361
|
+
cwd = process.cwd();
|
|
362
|
+
} catch {
|
|
363
|
+
const candidates = [
|
|
364
|
+
process.env.PWD,
|
|
365
|
+
os.homedir(),
|
|
366
|
+
os.tmpdir()
|
|
367
|
+
];
|
|
368
|
+
for (const candidate of candidates) {
|
|
369
|
+
if (!candidate || !path$1.isAbsolute(candidate)) continue;
|
|
370
|
+
try {
|
|
371
|
+
process.chdir(candidate);
|
|
372
|
+
cwd = candidate;
|
|
373
|
+
break;
|
|
374
|
+
} catch {}
|
|
375
|
+
}
|
|
376
|
+
if (cwd === void 0) throw new Error("Unable to determine an accessible working directory");
|
|
394
377
|
}
|
|
395
|
-
|
|
378
|
+
projectDir = standardizeProjectPath(cwd);
|
|
396
379
|
}
|
|
397
380
|
return projectDir;
|
|
398
381
|
}
|
|
@@ -411,6 +394,7 @@ var DirResolver = class {
|
|
|
411
394
|
agentDir;
|
|
412
395
|
#rootDirs;
|
|
413
396
|
#agentDirs;
|
|
397
|
+
#baseRootDirs;
|
|
414
398
|
#rootCache = /* @__PURE__ */ new Map();
|
|
415
399
|
#agentCache = /* @__PURE__ */ new Map();
|
|
416
400
|
constructor(options = {}) {
|
|
@@ -423,7 +407,8 @@ var DirResolver = class {
|
|
|
423
407
|
let xdgData;
|
|
424
408
|
let xdgState;
|
|
425
409
|
let xdgCache;
|
|
426
|
-
|
|
410
|
+
const xdgPlatform = process.platform === "linux" || process.platform === "darwin";
|
|
411
|
+
if (xdgPlatform && isDefault) {
|
|
427
412
|
const resolveIf = (envVar) => {
|
|
428
413
|
const value = process.env[envVar];
|
|
429
414
|
if (!value) return void 0;
|
|
@@ -434,13 +419,22 @@ var DirResolver = class {
|
|
|
434
419
|
if (fs.existsSync(profilePath)) return profilePath;
|
|
435
420
|
return;
|
|
436
421
|
}
|
|
437
|
-
|
|
422
|
+
return fs.existsSync(appRoot) ? appRoot : void 0;
|
|
438
423
|
} catch {}
|
|
439
424
|
};
|
|
440
425
|
xdgData = resolveIf("XDG_DATA_HOME");
|
|
441
426
|
xdgState = resolveIf("XDG_STATE_HOME");
|
|
442
427
|
xdgCache = resolveIf("XDG_CACHE_HOME");
|
|
443
428
|
}
|
|
429
|
+
const resolveBase = (envVar) => {
|
|
430
|
+
if (!xdgPlatform) return void 0;
|
|
431
|
+
const value = process.env[envVar];
|
|
432
|
+
if (!value) return void 0;
|
|
433
|
+
try {
|
|
434
|
+
const appRoot = path$1.join(value, "omp");
|
|
435
|
+
return fs.existsSync(appRoot) ? appRoot : void 0;
|
|
436
|
+
} catch {}
|
|
437
|
+
};
|
|
444
438
|
this.#rootDirs = {
|
|
445
439
|
data: xdgData ?? this.configRoot,
|
|
446
440
|
state: xdgState ?? this.configRoot,
|
|
@@ -451,6 +445,16 @@ var DirResolver = class {
|
|
|
451
445
|
state: xdgState ?? this.agentDir,
|
|
452
446
|
cache: xdgCache ?? this.agentDir
|
|
453
447
|
};
|
|
448
|
+
const baseRoot = getBaseConfigRoot();
|
|
449
|
+
this.#baseRootDirs = {
|
|
450
|
+
data: resolveBase("XDG_DATA_HOME") ?? baseRoot,
|
|
451
|
+
state: resolveBase("XDG_STATE_HOME") ?? baseRoot,
|
|
452
|
+
cache: resolveBase("XDG_CACHE_HOME") ?? baseRoot
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/** Profile-independent config-root subdirectory, with optional XDG override. Shared across profiles. */
|
|
456
|
+
baseRootSubdir(subdir, xdg) {
|
|
457
|
+
return path$1.join(xdg ? this.#baseRootDirs[xdg] : getBaseConfigRoot(), subdir);
|
|
454
458
|
}
|
|
455
459
|
/** Config-root subdirectory, with optional XDG override. */
|
|
456
460
|
rootSubdir(subdir, xdg) {
|
|
@@ -528,10 +532,30 @@ function getConfigRootDir() {
|
|
|
528
532
|
function getAgentDir() {
|
|
529
533
|
return dirs.agentDir;
|
|
530
534
|
}
|
|
531
|
-
/**
|
|
535
|
+
/**
|
|
536
|
+
* Get the logs directory (~/.omp/logs). `bun test` processes get
|
|
537
|
+
* `<tmpdir>/omp-test-logs-<uid>` instead, so suites never write into the
|
|
538
|
+
* developer's real logs; the uid suffix (as for the test session-owners dir)
|
|
539
|
+
* keeps users of a shared `/tmp` from owning each other's directory.
|
|
540
|
+
*/
|
|
532
541
|
function getLogsDir() {
|
|
542
|
+
if (isBunTestRuntime()) {
|
|
543
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : os.userInfo().username;
|
|
544
|
+
return path$1.join(os.tmpdir(), `omp-test-logs-${uid}`);
|
|
545
|
+
}
|
|
533
546
|
return dirs.rootSubdir("logs", "state");
|
|
534
547
|
}
|
|
548
|
+
/**
|
|
549
|
+
* Local-timezone `YYYY-MM-DD` day key (zero-padded), formatted exactly like
|
|
550
|
+
* the rotating log sink's file naming: log files are named `omp.<day>.<pid>.log`
|
|
551
|
+
* with the LOCAL day, not the UTC day `toISOString()` yields. Anything that
|
|
552
|
+
* computes "today's" log path or matches same-day log files by name must use
|
|
553
|
+
* this key, or between local midnight and UTC midnight it points at files that
|
|
554
|
+
* do not exist (e.g. 00:00–08:00 in UTC+8).
|
|
555
|
+
*/
|
|
556
|
+
function localDay(date) {
|
|
557
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
558
|
+
}
|
|
535
559
|
/** Get the remote mount directory (~/.omp/remote). */
|
|
536
560
|
function getRemoteDir() {
|
|
537
561
|
return dirs.rootSubdir("remote", "data");
|
|
@@ -552,6 +576,10 @@ function getAutoQaDbPath() {
|
|
|
552
576
|
function getDocumentConversionCacheDir(agentDir) {
|
|
553
577
|
return dirs.agentSubdir(agentDir, path$1.join("cache", "document-conversions"), "cache");
|
|
554
578
|
}
|
|
579
|
+
/** Get the custom themes directory (~/.omp/agent/themes). */
|
|
580
|
+
function getCustomThemesDir(agentDir) {
|
|
581
|
+
return dirs.agentSubdir(agentDir, "themes");
|
|
582
|
+
}
|
|
555
583
|
/** Get the tools directory (~/.omp/agent/tools). */
|
|
556
584
|
function getToolsDir(agentDir) {
|
|
557
585
|
return dirs.agentSubdir(agentDir, "tools");
|
|
@@ -614,7 +642,38 @@ function getInstallId() {
|
|
|
614
642
|
return next;
|
|
615
643
|
}
|
|
616
644
|
//#endregion
|
|
617
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
645
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/path.ts
|
|
646
|
+
const WINDOWS_DRIVE_EXTENDED_PREFIX = /^\\\\[?]\\([A-Za-z]:[\\/].*)$/;
|
|
647
|
+
const WINDOWS_UNC_EXTENDED_PREFIX = /^\\\\[?]\\UNC[\\/]([^\\/]+)[\\/](.+)$/i;
|
|
648
|
+
const WINDOWS_DRIVE_EXTENDED_FORWARD_PREFIX = /^\/\/[?]\/([A-Za-z]:\/.*)$/;
|
|
649
|
+
const WINDOWS_UNC_EXTENDED_FORWARD_PREFIX = /^\/\/[?]\/UNC\/([^/]+)\/(.+)$/i;
|
|
650
|
+
const WINDOWS_DRIVE_NT_PREFIX = /^\\\\[?][?]\\([A-Za-z]:[\\/].*)$/;
|
|
651
|
+
const WINDOWS_UNC_NT_PREFIX = /^\\\\[?][?]\\UNC[\\/]([^\\/]+)[\\/](.+)$/i;
|
|
652
|
+
/** Removes Win32 extended-length prefixes before passing paths to Bun APIs. */
|
|
653
|
+
function stripWindowsExtendedLengthPathPrefix(filePath, platform = process.platform) {
|
|
654
|
+
if (platform !== "win32") return filePath;
|
|
655
|
+
const uncMatch = WINDOWS_UNC_EXTENDED_PREFIX.exec(filePath) ?? WINDOWS_UNC_NT_PREFIX.exec(filePath);
|
|
656
|
+
if (uncMatch) return `\\\\${uncMatch[1]}\\${uncMatch[2]}`;
|
|
657
|
+
const driveMatch = WINDOWS_DRIVE_EXTENDED_PREFIX.exec(filePath) ?? WINDOWS_DRIVE_NT_PREFIX.exec(filePath);
|
|
658
|
+
if (driveMatch) return driveMatch[1];
|
|
659
|
+
const forwardUncMatch = WINDOWS_UNC_EXTENDED_FORWARD_PREFIX.exec(filePath);
|
|
660
|
+
if (forwardUncMatch) return `//${forwardUncMatch[1]}/${forwardUncMatch[2]}`;
|
|
661
|
+
const forwardDriveMatch = WINDOWS_DRIVE_EXTENDED_FORWARD_PREFIX.exec(filePath);
|
|
662
|
+
if (forwardDriveMatch) return forwardDriveMatch[1];
|
|
663
|
+
return filePath;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Test whether a path is fully qualified and drive-independent.
|
|
667
|
+
* On Windows, requires a drive letter with separator (e.g. `C:\`) or UNC (`\\server\share` or `//server/share`).
|
|
668
|
+
* On POSIX, requires an absolute path.
|
|
669
|
+
*/
|
|
670
|
+
function isFullyQualifiedPath(filePath, platform = process.platform) {
|
|
671
|
+
if (!(platform === "win32" ? path$1.win32 : path$1.posix).isAbsolute(filePath)) return false;
|
|
672
|
+
if (platform === "win32") return /^[a-zA-Z]:[/\\]/.test(filePath) || /^[\\/]{2}[^\\/]/.test(filePath);
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/env.ts
|
|
618
677
|
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
619
678
|
/**
|
|
620
679
|
* Strict shell-identifier shape. Used for dotenv keys we accept into
|
|
@@ -672,49 +731,17 @@ function readLaunchEnv() {
|
|
|
672
731
|
readLaunchEnv();
|
|
673
732
|
const projectEnvNamesLoadedByOmp = /* @__PURE__ */ new Set();
|
|
674
733
|
/**
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
* unquoted values, and single/double/backtick quoting (a `#` inside quotes
|
|
678
|
-
* stays literal). Returns undefined for blank lines, comments, and malformed
|
|
679
|
-
* names.
|
|
680
|
-
*/
|
|
681
|
-
function parseEnvLine(line) {
|
|
682
|
-
const trimmed = line.trim();
|
|
683
|
-
if (!trimmed || trimmed.startsWith("#")) return void 0;
|
|
684
|
-
const eqIndex = trimmed.indexOf("=");
|
|
685
|
-
if (eqIndex === -1) return void 0;
|
|
686
|
-
let key = trimmed.slice(0, eqIndex).trim();
|
|
687
|
-
const exported = key.match(/^export[ \t]+(.*)$/);
|
|
688
|
-
if (exported) key = exported[1].trim();
|
|
689
|
-
if (!isValidEnvName(key)) return void 0;
|
|
690
|
-
const raw = trimmed.slice(eqIndex + 1).replace(/^[ \t]+/, "");
|
|
691
|
-
const quote = raw[0];
|
|
692
|
-
if (quote === "\"" || quote === "'" || quote === "`") {
|
|
693
|
-
let close = raw.indexOf(quote, 1);
|
|
694
|
-
while (close !== -1 && raw[close - 1] === "\\") close = raw.indexOf(quote, close + 1);
|
|
695
|
-
return {
|
|
696
|
-
key,
|
|
697
|
-
value: close === -1 ? raw.slice(1) : raw.slice(1, close)
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
const commentIndex = raw.search(/[ \t]#/);
|
|
701
|
-
return {
|
|
702
|
-
key,
|
|
703
|
-
value: (commentIndex === -1 ? raw : raw.slice(0, commentIndex)).trimEnd()
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Parses a .env file synchronously into key-value string pairs using
|
|
708
|
-
* {@link parseEnvLine} for Bun-compatible line semantics, then mirrors valid
|
|
734
|
+
* Parses a complete .env file with the runtime's dotenv grammar, then retains
|
|
735
|
+
* only shell-identifier names and spawn-safe values before mirroring valid
|
|
709
736
|
* `OMP_` variables to their `PI_` aliases.
|
|
710
737
|
*/
|
|
711
738
|
function parseEnvFile(filePath) {
|
|
712
739
|
const result = {};
|
|
713
740
|
try {
|
|
714
|
-
const
|
|
715
|
-
for (const
|
|
716
|
-
const
|
|
717
|
-
if (
|
|
741
|
+
const parsed = parseEnv(fs.readFileSync(filePath, "utf-8"));
|
|
742
|
+
for (const key in parsed) {
|
|
743
|
+
const value = parsed[key];
|
|
744
|
+
if (value !== void 0 && isValidEnvName(key) && isSafeEnvValue(value)) result[key] = value;
|
|
718
745
|
}
|
|
719
746
|
} catch {}
|
|
720
747
|
for (const k in result) if (k.startsWith("OMP_")) result[`PI_${k.slice(4)}`] = result[k];
|
|
@@ -768,12 +795,6 @@ function $envpos(name, defaultValue) {
|
|
|
768
795
|
if (Number.isNaN(parsed) || parsed <= 0) return defaultValue;
|
|
769
796
|
return parsed;
|
|
770
797
|
}
|
|
771
|
-
const BUN_TEST_ENTRY_PATTERN = /[._](?:test|spec)\.[cm]?[jt]sx?$/;
|
|
772
|
-
/** True when the process is an explicitly marked test child or Bun is running a test entrypoint. */
|
|
773
|
-
function isBunTestRuntime() {
|
|
774
|
-
if (Bun.env.PI_TEST_RUNTIME === "1") return true;
|
|
775
|
-
return (Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test") && BUN_TEST_ENTRY_PATTERN.test(Bun.main);
|
|
776
|
-
}
|
|
777
798
|
let terminalHeadless = isBunTestRuntime();
|
|
778
799
|
/**
|
|
779
800
|
* True when real-terminal side effects must be suppressed: stdout escape/frame
|
|
@@ -1550,11 +1571,6 @@ function installBunShim() {
|
|
|
1550
1571
|
}
|
|
1551
1572
|
//#endregion
|
|
1552
1573
|
//#region src/tools/shared/bun-named-shim.ts
|
|
1553
|
-
/**
|
|
1554
|
-
* DSH named-import shim for `import { ... } from "bun"` (pi-utils and other
|
|
1555
|
-
* always-bundled OMP packages). tsdown aliases "bun" to this module.
|
|
1556
|
-
* Runtime Bun global is installed separately by src/tools/shared/bun-shim.ts.
|
|
1557
|
-
*/
|
|
1558
1574
|
/** Bun.YAML (pi-utils frontmatter) — yaml package under the hood. */
|
|
1559
1575
|
const YAML = {
|
|
1560
1576
|
parse: (s) => parse(s),
|
|
@@ -1638,64 +1654,163 @@ var CookieMap = class {
|
|
|
1638
1654
|
}
|
|
1639
1655
|
};
|
|
1640
1656
|
//#endregion
|
|
1641
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
1642
|
-
/**
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1657
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/fs-open.ts
|
|
1658
|
+
/** Close-on-exec file opens for descriptors held past a child spawn. */
|
|
1659
|
+
/**
|
|
1660
|
+
* `O_CLOEXEC`, which neither Node nor Bun exposes in `fs.constants`.
|
|
1661
|
+
*
|
|
1662
|
+
* Kernel ABI values: `0o2000000` on Linux, `0x0100_0000` on Darwin. Windows has
|
|
1663
|
+
* no `exec` and its CRT handles are inherited only when a spawn explicitly asks
|
|
1664
|
+
* for it, so the bit is 0 there — as it is on any other platform, where adding
|
|
1665
|
+
* an unknown flag bit would be worse than leaving the descriptor inheritable.
|
|
1666
|
+
*/
|
|
1667
|
+
const O_CLOEXEC = process.platform === "linux" ? 524288 : process.platform === "darwin" ? 16777216 : 0;
|
|
1668
|
+
/**
|
|
1669
|
+
* `fs.openSync` with close-on-exec, matching Node's default.
|
|
1670
|
+
*
|
|
1671
|
+
* libuv adds `O_CLOEXEC` inside `uv__fs_open`, so on Node every `fs` descriptor
|
|
1672
|
+
* dies at `exec`. Bun's `fs.open*` did not before 1.4.3 (still allowed by the
|
|
1673
|
+
* `bun@>=1.4` pin), and descriptors it returns survive
|
|
1674
|
+
* into any child spawned through a path that does not close strays itself — the
|
|
1675
|
+
* bash tool's shell `fork`/`exec`s user commands and hands them the lot. Use
|
|
1676
|
+
* this for every descriptor kept open past such a spawn (session transcripts,
|
|
1677
|
+
* logs, spools); a child must never hold a writable handle to them (#13224).
|
|
1678
|
+
*
|
|
1679
|
+
* Flags are numeric only: the bit cannot be OR-ed into `"a"`/`"w+"` strings.
|
|
1680
|
+
*
|
|
1681
|
+
* @example
|
|
1682
|
+
* const fd = openCloexecSync(logPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND);
|
|
1683
|
+
*/
|
|
1684
|
+
function openCloexecSync(filePath, flags, mode) {
|
|
1685
|
+
return fs.openSync(filePath, flags | O_CLOEXEC, mode);
|
|
1686
|
+
}
|
|
1687
|
+
//#endregion
|
|
1688
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/logger/rotating-file.ts
|
|
1689
|
+
/** Process-local rotating file sink: local-day and size rotation with bounded retention. */
|
|
1690
|
+
/**
|
|
1691
|
+
* Synchronous append sink with local-day and size rotation plus bounded retention.
|
|
1692
|
+
*
|
|
1693
|
+
* Construction performs no filesystem I/O: the first file is created when the
|
|
1694
|
+
* first record is written out, so a process that never logs leaves nothing on
|
|
1695
|
+
* disk. Every file name embeds the owning process's suffix (its PID), so the
|
|
1696
|
+
* set of files this sink created is tracked in memory; files left by earlier
|
|
1697
|
+
* processes are pruned by name pattern in the logger, not by this sink.
|
|
1698
|
+
*
|
|
1699
|
+
* With {@link RotatingFileOptions.batch}, records are buffered and written in
|
|
1700
|
+
* one `write` per batch. Buffered bytes already count toward the active file's
|
|
1701
|
+
* size, and the buffer is written to its file before any rotation, so file
|
|
1702
|
+
* selection matches an unbatched sink record for record.
|
|
1703
|
+
*/
|
|
1649
1704
|
var RotatingFileSink = class {
|
|
1650
1705
|
#directory;
|
|
1651
1706
|
#filenamePrefix;
|
|
1652
1707
|
#filenameSuffix;
|
|
1653
|
-
#auditFile;
|
|
1654
1708
|
#maxBytes;
|
|
1655
1709
|
#maxFiles;
|
|
1656
|
-
#
|
|
1710
|
+
#batch;
|
|
1711
|
+
#onRotate;
|
|
1712
|
+
/** Files this sink created, oldest first. */
|
|
1713
|
+
#files = [];
|
|
1657
1714
|
#activeDay;
|
|
1658
1715
|
#activeIndex = 0;
|
|
1659
1716
|
#activePath;
|
|
1717
|
+
/** Size of the active file including buffered records not yet written. */
|
|
1660
1718
|
#activeBytes = 0;
|
|
1661
1719
|
#closed = false;
|
|
1720
|
+
#fd;
|
|
1721
|
+
/** Records for the active file not yet written, each ending in EOL. */
|
|
1722
|
+
#pending = "";
|
|
1723
|
+
#pendingBytes = 0;
|
|
1724
|
+
#flushTimer;
|
|
1662
1725
|
constructor(options) {
|
|
1663
1726
|
this.#directory = options.directory;
|
|
1664
1727
|
this.#filenamePrefix = options.filenamePrefix;
|
|
1665
1728
|
this.#filenameSuffix = options.filenameSuffix;
|
|
1666
|
-
this.#auditFile = options.auditFile;
|
|
1667
1729
|
this.#maxBytes = options.maxBytes;
|
|
1668
1730
|
this.#maxFiles = options.maxFiles;
|
|
1669
|
-
this.#
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1731
|
+
this.#batch = options.batch;
|
|
1732
|
+
this.#onRotate = options.onRotate;
|
|
1733
|
+
}
|
|
1734
|
+
#openFd(filePath) {
|
|
1735
|
+
this.#closeFd();
|
|
1736
|
+
this.#fd = openCloexecSync(filePath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND);
|
|
1737
|
+
try {
|
|
1738
|
+
this.#onRotate?.(filePath);
|
|
1739
|
+
} catch {}
|
|
1740
|
+
}
|
|
1741
|
+
#closeFd() {
|
|
1742
|
+
if (this.#fd !== void 0) {
|
|
1743
|
+
try {
|
|
1744
|
+
fs.closeSync(this.#fd);
|
|
1745
|
+
} catch {}
|
|
1746
|
+
this.#fd = void 0;
|
|
1676
1747
|
}
|
|
1677
1748
|
}
|
|
1678
|
-
/**
|
|
1679
|
-
|
|
1749
|
+
/**
|
|
1750
|
+
* Append one already-formatted log record. A batching sink buffers it unless
|
|
1751
|
+
* `flushNow` is set or the buffer reached its size threshold; either way the
|
|
1752
|
+
* whole buffer, this record included, is then written in order. `day` is the
|
|
1753
|
+
* record's `localDay` key, so callers that already took the record's `Date`
|
|
1754
|
+
* reuse it.
|
|
1755
|
+
*/
|
|
1756
|
+
write(line, flushNow = false, day = localDay(/* @__PURE__ */ new Date())) {
|
|
1680
1757
|
if (this.#closed) return;
|
|
1681
|
-
|
|
1682
|
-
this.#selectFile(this.#localDay(now));
|
|
1683
|
-
const activePath = this.#activePath;
|
|
1684
|
-
if (!activePath) return;
|
|
1685
|
-
this.#registerFile(activePath, now.getTime());
|
|
1758
|
+
this.#selectFile(day);
|
|
1686
1759
|
const record = `${line}${os.EOL}`;
|
|
1687
|
-
|
|
1688
|
-
this.#
|
|
1760
|
+
const bytes = Buffer.byteLength(record, "utf8");
|
|
1761
|
+
this.#pending += record;
|
|
1762
|
+
this.#pendingBytes += bytes;
|
|
1763
|
+
this.#activeBytes += bytes;
|
|
1764
|
+
const batch = this.#batch;
|
|
1765
|
+
if (flushNow || !batch || this.#pendingBytes >= batch.maxBytes) this.flush();
|
|
1766
|
+
else if (this.#flushTimer === void 0) {
|
|
1767
|
+
this.#flushTimer = setTimeout(this.#flushFromTimer, batch.intervalMs);
|
|
1768
|
+
this.#flushTimer.unref();
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
/** Write every buffered record to the active file now. */
|
|
1772
|
+
flush() {
|
|
1773
|
+
if (this.#flushTimer !== void 0) {
|
|
1774
|
+
clearTimeout(this.#flushTimer);
|
|
1775
|
+
this.#flushTimer = void 0;
|
|
1776
|
+
}
|
|
1777
|
+
const activePath = this.#activePath;
|
|
1778
|
+
if (this.#pendingBytes === 0 || !activePath) return;
|
|
1779
|
+
const buf = Buffer.from(this.#pending, "utf8");
|
|
1780
|
+
this.#pending = "";
|
|
1781
|
+
this.#pendingBytes = 0;
|
|
1782
|
+
if (this.#fd === void 0) {
|
|
1783
|
+
this.#registerFile(activePath);
|
|
1784
|
+
this.#openFd(activePath);
|
|
1785
|
+
}
|
|
1786
|
+
let off = 0;
|
|
1787
|
+
while (off < buf.length) {
|
|
1788
|
+
const written = fs.writeSync(this.#fd, buf, off);
|
|
1789
|
+
if (written <= 0) break;
|
|
1790
|
+
off += written;
|
|
1791
|
+
}
|
|
1689
1792
|
}
|
|
1690
|
-
|
|
1793
|
+
#flushFromTimer = () => {
|
|
1794
|
+
this.#flushTimer = void 0;
|
|
1795
|
+
try {
|
|
1796
|
+
this.flush();
|
|
1797
|
+
} catch {}
|
|
1798
|
+
};
|
|
1799
|
+
/** Write buffered records, then stop accepting records. */
|
|
1691
1800
|
close() {
|
|
1801
|
+
if (this.#closed) return;
|
|
1802
|
+
try {
|
|
1803
|
+
this.flush();
|
|
1804
|
+
} catch {}
|
|
1692
1805
|
this.#closed = true;
|
|
1693
|
-
|
|
1694
|
-
#localDay(date) {
|
|
1695
|
-
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
1806
|
+
this.#closeFd();
|
|
1696
1807
|
}
|
|
1697
1808
|
#selectFile(day) {
|
|
1698
|
-
|
|
1809
|
+
const dayChanged = day !== this.#activeDay;
|
|
1810
|
+
if (!dayChanged && this.#activeBytes <= this.#maxBytes) return;
|
|
1811
|
+
this.flush();
|
|
1812
|
+
this.#closeFd();
|
|
1813
|
+
if (dayChanged) {
|
|
1699
1814
|
this.#activeDay = day;
|
|
1700
1815
|
this.#activeIndex = 0;
|
|
1701
1816
|
this.#setActivePath(day, 0);
|
|
@@ -1714,46 +1829,120 @@ var RotatingFileSink = class {
|
|
|
1714
1829
|
this.#activeBytes = 0;
|
|
1715
1830
|
}
|
|
1716
1831
|
}
|
|
1717
|
-
#registerFile(filePath
|
|
1718
|
-
if (this.#files.
|
|
1719
|
-
|
|
1720
|
-
this.#files.push({
|
|
1721
|
-
date,
|
|
1722
|
-
name: filePath,
|
|
1723
|
-
hash
|
|
1724
|
-
});
|
|
1832
|
+
#registerFile(filePath) {
|
|
1833
|
+
if (this.#files.includes(filePath)) return;
|
|
1834
|
+
this.#files.push(filePath);
|
|
1725
1835
|
while (this.#files.length > this.#maxFiles) {
|
|
1726
1836
|
const removed = this.#files.shift();
|
|
1727
1837
|
if (!removed) break;
|
|
1728
1838
|
try {
|
|
1729
|
-
fs.rmSync(removed
|
|
1839
|
+
fs.rmSync(removed, { force: true });
|
|
1730
1840
|
} catch {}
|
|
1731
1841
|
}
|
|
1732
|
-
this.#writeAudit();
|
|
1733
|
-
}
|
|
1734
|
-
#readAudit() {
|
|
1735
|
-
try {
|
|
1736
|
-
const parsed = JSON.parse(fs.readFileSync(this.#auditFile, "utf8"));
|
|
1737
|
-
return Array.isArray(parsed.files) ? parsed.files.filter(isAuditEntry) : [];
|
|
1738
|
-
} catch {
|
|
1739
|
-
return [];
|
|
1740
|
-
}
|
|
1741
1842
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1843
|
+
};
|
|
1844
|
+
//#endregion
|
|
1845
|
+
//#region src/tools/shared/bun-ffi-shim.ts
|
|
1846
|
+
/**
|
|
1847
|
+
* DSH named-import shim for `bun:ffi` (pi-utils stderr-guard / process-name).
|
|
1848
|
+
* Both consumers degrade silently when dlopen is unavailable ("Never throws:
|
|
1849
|
+
* bun:ffi unavailability ... degrades silently"), so this shim returns null.
|
|
1850
|
+
*/
|
|
1851
|
+
const FFIType = {};
|
|
1852
|
+
function dlopen() {
|
|
1853
|
+
return null;
|
|
1854
|
+
}
|
|
1855
|
+
const ptr = () => 0;
|
|
1856
|
+
//#endregion
|
|
1857
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/stderr-guard.ts
|
|
1858
|
+
/**
|
|
1859
|
+
* Terminal stderr guard: keeps unmanaged fd-2 writes off the terminal while a
|
|
1860
|
+
* TUI owns the viewport.
|
|
1861
|
+
*
|
|
1862
|
+
* Native runtimes and libraries can write directly to file descriptor 2 at
|
|
1863
|
+
* arbitrary times. Examples include macOS libmalloc diagnostics and Bun HTTP
|
|
1864
|
+
* decompression diagnostics on Linux. Those bytes bypass the renderer and
|
|
1865
|
+
* paint straight into the viewport.
|
|
1866
|
+
*
|
|
1867
|
+
* While the TUI owns the terminal, dup fd 2 aside and dup2 a redirect target
|
|
1868
|
+
* over it; restore the saved fd whenever terminal ownership is released
|
|
1869
|
+
* (external editor, Ctrl+Z suspend, shutdown, crash restore). This mirrors
|
|
1870
|
+
* openai/codex#24459, but redirects to the omp log file instead of /dev/null
|
|
1871
|
+
* so diagnostics stay greppable and Bun native-crash reports (which abort
|
|
1872
|
+
* before any JS cleanup can restore fd 2) are preserved.
|
|
1873
|
+
*
|
|
1874
|
+
* Only dup/dup2 go through bun:ffi. fcntl is deliberately avoided: it is
|
|
1875
|
+
* variadic, and the arm64-darwin ABI passes variadic arguments on the stack,
|
|
1876
|
+
* so a fixed-arity FFI signature would read garbage for the third argument.
|
|
1877
|
+
*/
|
|
1878
|
+
const STDERR_FILENO = 2;
|
|
1879
|
+
let libcFdOpsCache;
|
|
1880
|
+
function libcFdOps() {
|
|
1881
|
+
if (libcFdOpsCache !== void 0) return libcFdOpsCache;
|
|
1882
|
+
libcFdOpsCache = null;
|
|
1883
|
+
if (process.platform === "win32") return null;
|
|
1884
|
+
const candidates = process.platform === "darwin" ? ["libSystem.B.dylib", "/usr/lib/libSystem.B.dylib"] : ["libc.so.6", "libc.so"];
|
|
1885
|
+
for (const candidate of candidates) try {
|
|
1886
|
+
libcFdOpsCache = dlopen(candidate, {
|
|
1887
|
+
dup: {
|
|
1888
|
+
args: [FFIType.i32],
|
|
1889
|
+
returns: FFIType.i32
|
|
1747
1890
|
},
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1891
|
+
dup2: {
|
|
1892
|
+
args: [FFIType.i32, FFIType.i32],
|
|
1893
|
+
returns: FFIType.i32
|
|
1894
|
+
}
|
|
1895
|
+
}).symbols;
|
|
1896
|
+
return libcFdOpsCache;
|
|
1897
|
+
} catch {}
|
|
1898
|
+
return libcFdOpsCache;
|
|
1899
|
+
}
|
|
1900
|
+
/** Saved dup of the real stderr while suppression is active, else null. */
|
|
1901
|
+
let savedStderrFd = null;
|
|
1902
|
+
/**
|
|
1903
|
+
* Point fd 2 at `logPath`, the log file the rotating sink is currently
|
|
1904
|
+
* writing.
|
|
1905
|
+
*
|
|
1906
|
+
* The logger calls this on every sink rotation. Without it fd 2 keeps the
|
|
1907
|
+
* descriptor opened at TUI start, so after local midnight raw stderr — this
|
|
1908
|
+
* process's and every child that inherited fd 2 — keeps landing in the
|
|
1909
|
+
* previous day's file, which the sink eventually prunes out from under it.
|
|
1910
|
+
*
|
|
1911
|
+
* The path is recorded even while suppression is inactive, so a later
|
|
1912
|
+
* {@link suppressTerminalStderr} redirects to the live file instead of
|
|
1913
|
+
* re-deriving a dated name. Best-effort: if the new file cannot be opened,
|
|
1914
|
+
* fd 2 keeps its current target.
|
|
1915
|
+
*/
|
|
1916
|
+
function setStderrRedirectTarget(logPath) {
|
|
1917
|
+
if (savedStderrFd === null) return;
|
|
1918
|
+
const libc = libcFdOps();
|
|
1919
|
+
if (!libc) return;
|
|
1920
|
+
let redirectFd;
|
|
1921
|
+
try {
|
|
1922
|
+
redirectFd = fs.openSync(logPath, "a");
|
|
1923
|
+
} catch {
|
|
1924
|
+
return;
|
|
1753
1925
|
}
|
|
1754
|
-
|
|
1926
|
+
libc.dup2(redirectFd, STDERR_FILENO);
|
|
1927
|
+
fs.closeSync(redirectFd);
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Re-point fd 2 at the saved terminal stderr. Safe to call unconditionally:
|
|
1931
|
+
* no-op when suppression is not active. Called at every terminal-ownership
|
|
1932
|
+
* release and by the postmortem fatal handlers before they print, so crash
|
|
1933
|
+
* reports reach the real terminal.
|
|
1934
|
+
*/
|
|
1935
|
+
function restoreTerminalStderr() {
|
|
1936
|
+
if (savedStderrFd === null) return;
|
|
1937
|
+
const saved = savedStderrFd;
|
|
1938
|
+
savedStderrFd = null;
|
|
1939
|
+
libcFdOps()?.dup2(saved, STDERR_FILENO);
|
|
1940
|
+
try {
|
|
1941
|
+
fs.closeSync(saved);
|
|
1942
|
+
} catch {}
|
|
1943
|
+
}
|
|
1755
1944
|
//#endregion
|
|
1756
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
1945
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/logger.ts
|
|
1757
1946
|
/**
|
|
1758
1947
|
* Centralized logger for omp.
|
|
1759
1948
|
*
|
|
@@ -1763,7 +1952,11 @@ var RotatingFileSink = class {
|
|
|
1763
1952
|
* transport so a process supervisor (pm2, journald, k8s) captures the logs.
|
|
1764
1953
|
*
|
|
1765
1954
|
* Each entry includes `process.pid` so concurrent omp instances stay
|
|
1766
|
-
* traceable.
|
|
1955
|
+
* traceable. The file is created on the first record written. Records are
|
|
1956
|
+
* batched — one write per second or per 64 KiB — while `warn`/`error` records
|
|
1957
|
+
* are written at once together with everything buffered before them; exit,
|
|
1958
|
+
* fatal, and signal paths flush the rest. `OMP_LOG_LEVEL` optionally limits
|
|
1959
|
+
* the levels written to the file (default: all).
|
|
1767
1960
|
*/
|
|
1768
1961
|
const logSinks = /* @__PURE__ */ new Set();
|
|
1769
1962
|
function emitToSinks(level, message, context) {
|
|
@@ -1779,7 +1972,12 @@ function emitToSinks(level, message, context) {
|
|
|
1779
1972
|
} catch {}
|
|
1780
1973
|
}
|
|
1781
1974
|
const PROCESS_LOG_PATTERN = /^omp\.(\d{4}-\d{2}-\d{2})\.(\d+)\.log(?:\.(\d+))?$/;
|
|
1975
|
+
/** Per-process audit files written by earlier releases; current sinks track rotations in memory. */
|
|
1782
1976
|
const PROCESS_AUDIT_PATTERN = /^\.omp\.(\d+)-audit\.json$/;
|
|
1977
|
+
/** Shared daily logs (plain, size-rolled, or gzipped) written by the winston-era logger. */
|
|
1978
|
+
const LEGACY_LOG_PATTERN = /^omp\.(\d{4}-\d{2}-\d{2})\.log(?:\.\d+)?(?:\.gz)?$/;
|
|
1979
|
+
/** Hash-named audit files written by winston-daily-rotate-file. */
|
|
1980
|
+
const LEGACY_AUDIT_PATTERN = /^\.[0-9a-f]{8,}-audit\.json$/;
|
|
1783
1981
|
const RETAINED_STALE_LOGS_PER_PROCESS_DAY = 1;
|
|
1784
1982
|
function processIsRunning(pid) {
|
|
1785
1983
|
try {
|
|
@@ -1791,9 +1989,11 @@ function processIsRunning(pid) {
|
|
|
1791
1989
|
}
|
|
1792
1990
|
/**
|
|
1793
1991
|
* Retain one newest completed-process log per process/day within the current
|
|
1794
|
-
* and previous four local calendar days,
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1992
|
+
* and previous four local calendar days, remove completed-process audit files
|
|
1993
|
+
* left by earlier releases, and age out legacy shared daily logs and their
|
|
1994
|
+
* hash-named audits past the same window. Live PID namespaces are never
|
|
1995
|
+
* touched. The calendar-day boundary preserves daily diagnostic coverage while
|
|
1996
|
+
* bounding completed-process storage and scans.
|
|
1797
1997
|
*/
|
|
1798
1998
|
function pruneStaleProcessLogs(dir) {
|
|
1799
1999
|
let entries;
|
|
@@ -1803,29 +2003,38 @@ function pruneStaleProcessLogs(dir) {
|
|
|
1803
2003
|
return;
|
|
1804
2004
|
}
|
|
1805
2005
|
const current = /* @__PURE__ */ new Date();
|
|
1806
|
-
const currentDate =
|
|
2006
|
+
const currentDate = localDay(current);
|
|
1807
2007
|
const cutoff = new Date(current);
|
|
1808
2008
|
cutoff.setDate(cutoff.getDate() - 4);
|
|
1809
|
-
const cutoffDate =
|
|
2009
|
+
const cutoffDate = localDay(cutoff);
|
|
2010
|
+
cutoff.setHours(0, 0, 0, 0);
|
|
2011
|
+
const cutoffMs = cutoff.getTime();
|
|
1810
2012
|
const staleLogsByProcessDay = /* @__PURE__ */ new Map();
|
|
1811
2013
|
for (const entry of entries) {
|
|
1812
2014
|
if (!entry.isFile()) continue;
|
|
2015
|
+
const entryPath = path$1.join(dir, entry.name);
|
|
2016
|
+
const legacyMatch = LEGACY_LOG_PATTERN.exec(entry.name);
|
|
2017
|
+
if (legacyMatch?.[1]) {
|
|
2018
|
+
if (legacyMatch[1] < cutoffDate) removeBestEffort(entryPath);
|
|
2019
|
+
continue;
|
|
2020
|
+
}
|
|
2021
|
+
if (LEGACY_AUDIT_PATTERN.test(entry.name)) {
|
|
2022
|
+
try {
|
|
2023
|
+
if (fs.statSync(entryPath).mtimeMs < cutoffMs) removeBestEffort(entryPath);
|
|
2024
|
+
} catch {}
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
1813
2027
|
const logMatch = PROCESS_LOG_PATTERN.exec(entry.name);
|
|
1814
2028
|
const auditMatch = PROCESS_AUDIT_PATTERN.exec(entry.name);
|
|
1815
2029
|
const pidText = logMatch?.[2] ?? auditMatch?.[1];
|
|
1816
2030
|
if (!pidText || processIsRunning(Number(pidText))) continue;
|
|
1817
|
-
const entryPath = path$1.join(dir, entry.name);
|
|
1818
2031
|
if (auditMatch) {
|
|
1819
|
-
|
|
1820
|
-
fs.rmSync(entryPath, { force: true });
|
|
1821
|
-
} catch {}
|
|
2032
|
+
removeBestEffort(entryPath);
|
|
1822
2033
|
continue;
|
|
1823
2034
|
}
|
|
1824
2035
|
if (!logMatch?.[1]) continue;
|
|
1825
2036
|
if (logMatch[1] < cutoffDate || logMatch[1] > currentDate) {
|
|
1826
|
-
|
|
1827
|
-
fs.rmSync(entryPath, { force: true });
|
|
1828
|
-
} catch {}
|
|
2037
|
+
removeBestEffort(entryPath);
|
|
1829
2038
|
continue;
|
|
1830
2039
|
}
|
|
1831
2040
|
const key = `${pidText}:${logMatch[1]}`;
|
|
@@ -1846,11 +2055,24 @@ function pruneStaleProcessLogs(dir) {
|
|
|
1846
2055
|
});
|
|
1847
2056
|
} catch {}
|
|
1848
2057
|
ranked.sort((a, b) => b.mtimeMs - a.mtimeMs || b.rollover - a.rollover || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
1849
|
-
for (const stale of ranked.slice(RETAINED_STALE_LOGS_PER_PROCESS_DAY))
|
|
1850
|
-
fs.rmSync(stale.path, { force: true });
|
|
1851
|
-
} catch {}
|
|
2058
|
+
for (const stale of ranked.slice(RETAINED_STALE_LOGS_PER_PROCESS_DAY)) removeBestEffort(stale.path);
|
|
1852
2059
|
}
|
|
1853
2060
|
}
|
|
2061
|
+
function removeBestEffort(filePath) {
|
|
2062
|
+
try {
|
|
2063
|
+
fs.rmSync(filePath, { force: true });
|
|
2064
|
+
} catch {}
|
|
2065
|
+
}
|
|
2066
|
+
const scheduledPruneDirs = /* @__PURE__ */ new Set();
|
|
2067
|
+
/** Run shared retention only after logger construction has returned to the event loop. */
|
|
2068
|
+
function schedulePruneStaleProcessLogs(dir) {
|
|
2069
|
+
if (scheduledPruneDirs.has(dir)) return;
|
|
2070
|
+
scheduledPruneDirs.add(dir);
|
|
2071
|
+
setImmediate(() => {
|
|
2072
|
+
scheduledPruneDirs.delete(dir);
|
|
2073
|
+
pruneStaleProcessLogs(dir);
|
|
2074
|
+
}).unref();
|
|
2075
|
+
}
|
|
1854
2076
|
/** Ensure a logs directory exists; return the resolved path. */
|
|
1855
2077
|
function ensureDir(dir) {
|
|
1856
2078
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
@@ -1876,6 +2098,27 @@ function jsonReplacer(_key, value) {
|
|
|
1876
2098
|
}
|
|
1877
2099
|
return value;
|
|
1878
2100
|
}
|
|
2101
|
+
const ERROR_SCAN_BUDGET = 128;
|
|
2102
|
+
/**
|
|
2103
|
+
* Whether `value` may contain an {@link Error} that {@link jsonReplacer} must
|
|
2104
|
+
* unwrap. Walks what `JSON.stringify` serializes (arrays and own enumerable
|
|
2105
|
+
* keys); objects with a `toJSON` other than `Date` may return an Error, and an
|
|
2106
|
+
* exhausted budget answers true.
|
|
2107
|
+
*/
|
|
2108
|
+
function mayContainError(root) {
|
|
2109
|
+
const stack = [root];
|
|
2110
|
+
let budget = ERROR_SCAN_BUDGET;
|
|
2111
|
+
while (stack.length > 0) {
|
|
2112
|
+
const value = stack.pop();
|
|
2113
|
+
if (value === null || typeof value !== "object") continue;
|
|
2114
|
+
if (value instanceof Error) return true;
|
|
2115
|
+
if (--budget < 0) return true;
|
|
2116
|
+
if (value instanceof Date) continue;
|
|
2117
|
+
if ("toJSON" in value && typeof value.toJSON === "function") return true;
|
|
2118
|
+
for (const child of Object.values(value)) stack.push(child);
|
|
2119
|
+
}
|
|
2120
|
+
return false;
|
|
2121
|
+
}
|
|
1879
2122
|
function padTimestampPart(value, width = 2) {
|
|
1880
2123
|
return String(value).padStart(width, "0");
|
|
1881
2124
|
}
|
|
@@ -1886,42 +2129,81 @@ function formatLocalTimestamp(date) {
|
|
|
1886
2129
|
return `${padTimestampPart(date.getFullYear(), 4)}-${padTimestampPart(date.getMonth() + 1)}-${padTimestampPart(date.getDate())}T${padTimestampPart(date.getHours())}:${padTimestampPart(date.getMinutes())}:${padTimestampPart(date.getSeconds())}.${padTimestampPart(date.getMilliseconds(), 3)}${offsetSign}${padTimestampPart(Math.floor(absoluteOffset / 60))}:${padTimestampPart(absoluteOffset % 60)}`;
|
|
1887
2130
|
}
|
|
1888
2131
|
const FORMAT_TOKEN_PATTERN = /%[scdjifoO%]/;
|
|
1889
|
-
|
|
2132
|
+
/** Serialize one log record: timestamp, level, pid, message, then context fields. */
|
|
2133
|
+
function formatLogRecord(level, message, context, timestamp) {
|
|
1890
2134
|
const metadata = !FORMAT_TOKEN_PATTERN.test(message) && context !== null && typeof context === "object" ? context : void 0;
|
|
1891
|
-
const info = Object.assign({}, metadata, {
|
|
1892
|
-
level,
|
|
1893
|
-
message
|
|
1894
|
-
});
|
|
1895
|
-
if (metadata?.message) info.message = `${message} ${metadata.message}`;
|
|
1896
|
-
if (metadata?.stack) info.stack = metadata.stack;
|
|
1897
|
-
if (metadata?.cause) info.cause = metadata.cause;
|
|
1898
|
-
return info;
|
|
1899
|
-
}
|
|
1900
|
-
function formatLogInfo(info) {
|
|
1901
|
-
const timestamp = formatLocalTimestamp(/* @__PURE__ */ new Date());
|
|
1902
|
-
info.timestamp = timestamp;
|
|
1903
2135
|
const entry = {
|
|
1904
2136
|
timestamp,
|
|
1905
|
-
level
|
|
2137
|
+
level,
|
|
1906
2138
|
pid: process.pid,
|
|
1907
|
-
message:
|
|
2139
|
+
message: metadata?.message ? `${message} ${metadata.message}` : message
|
|
1908
2140
|
};
|
|
1909
|
-
|
|
1910
|
-
|
|
2141
|
+
if (metadata) {
|
|
2142
|
+
for (const key of Object.keys(metadata)) if (key !== "level" && key !== "timestamp" && key !== "message") entry[key] = metadata[key];
|
|
2143
|
+
if (metadata.stack) entry.stack = metadata.stack;
|
|
2144
|
+
if (metadata.cause) entry.cause = metadata.cause;
|
|
2145
|
+
}
|
|
2146
|
+
return mayContainError(entry) ? JSON.stringify(entry, jsonReplacer) : JSON.stringify(entry);
|
|
2147
|
+
}
|
|
2148
|
+
/** Longest a buffered file record waits before it is written. */
|
|
2149
|
+
const FILE_FLUSH_INTERVAL_MS = 1e3;
|
|
2150
|
+
/** Buffered bytes at which the file batch is written immediately. */
|
|
2151
|
+
const FILE_FLUSH_BYTES = 65536;
|
|
2152
|
+
let exitFlushInstalled = false;
|
|
2153
|
+
/** Set once the process is exiting: every later record is written immediately. */
|
|
2154
|
+
let exiting = false;
|
|
2155
|
+
function flushOnExit() {
|
|
2156
|
+
exiting = true;
|
|
2157
|
+
flush();
|
|
1911
2158
|
}
|
|
1912
2159
|
/** Build a rotating file sink with process-local rotation and shared retention. */
|
|
1913
2160
|
function makeFileTransport(dir) {
|
|
1914
2161
|
const logsDir = ensureDir(dir ?? getLogsDir());
|
|
1915
|
-
|
|
2162
|
+
schedulePruneStaleProcessLogs(logsDir);
|
|
2163
|
+
if (!exitFlushInstalled) {
|
|
2164
|
+
exitFlushInstalled = true;
|
|
2165
|
+
process.on("exit", flushOnExit);
|
|
2166
|
+
}
|
|
1916
2167
|
return new RotatingFileSink({
|
|
1917
2168
|
directory: logsDir,
|
|
1918
2169
|
filenamePrefix: "omp",
|
|
1919
2170
|
filenameSuffix: String(process.pid),
|
|
1920
2171
|
maxBytes: 10485760,
|
|
1921
2172
|
maxFiles: 5,
|
|
1922
|
-
|
|
2173
|
+
batch: {
|
|
2174
|
+
intervalMs: FILE_FLUSH_INTERVAL_MS,
|
|
2175
|
+
maxBytes: FILE_FLUSH_BYTES
|
|
2176
|
+
},
|
|
2177
|
+
onRotate: setStderrRedirectTarget
|
|
1923
2178
|
});
|
|
1924
2179
|
}
|
|
2180
|
+
const LOG_LEVEL_RANK = {
|
|
2181
|
+
error: 0,
|
|
2182
|
+
warn: 1,
|
|
2183
|
+
info: 2,
|
|
2184
|
+
debug: 3
|
|
2185
|
+
};
|
|
2186
|
+
/** `warn` and `error` records are written at once, with everything buffered before them. */
|
|
2187
|
+
const IMMEDIATE_FLUSH_RANK = LOG_LEVEL_RANK.warn;
|
|
2188
|
+
/**
|
|
2189
|
+
* Rank of the most verbose level the file transport persists: `OMP_LOG_LEVEL`
|
|
2190
|
+
* (error|warn|info|debug, case-insensitive), default `debug` (everything).
|
|
2191
|
+
*/
|
|
2192
|
+
function resolveFileLevelRank() {
|
|
2193
|
+
const requested = process.env.OMP_LOG_LEVEL?.trim().toLowerCase();
|
|
2194
|
+
return requested && Object.hasOwn(LOG_LEVEL_RANK, requested) ? LOG_LEVEL_RANK[requested] : LOG_LEVEL_RANK.debug;
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Write buffered file records to disk now. The file transport batches records
|
|
2198
|
+
* (one write per second or per 64 KiB) and writes `warn`/`error` records
|
|
2199
|
+
* immediately; process exit flushes on its own. Call this before handing the
|
|
2200
|
+
* log file to something that reads it in-process. Never throws.
|
|
2201
|
+
*/
|
|
2202
|
+
function flush() {
|
|
2203
|
+
try {
|
|
2204
|
+
activeTransports?.file?.flush();
|
|
2205
|
+
} catch {}
|
|
2206
|
+
}
|
|
1925
2207
|
/**
|
|
1926
2208
|
* Desired transport configuration, applied when local logging is initialized.
|
|
1927
2209
|
* Default: file ON (TUI-safe), console OFF.
|
|
@@ -1932,7 +2214,8 @@ let activeTransports;
|
|
|
1932
2214
|
function buildTransports(opts) {
|
|
1933
2215
|
return {
|
|
1934
2216
|
file: opts.file ? makeFileTransport(typeof opts.file === "string" ? opts.file : void 0) : void 0,
|
|
1935
|
-
console: opts.console === true
|
|
2217
|
+
console: opts.console === true,
|
|
2218
|
+
fileLevelRank: resolveFileLevelRank()
|
|
1936
2219
|
};
|
|
1937
2220
|
}
|
|
1938
2221
|
function getLocalTransports() {
|
|
@@ -1941,11 +2224,13 @@ function getLocalTransports() {
|
|
|
1941
2224
|
}
|
|
1942
2225
|
function emitLocally(level, message, context) {
|
|
1943
2226
|
const transports = getLocalTransports();
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2227
|
+
const rank = LOG_LEVEL_RANK[level];
|
|
2228
|
+
const file = rank <= transports.fileLevelRank ? transports.file : void 0;
|
|
2229
|
+
if (!file && !transports.console) return;
|
|
2230
|
+
const now = /* @__PURE__ */ new Date();
|
|
2231
|
+
const line = formatLogRecord(level, message, context, formatLocalTimestamp(now));
|
|
2232
|
+
file?.write(line, rank <= IMMEDIATE_FLUSH_RANK || exiting, localDay(now));
|
|
2233
|
+
if (transports.console) fs.writeSync(1, `${line}${os.EOL}`);
|
|
1949
2234
|
}
|
|
1950
2235
|
/**
|
|
1951
2236
|
* Log an error message.
|
|
@@ -2022,9 +2307,12 @@ function time(op, fn, ...args) {
|
|
|
2022
2307
|
}
|
|
2023
2308
|
}
|
|
2024
2309
|
//#endregion
|
|
2025
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
2026
|
-
function
|
|
2027
|
-
|
|
2310
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/frontmatter.ts
|
|
2311
|
+
function stripLeadingHtmlComments(content) {
|
|
2312
|
+
const leading = /^(?:(?:\s*<!--[\s\S]*?-->)+)/.exec(content);
|
|
2313
|
+
if (!leading) return content;
|
|
2314
|
+
const rest = content.slice(leading[0].length);
|
|
2315
|
+
return /^\s*---/.test(rest) ? rest.replace(/^\s+/, "") : content;
|
|
2028
2316
|
}
|
|
2029
2317
|
/** Convert kebab-case to camelCase (e.g. "thinking-level" -> "thinkingLevel") */
|
|
2030
2318
|
function kebabToCamel(key) {
|
|
@@ -2118,7 +2406,8 @@ function parseFrontmatter(content, options) {
|
|
|
2118
2406
|
const loc = location ?? source;
|
|
2119
2407
|
const frontmatter = { ...fallback };
|
|
2120
2408
|
const newlineNormalized = normalize ? content.replace(/\r\n?/g, "\n") : content;
|
|
2121
|
-
const
|
|
2409
|
+
const repairHtmlComments = normalize && repair;
|
|
2410
|
+
const normalized = repairHtmlComments ? stripLeadingHtmlComments(newlineNormalized) : newlineNormalized;
|
|
2122
2411
|
if (!normalized.startsWith("---")) return {
|
|
2123
2412
|
frontmatter,
|
|
2124
2413
|
body: normalized
|
|
@@ -2128,7 +2417,8 @@ function parseFrontmatter(content, options) {
|
|
|
2128
2417
|
frontmatter,
|
|
2129
2418
|
body: normalized
|
|
2130
2419
|
};
|
|
2131
|
-
const
|
|
2420
|
+
const rawMetadata = normalized.slice(4, endIndex);
|
|
2421
|
+
const metadata = repairHtmlComments ? rawMetadata.replace(/<!--[\s\S]*?-->/g, "") : rawMetadata;
|
|
2132
2422
|
const body = normalized.slice(endIndex + 4).trim();
|
|
2133
2423
|
try {
|
|
2134
2424
|
const loaded = parseYamlRecord(metadata, repair);
|
|
@@ -2173,6 +2463,15 @@ function parseFrontmatter(content, options) {
|
|
|
2173
2463
|
}
|
|
2174
2464
|
}
|
|
2175
2465
|
const U$1 = 117;
|
|
2466
|
+
/**
|
|
2467
|
+
* Minimum remaining string length for bulk-skipping ordinary characters in
|
|
2468
|
+
* {@link JsonLexer.string} with two engine `indexOf` scans (nearest quote or
|
|
2469
|
+
* backslash) instead of one `charCodeAt` per character. Below this the
|
|
2470
|
+
* `indexOf` setup costs more than the per-char loop on quote-dense input;
|
|
2471
|
+
* long string runs — the multi-KB tool-argument payloads that dominate
|
|
2472
|
+
* streaming re-parses — scan several times faster above it.
|
|
2473
|
+
*/
|
|
2474
|
+
const STRING_BULK_SCAN_MIN = 32;
|
|
2176
2475
|
/** Valid chars after `\` in a strict JSON escape: `" \ / b f n r t u`. */
|
|
2177
2476
|
const VALID_ESCAPE_CHAR = /* @__PURE__ */ new Uint8Array(128);
|
|
2178
2477
|
for (const ch of "\"\\/bfnrtu") VALID_ESCAPE_CHAR[ch.charCodeAt(0)] = 1;
|
|
@@ -2270,6 +2569,17 @@ var JsonLexer = class {
|
|
|
2270
2569
|
src;
|
|
2271
2570
|
mode;
|
|
2272
2571
|
pos;
|
|
2572
|
+
/**
|
|
2573
|
+
* Shared across {@link string} calls: `src` has no backslash in
|
|
2574
|
+
* `[#backslashFrom, #backslashAt)` and `#backslashAt` is a backslash or
|
|
2575
|
+
* `src.length`. An escape-free run can extend past its string's closing
|
|
2576
|
+
* quote to the end of the document, so searching per string would rescan
|
|
2577
|
+
* the same tail for every sibling string (quadratic on objects with many
|
|
2578
|
+
* short values). `src` is immutable, so the range never goes stale; a
|
|
2579
|
+
* cursor outside it (e.g. a rollback) re-searches.
|
|
2580
|
+
*/
|
|
2581
|
+
#backslashFrom = 0;
|
|
2582
|
+
#backslashAt = -1;
|
|
2273
2583
|
constructor(src, mode, pos = 0) {
|
|
2274
2584
|
this.src = src;
|
|
2275
2585
|
this.mode = mode;
|
|
@@ -2299,7 +2609,27 @@ var JsonLexer = class {
|
|
|
2299
2609
|
let runStart = i;
|
|
2300
2610
|
let unstableFrom = -1;
|
|
2301
2611
|
const lenient = quote === 39 || this.mode === "streaming";
|
|
2612
|
+
const quoteChar = String.fromCharCode(quote);
|
|
2613
|
+
let nextQuote = quoteChar.charCodeAt(0) === quote ? -1 : n;
|
|
2614
|
+
let nextBackslash = -1;
|
|
2302
2615
|
while (i < n) {
|
|
2616
|
+
const c0 = s.charCodeAt(i);
|
|
2617
|
+
if (c0 !== 92 && c0 !== quote && n - i > STRING_BULK_SCAN_MIN) {
|
|
2618
|
+
if (nextQuote < i) {
|
|
2619
|
+
const q = s.indexOf(quoteChar, i);
|
|
2620
|
+
nextQuote = q === -1 ? n : q;
|
|
2621
|
+
}
|
|
2622
|
+
if (nextBackslash < i) {
|
|
2623
|
+
if (i < this.#backslashFrom || i > this.#backslashAt) {
|
|
2624
|
+
const b = s.indexOf("\\", i);
|
|
2625
|
+
this.#backslashFrom = i;
|
|
2626
|
+
this.#backslashAt = b === -1 ? n : b;
|
|
2627
|
+
}
|
|
2628
|
+
nextBackslash = this.#backslashAt;
|
|
2629
|
+
}
|
|
2630
|
+
i = nextQuote < nextBackslash ? nextQuote : nextBackslash;
|
|
2631
|
+
if (i >= n) break;
|
|
2632
|
+
}
|
|
2303
2633
|
const cc = s.charCodeAt(i);
|
|
2304
2634
|
if (cc !== 92 && cc !== quote) {
|
|
2305
2635
|
i++;
|
|
@@ -2476,7 +2806,7 @@ var JsonLexer = class {
|
|
|
2476
2806
|
}
|
|
2477
2807
|
};
|
|
2478
2808
|
//#endregion
|
|
2479
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
2809
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/json-parse.ts
|
|
2480
2810
|
const U = 117;
|
|
2481
2811
|
(() => {
|
|
2482
2812
|
const e = [];
|
|
@@ -2655,16 +2985,25 @@ function parseJsonWithRepair(json) {
|
|
|
2655
2985
|
function parseStreamingJson(partialJson) {
|
|
2656
2986
|
const trimmed = partialJson?.trimStart();
|
|
2657
2987
|
if (!trimmed) return {};
|
|
2658
|
-
|
|
2988
|
+
const first = trimmed.charCodeAt(0);
|
|
2989
|
+
if (first !== 123 && first !== 91 || canCloseContainer(trimmed)) try {
|
|
2659
2990
|
return JSON.parse(trimmed);
|
|
2991
|
+
} catch {}
|
|
2992
|
+
try {
|
|
2993
|
+
return new RelaxedJson(trimmed, true).parse() ?? {};
|
|
2660
2994
|
} catch {
|
|
2661
|
-
|
|
2662
|
-
return new RelaxedJson(trimmed, true).parse() ?? {};
|
|
2663
|
-
} catch {
|
|
2664
|
-
return {};
|
|
2665
|
-
}
|
|
2995
|
+
return {};
|
|
2666
2996
|
}
|
|
2667
2997
|
}
|
|
2998
|
+
/** True when the last non-whitespace character is `}` or `]`. */
|
|
2999
|
+
function canCloseContainer(text) {
|
|
3000
|
+
for (let i = text.length - 1; i >= 0; i--) {
|
|
3001
|
+
const code = text.charCodeAt(i);
|
|
3002
|
+
if (code === 32 || code === 9 || code === 10 || code === 13) continue;
|
|
3003
|
+
return code === 125 || code === 93;
|
|
3004
|
+
}
|
|
3005
|
+
return false;
|
|
3006
|
+
}
|
|
2668
3007
|
/**
|
|
2669
3008
|
* Throttled variant of {@link parseStreamingJson} for the per-delta hot path.
|
|
2670
3009
|
*
|
|
@@ -2885,10 +3224,71 @@ function classifyJsonPrefix(text) {
|
|
|
2885
3224
|
}
|
|
2886
3225
|
return expect === 7 ? "complete" : "prefix";
|
|
2887
3226
|
}
|
|
2888
|
-
new Intl.Segmenter();
|
|
2889
3227
|
//#endregion
|
|
2890
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
2891
|
-
|
|
3228
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/loop-phase.ts
|
|
3229
|
+
/**
|
|
3230
|
+
* Process-global synchronous loop phases. The watchdog arms a late window and
|
|
3231
|
+
* reads attribution via {@link takeLoopPhaseAttribution}; attribution totals
|
|
3232
|
+
* only the innermost label's time inside that window. A label is named only
|
|
3233
|
+
* when its total outweighs all unlabeled time, otherwise the block remains
|
|
3234
|
+
* "unknown". Disarmed push/pop do not read the clock.
|
|
3235
|
+
*
|
|
3236
|
+
* This is deliberately a process-global stack and not part of the logger span
|
|
3237
|
+
* machinery: `main.ts` ends timing spans before the interactive TUI starts, so
|
|
3238
|
+
* `logger.openSpanPath()` is empty in a live session.
|
|
3239
|
+
*
|
|
3240
|
+
* Correctness constraint: each `pushLoopPhase` must be balanced by a
|
|
3241
|
+
* `popLoopPhase` within the SAME synchronous execution (always via `try`/
|
|
3242
|
+
* `finally`). The stack is global and shared, so a label held across an
|
|
3243
|
+
* `await`/async boundary — or interleaved between concurrent tasks — would
|
|
3244
|
+
* misattribute or leak phases. Instrument only synchronous spans; for async
|
|
3245
|
+
* work, push/pop around each synchronous chunk, not across the await.
|
|
3246
|
+
*/
|
|
3247
|
+
const stack = [];
|
|
3248
|
+
const defaultClock = () => performance.now();
|
|
3249
|
+
let windowStart = Number.POSITIVE_INFINITY;
|
|
3250
|
+
let segmentStart = windowStart;
|
|
3251
|
+
let clock = defaultClock;
|
|
3252
|
+
const totals = /* @__PURE__ */ new Map();
|
|
3253
|
+
let labeledMs = 0;
|
|
3254
|
+
function boundary(t) {
|
|
3255
|
+
const ms = Math.max(0, t - Math.max(segmentStart, windowStart));
|
|
3256
|
+
const label = stack[stack.length - 1];
|
|
3257
|
+
if (label !== void 0 && ms > 0) {
|
|
3258
|
+
totals.set(label, (totals.get(label) ?? 0) + ms);
|
|
3259
|
+
labeledMs += ms;
|
|
3260
|
+
}
|
|
3261
|
+
segmentStart = t;
|
|
3262
|
+
}
|
|
3263
|
+
/** Arm an attribution window starting at `start` (a deadline on `now`'s clock). Called with no arguments it disarms and clears. */
|
|
3264
|
+
function resetLoopPhaseWindow(start = Number.POSITIVE_INFINITY, now = defaultClock) {
|
|
3265
|
+
windowStart = segmentStart = start;
|
|
3266
|
+
clock = now;
|
|
3267
|
+
totals.clear();
|
|
3268
|
+
labeledMs = 0;
|
|
3269
|
+
}
|
|
3270
|
+
/** Attribute the window [start, end] and disarm it. Returns undefined when no window is armed or no label outweighs unlabeled time. */
|
|
3271
|
+
function takeLoopPhaseAttribution(end) {
|
|
3272
|
+
if (windowStart === Number.POSITIVE_INFINITY) return void 0;
|
|
3273
|
+
const t = end ?? clock();
|
|
3274
|
+
boundary(t);
|
|
3275
|
+
const unknownMs = Math.max(0, t - windowStart - labeledMs);
|
|
3276
|
+
let winner;
|
|
3277
|
+
let winnerMs = 0;
|
|
3278
|
+
for (const [label, ms] of totals) if (ms >= winnerMs) {
|
|
3279
|
+
winner = label;
|
|
3280
|
+
winnerMs = ms;
|
|
3281
|
+
}
|
|
3282
|
+
const result = winner !== void 0 && winnerMs > unknownMs ? {
|
|
3283
|
+
label: winner,
|
|
3284
|
+
ms: winnerMs
|
|
3285
|
+
} : void 0;
|
|
3286
|
+
resetLoopPhaseWindow();
|
|
3287
|
+
return result;
|
|
3288
|
+
}
|
|
3289
|
+
//#endregion
|
|
3290
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/mime.ts
|
|
3291
|
+
const IMAGE_METADATA_HEADER_BYTES = 262144;
|
|
2892
3292
|
const PNG_MAGIC = Buffer.from([
|
|
2893
3293
|
137,
|
|
2894
3294
|
80,
|
|
@@ -3065,83 +3465,24 @@ function parseWebpMetadata(header) {
|
|
|
3065
3465
|
function parseImageMetadata(header) {
|
|
3066
3466
|
return parsePngMetadata(header) ?? parseJpegMetadata(header) ?? parseGifMetadata(header) ?? parseWebpMetadata(header);
|
|
3067
3467
|
}
|
|
3068
|
-
|
|
3069
|
-
return peekFile(filePath, maxBytes, parseImageMetadata);
|
|
3070
|
-
}
|
|
3071
|
-
//#endregion
|
|
3072
|
-
//#region src/tools/shared/bun-ffi-shim.ts
|
|
3073
|
-
/**
|
|
3074
|
-
* DSH named-import shim for `bun:ffi` (pi-utils stderr-guard / process-name).
|
|
3075
|
-
* Both consumers degrade silently when dlopen is unavailable ("Never throws:
|
|
3076
|
-
* bun:ffi unavailability ... degrades silently"), so this shim returns null.
|
|
3077
|
-
*/
|
|
3078
|
-
const FFIType = {};
|
|
3079
|
-
function dlopen() {
|
|
3080
|
-
return null;
|
|
3081
|
-
}
|
|
3082
|
-
//#endregion
|
|
3083
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/stderr-guard.ts
|
|
3468
|
+
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3084
3469
|
/**
|
|
3085
|
-
*
|
|
3086
|
-
*
|
|
3087
|
-
*
|
|
3088
|
-
* On macOS, runtime diagnostics are written by the platform directly to file
|
|
3089
|
-
* descriptor 2 at arbitrary times — e.g. libmalloc's "MallocStackLogging:
|
|
3090
|
-
* can't turn off malloc stack logging because it was not enabled" when the OS
|
|
3091
|
-
* broadcasts a memory-diagnostic event to long-lived processes. Those bytes
|
|
3092
|
-
* bypass the renderer and paint straight into the viewport. Stripping the
|
|
3093
|
-
* MallocStackLogging* env vars (cli.ts) only protects child processes; it
|
|
3094
|
-
* cannot stop libmalloc inside THIS process from logging.
|
|
3095
|
-
*
|
|
3096
|
-
* Fix (mirrors openai/codex#24459): while the TUI owns the terminal, dup fd 2
|
|
3097
|
-
* aside and dup2 a redirect target over it; restore the saved fd whenever
|
|
3098
|
-
* terminal ownership is released (external editor, Ctrl+Z suspend, shutdown,
|
|
3099
|
-
* crash restore). Unlike codex we redirect to the omp log file — not
|
|
3100
|
-
* /dev/null — so the diagnostics stay greppable and Bun native-crash reports
|
|
3101
|
-
* (which abort before any JS cleanup can restore fd 2) are preserved.
|
|
3102
|
-
*
|
|
3103
|
-
* Only dup/dup2 go through bun:ffi. fcntl is deliberately avoided: it is
|
|
3104
|
-
* variadic, and the arm64-darwin ABI passes variadic arguments on the stack,
|
|
3105
|
-
* so a fixed-arity FFI signature would read garbage for the third argument.
|
|
3470
|
+
* Validate standard base64 image data without decoding it. Padding may be omitted, but
|
|
3471
|
+
* present padding must be canonical, the trailing bits must be zero, and the data non-empty.
|
|
3106
3472
|
*/
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
if (
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
const
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
dup2: {
|
|
3121
|
-
args: [FFIType.i32, FFIType.i32],
|
|
3122
|
-
returns: FFIType.i32
|
|
3123
|
-
}
|
|
3124
|
-
}).symbols;
|
|
3125
|
-
return libcFdOpsCache;
|
|
3126
|
-
} catch {}
|
|
3127
|
-
return libcFdOpsCache;
|
|
3128
|
-
}
|
|
3129
|
-
/** Saved dup of the real stderr while suppression is active, else null. */
|
|
3130
|
-
let savedStderrFd = null;
|
|
3131
|
-
/**
|
|
3132
|
-
* Re-point fd 2 at the saved terminal stderr. Safe to call unconditionally:
|
|
3133
|
-
* no-op when suppression is not active. Called at every terminal-ownership
|
|
3134
|
-
* release and by the postmortem fatal handlers before they print, so crash
|
|
3135
|
-
* reports reach the real terminal.
|
|
3136
|
-
*/
|
|
3137
|
-
function restoreTerminalStderr() {
|
|
3138
|
-
if (savedStderrFd === null) return;
|
|
3139
|
-
const saved = savedStderrFd;
|
|
3140
|
-
savedStderrFd = null;
|
|
3141
|
-
libcFdOps()?.dup2(saved, STDERR_FILENO);
|
|
3142
|
-
try {
|
|
3143
|
-
fs.closeSync(saved);
|
|
3144
|
-
} catch {}
|
|
3473
|
+
function base64ImageDataFailureReason(data) {
|
|
3474
|
+
if (data.length === 0) return "empty image data";
|
|
3475
|
+
const match = /^([A-Za-z0-9+/]+)(={0,2})$/.exec(data);
|
|
3476
|
+
if (!match) return "invalid base64 image data";
|
|
3477
|
+
const encoded = match[1];
|
|
3478
|
+
const paddingLength = match[2].length;
|
|
3479
|
+
const remainder = encoded.length % 4;
|
|
3480
|
+
if (remainder === 1 || paddingLength === 1 && remainder !== 3 || paddingLength === 2 && remainder !== 2) return "invalid base64 image data";
|
|
3481
|
+
const lastSextet = BASE64_ALPHABET.indexOf(encoded[encoded.length - 1]);
|
|
3482
|
+
if (remainder === 2 && (lastSextet & 15) !== 0 || remainder === 3 && (lastSextet & 3) !== 0) return "invalid base64 image data";
|
|
3483
|
+
}
|
|
3484
|
+
function readImageMetadata(filePath, maxBytes = IMAGE_METADATA_HEADER_BYTES) {
|
|
3485
|
+
return peekFile(filePath, maxBytes, parseImageMetadata);
|
|
3145
3486
|
}
|
|
3146
3487
|
const callbackList = [];
|
|
3147
3488
|
let cleanupStage = "idle";
|
|
@@ -3163,6 +3504,25 @@ const CLEANUP_DEADLINE_MS = 1e4;
|
|
|
3163
3504
|
*/
|
|
3164
3505
|
const NATIVE_PROCESS_EXIT = Symbol.for("omp.postmortem.nativeProcessExit");
|
|
3165
3506
|
/**
|
|
3507
|
+
* Walk a guarded exit primitive down to the native it shadows.
|
|
3508
|
+
*
|
|
3509
|
+
* `withHostGuard` stamps each throwing replacement with the primitive it
|
|
3510
|
+
* shadows under {@link NATIVE_PROCESS_EXIT}; nested guard windows stack, so a
|
|
3511
|
+
* single unwrap can still land on another throwing stub. Follow the chain
|
|
3512
|
+
* (cycle-guarded) until a link carries no stamp — that link is native.
|
|
3513
|
+
*/
|
|
3514
|
+
function nativeHardExit(fn) {
|
|
3515
|
+
let current = fn;
|
|
3516
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3517
|
+
while (typeof current === "function" && !seen.has(current)) {
|
|
3518
|
+
seen.add(current);
|
|
3519
|
+
const behind = Reflect.get(current, NATIVE_PROCESS_EXIT);
|
|
3520
|
+
if (typeof behind !== "function") return current;
|
|
3521
|
+
current = behind;
|
|
3522
|
+
}
|
|
3523
|
+
return typeof current === "function" ? current : void 0;
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3166
3526
|
* Hard-exit the process through the native primitive, resolved on every call.
|
|
3167
3527
|
*
|
|
3168
3528
|
* The native exit is deliberately re-resolved here rather than bound at module
|
|
@@ -3173,13 +3533,35 @@ const NATIVE_PROCESS_EXIT = Symbol.for("omp.postmortem.nativeProcessExit");
|
|
|
3173
3533
|
* init could freeze the throwing stub forever and turn every later shutdown
|
|
3174
3534
|
* (SIGHUP/SIGINT/fatal) into an unhandled-rejection loop (#7393). When the
|
|
3175
3535
|
* guard is active the stub carries the native exit under
|
|
3176
|
-
* {@link NATIVE_PROCESS_EXIT}
|
|
3177
|
-
*
|
|
3536
|
+
* {@link NATIVE_PROCESS_EXIT} (#6488).
|
|
3537
|
+
*
|
|
3538
|
+
* Both globals are reinstalled to their natives before exiting: Bun's
|
|
3539
|
+
* `process.exit` re-reads `process.reallyExit` at call time, so exiting through
|
|
3540
|
+
* one primitive while its sibling still holds the throwing stub re-enters the
|
|
3541
|
+
* guard and loops the rejection storm (#11789). After restoring, `reallyExit`
|
|
3542
|
+
* (the low-level primitive) is preferred; `process.exit` and finally `SIGKILL`
|
|
3543
|
+
* are fallbacks so a poisoned or absent chain can never leave the process alive.
|
|
3544
|
+
*
|
|
3545
|
+
* Buffered log records are written first: `reallyExit` skips the `exit` event
|
|
3546
|
+
* the logger otherwise flushes on, which would drop the last batch on every
|
|
3547
|
+
* signal and fatal exit.
|
|
3178
3548
|
*/
|
|
3179
3549
|
function exitProcess(code) {
|
|
3180
|
-
|
|
3181
|
-
const
|
|
3182
|
-
|
|
3550
|
+
flush();
|
|
3551
|
+
const reallyExit = nativeHardExit(typeof process.reallyExit === "function" ? process.reallyExit : void 0);
|
|
3552
|
+
const exit = nativeHardExit(process.exit);
|
|
3553
|
+
if (reallyExit) process.reallyExit = reallyExit;
|
|
3554
|
+
if (exit) process.exit = exit;
|
|
3555
|
+
try {
|
|
3556
|
+
reallyExit?.call(process, code);
|
|
3557
|
+
} catch {}
|
|
3558
|
+
try {
|
|
3559
|
+
exit?.call(process, code);
|
|
3560
|
+
} catch {}
|
|
3561
|
+
try {
|
|
3562
|
+
process.kill(process.pid, "SIGKILL");
|
|
3563
|
+
} catch {}
|
|
3564
|
+
throw new Error(`exitProcess(${code}) failed to terminate the process`);
|
|
3183
3565
|
}
|
|
3184
3566
|
let cleanupPromise;
|
|
3185
3567
|
const fatalRecoveryHintProviders = /* @__PURE__ */ new Set();
|
|
@@ -3382,12 +3764,19 @@ async function exitAfterFatal(output, logMessage, err, reason) {
|
|
|
3382
3764
|
exitProcess(1);
|
|
3383
3765
|
}
|
|
3384
3766
|
}
|
|
3385
|
-
|
|
3767
|
+
/** Contain an EPIPE from an optional worker IPC `send()` (#2997, #9158). */
|
|
3768
|
+
function handleWorkerSendEpipe(err) {
|
|
3769
|
+
if (!isIpcSendEpipe(err)) return false;
|
|
3770
|
+
warn("Ignoring EPIPE from worker IPC send; optional subsystem will self-recover", { err });
|
|
3771
|
+
return true;
|
|
3772
|
+
}
|
|
3773
|
+
if (Bun.isMainThread) process.on("SIGINT", async () => {
|
|
3386
3774
|
await runCleanup("sigint");
|
|
3387
3775
|
exitProcess(130);
|
|
3388
|
-
}).on("SIGUSR1", () => {
|
|
3776
|
+
}).on("SIGUSR1", async () => {
|
|
3389
3777
|
if (inspectorOpened) return;
|
|
3390
3778
|
inspectorOpened = true;
|
|
3779
|
+
const { default: inspector } = await import("node:inspector");
|
|
3391
3780
|
inspector.open(void 0, void 0, false);
|
|
3392
3781
|
const url = inspector.url();
|
|
3393
3782
|
process.stderr.write(`Inspector opened: ${url}\n`);
|
|
@@ -3397,10 +3786,7 @@ if (isMainThread) process.on("SIGINT", async () => {
|
|
|
3397
3786
|
return;
|
|
3398
3787
|
}
|
|
3399
3788
|
const err = thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
3400
|
-
if (
|
|
3401
|
-
warn("Ignoring EPIPE from worker IPC send; optional subsystem will self-recover", { err });
|
|
3402
|
-
return;
|
|
3403
|
-
}
|
|
3789
|
+
if (handleWorkerSendEpipe(err)) return;
|
|
3404
3790
|
if (isWorkerIpcDeserializeError(err)) {
|
|
3405
3791
|
warn("Malformed worker IPC frame; faulting active worker subsystems", { err });
|
|
3406
3792
|
faultWorkerIpcChannels(err);
|
|
@@ -3413,10 +3799,7 @@ if (isMainThread) process.on("SIGINT", async () => {
|
|
|
3413
3799
|
await exitAfterFatal(`${formatFatalError("Uncaught Exception", err)}${formatFatalRecoveryHints()}`, "Uncaught exception", err, "uncaught_exception");
|
|
3414
3800
|
}).on("unhandledRejection", async (reason) => {
|
|
3415
3801
|
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
3416
|
-
if (
|
|
3417
|
-
warn("Ignoring EPIPE from worker IPC send; optional subsystem will self-recover", { err });
|
|
3418
|
-
return;
|
|
3419
|
-
}
|
|
3802
|
+
if (handleWorkerSendEpipe(err)) return;
|
|
3420
3803
|
if (isExpectedCleanupError(reason)) {
|
|
3421
3804
|
warn("Ignoring expected cleanup rejection", { err });
|
|
3422
3805
|
return;
|
|
@@ -3471,8 +3854,8 @@ function register(id, callback, options = {}) {
|
|
|
3471
3854
|
try {
|
|
3472
3855
|
const pending = invokeCleanup(registration, reason, keepAlive, cleanupPass);
|
|
3473
3856
|
if (!pending) return;
|
|
3474
|
-
const tracked = pending.catch((error$
|
|
3475
|
-
const err = error$
|
|
3857
|
+
const tracked = pending.catch((error$3) => {
|
|
3858
|
+
const err = error$3 instanceof Error ? error$3 : new Error(String(error$3));
|
|
3476
3859
|
error("Cleanup callback failed", {
|
|
3477
3860
|
err,
|
|
3478
3861
|
id,
|
|
@@ -3480,8 +3863,8 @@ function register(id, callback, options = {}) {
|
|
|
3480
3863
|
});
|
|
3481
3864
|
});
|
|
3482
3865
|
activeLatePromises?.push(tracked);
|
|
3483
|
-
} catch (error$
|
|
3484
|
-
const err = error$
|
|
3866
|
+
} catch (error$2) {
|
|
3867
|
+
const err = error$2 instanceof Error ? error$2 : new Error(String(error$2));
|
|
3485
3868
|
error("Cleanup callback failed", {
|
|
3486
3869
|
err,
|
|
3487
3870
|
id,
|
|
@@ -3504,7 +3887,7 @@ function register(id, callback, options = {}) {
|
|
|
3504
3887
|
return cancel;
|
|
3505
3888
|
}
|
|
3506
3889
|
//#endregion
|
|
3507
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
3890
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/which.ts
|
|
3508
3891
|
const XCODE_BINS = /* @__PURE__ */ new Set([
|
|
3509
3892
|
"clang",
|
|
3510
3893
|
"clang++",
|
|
@@ -3599,14 +3982,16 @@ function darwinWhich(command, options) {
|
|
|
3599
3982
|
if (isXcodeBin(command)) return getMacosToolPaths().get(command) ?? null;
|
|
3600
3983
|
return null;
|
|
3601
3984
|
}
|
|
3985
|
+
function filterAbsoluteSearchPath(rawPath) {
|
|
3986
|
+
if (!rawPath) return null;
|
|
3987
|
+
return rawPath.split(path$1.delimiter).filter((dir) => dir.length > 0 && isFullyQualifiedPath(dir)).join(path$1.delimiter) || null;
|
|
3988
|
+
}
|
|
3602
3989
|
const whichFresh = os.platform() === "darwin" ? darwinWhich : (command, options) => Bun.which(command, options);
|
|
3603
3990
|
function cacheKey$1(command, options) {
|
|
3604
3991
|
if (!options) return command;
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
if (options.PATH) h = Bun.hash(options.PATH, h);
|
|
3609
|
-
return h;
|
|
3992
|
+
const cwd = options?.cwd ?? "";
|
|
3993
|
+
const binPath = options?.PATH ?? "";
|
|
3994
|
+
return `${command.length}:${command}${cwd.length}:${cwd}${binPath.length}:${binPath}`;
|
|
3610
3995
|
}
|
|
3611
3996
|
/**
|
|
3612
3997
|
* Locate binary on PATH (with flexible caching).
|
|
@@ -3617,10 +4002,18 @@ function cacheKey$1(command, options) {
|
|
|
3617
4002
|
*/
|
|
3618
4003
|
function $which(command, options) {
|
|
3619
4004
|
const cachePolicy = options?.cache ?? 0;
|
|
3620
|
-
|
|
4005
|
+
let lookupOptions = options?.PATH !== void 0 || process.env.PATH === void 0 ? options : {
|
|
3621
4006
|
...options,
|
|
3622
4007
|
PATH: process.env.PATH
|
|
3623
4008
|
};
|
|
4009
|
+
if (options?.requireAbsolutePaths) {
|
|
4010
|
+
const safePath = filterAbsoluteSearchPath(lookupOptions?.PATH);
|
|
4011
|
+
if (!safePath) return null;
|
|
4012
|
+
lookupOptions = {
|
|
4013
|
+
...lookupOptions,
|
|
4014
|
+
PATH: safePath
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
3624
4017
|
let key;
|
|
3625
4018
|
if (cachePolicy !== 1) {
|
|
3626
4019
|
key = cacheKey$1(command, lookupOptions);
|
|
@@ -3630,11 +4023,148 @@ function $which(command, options) {
|
|
|
3630
4023
|
}
|
|
3631
4024
|
}
|
|
3632
4025
|
const result = whichFresh(command, lookupOptions);
|
|
4026
|
+
if (result && options?.requireAbsolutePaths && !isFullyQualifiedPath(result)) return null;
|
|
3633
4027
|
if (key != null && cachePolicy !== 3) toolCache.set(key, result);
|
|
3634
4028
|
return result;
|
|
3635
4029
|
}
|
|
3636
4030
|
//#endregion
|
|
3637
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
4031
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/lru.ts
|
|
4032
|
+
function positiveInteger(value, name) {
|
|
4033
|
+
if (value === void 0) return 0;
|
|
4034
|
+
if (!Number.isInteger(value) || value < 0) throw new TypeError(`${name} must be a non-negative integer`);
|
|
4035
|
+
return value;
|
|
4036
|
+
}
|
|
4037
|
+
/** A bounded least-recently-used cache with optional size and lifetime limits. */
|
|
4038
|
+
var LRUCache = class {
|
|
4039
|
+
#entries = /* @__PURE__ */ new Map();
|
|
4040
|
+
#max;
|
|
4041
|
+
#maxSize;
|
|
4042
|
+
#maxEntrySize;
|
|
4043
|
+
#sizeCalculation;
|
|
4044
|
+
#ttl;
|
|
4045
|
+
#updateAgeOnGet;
|
|
4046
|
+
#dispose;
|
|
4047
|
+
#calculatedSize = 0;
|
|
4048
|
+
/** Creates an empty cache. */
|
|
4049
|
+
constructor(options) {
|
|
4050
|
+
this.#max = positiveInteger(options.max, "max");
|
|
4051
|
+
this.#maxSize = positiveInteger(options.maxSize, "maxSize");
|
|
4052
|
+
const explicitMaxEntrySize = positiveInteger(options.maxEntrySize, "maxEntrySize");
|
|
4053
|
+
this.#maxEntrySize = explicitMaxEntrySize || this.#maxSize;
|
|
4054
|
+
this.#ttl = positiveInteger(options.ttl, "ttl");
|
|
4055
|
+
if (this.#max === 0 && this.#maxSize === 0 && this.#ttl === 0) throw new TypeError("At least one of max, maxSize, or ttl is required");
|
|
4056
|
+
if ((this.#maxSize !== 0 || this.#maxEntrySize !== 0) && options.sizeCalculation === void 0) throw new TypeError("sizeCalculation is required when a size limit is set");
|
|
4057
|
+
this.#sizeCalculation = options.sizeCalculation;
|
|
4058
|
+
this.#updateAgeOnGet = options.updateAgeOnGet === true;
|
|
4059
|
+
this.#dispose = options.dispose;
|
|
4060
|
+
}
|
|
4061
|
+
/** Number of entries, including stale entries not yet removed by `get`. */
|
|
4062
|
+
get size() {
|
|
4063
|
+
return this.#entries.size;
|
|
4064
|
+
}
|
|
4065
|
+
/** Aggregate calculated size of retained entries. */
|
|
4066
|
+
get calculatedSize() {
|
|
4067
|
+
return this.#calculatedSize;
|
|
4068
|
+
}
|
|
4069
|
+
/** Stores a value and makes it most recently used. */
|
|
4070
|
+
set(key, value) {
|
|
4071
|
+
if (value === void 0) {
|
|
4072
|
+
this.delete(key);
|
|
4073
|
+
return this;
|
|
4074
|
+
}
|
|
4075
|
+
const size = this.#entrySize(value, key);
|
|
4076
|
+
const previous = this.#entries.get(key);
|
|
4077
|
+
if (this.#maxEntrySize !== 0 && size > this.#maxEntrySize) {
|
|
4078
|
+
if (previous !== void 0) this.#remove(key, previous, "set");
|
|
4079
|
+
return this;
|
|
4080
|
+
}
|
|
4081
|
+
if (previous !== void 0) {
|
|
4082
|
+
if (previous.value !== value) this.#dispose?.(previous.value, key, "set");
|
|
4083
|
+
this.#calculatedSize -= previous.size;
|
|
4084
|
+
this.#entries.delete(key);
|
|
4085
|
+
}
|
|
4086
|
+
while (this.#max !== 0 && this.#entries.size >= this.#max || this.#maxSize !== 0 && this.#calculatedSize + size > this.#maxSize) {
|
|
4087
|
+
const oldest = this.#entries.entries().next().value;
|
|
4088
|
+
if (oldest === void 0) break;
|
|
4089
|
+
this.#remove(oldest[0], oldest[1], "evict");
|
|
4090
|
+
}
|
|
4091
|
+
this.#entries.set(key, {
|
|
4092
|
+
value,
|
|
4093
|
+
size,
|
|
4094
|
+
start: this.#ttl === 0 ? 0 : performance.now()
|
|
4095
|
+
});
|
|
4096
|
+
this.#calculatedSize += size;
|
|
4097
|
+
return this;
|
|
4098
|
+
}
|
|
4099
|
+
/** Returns a value and makes a fresh entry most recently used. */
|
|
4100
|
+
get(key) {
|
|
4101
|
+
const entry = this.#entries.get(key);
|
|
4102
|
+
if (entry === void 0) return void 0;
|
|
4103
|
+
if (this.#isStale(entry)) {
|
|
4104
|
+
this.#remove(key, entry, "expire");
|
|
4105
|
+
return;
|
|
4106
|
+
}
|
|
4107
|
+
if (this.#updateAgeOnGet && this.#ttl !== 0) entry.start = performance.now();
|
|
4108
|
+
this.#entries.delete(key);
|
|
4109
|
+
this.#entries.set(key, entry);
|
|
4110
|
+
return entry.value;
|
|
4111
|
+
}
|
|
4112
|
+
/** Reports whether a fresh value is present without changing recency. */
|
|
4113
|
+
has(key) {
|
|
4114
|
+
const entry = this.#entries.get(key);
|
|
4115
|
+
return entry !== void 0 && !this.#isStale(entry);
|
|
4116
|
+
}
|
|
4117
|
+
/** Returns a fresh value without changing recency or removing stale data. */
|
|
4118
|
+
peek(key) {
|
|
4119
|
+
const entry = this.#entries.get(key);
|
|
4120
|
+
return entry === void 0 || this.#isStale(entry) ? void 0 : entry.value;
|
|
4121
|
+
}
|
|
4122
|
+
/** Removes a value, returning whether one was present. */
|
|
4123
|
+
delete(key) {
|
|
4124
|
+
const entry = this.#entries.get(key);
|
|
4125
|
+
if (entry === void 0) return false;
|
|
4126
|
+
this.#remove(key, entry, "delete");
|
|
4127
|
+
return true;
|
|
4128
|
+
}
|
|
4129
|
+
/** Removes every value from least to most recently used. */
|
|
4130
|
+
clear() {
|
|
4131
|
+
for (const [key, entry] of this.#entries) this.#dispose?.(entry.value, key, "delete");
|
|
4132
|
+
this.#entries.clear();
|
|
4133
|
+
this.#calculatedSize = 0;
|
|
4134
|
+
}
|
|
4135
|
+
/** Iterates fresh keys from most to least recently used. */
|
|
4136
|
+
*keys() {
|
|
4137
|
+
const entries = [...this.#entries.entries()];
|
|
4138
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
4139
|
+
const [key, entry] = entries[index];
|
|
4140
|
+
if (!this.#isStale(entry)) yield key;
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
/** Iterates fresh values from most to least recently used. */
|
|
4144
|
+
*values() {
|
|
4145
|
+
const entries = [...this.#entries.values()];
|
|
4146
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
4147
|
+
const entry = entries[index];
|
|
4148
|
+
if (!this.#isStale(entry)) yield entry.value;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
#entrySize(value, key) {
|
|
4152
|
+
if (this.#sizeCalculation === void 0) return 0;
|
|
4153
|
+
const size = this.#sizeCalculation(value, key);
|
|
4154
|
+
if (!Number.isInteger(size) || size <= 0) throw new TypeError("sizeCalculation return invalid (expect positive integer)");
|
|
4155
|
+
return size;
|
|
4156
|
+
}
|
|
4157
|
+
#isStale(entry) {
|
|
4158
|
+
return this.#ttl !== 0 && performance.now() - entry.start > this.#ttl;
|
|
4159
|
+
}
|
|
4160
|
+
#remove(key, entry, reason) {
|
|
4161
|
+
this.#dispose?.(entry.value, key, reason);
|
|
4162
|
+
this.#entries.delete(key);
|
|
4163
|
+
this.#calculatedSize -= entry.size;
|
|
4164
|
+
}
|
|
4165
|
+
};
|
|
4166
|
+
//#endregion
|
|
4167
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/template.ts
|
|
3638
4168
|
/** A string that bypasses HTML escaping when interpolated. */
|
|
3639
4169
|
var SafeString = class {
|
|
3640
4170
|
#value;
|
|
@@ -3856,7 +4386,8 @@ function parseTemplate(source) {
|
|
|
3856
4386
|
stack.push({
|
|
3857
4387
|
node: nested,
|
|
3858
4388
|
target: parentTarget,
|
|
3859
|
-
inverted: false
|
|
4389
|
+
inverted: false,
|
|
4390
|
+
chained: true
|
|
3860
4391
|
});
|
|
3861
4392
|
}
|
|
3862
4393
|
continue;
|
|
@@ -3874,13 +4405,15 @@ function parseTemplate(source) {
|
|
|
3874
4405
|
stack.push({
|
|
3875
4406
|
node,
|
|
3876
4407
|
target,
|
|
3877
|
-
inverted
|
|
4408
|
+
inverted,
|
|
4409
|
+
chained: false
|
|
3878
4410
|
});
|
|
3879
4411
|
target = node.body;
|
|
3880
4412
|
continue;
|
|
3881
4413
|
}
|
|
3882
4414
|
if (raw.startsWith("/")) {
|
|
3883
|
-
|
|
4415
|
+
let current = stack.pop();
|
|
4416
|
+
while (current?.chained) current = stack.pop();
|
|
3884
4417
|
if (!current || current.node.expression.name !== raw.slice(1).trim()) throw new Error(`Parse error: mismatched ${raw}`);
|
|
3885
4418
|
if (current.inverted) [current.node.body, current.node.inverse] = [current.node.inverse, current.node.body];
|
|
3886
4419
|
target = current.target;
|
|
@@ -4136,7 +4669,7 @@ var TemplateEngine = class {
|
|
|
4136
4669
|
}
|
|
4137
4670
|
};
|
|
4138
4671
|
//#endregion
|
|
4139
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
4672
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/prompt.ts
|
|
4140
4673
|
const OPENING_XML = /^<([a-z_-]+)(?:\s+[^>]*)?>$/;
|
|
4141
4674
|
/**
|
|
4142
4675
|
* Closing XML tag matcher, manual equivalent of `/^<\/([a-z_-]+)>$/` — avoids a
|
|
@@ -4508,7 +5041,7 @@ handlebars.registerHelper("includes", (collection, item) => {
|
|
|
4508
5041
|
*/
|
|
4509
5042
|
handlebars.registerHelper("not", (value) => !value);
|
|
4510
5043
|
handlebars.registerHelper("jsonStringify", (value) => JSON.stringify(value));
|
|
4511
|
-
const compiledTemplateCache =
|
|
5044
|
+
const compiledTemplateCache = new LRUCache({ max: 256 });
|
|
4512
5045
|
function compile(template) {
|
|
4513
5046
|
const cached = compiledTemplateCache.get(template);
|
|
4514
5047
|
if (cached) return cached;
|
|
@@ -4579,7 +5112,7 @@ function _usingCtx() {
|
|
|
4579
5112
|
};
|
|
4580
5113
|
}
|
|
4581
5114
|
//#endregion
|
|
4582
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
5115
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/ptree.ts
|
|
4583
5116
|
/**
|
|
4584
5117
|
* Process tree management utilities for Bun subprocesses.
|
|
4585
5118
|
*
|
|
@@ -4590,6 +5123,8 @@ function _usingCtx() {
|
|
|
4590
5123
|
*/
|
|
4591
5124
|
const LINUX_SUBREAPER_COMMAND_ENV = "OMP_PTREE_SUBREAPER_COMMAND";
|
|
4592
5125
|
const LINUX_SUBREAPER_BUN_BE_BUN_ENV = "OMP_PTREE_SUBREAPER_BUN_BE_BUN";
|
|
5126
|
+
const SUBREAPER_KILL_WINDOW_MS = 100;
|
|
5127
|
+
const SUBREAPER_KILL_POLL_MS = 5;
|
|
4593
5128
|
/**
|
|
4594
5129
|
* Build the Linux child-subreaper entrypoint.
|
|
4595
5130
|
*
|
|
@@ -4723,11 +5258,13 @@ var ChildProcess = class {
|
|
|
4723
5258
|
#stderrDone;
|
|
4724
5259
|
#exited;
|
|
4725
5260
|
#openPipeReaders = 1;
|
|
4726
|
-
#
|
|
4727
|
-
|
|
5261
|
+
#cutoff = false;
|
|
5262
|
+
/** Readers of piped stdio; the timeout cancels them so pending reads settle. */
|
|
5263
|
+
#pipeReaders = /* @__PURE__ */ new Set();
|
|
4728
5264
|
#timeoutTimer;
|
|
4729
5265
|
#stderrStream;
|
|
4730
5266
|
#terminating;
|
|
5267
|
+
#hardKillSweep;
|
|
4731
5268
|
#terminateGroup;
|
|
4732
5269
|
#hardKillTree;
|
|
4733
5270
|
#windowsRootProcess;
|
|
@@ -4750,28 +5287,23 @@ var ChildProcess = class {
|
|
|
4750
5287
|
}
|
|
4751
5288
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
4752
5289
|
this.#exited = promise;
|
|
4753
|
-
const drainCutoff = Promise.withResolvers();
|
|
4754
|
-
this.#drainCutoff = drainCutoff.promise;
|
|
4755
|
-
this.#resolveDrainCutoff = drainCutoff.resolve;
|
|
4756
|
-
const pipeCutoff = this.#drainCutoff;
|
|
4757
5290
|
this.#stderrDone = (async () => {
|
|
4758
5291
|
const reader = stderrStream.getReader();
|
|
5292
|
+
this.#pipeReaders.add(reader);
|
|
4759
5293
|
try {
|
|
4760
5294
|
for (;;) {
|
|
4761
|
-
|
|
4762
|
-
cutoff: false,
|
|
4763
|
-
r
|
|
4764
|
-
})), pipeCutoff.then(() => ({ cutoff: true }))]);
|
|
4765
|
-
if (chunk.cutoff) {
|
|
5295
|
+
if (this.#cutoff) {
|
|
4766
5296
|
await reader.cancel().catch(() => {});
|
|
4767
5297
|
break;
|
|
4768
5298
|
}
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
this.#
|
|
5299
|
+
const r = await reader.read();
|
|
5300
|
+
if (r.done) break;
|
|
5301
|
+
this.#stderrChunks?.push(r.value);
|
|
5302
|
+
this.#stderrTail += dec.decode(r.value, { stream: true });
|
|
4772
5303
|
trim();
|
|
4773
5304
|
}
|
|
4774
5305
|
} catch {}
|
|
5306
|
+
this.#pipeReaders.delete(reader);
|
|
4775
5307
|
this.#openPipeReaders--;
|
|
4776
5308
|
this.#stderrTail += dec.decode();
|
|
4777
5309
|
trim();
|
|
@@ -4848,11 +5380,16 @@ var ChildProcess = class {
|
|
|
4848
5380
|
this.#exitReasonPending = reason;
|
|
4849
5381
|
if (this.proc.exitCode !== null) this.#exitReason = reason;
|
|
4850
5382
|
}
|
|
5383
|
+
if (this.#hardKillSweep) return;
|
|
4851
5384
|
if (gracefulMs !== void 0 && gracefulMs < 0 && this.#hardKillTree && this.proc.exitCode === null) {
|
|
4852
5385
|
const root = Process.fromPid(this.proc.pid);
|
|
4853
5386
|
if (root) {
|
|
4854
|
-
root.
|
|
4855
|
-
this.#
|
|
5387
|
+
const sweep = this.#hardKillSubreaperTree(root).catch((e) => void 0);
|
|
5388
|
+
this.#hardKillSweep = sweep;
|
|
5389
|
+
this.#terminating = sweep;
|
|
5390
|
+
sweep.finally(() => {
|
|
5391
|
+
if (this.#hardKillSweep === sweep) this.#hardKillSweep = void 0;
|
|
5392
|
+
});
|
|
4856
5393
|
return;
|
|
4857
5394
|
}
|
|
4858
5395
|
}
|
|
@@ -4873,11 +5410,56 @@ var ChildProcess = class {
|
|
|
4873
5410
|
gracefulMs,
|
|
4874
5411
|
group: this.#terminateGroup
|
|
4875
5412
|
};
|
|
4876
|
-
this.#terminating =
|
|
5413
|
+
this.#terminating = this.#terminateTree(options);
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
/**
|
|
5417
|
+
* Native tree termination. A native handle that cannot be resolved or used
|
|
5418
|
+
* — the PID left the process table, or a `bun test --isolate` realm whose
|
|
5419
|
+
* napi classes another realm superseded ("Value is not an instance of class
|
|
5420
|
+
* `Process`") — must not leave the child running, so signal it directly.
|
|
5421
|
+
*/
|
|
5422
|
+
#terminateTree(options) {
|
|
5423
|
+
const gracefulMs = options?.gracefulMs ?? 1e3;
|
|
5424
|
+
try {
|
|
5425
|
+
const root = this.#windowsRootProcess ?? Process.fromPid(this.proc.pid);
|
|
5426
|
+
if (root) return root.terminate(options).catch(() => this.#signalChild(gracefulMs));
|
|
5427
|
+
} catch {}
|
|
5428
|
+
return this.#signalChild(gracefulMs);
|
|
5429
|
+
}
|
|
5430
|
+
/** SIGTERM the child (its group when it leads one), then SIGKILL if it outlives `gracefulMs`. */
|
|
5431
|
+
async #signalChild(gracefulMs) {
|
|
5432
|
+
const signal = (name) => {
|
|
5433
|
+
try {
|
|
5434
|
+
if (this.#terminateGroup && process.platform !== "win32") process.kill(-this.proc.pid, name);
|
|
5435
|
+
else this.proc.kill(name);
|
|
5436
|
+
} catch {}
|
|
5437
|
+
};
|
|
5438
|
+
if (gracefulMs >= 0) {
|
|
5439
|
+
signal("SIGTERM");
|
|
5440
|
+
await Promise.race([this.proc.exited, Bun.sleep(gracefulMs)]);
|
|
5441
|
+
}
|
|
5442
|
+
if (this.proc.exitCode === null && this.proc.signalCode === null) signal("SIGKILL");
|
|
5443
|
+
}
|
|
5444
|
+
async #hardKillSubreaperTree(root) {
|
|
5445
|
+
try {
|
|
5446
|
+
const deadline = Date.now() + SUBREAPER_KILL_WINDOW_MS;
|
|
5447
|
+
let emptySweeps = 0;
|
|
5448
|
+
while (emptySweeps < 2 && Date.now() < deadline) {
|
|
5449
|
+
const children = root.children();
|
|
5450
|
+
if (children.length === 0) emptySweeps++;
|
|
5451
|
+
else {
|
|
5452
|
+
emptySweeps = 0;
|
|
5453
|
+
for (const child of children) child.killTree(9);
|
|
5454
|
+
}
|
|
5455
|
+
if (emptySweeps < 2) await Bun.sleep(SUBREAPER_KILL_POLL_MS);
|
|
5456
|
+
}
|
|
5457
|
+
} finally {
|
|
5458
|
+
root.killTree(9);
|
|
4877
5459
|
}
|
|
4878
5460
|
}
|
|
4879
5461
|
async #throwIfAborted() {
|
|
4880
|
-
const exitReason = this
|
|
5462
|
+
const exitReason = this.#exitReason ?? this.#exitReasonPending;
|
|
4881
5463
|
if (!exitReason?.aborted) return;
|
|
4882
5464
|
if (this.#terminating) await this.#terminating;
|
|
4883
5465
|
throw exitReason;
|
|
@@ -4895,45 +5477,43 @@ var ChildProcess = class {
|
|
|
4895
5477
|
async #readStream(stream) {
|
|
4896
5478
|
this.#openPipeReaders++;
|
|
4897
5479
|
const reader = stream.getReader();
|
|
5480
|
+
this.#pipeReaders.add(reader);
|
|
4898
5481
|
const dec = new TextDecoder();
|
|
4899
5482
|
let out = "";
|
|
4900
5483
|
try {
|
|
4901
5484
|
for (;;) {
|
|
4902
|
-
|
|
4903
|
-
cutoff: false,
|
|
4904
|
-
r
|
|
4905
|
-
})), this.#drainCutoff.then(() => ({ cutoff: true }))]);
|
|
4906
|
-
if (chunk.cutoff) {
|
|
5485
|
+
if (this.#cutoff) {
|
|
4907
5486
|
await reader.cancel().catch(() => {});
|
|
4908
5487
|
break;
|
|
4909
5488
|
}
|
|
4910
|
-
|
|
4911
|
-
|
|
5489
|
+
const r = await reader.read();
|
|
5490
|
+
if (r.done) break;
|
|
5491
|
+
out += dec.decode(r.value, { stream: true });
|
|
4912
5492
|
}
|
|
4913
5493
|
} catch {}
|
|
5494
|
+
this.#pipeReaders.delete(reader);
|
|
4914
5495
|
this.#openPipeReaders--;
|
|
4915
5496
|
return out + dec.decode();
|
|
4916
5497
|
}
|
|
4917
5498
|
async #readBytes() {
|
|
4918
5499
|
const reader = this.proc.stdout.getReader();
|
|
4919
5500
|
this.#openPipeReaders++;
|
|
5501
|
+
this.#pipeReaders.add(reader);
|
|
4920
5502
|
const chunks = [];
|
|
4921
5503
|
let length = 0;
|
|
4922
5504
|
try {
|
|
4923
5505
|
for (;;) {
|
|
4924
|
-
|
|
4925
|
-
cutoff: false,
|
|
4926
|
-
r
|
|
4927
|
-
})), this.#drainCutoff.then(() => ({ cutoff: true }))]);
|
|
4928
|
-
if (chunk.cutoff) {
|
|
5506
|
+
if (this.#cutoff) {
|
|
4929
5507
|
await reader.cancel().catch(() => {});
|
|
4930
5508
|
break;
|
|
4931
5509
|
}
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
5510
|
+
const r = await reader.read();
|
|
5511
|
+
if (r.done) break;
|
|
5512
|
+
chunks.push(r.value);
|
|
5513
|
+
length += r.value.byteLength;
|
|
4935
5514
|
}
|
|
4936
5515
|
} catch {} finally {
|
|
5516
|
+
this.#pipeReaders.delete(reader);
|
|
4937
5517
|
this.#openPipeReaders--;
|
|
4938
5518
|
reader.releaseLock();
|
|
4939
5519
|
}
|
|
@@ -4996,10 +5576,11 @@ var ChildProcess = class {
|
|
|
4996
5576
|
};
|
|
4997
5577
|
}
|
|
4998
5578
|
attachSignal(signal) {
|
|
5579
|
+
const cleanup = this.#exited.catch(() => {});
|
|
4999
5580
|
const onAbort = () => this.kill(new AbortError(signal.reason, "<cancelled>"));
|
|
5000
5581
|
if (signal.aborted) return void onAbort();
|
|
5001
5582
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
5002
|
-
|
|
5583
|
+
cleanup.finally(() => signal.removeEventListener("abort", onAbort));
|
|
5003
5584
|
}
|
|
5004
5585
|
#clearTimeout() {
|
|
5005
5586
|
if (!this.#timeoutTimer) return;
|
|
@@ -5011,7 +5592,8 @@ var ChildProcess = class {
|
|
|
5011
5592
|
this.#exited.catch(() => {});
|
|
5012
5593
|
const timer = setTimeout(() => {
|
|
5013
5594
|
if (this.proc.exitCode === null || this.#openPipeReaders > 0 && (this.#terminateGroup || this.#windowsRootProcess)) this.kill(new TimeoutError(ms, this.#stderrTail), -1);
|
|
5014
|
-
this.#
|
|
5595
|
+
this.#cutoff = true;
|
|
5596
|
+
for (const reader of this.#pipeReaders) reader.cancel().catch(() => {});
|
|
5015
5597
|
}, ms);
|
|
5016
5598
|
timer.unref?.();
|
|
5017
5599
|
this.#timeoutTimer = timer;
|
|
@@ -5050,22 +5632,22 @@ function spawnInternal(cmd, opts, retainFullStderr) {
|
|
|
5050
5632
|
/** Spawn, wait, and return captured output. */
|
|
5051
5633
|
async function exec(cmd, opts) {
|
|
5052
5634
|
try {
|
|
5053
|
-
var _usingCtx$
|
|
5635
|
+
var _usingCtx$2 = _usingCtx();
|
|
5054
5636
|
const { input, stderr, allowAbort, allowNonZero, ...spawnOpts } = opts ?? {};
|
|
5055
5637
|
const stdin = typeof input === "string" ? Buffer.from(input) : input;
|
|
5056
5638
|
const resolved = stdin === void 0 ? spawnOpts : {
|
|
5057
5639
|
...spawnOpts,
|
|
5058
5640
|
stdin
|
|
5059
5641
|
};
|
|
5060
|
-
return await _usingCtx$
|
|
5642
|
+
return await _usingCtx$2.u(spawnInternal(cmd, resolved, stderr === "full")).wait({
|
|
5061
5643
|
stderr,
|
|
5062
5644
|
allowAbort,
|
|
5063
5645
|
allowNonZero
|
|
5064
5646
|
});
|
|
5065
5647
|
} catch (_) {
|
|
5066
|
-
_usingCtx$
|
|
5648
|
+
_usingCtx$2.e = _;
|
|
5067
5649
|
} finally {
|
|
5068
|
-
_usingCtx$
|
|
5650
|
+
_usingCtx$2.d();
|
|
5069
5651
|
}
|
|
5070
5652
|
}
|
|
5071
5653
|
/** Combine AbortSignals and timeout values into a single signal. */
|
|
@@ -5091,19 +5673,21 @@ function combineSignals(...signals) {
|
|
|
5091
5673
|
}
|
|
5092
5674
|
}
|
|
5093
5675
|
//#endregion
|
|
5094
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
5676
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/snowflake.ts
|
|
5095
5677
|
function randu32() {
|
|
5096
5678
|
return crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(1))[0];
|
|
5097
5679
|
}
|
|
5098
5680
|
const EPOCH = 14200704e5;
|
|
5099
5681
|
const MAX_SEQ = 4194303;
|
|
5682
|
+
const MAX_DT = 2 ** 42 - 1;
|
|
5100
5683
|
let Snowflake;
|
|
5101
5684
|
(function(_Snowflake) {
|
|
5102
5685
|
const PATTERN = _Snowflake.PATTERN = /^[0-9a-f]{16}$/;
|
|
5103
5686
|
_Snowflake.EPOCH_TIMESTAMP = EPOCH;
|
|
5104
5687
|
_Snowflake.MAX_SEQUENCE = MAX_SEQ;
|
|
5688
|
+
_Snowflake.MAX_TIMESTAMP = EPOCH + MAX_DT;
|
|
5105
5689
|
function formatParts(dt, seq) {
|
|
5106
|
-
return (BigInt(dt) << 22n | BigInt(seq)).toString(16).padStart(16, "0");
|
|
5690
|
+
return (BigInt(Math.min(Math.max(dt, 0), MAX_DT)) << 22n | BigInt(seq)).toString(16).padStart(16, "0");
|
|
5107
5691
|
}
|
|
5108
5692
|
_Snowflake.formatParts = formatParts;
|
|
5109
5693
|
class Source {
|
|
@@ -5170,9 +5754,11 @@ let Snowflake;
|
|
|
5170
5754
|
_Snowflake.getDate = getDate;
|
|
5171
5755
|
})(Snowflake || (Snowflake = {}));
|
|
5172
5756
|
//#endregion
|
|
5173
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
5757
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/stream.ts
|
|
5174
5758
|
const trailingEvents = /* @__PURE__ */ new WeakSet();
|
|
5175
5759
|
const LF = 10;
|
|
5760
|
+
const CR = 13;
|
|
5761
|
+
const CONCAT_SINK_RETAIN_BYTES = 1048576;
|
|
5176
5762
|
async function* readJsonl(stream, signal) {
|
|
5177
5763
|
const buffer = new ConcatSink();
|
|
5178
5764
|
const source = abortableSource(stream, signal);
|
|
@@ -5193,9 +5779,19 @@ async function* readJsonl(stream, signal) {
|
|
|
5193
5779
|
throw err;
|
|
5194
5780
|
}
|
|
5195
5781
|
}
|
|
5782
|
+
/**
|
|
5783
|
+
* Amortized byte accumulator for chunked stream readers.
|
|
5784
|
+
*
|
|
5785
|
+
* Holds the unconsumed tail of a stream in a single growing `Buffer` so that
|
|
5786
|
+
* appending N chunks costs O(total bytes) instead of re-copying the whole
|
|
5787
|
+
* prefix per chunk. Backs {@link readLines}, {@link readJsonl} and
|
|
5788
|
+
* {@link readSseEvents}; also usable directly when a reader needs its own
|
|
5789
|
+
* framing loop (see `consume` and `flush`).
|
|
5790
|
+
*/
|
|
5196
5791
|
var ConcatSink = class {
|
|
5197
5792
|
#space;
|
|
5198
5793
|
#length = 0;
|
|
5794
|
+
#skipLeadingLf = false;
|
|
5199
5795
|
#ensureCapacity(size) {
|
|
5200
5796
|
const space = this.#space;
|
|
5201
5797
|
if (space && space.length >= size) return space;
|
|
@@ -5205,6 +5801,20 @@ var ConcatSink = class {
|
|
|
5205
5801
|
this.#space = next;
|
|
5206
5802
|
return next;
|
|
5207
5803
|
}
|
|
5804
|
+
/** Drop or right-size an oversized buffer after the buffered length shrank. */
|
|
5805
|
+
#releaseCapacity() {
|
|
5806
|
+
const space = this.#space;
|
|
5807
|
+
if (!space || space.length <= CONCAT_SINK_RETAIN_BYTES) return;
|
|
5808
|
+
const length = this.#length;
|
|
5809
|
+
if (length === 0) {
|
|
5810
|
+
this.#space = void 0;
|
|
5811
|
+
return;
|
|
5812
|
+
}
|
|
5813
|
+
if (length * 4 > space.length) return;
|
|
5814
|
+
const next = Buffer.allocUnsafe(length);
|
|
5815
|
+
space.copy(next, 0, 0, length);
|
|
5816
|
+
this.#space = next;
|
|
5817
|
+
}
|
|
5208
5818
|
append(chunk) {
|
|
5209
5819
|
const n = chunk.length;
|
|
5210
5820
|
if (!n) return;
|
|
@@ -5213,24 +5823,44 @@ var ConcatSink = class {
|
|
|
5213
5823
|
this.#length += n;
|
|
5214
5824
|
}
|
|
5215
5825
|
reset(chunk) {
|
|
5826
|
+
this.#length = 0;
|
|
5827
|
+
this.#releaseCapacity();
|
|
5216
5828
|
const n = chunk.length;
|
|
5217
|
-
if (!n)
|
|
5218
|
-
this.#length = 0;
|
|
5219
|
-
return;
|
|
5220
|
-
}
|
|
5829
|
+
if (!n) return;
|
|
5221
5830
|
this.#ensureCapacity(n).set(chunk, 0);
|
|
5222
5831
|
this.#length = n;
|
|
5223
5832
|
}
|
|
5224
5833
|
get isEmpty() {
|
|
5225
5834
|
return this.#length === 0;
|
|
5226
5835
|
}
|
|
5836
|
+
/**
|
|
5837
|
+
* The buffered bytes as a live view — invalidated by the next `append`,
|
|
5838
|
+
* `reset` or `consume`.
|
|
5839
|
+
*/
|
|
5227
5840
|
flush() {
|
|
5228
5841
|
if (!this.#length) return void 0;
|
|
5229
5842
|
return this.#space.subarray(0, this.#length);
|
|
5230
5843
|
}
|
|
5844
|
+
/** Drop the first `count` buffered bytes, keeping the remainder. */
|
|
5845
|
+
consume(count) {
|
|
5846
|
+
if (count <= 0) return;
|
|
5847
|
+
if (count >= this.#length) this.#length = 0;
|
|
5848
|
+
else {
|
|
5849
|
+
this.#space.copyWithin(0, count, this.#length);
|
|
5850
|
+
this.#length -= count;
|
|
5851
|
+
}
|
|
5852
|
+
this.#releaseCapacity();
|
|
5853
|
+
}
|
|
5231
5854
|
clear() {
|
|
5232
5855
|
this.#length = 0;
|
|
5856
|
+
this.#releaseCapacity();
|
|
5233
5857
|
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Append a chunk and yield each complete LF-delimited line.
|
|
5860
|
+
*
|
|
5861
|
+
* Yielded lines are owned snapshots. Unlike {@link flush}, they remain
|
|
5862
|
+
* valid after this sink or the input chunk is mutated.
|
|
5863
|
+
*/
|
|
5234
5864
|
*appendAndFlushLines(chunk) {
|
|
5235
5865
|
let pos = 0;
|
|
5236
5866
|
while (pos < chunk.length) {
|
|
@@ -5241,30 +5871,37 @@ var ConcatSink = class {
|
|
|
5241
5871
|
}
|
|
5242
5872
|
const suffix = chunk.subarray(pos, nl);
|
|
5243
5873
|
pos = nl + 1;
|
|
5244
|
-
if (this.isEmpty) yield suffix;
|
|
5874
|
+
if (this.isEmpty) yield new Uint8Array(suffix);
|
|
5245
5875
|
else {
|
|
5246
5876
|
this.append(suffix);
|
|
5247
5877
|
const payload = this.flush();
|
|
5248
5878
|
if (payload) {
|
|
5249
|
-
yield payload;
|
|
5879
|
+
yield new Uint8Array(payload);
|
|
5250
5880
|
this.clear();
|
|
5251
5881
|
}
|
|
5252
5882
|
}
|
|
5253
5883
|
}
|
|
5254
5884
|
}
|
|
5255
5885
|
appendAndFlushText(chunk, decoder) {
|
|
5256
|
-
|
|
5257
|
-
if (
|
|
5258
|
-
|
|
5886
|
+
let start = 0;
|
|
5887
|
+
if (this.#skipLeadingLf) {
|
|
5888
|
+
if (chunk.length === 0) return void 0;
|
|
5889
|
+
this.#skipLeadingLf = false;
|
|
5890
|
+
if (chunk[0] === LF) start = 1;
|
|
5891
|
+
}
|
|
5892
|
+
const lastLineEnd = Math.max(chunk.lastIndexOf(LF), chunk.lastIndexOf(CR));
|
|
5893
|
+
if (lastLineEnd < start) {
|
|
5894
|
+
if (start < chunk.length) this.append(chunk.subarray(start));
|
|
5259
5895
|
return;
|
|
5260
5896
|
}
|
|
5261
|
-
const completeEnd =
|
|
5897
|
+
const completeEnd = lastLineEnd + 1;
|
|
5898
|
+
this.#skipLeadingLf = chunk[lastLineEnd] === CR && completeEnd === chunk.length;
|
|
5262
5899
|
let text;
|
|
5263
5900
|
if (this.isEmpty) {
|
|
5264
|
-
const complete = completeEnd === chunk.length ? chunk : chunk.subarray(
|
|
5901
|
+
const complete = start === 0 && completeEnd === chunk.length ? chunk : chunk.subarray(start, completeEnd);
|
|
5265
5902
|
text = decoder.decode(complete);
|
|
5266
5903
|
} else {
|
|
5267
|
-
this.append(
|
|
5904
|
+
this.append(chunk.subarray(start, completeEnd));
|
|
5268
5905
|
text = decoder.decode(this.flush());
|
|
5269
5906
|
this.clear();
|
|
5270
5907
|
}
|
|
@@ -5294,13 +5931,13 @@ var ConcatSink = class {
|
|
|
5294
5931
|
const { values, error, read, done } = Bun.JSONL.parseChunk(space, 0, total);
|
|
5295
5932
|
if (values.length > 0) yield* values;
|
|
5296
5933
|
if (error) throw error;
|
|
5297
|
-
if (done)
|
|
5298
|
-
|
|
5299
|
-
|
|
5934
|
+
if (done) this.#length = 0;
|
|
5935
|
+
else {
|
|
5936
|
+
const rem = total - read;
|
|
5937
|
+
if (rem < total) space.copyWithin(0, read, total);
|
|
5938
|
+
this.#length = rem;
|
|
5300
5939
|
}
|
|
5301
|
-
|
|
5302
|
-
if (rem < total) space.copyWithin(0, read, total);
|
|
5303
|
-
this.#length = rem;
|
|
5940
|
+
this.#releaseCapacity();
|
|
5304
5941
|
}
|
|
5305
5942
|
};
|
|
5306
5943
|
function notifySseEventObserver(observer, event) {
|
|
@@ -5315,51 +5952,104 @@ function isRecoverableTrailingJson(data) {
|
|
|
5315
5952
|
const recovered = parseStreamingJson(data);
|
|
5316
5953
|
return typeof recovered === "object" && recovered !== null;
|
|
5317
5954
|
}
|
|
5318
|
-
|
|
5319
|
-
|
|
5955
|
+
/**
|
|
5956
|
+
* Shared `data:`-line framing for {@link readSseJson} and
|
|
5957
|
+
* {@link readSseJsonOrText}: skips empty events, stops at the OpenAI `[DONE]`
|
|
5958
|
+
* sentinel (reporting it through `onDone`), notifies the diagnostic observer,
|
|
5959
|
+
* and treats a container-shaped stream tail as a clean end of iteration.
|
|
5960
|
+
*/
|
|
5961
|
+
async function* readSseFrames(stream, signal, onEvent, onDone) {
|
|
5962
|
+
for await (const sse of readSseEvents(stream, signal, onEvent ? { captureRaw: true } : void 0)) {
|
|
5320
5963
|
const isTrailing = trailingEvents.has(sse);
|
|
5321
5964
|
notifySseEventObserver(onEvent, sse);
|
|
5322
5965
|
const data = sse.data;
|
|
5323
5966
|
if (data === "" || data === "[DONE]") {
|
|
5324
|
-
if (data === "[DONE]")
|
|
5967
|
+
if (data === "[DONE]") {
|
|
5968
|
+
onDone?.();
|
|
5969
|
+
return;
|
|
5970
|
+
}
|
|
5325
5971
|
continue;
|
|
5326
5972
|
}
|
|
5327
5973
|
try {
|
|
5328
|
-
yield
|
|
5974
|
+
yield {
|
|
5975
|
+
ok: true,
|
|
5976
|
+
value: JSON.parse(data)
|
|
5977
|
+
};
|
|
5329
5978
|
} catch (err) {
|
|
5330
5979
|
if (err instanceof SyntaxError && isTrailing && isRecoverableTrailingJson(data)) return;
|
|
5980
|
+
if (err instanceof SyntaxError) {
|
|
5981
|
+
yield {
|
|
5982
|
+
ok: false,
|
|
5983
|
+
raw: data,
|
|
5984
|
+
error: err
|
|
5985
|
+
};
|
|
5986
|
+
continue;
|
|
5987
|
+
}
|
|
5331
5988
|
throw err;
|
|
5332
5989
|
}
|
|
5333
5990
|
}
|
|
5334
5991
|
}
|
|
5992
|
+
async function* readSseJson(stream, signal, onEvent) {
|
|
5993
|
+
for await (const frame of readSseFrames(stream, signal, onEvent)) {
|
|
5994
|
+
if (!frame.ok) throw frame.error;
|
|
5995
|
+
yield frame.value;
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5998
|
+
/**
|
|
5999
|
+
* Like {@link readSseJson}, but a `data:` frame that is not valid JSON is yielded
|
|
6000
|
+
* as its raw text instead of raising a `SyntaxError`. Cut-off container-shaped
|
|
6001
|
+
* stream tails stay recoverable, exactly as they are in {@link readSseJson}.
|
|
6002
|
+
*
|
|
6003
|
+
* Consumers that only understand objects must treat a `string` yield as a
|
|
6004
|
+
* transport-level failure (for example a `429 Too Many Requests` or an HTML
|
|
6005
|
+
* throttle page from a reverse proxy that already committed to the stream). This
|
|
6006
|
+
* exists because `readSseJson`'s baseline consumers span unrelated transports
|
|
6007
|
+
* whose error handling a text yield would subtly change; new call sites opt in.
|
|
6008
|
+
*
|
|
6009
|
+
* Note that the text lane is only the frames `JSON.parse` *rejected*: a frame
|
|
6010
|
+
* carrying a JSON-encoded string (`data: "429 Too Many Requests"`) parses, so it
|
|
6011
|
+
* is yielded as that string and is indistinguishable from a rejected frame by
|
|
6012
|
+
* type alone. Consumers branching on `typeof === "string"` therefore see both,
|
|
6013
|
+
* which is the safe direction — each is classified as text rather than trusted as
|
|
6014
|
+
* an event object.
|
|
6015
|
+
*/
|
|
6016
|
+
async function* readSseJsonOrText(stream, signal, onEvent, onDone) {
|
|
6017
|
+
for await (const frame of readSseFrames(stream, signal, onEvent, onDone)) if (!frame.ok) yield frame.raw;
|
|
6018
|
+
else yield frame.value;
|
|
6019
|
+
}
|
|
5335
6020
|
const SSE_DECODER = new TextDecoder("utf-8");
|
|
6021
|
+
/**
|
|
6022
|
+
* `raw` of every event dispatched with capture off. Shared instead of one
|
|
6023
|
+
* fresh array per event; frozen so an in-place mutation throws rather than
|
|
6024
|
+
* leaking lines into every other event (consumers copy or reassign `raw`).
|
|
6025
|
+
*/
|
|
6026
|
+
const EMPTY_RAW = Object.freeze([]);
|
|
5336
6027
|
function flushSseEvent(state) {
|
|
5337
6028
|
if (state.event === null && state.data === null && state.id === void 0 && state.retry === void 0) {
|
|
5338
|
-
state.raw = [];
|
|
6029
|
+
if (state.raw !== null) state.raw = [];
|
|
5339
6030
|
return null;
|
|
5340
6031
|
}
|
|
5341
6032
|
const event = {
|
|
5342
6033
|
event: state.event,
|
|
5343
6034
|
data: state.data ?? "",
|
|
5344
|
-
raw: state.raw
|
|
6035
|
+
raw: state.raw ?? EMPTY_RAW
|
|
5345
6036
|
};
|
|
5346
6037
|
if (state.id !== void 0) event.id = state.id;
|
|
5347
6038
|
if (state.retry !== void 0) event.retry = state.retry;
|
|
5348
6039
|
state.event = null;
|
|
5349
6040
|
state.data = null;
|
|
5350
|
-
state.raw = [];
|
|
6041
|
+
if (state.raw !== null) state.raw = [];
|
|
5351
6042
|
state.id = void 0;
|
|
5352
6043
|
state.retry = void 0;
|
|
5353
6044
|
return event;
|
|
5354
6045
|
}
|
|
5355
6046
|
function pushSseLine(line, state) {
|
|
5356
|
-
if (line.charCodeAt(line.length - 1) === 13) line = line.slice(0, -1);
|
|
5357
6047
|
if (line.length === 0) return flushSseEvent(state);
|
|
5358
6048
|
if (line.charCodeAt(0) === 58) {
|
|
5359
|
-
state.raw
|
|
6049
|
+
state.raw?.push(line);
|
|
5360
6050
|
return null;
|
|
5361
6051
|
}
|
|
5362
|
-
state.raw
|
|
6052
|
+
state.raw?.push(line);
|
|
5363
6053
|
const colon = line.indexOf(":");
|
|
5364
6054
|
const fieldName = colon === -1 ? line : line.slice(0, colon);
|
|
5365
6055
|
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
@@ -5389,31 +6079,12 @@ function pushSseLine(line, state) {
|
|
|
5389
6079
|
}
|
|
5390
6080
|
return null;
|
|
5391
6081
|
}
|
|
5392
|
-
|
|
5393
|
-
* Stream raw Server-Sent Events from an HTTP response body.
|
|
5394
|
-
*
|
|
5395
|
-
* Yields one `ServerSentEvent` per blank-line dispatch. The consumer is
|
|
5396
|
-
* responsible for parsing `data` (e.g. JSON, plain text, error envelope).
|
|
5397
|
-
* Use `readSseJson` instead when every event is a single `data:` JSON object
|
|
5398
|
-
* and you don't need access to the `event:` field.
|
|
5399
|
-
*
|
|
5400
|
-
* Internally backed by a Buffer-based reader (`ConcatSink`) that batches all
|
|
5401
|
-
* complete lines in each source chunk into one UTF-8 decode.
|
|
5402
|
-
*
|
|
5403
|
-
* @example
|
|
5404
|
-
* ```ts
|
|
5405
|
-
* for await (const sse of readSseEvents(response.body!)) {
|
|
5406
|
-
* if (sse.event === "ping") continue;
|
|
5407
|
-
* const obj = JSON.parse(sse.data);
|
|
5408
|
-
* }
|
|
5409
|
-
* ```
|
|
5410
|
-
*/
|
|
5411
|
-
async function* readSseEvents(stream, signal) {
|
|
6082
|
+
async function* readSseEvents(stream, signal, options) {
|
|
5412
6083
|
const lineBuffer = new ConcatSink();
|
|
5413
6084
|
const state = {
|
|
5414
6085
|
event: null,
|
|
5415
6086
|
data: null,
|
|
5416
|
-
raw: []
|
|
6087
|
+
raw: options?.captureRaw === true ? [] : null
|
|
5417
6088
|
};
|
|
5418
6089
|
const source = abortableSource(stream, signal);
|
|
5419
6090
|
try {
|
|
@@ -5422,10 +6093,16 @@ async function* readSseEvents(stream, signal) {
|
|
|
5422
6093
|
if (text === void 0) continue;
|
|
5423
6094
|
let start = 0;
|
|
5424
6095
|
while (start < text.length) {
|
|
5425
|
-
|
|
5426
|
-
|
|
6096
|
+
let lineEnd = start;
|
|
6097
|
+
while (lineEnd < text.length) {
|
|
6098
|
+
const code = text.charCodeAt(lineEnd);
|
|
6099
|
+
if (code === LF || code === CR) break;
|
|
6100
|
+
lineEnd++;
|
|
6101
|
+
}
|
|
6102
|
+
const event = pushSseLine(text.slice(start, lineEnd), state);
|
|
5427
6103
|
if (event) yield event;
|
|
5428
|
-
|
|
6104
|
+
if (text.charCodeAt(lineEnd) === CR && text.charCodeAt(lineEnd + 1) === LF) lineEnd++;
|
|
6105
|
+
start = lineEnd + 1;
|
|
5429
6106
|
}
|
|
5430
6107
|
}
|
|
5431
6108
|
if (!lineBuffer.isEmpty) {
|
|
@@ -5450,7 +6127,14 @@ async function* readSseEvents(stream, signal) {
|
|
|
5450
6127
|
}
|
|
5451
6128
|
}
|
|
5452
6129
|
//#endregion
|
|
5453
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
6130
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/temp.ts
|
|
6131
|
+
/**
|
|
6132
|
+
* Owned temporary directory, removed via `remove()` or `using`/`await using`.
|
|
6133
|
+
*
|
|
6134
|
+
* Prefixes follow `mkdtemp`: `"@name-"` creates under the OS temp dir, any other
|
|
6135
|
+
* prefix is a path (a bare `"name-"` lands in the process cwd), and no prefix
|
|
6136
|
+
* means `"@pi-temp-"`.
|
|
6137
|
+
*/
|
|
5454
6138
|
var TempDir = class TempDir {
|
|
5455
6139
|
#path;
|
|
5456
6140
|
constructor(path) {
|
|
@@ -5513,7 +6197,6 @@ const kRetryableRemoveErrorCodes = /* @__PURE__ */ new Set([
|
|
|
5513
6197
|
"EPERM",
|
|
5514
6198
|
"ENOTEMPTY"
|
|
5515
6199
|
]);
|
|
5516
|
-
const kSleepBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
5517
6200
|
/** Removes a path recursively, retrying transient Windows deletion failures. */
|
|
5518
6201
|
async function removeWithRetries(target) {
|
|
5519
6202
|
for (let attempt = 0;; attempt++) try {
|
|
@@ -5521,6 +6204,7 @@ async function removeWithRetries(target) {
|
|
|
5521
6204
|
return;
|
|
5522
6205
|
} catch (err) {
|
|
5523
6206
|
if (!shouldRetryRemove(err, attempt)) throw err;
|
|
6207
|
+
if (attempt === 0) Bun.gc(true);
|
|
5524
6208
|
await Bun.sleep(kRemoveRetryDelayMs);
|
|
5525
6209
|
}
|
|
5526
6210
|
}
|
|
@@ -5530,7 +6214,8 @@ function removeSyncWithRetries(target) {
|
|
|
5530
6214
|
return;
|
|
5531
6215
|
} catch (err) {
|
|
5532
6216
|
if (!shouldRetryRemove(err, attempt)) throw err;
|
|
5533
|
-
|
|
6217
|
+
if (attempt === 0) Bun.gc(true);
|
|
6218
|
+
Bun.sleepSync(kRemoveRetryDelayMs);
|
|
5534
6219
|
}
|
|
5535
6220
|
}
|
|
5536
6221
|
function shouldRetryRemove(err, attempt) {
|
|
@@ -5539,15 +6224,8 @@ function shouldRetryRemove(err, attempt) {
|
|
|
5539
6224
|
function isRetryableRemoveError(err) {
|
|
5540
6225
|
return typeof err === "object" && err !== null && "code" in err && typeof err.code === "string" && kRetryableRemoveErrorCodes.has(err.code);
|
|
5541
6226
|
}
|
|
5542
|
-
function sleepSync(ms) {
|
|
5543
|
-
if ("sleepSync" in Bun && typeof Bun.sleepSync === "function") {
|
|
5544
|
-
Bun.sleepSync(ms);
|
|
5545
|
-
return;
|
|
5546
|
-
}
|
|
5547
|
-
Atomics.wait(kSleepBuffer, 0, 0, ms);
|
|
5548
|
-
}
|
|
5549
6227
|
//#endregion
|
|
5550
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
6228
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/tls-fetch.ts
|
|
5551
6229
|
/**
|
|
5552
6230
|
* `NODE_EXTRA_CA_CERTS` shim for Bun's `fetch`.
|
|
5553
6231
|
*
|
|
@@ -5656,7 +6334,7 @@ function wrapFetchForExtraCa(fetchImpl) {
|
|
|
5656
6334
|
}, fetchImpl.preconnect ? { preconnect: fetchImpl.preconnect } : {}, { [EXTRA_CA_FETCH_MARKER]: true });
|
|
5657
6335
|
}
|
|
5658
6336
|
//#endregion
|
|
5659
|
-
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.
|
|
6337
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.12.6/node_modules/@oh-my-pi/pi-utils/src/index.ts
|
|
5660
6338
|
function isPlainObject(val) {
|
|
5661
6339
|
return Object.getPrototypeOf(val) === Object.prototype || Array.isArray(val);
|
|
5662
6340
|
}
|
|
@@ -5667,5 +6345,41 @@ function structuredCloneJSON(value) {
|
|
|
5667
6345
|
} catch {}
|
|
5668
6346
|
return JSON.parse(JSON.stringify(value));
|
|
5669
6347
|
}
|
|
6348
|
+
/**
|
|
6349
|
+
* Deep-copies a JSON-shaped tree (arrays and plain or null-prototype objects)
|
|
6350
|
+
* so later mutation of the source can never reach the copy. Strings and other
|
|
6351
|
+
* primitives are immutable and shared, so the cost is O(containers) instead of
|
|
6352
|
+
* `structuredClone`'s O(bytes) — the difference matters for hot paths that
|
|
6353
|
+
* re-copy large string payloads (e.g. streamed tool-call arguments) per delta.
|
|
6354
|
+
*
|
|
6355
|
+
* Copies own enumerable string keys only. Any other object (class instance,
|
|
6356
|
+
* Date, Map, inherited-prototype object) is copied with {@link structuredCloneJSON}.
|
|
6357
|
+
* Not cycle-safe: callers MUST pass acyclic data, as any JSON-serializable value is.
|
|
6358
|
+
*/
|
|
6359
|
+
function cloneJsonTree(value) {
|
|
6360
|
+
return cloneJsonNode(value);
|
|
6361
|
+
}
|
|
6362
|
+
function isJsonRecord(value) {
|
|
6363
|
+
const proto = Object.getPrototypeOf(value);
|
|
6364
|
+
return proto === Object.prototype || proto === null;
|
|
6365
|
+
}
|
|
6366
|
+
function cloneJsonNode(value) {
|
|
6367
|
+
if (value === null || typeof value !== "object") return value;
|
|
6368
|
+
if (Array.isArray(value)) return value.map(cloneJsonNode);
|
|
6369
|
+
if (!isJsonRecord(value)) return structuredCloneJSON(value);
|
|
6370
|
+
const out = {};
|
|
6371
|
+
for (const key in value) {
|
|
6372
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
6373
|
+
const child = cloneJsonNode(value[key]);
|
|
6374
|
+
if (key === "__proto__") Object.defineProperty(out, key, {
|
|
6375
|
+
value: child,
|
|
6376
|
+
enumerable: true,
|
|
6377
|
+
writable: true,
|
|
6378
|
+
configurable: true
|
|
6379
|
+
});
|
|
6380
|
+
else out[key] = child;
|
|
6381
|
+
}
|
|
6382
|
+
return out;
|
|
6383
|
+
}
|
|
5670
6384
|
//#endregion
|
|
5671
|
-
export {
|
|
6385
|
+
export { getAutoQaDbPath as $, FFIType as A, formatNumber as B, parseStreamingJson as C, error as D, debug as E, CookieMap as F, $pickenv as G, $env as H, installBunShim as I, stripWindowsExtendedLengthPathPrefix as J, isTerminalHeadless as K, formatAge as L, ptr as M, $ as N, time as O, Cookie as P, getAgentDir as Q, formatBytes as R, parseJsonWithRepair as S, parseFrontmatter as T, $envpos as U, truncate as V, $flag as W, USER_AGENT as X, APP_URL as Y, VERSION as Z, parseImageMetadata as _, findOnPath as _t, readJsonl as a, getLogsDir as at, takeLoopPhaseAttribution as b, readSseJsonOrText as c, getToolsDir as ct, exec as d, isEnoent as dt, getCustomThemesDir as et, render as f, isEnotdir as ft, base64ImageDataFailureReason as g, untilAborted as gt, register as h, once as ht, TempDir as i, getInstallId as it, dlopen as j, warn as k, Snowflake as l, isBunTestRuntime as lt, $which as m, peekFile as mt, structuredCloneJSON as n, getDocsRsCacheDir as nt, readSseEvents as o, getPuppeteerDir as ot, LRUCache as p, isProbablyBinary as pt, isWsl as q, wrapFetchForExtraCa as r, getDocumentConversionCacheDir as rt, readSseJson as s, getRemoteDir as st, cloneJsonTree as t, getDebugLogPath as tt, combineSignals as u, hasFsCode as ut, readImageMetadata as v, parseStreamingJsonThrottled as w, classifyJsonPrefix as x, resetLoopPhaseWindow as y, formatCount as z };
|