@linxiraos/pi-utils 1.1.20 → 1.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -5
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES.txt +3 -40
- package/dist/types/dirs.d.ts +26 -4
- package/dist/types/file-lock.d.ts +2 -6
- package/dist/types/fs-open.d.ts +16 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/logger/rotating-file.d.ts +9 -0
- package/dist/types/mermaid-ascii.d.ts +12 -4
- package/dist/types/postmortem.d.ts +23 -0
- package/dist/types/procmgr.d.ts +3 -0
- package/dist/types/stderr-guard.d.ts +23 -4
- package/dist/types/stream.d.ts +11 -4
- package/package.json +2 -2
- package/src/cli.ts +1 -1
- package/src/dirs.ts +54 -12
- package/src/file-lock.ts +7 -9
- package/src/fs-open.ts +31 -0
- package/src/index.ts +40 -0
- package/src/logger/rotating-file.ts +22 -9
- package/src/logger.ts +10 -9
- package/src/mermaid-ascii.ts +13 -7
- package/src/module-timer.ts +7 -4
- package/src/postmortem.ts +57 -0
- package/src/procmgr.ts +18 -18
- package/src/ptree.ts +3 -3
- package/src/runtime-install.ts +43 -7
- package/src/sqlite.ts +29 -2
- package/src/stderr-guard.ts +53 -21
- package/src/stream.ts +26 -8
- package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +0 -41
- package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +0 -89
- package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +0 -7
- package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +0 -12
- package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +0 -66
- package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +0 -48
- package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +0 -43
- package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +0 -7
- package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +0 -56
- package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +0 -65
- package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +0 -27
- package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +0 -17
- package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +0 -7
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +0 -11
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +0 -34
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +0 -11
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +0 -11
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +0 -26
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +0 -31
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +0 -11
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +0 -59
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +0 -17
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +0 -30
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +0 -55
- package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +0 -206
- package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +0 -51
- package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +0 -2
- package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +0 -6
- package/dist/types/vendor/mermaid-ascii/class/types.d.ts +0 -102
- package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +0 -6
- package/dist/types/vendor/mermaid-ascii/er/types.d.ts +0 -76
- package/dist/types/vendor/mermaid-ascii/index.d.ts +0 -1
- package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +0 -9
- package/dist/types/vendor/mermaid-ascii/parser.d.ts +0 -7
- package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +0 -6
- package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +0 -130
- package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +0 -23
- package/dist/types/vendor/mermaid-ascii/types.d.ts +0 -114
- package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +0 -25
- package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +0 -6
- package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +0 -145
- package/src/vendor/mermaid-ascii/NOTICE +0 -33
- package/src/vendor/mermaid-ascii/ascii/ansi.ts +0 -409
- package/src/vendor/mermaid-ascii/ascii/canvas.ts +0 -478
- package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +0 -699
- package/src/vendor/mermaid-ascii/ascii/converter.ts +0 -271
- package/src/vendor/mermaid-ascii/ascii/draw.ts +0 -1389
- package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +0 -328
- package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +0 -297
- package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +0 -441
- package/src/vendor/mermaid-ascii/ascii/grid.ts +0 -593
- package/src/vendor/mermaid-ascii/ascii/index.ts +0 -187
- package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +0 -78
- package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +0 -277
- package/src/vendor/mermaid-ascii/ascii/sequence.ts +0 -460
- package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +0 -27
- package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +0 -127
- package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +0 -27
- package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +0 -27
- package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +0 -101
- package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +0 -175
- package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +0 -27
- package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +0 -296
- package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +0 -114
- package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +0 -192
- package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +0 -73
- package/src/vendor/mermaid-ascii/ascii/types.ts +0 -273
- package/src/vendor/mermaid-ascii/ascii/validate.ts +0 -120
- package/src/vendor/mermaid-ascii/ascii/xychart.ts +0 -875
- package/src/vendor/mermaid-ascii/class/parser.ts +0 -290
- package/src/vendor/mermaid-ascii/class/types.ts +0 -121
- package/src/vendor/mermaid-ascii/er/parser.ts +0 -181
- package/src/vendor/mermaid-ascii/er/types.ts +0 -91
- package/src/vendor/mermaid-ascii/index.ts +0 -14
- package/src/vendor/mermaid-ascii/multiline-utils.ts +0 -30
- package/src/vendor/mermaid-ascii/parser.ts +0 -645
- package/src/vendor/mermaid-ascii/sequence/parser.ts +0 -207
- package/src/vendor/mermaid-ascii/sequence/types.ts +0 -146
- package/src/vendor/mermaid-ascii/text-metrics.ts +0 -74
- package/src/vendor/mermaid-ascii/types.ts +0 -164
- package/src/vendor/mermaid-ascii/xychart/colors.ts +0 -140
- package/src/vendor/mermaid-ascii/xychart/parser.ts +0 -115
- package/src/vendor/mermaid-ascii/xychart/types.ts +0 -150
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [1.1.
|
|
5
|
+
## [1.1.23] - 2026-10-01
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## [1.1.22] - 2026-09-30
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- 版本线推进;本版无独立用户可见变化。
|
|
9
|
+
- **CLI bin 命名**:新增 `CLI_BIN_NAME`(zeta-c),Usage/进程标题/补全驱动统一;配置根 `.zeta`、`ZETA_*`、`APP_NAME` 不变。
|
|
12
10
|
|
|
13
11
|
## [1.1.18] - 2026-09-22
|
|
14
12
|
|
|
@@ -17,3 +15,7 @@
|
|
|
17
15
|
## [1.1.16] - 2026-09-19
|
|
18
16
|
|
|
19
17
|
- 上游 v18.2.5 同步:JSON 与缓存逻辑流线化重构、which 缓存改字符串键重写。
|
|
18
|
+
- Optimized model configuration command execution by deduplicating requests and adding failure backoff
|
|
19
|
+
- Prevented unnecessary credential command execution when runtime API keys are configured
|
|
20
|
+
- Retained `readLines()` results no longer change when later chunks reuse the internal buffer.
|
|
21
|
+
- Long sleeps honor elapsed time and re-arm after premature timer wakes without overflowing native timer delays.
|
package/README.md
CHANGED
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -78,8 +78,8 @@ below.
|
|
|
78
78
|
uutils coreutils (https://github.com/uutils/coreutils), MIT
|
|
79
79
|
--------------------------------------------------------------------------------
|
|
80
80
|
Covers: base32, base64, basename, cat, cksum (shared checksum machinery),
|
|
81
|
-
b2sum, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, comm,
|
|
82
|
-
date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv, nproc, paste,
|
|
81
|
+
b2sum, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, comm, cp,
|
|
82
|
+
cut, date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv, nproc, paste,
|
|
83
83
|
printenv, readlink, realpath, rm, seq, sort, stat, tac, tail, tee, touch, tr,
|
|
84
84
|
truncate, uname, uniq, wc, whoami, yes.
|
|
85
85
|
|
|
@@ -293,7 +293,7 @@ MIT License
|
|
|
293
293
|
Copyright (c) 2026 Sander Land
|
|
294
294
|
(measured tokenizer vocabulary data, reconstruction model, and reference
|
|
295
295
|
implementation: https://github.com/sanderland/ctok)
|
|
296
|
-
Copyright (c) 2026 Can Bölük and the
|
|
296
|
+
Copyright (c) 2026 Can Bölük and the omp contributors
|
|
297
297
|
Copyright (c) 2026 Stencil Labs, Inc.
|
|
298
298
|
(Rust implementation and the compact binary vocabulary encoding in
|
|
299
299
|
crates/pi-natives/src/utok/claude)
|
|
@@ -335,43 +335,6 @@ This license allows the work and adaptations of it to be shared and used
|
|
|
335
335
|
commercially, as long as it is attributed to Poppy Works. The font is bundled
|
|
336
336
|
here (crates/pi-natives/src/fonts/Silver.ttf) as a CJK/Unicode bitmap fallback.
|
|
337
337
|
|
|
338
|
-
-------------------------------------------------------------------------------
|
|
339
|
-
packages/utils/src/vendor/mermaid-ascii/NOTICE
|
|
340
|
-
|
|
341
|
-
This directory contains an in-house Mermaid-diagram-to-ASCII renderer adapted
|
|
342
|
-
from beautiful-mermaid (https://github.com/lukilabs/beautiful-mermaid), used
|
|
343
|
-
under the MIT License.
|
|
344
|
-
|
|
345
|
-
Copyright (c) 2026 Craft Docs
|
|
346
|
-
|
|
347
|
-
Only the ASCII rendering pipeline is ported (flowchart/state, sequence, class,
|
|
348
|
-
ER, and xychart diagrams); the SVG renderer and its `elkjs` graph-layout
|
|
349
|
-
dependency, the browser entry point, and the SVG theme/style modules were
|
|
350
|
-
dropped. Terminal display width is reimplemented on `Bun.stringWidth`, and
|
|
351
|
-
inline label formatting (HTML tags, markdown emphasis) is reduced to plain text
|
|
352
|
-
for ASCII output. Layout and edge-routing logic is preserved faithfully so
|
|
353
|
-
ASCII output matches the upstream package.
|
|
354
|
-
|
|
355
|
-
MIT License
|
|
356
|
-
|
|
357
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
358
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
359
|
-
in the Software without restriction, including without limitation the rights
|
|
360
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
361
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
362
|
-
furnished to do so, subject to the following conditions:
|
|
363
|
-
|
|
364
|
-
The above copyright notice and this permission notice shall be included in all
|
|
365
|
-
copies or substantial portions of the Software.
|
|
366
|
-
|
|
367
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
368
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
369
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
370
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
371
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
372
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
373
|
-
SOFTWARE.
|
|
374
|
-
|
|
375
338
|
-------------------------------------------------------------------------------
|
|
376
339
|
packages/coding-agent/src/markit/NOTICE
|
|
377
340
|
|
package/dist/types/dirs.d.ts
CHANGED
|
@@ -10,8 +10,16 @@
|
|
|
10
10
|
* move data to the new locations. No filesystem existence checks are performed
|
|
11
11
|
* — if the env var is set, zeta trusts that the migration has been done.
|
|
12
12
|
*/
|
|
13
|
-
/** App name (e.g. "zeta")
|
|
13
|
+
/** App name (e.g. "zeta") — product-level identity: config-dir root, log file
|
|
14
|
+
* names, provider attribution headers, splash wordmark. NOT the CLI command. */
|
|
14
15
|
export declare const APP_NAME: string;
|
|
16
|
+
/** CLI binary name (`npm i -g @linxiraos/zeta` installs `zeta-c`, with
|
|
17
|
+
* `zeta-cli` and `zetacode` as aliases). Every user-facing "run this command"
|
|
18
|
+
* string and process title uses this; the bare `zeta` name belongs to the
|
|
19
|
+
* @linxiraos/main workspace product, not this package. */
|
|
20
|
+
export declare const CLI_BIN_NAME: string;
|
|
21
|
+
/** Public homepage that inference gateways (OpenRouter, Vercel AI Gateway) credit our traffic to. */
|
|
22
|
+
export declare const APP_URL: string;
|
|
15
23
|
/** Config directory name (e.g. ".zeta") */
|
|
16
24
|
export declare const CONFIG_DIR_NAME: string;
|
|
17
25
|
/** Ordered main settings filenames: canonical write target first, legacy-compatible YAML fallback second. */
|
|
@@ -131,7 +139,16 @@ export declare function getProjectAgentDir(cwd?: string): string;
|
|
|
131
139
|
export declare function getReportsDir(): string;
|
|
132
140
|
/** Get the logs directory (~/.zeta/logs). */
|
|
133
141
|
export declare function getLogsDir(): string;
|
|
134
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* Local-timezone `YYYY-MM-DD` day key (zero-padded), formatted exactly like
|
|
144
|
+
* the rotating log sink's file naming: log files are named `omp.<day>.<pid>.log`
|
|
145
|
+
* with the LOCAL day, not the UTC day `toISOString()` yields. Anything that
|
|
146
|
+
* computes "today's" log path or matches same-day log files by name must use
|
|
147
|
+
* this key, or between local midnight and UTC midnight it points at files that
|
|
148
|
+
* do not exist (e.g. 00:00–08:00 in UTC+8).
|
|
149
|
+
*/
|
|
150
|
+
export declare function localDay(date: Date): string;
|
|
151
|
+
/** Get this process's dated log path (~/.zeta/logs/zeta.YYYY-MM-DD.PID.log, local-day named like the rotating sink). */
|
|
135
152
|
export declare function getLogPath(date?: Date, pid?: number): string;
|
|
136
153
|
/**
|
|
137
154
|
* Get the plugins directory (~/.zeta/plugins or its XDG equivalent).
|
|
@@ -202,8 +219,6 @@ export declare function getAutoQaDbPath(): string;
|
|
|
202
219
|
export declare function hashPath(absPath: string): string;
|
|
203
220
|
/** Get the path to a single worktree directory (~/.zeta/wt/<segment>). */
|
|
204
221
|
export declare function getWorktreeDir(segment: string): string;
|
|
205
|
-
/** Get the GPU cache path (~/.zeta/gpu_cache.json). */
|
|
206
|
-
export declare function getGpuCachePath(): string;
|
|
207
222
|
/**
|
|
208
223
|
* Get the GitHub view cache database path (~/.zeta/cache/github-cache.db).
|
|
209
224
|
* Honors the `OMP_GITHUB_CACHE_DB` env var when set so tests can isolate the
|
|
@@ -215,6 +230,11 @@ export declare function getGithubCacheDbPath(): string;
|
|
|
215
230
|
* Honors `OMP_COMMIT_CACHE_DB` so tests and operators can isolate the cache.
|
|
216
231
|
*/
|
|
217
232
|
export declare function getCommitCacheDbPath(): string;
|
|
233
|
+
/**
|
|
234
|
+
* Get the judgment answer cache database path (~/.zeta/cache/judgment-cache.db).
|
|
235
|
+
* Honors `OMP_JUDGMENT_CACHE_DB` so tests and operators can isolate the cache.
|
|
236
|
+
*/
|
|
237
|
+
export declare function getJudgmentCacheDbPath(): string;
|
|
218
238
|
/** Get the legacy Pi extension parse cache database path. */
|
|
219
239
|
export declare function getLegacyPiExtensionCacheDbPath(): string;
|
|
220
240
|
/**
|
|
@@ -259,6 +279,8 @@ export declare function getTinyModelsCacheDir(agentDir?: string): string;
|
|
|
259
279
|
export declare function getDocumentConversionCacheDir(agentDir?: string): string;
|
|
260
280
|
/** Get the per-project composer speculative cache directory (~/.zeta/agent/cache/composer; XDG default: $XDG_CACHE_HOME/zeta/cache/composer). */
|
|
261
281
|
export declare function getComposerCacheDir(agentDir?: string): string;
|
|
282
|
+
/** Get the composer speculative cache database (~/.zeta/agent/cache/composer.db; XDG default: $XDG_CACHE_HOME/zeta/cache/composer.db). */
|
|
283
|
+
export declare function getComposerCacheDbPath(agentDir?: string): string;
|
|
262
284
|
/** Get the sessions directory (~/.zeta/agent/sessions). */
|
|
263
285
|
export declare function getSessionsDir(agentDir?: string): string;
|
|
264
286
|
/** Get the content-addressed blob store directory (~/.zeta/agent/blobs). */
|
|
@@ -5,12 +5,8 @@ export interface FileLockOptions {
|
|
|
5
5
|
retries?: number;
|
|
6
6
|
/** Delay between acquisition attempts. */
|
|
7
7
|
retryDelayMs?: number;
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
/** An exclusive OS-backed lease. Releasing an already released handle is safe. */
|
|
12
|
-
export interface FileLockHandle {
|
|
13
|
-
release(): void;
|
|
8
|
+
/** Cancel acquisition while waiting for another process to release the resource. */
|
|
9
|
+
signal?: AbortSignal;
|
|
14
10
|
}
|
|
15
11
|
/** An exclusive OS-backed lease. Releasing an already released handle is safe. */
|
|
16
12
|
export interface FileLockHandle {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fs.openSync` with close-on-exec, matching Node's default.
|
|
3
|
+
*
|
|
4
|
+
* libuv adds `O_CLOEXEC` inside `uv__fs_open`, so on Node every `fs` descriptor
|
|
5
|
+
* dies at `exec`. Bun's `fs.open*` does not, and descriptors it returns survive
|
|
6
|
+
* into any child spawned through a path that does not close strays itself — the
|
|
7
|
+
* bash tool's shell `fork`/`exec`s user commands and hands them the lot. Use
|
|
8
|
+
* this for every descriptor kept open past such a spawn (session transcripts,
|
|
9
|
+
* logs, spools); a child must never hold a writable handle to them (#13224).
|
|
10
|
+
*
|
|
11
|
+
* Flags are numeric only: the bit cannot be OR-ed into `"a"`/`"w+"` strings.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const fd = openCloexecSync(logPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND);
|
|
15
|
+
*/
|
|
16
|
+
export declare function openCloexecSync(filePath: string, flags: number, mode?: number): number;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./file-lock.js";
|
|
|
10
10
|
export * from "./format.js";
|
|
11
11
|
export * from "./frontmatter.js";
|
|
12
12
|
export * from "./fs-error.js";
|
|
13
|
+
export * from "./fs-open.js";
|
|
13
14
|
export * from "./incoming-json.js";
|
|
14
15
|
export * from "./json.js";
|
|
15
16
|
export * from "./json-parse.js";
|
|
@@ -42,3 +43,15 @@ export * from "./version.js";
|
|
|
42
43
|
export * from "./which.js";
|
|
43
44
|
export * from "./yaml-config.js";
|
|
44
45
|
export declare function structuredCloneJSON<T>(value: T): T;
|
|
46
|
+
/**
|
|
47
|
+
* Deep-copies a JSON-shaped tree (arrays and plain or null-prototype objects)
|
|
48
|
+
* so later mutation of the source can never reach the copy. Strings and other
|
|
49
|
+
* primitives are immutable and shared, so the cost is O(containers) instead of
|
|
50
|
+
* `structuredClone`'s O(bytes) — the difference matters for hot paths that
|
|
51
|
+
* re-copy large string payloads (e.g. streamed tool-call arguments) per delta.
|
|
52
|
+
*
|
|
53
|
+
* Copies own enumerable string keys only. Any other object (class instance,
|
|
54
|
+
* Date, Map, inherited-prototype object) is copied with {@link structuredCloneJSON}.
|
|
55
|
+
* Not cycle-safe: callers MUST pass acyclic data, as any JSON-serializable value is.
|
|
56
|
+
*/
|
|
57
|
+
export declare function cloneJsonTree<T>(value: T): T;
|
|
@@ -6,6 +6,15 @@ export interface RotatingFileOptions {
|
|
|
6
6
|
readonly auditFile: string;
|
|
7
7
|
readonly maxBytes: number;
|
|
8
8
|
readonly maxFiles: number;
|
|
9
|
+
/**
|
|
10
|
+
* Called with the new active file path whenever the sink opens a different
|
|
11
|
+
* file — construction, local-day rotation, and size rotation alike.
|
|
12
|
+
*
|
|
13
|
+
* Consumers that hold their own descriptor on the active log (the macOS
|
|
14
|
+
* stderr guard dup2s it onto fd 2) use this to follow the sink instead of
|
|
15
|
+
* staying pinned to a file the sink later prunes.
|
|
16
|
+
*/
|
|
17
|
+
readonly onRotate?: (filePath: string) => void;
|
|
9
18
|
}
|
|
10
19
|
/** Synchronous append sink with local-day and size rotation plus bounded retention. */
|
|
11
20
|
export declare class RotatingFileSink {
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { type MermaidRenderOptions, renderMermaidAscii } from "@linxiraos/pi-natives";
|
|
2
|
+
/**
|
|
3
|
+
* Native Mermaid → ASCII/Unicode renderer (flowchart, state, sequence, class,
|
|
4
|
+
* ER, xychart). Synchronous because callers render inside the compositor;
|
|
5
|
+
* throws on empty input, an unknown flowchart header, or an invalid
|
|
6
|
+
* `direction`/`colorMode` value.
|
|
7
|
+
*/
|
|
8
|
+
export { renderMermaidAscii };
|
|
9
|
+
/** Options for {@link renderMermaidAscii}; every field optional. */
|
|
10
|
+
export type MermaidAsciiRenderOptions = MermaidRenderOptions;
|
|
11
|
+
/** {@link renderMermaidAscii}, returning `null` instead of throwing. */
|
|
12
|
+
export declare function renderMermaidAsciiSafe(source: string, options?: MermaidAsciiRenderOptions): string | null;
|
|
5
13
|
/**
|
|
6
14
|
* Extract mermaid code blocks from markdown text.
|
|
7
15
|
*/
|
|
@@ -119,6 +119,29 @@ export declare function registerWorkerIpcFaultHandler(handler: (err: Error) => v
|
|
|
119
119
|
* when the last registrant unregisters.
|
|
120
120
|
*/
|
|
121
121
|
export declare function registerStdioDisconnectHandling(): () => void;
|
|
122
|
+
/**
|
|
123
|
+
* Fail a process entry whose event loop drains while `work` is still pending.
|
|
124
|
+
*
|
|
125
|
+
* The CLI starts from a floating `runCli()` call because top-level await breaks
|
|
126
|
+
* `--bytecode` builds. When a one-shot command awaits a promise that never
|
|
127
|
+
* settles and holds no live handle, the loop drains and Bun exits 0 without
|
|
128
|
+
* output: an unfinished command reported as success. `beforeExit` fires in
|
|
129
|
+
* that state; explicit exits (`process.exit`, {@link quit},
|
|
130
|
+
* {@link exitProcess}, the signal handlers) never emit it.
|
|
131
|
+
*
|
|
132
|
+
* `beforeExit` alone is not the verdict: on Windows Bun emits it while I/O is
|
|
133
|
+
* still in flight and then keeps running the loop, so a command that goes on
|
|
134
|
+
* to complete must keep its own exit code (#13470). The verdict is taken at
|
|
135
|
+
* `exit` instead, and only when the last `beforeExit` was final: an unref'd
|
|
136
|
+
* timer armed there runs only if the loop turns again, clearing the mark
|
|
137
|
+
* before a later explicit exit. Any later drain emits `beforeExit` again and
|
|
138
|
+
* re-arms it, so other `beforeExit` listeners (LSP shutdown) still run before
|
|
139
|
+
* the process exits 1.
|
|
140
|
+
*
|
|
141
|
+
* `describe` names what was running (e.g. the resolved subcommand) at report
|
|
142
|
+
* time; it must return only a command name, never user arguments.
|
|
143
|
+
*/
|
|
144
|
+
export declare function reportUnsettledEntry(work: Promise<unknown>, describe?: () => string | undefined): void;
|
|
122
145
|
/**
|
|
123
146
|
* Mark an error as expected cleanup fallout so the global fatal handlers
|
|
124
147
|
* downgrade it to a log line instead of tearing down the process. Use for
|
package/dist/types/procmgr.d.ts
CHANGED
|
@@ -65,6 +65,9 @@ export declare function resolveWindowsShell(env?: Record<string, string | undefi
|
|
|
65
65
|
* {@link resolveWindowsShell}) — never fails
|
|
66
66
|
* 3. On Unix: $SHELL if bash/zsh, then fallback paths
|
|
67
67
|
* 4. Fallback: sh
|
|
68
|
+
*
|
|
69
|
+
* Custom and auto-resolved configs are cached separately, so clearing
|
|
70
|
+
* `shellPath` mid-process immediately falls back to the platform default.
|
|
68
71
|
*/
|
|
69
72
|
export declare function getShellConfig(customShellPath?: string, options?: ShellConfigOptions): ShellConfig;
|
|
70
73
|
/**
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
export interface SuppressTerminalStderrOptions {
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Redirect target path; defaults to the log file the rotating sink last
|
|
4
|
+
* reported (see {@link setStderrRedirectTarget}), then today's omp log
|
|
5
|
+
* file, then /dev/null.
|
|
6
|
+
*/
|
|
3
7
|
redirectPath?: string;
|
|
4
|
-
/** Bypass the
|
|
8
|
+
/** Bypass the same-terminal gate. Tests only. */
|
|
5
9
|
force?: boolean;
|
|
6
10
|
}
|
|
7
11
|
/**
|
|
8
12
|
* Redirect fd 2 away from the terminal while the TUI owns the viewport.
|
|
9
13
|
* Returns true when suppression is (already) active. No-op — returning
|
|
10
|
-
* false —
|
|
11
|
-
*
|
|
14
|
+
* false — when stderr does not target the stdout terminal or when libc fd
|
|
15
|
+
* operations are unavailable.
|
|
12
16
|
*/
|
|
13
17
|
export declare function suppressTerminalStderr(options?: SuppressTerminalStderrOptions): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Point fd 2 at `logPath`, the log file the rotating sink is currently
|
|
20
|
+
* writing.
|
|
21
|
+
*
|
|
22
|
+
* The logger calls this on every sink rotation. Without it fd 2 keeps the
|
|
23
|
+
* descriptor opened at TUI start, so after local midnight raw stderr — this
|
|
24
|
+
* process's and every child that inherited fd 2 — keeps landing in the
|
|
25
|
+
* previous day's file, which the sink eventually prunes out from under it.
|
|
26
|
+
*
|
|
27
|
+
* The path is recorded even while suppression is inactive, so a later
|
|
28
|
+
* {@link suppressTerminalStderr} redirects to the live file instead of
|
|
29
|
+
* re-deriving a dated name. Best-effort: if the new file cannot be opened,
|
|
30
|
+
* fd 2 keeps its current target.
|
|
31
|
+
*/
|
|
32
|
+
export declare function setStderrRedirectTarget(logPath: string): void;
|
|
14
33
|
/**
|
|
15
34
|
* Re-point fd 2 at the saved terminal stderr. Safe to call unconditionally:
|
|
16
35
|
* no-op when suppression is not active. Called at every terminal-ownership
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -74,7 +74,13 @@ export declare function readSseJson<T>(stream: ReadableStream<Uint8Array>, signa
|
|
|
74
74
|
* which is the safe direction — each is classified as text rather than trusted as
|
|
75
75
|
* an event object.
|
|
76
76
|
*/
|
|
77
|
-
export declare function readSseJsonOrText<T>(stream: ReadableStream<Uint8Array>, signal?: AbortSignal, onEvent?: SseEventObserver
|
|
77
|
+
export declare function readSseJsonOrText<T>(stream: ReadableStream<Uint8Array>, signal?: AbortSignal, onEvent?: SseEventObserver,
|
|
78
|
+
/**
|
|
79
|
+
* Called when iteration ends on the OpenAI `[DONE]` sentinel. Lets a
|
|
80
|
+
* consumer tell a server-agreed end from a bare EOF without attaching an
|
|
81
|
+
* `onEvent` observer (which turns on per-line raw capture).
|
|
82
|
+
*/
|
|
83
|
+
onDone?: () => void): AsyncGenerator<T | string>;
|
|
78
84
|
/**
|
|
79
85
|
* A single Server-Sent Event dispatched on a blank-line boundary.
|
|
80
86
|
*
|
|
@@ -95,12 +101,13 @@ export interface ServerSentEvent {
|
|
|
95
101
|
* Decoded wire lines for this event (`event:`/`data:`/etc.), for the
|
|
96
102
|
* diagnostic pipeline. Populated only when the reader opts in via
|
|
97
103
|
* {@link ReadSseEventsOptions.captureRaw} (or attaches an `onSseEvent`
|
|
98
|
-
* observer to the JSON readers, which opt in automatically); otherwise
|
|
99
|
-
*
|
|
104
|
+
* observer to the JSON readers, which opt in automatically); otherwise a
|
|
105
|
+
* shared, frozen empty array — copy or reassign it, never mutate it in
|
|
106
|
+
* place. Direct `readSseEvents` callers that need wire text must pass
|
|
100
107
|
* `{ captureRaw: true }` — the field is allocation-free by default so
|
|
101
108
|
* the token path pays no per-frame array/slice cost.
|
|
102
109
|
*/
|
|
103
|
-
raw: string[];
|
|
110
|
+
raw: readonly string[];
|
|
104
111
|
id?: string;
|
|
105
112
|
retry?: number;
|
|
106
113
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxiraos/pi-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.23",
|
|
4
4
|
"description": "Shared utilities for pi packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"utilities",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@linxiraos/pi-natives": "1.1.
|
|
57
|
+
"@linxiraos/pi-natives": "1.1.23"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/bun": "^1.3.14"
|
package/src/cli.ts
CHANGED
|
@@ -463,7 +463,7 @@ export async function run(opts: RunOptions): Promise<void> {
|
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
// Per-command help: load only the requested command. Loading the full
|
|
466
|
-
// command table here would make `
|
|
466
|
+
// command table here would make `zeta <cmd> --help` hang or crash whenever
|
|
467
467
|
// any *unrelated* command module misbehaves at import time.
|
|
468
468
|
if (commandArgv.includes("--help") || commandArgv.includes("-h")) {
|
|
469
469
|
const entry = findEntry(opts.commands, commandId);
|
package/src/dirs.ts
CHANGED
|
@@ -14,12 +14,23 @@
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as os from "node:os";
|
|
16
16
|
import * as path from "node:path";
|
|
17
|
+
import { expandWindowsLongPath } from "@linxiraos/pi-natives/path";
|
|
17
18
|
import { engines, version } from "../package.json" with { type: "json" };
|
|
18
19
|
import { isEnoent, isEnotdir } from "./fs-error";
|
|
19
20
|
|
|
20
|
-
/** App name (e.g. "zeta")
|
|
21
|
+
/** App name (e.g. "zeta") — product-level identity: config-dir root, log file
|
|
22
|
+
* names, provider attribution headers, splash wordmark. NOT the CLI command. */
|
|
21
23
|
export const APP_NAME: string = "zeta";
|
|
22
24
|
|
|
25
|
+
/** CLI binary name (`npm i -g @linxiraos/zeta` installs `zeta-c`, with
|
|
26
|
+
* `zeta-cli` and `zetacode` as aliases). Every user-facing "run this command"
|
|
27
|
+
* string and process title uses this; the bare `zeta` name belongs to the
|
|
28
|
+
* @linxiraos/main workspace product, not this package. */
|
|
29
|
+
export const CLI_BIN_NAME: string = "zeta-c";
|
|
30
|
+
|
|
31
|
+
/** Public homepage that inference gateways (OpenRouter, Vercel AI Gateway) credit our traffic to. */
|
|
32
|
+
export const APP_URL: string = "https://linxira-os.github.io/zeta/";
|
|
33
|
+
|
|
23
34
|
/** Config directory name (e.g. ".zeta") */
|
|
24
35
|
export const CONFIG_DIR_NAME: string = ".zeta";
|
|
25
36
|
|
|
@@ -140,6 +151,11 @@ function standardizeMacOSPath(p: string): string {
|
|
|
140
151
|
return p;
|
|
141
152
|
}
|
|
142
153
|
|
|
154
|
+
/** Keep the current directory's spelling while expanding Windows 8.3 aliases. */
|
|
155
|
+
function standardizeProjectPath(p: string): string {
|
|
156
|
+
return process.platform === "win32" ? expandWindowsLongPath(p) : standardizeMacOSPath(p);
|
|
157
|
+
}
|
|
158
|
+
|
|
143
159
|
export function resolveEquivalentPath(inputPath: string): string {
|
|
144
160
|
const resolvedPath = path.resolve(inputPath);
|
|
145
161
|
try {
|
|
@@ -186,29 +202,33 @@ let projectDir: string | undefined;
|
|
|
186
202
|
/** Get the project directory. */
|
|
187
203
|
export function getProjectDir(): string {
|
|
188
204
|
if (projectDir === undefined) {
|
|
205
|
+
let cwd: string | undefined;
|
|
189
206
|
try {
|
|
190
|
-
|
|
207
|
+
cwd = process.cwd();
|
|
191
208
|
} catch {
|
|
192
209
|
const candidates = [process.env.PWD, os.homedir(), os.tmpdir()];
|
|
193
210
|
for (const candidate of candidates) {
|
|
194
211
|
if (!candidate || !path.isAbsolute(candidate)) continue;
|
|
195
212
|
try {
|
|
196
213
|
process.chdir(candidate);
|
|
197
|
-
|
|
214
|
+
cwd = candidate;
|
|
198
215
|
break;
|
|
199
216
|
} catch {}
|
|
200
217
|
}
|
|
201
|
-
if (
|
|
218
|
+
if (cwd === undefined) {
|
|
202
219
|
throw new Error("Unable to determine an accessible working directory");
|
|
203
220
|
}
|
|
204
221
|
}
|
|
222
|
+
// Normalize outside the fallback: a native-addon failure is not an inaccessible cwd,
|
|
223
|
+
// and must surface as itself instead of relocating the process.
|
|
224
|
+
projectDir = standardizeProjectPath(cwd);
|
|
205
225
|
}
|
|
206
226
|
return projectDir;
|
|
207
227
|
}
|
|
208
228
|
|
|
209
229
|
/** Set the project directory. */
|
|
210
230
|
export function setProjectDir(dir: string): void {
|
|
211
|
-
const resolved =
|
|
231
|
+
const resolved = standardizeProjectPath(path.resolve(dir));
|
|
212
232
|
process.chdir(resolved);
|
|
213
233
|
projectDir = resolved;
|
|
214
234
|
}
|
|
@@ -581,9 +601,21 @@ export function getLogsDir(): string {
|
|
|
581
601
|
return dirs.rootSubdir("logs", "state");
|
|
582
602
|
}
|
|
583
603
|
|
|
584
|
-
/**
|
|
604
|
+
/**
|
|
605
|
+
* Local-timezone `YYYY-MM-DD` day key (zero-padded), formatted exactly like
|
|
606
|
+
* the rotating log sink's file naming: log files are named `omp.<day>.<pid>.log`
|
|
607
|
+
* with the LOCAL day, not the UTC day `toISOString()` yields. Anything that
|
|
608
|
+
* computes "today's" log path or matches same-day log files by name must use
|
|
609
|
+
* this key, or between local midnight and UTC midnight it points at files that
|
|
610
|
+
* do not exist (e.g. 00:00–08:00 in UTC+8).
|
|
611
|
+
*/
|
|
612
|
+
export function localDay(date: Date): string {
|
|
613
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** Get this process's dated log path (~/.zeta/logs/zeta.YYYY-MM-DD.PID.log, local-day named like the rotating sink). */
|
|
585
617
|
export function getLogPath(date = new Date(), pid = process.pid): string {
|
|
586
|
-
return path.join(getLogsDir(), `${APP_NAME}.${date
|
|
618
|
+
return path.join(getLogsDir(), `${APP_NAME}.${localDay(date)}.${pid}.log`);
|
|
587
619
|
}
|
|
588
620
|
|
|
589
621
|
/**
|
|
@@ -732,11 +764,6 @@ export function getWorktreeDir(segment: string): string {
|
|
|
732
764
|
return path.join(getWorktreesDir(), segment);
|
|
733
765
|
}
|
|
734
766
|
|
|
735
|
-
/** Get the GPU cache path (~/.zeta/gpu_cache.json). */
|
|
736
|
-
export function getGpuCachePath(): string {
|
|
737
|
-
return dirs.rootSubdir("gpu_cache.json", "cache");
|
|
738
|
-
}
|
|
739
|
-
|
|
740
767
|
/**
|
|
741
768
|
* Get the GitHub view cache database path (~/.zeta/cache/github-cache.db).
|
|
742
769
|
* Honors the `OMP_GITHUB_CACHE_DB` env var when set so tests can isolate the
|
|
@@ -757,6 +784,16 @@ export function getCommitCacheDbPath(): string {
|
|
|
757
784
|
return dirs.rootSubdir(path.join("cache", "commit-inference.db"), "cache");
|
|
758
785
|
}
|
|
759
786
|
|
|
787
|
+
/**
|
|
788
|
+
* Get the judgment answer cache database path (~/.zeta/cache/judgment-cache.db).
|
|
789
|
+
* Honors `OMP_JUDGMENT_CACHE_DB` so tests and operators can isolate the cache.
|
|
790
|
+
*/
|
|
791
|
+
export function getJudgmentCacheDbPath(): string {
|
|
792
|
+
const override = process.env.OMP_JUDGMENT_CACHE_DB;
|
|
793
|
+
if (override) return override;
|
|
794
|
+
return dirs.rootSubdir(path.join("cache", "judgment-cache.db"), "cache");
|
|
795
|
+
}
|
|
796
|
+
|
|
760
797
|
/** Get the legacy Pi extension parse cache database path. */
|
|
761
798
|
export function getLegacyPiExtensionCacheDbPath(): string {
|
|
762
799
|
return dirs.rootSubdir(path.join("cache", "legacy-pi-extension-cache.db"), "cache");
|
|
@@ -866,6 +903,11 @@ export function getComposerCacheDir(agentDir?: string): string {
|
|
|
866
903
|
return dirs.agentSubdir(agentDir, path.join("cache", "composer"), "cache");
|
|
867
904
|
}
|
|
868
905
|
|
|
906
|
+
/** Get the composer speculative cache database (~/.zeta/agent/cache/composer.db; XDG default: $XDG_CACHE_HOME/zeta/cache/composer.db). */
|
|
907
|
+
export function getComposerCacheDbPath(agentDir?: string): string {
|
|
908
|
+
return dirs.agentSubdir(agentDir, path.join("cache", "composer.db"), "cache");
|
|
909
|
+
}
|
|
910
|
+
|
|
869
911
|
/** Get the sessions directory (~/.zeta/agent/sessions). */
|
|
870
912
|
export function getSessionsDir(agentDir?: string): string {
|
|
871
913
|
return dirs.agentSubdir(agentDir, "sessions", "data");
|
package/src/file-lock.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* mutexes, and other Unix platforms use `flock(2)` on `${filePath}.lock`.
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
+
import { scheduler } from "node:timers/promises";
|
|
8
9
|
import { FileLock as NativeFileLock } from "@linxiraos/pi-natives";
|
|
9
10
|
|
|
10
11
|
/** Controls bounded waiting when an advisory file lock is contended. */
|
|
@@ -13,8 +14,8 @@ export interface FileLockOptions {
|
|
|
13
14
|
retries?: number;
|
|
14
15
|
/** Delay between acquisition attempts. */
|
|
15
16
|
retryDelayMs?: number;
|
|
16
|
-
/**
|
|
17
|
-
|
|
17
|
+
/** Cancel acquisition while waiting for another process to release the resource. */
|
|
18
|
+
signal?: AbortSignal;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/** An exclusive OS-backed lease. Releasing an already released handle is safe. */
|
|
@@ -22,12 +23,7 @@ export interface FileLockHandle {
|
|
|
22
23
|
release(): void;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
export interface FileLockHandle {
|
|
27
|
-
release(): void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const DEFAULT_OPTIONS: Required<FileLockOptions> = {
|
|
26
|
+
const DEFAULT_OPTIONS = {
|
|
31
27
|
retries: 50,
|
|
32
28
|
retryDelayMs: 100,
|
|
33
29
|
staleMs: 0,
|
|
@@ -48,9 +44,10 @@ export async function acquireFileLock(filePath: string, options: FileLockOptions
|
|
|
48
44
|
const lockPath = getLockPath(filePath);
|
|
49
45
|
|
|
50
46
|
for (let attempt = 0; attempt < opts.retries; attempt++) {
|
|
47
|
+
opts.signal?.throwIfAborted();
|
|
51
48
|
const lock = tryAcquireLock(lockPath);
|
|
52
49
|
if (lock) return lock;
|
|
53
|
-
if (attempt + 1 < opts.retries) await
|
|
50
|
+
if (attempt + 1 < opts.retries) await scheduler.wait(opts.retryDelayMs, { signal: opts.signal });
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
throw new Error(`Failed to acquire lock for ${filePath} after ${opts.retries} attempts`);
|
|
@@ -61,6 +58,7 @@ function acquireLockSync(filePath: string, options: FileLockOptions = {}): Nativ
|
|
|
61
58
|
const lockPath = getLockPath(filePath);
|
|
62
59
|
|
|
63
60
|
for (let attempt = 0; attempt < opts.retries; attempt++) {
|
|
61
|
+
opts.signal?.throwIfAborted();
|
|
64
62
|
const lock = tryAcquireLock(lockPath);
|
|
65
63
|
if (lock) return lock;
|
|
66
64
|
if (attempt + 1 < opts.retries && opts.retryDelayMs > 0) Bun.sleepSync(opts.retryDelayMs);
|
package/src/fs-open.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Close-on-exec file opens for descriptors held past a child spawn. */
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `O_CLOEXEC`, which neither Node nor Bun exposes in `fs.constants`.
|
|
6
|
+
*
|
|
7
|
+
* Kernel ABI values: `0o2000000` on Linux, `0x0100_0000` on Darwin. Windows has
|
|
8
|
+
* no `exec` and its CRT handles are inherited only when a spawn explicitly asks
|
|
9
|
+
* for it, so the bit is 0 there — as it is on any other platform, where adding
|
|
10
|
+
* an unknown flag bit would be worse than leaving the descriptor inheritable.
|
|
11
|
+
*/
|
|
12
|
+
const O_CLOEXEC = process.platform === "linux" ? 0o2000000 : process.platform === "darwin" ? 0x0100_0000 : 0;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `fs.openSync` with close-on-exec, matching Node's default.
|
|
16
|
+
*
|
|
17
|
+
* libuv adds `O_CLOEXEC` inside `uv__fs_open`, so on Node every `fs` descriptor
|
|
18
|
+
* dies at `exec`. Bun's `fs.open*` does not, and descriptors it returns survive
|
|
19
|
+
* into any child spawned through a path that does not close strays itself — the
|
|
20
|
+
* bash tool's shell `fork`/`exec`s user commands and hands them the lot. Use
|
|
21
|
+
* this for every descriptor kept open past such a spawn (session transcripts,
|
|
22
|
+
* logs, spools); a child must never hold a writable handle to them (#13224).
|
|
23
|
+
*
|
|
24
|
+
* Flags are numeric only: the bit cannot be OR-ed into `"a"`/`"w+"` strings.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const fd = openCloexecSync(logPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_APPEND);
|
|
28
|
+
*/
|
|
29
|
+
export function openCloexecSync(filePath: string, flags: number, mode?: number): number {
|
|
30
|
+
return fs.openSync(filePath, flags | O_CLOEXEC, mode);
|
|
31
|
+
}
|