@linxiraos/pi-natives 1.2.0 → 1.3.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/README.md +11 -1
- package/THIRD-PARTY-NOTICES.txt +5 -5
- package/native/desktop-adapter.js +2 -0
- package/native/index.d.ts +184 -7
- package/native/index.js +4 -0
- package/native/loader-state.js +15 -9
- package/native/vcs.d.ts +3 -0
- package/native/vcs.js +53 -0
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Native Rust functionality via N-API.
|
|
|
10
10
|
- **Audio**: Cross-platform low-latency microphone capture and gapless speaker playback
|
|
11
11
|
- **WebRTC**: Native Opus media, SDP offer/answer negotiation, and data-channel events for live sessions
|
|
12
12
|
- **File locking**: Process-owned cross-process locks with in-memory kernel names on Linux/Windows and `flock(2)` sidecars on other Unix platforms
|
|
13
|
+
- **Local IPC**: `IpcServer`/`IpcConnection` over Unix sockets and Windows named pipes — owner-only access, single-instance binding with stale-endpoint takeover, newline or raw framing; `ipcEndpoint(dir, name)` names endpoints per platform
|
|
13
14
|
- **PDF**: In-memory PDF-to-Markdown extraction with OCR-page classification via `pdf-inspector`
|
|
14
15
|
- **Binary patches**: `applyBinaryPatch(oldPath, patchPath, outPath)` applies an HDiffPatch single-stream patch (HDIFFSF20, zstd or uncompressed), streaming from files; callers verify digests
|
|
15
16
|
|
|
@@ -40,13 +41,22 @@ const files = await find({
|
|
|
40
41
|
|
|
41
42
|
// SIXEL encode for a terminal cell box (px), off the JS thread
|
|
42
43
|
// (`encodeSixel` is the synchronous form)
|
|
43
|
-
const
|
|
44
|
+
const backgroundRgb = 0x202040; // Actual terminal background, packed 0xRRGGBB
|
|
45
|
+
const sequence = await encodeSixelAsync(pngBytes, widthPx, heightPx, backgroundRgb);
|
|
44
46
|
|
|
45
47
|
// Extract PDF text and identify pages that still need OCR
|
|
46
48
|
const pdf = await pdfToMarkdown(pdfBytes);
|
|
47
49
|
console.log(pdf.markdown, pdf.pagesNeedingOcr);
|
|
48
50
|
```
|
|
49
51
|
|
|
52
|
+
Both SIXEL encoder forms require the terminal background as a fourth argument,
|
|
53
|
+
packed as a 24-bit `0xRRGGBB` number. When resizing images with alpha, RGB is
|
|
54
|
+
premultiplied before Lanczos filtering so transparent pixels cannot darken or
|
|
55
|
+
discolor visible edges. After resizing, the terminal background is composited
|
|
56
|
+
without applying alpha twice. Fully transparent pixels remain transparent;
|
|
57
|
+
unscaled images retain their existing blending path. Re-encode when the terminal
|
|
58
|
+
background RGB changes.
|
|
59
|
+
|
|
50
60
|
### Lazy Windows path helpers
|
|
51
61
|
|
|
52
62
|
Import `expandWindowsLongPath` and `getWindowsShortPath` from
|
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -8,8 +8,8 @@ no rights in third-party code, fonts, data, or marks identified below.
|
|
|
8
8
|
PYTHON DEPENDENCY DISTRIBUTION ROLE
|
|
9
9
|
===================================
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
Uvicorn, HTTPX, Pydantic, pydantic-settings, python-dotenv, Click, and
|
|
11
|
+
zeta-rpc has no third-party runtime dependencies. robomp declares FastAPI,
|
|
12
|
+
Uvicorn, HTTPX, Pydantic, pydantic-settings, python-dotenv, Click, and zeta-rpc
|
|
13
13
|
as ordinary installer-resolved dependencies. The Python wheels do not vendor
|
|
14
14
|
those projects; each dependency remains a separately installed distribution
|
|
15
15
|
carrying its own license metadata and files. Container/system assemblers must
|
|
@@ -79,9 +79,9 @@ uutils coreutils (https://github.com/uutils/coreutils), MIT
|
|
|
79
79
|
--------------------------------------------------------------------------------
|
|
80
80
|
Covers: base32, base64, basename, cat, cksum (shared checksum machinery),
|
|
81
81
|
b2sum, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, comm, cp,
|
|
82
|
-
cut, date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv,
|
|
83
|
-
printenv, readlink, realpath, rm, seq, sort, stat, tac, tail, tee,
|
|
84
|
-
truncate, uname, uniq, wc, whoami, yes.
|
|
82
|
+
cut, date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv, nl, nproc,
|
|
83
|
+
paste, printenv, readlink, realpath, rm, seq, sort, stat, tac, tail, tee,
|
|
84
|
+
touch, tr, truncate, uname, uniq, wc, whoami, yes.
|
|
85
85
|
|
|
86
86
|
Copyright (c) uutils developers
|
|
87
87
|
|
package/native/index.d.ts
CHANGED
|
@@ -43,6 +43,19 @@ export declare class DesktopSession {
|
|
|
43
43
|
* behind the worker, so it answers at once even while a request is stuck.
|
|
44
44
|
*/
|
|
45
45
|
get screenState(): DesktopScreenState
|
|
46
|
+
/**
|
|
47
|
+
* Keeps the display awake from the next activity request until
|
|
48
|
+
* `releaseDisplayAwake()`, however long the session sits idle in between,
|
|
49
|
+
* instead of for the grace period after the last request. An agent calls
|
|
50
|
+
* this when it acts inside a turn.
|
|
51
|
+
*/
|
|
52
|
+
holdDisplayUntilReleased(): void
|
|
53
|
+
/**
|
|
54
|
+
* Lets the display sleep now if a turn holds it, without waiting behind
|
|
55
|
+
* queued requests, and returns later activity to the grace period. Activity
|
|
56
|
+
* outside a turn keeps its grace. An agent calls this when its turn ends.
|
|
57
|
+
*/
|
|
58
|
+
releaseDisplayAwake(): void
|
|
46
59
|
listDisplays(): Promise<Array<DesktopDisplay>>
|
|
47
60
|
listWindows(): Promise<Array<DesktopWindow>>
|
|
48
61
|
listApplications(options?: ApplicationQuery | undefined | null): Promise<Array<Application>>
|
|
@@ -235,6 +248,85 @@ export declare class HighlightStream {
|
|
|
235
248
|
push(chunk: string): string
|
|
236
249
|
}
|
|
237
250
|
|
|
251
|
+
/** One accepted or dialed IPC connection. */
|
|
252
|
+
export declare class IpcConnection {
|
|
253
|
+
/**
|
|
254
|
+
* Dial `endpoint`. Rejects with code ENOENT (no endpoint), ECONNREFUSED
|
|
255
|
+
* (stale socket / no listener), ETIMEDOUT, EACCES (peer owned by another
|
|
256
|
+
* user, or permission), or the OS errno name.
|
|
257
|
+
*/
|
|
258
|
+
static connect(endpoint: string, options: IpcConnectOptions): Promise<IpcConnection>
|
|
259
|
+
/**
|
|
260
|
+
* Begin delivery on a `lines` connection (throws `TypeError` on a `raw`
|
|
261
|
+
* one). Call `start`/`startRaw` exactly once. Frames received before
|
|
262
|
+
* start are buffered, never dropped. `onLine` gets each non-empty line
|
|
263
|
+
* (one trailing CR stripped, invalid UTF-8 replaced lossily). `onClose`
|
|
264
|
+
* fires exactly once, after the last frame — including after local
|
|
265
|
+
* `end()`/`destroy()`: `null` for an orderly close, otherwise
|
|
266
|
+
* `"<CODE>: <message>"`.
|
|
267
|
+
*/
|
|
268
|
+
start(onLine: (line: string) => void, onClose: (reason: string | null) => void): void
|
|
269
|
+
/**
|
|
270
|
+
* Same as `start()` for a `raw` connection (throws `TypeError` on a
|
|
271
|
+
* `lines` one): `onChunk` gets each received chunk.
|
|
272
|
+
*/
|
|
273
|
+
startRaw(onChunk: (chunk: Uint8Array) => void, onClose: (reason: string | null) => void): void
|
|
274
|
+
/**
|
|
275
|
+
* Send one frame. lines: a string without LF (`TypeError` otherwise);
|
|
276
|
+
* LF is appended. raw: string (UTF-8) or `Uint8Array` written as-is.
|
|
277
|
+
* Frames sent during one JS task are written together when it ends (a
|
|
278
|
+
* microtask), in order; `end()` writes them first. Returns false once
|
|
279
|
+
* unwritten bytes exceed 1 MiB (await `drained()` before sending more).
|
|
280
|
+
* After close: returns false and drops the frame.
|
|
281
|
+
*/
|
|
282
|
+
send(frame: string | Uint8Array): boolean
|
|
283
|
+
/** Resolves when the write queue is empty, or the connection closed. */
|
|
284
|
+
drained(): Promise<void>
|
|
285
|
+
/**
|
|
286
|
+
* Graceful: write everything queued, then shut the write half (Unix) /
|
|
287
|
+
* close the pipe (Windows); onClose follows when the read side ends.
|
|
288
|
+
*/
|
|
289
|
+
end(): void
|
|
290
|
+
/**
|
|
291
|
+
* Immediate close; queued writes are dropped; onClose(null) follows.
|
|
292
|
+
* Idempotent.
|
|
293
|
+
*/
|
|
294
|
+
destroy(): void
|
|
295
|
+
/** Keep the event loop alive while delivering (default). */
|
|
296
|
+
ref(): void
|
|
297
|
+
/** Let the event loop exit while this connection is open. */
|
|
298
|
+
unref(): void
|
|
299
|
+
/** Whether the connection is closed (or `destroy()` was called). */
|
|
300
|
+
get closed(): boolean
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Listening IPC endpoint. */
|
|
304
|
+
export declare class IpcServer {
|
|
305
|
+
/**
|
|
306
|
+
* Bind and listen.
|
|
307
|
+
*
|
|
308
|
+
* Unix: creates the parent dir (0700) if missing; under an exclusive flock
|
|
309
|
+
* on `<path>.bind.lock`, rejects EADDRINUSE when a live server answers,
|
|
310
|
+
* otherwise replaces the stale socket file; the socket is chmod 0600 before
|
|
311
|
+
* `listen()`. Windows: first pipe instance (exists → EADDRINUSE), remote
|
|
312
|
+
* clients rejected, DACL granting only the current user.
|
|
313
|
+
* `onConnection` receives each accepted, peer-verified connection (not yet
|
|
314
|
+
* started).
|
|
315
|
+
*/
|
|
316
|
+
static listen(endpoint: string, options: IpcListenOptions, onConnection: (connection: IpcConnection) => void): Promise<IpcServer>
|
|
317
|
+
/** The endpoint this server listens on. */
|
|
318
|
+
get endpoint(): string
|
|
319
|
+
/**
|
|
320
|
+
* Stop accepting. Unix: unlink the socket file if it is still the inode
|
|
321
|
+
* this server bound. Accepted connections are untouched. Idempotent.
|
|
322
|
+
*/
|
|
323
|
+
close(): Promise<void>
|
|
324
|
+
/** Keep the event loop alive while listening (default). */
|
|
325
|
+
ref(): void
|
|
326
|
+
/** Let the event loop exit while listening. */
|
|
327
|
+
unref(): void
|
|
328
|
+
}
|
|
329
|
+
|
|
238
330
|
/** WebRTC peer that accepts 16 kHz mono PCM and renders remote Opus audio. */
|
|
239
331
|
export declare class LiveWebRtcPeer {
|
|
240
332
|
/**
|
|
@@ -1702,13 +1794,17 @@ export interface EnclosingBoundaryOptions {
|
|
|
1702
1794
|
* the calling thread.
|
|
1703
1795
|
*
|
|
1704
1796
|
* The input image is decoded and resized to the requested pixel dimensions
|
|
1705
|
-
*
|
|
1706
|
-
*
|
|
1797
|
+
* (not resampled when it already has them), then partial alpha is composited
|
|
1798
|
+
* against `background_rgb` (packed `0xRRGGBB`). Fully transparent pixels
|
|
1799
|
+
* remain transparent. Encoding uses whole 6-pixel bands: a partial last band
|
|
1800
|
+
* is dropped, see [`band_rows`]. Prefer
|
|
1801
|
+
* [`encode_sixel_async`] unless the caller cannot wait: the decode, resize
|
|
1802
|
+
* and dither block the JS thread.
|
|
1707
1803
|
*
|
|
1708
1804
|
* # Errors
|
|
1709
1805
|
* Returns an error if decoding, resizing, or SIXEL encoding fails.
|
|
1710
1806
|
*/
|
|
1711
|
-
export declare function encodeSixel(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number): string
|
|
1807
|
+
export declare function encodeSixel(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number, backgroundRgb: number): string
|
|
1712
1808
|
|
|
1713
1809
|
/**
|
|
1714
1810
|
* Same result as [`encode_sixel`], but the decode, resize and dither run on
|
|
@@ -1717,7 +1813,7 @@ export declare function encodeSixel(bytes: Uint8Array, targetWidthPx: number, ta
|
|
|
1717
1813
|
* # Errors
|
|
1718
1814
|
* Rejects if decoding, resizing, or SIXEL encoding fails.
|
|
1719
1815
|
*/
|
|
1720
|
-
export declare function encodeSixelAsync(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number): Promise<string>
|
|
1816
|
+
export declare function encodeSixelAsync(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number, backgroundRgb: number): Promise<string>
|
|
1721
1817
|
|
|
1722
1818
|
/** Tokenizer encoding to use. */
|
|
1723
1819
|
export declare enum Encoding {
|
|
@@ -1979,7 +2075,10 @@ export interface GrepOptions {
|
|
|
1979
2075
|
pattern: string
|
|
1980
2076
|
/** Directory or file to search: a host path or an absolute `scheme://` URL. */
|
|
1981
2077
|
path: string
|
|
1982
|
-
/**
|
|
2078
|
+
/**
|
|
2079
|
+
* Glob filter for file paths (e.g., "*.ts"); a matched directory selects
|
|
2080
|
+
* every file under it.
|
|
2081
|
+
*/
|
|
1983
2082
|
glob?: string
|
|
1984
2083
|
/**
|
|
1985
2084
|
* Match simple glob patterns at any depth (default: true; `*.ts` ->
|
|
@@ -2193,6 +2292,44 @@ export interface InlineSloppyRegion {
|
|
|
2193
2292
|
*/
|
|
2194
2293
|
export declare function invalidateFsScanCache(path?: string | undefined | null): void
|
|
2195
2294
|
|
|
2295
|
+
/** Options for `IpcConnection.connect`. */
|
|
2296
|
+
export interface IpcConnectOptions {
|
|
2297
|
+
/**
|
|
2298
|
+
* `lines`: UTF-8 text frames delimited by LF; `raw`: byte chunks as
|
|
2299
|
+
* received.
|
|
2300
|
+
*/
|
|
2301
|
+
framing: "lines" | "raw"
|
|
2302
|
+
/** Connect deadline. Default 3000 ms. Expiry rejects with code ETIMEDOUT. */
|
|
2303
|
+
timeoutMs?: number
|
|
2304
|
+
/** Max bytes of one received line (lines framing). Default 64 MiB. */
|
|
2305
|
+
maxFrameBytes?: number
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
/**
|
|
2309
|
+
* Platform endpoint for `name` scoped to directory `dir`.
|
|
2310
|
+
*
|
|
2311
|
+
* Unix: `<dir>/<stem>.sock`; when that path does not fit `sun_path` (104 bytes
|
|
2312
|
+
* on macOS/BSD, 108 on Linux, including NUL), `/tmp/omp-<euid>/<hash16>.sock`.
|
|
2313
|
+
* Windows: `\\.\pipe\omp-<stem>-<hash16(lowercase(resolve(dir)))>`.
|
|
2314
|
+
* `stem` is `name` with every char outside `[A-Za-z0-9._-]` replaced by "_".
|
|
2315
|
+
* Pure and deterministic: every process computes the same endpoint.
|
|
2316
|
+
*/
|
|
2317
|
+
export declare function ipcEndpoint(dir: string, name: string): string
|
|
2318
|
+
|
|
2319
|
+
/** Options for `IpcServer.listen`. */
|
|
2320
|
+
export interface IpcListenOptions {
|
|
2321
|
+
/**
|
|
2322
|
+
* `lines`: UTF-8 text frames delimited by LF; `raw`: byte chunks as
|
|
2323
|
+
* received.
|
|
2324
|
+
*/
|
|
2325
|
+
framing: "lines" | "raw"
|
|
2326
|
+
/**
|
|
2327
|
+
* Max bytes of one received line (lines framing). Default 64 MiB.
|
|
2328
|
+
* Exceeding closes that connection with reason "EMSGSIZE: …".
|
|
2329
|
+
*/
|
|
2330
|
+
maxFrameBytes?: number
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2196
2333
|
/** Kind enum of the backend selected by default for this build target. */
|
|
2197
2334
|
export declare function isoBackend(): IsoBackendKind
|
|
2198
2335
|
|
|
@@ -2342,6 +2479,13 @@ export interface ListWorkspaceOptions {
|
|
|
2342
2479
|
* to find them. Default: false.
|
|
2343
2480
|
*/
|
|
2344
2481
|
collectAgentsMd?: boolean
|
|
2482
|
+
/**
|
|
2483
|
+
* Report unreadable directories instead of silently skipping them: an
|
|
2484
|
+
* unreadable root fails the scan, and unreadable subdirectories are
|
|
2485
|
+
* returned in `unreadableDirectories` while the walk continues. Default:
|
|
2486
|
+
* false (startup discovery stays best-effort).
|
|
2487
|
+
*/
|
|
2488
|
+
reportDirectoryErrors?: boolean
|
|
2345
2489
|
/** Timeout in milliseconds for the operation. */
|
|
2346
2490
|
timeoutMs?: number
|
|
2347
2491
|
/** Abort signal for cancelling the operation. */
|
|
@@ -2359,6 +2503,11 @@ export interface ListWorkspaceResult {
|
|
|
2359
2503
|
agentsMdFiles: Array<string>
|
|
2360
2504
|
/** True when any output cap was hit. */
|
|
2361
2505
|
truncated: boolean
|
|
2506
|
+
/**
|
|
2507
|
+
* Subdirectories whose contents could not be listed. Always empty unless
|
|
2508
|
+
* `reportDirectoryErrors` is set.
|
|
2509
|
+
*/
|
|
2510
|
+
unreadableDirectories: Array<WorkspaceDirectoryError>
|
|
2362
2511
|
}
|
|
2363
2512
|
|
|
2364
2513
|
/**
|
|
@@ -2592,6 +2741,9 @@ export interface NodeSpan {
|
|
|
2592
2741
|
/** Decode notebook JSON into the editable cell-marker text. */
|
|
2593
2742
|
export declare function notebookToEditableText(json: string, displayPath: string): string
|
|
2594
2743
|
|
|
2744
|
+
/** Check syntax through the shared parse cache without constructing a summary. */
|
|
2745
|
+
export declare function parseCodeStatus(options: ParseStatusOptions): ParseStatus
|
|
2746
|
+
|
|
2595
2747
|
/** Parsed Kitty keyboard protocol sequence result for a Kitty input sequence. */
|
|
2596
2748
|
export interface ParsedKittyResult {
|
|
2597
2749
|
/** Primary codepoint associated with the key. */
|
|
@@ -2620,6 +2772,23 @@ export declare function parseKey(data: string, kittyProtocolActive: boolean): st
|
|
|
2620
2772
|
*/
|
|
2621
2773
|
export declare function parseKittySequence(data: string): ParsedKittyResult | null
|
|
2622
2774
|
|
|
2775
|
+
export interface ParseStatus {
|
|
2776
|
+
/** Canonical language name only when parsing succeeded. */
|
|
2777
|
+
language?: string
|
|
2778
|
+
/** True when non-empty source parses without syntax errors. */
|
|
2779
|
+
parsed: boolean
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
/** Options for checking source syntax without generating a summary. */
|
|
2783
|
+
export interface ParseStatusOptions {
|
|
2784
|
+
/** Source code to parse; empty input is not parsed. */
|
|
2785
|
+
code: string
|
|
2786
|
+
/** Language alias; a non-blank value overrides path-based detection. */
|
|
2787
|
+
lang?: string
|
|
2788
|
+
/** File path used to infer language when `lang` is omitted or blank. */
|
|
2789
|
+
path?: string
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2623
2792
|
/** One hunk of a unified diff, matching jsdiff `structuredPatch` hunks. */
|
|
2624
2793
|
export interface PatchHunk {
|
|
2625
2794
|
/** 1-based first line of the hunk in the old text. */
|
|
@@ -3733,8 +3902,8 @@ export declare function visibleWidth(text: string, tabWidth: number): number
|
|
|
3733
3902
|
* [`enclosing_block_boundaries`], [`block_range_at`] and [`node_chain_at`]
|
|
3734
3903
|
* are synchronous and parse on the JS thread when their source is not
|
|
3735
3904
|
* cached; awaiting this first makes that parse a cache hit. Resolves without
|
|
3736
|
-
* parsing when the language is unrecognized or the source is
|
|
3737
|
-
* the cache
|
|
3905
|
+
* parsing when the language is unrecognized or the source is over 2 MiB,
|
|
3906
|
+
* where a typical tree would not fit the cache's memory budget.
|
|
3738
3907
|
*/
|
|
3739
3908
|
export declare function warmBlockParse(options: BlockParseOptions): Promise<undefined>
|
|
3740
3909
|
|
|
@@ -3791,6 +3960,14 @@ export interface WorkProfile {
|
|
|
3791
3960
|
sampleCount: number
|
|
3792
3961
|
}
|
|
3793
3962
|
|
|
3963
|
+
/** A subdirectory the scan could not list. */
|
|
3964
|
+
export interface WorkspaceDirectoryError {
|
|
3965
|
+
/** Forward-slash path relative to the scan root. */
|
|
3966
|
+
path: string
|
|
3967
|
+
/** Platform error, e.g. `Permission denied (os error 13)`. */
|
|
3968
|
+
message: string
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3794
3971
|
/**
|
|
3795
3972
|
* Wrap text to a visible width, preserving ANSI escape codes across line
|
|
3796
3973
|
* breaks.
|
package/native/index.js
CHANGED
|
@@ -31,6 +31,8 @@ export const EditSession = nativeBindings.EditSession;
|
|
|
31
31
|
export const EditStore = nativeBindings.EditStore;
|
|
32
32
|
export const FileLock = nativeBindings.FileLock;
|
|
33
33
|
export const HighlightStream = nativeBindings.HighlightStream;
|
|
34
|
+
export const IpcConnection = nativeBindings.IpcConnection;
|
|
35
|
+
export const IpcServer = nativeBindings.IpcServer;
|
|
34
36
|
export const LiveWebRtcPeer = nativeBindings.LiveWebRtcPeer;
|
|
35
37
|
export const MacAppearanceObserver = nativeBindings.MacAppearanceObserver;
|
|
36
38
|
export const NativeOAuthCallback = nativeBindings.NativeOAuthCallback;
|
|
@@ -95,6 +97,7 @@ export const hasMatch = nativeBindings.hasMatch ?? missingNativeExport("hasMatch
|
|
|
95
97
|
export const highlightCode = nativeBindings.highlightCode ?? missingNativeExport("highlightCode");
|
|
96
98
|
export const htmlToMarkdown = nativeBindings.htmlToMarkdown ?? missingNativeExport("htmlToMarkdown");
|
|
97
99
|
export const invalidateFsScanCache = nativeBindings.invalidateFsScanCache ?? missingNativeExport("invalidateFsScanCache");
|
|
100
|
+
export const ipcEndpoint = nativeBindings.ipcEndpoint ?? missingNativeExport("ipcEndpoint");
|
|
98
101
|
export const isoBackend = nativeBindings.isoBackend ?? missingNativeExport("isoBackend");
|
|
99
102
|
export const isoDiff = nativeBindings.isoDiff ?? missingNativeExport("isoDiff");
|
|
100
103
|
export const isoIsUnavailableError = nativeBindings.isoIsUnavailableError ?? missingNativeExport("isoIsUnavailableError");
|
|
@@ -113,6 +116,7 @@ export const matchesLegacySequence = nativeBindings.matchesLegacySequence ?? mis
|
|
|
113
116
|
export const mmrRerankIndices = nativeBindings.mmrRerankIndices ?? missingNativeExport("mmrRerankIndices");
|
|
114
117
|
export const nodeChainAt = nativeBindings.nodeChainAt ?? missingNativeExport("nodeChainAt");
|
|
115
118
|
export const notebookToEditableText = nativeBindings.notebookToEditableText ?? missingNativeExport("notebookToEditableText");
|
|
119
|
+
export const parseCodeStatus = nativeBindings.parseCodeStatus ?? missingNativeExport("parseCodeStatus");
|
|
116
120
|
export const parseKey = nativeBindings.parseKey ?? missingNativeExport("parseKey");
|
|
117
121
|
export const parseKittySequence = nativeBindings.parseKittySequence ?? missingNativeExport("parseKittySequence");
|
|
118
122
|
export const pdfToMarkdown = nativeBindings.pdfToMarkdown ?? missingNativeExport("pdfToMarkdown");
|
package/native/loader-state.js
CHANGED
|
@@ -65,8 +65,8 @@ function getNativesDir() {
|
|
|
65
65
|
if (path.isAbsolute(dir)) return path.normalize(dir);
|
|
66
66
|
}
|
|
67
67
|
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
68
|
-
if (xdgDataHome && fs.existsSync(path.join(xdgDataHome, "
|
|
69
|
-
return path.join(xdgDataHome, "
|
|
68
|
+
if (xdgDataHome && fs.existsSync(path.join(xdgDataHome, "zeta"))) {
|
|
69
|
+
return path.join(xdgDataHome, "zeta", "natives");
|
|
70
70
|
}
|
|
71
71
|
return path.join(os.homedir(), ".zeta", "natives");
|
|
72
72
|
}
|
|
@@ -499,13 +499,11 @@ export function extractEmbeddedAddons({ files, targetDir }) {
|
|
|
499
499
|
return writtenPaths;
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
-
function maybeExtractEmbeddedAddon(ctx, errors) {
|
|
502
|
+
function maybeExtractEmbeddedAddon(ctx, errors, file = selectEmbeddedAddonFile(ctx.selectedVariant)) {
|
|
503
503
|
if (!ctx.isCompiledBinary || !embeddedAddon) return null;
|
|
504
504
|
if (embeddedAddon.platformTag !== ctx.platformTag || embeddedAddon.version !== ctx.packageVersion) return null;
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
if (!selectedEmbeddedFile) return null;
|
|
508
|
-
const targetPath = path.join(ctx.versionedDir, selectedEmbeddedFile.filename);
|
|
505
|
+
if (!file) return null;
|
|
506
|
+
const targetPath = path.join(ctx.versionedDir, file.filename);
|
|
509
507
|
|
|
510
508
|
startupMarker("native:extractEmbeddedAddon:start");
|
|
511
509
|
try {
|
|
@@ -517,11 +515,11 @@ function maybeExtractEmbeddedAddon(ctx, errors) {
|
|
|
517
515
|
}
|
|
518
516
|
|
|
519
517
|
try {
|
|
520
|
-
extractEmbeddedAddons({ files:
|
|
518
|
+
extractEmbeddedAddons({ files: [file], targetDir: ctx.versionedDir });
|
|
521
519
|
return targetPath;
|
|
522
520
|
} catch (err) {
|
|
523
521
|
const message = err instanceof Error ? err.message : String(err);
|
|
524
|
-
errors.push(`embedded addon extraction (${
|
|
522
|
+
errors.push(`embedded addon extraction (${file.filename}): ${message}`);
|
|
525
523
|
return null;
|
|
526
524
|
}
|
|
527
525
|
}
|
|
@@ -866,8 +864,16 @@ export function loadNative() {
|
|
|
866
864
|
const stagedCandidate = embeddedCandidate ? null : maybeStageNodeModulesAddon(ctx, errors);
|
|
867
865
|
const prepended = [embeddedCandidate, stagedCandidate].filter(c => typeof c === "string");
|
|
868
866
|
const runtimeCandidates = prepended.length > 0 ? [...prepended, ...ctx.candidates] : ctx.candidates;
|
|
867
|
+
const fallbackEmbeddedFile =
|
|
868
|
+
ctx.selectedVariant === "modern" && embeddedAddon
|
|
869
|
+
? embeddedAddon.files.find(file => file.variant === "baseline")
|
|
870
|
+
: null;
|
|
871
|
+
const fallbackEmbeddedPath = fallbackEmbeddedFile && path.join(ctx.versionedDir, fallbackEmbeddedFile.filename);
|
|
869
872
|
|
|
870
873
|
for (const candidate of runtimeCandidates) {
|
|
874
|
+
if (fallbackEmbeddedFile && candidate === fallbackEmbeddedPath) {
|
|
875
|
+
maybeExtractEmbeddedAddon(ctx, errors, fallbackEmbeddedFile);
|
|
876
|
+
}
|
|
871
877
|
try {
|
|
872
878
|
startupMarker(`native:require:${path.basename(candidate)}`);
|
|
873
879
|
const bindings = require_(candidate);
|
package/native/vcs.d.ts
CHANGED
|
@@ -81,6 +81,9 @@ export declare function detachGitDir(
|
|
|
81
81
|
signal?: AbortSignal,
|
|
82
82
|
): Promise<"no-git" | "independent" | "detached">;
|
|
83
83
|
|
|
84
|
+
/** Preserve isolation-local Git objects in the source without changing its refs or index. */
|
|
85
|
+
export declare function copyGitObjects(isolationRoot: string, sourceRoot: string): Promise<void>;
|
|
86
|
+
|
|
84
87
|
/** Run Git's checkout hook in a new linked worktree and return its output and exit status. */
|
|
85
88
|
export declare function runPostCheckoutHook(
|
|
86
89
|
worktreeRoot: string,
|
package/native/vcs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { $ } from "bun";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
3
4
|
import { loadNative } from "./loader-state.js";
|
|
4
5
|
|
|
5
6
|
let native;
|
|
@@ -96,6 +97,58 @@ export function detachGitDir(worktreeRoot, sourceCommonDir, signal) {
|
|
|
96
97
|
return api().vcsDetachGitDir(worktreeRoot, sourceCommonDir, signal);
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Publish `from` at `to` only once fully written: copy to a sibling temp name,
|
|
102
|
+
* then hard-link it into place (Git's own `finalize_object_file` strategy) so
|
|
103
|
+
* readers never see a partial file and an interrupted copy leaves only a
|
|
104
|
+
* `tmp_` file that `git gc` prunes. An existing `to` was itself published
|
|
105
|
+
* whole and holds the same content-addressed bytes, so it is kept.
|
|
106
|
+
*/
|
|
107
|
+
async function publishGitObjectFile(from, to) {
|
|
108
|
+
const tmp = path.join(path.dirname(to), `tmp_obj_omp_${crypto.randomUUID()}`);
|
|
109
|
+
try {
|
|
110
|
+
await fs.promises.copyFile(from, tmp, fs.constants.COPYFILE_EXCL);
|
|
111
|
+
try {
|
|
112
|
+
await fs.promises.link(tmp, to);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error?.code === "EEXIST") return;
|
|
115
|
+
// Filesystems without hard links: rename is still atomic, and any
|
|
116
|
+
// file it replaces holds identical content-addressed bytes.
|
|
117
|
+
if (error?.code !== "EPERM" && error?.code !== "ENOTSUP" && error?.code !== "EXDEV") throw error;
|
|
118
|
+
await fs.promises.rename(tmp, to);
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
await fs.promises.rm(tmp, { force: true });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Keep objects created in an isolated Git repository reachable after its
|
|
127
|
+
* worktree is removed, without changing the destination's refs or index.
|
|
128
|
+
* Every file is published atomically; a pack is published before its index.
|
|
129
|
+
*/
|
|
130
|
+
export async function copyGitObjects(isolationRoot, sourceRoot) {
|
|
131
|
+
const from = path.join(requireGit(isolationRoot).info().commonDir, "objects");
|
|
132
|
+
const to = path.join(requireGit(sourceRoot).info().commonDir, "objects");
|
|
133
|
+
for (const entry of await fs.promises.readdir(from, { withFileTypes: true })) {
|
|
134
|
+
if (!entry.isDirectory() || !/^[0-9a-f]{2}$/.test(entry.name)) continue;
|
|
135
|
+
await fs.promises.mkdir(path.join(to, entry.name), { recursive: true });
|
|
136
|
+
for (const name of await fs.promises.readdir(path.join(from, entry.name))) {
|
|
137
|
+
if (!/^[0-9a-f]{38,62}$/.test(name)) continue;
|
|
138
|
+
await publishGitObjectFile(path.join(from, entry.name, name), path.join(to, entry.name, name));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const packs = (await fs.promises.readdir(path.join(from, "pack"))).filter(name => name.startsWith("pack-"));
|
|
142
|
+
await fs.promises.mkdir(path.join(to, "pack"), { recursive: true });
|
|
143
|
+
// The index must only become visible after the pack it describes.
|
|
144
|
+
for (const name of packs.filter(name => name.endsWith(".pack"))) {
|
|
145
|
+
await publishGitObjectFile(path.join(from, "pack", name), path.join(to, "pack", name));
|
|
146
|
+
}
|
|
147
|
+
for (const name of packs.filter(name => /\.(?:idx|rev|keep|promisor|mtimes)$/.test(name))) {
|
|
148
|
+
await publishGitObjectFile(path.join(from, "pack", name), path.join(to, "pack", name));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
99
152
|
/**
|
|
100
153
|
* Run Git's checkout hook in a newly created linked worktree, using the same
|
|
101
154
|
* null old HEAD and branch-switch flag as `git worktree add`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxiraos/pi-natives",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Native Rust bindings for PDF conversion, audio, WebRTC, grep, clipboard, image processing, syntax highlighting, PTY, and shell operations via N-API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://linxira-os.github.io/zeta/",
|
|
@@ -32,18 +32,19 @@
|
|
|
32
32
|
"build": "bun ../../scripts/bazel-natives.ts host --dest native",
|
|
33
33
|
"build:bindings": "bun scripts/build-bindings.ts",
|
|
34
34
|
"check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
|
|
35
|
-
"check:types": "
|
|
35
|
+
"check:types": "tsc -p tsconfig.json --noEmit",
|
|
36
36
|
"lint": "oxlint .",
|
|
37
37
|
"test": "bun test --parallel",
|
|
38
38
|
"fix": "oxlint --fix --fix-suggestions . && bun run fmt",
|
|
39
39
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'",
|
|
40
40
|
"gen:npm": "bun scripts/gen-npm-packages.ts",
|
|
41
41
|
"bench": "bun bench/grep.ts",
|
|
42
|
-
"bench:text": "bun bench/text.ts"
|
|
42
|
+
"bench:text": "bun bench/text.ts",
|
|
43
|
+
"bench:ipc": "bun bench/ipc.ts"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@napi-rs/cli": "3.7.2",
|
|
46
|
-
"@types/bun": "
|
|
47
|
+
"@types/bun": "1.4.3",
|
|
47
48
|
"cli-truncate": "6.1.1",
|
|
48
49
|
"diff": "9.0.0",
|
|
49
50
|
"gpt-tokenizer": "4.0.0",
|
|
@@ -109,11 +110,11 @@
|
|
|
109
110
|
}
|
|
110
111
|
},
|
|
111
112
|
"optionalDependencies": {
|
|
112
|
-
"@linxiraos/pi-natives-linux-x64": "1.
|
|
113
|
-
"@linxiraos/pi-natives-linux-arm64": "1.
|
|
114
|
-
"@linxiraos/pi-natives-darwin-x64": "1.
|
|
115
|
-
"@linxiraos/pi-natives-darwin-arm64": "1.
|
|
116
|
-
"@linxiraos/pi-natives-win32-x64": "1.
|
|
117
|
-
"@linxiraos/pi-natives-win32-arm64": "1.
|
|
113
|
+
"@linxiraos/pi-natives-linux-x64": "1.3.0",
|
|
114
|
+
"@linxiraos/pi-natives-linux-arm64": "1.3.0",
|
|
115
|
+
"@linxiraos/pi-natives-darwin-x64": "1.3.0",
|
|
116
|
+
"@linxiraos/pi-natives-darwin-arm64": "1.3.0",
|
|
117
|
+
"@linxiraos/pi-natives-win32-x64": "1.3.0",
|
|
118
|
+
"@linxiraos/pi-natives-win32-arm64": "1.3.0"
|
|
118
119
|
}
|
|
119
120
|
}
|