@gajae-code/natives 0.11.1 → 0.11.3

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
@@ -114,8 +114,9 @@ export declare class NotificationServer {
114
114
  /** Register the reply callback. Must be called before [`Self::start`]. */
115
115
  onReply(callback: (err: null | Error, reply: ReplyEvent) => void): void
116
116
  /**
117
- * Register the inbound-message callback (free-text injections and in-thread
118
- * config commands). Must be called before [`Self::start`].
117
+ * Register the authenticated inbound-message callback (free-text,
118
+ * side-question request/cancel, and in-thread config/control commands).
119
+ * Must be called before [`Self::start`].
119
120
  */
120
121
  onInbound(callback: (err: null | Error, msg: InboundEvent) => void): void
121
122
  /**
@@ -123,6 +124,11 @@ export declare class NotificationServer {
123
124
  * [`Self::start`].
124
125
  */
125
126
  onSdkFrame(callback: (err: null | Error, frame: SdkFrameEvent) => void): void
127
+ /**
128
+ * Register the negotiated-capabilities callback. Must be called before
129
+ * [`Self::start`].
130
+ */
131
+ onNegotiatedCapabilities(callback: (err: null | Error, connectionId: string, capabilities: string[]) => void): void
126
132
  /**
127
133
  * Register the connection-close callback. Must be called before
128
134
  * [`Self::start`].
@@ -176,14 +182,30 @@ export declare class NotificationServer {
176
182
  /**
177
183
  * Broadcast an ephemeral threaded-session frame. `frame_json` is a JSON
178
184
  * `ServerMessage` (e.g. `identity_header`, `context_update`, `turn_stream`,
179
- * `image_attachment`, `session_closed`, `config_update`, `hello`). Not
180
- * buffered for replay.
185
+ * `ephemeral_turn_result`, `image_attachment`, `session_closed`,
186
+ * `config_update`, `hello`). Not buffered for replay.
181
187
  *
182
188
  * # Errors
183
189
  * Fails if not started or `frame_json` is not a valid `ServerMessage`.
184
190
  */
185
191
  pushFrame(frameJson: string): void
186
- /** Send raw JSON to one connected v3 SDK client. */
192
+ /**
193
+ * Broadcast a TypeScript-constructed turn frame without re-parsing JSON.
194
+ * External frames must continue through [`Self::push_frame`] for serde
195
+ * validation.
196
+ */
197
+ pushTurnStreamUnchecked(sessionId: string, phase: string, text: string, finalAnswer?: boolean | undefined | null, messageRef?: string | undefined | null): void
198
+ /**
199
+ * Broadcast a file attachment from raw N-API bytes, encoding the unchanged
200
+ * base64 wire field only in Rust.
201
+ */
202
+ pushFileAttachmentUnchecked(sessionId: string, name: string, mime: string | undefined | null, data: Buffer, caption?: string | undefined | null): void
203
+ /**
204
+ * Return counters guarding the known-good frame crossing against
205
+ * regressions.
206
+ */
207
+ knownGoodFrameStats(): KnownGoodFrameStats
208
+ /** Send a validated, bounded JSON envelope to one connected v3 SDK client. */
187
209
  sendTo(connectionId: string, json: string): void
188
210
  /**
189
211
  * Publish a replayable `session_ready` readiness signal. `ready_json` is a
@@ -351,7 +373,7 @@ export declare class Shell {
351
373
  * `packages/natives/native/index.js` (which derives the name from
352
374
  * `package.json#version`).
353
375
  */
354
- export declare function __piNativesV0_11_1(): void
376
+ export declare function __piNativesV0_11_3(): void
355
377
 
356
378
  /**
357
379
  * Apply conservative pre-execution rewrites to a bash command.
@@ -362,6 +384,8 @@ export declare function __piNativesV0_11_1(): void
362
384
  */
363
385
  export declare function applyBashFixups(command: string): BashFixupResult
364
386
 
387
+ export declare function applyOwnerOnlyPathSecurity(path: string, kind: "directory" | "file"): NativeOwnerOnlySecurityResult
388
+
365
389
  /** Typed terminal acknowledgement result returned by acknowledgement promises. */
366
390
  export interface AskSelectedAckOutcomeEvent {
367
391
  status: string
@@ -571,6 +595,8 @@ export interface BuildInfo {
571
595
  languageSet: string
572
596
  }
573
597
 
598
+ export declare function canonicalExistingDirectoryIdentity(path: string | Uint8Array): NativeCanonicalDirectoryIdentity
599
+
574
600
  /** Clipboard image payload encoded as PNG bytes. */
575
601
  export interface ClipboardImage {
576
602
  /** PNG-encoded image bytes. */
@@ -683,6 +709,29 @@ export declare enum Ellipsis {
683
709
  */
684
710
  export declare function encodeSixel(bytes: Uint8Array, targetWidthPx: number, targetHeightPx: number): string
685
711
 
712
+ /**
713
+ * Remove an already durably planned detached directory only when a fresh
714
+ * descriptor-relative snapshot exactly equals the persisted snapshot. The
715
+ * caller-planned root remains in place while its opened descriptor is
716
+ * authoritative throughout recursive removal.
717
+ */
718
+ export declare function exactRemoveDirectoryTree(path: string, snapshot: NativeDirectoryTreeSnapshot): NativeExactUnlinkResult
719
+
720
+ /**
721
+ * Restore only the detached object that still has the supplied platform
722
+ * identity. The detached and original paths must retain the same validated
723
+ * parent, and restoration never replaces an existing original path.
724
+ */
725
+ export declare function exactRestore(detachedPath: string, originalPath: string, identity: NativeExactFileIdentity): NativeExactUnlinkResult
726
+
727
+ /**
728
+ * Delete only the regular file that still has the supplied platform identity.
729
+ *
730
+ * This never follows a symlink or reparse point in the target path and reports
731
+ * validation failures as typed results rather than deleting a replacement.
732
+ */
733
+ export declare function exactUnlink(path: string, identity: NativeExactFileIdentity): NativeExactUnlinkResult
734
+
686
735
  /**
687
736
  * Execute a brush shell command.
688
737
  *
@@ -1045,26 +1094,51 @@ export interface HtmlToMarkdownOptions {
1045
1094
  }
1046
1095
 
1047
1096
  /**
1048
- * An inbound message forwarded to the TypeScript host: a free-text injection,
1049
- * in-thread config command, or deterministic control command.
1097
+ * An authenticated inbound message forwarded to the TypeScript host: free-text
1098
+ * injection, ephemeral side-question request/cancel, in-thread config command,
1099
+ * or deterministic control command.
1050
1100
  */
1051
1101
  export interface InboundEvent {
1052
- /** Inbound kind (`user_message`, `config_command`, or `control_command`). */
1102
+ /**
1103
+ * Inbound kind (`user_message`, `ephemeral_turn`,
1104
+ * `ephemeral_turn_cancel`, `config_command`, or `control_command`).
1105
+ */
1053
1106
  kind: string
1107
+ /**
1108
+ * Server-authenticated identity of the WebSocket connection that delivered
1109
+ * this event.
1110
+ */
1111
+ connectionId: string
1054
1112
  /** The session this inbound belongs to. */
1055
1113
  sessionId: string
1056
- /** Free-text body (`user_message` only). */
1114
+ /** Free-text body (`user_message` or `ephemeral_turn` only). */
1057
1115
  text?: string
1058
- /** Telegram update id for dedupe (`user_message` only). */
1116
+ /**
1117
+ * Telegram update id for dedupe (`user_message`, `ephemeral_turn`, or
1118
+ * `ephemeral_turn_cancel` only).
1119
+ */
1059
1120
  updateId?: number
1060
- /** Originating thread/topic id (`user_message` only). */
1121
+ /**
1122
+ * Originating thread/topic id (`user_message`, `ephemeral_turn`, or
1123
+ * `ephemeral_turn_cancel` only).
1124
+ */
1061
1125
  threadId?: string
1126
+ /**
1127
+ * Originating Telegram message id (`ephemeral_turn` and
1128
+ * `ephemeral_turn_cancel` only).
1129
+ */
1130
+ messageId?: number
1062
1131
  /** Requested verbosity `"lean"|"verbose"` (`config_command` only). */
1063
1132
  verbosity?: string
1064
1133
  /** Requested redaction state (`config_command` only). */
1065
1134
  redact?: boolean
1066
- /** Client-generated request id (`control_command` only). */
1135
+ /**
1136
+ * Client-generated request id (`ephemeral_turn`, `ephemeral_turn_cancel`,
1137
+ * or `control_command` only).
1138
+ */
1067
1139
  requestId?: string
1140
+ /** Cancellation reason (`ephemeral_turn_cancel` only). */
1141
+ reason?: string
1068
1142
  /** JSON-encoded command payload (`control_command` only). */
1069
1143
  commandJson?: string
1070
1144
  /**
@@ -1216,6 +1290,19 @@ export declare enum KeyEventType {
1216
1290
  Release = 3
1217
1291
  }
1218
1292
 
1293
+ /** Observable counters for the internal known-good N-API frame lane. */
1294
+ export interface KnownGoodFrameStats {
1295
+ /** Frames constructed as `TurnStream` without parsing a JSON string. */
1296
+ knownGoodTurnStreamFrames: number
1297
+ /** JSON serde parses of externally supplied `turn_stream` frames. */
1298
+ turnStreamSerdeValidationParses: number
1299
+ /**
1300
+ * Base64 characters encoded in Rust for `file_attachment` frames (the JS
1301
+ * side crosses raw `Buffer` bytes and never allocates the base64 string).
1302
+ */
1303
+ fileAttachmentRustBase64Chars: number
1304
+ }
1305
+
1219
1306
  /** A lifecycle request forwarded to the TypeScript daemon for orchestration. */
1220
1307
  export interface LifecycleRequestEvent {
1221
1308
  /** One of `"session_create"`, `"session_close"`, `"session_resume"`. */
@@ -1415,6 +1502,103 @@ export interface MinimizerResult {
1415
1502
 
1416
1503
  export declare function nativeBuildInfo(): BuildInfo
1417
1504
 
1505
+ /** Result of resolving an existing directory to its stable platform identity. */
1506
+ export type NativeCanonicalDirectoryIdentity =
1507
+ | { ok: true; platform: "posix" | "win32"; canonicalPath: string; code?: never }
1508
+ | {
1509
+ ok: false;
1510
+ platform?: never;
1511
+ canonicalPath?: never;
1512
+ code: "not_found" | "not_directory" | "not_utf8" | "network_unsupported" | "identity_unavailable" | "io_error";
1513
+ }
1514
+
1515
+ /**
1516
+ * A deterministic, no-follow description of a directory tree. `relative_path`
1517
+ * is UTF-8, uses `/` separators, and is empty only for the root entry.
1518
+ */
1519
+ export interface NativeDirectoryTreeEntry {
1520
+ relativePath: string
1521
+ kind: string
1522
+ dev: string
1523
+ ino: string
1524
+ size: string
1525
+ mtimeNs: string
1526
+ sha256?: string
1527
+ }
1528
+
1529
+ export interface NativeDirectoryTreeResult {
1530
+ ok: boolean
1531
+ code?: string
1532
+ snapshot?: NativeDirectoryTreeSnapshot
1533
+ }
1534
+
1535
+ /**
1536
+ * Stable evidence returned by `snapshot_directory_tree` and consumed verbatim
1537
+ * by `exact_remove_directory_tree`.
1538
+ */
1539
+ export interface NativeDirectoryTreeSnapshot {
1540
+ rootDev: string
1541
+ rootIno: string
1542
+ entries: Array<NativeDirectoryTreeEntry>
1543
+ }
1544
+
1545
+ /**
1546
+ * Caller-supplied identity and preauthorized quarantine evidence for exact
1547
+ * deletion.
1548
+ */
1549
+ export interface NativeExactFileIdentity {
1550
+ dev: bigint
1551
+ ino: bigint
1552
+ size: bigint
1553
+ mtimeNs: bigint
1554
+ /**
1555
+ * When true, atomically detach a directory rather than deleting a regular
1556
+ * file.
1557
+ */
1558
+ directory?: boolean
1559
+ /**
1560
+ * Keep a regular file in quarantine after its identity has been verified
1561
+ * instead of unlinking it. This makes cross-device retirement recoverable.
1562
+ */
1563
+ detachOnly?: boolean
1564
+ /**
1565
+ * A caller-persisted, single-component no-replace quarantine destination.
1566
+ * Required for every exact deletion so authority survives a post-detach
1567
+ * crash.
1568
+ */
1569
+ quarantineName?: string
1570
+ /**
1571
+ * SHA-256 of regular-file bytes. Required for regular-file deletion and
1572
+ * verified from the detached object before unlinking it.
1573
+ */
1574
+ sha256?: string
1575
+ }
1576
+
1577
+ /** Typed result of an identity-bound regular-file deletion or directory detach. */
1578
+ export interface NativeExactUnlinkResult {
1579
+ ok: boolean
1580
+ code?: string
1581
+ detachedPath?: string
1582
+ }
1583
+
1584
+ /** Result of applying or checking owner-only path security. */
1585
+ export type NativeOwnerOnlySecurityResult =
1586
+ | { ok: true; code?: never }
1587
+ | {
1588
+ ok: false;
1589
+ code:
1590
+ | "not_found"
1591
+ | "not_directory"
1592
+ | "network_unsupported"
1593
+ | "reparse_point"
1594
+ | "acl_unavailable"
1595
+ | "acl_apply_failed"
1596
+ | "acl_verify_failed"
1597
+ | "identity_unavailable"
1598
+ | "owner_mismatch"
1599
+ | "io_error";
1600
+ }
1601
+
1418
1602
  /** Bound endpoint info returned from [`NotificationServer::start`]. */
1419
1603
  export interface NotificationEndpoint {
1420
1604
  /** Bind host (loopback). */
@@ -1543,6 +1727,8 @@ export declare function ptyTimeoutCount(): bigint
1543
1727
  */
1544
1728
  export declare function readImageFromClipboard(): Promise<ClipboardImage | undefined | null>
1545
1729
 
1730
+ export declare function renameNoReplacePath(sourcePath: string, destinationPath: string): NativeExactUnlinkResult
1731
+
1546
1732
  /** A client reply forwarded to the TypeScript host for gate resolution. */
1547
1733
  export interface ReplyEvent {
1548
1734
  /**
@@ -1698,6 +1884,13 @@ export interface SliceResult {
1698
1884
  */
1699
1885
  export declare function sliceWithWidth(line: string, startCol: number, length: number, strict: boolean | undefined | null, tabWidth: number): SliceResult
1700
1886
 
1887
+ /**
1888
+ * Capture a deterministic, descriptor-relative snapshot of a regular-file and
1889
+ * directory-only tree. Symlinks, special files, non-UTF-8 names, and topology
1890
+ * changes are rejected rather than followed.
1891
+ */
1892
+ export declare function snapshotDirectoryTree(path: string): NativeDirectoryTreeResult
1893
+
1701
1894
  export declare function summarizeCode(options: SummaryOptions): SummaryResult
1702
1895
 
1703
1896
  export interface SummaryOptions {
@@ -1749,6 +1942,8 @@ export declare function truncateLinesToWidth(lines: Array<string>, maxWidth: num
1749
1942
 
1750
1943
  export declare function truncateToWidth(text: string, maxWidth: number, ellipsisKind: Ellipsis | undefined | null, pad: boolean | undefined | null, tabWidth: number): string
1751
1944
 
1945
+ export declare function verifyOwnerOnlyPathSecurity(path: string, kind: "directory" | "file"): NativeOwnerOnlySecurityResult
1946
+
1752
1947
  /**
1753
1948
  * Calculate visible width of text, excluding ANSI escape sequences.
1754
1949
  *
package/native/index.js CHANGED
@@ -27,15 +27,20 @@ export const PtySession = nativeBindings.PtySession;
27
27
  export const Shell = nativeBindings.Shell;
28
28
 
29
29
  // functions
30
- export const __piNativesV0_11_1 = nativeBindings.__piNativesV0_11_1;
30
+ export const __piNativesV0_11_3 = nativeBindings.__piNativesV0_11_3;
31
31
  export const applyBashFixups = nativeBindings.applyBashFixups;
32
+ export const applyOwnerOnlyPathSecurity = nativeBindings.applyOwnerOnlyPathSecurity;
32
33
  export const astEdit = nativeBindings.astEdit;
33
34
  export const astGrep = nativeBindings.astGrep;
35
+ export const canonicalExistingDirectoryIdentity = nativeBindings.canonicalExistingDirectoryIdentity;
34
36
  export const computerScreenshot = nativeBindings.computerScreenshot;
35
37
  export const copyToClipboard = nativeBindings.copyToClipboard;
36
38
  export const detectMacOSAppearance = nativeBindings.detectMacOSAppearance;
37
39
  export const diffLines = nativeBindings.diffLines;
38
40
  export const encodeSixel = nativeBindings.encodeSixel;
41
+ export const exactRemoveDirectoryTree = nativeBindings.exactRemoveDirectoryTree;
42
+ export const exactRestore = nativeBindings.exactRestore;
43
+ export const exactUnlink = nativeBindings.exactUnlink;
39
44
  export const executeShell = nativeBindings.executeShell;
40
45
  export const extractSegments = nativeBindings.extractSegments;
41
46
  export const fuzzyFind = nativeBindings.fuzzyFind;
@@ -67,12 +72,15 @@ export const parseKey = nativeBindings.parseKey;
67
72
  export const parseKittySequence = nativeBindings.parseKittySequence;
68
73
  export const ptyTimeoutCount = nativeBindings.ptyTimeoutCount;
69
74
  export const readImageFromClipboard = nativeBindings.readImageFromClipboard;
75
+ export const renameNoReplacePath = nativeBindings.renameNoReplacePath;
70
76
  export const search = nativeBindings.search;
71
77
  export const sliceWithWidth = nativeBindings.sliceWithWidth;
78
+ export const snapshotDirectoryTree = nativeBindings.snapshotDirectoryTree;
72
79
  export const summarizeCode = nativeBindings.summarizeCode;
73
80
  export const supportsLanguage = nativeBindings.supportsLanguage;
74
81
  export const truncateLinesToWidth = nativeBindings.truncateLinesToWidth;
75
82
  export const truncateToWidth = nativeBindings.truncateToWidth;
83
+ export const verifyOwnerOnlyPathSecurity = nativeBindings.verifyOwnerOnlyPathSecurity;
76
84
  export const visibleWidth = nativeBindings.visibleWidth;
77
85
  export const visibleWidths = nativeBindings.visibleWidths;
78
86
  export const wrapTextWithAnsi = nativeBindings.wrapTextWithAnsi;
@@ -71,4 +71,12 @@ export interface LoadFromCandidatesResult<T> {
71
71
 
72
72
  export function loadFromCandidates<T>(input: LoadFromCandidatesInput<T>): LoadFromCandidatesResult<T>;
73
73
 
74
+ export interface CachedEmbeddedExtractionIsFreshInput {
75
+ targetPath: string;
76
+ embeddedPath: string;
77
+ sizeOf: (path: string) => number | null;
78
+ }
79
+
80
+ export function cachedEmbeddedExtractionIsFresh(input: CachedEmbeddedExtractionIsFreshInput): boolean;
81
+
74
82
  export function loadNative(): Record<string, unknown>;
@@ -223,6 +223,23 @@ export function loadFromCandidates({ candidates, requireCandidate, validateCandi
223
223
  return { bindings: null, errors };
224
224
  }
225
225
 
226
+ /**
227
+ * Decide whether a previously extracted embedded addon may be reused. A cached
228
+ * extraction from an earlier build of the same version carries the same version
229
+ * sentinel yet can expose a different native surface, so it is fresh only when
230
+ * its byte size matches the embedded payload. `sizeOf` returns the byte size of
231
+ * a path, or `null` when it cannot be inspected.
232
+ * @param {{ targetPath: string; embeddedPath: string; sizeOf: (path: string) => number | null }} input
233
+ * @returns {boolean}
234
+ */
235
+ export function cachedEmbeddedExtractionIsFresh({ targetPath, embeddedPath, sizeOf }) {
236
+ const cachedSize = sizeOf(targetPath);
237
+ if (cachedSize === null) return false;
238
+ const embeddedSize = sizeOf(embeddedPath);
239
+ if (embeddedSize === null) return false;
240
+ return cachedSize === embeddedSize;
241
+ }
242
+
226
243
  // =========================================================================
227
244
  // Side-effectful loader. Everything below runs only when `loadNative()` is
228
245
  // called from `native/index.js` — tests that only import the pure helpers
@@ -310,7 +327,23 @@ function maybeExtractEmbeddedAddon(ctx, errors) {
310
327
  const selectedEmbeddedFile = selectEmbeddedAddonFile(ctx.selectedVariant);
311
328
  if (!selectedEmbeddedFile) return null;
312
329
  const targetPath = path.join(ctx.versionedDir, selectedEmbeddedFile.filename);
313
- if (fs.existsSync(targetPath)) return targetPath;
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;
341
+ }
342
+ };
343
+ if (cachedEmbeddedExtractionIsFresh({ targetPath, embeddedPath: selectedEmbeddedFile.filePath, sizeOf })) {
344
+ return targetPath;
345
+ }
346
+ }
314
347
 
315
348
  try {
316
349
  fs.mkdirSync(ctx.versionedDir, { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gajae-code/natives",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
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.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"
64
+ "@gajae-code/natives-darwin-arm64": "0.11.3",
65
+ "@gajae-code/natives-darwin-x64": "0.11.3",
66
+ "@gajae-code/natives-linux-arm64": "0.11.3",
67
+ "@gajae-code/natives-linux-x64": "0.11.3",
68
+ "@gajae-code/natives-win32-x64": "0.11.3"
69
69
  },
70
70
  "exports": {
71
71
  ".": {