@linxiraos/pi-natives 1.1.26 → 1.1.27
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 +9 -3
- package/native/desktop-adapter.d.ts +3 -27
- package/native/desktop-adapter.js +32 -400
- package/native/index.d.ts +171 -12
- package/native/index.js +3 -0
- package/native/vcs.d.ts +6 -0
- package/native/vcs.js +14 -0
- package/package.json +7 -7
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
|
+
- **PDF**: In-memory PDF-to-Markdown extraction with OCR-page classification via `pdf-inspector`
|
|
13
14
|
|
|
14
15
|
General-purpose image processing (decode/resize/encode for files and buffers)
|
|
15
16
|
lives in [`Bun.Image`](https://bun.com/docs/runtime/image) on the JS side; this
|
|
@@ -19,7 +20,7 @@ that terminal protocol.
|
|
|
19
20
|
## Usage
|
|
20
21
|
|
|
21
22
|
```typescript
|
|
22
|
-
import {
|
|
23
|
+
import { encodeSixelAsync, grep, pdfToMarkdown } from "@linxiraos/pi-natives";
|
|
23
24
|
|
|
24
25
|
// Grep for a pattern
|
|
25
26
|
const results = await grep({
|
|
@@ -36,8 +37,13 @@ const files = await find({
|
|
|
36
37
|
fileType: "file",
|
|
37
38
|
});
|
|
38
39
|
|
|
39
|
-
// SIXEL encode for a terminal cell box (px)
|
|
40
|
-
|
|
40
|
+
// SIXEL encode for a terminal cell box (px), off the JS thread
|
|
41
|
+
// (`encodeSixel` is the synchronous form)
|
|
42
|
+
const sequence = await encodeSixelAsync(pngBytes, widthPx, heightPx);
|
|
43
|
+
|
|
44
|
+
// Extract PDF text and identify pages that still need OCR
|
|
45
|
+
const pdf = await pdfToMarkdown(pdfBytes);
|
|
46
|
+
console.log(pdf.markdown, pdf.pagesNeedingOcr);
|
|
41
47
|
```
|
|
42
48
|
|
|
43
49
|
### Lazy Windows path helpers
|
|
@@ -1,28 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
readonly [key: string]: unknown;
|
|
3
|
-
readonly ax: boolean;
|
|
4
|
-
readonly backgroundWindowInput: boolean;
|
|
5
|
-
readonly takeover: boolean;
|
|
6
|
-
readonly axPermission: string;
|
|
7
|
-
}
|
|
1
|
+
import type { DesktopSession } from "./index.js";
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
listWindows(): Promise<Array<Record<string, unknown>>>;
|
|
12
|
-
capture(target: string, caps?: unknown): Promise<Record<string, unknown>>;
|
|
13
|
-
click(
|
|
14
|
-
target: string,
|
|
15
|
-
x: number,
|
|
16
|
-
y: number,
|
|
17
|
-
options?: { button?: string; count?: number; modifiers?: string[]; takeover?: boolean },
|
|
18
|
-
): Promise<void>;
|
|
19
|
-
typeText(target: string, text: string, options?: { takeover?: boolean }): Promise<void>;
|
|
20
|
-
keyChord(target: string, keys: string[], options?: { takeover?: boolean }): Promise<void>;
|
|
21
|
-
close(): Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface AdaptedDesktopSessionConstructor {
|
|
25
|
-
new (options: Record<string, unknown>): AdaptedDesktopSession;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function adaptDesktopSession(NativeDesktopSession: unknown): AdaptedDesktopSessionConstructor;
|
|
3
|
+
/** Return the current native ABI, or a constructor that rejects an outdated addon on first desktop use. */
|
|
4
|
+
export function adaptDesktopSession(NativeDesktopSession: unknown): typeof DesktopSession;
|
|
@@ -1,404 +1,36 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function normalizeError(error, fallbackCode) {
|
|
22
|
-
if (!(error instanceof Error)) return desktopError(fallbackCode, String(error));
|
|
23
|
-
if (/^[A-Z][A-Za-z]+: /.test(error.message)) return error;
|
|
24
|
-
|
|
25
|
-
const match = /^(DESKTOP_[A-Z_]+):\s*(.*)$/.exec(error.message);
|
|
26
|
-
if (match === null) return desktopError(fallbackCode, error.message);
|
|
27
|
-
const code = LEGACY_ERROR_CODES[match[1]] ?? fallbackCode;
|
|
28
|
-
return desktopError(code, match[2]);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function normalizeCapabilities(capabilities) {
|
|
32
|
-
return {
|
|
33
|
-
...capabilities,
|
|
34
|
-
ax: false,
|
|
35
|
-
backgroundWindowInput: false,
|
|
36
|
-
takeover: true,
|
|
37
|
-
axPermission: "unavailable",
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function legacyPoint(point) {
|
|
42
|
-
return { x: Math.round(point.x), y: Math.round(point.y) };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function captureCapsKey(caps) {
|
|
46
|
-
return `${caps?.maxWidth ?? ""}:${caps?.maxHeight ?? ""}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function legacyButton(button) {
|
|
50
|
-
return button === "middle" ? "wheel" : button;
|
|
51
|
-
}
|
|
52
|
-
function sourceDimensions(capture) {
|
|
53
|
-
if (capture.sourceWidth !== undefined && capture.sourceHeight !== undefined) {
|
|
54
|
-
return { sourceWidth: capture.sourceWidth, sourceHeight: capture.sourceHeight };
|
|
55
|
-
}
|
|
56
|
-
const displays = Array.isArray(capture.displays) ? capture.displays : [];
|
|
57
|
-
if (displays.length === 0) {
|
|
58
|
-
return { sourceWidth: capture.width, sourceHeight: capture.height };
|
|
59
|
-
}
|
|
60
|
-
const minX = Math.min(...displays.map(display => display.x));
|
|
61
|
-
const minY = Math.min(...displays.map(display => display.y));
|
|
62
|
-
const maxX = Math.max(...displays.map(display => display.x + display.width));
|
|
63
|
-
const maxY = Math.max(...displays.map(display => display.y + display.height));
|
|
64
|
-
const nativeScale = Math.max(1, ...displays.map(display => display.scale ?? 1));
|
|
65
|
-
return {
|
|
66
|
-
sourceWidth: Math.max(1, Math.round((maxX - minX) * nativeScale)),
|
|
67
|
-
sourceHeight: Math.max(1, Math.round((maxY - minY) * nativeScale)),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function displaysEqual(a, b) {
|
|
72
|
-
if (a === b) return true;
|
|
73
|
-
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
74
|
-
for (let i = 0; i < a.length; i++) {
|
|
75
|
-
const x = a[i];
|
|
76
|
-
const y = b[i];
|
|
77
|
-
if (
|
|
78
|
-
x === y ||
|
|
79
|
-
(x?.id === y?.id &&
|
|
80
|
-
x?.x === y?.x &&
|
|
81
|
-
x?.y === y?.y &&
|
|
82
|
-
x?.width === y?.width &&
|
|
83
|
-
x?.height === y?.height &&
|
|
84
|
-
x?.scale === y?.scale &&
|
|
85
|
-
x?.pixelX === y?.pixelX &&
|
|
86
|
-
x?.pixelY === y?.pixelY &&
|
|
87
|
-
x?.pixelWidth === y?.pixelWidth &&
|
|
88
|
-
x?.pixelHeight === y?.pixelHeight)
|
|
89
|
-
) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Field-by-field frame comparison: the JSON.stringify signature allocated a
|
|
98
|
-
// full string over the display list on every capture and every input action.
|
|
99
|
-
// A rolling hash would risk collisions where string equality had none, so
|
|
100
|
-
// compare the same fields directly instead.
|
|
101
|
-
function sameFrame(a, b) {
|
|
102
|
-
if (a === b) return true;
|
|
103
|
-
if (!a || !b) return false;
|
|
104
|
-
return a.target === b.target && a.width === b.width && a.height === b.height && displaysEqual(a.displays, b.displays);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Lightweight projection of the fields sameFrame reads: the stored capture
|
|
108
|
-
// must NOT retain the full screenshot (`data: Uint8Array`, multi-MB per
|
|
109
|
-
// target) after callers finish with it.
|
|
110
|
-
function frameIdentity(capture) {
|
|
111
|
-
const displays = Array.isArray(capture.displays) ? capture.displays.map(displayOf) : capture.displays;
|
|
112
|
-
return { target: capture.target, width: capture.width, height: capture.height, displays };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function displayOf(display) {
|
|
116
|
-
return {
|
|
117
|
-
id: display?.id,
|
|
118
|
-
x: display?.x,
|
|
119
|
-
y: display?.y,
|
|
120
|
-
width: display?.width,
|
|
121
|
-
height: display?.height,
|
|
122
|
-
scale: display?.scale,
|
|
123
|
-
pixelX: display?.pixelX,
|
|
124
|
-
pixelY: display?.pixelY,
|
|
125
|
-
pixelWidth: display?.pixelWidth,
|
|
126
|
-
pixelHeight: display?.pixelHeight,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function frameSignature(capture) {
|
|
131
|
-
return JSON.stringify({
|
|
132
|
-
target: capture.target,
|
|
133
|
-
width: capture.width,
|
|
134
|
-
height: capture.height,
|
|
135
|
-
displays: capture.displays?.map(display => ({
|
|
136
|
-
id: display.id,
|
|
137
|
-
x: display.x,
|
|
138
|
-
y: display.y,
|
|
139
|
-
width: display.width,
|
|
140
|
-
height: display.height,
|
|
141
|
-
scale: display.scale,
|
|
142
|
-
pixelX: display.pixelX,
|
|
143
|
-
pixelY: display.pixelY,
|
|
144
|
-
pixelWidth: display.pixelWidth,
|
|
145
|
-
pixelHeight: display.pixelHeight,
|
|
146
|
-
})),
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Adapt the pre-parity desktop addon ABI used by pull-request CI artifacts to
|
|
152
|
-
* the current session contract. Released addons exposed capture/execute/close;
|
|
153
|
-
* current addons already expose the complete API and pass through unchanged.
|
|
154
|
-
*/
|
|
1
|
+
const REQUIRED_METHODS = [
|
|
2
|
+
"click",
|
|
3
|
+
"capture",
|
|
4
|
+
"captureRegion",
|
|
5
|
+
"cancel",
|
|
6
|
+
"retire",
|
|
7
|
+
"observe",
|
|
8
|
+
"listApplications",
|
|
9
|
+
"openApplication",
|
|
10
|
+
"menuItems",
|
|
11
|
+
"menuSelect",
|
|
12
|
+
"holdKeys",
|
|
13
|
+
"holdMouse",
|
|
14
|
+
"acquireControl",
|
|
15
|
+
"releaseControl",
|
|
16
|
+
"controlState",
|
|
17
|
+
"bringToCurrentSpace",
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/** Require the current desktop ABI; partial legacy emulation cannot preserve input ownership. */
|
|
155
21
|
export function adaptDesktopSession(NativeDesktopSession) {
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
constructor(options) {
|
|
169
|
-
try {
|
|
170
|
-
this.#nativeDesktopSession = NativeDesktopSession;
|
|
171
|
-
this.#options = options;
|
|
172
|
-
this.#native = new NativeDesktopSession(options);
|
|
173
|
-
this.#sessions = new Map([[captureCapsKey(), this.#native]]);
|
|
174
|
-
} catch (error) {
|
|
175
|
-
throw normalizeError(error, "Internal");
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
get capabilities() {
|
|
180
|
-
return normalizeCapabilities(this.#native.capabilities);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
#ensureOpen() {
|
|
184
|
-
if (this.#closed) throw desktopError("Closed", "desktop session is closed");
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
#nativeForCapturedTarget(target) {
|
|
188
|
-
const capture = this.#capturedTargets.get(target);
|
|
189
|
-
if (capture) return capture.native;
|
|
190
|
-
throw desktopError(
|
|
191
|
-
"InvalidCoordinateFrame",
|
|
192
|
-
`no capture of '${target}' yet — take a screenshot of this target first`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
#sessionForCapture(caps) {
|
|
197
|
-
const key = captureCapsKey(caps);
|
|
198
|
-
const existing = this.#sessions.get(key);
|
|
199
|
-
if (existing) return existing;
|
|
200
|
-
try {
|
|
201
|
-
const native = new this.#nativeDesktopSession({
|
|
202
|
-
...this.#options,
|
|
203
|
-
maxWidth: caps?.maxWidth,
|
|
204
|
-
maxHeight: caps?.maxHeight,
|
|
205
|
-
});
|
|
206
|
-
this.#sessions.set(key, native);
|
|
207
|
-
return native;
|
|
208
|
-
} catch (error) {
|
|
209
|
-
throw normalizeError(error, "CaptureFailed");
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
#ensureForeground(target, options) {
|
|
214
|
-
if (target !== "desktop" && options?.takeover !== true) {
|
|
215
|
-
throw desktopError(
|
|
216
|
-
"BackgroundUnavailable",
|
|
217
|
-
"the installed native addon supports foreground input only",
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async #execute(actions, target, native = this.#native, fallbackCode = "InputFailed") {
|
|
223
|
-
try {
|
|
224
|
-
const capture = await native.execute(Array.isArray(actions) ? actions : [actions], target);
|
|
225
|
-
const previous = this.#capturedTargets.get(target);
|
|
226
|
-
if (capture && previous?.native === native && !sameFrame(capture, previous.capture)) {
|
|
227
|
-
this.#capturedTargets.delete(target);
|
|
228
|
-
}
|
|
229
|
-
} catch (error) {
|
|
230
|
-
throw normalizeError(error, fallbackCode);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async listDisplays() {
|
|
235
|
-
this.#ensureOpen();
|
|
236
|
-
throw desktopError("CaptureFailed", "the installed native addon does not expose display enumeration");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async listWindows() {
|
|
240
|
-
this.#ensureOpen();
|
|
241
|
-
if (typeof this.#native.listWindows !== "function") {
|
|
242
|
-
throw desktopError("CaptureFailed", "the installed native addon does not expose window enumeration");
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
return await this.#native.listWindows();
|
|
246
|
-
} catch (error) {
|
|
247
|
-
throw normalizeError(error, "CaptureFailed");
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
async capture(target, caps) {
|
|
252
|
-
this.#ensureOpen();
|
|
253
|
-
if (target !== "desktop" && !/^\d+$/.test(target)) {
|
|
254
|
-
throw desktopError("InvalidTarget", `invalid window target '${target}'`);
|
|
255
|
-
}
|
|
256
|
-
if (target !== "desktop" && typeof this.#native.listWindows !== "function") {
|
|
257
|
-
throw desktopError("CaptureFailed", "the installed native addon does not support window capture");
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
const native = this.#sessionForCapture(caps);
|
|
261
|
-
const capture = await native.capture(target);
|
|
262
|
-
const adapted = { ...capture, ...sourceDimensions(capture), target };
|
|
263
|
-
// Store only the comparison fields, not the screenshot bytes:
|
|
264
|
-
// `adapted` carries the full PNG `data` (multi-MB per target).
|
|
265
|
-
// Keep the string signature for any external reader of the
|
|
266
|
-
// stored shape.
|
|
267
|
-
this.#capturedTargets.set(target, { native, capture: frameIdentity(adapted), signature: frameSignature(adapted) });
|
|
268
|
-
return adapted;
|
|
269
|
-
} catch (error) {
|
|
270
|
-
throw normalizeError(error, "CaptureFailed");
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async click(target, x, y, options) {
|
|
275
|
-
this.#ensureOpen();
|
|
276
|
-
this.#ensureForeground(target, options);
|
|
277
|
-
const native = this.#nativeForCapturedTarget(target);
|
|
278
|
-
const count = Math.max(1, options?.count ?? 1);
|
|
279
|
-
const button = legacyButton(options?.button ?? "left");
|
|
280
|
-
const point = { x: Math.round(x), y: Math.round(y), keys: options?.modifiers ?? [] };
|
|
281
|
-
const actions =
|
|
282
|
-
count === 2 && button === "left"
|
|
283
|
-
? [{ type: "double_click", ...point }]
|
|
284
|
-
: Array.from({ length: count }, () => ({ type: "click", ...point, button }));
|
|
285
|
-
await this.#execute(actions, target, native);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async moveMouse(target, x, y, options) {
|
|
289
|
-
this.#ensureOpen();
|
|
290
|
-
this.#ensureForeground(target, options);
|
|
291
|
-
await this.#execute(
|
|
292
|
-
{ type: "move", x: Math.round(x), y: Math.round(y), keys: options?.modifiers ?? [] },
|
|
293
|
-
target,
|
|
294
|
-
this.#nativeForCapturedTarget(target),
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
async drag(target, path, options) {
|
|
299
|
-
this.#ensureOpen();
|
|
300
|
-
this.#ensureForeground(target, options);
|
|
301
|
-
await this.#execute(
|
|
302
|
-
{ type: "drag", path: path.map(legacyPoint), keys: options?.modifiers ?? [] },
|
|
303
|
-
target,
|
|
304
|
-
this.#nativeForCapturedTarget(target),
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async scroll(target, x, y, dx, dy, options) {
|
|
309
|
-
this.#ensureOpen();
|
|
310
|
-
this.#ensureForeground(target, options);
|
|
311
|
-
await this.#execute(
|
|
312
|
-
{
|
|
313
|
-
type: "scroll",
|
|
314
|
-
x: Math.round(x),
|
|
315
|
-
y: Math.round(y),
|
|
316
|
-
scroll_x: Math.round(dx),
|
|
317
|
-
scroll_y: Math.round(dy),
|
|
318
|
-
keys: options?.modifiers ?? [],
|
|
319
|
-
},
|
|
320
|
-
target,
|
|
321
|
-
this.#nativeForCapturedTarget(target),
|
|
22
|
+
const prototype = NativeDesktopSession?.prototype;
|
|
23
|
+
const missing = REQUIRED_METHODS.filter(method => typeof prototype?.[method] !== "function");
|
|
24
|
+
if (missing.length === 0) {
|
|
25
|
+
return NativeDesktopSession;
|
|
26
|
+
}
|
|
27
|
+
// The shared native entrypoint is also imported by non-desktop tools. Keep
|
|
28
|
+
// their imports usable, but never emulate the new ABI with unsafe fallbacks.
|
|
29
|
+
return class UnsupportedDesktopSession {
|
|
30
|
+
constructor() {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Unsupported: desktop native addon is outdated (missing ${missing.join(", ")}). Update the native addon or run bun run build:native from the repository root.`,
|
|
322
33
|
);
|
|
323
34
|
}
|
|
324
|
-
|
|
325
|
-
async typeText(target, text, options) {
|
|
326
|
-
this.#ensureOpen();
|
|
327
|
-
this.#ensureForeground(target, options);
|
|
328
|
-
await this.#execute({ type: "type", text }, target, this.#capturedTargets.get(target)?.native ?? this.#native);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
async keyChord(target, keys, options) {
|
|
332
|
-
this.#ensureOpen();
|
|
333
|
-
this.#ensureForeground(target, options);
|
|
334
|
-
await this.#execute({ type: "keypress", keys }, target, this.#capturedTargets.get(target)?.native ?? this.#native);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async raiseWindow() {
|
|
338
|
-
this.#ensureOpen();
|
|
339
|
-
throw desktopError("BackgroundUnavailable", "the installed native addon does not support window control");
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
async axSnapshot() {
|
|
343
|
-
this.#ensureOpen();
|
|
344
|
-
throw desktopError("AxUnsupported", "accessibility is unavailable in the installed native addon");
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
async axQuery() {
|
|
348
|
-
return this.axSnapshot();
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
async axElementAt() {
|
|
352
|
-
return this.axSnapshot();
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
async axFocused() {
|
|
356
|
-
return this.axSnapshot();
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
async axNode() {
|
|
360
|
-
return this.axSnapshot();
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
async axAttributes() {
|
|
364
|
-
return this.axSnapshot();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
async axChildren() {
|
|
368
|
-
return this.axSnapshot();
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
async axParent() {
|
|
372
|
-
return this.axSnapshot();
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async axPerform() {
|
|
376
|
-
return this.axSnapshot();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
async axSetValue() {
|
|
380
|
-
return this.axSnapshot();
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
async axFocus() {
|
|
384
|
-
return this.axSnapshot();
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
async axClick() {
|
|
388
|
-
return this.axSnapshot();
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
async close() {
|
|
392
|
-
if (this.#closed) return;
|
|
393
|
-
this.#closed = true;
|
|
394
|
-
try {
|
|
395
|
-
await Promise.all([...this.#sessions.values()].map(native => native.close()));
|
|
396
|
-
} catch (error) {
|
|
397
|
-
throw normalizeError(error, "Internal");
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
ADAPTED_SESSION_CLASSES.set(NativeDesktopSession, DesktopSession);
|
|
403
|
-
return DesktopSession;
|
|
35
|
+
};
|
|
404
36
|
}
|
package/native/index.d.ts
CHANGED
|
@@ -31,10 +31,42 @@ export declare class AudioPlayback {
|
|
|
31
31
|
/** Persistent, serialized native desktop capture/input/accessibility session. */
|
|
32
32
|
export declare class DesktopSession {
|
|
33
33
|
constructor(options?: DesktopSessionOptions | undefined | null)
|
|
34
|
+
/**
|
|
35
|
+
* Asks the worker when it is idle, so permissions are read live. While
|
|
36
|
+
* another operation holds the worker, answers from the snapshot of the
|
|
37
|
+
* latest capabilities read or capture instead of blocking the JS thread
|
|
38
|
+
* behind it.
|
|
39
|
+
*/
|
|
34
40
|
get capabilities(): DesktopCapabilities
|
|
35
41
|
listDisplays(): Promise<Array<DesktopDisplay>>
|
|
36
42
|
listWindows(): Promise<Array<DesktopWindow>>
|
|
43
|
+
listApplications(options?: ApplicationQuery | undefined | null): Promise<Array<Application>>
|
|
44
|
+
openApplication(id: string, options?: ApplicationOpenOptions | undefined | null): Promise<Application>
|
|
45
|
+
/**
|
|
46
|
+
* Capture and accessibility share one serialized request. Neither a failed
|
|
47
|
+
* snapshot nor an abandoned reply replaces the last delivered input frame.
|
|
48
|
+
*/
|
|
49
|
+
observe(target: string, caps?: CaptureCaps | undefined | null, axOptions?: AxSnapshotOptions | undefined | null): Promise<DesktopObservation>
|
|
50
|
+
menuItems(target: string, path?: Array<string> | undefined | null): Promise<Array<DesktopMenuItem>>
|
|
51
|
+
menuSelect(target: string, path: Array<string>): Promise<undefined>
|
|
52
|
+
bringToCurrentSpace(windowId: string): Promise<undefined>
|
|
53
|
+
holdKeys(target: string, keys: Array<string>, options: HoldOptions): Promise<undefined>
|
|
54
|
+
holdMouse(target: string, x: number, y: number, options: HoldOptions): Promise<undefined>
|
|
55
|
+
/**
|
|
56
|
+
* Native ownership only. Human approval is required by the host before
|
|
57
|
+
* calling this method.
|
|
58
|
+
*/
|
|
59
|
+
acquireControl(): Promise<DesktopControlState>
|
|
60
|
+
releaseControl(): void
|
|
61
|
+
controlState(): DesktopControlState
|
|
62
|
+
/** Retire queued work from a completed helper without revoking task control. */
|
|
63
|
+
retire(): void
|
|
37
64
|
capture(target: string, caps?: CaptureCaps | undefined | null): Promise<DesktopCapture>
|
|
65
|
+
/**
|
|
66
|
+
* Capture a fresh native-detail region without replacing the full input
|
|
67
|
+
* coordinate frame.
|
|
68
|
+
*/
|
|
69
|
+
captureRegion(target: string, region: CaptureRegion, caps?: CaptureCaps | undefined | null): Promise<DesktopCapture>
|
|
38
70
|
click(target: string, x: number, y: number, opts?: PointerOptions | undefined | null): Promise<undefined>
|
|
39
71
|
moveMouse(target: string, x: number, y: number, opts?: PointerOptions | undefined | null): Promise<undefined>
|
|
40
72
|
drag(target: string, path: Array<DesktopPoint>, opts?: PointerOptions | undefined | null): Promise<undefined>
|
|
@@ -58,6 +90,11 @@ export declare class DesktopSession {
|
|
|
58
90
|
axSetValue(reference: string, value: string): Promise<undefined>
|
|
59
91
|
axFocus(reference: string): Promise<undefined>
|
|
60
92
|
axClick(reference: string, opts?: PointerOptions | undefined | null): Promise<undefined>
|
|
93
|
+
/**
|
|
94
|
+
* Immediately cancel operations submitted before this call. Later
|
|
95
|
+
* operations may proceed.
|
|
96
|
+
*/
|
|
97
|
+
cancel(): void
|
|
61
98
|
close(): Promise<undefined>
|
|
62
99
|
}
|
|
63
100
|
|
|
@@ -722,6 +759,29 @@ export declare function appleFmCancel(handle: number): void
|
|
|
722
759
|
*/
|
|
723
760
|
export declare function appleFmGenerate(request: string, onEvent: (err: null | Error, event: string) => void): number
|
|
724
761
|
|
|
762
|
+
/**
|
|
763
|
+
* Installed application identity with currently observable process
|
|
764
|
+
* information.
|
|
765
|
+
*/
|
|
766
|
+
export interface Application {
|
|
767
|
+
id: string
|
|
768
|
+
name: string
|
|
769
|
+
path: string
|
|
770
|
+
running: boolean
|
|
771
|
+
pid?: number
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** Controls whether launching an application deliberately activates it. */
|
|
775
|
+
export interface ApplicationOpenOptions {
|
|
776
|
+
activate?: boolean
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Filters the native application inventory without requiring screen access. */
|
|
780
|
+
export interface ApplicationQuery {
|
|
781
|
+
query?: string
|
|
782
|
+
runningOnly?: boolean
|
|
783
|
+
}
|
|
784
|
+
|
|
725
785
|
/**
|
|
726
786
|
* Apply ast-grep rewrite rules to matching files; honors `dryRun` and returns
|
|
727
787
|
* a promise.
|
|
@@ -1013,6 +1073,15 @@ export interface AxSnapshotOptions {
|
|
|
1013
1073
|
all?: boolean
|
|
1014
1074
|
}
|
|
1015
1075
|
|
|
1076
|
+
export interface BlockParseOptions {
|
|
1077
|
+
/** Source code to parse. */
|
|
1078
|
+
code: string
|
|
1079
|
+
/** Language alias (e.g. "rust", "typescript") used before path inference. */
|
|
1080
|
+
lang?: string
|
|
1081
|
+
/** File path used to infer language by extension when `lang` is omitted. */
|
|
1082
|
+
path?: string
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1016
1085
|
export interface BlockRange {
|
|
1017
1086
|
/** 1-indexed inclusive first line of the resolved block. */
|
|
1018
1087
|
startLine: number
|
|
@@ -1045,6 +1114,14 @@ export interface CaptureCaps {
|
|
|
1045
1114
|
maxHeight?: number
|
|
1046
1115
|
}
|
|
1047
1116
|
|
|
1117
|
+
/** Rectangle in pixels of the most recent full screenshot of the same target. */
|
|
1118
|
+
export interface CaptureRegion {
|
|
1119
|
+
x: number
|
|
1120
|
+
y: number
|
|
1121
|
+
width: number
|
|
1122
|
+
height: number
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1048
1125
|
/** Clipboard image payload encoded as PNG bytes. */
|
|
1049
1126
|
export interface ClipboardImage {
|
|
1050
1127
|
/** PNG-encoded image bytes. */
|
|
@@ -1100,7 +1177,7 @@ export declare function cosineSimilarityPairs(vectors: Float64Array, count: numb
|
|
|
1100
1177
|
export declare function countTokens(input: string | string[], encoding?: Encoding | undefined | null): number
|
|
1101
1178
|
|
|
1102
1179
|
/**
|
|
1103
|
-
* Decode one complete SIXEL control string into a PNG.
|
|
1180
|
+
* Decode one complete SIXEL control string into a PNG on the calling thread.
|
|
1104
1181
|
*
|
|
1105
1182
|
* The decoder is deliberately bounded before handing the stream to
|
|
1106
1183
|
* `icy_sixel`: raster declarations, repeats, and row advances are scanned
|
|
@@ -1109,6 +1186,12 @@ export declare function countTokens(input: string | string[], encoding?: Encodin
|
|
|
1109
1186
|
*/
|
|
1110
1187
|
export declare function decodeSixelToPng(bytes: Uint8Array): Uint8Array
|
|
1111
1188
|
|
|
1189
|
+
/**
|
|
1190
|
+
* Same result as [`decode_sixel_to_png`], but the decode runs on the native
|
|
1191
|
+
* blocking pool instead of the JS thread.
|
|
1192
|
+
*/
|
|
1193
|
+
export declare function decodeSixelToPngAsync(bytes: Uint8Array): Promise<Uint8Array>
|
|
1194
|
+
|
|
1112
1195
|
export interface DesktopCapabilities {
|
|
1113
1196
|
backend: string
|
|
1114
1197
|
displayServer?: string
|
|
@@ -1121,6 +1204,15 @@ export interface DesktopCapabilities {
|
|
|
1121
1204
|
* target and post real input).
|
|
1122
1205
|
*/
|
|
1123
1206
|
takeover: boolean
|
|
1207
|
+
applications: boolean
|
|
1208
|
+
menus: boolean
|
|
1209
|
+
heldInput: boolean
|
|
1210
|
+
spaces: boolean
|
|
1211
|
+
/**
|
|
1212
|
+
* Native global Escape cancellation while input/control ownership is held.
|
|
1213
|
+
* Wayland requires the host interrupt action instead.
|
|
1214
|
+
*/
|
|
1215
|
+
globalEscape: boolean
|
|
1124
1216
|
capturePermission: string
|
|
1125
1217
|
inputPermission: string
|
|
1126
1218
|
axPermission: string
|
|
@@ -1138,12 +1230,24 @@ export interface DesktopCapture {
|
|
|
1138
1230
|
* unscaled.
|
|
1139
1231
|
*/
|
|
1140
1232
|
sourceHeight: number
|
|
1233
|
+
/** Dimensions of the full screenshot coordinate frame used by pointer input. */
|
|
1234
|
+
coordinateWidth: number
|
|
1235
|
+
coordinateHeight: number
|
|
1236
|
+
/**
|
|
1237
|
+
* Region in the full screenshot's coordinates; zoom pixels are not input
|
|
1238
|
+
* coordinates.
|
|
1239
|
+
*/
|
|
1240
|
+
region?: CaptureRegion
|
|
1141
1241
|
target: string
|
|
1142
1242
|
displays: Array<DesktopDisplay>
|
|
1143
1243
|
backend: string
|
|
1144
1244
|
displayServer?: string
|
|
1145
1245
|
}
|
|
1146
1246
|
|
|
1247
|
+
export interface DesktopControlState {
|
|
1248
|
+
active: boolean
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1147
1251
|
/**
|
|
1148
1252
|
* Monitor geometry in both global logical desktop coordinates and composite
|
|
1149
1253
|
* screenshot pixels.
|
|
@@ -1163,6 +1267,24 @@ export interface DesktopDisplay {
|
|
|
1163
1267
|
isPrimary: boolean
|
|
1164
1268
|
}
|
|
1165
1269
|
|
|
1270
|
+
/**
|
|
1271
|
+
* One immediate child of a native application menu. Paths contain the actual
|
|
1272
|
+
* native labels, including ellipses; selection accepts normalized labels.
|
|
1273
|
+
*/
|
|
1274
|
+
export interface DesktopMenuItem {
|
|
1275
|
+
title: string
|
|
1276
|
+
path: Array<string>
|
|
1277
|
+
enabled: boolean
|
|
1278
|
+
checked: boolean
|
|
1279
|
+
hasSubmenu: boolean
|
|
1280
|
+
shortcut?: string
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
export interface DesktopObservation {
|
|
1284
|
+
capture: DesktopCapture
|
|
1285
|
+
accessibility: AxSnapshot
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1166
1288
|
export interface DesktopPoint {
|
|
1167
1289
|
x: number
|
|
1168
1290
|
y: number
|
|
@@ -1520,16 +1642,27 @@ export interface EnclosingBoundaryOptions {
|
|
|
1520
1642
|
}
|
|
1521
1643
|
|
|
1522
1644
|
/**
|
|
1523
|
-
* Encode image bytes into a SIXEL escape sequence for terminal rendering
|
|
1645
|
+
* Encode image bytes into a SIXEL escape sequence for terminal rendering, on
|
|
1646
|
+
* the calling thread.
|
|
1524
1647
|
*
|
|
1525
1648
|
* The input image is decoded and resized to the requested pixel dimensions
|
|
1526
|
-
* before encoding.
|
|
1649
|
+
* before encoding. Prefer [`encode_sixel_async`] unless the caller cannot
|
|
1650
|
+
* wait: the decode, resize and dither block the JS thread.
|
|
1527
1651
|
*
|
|
1528
1652
|
* # Errors
|
|
1529
1653
|
* Returns an error if decoding, resizing, or SIXEL encoding fails.
|
|
1530
1654
|
*/
|
|
1531
1655
|
export declare function encodeSixel(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number): string
|
|
1532
1656
|
|
|
1657
|
+
/**
|
|
1658
|
+
* Same result as [`encode_sixel`], but the decode, resize and dither run on
|
|
1659
|
+
* the native blocking pool, so they never stall the JavaScript event loop.
|
|
1660
|
+
*
|
|
1661
|
+
* # Errors
|
|
1662
|
+
* Rejects if decoding, resizing, or SIXEL encoding fails.
|
|
1663
|
+
*/
|
|
1664
|
+
export declare function encodeSixelAsync(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number): Promise<string>
|
|
1665
|
+
|
|
1533
1666
|
/** Tokenizer encoding to use. */
|
|
1534
1667
|
export declare enum Encoding {
|
|
1535
1668
|
/** GPT-4o / o1 / GPT-5 (default). */
|
|
@@ -1957,6 +2090,14 @@ export interface HighlightColors {
|
|
|
1957
2090
|
deleted?: string
|
|
1958
2091
|
}
|
|
1959
2092
|
|
|
2093
|
+
export interface HoldOptions {
|
|
2094
|
+
/** Duration in seconds, from zero through 100. */
|
|
2095
|
+
duration: number
|
|
2096
|
+
button?: string
|
|
2097
|
+
keys?: Array<string>
|
|
2098
|
+
takeover?: boolean
|
|
2099
|
+
}
|
|
2100
|
+
|
|
1960
2101
|
/**
|
|
1961
2102
|
* Convert HTML source to Markdown with optional preprocessing.
|
|
1962
2103
|
*
|
|
@@ -2066,10 +2207,13 @@ export interface IsoProbeResult {
|
|
|
2066
2207
|
}
|
|
2067
2208
|
|
|
2068
2209
|
/**
|
|
2069
|
-
* Pick the best backend available right now.
|
|
2070
|
-
*
|
|
2210
|
+
* Pick the best backend available right now.
|
|
2211
|
+
*
|
|
2212
|
+
* `preferred` is treated as a hint — see [`pi_iso::resolve`] for the exact
|
|
2213
|
+
* priority rules. Backend probes may spawn CLIs, so they run on the native
|
|
2214
|
+
* blocking pool.
|
|
2071
2215
|
*/
|
|
2072
|
-
export declare function isoResolve(preferred?: IsoBackendKind | undefined | null): IsoResolveResult
|
|
2216
|
+
export declare function isoResolve(preferred?: IsoBackendKind | undefined | null): Promise<IsoResolveResult>
|
|
2073
2217
|
|
|
2074
2218
|
/** Outcome of [`iso_resolve`]. */
|
|
2075
2219
|
export interface IsoResolveResult {
|
|
@@ -2457,6 +2601,8 @@ export interface PointerOptions {
|
|
|
2457
2601
|
button?: string
|
|
2458
2602
|
count?: number
|
|
2459
2603
|
modifiers?: Array<string>
|
|
2604
|
+
/** Arbitrary keys held for the duration of a drag. */
|
|
2605
|
+
keys?: Array<string>
|
|
2460
2606
|
/**
|
|
2461
2607
|
* Briefly activate the target window and post real input instead of the
|
|
2462
2608
|
* default background delivery.
|
|
@@ -2627,10 +2773,11 @@ export declare function renderMermaidAscii(text: string, options?: MermaidRender
|
|
|
2627
2773
|
* Render one snapcompact frame on a libuv worker: print pre-normalized text
|
|
2628
2774
|
* onto a `size`-wide bitmap and encode it as PNG.
|
|
2629
2775
|
*
|
|
2630
|
-
* The bitmap height hugs the rows the text
|
|
2631
|
-
* (`usedRows * lineRepeat * cellHeight`),
|
|
2632
|
-
*
|
|
2633
|
-
* floor(size/cellHeight/lineRepeat)` characters;
|
|
2776
|
+
* The bitmap height hugs the rows the text occupies
|
|
2777
|
+
* (`usedRows * lineRepeat * cellHeight`), with a 64px floor for vision
|
|
2778
|
+
* processors that reject smaller dimensions. The glyph grid holds
|
|
2779
|
+
* `floor(size/cellWidth) * floor(size/cellHeight/lineRepeat)` characters;
|
|
2780
|
+
* input beyond that is ignored.
|
|
2634
2781
|
* Native-cell bitmap-font shapes encode as indexed PNG; stretched bitmap-font
|
|
2635
2782
|
* shapes (target cell != font cell) encode as RGB. TrueType shapes encode RGB
|
|
2636
2783
|
* directly from grayscale coverage.
|
|
@@ -3108,8 +3255,8 @@ export declare function sliceWithWidth(line: string, startCol: number, length: n
|
|
|
3108
3255
|
export interface SnapcompactRenderOptions {
|
|
3109
3256
|
/**
|
|
3110
3257
|
* Frame width in pixels; also bounds the grid rows
|
|
3111
|
-
* (`floor(size/cellHeight/lineRepeat)`). Output height hugs the rows
|
|
3112
|
-
*
|
|
3258
|
+
* (`floor(size/cellHeight/lineRepeat)`). Output height hugs the used rows
|
|
3259
|
+
* with a 64px floor, rather than padding every frame to a square.
|
|
3113
3260
|
*/
|
|
3114
3261
|
size: number
|
|
3115
3262
|
/**
|
|
@@ -3496,6 +3643,18 @@ export interface VectorTopK {
|
|
|
3496
3643
|
*/
|
|
3497
3644
|
export declare function visibleWidth(text: string, tabWidth: number): number
|
|
3498
3645
|
|
|
3646
|
+
/**
|
|
3647
|
+
* Parse `options.code` into the shared tree cache on the native blocking
|
|
3648
|
+
* pool.
|
|
3649
|
+
*
|
|
3650
|
+
* [`enclosing_block_boundaries`], [`block_range_at`] and [`node_chain_at`]
|
|
3651
|
+
* are synchronous and parse on the JS thread when their source is not
|
|
3652
|
+
* cached; awaiting this first makes that parse a cache hit. Resolves without
|
|
3653
|
+
* parsing when the language is unrecognized or the source is too large for
|
|
3654
|
+
* the cache to keep.
|
|
3655
|
+
*/
|
|
3656
|
+
export declare function warmBlockParse(options: BlockParseOptions): Promise<undefined>
|
|
3657
|
+
|
|
3499
3658
|
/**
|
|
3500
3659
|
* Warm syntax grammars, scope matchers, and the regexes of commonly
|
|
3501
3660
|
* highlighted languages on the native worker pool.
|
package/native/index.js
CHANGED
|
@@ -58,6 +58,7 @@ export const copyToClipboard = nativeBindings.copyToClipboard ?? missingNativeEx
|
|
|
58
58
|
export const cosineSimilarityPairs = nativeBindings.cosineSimilarityPairs ?? missingNativeExport("cosineSimilarityPairs");
|
|
59
59
|
export const countTokens = nativeBindings.countTokens ?? missingNativeExport("countTokens");
|
|
60
60
|
export const decodeSixelToPng = nativeBindings.decodeSixelToPng ?? missingNativeExport("decodeSixelToPng");
|
|
61
|
+
export const decodeSixelToPngAsync = nativeBindings.decodeSixelToPngAsync ?? missingNativeExport("decodeSixelToPngAsync");
|
|
61
62
|
export const detectMacOSAppearance = nativeBindings.detectMacOSAppearance ?? missingNativeExport("detectMacOSAppearance");
|
|
62
63
|
export const deviceCheckGenerateToken = nativeBindings.deviceCheckGenerateToken ?? missingNativeExport("deviceCheckGenerateToken");
|
|
63
64
|
export const diffLineRuns = nativeBindings.diffLineRuns ?? missingNativeExport("diffLineRuns");
|
|
@@ -70,6 +71,7 @@ export const editGrammar = nativeBindings.editGrammar ?? missingNativeExport("ed
|
|
|
70
71
|
export const editInspect = nativeBindings.editInspect ?? missingNativeExport("editInspect");
|
|
71
72
|
export const enclosingBlockBoundaries = nativeBindings.enclosingBlockBoundaries ?? missingNativeExport("enclosingBlockBoundaries");
|
|
72
73
|
export const encodeSixel = nativeBindings.encodeSixel ?? missingNativeExport("encodeSixel");
|
|
74
|
+
export const encodeSixelAsync = nativeBindings.encodeSixelAsync ?? missingNativeExport("encodeSixelAsync");
|
|
73
75
|
export const execReplace = nativeBindings.execReplace ?? missingNativeExport("execReplace");
|
|
74
76
|
export const executeShell = nativeBindings.executeShell ?? missingNativeExport("executeShell");
|
|
75
77
|
export const expandWindowsLongPath = nativeBindings.expandWindowsLongPath ?? missingNativeExport("expandWindowsLongPath");
|
|
@@ -138,6 +140,7 @@ export const vcsJoinPatches = nativeBindings.vcsJoinPatches ?? missingNativeExpo
|
|
|
138
140
|
export const vcsValidateHunkSelections = nativeBindings.vcsValidateHunkSelections ?? missingNativeExport("vcsValidateHunkSelections");
|
|
139
141
|
export const vectorIndexTopK = nativeBindings.vectorIndexTopK ?? missingNativeExport("vectorIndexTopK");
|
|
140
142
|
export const visibleWidth = nativeBindings.visibleWidth ?? missingNativeExport("visibleWidth");
|
|
143
|
+
export const warmBlockParse = nativeBindings.warmBlockParse ?? missingNativeExport("warmBlockParse");
|
|
141
144
|
export const warmHighlighter = nativeBindings.warmHighlighter ?? missingNativeExport("warmHighlighter");
|
|
142
145
|
export const wrapTextWithAnsi = nativeBindings.wrapTextWithAnsi ?? missingNativeExport("wrapTextWithAnsi");
|
|
143
146
|
|
package/native/vcs.d.ts
CHANGED
|
@@ -81,6 +81,12 @@ export declare function detachGitDir(
|
|
|
81
81
|
signal?: AbortSignal,
|
|
82
82
|
): Promise<"no-git" | "independent" | "detached">;
|
|
83
83
|
|
|
84
|
+
/** Run Git's checkout hook in a new linked worktree and return its output and exit status. */
|
|
85
|
+
export declare function runPostCheckoutHook(
|
|
86
|
+
worktreeRoot: string,
|
|
87
|
+
head: string,
|
|
88
|
+
): Promise<{ exitCode: number; stdout: Uint8Array; stderr: Uint8Array }>;
|
|
89
|
+
|
|
84
90
|
/** Join patch fragments, preserving each part's trailing newline. */
|
|
85
91
|
export declare function joinPatches(parts: string[]): string;
|
|
86
92
|
|
package/native/vcs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { $ } from "bun";
|
|
1
2
|
import * as fs from "node:fs";
|
|
2
3
|
import { loadNative } from "./loader-state.js";
|
|
3
4
|
|
|
@@ -95,6 +96,19 @@ export function detachGitDir(worktreeRoot, sourceCommonDir, signal) {
|
|
|
95
96
|
return api().vcsDetachGitDir(worktreeRoot, sourceCommonDir, signal);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Run Git's checkout hook in a newly created linked worktree, using the same
|
|
101
|
+
* null old HEAD and branch-switch flag as `git worktree add`.
|
|
102
|
+
*/
|
|
103
|
+
export async function runPostCheckoutHook(worktreeRoot, head) {
|
|
104
|
+
const nullOid = "0".repeat(head.length);
|
|
105
|
+
const result = await $`git hook run --ignore-missing post-checkout -- ${nullOid} ${head} 1`
|
|
106
|
+
.cwd(worktreeRoot)
|
|
107
|
+
.quiet()
|
|
108
|
+
.nothrow();
|
|
109
|
+
return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
|
|
110
|
+
}
|
|
111
|
+
|
|
98
112
|
/** Join patch fragments, preserving each part's trailing newline. */
|
|
99
113
|
export function joinPatches(parts) {
|
|
100
114
|
return api().vcsJoinPatches(parts);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxiraos/pi-natives",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.27",
|
|
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/",
|
|
@@ -111,11 +111,11 @@
|
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
113
|
"optionalDependencies": {
|
|
114
|
-
"@linxiraos/pi-natives-linux-x64": "1.1.
|
|
115
|
-
"@linxiraos/pi-natives-linux-arm64": "1.1.
|
|
116
|
-
"@linxiraos/pi-natives-darwin-x64": "1.1.
|
|
117
|
-
"@linxiraos/pi-natives-darwin-arm64": "1.1.
|
|
118
|
-
"@linxiraos/pi-natives-win32-x64": "1.1.
|
|
119
|
-
"@linxiraos/pi-natives-win32-arm64": "1.1.
|
|
114
|
+
"@linxiraos/pi-natives-linux-x64": "1.1.27",
|
|
115
|
+
"@linxiraos/pi-natives-linux-arm64": "1.1.27",
|
|
116
|
+
"@linxiraos/pi-natives-darwin-x64": "1.1.27",
|
|
117
|
+
"@linxiraos/pi-natives-darwin-arm64": "1.1.27",
|
|
118
|
+
"@linxiraos/pi-natives-win32-x64": "1.1.27",
|
|
119
|
+
"@linxiraos/pi-natives-win32-arm64": "1.1.27"
|
|
120
120
|
}
|
|
121
121
|
}
|