fix(proxy): installer — no prompts during dry-run; never pin service to agent-bundled node
- askConfirm short-circuits non-interactively in --dry-run (prints what would be asked) - systemd node resolution order: existing unit's pinned binary > newest nvm node > PATH lookup excluding hermes-agent runtimes > process.execPath. On the live box this keeps the unit byte-identical (true idempotency for the service section).
This commit is contained in:
1 parent
3f9be30784
commit
753fac9fe9
1 file changed
+25
-1
@@ -94,6 +94,7 @@ function askSecret(question) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function askConfirm(question, defaultYes = true) {
|
async function askConfirm(question, defaultYes = true) {
|
||||||
|
if (FLAGS.dryRun) { console.log(`(dry-run) would ask: ${question} -> default ${defaultYes ? 'y' : 'n'}`); return defaultYes; }
|
||||||
if (FLAGS.nonInteractive) return defaultYes;
|
if (FLAGS.nonInteractive) return defaultYes;
|
||||||
const a = (await ask(`${question} (y/n)`, defaultYes ? 'y' : 'n')).toLowerCase();
|
const a = (await ask(`${question} (y/n)`, defaultYes ? 'y' : 'n')).toLowerCase();
|
||||||
return a === 'y';
|
return a === 'y';
|
||||||
@@ -399,7 +400,30 @@ function sectionUnit() {
|
|||||||
const S = 'systemd service';
|
const S = 'systemd service';
|
||||||
if (FLAGS.noSystemd) { report(S, 'SKIPPED', '--no-systemd'); return true; }
|
if (FLAGS.noSystemd) { report(S, 'SKIPPED', '--no-systemd'); return true; }
|
||||||
const unitPath = path.join(SYSTEMD_USER_DIR, 'proxy-bridge.service');
|
const unitPath = path.join(SYSTEMD_USER_DIR, 'proxy-bridge.service');
|
||||||
const nodeBin = process.execPath;
|
// The service must not run on an agent-bundled runtime: prefer (1) the node
|
||||||
|
// already pinned in an existing unit (stable, known-good), (2) the newest
|
||||||
|
// nvm-installed node, (3) PATH lookup that does NOT resolve into the hermes
|
||||||
|
// agent dir, (4) process.execPath as a last resort.
|
||||||
|
let nodeBin = null;
|
||||||
|
try {
|
||||||
|
const existing = fs.readFileSync(unitPath, 'utf8');
|
||||||
|
const m = existing.match(/^ExecStart=(\S+)\s+/m);
|
||||||
|
if (m && fs.existsSync(m[1])) nodeBin = m[1];
|
||||||
|
} catch {}
|
||||||
|
if (!nodeBin) {
|
||||||
|
const nvmRoot = path.join(os.homedir(), '.nvm', 'versions', 'node');
|
||||||
|
try {
|
||||||
|
const versions = fs.readdirSync(nvmRoot).filter(v => /^v\d+/.test(v)).sort((a, b) =>
|
||||||
|
a.localeCompare(b, undefined, { numeric: true }));
|
||||||
|
if (versions.length > 0) nodeBin = path.join(nvmRoot, versions[versions.length - 1], 'bin', 'node');
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (!nodeBin) {
|
||||||
|
const loginNode = run('bash', ['--noprofile', '--norc', '-c', 'command -v node'], { readOnly: true });
|
||||||
|
const cand = loginNode.status === 0 ? loginNode.stdout.trim() : '';
|
||||||
|
if (cand && !fs.realpathSync(cand).includes('/.hermes/')) nodeBin = cand;
|
||||||
|
}
|
||||||
|
if (!nodeBin) nodeBin = process.execPath;
|
||||||
const want = `[Unit]\nDescription=Local Proxy Bridge (Keyring Auth)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=${nodeBin} ${BRIDGE_DIR}/bridge.js\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=default.target\n`;
|
const want = `[Unit]\nDescription=Local Proxy Bridge (Keyring Auth)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=${nodeBin} ${BRIDGE_DIR}/bridge.js\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=default.target\n`;
|
||||||
let current = null;
|
let current = null;
|
||||||
try { current = fs.readFileSync(unitPath, 'utf8'); } catch {}
|
try { current = fs.readFileSync(unitPath, 'utf8'); } catch {}
|
||||||
|
|||||||
Reference in new issue
Block a user