@gajae-code/natives 0.10.2 → 0.11.1
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/native/index.d.ts +70 -9
- package/native/index.js +1 -1
- package/native/loader-state.d.ts +15 -0
- package/native/loader-state.js +47 -20
- package/package.json +6 -6
package/native/index.d.ts
CHANGED
|
@@ -118,6 +118,16 @@ export declare class NotificationServer {
|
|
|
118
118
|
* config commands). Must be called before [`Self::start`].
|
|
119
119
|
*/
|
|
120
120
|
onInbound(callback: (err: null | Error, msg: InboundEvent) => void): void
|
|
121
|
+
/**
|
|
122
|
+
* Register the raw v3 SDK frame callback. Must be called before
|
|
123
|
+
* [`Self::start`].
|
|
124
|
+
*/
|
|
125
|
+
onSdkFrame(callback: (err: null | Error, frame: SdkFrameEvent) => void): void
|
|
126
|
+
/**
|
|
127
|
+
* Register the connection-close callback. Must be called before
|
|
128
|
+
* [`Self::start`].
|
|
129
|
+
*/
|
|
130
|
+
onConnectionClose(callback: (err: null | Error, connectionId: string) => void): void
|
|
121
131
|
/**
|
|
122
132
|
* Bind the loopback endpoint and start serving. Resolves with the bound
|
|
123
133
|
* endpoint info once the socket is bound.
|
|
@@ -129,12 +139,32 @@ export declare class NotificationServer {
|
|
|
129
139
|
/**
|
|
130
140
|
* Broadcast an `action_needed` ask. `needed_json` is a JSON `ActionNeeded`.
|
|
131
141
|
*
|
|
132
|
-
* `repliable` should be `true` only
|
|
142
|
+
* `repliable` should be `true` only when an SDK workflow-gate resolver is
|
|
143
|
+
* available.
|
|
133
144
|
*
|
|
134
145
|
* # Errors
|
|
135
146
|
* Fails if not started or `needed_json` is invalid.
|
|
136
147
|
*/
|
|
137
148
|
registerAsk(neededJson: string, repliable: boolean): void
|
|
149
|
+
/**
|
|
150
|
+
* Register a correlated workflow-gate ask. `workflow_json` must be an
|
|
151
|
+
* `action_needed` wire frame carrying a nonempty `workflowGateId`.
|
|
152
|
+
*/
|
|
153
|
+
registerWorkflowGateAsk(workflowJson: string, repliable: boolean): void
|
|
154
|
+
/**
|
|
155
|
+
* Register an ask and return an opaque in-process capability. Pass it
|
|
156
|
+
* unchanged to [`Self::retire_if_unclaimed`]; do not construct, persist,
|
|
157
|
+
* inspect, or treat it as workflow-gate authority. A supplied
|
|
158
|
+
* `workflowGateId` is preserved.
|
|
159
|
+
*/
|
|
160
|
+
registerArbitratedAsk(neededJson: string, repliable: boolean): PresentationLease
|
|
161
|
+
/**
|
|
162
|
+
* Atomically terminalize the exact presentation named by an opaque lease.
|
|
163
|
+
* The typed status proves whether it retired, was already terminal, was
|
|
164
|
+
* claimed, or became stale without exposing claims, receipts, registration
|
|
165
|
+
* state, or workflow-gate authority.
|
|
166
|
+
*/
|
|
167
|
+
retireIfUnclaimed(lease: PresentationLease): RetireIfUnclaimedResult
|
|
138
168
|
/**
|
|
139
169
|
* Broadcast an ephemeral `action_needed` idle ping. `needed_json` is JSON
|
|
140
170
|
* `ActionNeeded`.
|
|
@@ -153,6 +183,8 @@ export declare class NotificationServer {
|
|
|
153
183
|
* Fails if not started or `frame_json` is not a valid `ServerMessage`.
|
|
154
184
|
*/
|
|
155
185
|
pushFrame(frameJson: string): void
|
|
186
|
+
/** Send raw JSON to one connected v3 SDK client. */
|
|
187
|
+
sendTo(connectionId: string, json: string): void
|
|
156
188
|
/**
|
|
157
189
|
* Publish a replayable `session_ready` readiness signal. `ready_json` is a
|
|
158
190
|
* JSON `SessionReady`. Unlike [`Self::push_frame`], this frame is buffered
|
|
@@ -165,11 +197,10 @@ export declare class NotificationServer {
|
|
|
165
197
|
*/
|
|
166
198
|
pushSessionReady(readyJson: string): void
|
|
167
199
|
/**
|
|
168
|
-
* Resolve
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* Fails if not started or `answer_json` is invalid.
|
|
200
|
+
* Resolve a legacy/non-arbitrated action locally (the CLI/TUI answered).
|
|
201
|
+
* Arbitrated presentations require their opaque exact lease to be passed to
|
|
202
|
+
* [`Self::retire_if_unclaimed`], so an id-only local resolution fails
|
|
203
|
+
* closed.
|
|
173
204
|
*/
|
|
174
205
|
resolveLocal(id: string, answerJson?: string | undefined | null): void
|
|
175
206
|
/**
|
|
@@ -207,7 +238,7 @@ export declare class NotificationServer {
|
|
|
207
238
|
*/
|
|
208
239
|
reject(id: string, reason?: string | undefined | null): void
|
|
209
240
|
/**
|
|
210
|
-
* Update whether the
|
|
241
|
+
* Update whether the SDK workflow-gate resolver is currently available.
|
|
211
242
|
*
|
|
212
243
|
* # Errors
|
|
213
244
|
* Fails if not started.
|
|
@@ -217,6 +248,8 @@ export declare class NotificationServer {
|
|
|
217
248
|
clientCount(): number
|
|
218
249
|
/** Stop the server (idempotent) and remove the endpoint discovery file. */
|
|
219
250
|
stop(): void
|
|
251
|
+
/** Stop the server and resolve only after all native socket owners exit. */
|
|
252
|
+
stopAndWait(): Promise<void>
|
|
220
253
|
}
|
|
221
254
|
|
|
222
255
|
/** Stable process reference. */
|
|
@@ -227,6 +260,8 @@ export declare class Process {
|
|
|
227
260
|
static fromPath(path: string): Array<Process>
|
|
228
261
|
/** Operating-system process identifier for this process reference. */
|
|
229
262
|
get pid(): number
|
|
263
|
+
/** Kernel-derived identity evidence for this exact process incarnation. */
|
|
264
|
+
get incarnation(): string
|
|
230
265
|
/** Parent process id for this process, when available. */
|
|
231
266
|
get ppid(): number | null
|
|
232
267
|
/** Launch arguments for this process. */
|
|
@@ -316,7 +351,7 @@ export declare class Shell {
|
|
|
316
351
|
* `packages/natives/native/index.js` (which derives the name from
|
|
317
352
|
* `package.json#version`).
|
|
318
353
|
*/
|
|
319
|
-
export declare function
|
|
354
|
+
export declare function __piNativesV0_11_1(): void
|
|
320
355
|
|
|
321
356
|
/**
|
|
322
357
|
* Apply conservative pre-execution rewrites to a bash command.
|
|
@@ -1420,6 +1455,18 @@ export declare function parseKey(data: string, kittyProtocolActive: boolean): st
|
|
|
1420
1455
|
*/
|
|
1421
1456
|
export declare function parseKittySequence(data: string): ParsedKittyResult | null
|
|
1422
1457
|
|
|
1458
|
+
/**
|
|
1459
|
+
* Opaque in-process presentation capability.
|
|
1460
|
+
*
|
|
1461
|
+
* Returned by [`NotificationServer::register_arbitrated_ask`]. Pass it
|
|
1462
|
+
* unchanged to [`NotificationServer::retire_if_unclaimed`]; do not construct,
|
|
1463
|
+
* persist, inspect, or treat it as workflow-gate authority.
|
|
1464
|
+
*/
|
|
1465
|
+
export interface PresentationLease {
|
|
1466
|
+
actionId: string
|
|
1467
|
+
registrationEpoch: number
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1423
1470
|
/** Current state of a process reference. */
|
|
1424
1471
|
export declare enum ProcessStatus {
|
|
1425
1472
|
/** The referenced process is still running. */
|
|
@@ -1498,7 +1545,10 @@ export declare function readImageFromClipboard(): Promise<ClipboardImage | undef
|
|
|
1498
1545
|
|
|
1499
1546
|
/** A client reply forwarded to the TypeScript host for gate resolution. */
|
|
1500
1547
|
export interface ReplyEvent {
|
|
1501
|
-
/**
|
|
1548
|
+
/**
|
|
1549
|
+
* The transient action/presentation id being answered. This is not the
|
|
1550
|
+
* durable workflow gate id.
|
|
1551
|
+
*/
|
|
1502
1552
|
id: string
|
|
1503
1553
|
/** JSON-encoded `ReplyAnswer` (number, string, or `{selected,custom}`). */
|
|
1504
1554
|
answerJson: string
|
|
@@ -1508,6 +1558,17 @@ export interface ReplyEvent {
|
|
|
1508
1558
|
replyReceiptId: string
|
|
1509
1559
|
}
|
|
1510
1560
|
|
|
1561
|
+
/** Public status of exact direct retirement. Claims and receipts remain native. */
|
|
1562
|
+
export interface RetireIfUnclaimedResult {
|
|
1563
|
+
status: 'retired' | 'already_terminal' | 'claimed' | 'stale'
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/** A raw v3 SDK frame paired with its actual WebSocket connection id. */
|
|
1567
|
+
export interface SdkFrameEvent {
|
|
1568
|
+
connectionId: string
|
|
1569
|
+
json: string
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1511
1572
|
/**
|
|
1512
1573
|
* Search content for a pattern (one-shot, compiles pattern each time).
|
|
1513
1574
|
* For repeated searches with the same pattern, use [`grep`] with file filters.
|
package/native/index.js
CHANGED
|
@@ -27,7 +27,7 @@ export const PtySession = nativeBindings.PtySession;
|
|
|
27
27
|
export const Shell = nativeBindings.Shell;
|
|
28
28
|
|
|
29
29
|
// functions
|
|
30
|
-
export const
|
|
30
|
+
export const __piNativesV0_11_1 = nativeBindings.__piNativesV0_11_1;
|
|
31
31
|
export const applyBashFixups = nativeBindings.applyBashFixups;
|
|
32
32
|
export const astEdit = nativeBindings.astEdit;
|
|
33
33
|
export const astGrep = nativeBindings.astGrep;
|
package/native/loader-state.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ export interface ResolveLoaderCandidatesInput {
|
|
|
47
47
|
addonFilenames: string[];
|
|
48
48
|
isCompiledBinary: boolean;
|
|
49
49
|
stageFromNodeModules?: boolean;
|
|
50
|
+
isWorkspaceLoad?: boolean;
|
|
50
51
|
optionalPackageNativeDirs?: string[];
|
|
51
52
|
nativeDir: string;
|
|
52
53
|
execDir: string;
|
|
@@ -56,4 +57,18 @@ export interface ResolveLoaderCandidatesInput {
|
|
|
56
57
|
|
|
57
58
|
export function resolveLoaderCandidates(input: ResolveLoaderCandidatesInput): string[];
|
|
58
59
|
|
|
60
|
+
export interface LoadFromCandidatesInput<T> {
|
|
61
|
+
candidates: string[];
|
|
62
|
+
requireCandidate: (candidate: string) => T;
|
|
63
|
+
validateCandidate: (bindings: T, candidate: string) => void;
|
|
64
|
+
describeCandidate: (candidate: string) => string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface LoadFromCandidatesResult<T> {
|
|
68
|
+
bindings: T | null;
|
|
69
|
+
errors: string[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function loadFromCandidates<T>(input: LoadFromCandidatesInput<T>): LoadFromCandidatesResult<T>;
|
|
73
|
+
|
|
59
74
|
export function loadNative(): Record<string, unknown>;
|
package/native/loader-state.js
CHANGED
|
@@ -146,6 +146,7 @@ export function shouldStageNodeModulesAddon({ platform, isCompiledBinary, native
|
|
|
146
146
|
* addonFilenames: string[];
|
|
147
147
|
* isCompiledBinary: boolean;
|
|
148
148
|
* stageFromNodeModules?: boolean;
|
|
149
|
+
* isWorkspaceLoad?: boolean;
|
|
149
150
|
* optionalPackageNativeDirs?: string[];
|
|
150
151
|
* nativeDir: string;
|
|
151
152
|
* execDir: string;
|
|
@@ -158,12 +159,14 @@ export function resolveLoaderCandidates({
|
|
|
158
159
|
addonFilenames,
|
|
159
160
|
isCompiledBinary,
|
|
160
161
|
stageFromNodeModules = false,
|
|
162
|
+
isWorkspaceLoad = false,
|
|
161
163
|
optionalPackageNativeDirs = [],
|
|
162
164
|
nativeDir,
|
|
163
165
|
execDir,
|
|
164
166
|
versionedDir,
|
|
165
167
|
userDataDir,
|
|
166
168
|
}) {
|
|
169
|
+
const workspaceCandidates = addonFilenames.map(filename => path.join(nativeDir, filename));
|
|
167
170
|
const optionalPackageCandidates = optionalPackageNativeDirs.flatMap(optionalNativeDir =>
|
|
168
171
|
addonFilenames.map(filename => path.join(optionalNativeDir, filename)),
|
|
169
172
|
);
|
|
@@ -171,7 +174,10 @@ export function resolveLoaderCandidates({
|
|
|
171
174
|
path.join(nativeDir, filename),
|
|
172
175
|
path.join(execDir, filename),
|
|
173
176
|
]);
|
|
174
|
-
const
|
|
177
|
+
const legacyExecCandidates = addonFilenames.map(filename => path.join(execDir, filename));
|
|
178
|
+
const baseReleaseCandidates = isWorkspaceLoad
|
|
179
|
+
? [...workspaceCandidates, ...optionalPackageCandidates, ...legacyExecCandidates]
|
|
180
|
+
: [...optionalPackageCandidates, ...legacyReleaseCandidates];
|
|
175
181
|
const compiledCandidates = addonFilenames.flatMap(filename => [
|
|
176
182
|
path.join(versionedDir, filename),
|
|
177
183
|
path.join(userDataDir, filename),
|
|
@@ -188,6 +194,35 @@ export function resolveLoaderCandidates({
|
|
|
188
194
|
return [...new Set(releaseCandidates)];
|
|
189
195
|
}
|
|
190
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Deterministically try candidate paths in order using injected operations.
|
|
199
|
+
* This leaves file loading and compatibility policy at the call site while
|
|
200
|
+
* making fallback behavior testable without a native addon on disk.
|
|
201
|
+
*
|
|
202
|
+
* @template T
|
|
203
|
+
* @param {{
|
|
204
|
+
* candidates: string[];
|
|
205
|
+
* requireCandidate: (candidate: string) => T;
|
|
206
|
+
* validateCandidate: (bindings: T, candidate: string) => void;
|
|
207
|
+
* describeCandidate: (candidate: string) => string;
|
|
208
|
+
* }} input
|
|
209
|
+
* @returns {{ bindings: T | null; errors: string[] }}
|
|
210
|
+
*/
|
|
211
|
+
export function loadFromCandidates({ candidates, requireCandidate, validateCandidate, describeCandidate }) {
|
|
212
|
+
const errors = [];
|
|
213
|
+
for (const candidate of candidates) {
|
|
214
|
+
try {
|
|
215
|
+
const bindings = requireCandidate(candidate);
|
|
216
|
+
validateCandidate(bindings, candidate);
|
|
217
|
+
return { bindings, errors };
|
|
218
|
+
} catch (err) {
|
|
219
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
220
|
+
errors.push(`${describeCandidate(candidate)}: ${message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return { bindings: null, errors };
|
|
224
|
+
}
|
|
225
|
+
|
|
191
226
|
// =========================================================================
|
|
192
227
|
// Side-effectful loader. Everything below runs only when `loadNative()` is
|
|
193
228
|
// called from `native/index.js` — tests that only import the pure helpers
|
|
@@ -342,12 +377,6 @@ function maybeStageNodeModulesAddon(ctx, errors) {
|
|
|
342
377
|
}
|
|
343
378
|
|
|
344
379
|
function validateLoadedBindings(ctx, bindings, candidate) {
|
|
345
|
-
// In workspace dev (running out of `packages/natives/native/` rather than a
|
|
346
|
-
// `node_modules` install or a compiled bundle) the local `.node` only gains
|
|
347
|
-
// the renamed sentinel after `bun --cwd=packages/natives run build`. Skip
|
|
348
|
-
// validation there so a stale post-pull dev tree boots while the rebuild
|
|
349
|
-
// completes; install and compiled-binary paths still validate.
|
|
350
|
-
if (ctx.isWorkspaceLoad) return;
|
|
351
380
|
if (typeof bindings[ctx.versionSentinelExport] === "function") return;
|
|
352
381
|
throw new Error(
|
|
353
382
|
`Loaded ${candidate} but it does not expose the @gajae-code/natives@${ctx.packageVersion} ` +
|
|
@@ -405,6 +434,8 @@ function initLoaderContext(require_) {
|
|
|
405
434
|
isCompiledBinary,
|
|
406
435
|
nativeDir,
|
|
407
436
|
});
|
|
437
|
+
const isWorkspaceLoad =
|
|
438
|
+
!isCompiledBinary && !nativeDir.includes("\\node_modules\\") && !nativeDir.includes("/node_modules/");
|
|
408
439
|
|
|
409
440
|
const selectedVariant = resolveCpuVariant(getVariantOverride());
|
|
410
441
|
const addonFilenames = getAddonFilenames({ tag: platformTag, arch: process.arch, variant: selectedVariant });
|
|
@@ -419,6 +450,7 @@ function initLoaderContext(require_) {
|
|
|
419
450
|
addonFilenames,
|
|
420
451
|
isCompiledBinary,
|
|
421
452
|
stageFromNodeModules,
|
|
453
|
+
isWorkspaceLoad,
|
|
422
454
|
optionalPackageNativeDirs,
|
|
423
455
|
nativeDir,
|
|
424
456
|
execDir,
|
|
@@ -434,8 +466,6 @@ function initLoaderContext(require_) {
|
|
|
434
466
|
// turns the silent `<sym> is not a function` crash from a Windows
|
|
435
467
|
// locked-file update into an actionable load-time error.
|
|
436
468
|
const versionSentinelExport = `__piNativesV${packageVersion.replace(/[^A-Za-z0-9]/g, "_")}`;
|
|
437
|
-
const isWorkspaceLoad =
|
|
438
|
-
!isCompiledBinary && !nativeDir.includes("\\node_modules\\") && !nativeDir.includes("/node_modules/");
|
|
439
469
|
|
|
440
470
|
return {
|
|
441
471
|
platformTag,
|
|
@@ -450,7 +480,6 @@ function initLoaderContext(require_) {
|
|
|
450
480
|
addonLabel,
|
|
451
481
|
candidates,
|
|
452
482
|
versionSentinelExport,
|
|
453
|
-
isWorkspaceLoad,
|
|
454
483
|
};
|
|
455
484
|
}
|
|
456
485
|
|
|
@@ -464,16 +493,14 @@ export function loadNative() {
|
|
|
464
493
|
const prepended = [embeddedCandidate, stagedCandidate].filter(c => typeof c === "string");
|
|
465
494
|
const runtimeCandidates = prepended.length > 0 ? [...prepended, ...ctx.candidates] : ctx.candidates;
|
|
466
495
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
}
|
|
496
|
+
const loaded = loadFromCandidates({
|
|
497
|
+
candidates: runtimeCandidates,
|
|
498
|
+
requireCandidate: candidate => require_(candidate),
|
|
499
|
+
validateCandidate: (bindings, candidate) => validateLoadedBindings(ctx, bindings, candidate),
|
|
500
|
+
describeCandidate: candidate => candidate,
|
|
501
|
+
});
|
|
502
|
+
if (loaded.bindings) return loaded.bindings;
|
|
503
|
+
errors.push(...loaded.errors);
|
|
477
504
|
|
|
478
505
|
if (!SUPPORTED_PLATFORMS.includes(ctx.platformTag)) {
|
|
479
506
|
throw new Error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gajae-code/natives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Native Rust bindings for grep, clipboard, image processing, syntax highlighting, PTY, and shell operations via N-API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"README.md"
|
|
62
62
|
],
|
|
63
63
|
"optionalDependencies": {
|
|
64
|
-
"@gajae-code/natives-darwin-arm64": "0.
|
|
65
|
-
"@gajae-code/natives-darwin-x64": "0.
|
|
66
|
-
"@gajae-code/natives-linux-arm64": "0.
|
|
67
|
-
"@gajae-code/natives-linux-x64": "0.
|
|
68
|
-
"@gajae-code/natives-win32-x64": "0.
|
|
64
|
+
"@gajae-code/natives-darwin-arm64": "0.11.1",
|
|
65
|
+
"@gajae-code/natives-darwin-x64": "0.11.1",
|
|
66
|
+
"@gajae-code/natives-linux-arm64": "0.11.1",
|
|
67
|
+
"@gajae-code/natives-linux-x64": "0.11.1",
|
|
68
|
+
"@gajae-code/natives-win32-x64": "0.11.1"
|
|
69
69
|
},
|
|
70
70
|
"exports": {
|
|
71
71
|
".": {
|