@oh-my-pi/pi-natives 18.17.0 → 18.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native/index.js +27 -27
- package/native/loader-state.d.ts +23 -0
- package/native/loader-state.js +143 -0
- package/package.json +7 -7
package/native/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadNative, missingNativeExport } from "./loader-state.js";
|
|
1
|
+
import { loadNative, missingNativeClass, missingNativeExport } from "./loader-state.js";
|
|
2
2
|
import { adaptDesktopSession } from "./desktop-adapter.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -23,33 +23,33 @@ import { adaptDesktopSession } from "./desktop-adapter.js";
|
|
|
23
23
|
const nativeBindings = loadNative();
|
|
24
24
|
// --- generated native exports (do not edit) ---
|
|
25
25
|
// classes
|
|
26
|
-
export const AudioCapture = nativeBindings.AudioCapture;
|
|
27
|
-
export const AudioPlayback = nativeBindings.AudioPlayback;
|
|
26
|
+
export const AudioCapture = nativeBindings.AudioCapture ?? missingNativeClass("AudioCapture");
|
|
27
|
+
export const AudioPlayback = nativeBindings.AudioPlayback ?? missingNativeClass("AudioPlayback");
|
|
28
28
|
export const DesktopSession = adaptDesktopSession(nativeBindings.DesktopSession);
|
|
29
|
-
export const DiffStream = nativeBindings.DiffStream;
|
|
30
|
-
export const EditSession = nativeBindings.EditSession;
|
|
31
|
-
export const EditStore = nativeBindings.EditStore;
|
|
32
|
-
export const FileLock = nativeBindings.FileLock;
|
|
33
|
-
export const HighlightStream = nativeBindings.HighlightStream;
|
|
34
|
-
export const IpcConnection = nativeBindings.IpcConnection;
|
|
35
|
-
export const IpcServer = nativeBindings.IpcServer;
|
|
36
|
-
export const LineMatcher = nativeBindings.LineMatcher;
|
|
37
|
-
export const LiveWebRtcPeer = nativeBindings.LiveWebRtcPeer;
|
|
38
|
-
export const MacAppearanceObserver = nativeBindings.MacAppearanceObserver;
|
|
39
|
-
export const MacOSFrontApp = nativeBindings.MacOSFrontApp;
|
|
40
|
-
export const NativeOAuthCallback = nativeBindings.NativeOAuthCallback;
|
|
41
|
-
export const PipeStreams = nativeBindings.PipeStreams;
|
|
42
|
-
export const PowerAssertion = nativeBindings.PowerAssertion;
|
|
43
|
-
export const Process = nativeBindings.Process;
|
|
44
|
-
export const PtySession = nativeBindings.PtySession;
|
|
45
|
-
export const RunTrace = nativeBindings.RunTrace;
|
|
46
|
-
export const Shell = nativeBindings.Shell;
|
|
47
|
-
export const ShellVisualization = nativeBindings.ShellVisualization;
|
|
48
|
-
export const TextPredictor = nativeBindings.TextPredictor;
|
|
49
|
-
export const TtyWriter = nativeBindings.TtyWriter;
|
|
50
|
-
export const VcsGitRepo = nativeBindings.VcsGitRepo;
|
|
51
|
-
export const VcsJjWorkspace = nativeBindings.VcsJjWorkspace;
|
|
52
|
-
export const VcsRepo = nativeBindings.VcsRepo;
|
|
29
|
+
export const DiffStream = nativeBindings.DiffStream ?? missingNativeClass("DiffStream");
|
|
30
|
+
export const EditSession = nativeBindings.EditSession ?? missingNativeClass("EditSession");
|
|
31
|
+
export const EditStore = nativeBindings.EditStore ?? missingNativeClass("EditStore");
|
|
32
|
+
export const FileLock = nativeBindings.FileLock ?? missingNativeClass("FileLock");
|
|
33
|
+
export const HighlightStream = nativeBindings.HighlightStream ?? missingNativeClass("HighlightStream");
|
|
34
|
+
export const IpcConnection = nativeBindings.IpcConnection ?? missingNativeClass("IpcConnection");
|
|
35
|
+
export const IpcServer = nativeBindings.IpcServer ?? missingNativeClass("IpcServer");
|
|
36
|
+
export const LineMatcher = nativeBindings.LineMatcher ?? missingNativeClass("LineMatcher");
|
|
37
|
+
export const LiveWebRtcPeer = nativeBindings.LiveWebRtcPeer ?? missingNativeClass("LiveWebRtcPeer");
|
|
38
|
+
export const MacAppearanceObserver = nativeBindings.MacAppearanceObserver ?? missingNativeClass("MacAppearanceObserver");
|
|
39
|
+
export const MacOSFrontApp = nativeBindings.MacOSFrontApp ?? missingNativeClass("MacOSFrontApp");
|
|
40
|
+
export const NativeOAuthCallback = nativeBindings.NativeOAuthCallback ?? missingNativeClass("NativeOAuthCallback");
|
|
41
|
+
export const PipeStreams = nativeBindings.PipeStreams ?? missingNativeClass("PipeStreams");
|
|
42
|
+
export const PowerAssertion = nativeBindings.PowerAssertion ?? missingNativeClass("PowerAssertion");
|
|
43
|
+
export const Process = nativeBindings.Process ?? missingNativeClass("Process");
|
|
44
|
+
export const PtySession = nativeBindings.PtySession ?? missingNativeClass("PtySession");
|
|
45
|
+
export const RunTrace = nativeBindings.RunTrace ?? missingNativeClass("RunTrace");
|
|
46
|
+
export const Shell = nativeBindings.Shell ?? missingNativeClass("Shell");
|
|
47
|
+
export const ShellVisualization = nativeBindings.ShellVisualization ?? missingNativeClass("ShellVisualization");
|
|
48
|
+
export const TextPredictor = nativeBindings.TextPredictor ?? missingNativeClass("TextPredictor");
|
|
49
|
+
export const TtyWriter = nativeBindings.TtyWriter ?? missingNativeClass("TtyWriter");
|
|
50
|
+
export const VcsGitRepo = nativeBindings.VcsGitRepo ?? missingNativeClass("VcsGitRepo");
|
|
51
|
+
export const VcsJjWorkspace = nativeBindings.VcsJjWorkspace ?? missingNativeClass("VcsJjWorkspace");
|
|
52
|
+
export const VcsRepo = nativeBindings.VcsRepo ?? missingNativeClass("VcsRepo");
|
|
53
53
|
|
|
54
54
|
// functions
|
|
55
55
|
export const __ompInstallTokioRuntime = nativeBindings.__ompInstallTokioRuntime ?? missingNativeExport("__ompInstallTokioRuntime");
|
package/native/loader-state.d.ts
CHANGED
|
@@ -145,6 +145,29 @@ export function missingNativeExport(
|
|
|
145
145
|
addon?: NativeAddonStatus | null,
|
|
146
146
|
): (() => never) | undefined;
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Stub for a class the addon does not provide: `undefined` on a current addon,
|
|
150
|
+
* a class whose constructor throws on a stale one.
|
|
151
|
+
*/
|
|
152
|
+
export function missingNativeClass(
|
|
153
|
+
symbolName: string,
|
|
154
|
+
addon?: NativeAddonStatus | null,
|
|
155
|
+
): (new (...args: unknown[]) => never) | undefined;
|
|
156
|
+
|
|
157
|
+
/** Notice a workspace tree prints when its addon is from another release. */
|
|
158
|
+
export function staleWorkspaceAddonNotice(addon: NativeAddonStatus): string;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Write {@link staleWorkspaceAddonNotice} once, from the main thread, and only
|
|
162
|
+
* on a workspace load. The latch is per loaded copy of this module. Returns
|
|
163
|
+
* whether this call wrote it.
|
|
164
|
+
*/
|
|
165
|
+
export function reportStaleWorkspaceAddon(
|
|
166
|
+
ctx: { isWorkspaceLoad: boolean },
|
|
167
|
+
addon: NativeAddonStatus,
|
|
168
|
+
deps?: { write?: (text: string) => void; state?: { reported: boolean } },
|
|
169
|
+
): boolean;
|
|
170
|
+
|
|
148
171
|
/** Actionable text for {@link missingNativeExport}. */
|
|
149
172
|
export function missingNativeExportMessage(symbolName: string, addon?: NativeAddonStatus | null): string;
|
|
150
173
|
|
package/native/loader-state.js
CHANGED
|
@@ -693,6 +693,48 @@ export function missingNativeExport(symbolName, addon = loadedAddon) {
|
|
|
693
693
|
};
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
+
/**
|
|
697
|
+
* Static reads a {@link missingNativeClass} stand-in answers normally rather
|
|
698
|
+
* than throwing: the function-object intrinsics every caller and inspector
|
|
699
|
+
* touches, and `then`, so an accidental `await` on the export resolves to the
|
|
700
|
+
* class instead of exploding somewhere unrelated.
|
|
701
|
+
*/
|
|
702
|
+
const CLASS_STUB_PLAIN_STATICS = new Set(["prototype", "name", "length", "constructor", "then"]);
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Class counterpart of {@link missingNativeExport}: a stand-in that fails with
|
|
706
|
+
* the addon's identity attached instead of as a bare `undefined` export, which
|
|
707
|
+
* surfaces as `undefined is not a constructor` at the callsite and names
|
|
708
|
+
* nothing.
|
|
709
|
+
*
|
|
710
|
+
* Same staleness rule as the function stub: only a stale addon gets a
|
|
711
|
+
* stand-in, so `typeof native.Thing === "function"` capability probes against
|
|
712
|
+
* a current addon keep their meaning.
|
|
713
|
+
*
|
|
714
|
+
* Several of these classes are entered through a static rather than a
|
|
715
|
+
* constructor — `FileLock.tryAcquire`, `IpcServer.listen`, `Process.fromPid`,
|
|
716
|
+
* `PowerAssertion.start` — so the stand-in answers any other static read with
|
|
717
|
+
* the same diagnostic. Only a stale addon reaches it, and only for a class
|
|
718
|
+
* that addon does not carry, so nothing on a healthy tree meets a proxy.
|
|
719
|
+
* @param {string} symbolName
|
|
720
|
+
* @param {ReturnType<typeof nativeAddonStatus>} [addon]
|
|
721
|
+
* @returns {(new (...args: unknown[]) => never) | undefined}
|
|
722
|
+
*/
|
|
723
|
+
export function missingNativeClass(symbolName, addon = loadedAddon) {
|
|
724
|
+
if (!addon?.stale) return undefined;
|
|
725
|
+
const stub = class MissingNativeClass {
|
|
726
|
+
constructor() {
|
|
727
|
+
throw new Error(missingNativeExportMessage(symbolName, addon));
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
return new Proxy(stub, {
|
|
731
|
+
get(target, key, receiver) {
|
|
732
|
+
if (typeof key === "symbol" || CLASS_STUB_PLAIN_STATICS.has(key)) return Reflect.get(target, key, receiver);
|
|
733
|
+
throw new Error(missingNativeExportMessage(`${symbolName}.${key}`, addon));
|
|
734
|
+
},
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
696
738
|
/**
|
|
697
739
|
* Actionable text for {@link missingNativeExport}. `addon` is injectable so the
|
|
698
740
|
* wording can be pinned without a stale `.node` on disk.
|
|
@@ -715,6 +757,106 @@ export function missingNativeExportMessage(symbolName, addon = loadedAddon) {
|
|
|
715
757
|
);
|
|
716
758
|
}
|
|
717
759
|
|
|
760
|
+
/**
|
|
761
|
+
* One-line notice for a workspace tree running an addon from another release.
|
|
762
|
+
*
|
|
763
|
+
* The missing-symbol stubs only speak when a caller touches a symbol the addon
|
|
764
|
+
* does not carry. A symbol it does carry runs that release's behaviour — an
|
|
765
|
+
* older shell, an older grep — with no marker at all, so this notice is the
|
|
766
|
+
* only provenance those failures get.
|
|
767
|
+
*
|
|
768
|
+
* `stale` is any release difference, so the wording must not assume the addon
|
|
769
|
+
* is the older side.
|
|
770
|
+
* @param {NonNullable<ReturnType<typeof nativeAddonStatus>>} addon
|
|
771
|
+
* @returns {string}
|
|
772
|
+
*/
|
|
773
|
+
export function staleWorkspaceAddonNotice(addon) {
|
|
774
|
+
const loaded = addon.version ? `@oh-my-pi/pi-natives@${addon.version}` : "an addon without a release stamp";
|
|
775
|
+
return (
|
|
776
|
+
`[pi-natives] ${addon.path} is ${loaded}, but this checkout is @${addon.packageVersion}. ` +
|
|
777
|
+
"Symbols this checkout expects may be missing and shared ones may behave differently; " +
|
|
778
|
+
"run `bun run build:native` before trusting results from this tree.\n"
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Whether the copy of this module running on the current thread has already
|
|
784
|
+
* written the mismatched-addon notice. One per loaded copy, which is one per
|
|
785
|
+
* main thread in every shape this package ships; the write itself is confined
|
|
786
|
+
* to the main thread by {@link onMainThread}.
|
|
787
|
+
*/
|
|
788
|
+
const staleNoticeState = { reported: false };
|
|
789
|
+
|
|
790
|
+
/** Memoized answer from {@link onMainThread}; `undefined` until first asked. */
|
|
791
|
+
let mainThreadAnswer;
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* True on a process's main thread, false inside a worker thread.
|
|
795
|
+
*
|
|
796
|
+
* Resolved through `createRequire` on first use rather than imported at module
|
|
797
|
+
* scope: `packages/coding-agent/src/cli.ts` keeps `node:worker_threads` out of
|
|
798
|
+
* ordinary startup on purpose, and this module is reached statically from
|
|
799
|
+
* `sqlite-locks` on launches that never load an addon. Asking only when a
|
|
800
|
+
* mismatch has already been found keeps that boundary. A runtime without the
|
|
801
|
+
* builtin answers "main thread", which is the safe side: the notice prints.
|
|
802
|
+
* @returns {boolean}
|
|
803
|
+
*/
|
|
804
|
+
function onMainThread() {
|
|
805
|
+
if (mainThreadAnswer === undefined) {
|
|
806
|
+
try {
|
|
807
|
+
mainThreadAnswer = createRequire(import.meta.url)("node:worker_threads").isMainThread !== false;
|
|
808
|
+
} catch {
|
|
809
|
+
mainThreadAnswer = true;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return mainThreadAnswer;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Write {@link staleWorkspaceAddonNotice} once, from the main thread, when a
|
|
817
|
+
* workspace tree loads an addon of another release.
|
|
818
|
+
*
|
|
819
|
+
* Three guards, each load-bearing.
|
|
820
|
+
*
|
|
821
|
+
* Main thread only, because fd 2 is shared and a worker reaches `loadNative()`
|
|
822
|
+
* long after interactive mode owns the terminal — the subagent, JS-eval and
|
|
823
|
+
* tab workers all start then, so a per-worker write paints into a rendered
|
|
824
|
+
* frame. The main thread has already said it at startup; a worker repeating it
|
|
825
|
+
* adds nothing and costs the display. A worker *process* keeps the notice: it
|
|
826
|
+
* has its own main thread, and `subprocess/worker-client.ts` captures its
|
|
827
|
+
* stderr to a file rather than the terminal.
|
|
828
|
+
*
|
|
829
|
+
* Latched, because `loadNative()` is not a startup-only call: the lazy
|
|
830
|
+
* entrypoints in `clipboard.js` and friends call it on every invocation.
|
|
831
|
+
*
|
|
832
|
+
* Workspace loads only, because {@link validateLoadedBindings} has a second
|
|
833
|
+
* success path — it accepts a pre-sentinel addon on an installed load
|
|
834
|
+
* (`isCompatiblePreSentinelNativeAddon`), which reports no release and so
|
|
835
|
+
* counts as `stale`. That user has no repository to build.
|
|
836
|
+
*
|
|
837
|
+
* The write is a synchronous `fs.writeSync` to fd 2 for the same reason as
|
|
838
|
+
* {@link startupMarker}: it must survive a crash that follows it. `write` and
|
|
839
|
+
* `state` are injectable so a test can exercise the guards without consuming
|
|
840
|
+
* this copy's latch.
|
|
841
|
+
* @param {{ isWorkspaceLoad: boolean }} ctx
|
|
842
|
+
* @param {NonNullable<ReturnType<typeof nativeAddonStatus>>} addon
|
|
843
|
+
* @param {{ write?: (text: string) => void; state?: { reported: boolean } }} [deps]
|
|
844
|
+
* @returns {boolean} whether this call wrote the notice
|
|
845
|
+
*/
|
|
846
|
+
export function reportStaleWorkspaceAddon(ctx, addon, deps = {}) {
|
|
847
|
+
const state = deps.state ?? staleNoticeState;
|
|
848
|
+
// Thread identity last: the three cheap gates reject every ordinary launch
|
|
849
|
+
// before `node:worker_threads` is resolved at all.
|
|
850
|
+
if (!ctx.isWorkspaceLoad || !addon.stale || state.reported || !onMainThread()) return false;
|
|
851
|
+
state.reported = true;
|
|
852
|
+
try {
|
|
853
|
+
(deps.write ?? (text => fs.writeSync(2, text)))(staleWorkspaceAddonNotice(addon));
|
|
854
|
+
} catch {
|
|
855
|
+
// stderr unavailable; the notice is best-effort, the stubs still speak
|
|
856
|
+
}
|
|
857
|
+
return true;
|
|
858
|
+
}
|
|
859
|
+
|
|
718
860
|
/**
|
|
719
861
|
* Install the addon's bounded Tokio runtime now that `dlopen` has returned and
|
|
720
862
|
* the dynamic-loader lock is released. The Rust `#[module_init]` deliberately
|
|
@@ -881,6 +1023,7 @@ export function loadNative() {
|
|
|
881
1023
|
installNativeTokioRuntime(bindings);
|
|
882
1024
|
configureGrammarDir(bindings, ctx.nativesDir);
|
|
883
1025
|
loadedAddon = describeLoadedAddon(bindings, candidate, ctx);
|
|
1026
|
+
reportStaleWorkspaceAddon(ctx, loadedAddon);
|
|
884
1027
|
cleanupStaleNativeVersions({ nativesDir: ctx.nativesDir, currentVersion: ctx.packageVersion });
|
|
885
1028
|
startupMarker("native:loadNative:done");
|
|
886
1029
|
return bindings;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-natives",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.18.0",
|
|
4
4
|
"description": "Native Rust bindings for PDF conversion, audio, WebRTC, grep, clipboard, image processing, syntax highlighting, PTY, and shell operations via N-API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
@@ -120,11 +120,11 @@
|
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
122
|
"optionalDependencies": {
|
|
123
|
-
"@oh-my-pi/pi-natives-linux-x64": "18.
|
|
124
|
-
"@oh-my-pi/pi-natives-linux-arm64": "18.
|
|
125
|
-
"@oh-my-pi/pi-natives-darwin-x64": "18.
|
|
126
|
-
"@oh-my-pi/pi-natives-darwin-arm64": "18.
|
|
127
|
-
"@oh-my-pi/pi-natives-win32-x64": "18.
|
|
128
|
-
"@oh-my-pi/pi-natives-win32-arm64": "18.
|
|
123
|
+
"@oh-my-pi/pi-natives-linux-x64": "18.18.0",
|
|
124
|
+
"@oh-my-pi/pi-natives-linux-arm64": "18.18.0",
|
|
125
|
+
"@oh-my-pi/pi-natives-darwin-x64": "18.18.0",
|
|
126
|
+
"@oh-my-pi/pi-natives-darwin-arm64": "18.18.0",
|
|
127
|
+
"@oh-my-pi/pi-natives-win32-x64": "18.18.0",
|
|
128
|
+
"@oh-my-pi/pi-natives-win32-arm64": "18.18.0"
|
|
129
129
|
}
|
|
130
130
|
}
|