agentworth 0.1.4 → 0.1.6
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/lib/resolver.js +41 -4
- package/package.json +1 -1
package/lib/resolver.js
CHANGED
|
@@ -264,6 +264,36 @@ export async function downloadAndExtractBinary(options = {}) {
|
|
|
264
264
|
* @param {string} [currentScriptPath]
|
|
265
265
|
* @returns {string | null}
|
|
266
266
|
*/
|
|
267
|
+
/**
|
|
268
|
+
* True when `candidate` and `selfPath` are the same file, following symlinks.
|
|
269
|
+
*/
|
|
270
|
+
export function isSelf(candidate, selfPath) {
|
|
271
|
+
if (!selfPath) return false;
|
|
272
|
+
try {
|
|
273
|
+
return fs.realpathSync(candidate) === fs.realpathSync(selfPath);
|
|
274
|
+
} catch {
|
|
275
|
+
return path.resolve(candidate) === path.resolve(selfPath);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* True when `candidate` is a Node script (a bin shim) rather than the native
|
|
281
|
+
* binary. Covers shims that are copies rather than symlinks.
|
|
282
|
+
*/
|
|
283
|
+
export function looksLikeNodeScript(filePath) {
|
|
284
|
+
try {
|
|
285
|
+
if (/\.(js|cjs|mjs)$/.test(filePath)) return true;
|
|
286
|
+
const fd = fs.openSync(filePath, 'r');
|
|
287
|
+
const buf = Buffer.alloc(64);
|
|
288
|
+
const n = fs.readSync(fd, buf, 0, 64, 0);
|
|
289
|
+
fs.closeSync(fd);
|
|
290
|
+
const first = buf.subarray(0, n).toString('utf8').split('\n')[0];
|
|
291
|
+
return first.startsWith('#!') && /\bnode\b/.test(first);
|
|
292
|
+
} catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
267
297
|
export function findPathBinary(binName = getBinaryName(), pathEnv = process.env.PATH, currentScriptPath) {
|
|
268
298
|
if (!pathEnv) {
|
|
269
299
|
return null;
|
|
@@ -274,8 +304,12 @@ export function findPathBinary(binName = getBinaryName(), pathEnv = process.env.
|
|
|
274
304
|
if (!entry) continue;
|
|
275
305
|
const candidate = path.join(entry, binName);
|
|
276
306
|
if (isExecutable(candidate)) {
|
|
277
|
-
// Avoid circular invocation if PATH points
|
|
278
|
-
|
|
307
|
+
// Avoid circular invocation if PATH points back at this JS wrapper.
|
|
308
|
+
// npm/npx put node_modules/.bin on PATH and the entry there is a SYMLINK
|
|
309
|
+
// to this very file. path.resolve() does not follow symlinks, so this guard
|
|
310
|
+
// missed it and the launcher spawned itself until the OS refused to fork
|
|
311
|
+
// (EAGAIN on macOS, v0.1.4). Compare real paths, and reject Node scripts.
|
|
312
|
+
if (isSelf(candidate, currentScriptPath) || looksLikeNodeScript(candidate)) {
|
|
279
313
|
continue;
|
|
280
314
|
}
|
|
281
315
|
return candidate;
|
|
@@ -337,8 +371,9 @@ export function resolveBinary(options = {}) {
|
|
|
337
371
|
}
|
|
338
372
|
|
|
339
373
|
// 3. System PATH (highest user priority for installed binaries)
|
|
374
|
+
// Skipped when already inside a launcher: the only thing PATH could offer is us.
|
|
340
375
|
const currentBin = path.resolve(baseDir, '..', 'bin', 'agentworth.js');
|
|
341
|
-
if (env.PATH !== undefined) {
|
|
376
|
+
if (env.PATH !== undefined && !env.AGENTWORTH_LAUNCHER_ACTIVE) {
|
|
342
377
|
const pathBin = findPathBinary(binName, env.PATH, currentBin);
|
|
343
378
|
if (pathBin) {
|
|
344
379
|
return {
|
|
@@ -487,9 +522,11 @@ export function run(argv = process.argv.slice(2), options = {}) {
|
|
|
487
522
|
return 1;
|
|
488
523
|
}
|
|
489
524
|
|
|
525
|
+
// Belt and braces: a child that somehow re-enters this launcher cannot loop.
|
|
526
|
+
const childEnv = { ...(options.env || process.env), AGENTWORTH_LAUNCHER_ACTIVE: '1' };
|
|
490
527
|
const result = spawnSync(binaryResult.path, resolvedArgs, {
|
|
491
528
|
stdio: 'inherit',
|
|
492
|
-
env:
|
|
529
|
+
env: childEnv,
|
|
493
530
|
});
|
|
494
531
|
|
|
495
532
|
if (result.error) {
|