@gajae-code/natives 0.11.7 → 0.11.8

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  /* auto-generated by NAPI-RS */
2
2
  /* eslint-disable */
3
+ /**
4
+ * macOS computer-use controller.
5
+ *
6
+ * This declaration and the named JS export are available on every platform so
7
+ * consumers can import them portably; the native controller itself is built
8
+ * only on macOS.
9
+ */
3
10
  export declare class ComputerController {
4
11
  constructor()
5
12
  screenshot(): ComputerScreenshot
@@ -201,6 +208,11 @@ export declare class NotificationServer {
201
208
  * Fails if not started or `frame_json` is not a valid `ServerMessage`.
202
209
  */
203
210
  pushFrame(frameJson: string): void
211
+ /**
212
+ * Deliver a frame through every authenticated connection and wait for each
213
+ * socket writer to settle within `timeout_ms`.
214
+ */
215
+ pushFrameAndWait(frameJson: string, timeoutMs: number): Promise<boolean>
204
216
  /**
205
217
  * Broadcast a TypeScript-constructed turn frame without re-parsing JSON.
206
218
  * External frames must continue through [`Self::push_frame`] for serde
@@ -386,7 +398,7 @@ export declare class RecoveryFsRoot {
386
398
  */
387
399
  appendManaged(relativePath: string, data: Uint8Array, expectedDev: string, expectedIno: string, expectedSize: string, expectedMtimeNs: string, expectedCtimeNs: string, expectedSha256: string): RecoveryFsResult
388
400
  /** Remove one exact managed regular file through retained authority. */
389
- removeManaged(relativePath: string, expectedDev: string, expectedIno: string, expectedSize: string, expectedMtimeNs: string, expectedCtimeNs: string, expectedSha256: string): RecoveryFsResult
401
+ removeManaged(relativePath: string, expectedDev: string, expectedIno: string, expectedSize: string, expectedMtimeNs: string, expectedCtimeNs: string, expectedSha256: string): RecoveryFsRetainedCleanupResult
390
402
  /**
391
403
  * Create each absent directory component beneath the retained root with
392
404
  * owner-only security. Existing components are re-opened no-follow.
@@ -397,22 +409,22 @@ export declare class RecoveryFsRoot {
397
409
  * retained root. The source identity is rechecked after the no-replace
398
410
  * rename, and the move is rolled back on a mismatch.
399
411
  */
400
- renameManagedFileNoReplace(sourceRelativePath: string, destinationRelativePath: string, expectedDev: string, expectedIno: string, expectedSize: string, expectedMtimeNs: string, expectedCtimeNs: string, expectedSha256: string): RecoveryFsResult
412
+ renameManagedFileNoReplace(sourceRelativePath: string, destinationRelativePath: string, expectedDev: string, expectedIno: string, expectedSize: string, expectedMtimeNs: string, expectedCtimeNs: string, expectedSha256: string): RecoveryFsPublishResult
401
413
  /** Snapshot a managed directory tree entirely through the retained root. */
402
414
  snapshotManagedTree(relativePath: string): NativeDirectoryTreeResult
403
415
  /**
404
416
  * Move an exact managed directory tree to an absent name through retained
405
417
  * authority.
406
418
  */
407
- renameManagedTreeNoReplace(sourceRelativePath: string, destinationRelativePath: string, expected: NativeDirectoryTreeSnapshot): RecoveryFsResult
419
+ renameManagedTreeNoReplace(sourceRelativePath: string, destinationRelativePath: string, expected: NativeDirectoryTreeSnapshot): RecoveryFsPublishResult
408
420
  /** Remove an exact managed directory tree through retained authority. */
409
- removeManagedTree(relativePath: string, expected: NativeDirectoryTreeSnapshot): RecoveryFsResult
421
+ removeManagedTree(relativePath: string, expected: NativeDirectoryTreeSnapshot): RecoveryFsRetainedCleanupResult
410
422
  /**
411
423
  * Atomically install an already-created regular file at an absent name.
412
424
  * Both names remain relative to this retained root and are never resolved
413
425
  * through a pathname after their parent descriptors are acquired.
414
426
  */
415
- install(sourceRelativePath: string, destinationRelativePath: string): RecoveryFsResult
427
+ install(sourceRelativePath: string, destinationRelativePath: string): RecoveryFsPublishResult
416
428
  /**
417
429
  * Synchronize the retained root directory, making a preceding create or
418
430
  * install durable when the filesystem supports directory fsync.
@@ -452,6 +464,15 @@ export declare class Shell {
452
464
  abort(): Promise<void>
453
465
  }
454
466
 
467
+ /**
468
+ * Publish-result wire-contract sentinel.
469
+ *
470
+ * The loader requires this in addition to the release sentinel, so a
471
+ * same-version modern artifact built before the retained-publish contract
472
+ * cannot be selected over a compatible baseline.
473
+ */
474
+ export declare function __piNativesPublishOutcomeV1(): void
475
+
455
476
  /**
456
477
  * Version sentinel — exists solely so the JS loader can prove at load time
457
478
  * that the `.node` file on disk is from the same package release as the
@@ -470,7 +491,7 @@ export declare class Shell {
470
491
  * `packages/natives/native/index.js` (which derives the name from
471
492
  * `package.json#version`).
472
493
  */
473
- export declare function __piNativesV0_11_7(): void
494
+ export declare function __piNativesV0_11_8(): void
474
495
 
475
496
  /**
476
497
  * Apply conservative pre-execution rewrites to a bash command.
@@ -1720,6 +1741,18 @@ export interface NativeExactUnlinkResult {
1720
1741
  retainedUnknownPath?: string
1721
1742
  }
1722
1743
 
1744
+ /** Dedicated result for an atomic no-replace namespace publication. */
1745
+ export interface NativeNoReplaceResult {
1746
+ ok: boolean
1747
+ code?: string
1748
+ mutationState: string
1749
+ durabilityState: string
1750
+ reason: string
1751
+ primitive: string
1752
+ phase: string
1753
+ diagnostic: NativePublishDiagnostic
1754
+ }
1755
+
1723
1756
  /** Result of applying or checking owner-only path security. */
1724
1757
  export type NativeOwnerOnlySecurityResult =
1725
1758
  | {
@@ -1950,6 +1983,38 @@ export interface RecoveryFsIdentity {
1950
1983
  sha256?: string
1951
1984
  }
1952
1985
 
1986
+ /** Bounded, path-free diagnostic evidence for one retained publication. */
1987
+ export interface RecoveryFsPublishDiagnostic {
1988
+ schemaVersion: number
1989
+ collectionState: string
1990
+ osCode?: number
1991
+ syncFailures?: Array<RecoveryFsPublishSyncFailure>
1992
+ }
1993
+
1994
+ /**
1995
+ * Explicit mutation and durability outcome for retained no-replace
1996
+ * publication.
1997
+ */
1998
+ export interface RecoveryFsPublishResult {
1999
+ ok: boolean
2000
+ code?: string
2001
+ identity?: RecoveryFsIdentity
2002
+ mutationState: string
2003
+ durabilityState: string
2004
+ reason: string
2005
+ primitive: string
2006
+ phase: string
2007
+ diagnostic: RecoveryFsPublishDiagnostic
2008
+ }
2009
+
2010
+ /** Bounded, path-free diagnostic evidence for one retained publication. */
2011
+ export interface RecoveryFsPublishSyncFailure {
2012
+ phase: string
2013
+ parentRole: string
2014
+ osCode?: number
2015
+ kind: string
2016
+ }
2017
+
1953
2018
  export interface RecoveryFsResult {
1954
2019
  ok: boolean
1955
2020
  code?: string
@@ -1957,7 +2022,20 @@ export interface RecoveryFsResult {
1957
2022
  data?: Uint8Array
1958
2023
  }
1959
2024
 
1960
- export declare function renameNoReplacePath(sourcePath: string, destinationPath: string): NativeExactUnlinkResult
2025
+ /**
2026
+ * Fail-closed outcome for a removal whose detached object remains retained.
2027
+ * `recovery_path` identifies evidence only; it grants no authority to replay
2028
+ * or delete the retained object.
2029
+ */
2030
+ export interface RecoveryFsRetainedCleanupResult {
2031
+ ok: boolean
2032
+ code?: string
2033
+ recoveryPath?: string
2034
+ identity?: RecoveryFsIdentity
2035
+ treeSnapshot?: NativeDirectoryTreeSnapshot
2036
+ }
2037
+
2038
+ export declare function renameNoReplacePath(sourcePath: string, destinationPath: string): NativeNoReplaceResult
1961
2039
 
1962
2040
  /**
1963
2041
  * Repair an owner-only ACL on a retained expected path.
@@ -2232,3 +2310,19 @@ export interface WorkProfile {
2232
2310
  * Returns UTF-16 lines with active SGR codes carried across line boundaries.
2233
2311
  */
2234
2312
  export declare function wrapTextWithAnsi(text: string, width: number, tabWidth: number): Array<string>
2313
+
2314
+ /** Bounded, path-free evidence for a parent-directory durability failure. */
2315
+ export interface NativePublishSyncFailure {
2316
+ phase: string
2317
+ parentRole: string
2318
+ osCode: number
2319
+ kind: string
2320
+ }
2321
+
2322
+ /** Bounded, path-free evidence for one atomic publication. */
2323
+ export interface NativePublishDiagnostic {
2324
+ schemaVersion: number
2325
+ collectionState: string
2326
+ osCode?: number
2327
+ syncFailures?: Array<NativePublishSyncFailure>
2328
+ }
package/native/index.js CHANGED
@@ -29,7 +29,8 @@ export const RecoveryFsRoot = nativeBindings.RecoveryFsRoot;
29
29
  export const Shell = nativeBindings.Shell;
30
30
 
31
31
  // functions
32
- export const __piNativesV0_11_7 = nativeBindings.__piNativesV0_11_7;
32
+ export const __piNativesPublishOutcomeV1 = nativeBindings.__piNativesPublishOutcomeV1;
33
+ export const __piNativesV0_11_8 = nativeBindings.__piNativesV0_11_8;
33
34
  export const applyBashFixups = nativeBindings.applyBashFixups;
34
35
  export const applyOwnerOnlyFdSecurity = nativeBindings.applyOwnerOnlyFdSecurity;
35
36
  export const applyOwnerOnlyPathSecurity = nativeBindings.applyOwnerOnlyPathSecurity;
@@ -79,4 +79,28 @@ export interface CachedEmbeddedExtractionIsFreshInput {
79
79
 
80
80
  export function cachedEmbeddedExtractionIsFresh(input: CachedEmbeddedExtractionIsFreshInput): boolean;
81
81
 
82
- export function loadNative(): Record<string, unknown>;
82
+ export interface LoaderContext {
83
+ isCompiledBinary: boolean;
84
+ platformTag: string;
85
+ packageVersion?: string;
86
+ addonLabel?: string;
87
+ addonFilenames?: string[];
88
+ versionedDir?: string;
89
+ candidates?: string[];
90
+ selectedVariant?: "modern" | "baseline" | null;
91
+ }
92
+
93
+ export function embeddedAddonIsAuthoritative(
94
+ ctx: LoaderContext,
95
+ addon?: EmbeddedAddon | null,
96
+ ): boolean;
97
+
98
+ export interface LoadNativeOptions {
99
+ context?: LoaderContext;
100
+ extractEmbeddedAddons?: (ctx: LoaderContext) => string[];
101
+ stageNodeModulesAddon?: () => string | null;
102
+ requireCandidate?: (candidate: string) => Record<string, unknown>;
103
+ validateCandidate?: (bindings: Record<string, unknown>) => void;
104
+ }
105
+
106
+ export function loadNative(options?: LoadNativeOptions): Record<string, unknown>;
@@ -306,64 +306,56 @@ function resolveCpuVariant(override) {
306
306
  return detectAvx2Support() ? "modern" : "baseline";
307
307
  }
308
308
 
309
- function selectEmbeddedAddonFile(selectedVariant) {
310
- if (!embeddedAddon) return null;
311
- const defaultFile = embeddedAddon.files.find(file => file.variant === "default") || null;
312
- if (process.arch !== "x64") return defaultFile || embeddedAddon.files[0] || null;
313
- if (selectedVariant === "modern") {
314
- return (
315
- embeddedAddon.files.find(file => file.variant === "modern") ||
316
- embeddedAddon.files.find(file => file.variant === "baseline") ||
317
- null
318
- );
319
- }
320
- return embeddedAddon.files.find(file => file.variant === "baseline") || null;
309
+ function embeddedAddonCandidates(selectedVariant) {
310
+ if (!embeddedAddon) return [];
311
+ const files = embeddedAddon.files;
312
+ const candidates = process.arch !== "x64"
313
+ ? [files.find(file => file.variant === "default"), ...files]
314
+ : selectedVariant === "modern"
315
+ ? [files.find(file => file.variant === "modern"), files.find(file => file.variant === "baseline")]
316
+ : [files.find(file => file.variant === "baseline")];
317
+ return [...new Set(candidates.filter(Boolean))];
321
318
  }
322
319
 
323
- function maybeExtractEmbeddedAddon(ctx, errors) {
324
- if (!ctx.isCompiledBinary || !embeddedAddon) return null;
325
- if (embeddedAddon.platformTag !== ctx.platformTag || embeddedAddon.version !== ctx.packageVersion) return null;
326
-
327
- const selectedEmbeddedFile = selectEmbeddedAddonFile(ctx.selectedVariant);
328
- if (!selectedEmbeddedFile) return null;
329
- const targetPath = path.join(ctx.versionedDir, selectedEmbeddedFile.filename);
330
- if (fs.existsSync(targetPath)) {
331
- // Guard against intra-version drift: a cached extraction written by an earlier
332
- // build of the same version carries the same version sentinel but can expose a
333
- // different native surface (e.g. a symbol added mid-cycle). The embedded addon
334
- // is the source of truth, so reuse the cached file only when it matches the
335
- // embedded payload size and re-extract otherwise.
336
- const sizeOf = candidate => {
337
- try {
338
- return fs.statSync(candidate).size;
339
- } catch {
340
- return null;
320
+ function maybeExtractEmbeddedAddons(ctx, errors) {
321
+ if (!ctx.isCompiledBinary || !embeddedAddon) return [];
322
+ if (embeddedAddon.platformTag !== ctx.platformTag || embeddedAddon.version !== ctx.packageVersion) return [];
323
+
324
+ const extracted = [];
325
+ for (const embeddedFile of embeddedAddonCandidates(ctx.selectedVariant)) {
326
+ const targetPath = path.join(ctx.versionedDir, embeddedFile.filename);
327
+ if (fs.existsSync(targetPath)) {
328
+ // Guard against intra-version drift: a cached extraction written by an earlier
329
+ // build of the same version carries the same version sentinel but can expose a
330
+ // different native surface (e.g. a symbol added mid-cycle). The embedded addon
331
+ // is the source of truth, so reuse the cached file only when it matches the
332
+ // embedded payload size and re-extract otherwise.
333
+ const sizeOf = candidate => {
334
+ try {
335
+ return fs.statSync(candidate).size;
336
+ } catch {
337
+ return null;
338
+ }
339
+ };
340
+ if (cachedEmbeddedExtractionIsFresh({ targetPath, embeddedPath: embeddedFile.filePath, sizeOf })) {
341
+ extracted.push(targetPath);
342
+ continue;
341
343
  }
342
- };
343
- if (cachedEmbeddedExtractionIsFresh({ targetPath, embeddedPath: selectedEmbeddedFile.filePath, sizeOf })) {
344
- return targetPath;
345
344
  }
346
- }
347
-
348
- try {
349
- fs.mkdirSync(ctx.versionedDir, { recursive: true });
350
- } catch (err) {
351
- const message = err instanceof Error ? err.message : String(err);
352
- errors.push(`embedded addon dir: ${message}`);
353
- return null;
354
- }
355
345
 
356
- try {
357
- const buffer = fs.readFileSync(selectedEmbeddedFile.filePath);
358
- const tempPath = `${targetPath}.tmp.${process.pid}`;
359
- fs.writeFileSync(tempPath, buffer);
360
- fs.renameSync(tempPath, targetPath);
361
- return targetPath;
362
- } catch (err) {
363
- const message = err instanceof Error ? err.message : String(err);
364
- errors.push(`embedded addon write (${selectedEmbeddedFile.filename}): ${message}`);
365
- return null;
346
+ try {
347
+ fs.mkdirSync(ctx.versionedDir, { recursive: true });
348
+ const buffer = fs.readFileSync(embeddedFile.filePath);
349
+ const tempPath = `${targetPath}.tmp.${process.pid}`;
350
+ fs.writeFileSync(tempPath, buffer);
351
+ fs.renameSync(tempPath, targetPath);
352
+ extracted.push(targetPath);
353
+ } catch (err) {
354
+ const message = err instanceof Error ? err.message : String(err);
355
+ errors.push(`embedded addon write (${embeddedFile.filename}): ${message}`);
356
+ }
366
357
  }
358
+ return extracted;
367
359
  }
368
360
 
369
361
  /**
@@ -410,12 +402,22 @@ function maybeStageNodeModulesAddon(ctx, errors) {
410
402
  }
411
403
 
412
404
  function validateLoadedBindings(ctx, bindings, candidate) {
413
- if (typeof bindings[ctx.versionSentinelExport] === "function") return;
414
- throw new Error(
415
- `Loaded ${candidate} but it does not expose the @gajae-code/natives@${ctx.packageVersion} ` +
416
- `version sentinel \`${ctx.versionSentinelExport}\`. The .node file on disk is from a different ` +
417
- "release than this loader — reinstall to re-sync.",
418
- );
405
+ if (typeof bindings[ctx.versionSentinelExport] !== "function") {
406
+ throw new Error(
407
+ `Loaded ${candidate} but it does not expose the @gajae-code/natives@${ctx.packageVersion} ` +
408
+ `version sentinel \`${ctx.versionSentinelExport}\`. The .node file on disk is from a different ` +
409
+ "release than this loader — reinstall to re-sync.",
410
+ );
411
+ }
412
+ if (typeof bindings.__piNativesPublishOutcomeV1 !== "function") {
413
+ throw new Error(
414
+ `Loaded ${candidate} but it lacks retained-publish capability sentinel ` +
415
+ "`__piNativesPublishOutcomeV1`; trying the next compatible artifact.",
416
+ );
417
+ }
418
+ if (typeof bindings.renameNoReplacePath !== "function") {
419
+ throw new Error(`Loaded ${candidate} but it lacks required atomic publish capability \`renameNoReplacePath\`.`);
420
+ }
419
421
  }
420
422
 
421
423
  function buildHelpMessage(ctx) {
@@ -516,20 +518,30 @@ function initLoaderContext(require_) {
516
518
  };
517
519
  }
518
520
 
519
- export function loadNative() {
520
- const require_ = createRequire(import.meta.url);
521
- const ctx = initLoaderContext(require_);
521
+ /** Embedded standalone payloads are the complete trust boundary for their matching build. */
522
+ export function embeddedAddonIsAuthoritative(ctx, addon = embeddedAddon) {
523
+ return (
524
+ ctx.isCompiledBinary && addon?.platformTag === ctx.platformTag && addon.version === ctx.packageVersion
525
+ );
526
+ }
522
527
 
523
- const errors = [];
524
- const embeddedCandidate = maybeExtractEmbeddedAddon(ctx, errors);
525
- const stagedCandidate = embeddedCandidate ? null : maybeStageNodeModulesAddon(ctx, errors);
526
- const prepended = [embeddedCandidate, stagedCandidate].filter(c => typeof c === "string");
527
- const runtimeCandidates = prepended.length > 0 ? [...prepended, ...ctx.candidates] : ctx.candidates;
528
+ export function loadNative(options = {}) {
529
+ const require_ = options.requireCandidate ? null : createRequire(import.meta.url);
530
+ const ctx = options.context ?? initLoaderContext(require_);
528
531
 
532
+ const errors = [];
533
+ const embeddedCandidates = (options.extractEmbeddedAddons ?? maybeExtractEmbeddedAddons)(ctx, errors);
534
+ const embeddedIsAuthoritative = embeddedAddonIsAuthoritative(ctx);
535
+ const stagedCandidate =
536
+ embeddedCandidates.length > 0 || embeddedIsAuthoritative
537
+ ? null
538
+ : (options.stageNodeModulesAddon ?? maybeStageNodeModulesAddon)(ctx, errors);
539
+ const prepended = [...embeddedCandidates, stagedCandidate].filter(c => typeof c === "string");
540
+ const runtimeCandidates = embeddedIsAuthoritative ? prepended : prepended.length > 0 ? [...prepended, ...ctx.candidates] : ctx.candidates;
529
541
  const loaded = loadFromCandidates({
530
542
  candidates: runtimeCandidates,
531
- requireCandidate: candidate => require_(candidate),
532
- validateCandidate: (bindings, candidate) => validateLoadedBindings(ctx, bindings, candidate),
543
+ requireCandidate: options.requireCandidate ?? (candidate => require_(candidate)),
544
+ validateCandidate: options.validateCandidate ?? ((bindings, candidate) => validateLoadedBindings(ctx, bindings, candidate)),
533
545
  describeCandidate: candidate => candidate,
534
546
  });
535
547
  if (loaded.bindings) return loaded.bindings;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gajae-code/natives",
3
- "version": "0.11.7",
3
+ "version": "0.11.8",
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.11.7",
65
- "@gajae-code/natives-darwin-x64": "0.11.7",
66
- "@gajae-code/natives-linux-arm64": "0.11.7",
67
- "@gajae-code/natives-linux-x64": "0.11.7",
68
- "@gajae-code/natives-win32-x64": "0.11.7"
64
+ "@gajae-code/natives-darwin-arm64": "0.11.8",
65
+ "@gajae-code/natives-darwin-x64": "0.11.8",
66
+ "@gajae-code/natives-linux-arm64": "0.11.8",
67
+ "@gajae-code/natives-linux-x64": "0.11.8",
68
+ "@gajae-code/natives-win32-x64": "0.11.8"
69
69
  },
70
70
  "exports": {
71
71
  ".": {