@linxiraos/pi-utils 1.1.8 → 1.1.10
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 +6 -3
- package/README.md +18 -18
- package/dist/types/async.d.ts +10 -0
- package/dist/types/dirs.d.ts +6 -4
- package/dist/types/incoming-json.d.ts +232 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/json-lexer.d.ts +116 -0
- package/dist/types/procmgr.d.ts +2 -0
- package/dist/types/tls-fetch.d.ts +22 -9
- package/package.json +46 -49
- package/src/async.ts +16 -0
- package/src/dirs.ts +15 -10
- package/src/incoming-json.ts +856 -0
- package/src/index.ts +1 -0
- package/src/json-lexer.ts +413 -0
- package/src/json-parse.ts +93 -350
- package/src/procmgr.ts +7 -0
- package/src/tls-fetch.ts +2 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [1.1.
|
|
5
|
+
## [1.1.10] - 2026-09-07
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
|
|
7
|
+
- Fixed `extractRetryHint` dropping the longer timing signal when an error body carries both an account reset and an appended retry hint: competing signals now merge by longest window instead of first match, so retries honor the provider's full backoff.
|
|
8
|
+
|
|
9
|
+
## [1.1.9] - 2026-09-05
|
|
10
|
+
|
|
11
|
+
- USER_AGENT constant restored to zeta/<version> (the v18.1.10 merge re-introduced the upstream omp/ UA on every provider request); brand wording in path-resolver docs corrected; profile validation error message Zeta'd.
|
|
9
12
|
|
|
10
13
|
## [1.1.6] - 2026-08-30
|
|
11
14
|
|
package/README.md
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
# @linxiraos/pi-utils
|
|
2
2
|
|
|
3
|
-
Shared utilities for [
|
|
3
|
+
Shared utilities for [oh-my-pi](https://github.com/can1357/oh-my-pi) packages. Zero ceremony, Bun-first.
|
|
4
4
|
|
|
5
5
|
## Notable modules
|
|
6
6
|
|
|
7
|
-
| Module
|
|
8
|
-
|
|
|
9
|
-
| `logger`
|
|
10
|
-
| `prompt`
|
|
11
|
-
| `dirs`
|
|
12
|
-
| `stream`
|
|
13
|
-
| `ptree` / `procmgr`
|
|
14
|
-
| `postmortem`
|
|
15
|
-
| `which`
|
|
16
|
-
| `fetch-retry`
|
|
17
|
-
| `fs-error`
|
|
18
|
-
| `env` / `worker-host`
|
|
19
|
-
| `abortable` / `async`
|
|
20
|
-
| `math-delimiters`
|
|
21
|
-
| `peek-file`
|
|
22
|
-
| `frontmatter`, `glob`, `mime`, `temp`, `format`, `color`, `snowflake`, `tab-spacing`, `path-tree`, `sanitize-text` | Smaller single-purpose helpers
|
|
7
|
+
| Module | Purpose |
|
|
8
|
+
| ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `logger` | Centralized logger writing to `~/.zeta/logs/` with rotation (TUI-safe — never stdout) |
|
|
10
|
+
| `prompt` | Handlebars-based prompt templating and formatting helpers |
|
|
11
|
+
| `dirs` | Path helpers for omp config directories (`~/.zeta`, XDG-aware on Linux) |
|
|
12
|
+
| `stream` | `readStream` / `readLines` helpers over `ReadableStream` |
|
|
13
|
+
| `ptree` / `procmgr` | Process trees, `ChildProcess` wrapper, process lifecycle management |
|
|
14
|
+
| `postmortem` | Cleanup callbacks on exit, signals, and fatal exceptions |
|
|
15
|
+
| `which` | `$which()` binary lookup with caching |
|
|
16
|
+
| `fetch-retry` | `fetch` with retry/backoff policies |
|
|
17
|
+
| `fs-error` | Errno guards (`isEnoent` and friends) |
|
|
18
|
+
| `env` / `worker-host` | Environment plumbing and side-effect-free worker-host entry contract (`workerHostEntry`) |
|
|
19
|
+
| `abortable` / `async` | AbortSignal-aware stream/promise helpers |
|
|
20
|
+
| `math-delimiters` | LaTeX span/block delimiter grammar (offsets only) shared by the TUI and collab-web renderers |
|
|
21
|
+
| `peek-file` | Read the first N bytes of a file with pooled buffers |
|
|
22
|
+
| `frontmatter`, `glob`, `mime`, `temp`, `format`, `color`, `snowflake`, `tab-spacing`, `path-tree`, `sanitize-text` | Smaller single-purpose helpers |
|
|
23
23
|
|
|
24
24
|
Import from the root barrel or per-module subpaths (`@linxiraos/pi-utils/<module>`).
|
|
25
25
|
|
|
@@ -33,5 +33,5 @@ Ships TypeScript source directly (no build step); requires Bun ≥ 1.3.14.
|
|
|
33
33
|
|
|
34
34
|
## References
|
|
35
35
|
|
|
36
|
-
- [Monorepo README](https://github.com/
|
|
36
|
+
- [Monorepo README](https://github.com/can1357/oh-my-pi#readme)
|
|
37
37
|
- [CHANGELOG](./CHANGELOG.md)
|
package/dist/types/async.d.ts
CHANGED
|
@@ -20,3 +20,13 @@ export declare class AsyncDrain<T> {
|
|
|
20
20
|
/** Runs the pending batch handler immediately and returns its completion promise. */
|
|
21
21
|
flush(): Promise<void>;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Runs async operations one at a time in call order. Each `run` starts after
|
|
25
|
+
* the previous operation settles (success or failure) and returns that
|
|
26
|
+
* operation's own promise, so a rejected step never poisons the queue. Used by
|
|
27
|
+
* stateful cursors whose concurrent pulls must not interleave.
|
|
28
|
+
*/
|
|
29
|
+
export declare class Serial {
|
|
30
|
+
#private;
|
|
31
|
+
run<T>(op: () => Promise<T>): Promise<T>;
|
|
32
|
+
}
|
package/dist/types/dirs.d.ts
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* variables are set, paths are redirected to XDG-compliant locations under
|
|
9
9
|
* $XDG_*_HOME/zeta/. This requires running `zeta config migrate` first to
|
|
10
10
|
* move data to the new locations. No filesystem existence checks are performed
|
|
11
|
-
* — if the env var is set,
|
|
11
|
+
* — if the env var is set, zeta trusts that the migration has been done.
|
|
12
12
|
*/
|
|
13
|
-
/** App name (e.g. "
|
|
13
|
+
/** App name (e.g. "zeta") */
|
|
14
14
|
export declare const APP_NAME: string;
|
|
15
15
|
/** Config directory name (e.g. ".zeta") */
|
|
16
16
|
export declare const CONFIG_DIR_NAME: string;
|
|
@@ -273,6 +273,8 @@ export declare function getCrashLogPath(agentDir?: string): string;
|
|
|
273
273
|
export declare function getDebugLogPath(agentDir?: string): string;
|
|
274
274
|
/** Get the secret placeholder key path (~/.zeta/agent/secret-placeholder.key; XDG default: $XDG_STATE_HOME/zeta/secret-placeholder.key). Adopts a legacy key on first XDG resolution. */
|
|
275
275
|
export declare function getSecretPlaceholderKeyPath(): string;
|
|
276
|
+
/** Directory holding the per-model tiny-worker sockets and logs (~/.zeta/run/tiny; XDG default: $XDG_STATE_HOME/zeta/run/tiny). */
|
|
277
|
+
export declare function getTinyWorkerRuntimeDir(): string;
|
|
276
278
|
/** Root directory containing every per-project daemon runtime scope (~/.zeta/run/daemons; XDG default: $XDG_STATE_HOME/zeta/run/daemons). */
|
|
277
279
|
export declare function getDaemonRuntimeRoot(): string;
|
|
278
280
|
/** Get the daemon runtime directory for a project (~/.zeta/run/daemons/<hash>; XDG default: $XDG_STATE_HOME/zeta/run/daemons/<hash>). */
|
|
@@ -317,9 +319,9 @@ export declare function getAppName(): string;
|
|
|
317
319
|
* profile / `getInstallId` call order.
|
|
318
320
|
*/
|
|
319
321
|
export declare function getInstallId(): string;
|
|
320
|
-
/** Test-only: clear cached install id. Never call from production code. */
|
|
321
|
-
export declare function __resetInstallIdCacheForTests(): void;
|
|
322
322
|
/** Get the project-level tracking directory (<project>/.zeta/tracking). */
|
|
323
323
|
export declare function getProjectTrackingDir(cwd?: string): string;
|
|
324
324
|
/** Get the global tracking index path (~/.zeta/agent/tracking-index.json). */
|
|
325
325
|
export declare function getTrackingIndexPath(agentDir?: string): string;
|
|
326
|
+
/** Test-only: clear cached install id. Never call from production code. */
|
|
327
|
+
export declare function __resetInstallIdCacheForTests(): void;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed cursors over a JSON document while its text is still arriving.
|
|
3
|
+
*
|
|
4
|
+
* {@link IncomingDoc.channel} returns a push-side {@link IncomingFeed} and a
|
|
5
|
+
* read-side {@link IncomingDoc}. The producer appends text fragments, then
|
|
6
|
+
* explicitly calls {@link IncomingFeed.finish} or {@link IncomingFeed.abort};
|
|
7
|
+
* a feed that is never closed leaves every pending pull waiting forever.
|
|
8
|
+
* There is one shared append-only buffer; cursors are cheap, immutable
|
|
9
|
+
* path handles over it, and every pull is an ordinary promise that
|
|
10
|
+
* re-scans the buffer whenever the feed changes. There are no snapshots,
|
|
11
|
+
* per-field events, or fan-out channels.
|
|
12
|
+
*
|
|
13
|
+
* A scalar completes at its closing quote/delimiter, and a container
|
|
14
|
+
* completes only when its closing delimiter arrives. Finished-but-truncated
|
|
15
|
+
* input rejects with kind `incomplete`; abandoned input rejects with
|
|
16
|
+
* `aborted`. String chunks contain only decoded text whose meaning is
|
|
17
|
+
* stable, so an escape or Unicode escape may span any number of fragments.
|
|
18
|
+
*
|
|
19
|
+
* Pulling an {@link IncomingObject.key} makes that key required: a missing
|
|
20
|
+
* or mistyped value is a structured {@link IncomingJsonError}. Object members
|
|
21
|
+
* never pulled are skipped without validation. {@link IncomingDoc.whole} is
|
|
22
|
+
* the explicit whole-document pull and runs only after successful input
|
|
23
|
+
* completion.
|
|
24
|
+
*
|
|
25
|
+
* Object cursors bind the first occurrence of a duplicate key, whereas
|
|
26
|
+
* complete-value pulls (`value()`, `collect()`, `whole()`) go through the
|
|
27
|
+
* final parser (`parseJsonWithRepair`), whose objects are last-write-wins.
|
|
28
|
+
*
|
|
29
|
+
* Mid-stream cursors tolerate incomplete tokens but read double-quoted
|
|
30
|
+
* strings with the final parser's strict closing rule: an unescaped inner
|
|
31
|
+
* `"` can never swallow a sibling key or value. A pulled scalar completes
|
|
32
|
+
* only once a value terminator follows it, like numbers, so structural
|
|
33
|
+
* garbage after a value surfaces as `incomplete` rather than a silently
|
|
34
|
+
* misparsed pull. Single-quote recovery (`'it's'`) is shared with the final
|
|
35
|
+
* parser and passes both.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const { feed, doc } = IncomingDoc.channel();
|
|
40
|
+
* const args = doc.root().object();
|
|
41
|
+
* const content = args.key("content").string();
|
|
42
|
+
* feed.push('{"path":"a.ts","content":"hel');
|
|
43
|
+
* await content.nextChunk(); // "hel"
|
|
44
|
+
* feed.push('lo"}');
|
|
45
|
+
* feed.finish();
|
|
46
|
+
* await content.nextChunk(); // "lo"
|
|
47
|
+
* await content.nextChunk(); // undefined
|
|
48
|
+
* await args.key("path").value<string>(); // "a.ts"
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
/** Location component in a pulled JSON path: an object member name or an array index. */
|
|
52
|
+
export type PullPathSegment = string | number;
|
|
53
|
+
/** JSON shape observed by a started pull. */
|
|
54
|
+
export type IncomingValueKind = "null" | "boolean" | "number" | "string" | "array" | "object";
|
|
55
|
+
/** Why a pull could not produce the requested shape. */
|
|
56
|
+
export type PullIssueKind =
|
|
57
|
+
/** The requested member was absent when its container completed. */
|
|
58
|
+
"missing"
|
|
59
|
+
/** The producer finished before the pulled value's closing token. */
|
|
60
|
+
| "incomplete"
|
|
61
|
+
/** The producer abandoned the input before the pull completed. */
|
|
62
|
+
| "aborted"
|
|
63
|
+
/** A complete pulled value could not be parsed. */
|
|
64
|
+
| "malformed"
|
|
65
|
+
/** A value was present with a different JSON shape (see `found`). */
|
|
66
|
+
| "mismatch";
|
|
67
|
+
/** Structured failure while awaiting an incoming JSON value. */
|
|
68
|
+
export declare class IncomingJsonError extends Error {
|
|
69
|
+
/** Full key/index path pulled by the consumer. */
|
|
70
|
+
readonly path: readonly PullPathSegment[];
|
|
71
|
+
/** Shape requested by the typed cursor. */
|
|
72
|
+
readonly expected: string;
|
|
73
|
+
/** Why the pull could not produce that shape. */
|
|
74
|
+
readonly kind: PullIssueKind;
|
|
75
|
+
/** Shape observed in the input when `kind` is `mismatch`. */
|
|
76
|
+
readonly found?: string;
|
|
77
|
+
constructor(path: readonly PullPathSegment[], expected: string, kind: PullIssueKind, options?: {
|
|
78
|
+
found?: string;
|
|
79
|
+
cause?: unknown;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
type Shape = {
|
|
83
|
+
kind: "null" | "array" | "object";
|
|
84
|
+
} | {
|
|
85
|
+
kind: "boolean";
|
|
86
|
+
value: boolean;
|
|
87
|
+
} | {
|
|
88
|
+
kind: "number";
|
|
89
|
+
value: number;
|
|
90
|
+
} | {
|
|
91
|
+
kind: "string";
|
|
92
|
+
value: string;
|
|
93
|
+
stableLen: number;
|
|
94
|
+
};
|
|
95
|
+
interface Located {
|
|
96
|
+
tag: "located";
|
|
97
|
+
/** Source offset of the value's first char. */
|
|
98
|
+
start: number;
|
|
99
|
+
/** Source offset just past the value's closing token; `undefined` while it is still open. */
|
|
100
|
+
end: number | undefined;
|
|
101
|
+
shape: Shape;
|
|
102
|
+
}
|
|
103
|
+
/** Readiness predicate a pull waits for once its value has been located. */
|
|
104
|
+
type Ready = (located: Located) => boolean;
|
|
105
|
+
/** Append-only buffer, terminal state, and change notification shared by one feed and its cursors. */
|
|
106
|
+
declare class Shared {
|
|
107
|
+
#private;
|
|
108
|
+
text: string;
|
|
109
|
+
end: "open" | "finished" | "aborted";
|
|
110
|
+
/** Resolves once the buffer or terminal state changes after this call. */
|
|
111
|
+
get changed(): Promise<void>;
|
|
112
|
+
notify(): void;
|
|
113
|
+
/** Await input completion; rejects with `aborted` when the feed was abandoned. */
|
|
114
|
+
finished(): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Await the value at `path` until `ready` accepts it. Resolves `undefined`
|
|
117
|
+
* when the value's container completed without it.
|
|
118
|
+
*/
|
|
119
|
+
pull(path: readonly PullPathSegment[], expected: string, ready: Ready): Promise<Located | undefined>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Push side of an {@link IncomingDoc} channel. Call {@link finish} to mark
|
|
123
|
+
* the document complete or {@link abort} to abandon it; both are idempotent
|
|
124
|
+
* and every pending pull settles on the first one.
|
|
125
|
+
*/
|
|
126
|
+
export declare class IncomingFeed {
|
|
127
|
+
#private;
|
|
128
|
+
constructor(shared: Shared);
|
|
129
|
+
/** Append one text fragment and wake every pending cursor. Throws once the feed is closed. */
|
|
130
|
+
push(fragment: string): void;
|
|
131
|
+
/** Mark the input complete. */
|
|
132
|
+
finish(): void;
|
|
133
|
+
/** Abandon the input; pending and future pulls reject with kind `aborted`. */
|
|
134
|
+
abort(): void;
|
|
135
|
+
}
|
|
136
|
+
/** Read side of one growing JSON document. */
|
|
137
|
+
export declare class IncomingDoc {
|
|
138
|
+
#private;
|
|
139
|
+
constructor(shared: Shared);
|
|
140
|
+
/** Create a push feed and its read-side document. */
|
|
141
|
+
static channel(): {
|
|
142
|
+
feed: IncomingFeed;
|
|
143
|
+
doc: IncomingDoc;
|
|
144
|
+
};
|
|
145
|
+
/** Text received so far. */
|
|
146
|
+
get text(): string;
|
|
147
|
+
/** Await explicit input completion; rejects with kind `aborted` if the feed was abandoned. */
|
|
148
|
+
finished(): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Parse the entire finished document with the final tolerant parser.
|
|
151
|
+
* Waits for {@link IncomingFeed.finish}; aborted input is not decoded and a
|
|
152
|
+
* document that fails the final parse rejects with kind `malformed`.
|
|
153
|
+
*/
|
|
154
|
+
whole<T = unknown>(): Promise<T>;
|
|
155
|
+
/** Cursor for the root JSON value. */
|
|
156
|
+
root(): IncomingJson;
|
|
157
|
+
}
|
|
158
|
+
/** Cursor for one JSON value in the incoming document, addressed by path. */
|
|
159
|
+
export declare class IncomingJson {
|
|
160
|
+
#private;
|
|
161
|
+
readonly path: readonly PullPathSegment[];
|
|
162
|
+
constructor(shared: Shared, path: readonly PullPathSegment[]);
|
|
163
|
+
/** Await the value's first token and report its JSON shape. */
|
|
164
|
+
kind(): Promise<IncomingValueKind>;
|
|
165
|
+
/** Await and parse the complete value. Containers go through the final tolerant parser. */
|
|
166
|
+
value<T = unknown>(): Promise<T>;
|
|
167
|
+
/** Await a complete number. */
|
|
168
|
+
number(): Promise<number>;
|
|
169
|
+
/** Await a complete boolean. */
|
|
170
|
+
boolean(): Promise<boolean>;
|
|
171
|
+
/** Await a complete `null`. */
|
|
172
|
+
null(): Promise<null>;
|
|
173
|
+
/** View this value as an incremental decoded string. */
|
|
174
|
+
string(): IncomingString;
|
|
175
|
+
/** View this value as an array of element cursors. */
|
|
176
|
+
array(): IncomingArray;
|
|
177
|
+
/** View this value as an object with keyed cursors. */
|
|
178
|
+
object(): IncomingObject;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Incremental decoded string consumer. Chunks are emitted in order without
|
|
182
|
+
* overlap and are always prefixes of the final decoded string;
|
|
183
|
+
* {@link text} returns the complete string independently of whether chunks
|
|
184
|
+
* were consumed. Async iteration yields chunks. Concurrent `nextChunk` /
|
|
185
|
+
* `nextLine` calls are served in call order, like a stream reader: a call
|
|
186
|
+
* whose result is abandoned still consumes its chunk.
|
|
187
|
+
*/
|
|
188
|
+
export declare class IncomingString implements AsyncIterable<string> {
|
|
189
|
+
#private;
|
|
190
|
+
constructor(shared: Shared, path: readonly PullPathSegment[]);
|
|
191
|
+
/** Await the next stable decoded chunk, or `undefined` after the closing quote. */
|
|
192
|
+
nextChunk(): Promise<string | undefined>;
|
|
193
|
+
/**
|
|
194
|
+
* Await the next complete decoded line, retaining its trailing newline.
|
|
195
|
+
* A final unterminated line is returned once the closing quote arrives.
|
|
196
|
+
*/
|
|
197
|
+
nextLine(): Promise<string | undefined>;
|
|
198
|
+
/** Iterate complete decoded lines. */
|
|
199
|
+
lines(): AsyncGenerator<string, void, undefined>;
|
|
200
|
+
[Symbol.asyncIterator](): AsyncGenerator<string, void, undefined>;
|
|
201
|
+
/** Await the closing quote and return the complete decoded string. */
|
|
202
|
+
text(): Promise<string>;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Linear cursor over elements of an incoming array. Async iteration yields
|
|
206
|
+
* element cursors. Concurrent `next` calls are served in call order.
|
|
207
|
+
*/
|
|
208
|
+
export declare class IncomingArray implements AsyncIterable<IncomingJson> {
|
|
209
|
+
#private;
|
|
210
|
+
constructor(shared: Shared, path: readonly PullPathSegment[]);
|
|
211
|
+
/** Await the start of the next element; `undefined` only after the closing bracket. */
|
|
212
|
+
next(): Promise<IncomingJson | undefined>;
|
|
213
|
+
[Symbol.asyncIterator](): AsyncGenerator<IncomingJson, void, undefined>;
|
|
214
|
+
/** Await the closing bracket and collect the fully parsed elements. */
|
|
215
|
+
collect<T = unknown>(): Promise<T[]>;
|
|
216
|
+
}
|
|
217
|
+
/** Keyed cursor and final collection for an incoming object. */
|
|
218
|
+
export declare class IncomingObject {
|
|
219
|
+
#private;
|
|
220
|
+
constructor(shared: Shared, path: readonly PullPathSegment[]);
|
|
221
|
+
/**
|
|
222
|
+
* Cursor bound to the first occurrence of `name`. Pulling it makes the
|
|
223
|
+
* key required: a missing member rejects with kind `missing`.
|
|
224
|
+
*/
|
|
225
|
+
key(name: string): IncomingJson;
|
|
226
|
+
/**
|
|
227
|
+
* Await the closing brace and collect the object through the final parser,
|
|
228
|
+
* whose duplicate keys are last-write-wins unlike {@link key}.
|
|
229
|
+
*/
|
|
230
|
+
collect<T = Record<string, unknown>>(): Promise<T>;
|
|
231
|
+
}
|
|
232
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./format.js";
|
|
|
10
10
|
export * from "./frontmatter.js";
|
|
11
11
|
export * from "./fs-error.js";
|
|
12
12
|
export * from "./glob.js";
|
|
13
|
+
export * from "./incoming-json.js";
|
|
13
14
|
export * from "./json.js";
|
|
14
15
|
export * from "./json-parse.js";
|
|
15
16
|
export * as logger from "./logger.js";
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tolerant JSON lexer shared by the final parser (`parseJsonWithRepair`), the
|
|
3
|
+
* streaming partial builder (`parseStreamingJson`), and the incoming cursors
|
|
4
|
+
* (`IncomingDoc`). {@link JsonLexerMode} selects how truncation and unescaped
|
|
5
|
+
* inner double quotes are treated.
|
|
6
|
+
*
|
|
7
|
+
* The grammar is a forgiving superset of JSON covering malformations commonly
|
|
8
|
+
* produced by language models:
|
|
9
|
+
*
|
|
10
|
+
* - single-quoted strings and unquoted object keys (JSON5);
|
|
11
|
+
* - trailing / stray commas, and `//` + block comments;
|
|
12
|
+
* - Python literals `True` / `False` / `None`, plus `0x` / `0b` numbers;
|
|
13
|
+
* - raw control characters and invalid `\x` escapes inside strings (kept literally);
|
|
14
|
+
* - unescaped quotes inside strings — a single quote only closes a string when
|
|
15
|
+
* followed by a value terminator, recovering apostrophes such as `'it's'`;
|
|
16
|
+
* the same recovery applies to double quotes in `streaming` mode only,
|
|
17
|
+
* everywhere else they close strictly;
|
|
18
|
+
* - unquoted string values in value position — an unrecognized bareword such
|
|
19
|
+
* as `{"paths": packages/foo/*}` is recovered as a string up to the next
|
|
20
|
+
* `,` / `}` / `]` / newline.
|
|
21
|
+
*/
|
|
22
|
+
export declare const QUOTE = 34;
|
|
23
|
+
export declare const SQUOTE = 39;
|
|
24
|
+
export declare const BACKSLASH = 92;
|
|
25
|
+
export declare const SLASH = 47;
|
|
26
|
+
export declare const COLON = 58;
|
|
27
|
+
export declare const COMMA = 44;
|
|
28
|
+
export declare const LBRACE = 123;
|
|
29
|
+
export declare const RBRACE = 125;
|
|
30
|
+
export declare const LBRACKET = 91;
|
|
31
|
+
export declare const RBRACKET = 93;
|
|
32
|
+
/** Valid chars after `\` in a strict JSON escape: `" \ / b f n r t u`. */
|
|
33
|
+
export declare const VALID_ESCAPE_CHAR: Uint8Array<ArrayBuffer>;
|
|
34
|
+
export declare function isHexDigit(cp: number): boolean;
|
|
35
|
+
/** JSON insignificant whitespace (RFC 8259 §2). */
|
|
36
|
+
export declare function isWhitespace(cp: number): boolean;
|
|
37
|
+
/** First char of a numeric token: sign, dot, or digit. */
|
|
38
|
+
export declare function isNumberStart(cp: number): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Grammar tolerance selected by the lexer's consumer.
|
|
41
|
+
*
|
|
42
|
+
* - `strict`: final parse — complete input required, double quotes close strictly.
|
|
43
|
+
* - `streaming`: mid-stream snapshot — incomplete tokens tolerated and unescaped
|
|
44
|
+
* inner double quotes recovered for display.
|
|
45
|
+
* - `incoming`: incremental typed pulls — incomplete tokens tolerated, but double
|
|
46
|
+
* quotes close strictly so pulled values match the final parse.
|
|
47
|
+
*/
|
|
48
|
+
export type JsonLexerMode = "strict" | "streaming" | "incoming";
|
|
49
|
+
/** Decoded state of a string token at the current streaming edge. */
|
|
50
|
+
export interface JsonStringProgress {
|
|
51
|
+
/** Decoded content so far (complete when `complete` is true). */
|
|
52
|
+
value: string;
|
|
53
|
+
/**
|
|
54
|
+
* Length of the prefix of `value` whose meaning cannot change when more
|
|
55
|
+
* input arrives. Excludes a trailing split escape, a high surrogate whose
|
|
56
|
+
* low half may still follow, and everything from a quote whose close/inner
|
|
57
|
+
* reading is still undecidable at the buffer edge.
|
|
58
|
+
*/
|
|
59
|
+
stableLen: number;
|
|
60
|
+
/** Whether the closing quote was consumed. */
|
|
61
|
+
complete: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Cursor over the input with the tolerant token readers. `pos` is the current
|
|
65
|
+
* offset; readers advance it. In `strict` mode a truncated or malformed token
|
|
66
|
+
* throws `SyntaxError`; the lenient modes report progress or return
|
|
67
|
+
* `undefined` so the caller can roll back.
|
|
68
|
+
*/
|
|
69
|
+
export declare class JsonLexer {
|
|
70
|
+
#private;
|
|
71
|
+
readonly src: string;
|
|
72
|
+
readonly mode: JsonLexerMode;
|
|
73
|
+
pos: number;
|
|
74
|
+
constructor(src: string, mode: JsonLexerMode, pos?: number);
|
|
75
|
+
get atEnd(): boolean;
|
|
76
|
+
/** Char code at the cursor; `NaN` at end of input (so every comparison is false). */
|
|
77
|
+
peek(): number;
|
|
78
|
+
/** Skip whitespace plus `//` line and `/* *\/` block comments. */
|
|
79
|
+
ws(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Read a string starting at the opening `quote`, retaining the information
|
|
82
|
+
* an incremental consumer needs. Strict mode throws on an unterminated
|
|
83
|
+
* string; lenient modes consume to the end of input and report progress.
|
|
84
|
+
*/
|
|
85
|
+
string(quote: number): JsonStringProgress;
|
|
86
|
+
/**
|
|
87
|
+
* Read a numeric token with JS `Number()` semantics (decimal with optional
|
|
88
|
+
* sign / leading or trailing dot / exponent, plus `0x` hex and `0b` binary).
|
|
89
|
+
* Non-finite or malformed tokens throw in strict mode and return
|
|
90
|
+
* `undefined` in lenient modes; the cursor is left past the token either way.
|
|
91
|
+
*/
|
|
92
|
+
number(): number | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Match a keyword literal at the cursor; consumes only on success and
|
|
95
|
+
* returns `undefined` otherwise. Requires a non-identifier boundary so
|
|
96
|
+
* `Truex` / `nullish` are not misread as the keyword followed by junk.
|
|
97
|
+
*/
|
|
98
|
+
keyword(): boolean | null | undefined;
|
|
99
|
+
/** Read an unquoted object key: everything up to `:` / `,` / `}` / whitespace. May be empty. */
|
|
100
|
+
unquotedKey(): string;
|
|
101
|
+
/**
|
|
102
|
+
* Recover an unquoted string value, e.g. `{"paths": packages/foo/*}`:
|
|
103
|
+
* consume until `,` / `}` / `]` / newline and trim trailing whitespace.
|
|
104
|
+
* Recovery still fails — so a final parse never accepts a half-formed or
|
|
105
|
+
* non-finite argument — when the token:
|
|
106
|
+
* - hits end-of-input before a delimiter (truncated value);
|
|
107
|
+
* - contains a `"`, `{`, `[`, or a key-like `:` — this grammar accepts
|
|
108
|
+
* unquoted keys, so a missed comma (`{"a": foo "b": 1}`) would otherwise
|
|
109
|
+
* silently swallow the following field. A colon followed by `/` or `\`
|
|
110
|
+
* stays literal so URL and Windows-path values recover;
|
|
111
|
+
* - is a non-finite atom ({@link NON_RECOVERABLE_BAREWORDS}).
|
|
112
|
+
*
|
|
113
|
+
* Failure throws in strict mode and returns `undefined` in lenient modes.
|
|
114
|
+
*/
|
|
115
|
+
bareword(): string | undefined;
|
|
116
|
+
}
|
package/dist/types/procmgr.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export declare function isCmdShell(shell: string): boolean;
|
|
|
32
32
|
* parse `-l` as the command and fail with `The term '-l' is not recognized`.
|
|
33
33
|
*/
|
|
34
34
|
export declare function isPowerShell(shell: string): boolean;
|
|
35
|
+
/** Whether the executable is a known shell whose command language uses POSIX quoting. */
|
|
36
|
+
export declare function isPosixShell(shell: string): boolean;
|
|
35
37
|
/**
|
|
36
38
|
* Resolve a basic shell (bash or sh) as fallback.
|
|
37
39
|
*/
|
|
@@ -16,8 +16,30 @@ export declare class ExtraCaError extends Error {
|
|
|
16
16
|
cause?: unknown;
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the PEM bytes referenced by `NODE_EXTRA_CA_CERTS`, or `undefined`
|
|
21
|
+
* when the env var is unset/empty.
|
|
22
|
+
*
|
|
23
|
+
* Accepts the same shapes Node accepts plus an inline-PEM escape hatch:
|
|
24
|
+
* - Inline PEM (`-----BEGIN CERTIFICATE-----...`). Literal `\n` escapes in
|
|
25
|
+
* the env value are expanded so callers can ship single-line PEMs through
|
|
26
|
+
* shell exports.
|
|
27
|
+
* - File path. Anything that does not contain a PEM header is treated as a
|
|
28
|
+
* path, matching Node's "extensionless filename is still a path" contract.
|
|
29
|
+
* `ENOENT` becomes {@link ExtraCaError}; other I/O errors bubble.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveExtraCa(): string | undefined;
|
|
19
32
|
/** Test seam: drop the cached PEM so a follow-up call re-reads the env. */
|
|
20
33
|
export declare function __resetExtraCaCache(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Merge `extraCa` into `init.tls.ca`. When the caller has not supplied a CA
|
|
36
|
+
* list, the system root store is included alongside the extra bundle —
|
|
37
|
+
* Bun's `tls.ca` replaces the default trust store, so omitting roots would
|
|
38
|
+
* break every public host. When the caller already curated a list (e.g.
|
|
39
|
+
* Anthropic Foundry's mTLS options, which already seed
|
|
40
|
+
* `tls.rootCertificates`), only the extra CA is appended.
|
|
41
|
+
*/
|
|
42
|
+
export declare function withExtraCaInit(init: RequestInit | undefined, extraCa: string): RequestInit;
|
|
21
43
|
/**
|
|
22
44
|
* Wrap `fetchImpl` so every call honours `NODE_EXTRA_CA_CERTS`. Idempotent:
|
|
23
45
|
* a fetch already wrapped is returned unchanged so repeated composition
|
|
@@ -26,12 +48,3 @@ export declare function __resetExtraCaCache(): void;
|
|
|
26
48
|
* default deployments pay nothing.
|
|
27
49
|
*/
|
|
28
50
|
export declare function wrapFetchForExtraCa(fetchImpl: FetchImpl): FetchImpl;
|
|
29
|
-
/**
|
|
30
|
-
* Convenience for options-bag composition (e.g. the stream-entry path in
|
|
31
|
-
* `@linxiraos/pi-ai`'s `stream.ts`, which mirrors `withRequestDebugFetch` so
|
|
32
|
-
* the proxy/debug/extra-CA wrappers compose uniformly). No-op when the env
|
|
33
|
-
* var is unset.
|
|
34
|
-
*/
|
|
35
|
-
export declare function withExtraCaFetch<T extends {
|
|
36
|
-
fetch?: FetchImpl;
|
|
37
|
-
} | undefined>(options: T): T;
|
package/package.json
CHANGED
|
@@ -1,44 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"version": "1.1.8",
|
|
5
|
-
"description": "Shared utilities for pi packages",
|
|
6
|
-
"homepage": "https://linxira-os.github.io/zeta/",
|
|
7
|
-
"author": "Can Boluk",
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
|
|
12
|
-
"directory": "packages/utils"
|
|
13
|
-
},
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/Linxira-OS/linxira-zeta/issues"
|
|
2
|
+
"engines": {
|
|
3
|
+
"bun": ">=1.3.14"
|
|
16
4
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"cli",
|
|
20
|
-
"logging",
|
|
21
|
-
"streams"
|
|
22
|
-
],
|
|
23
|
-
"main": "./src/index.ts",
|
|
24
|
-
"types": "./dist/types/index.d.ts",
|
|
25
|
-
"scripts": {
|
|
26
|
-
"check": "biome check . && bun run check:types",
|
|
27
|
-
"check:types": "tsgo -p tsconfig.json --noEmit",
|
|
28
|
-
"lint": "biome lint .",
|
|
29
|
-
"test": "bun test --parallel",
|
|
30
|
-
"fix": "biome check --write --unsafe .",
|
|
31
|
-
"fmt": "biome format --write ."
|
|
5
|
+
"devDependencies": {
|
|
6
|
+
"@types/bun": "^1.3.14"
|
|
32
7
|
},
|
|
33
8
|
"dependencies": {
|
|
34
|
-
"@linxiraos/pi-natives": "1.1.
|
|
9
|
+
"@linxiraos/pi-natives": "1.1.10"
|
|
35
10
|
},
|
|
36
|
-
"
|
|
37
|
-
"
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "bun test --parallel",
|
|
13
|
+
"check:types": "tsgo -p tsconfig.json --noEmit",
|
|
14
|
+
"check": "tsgo -p tsconfig.json --noEmit"
|
|
38
15
|
},
|
|
39
|
-
"
|
|
40
|
-
"
|
|
16
|
+
"exports": {
|
|
17
|
+
"./*.js": "./src/*.ts",
|
|
18
|
+
"./*": {
|
|
19
|
+
"import": "./src/*.ts",
|
|
20
|
+
"types": "./dist/types/*.d.ts"
|
|
21
|
+
},
|
|
22
|
+
"./ar": {
|
|
23
|
+
"import": "./src/ar/index.ts",
|
|
24
|
+
"types": "./dist/types/ar/index.d.ts"
|
|
25
|
+
},
|
|
26
|
+
".": {
|
|
27
|
+
"import": "./src/index.ts",
|
|
28
|
+
"types": "./dist/types/index.d.ts"
|
|
29
|
+
}
|
|
41
30
|
},
|
|
31
|
+
"types": "./dist/types/index.d.ts",
|
|
32
|
+
"main": "./src/index.ts",
|
|
33
|
+
"type": "module",
|
|
42
34
|
"files": [
|
|
43
35
|
"src",
|
|
44
36
|
"README.md",
|
|
@@ -47,19 +39,24 @@
|
|
|
47
39
|
"THIRD-PARTY-NOTICES.txt",
|
|
48
40
|
"dist/types"
|
|
49
41
|
],
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
|
|
42
|
+
"repository": {
|
|
43
|
+
"directory": "packages/utils",
|
|
44
|
+
"url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
|
|
45
|
+
"type": "git"
|
|
46
|
+
},
|
|
47
|
+
"author": "Can Boluk",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/Linxira-OS/linxira-zeta/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://linxira-os.github.io/zeta/",
|
|
53
|
+
"keywords": [
|
|
54
|
+
"utilities",
|
|
55
|
+
"cli",
|
|
56
|
+
"logging",
|
|
57
|
+
"streams"
|
|
58
|
+
],
|
|
59
|
+
"description": "Shared utilities for pi packages",
|
|
60
|
+
"version": "1.1.10",
|
|
61
|
+
"name": "@linxiraos/pi-utils"
|
|
65
62
|
}
|