@ngockhoale/ukit 2.2.6 → 2.2.7
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/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/templates/.omp/hooks/pre/ukit-bridge.js +29 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.2.7 - 2026-08-28
|
|
6
|
+
|
|
7
|
+
Follow-up to 2.2.6: that release only made the wrong-runtime failure message *explain* the
|
|
8
|
+
`UKIT_NODE_PATH` workaround better, but a user still had to diagnose the error and export the
|
|
9
|
+
variable by hand. This release removes the manual step for the common case.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **The omp bridge now auto-detects a working Node.js binary instead of requiring
|
|
14
|
+
`UKIT_NODE_PATH` to be set by hand.** [`ukit-bridge.js`](.omp/hooks/pre/ukit-bridge.js) probes
|
|
15
|
+
`process.execPath` with `--version` before trusting it; if that fails (e.g. a Bun-hosted or
|
|
16
|
+
self-compiled omp reporting its own binary as `process.execPath`), it falls back to the bare
|
|
17
|
+
`node` command name and lets normal OS `PATH` resolution find a real Node.js binary — this
|
|
18
|
+
works the same way on macOS, Linux, and Windows, since Node's `child_process` spawn does `PATH`
|
|
19
|
+
lookup for bare executable names on all three. An explicit `UKIT_NODE_PATH` still always wins
|
|
20
|
+
and skips the probe. The probe result is cached for the life of the omp process, so it only
|
|
21
|
+
runs once, not on every tool call. The transport-failure message's `UKIT_NODE_PATH` hint now
|
|
22
|
+
only appears when both the probe and the `node` PATH fallback have failed.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **1 regression test** in `tests/hooks/ompHookBridge.test.js` — with `UKIT_NODE_PATH` unset and
|
|
27
|
+
`process.execPath` mocked to fail the `--version` probe, the bridge falls back to invoking the
|
|
28
|
+
bare `node` command instead of blocking the tool call.
|
|
29
|
+
|
|
5
30
|
## 2.2.6 - 2026-08-27
|
|
6
31
|
|
|
7
32
|
Follow-up to a real-world report: a user on a Bun-hosted/self-compiled omp build kept getting
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
11
12
|
import {
|
|
12
13
|
evaluateCompletion,
|
|
13
14
|
incrementContinuation,
|
|
@@ -181,6 +182,30 @@ function recordHookErrorDiagnostic(projectRoot, sessionId, diagnostic) {
|
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
184
|
|
|
185
|
+
// Cached across the omp process lifetime: resolution below spawns a probe process, and every
|
|
186
|
+
// tool call would otherwise pay that cost again.
|
|
187
|
+
let cachedNodeExecutable = null;
|
|
188
|
+
|
|
189
|
+
function isRealNodeBinary(candidate) {
|
|
190
|
+
try {
|
|
191
|
+
const probe = spawnSync(candidate, ['--version'], { timeout: 2000, encoding: 'utf8' });
|
|
192
|
+
return !probe.error && typeof probe.stdout === 'string' && /^v\d+\.\d+\.\d+/.test(probe.stdout.trim());
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Do not blindly trust process.execPath: under a Bun-hosted or self-compiled omp, it can report
|
|
199
|
+
// omp's own binary path instead of a real Node.js one. Probe it first; if it fails, fall back to
|
|
200
|
+
// the bare "node" command name so PATH resolution (cross-platform: macOS/Linux/Windows) finds a
|
|
201
|
+
// real Node.js binary automatically, without requiring a manual UKIT_NODE_PATH export.
|
|
202
|
+
function resolveNodeExecutable() {
|
|
203
|
+
if (process.env.UKIT_NODE_PATH) return process.env.UKIT_NODE_PATH;
|
|
204
|
+
if (cachedNodeExecutable) return cachedNodeExecutable;
|
|
205
|
+
cachedNodeExecutable = isRealNodeBinary(process.execPath) ? process.execPath : 'node';
|
|
206
|
+
return cachedNodeExecutable;
|
|
207
|
+
}
|
|
208
|
+
|
|
184
209
|
export async function runScriptChain(
|
|
185
210
|
pi,
|
|
186
211
|
scripts,
|
|
@@ -195,8 +220,7 @@ export async function runScriptChain(
|
|
|
195
220
|
|
|
196
221
|
const runnerPath = path.join(projectRoot, '.claude', 'ukit', 'runtime', 'hook-chain-runner.mjs');
|
|
197
222
|
const scriptPaths = scripts.map((scriptName) => path.join(projectRoot, '.claude', 'hooks', scriptName));
|
|
198
|
-
|
|
199
|
-
const nodeExecutable = process.env.UKIT_NODE_PATH || process.execPath;
|
|
223
|
+
const nodeExecutable = resolveNodeExecutable();
|
|
200
224
|
const startedAt = Date.now();
|
|
201
225
|
let execResult;
|
|
202
226
|
try {
|
|
@@ -243,9 +267,9 @@ export async function runScriptChain(
|
|
|
243
267
|
recordHookErrorDiagnostic(projectRoot, payload.session_id, diagnostic);
|
|
244
268
|
const nodePathHint = process.env.UKIT_NODE_PATH
|
|
245
269
|
? ''
|
|
246
|
-
: `
|
|
247
|
-
+ `
|
|
248
|
-
+ `to
|
|
270
|
+
: ` UKit already tried process.execPath and a "node" PATH lookup; neither resolved to a `
|
|
271
|
+
+ `working Node.js binary (runtime="${diagnostic.nodeExecutable}"). Set UKIT_NODE_PATH to `
|
|
272
|
+
+ `an explicit Node.js binary path to override, e.g.: export UKIT_NODE_PATH="$(command -v node)".`;
|
|
249
273
|
const reason = `UKit OMP hook runner failed before producing a valid result `
|
|
250
274
|
+ `(killed=${diagnostic.killed}, code=${diagnostic.code}, elapsedMs=${diagnostic.elapsedMs}, `
|
|
251
275
|
+ `runtime=${diagnostic.nodeExecutable}). No safety-gate verdict was available for [${scripts.join(', ')}]. `
|