fix(agent): native enroll + launchd install real-deploy shakedown
Fixes found deploying `pair --install` against the live control-plane: - native enroll tolerates hostContentSecret:null and converts the base64-DER cert + caChain[] the CP returns into PEM for frpc/keystore - launchd/systemd units use an absolute node (process.execPath) + a PATH with /usr/local/bin (bare `node` died with EX_CONFIG 78) - the agent unit now carries ENROLL_URL/RELAY_URL/STATE_DIR/LOCAL_TARGET_URL (`run` loadConfig()-validates them; missing → supervisor exited 1) 281 tests pass. Follow-up: add unit tests for the native-enroll tolerance + agent-unit env (only install node-path is covered so far).
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
* - FIX M-host-2service: base-app env (BIND_HOST/ALLOWED_ORIGINS/PORT/…) is routed to the
|
||||
* base-app unit ONLY; the agent unit (which supervises frpc) never carries it.
|
||||
*/
|
||||
import { dirname } from 'node:path'
|
||||
import type { AgentConfig } from '../config/agentConfig.js'
|
||||
import {
|
||||
agentLabel,
|
||||
@@ -202,13 +203,31 @@ export async function installService(
|
||||
// so nothing is ever written for a rejected install.
|
||||
const baseAppEnv = resolveBaseAppEnv(cfg, options)
|
||||
const bin = deps.binPath()
|
||||
const baseAppExec = options.baseAppExec ?? DEFAULT_BASE_APP_EXEC
|
||||
const rawExec = options.baseAppExec ?? DEFAULT_BASE_APP_EXEC
|
||||
// launchd/systemd start with a minimal PATH that excludes /usr/local/bin (where a nvm/brew `node`
|
||||
// symlink usually lives), so a bare `node` program dies with EX_CONFIG(78). Use the absolute node
|
||||
// (process.execPath) and export a PATH so the units — and the base app's tmux/node-pty/frpc
|
||||
// subprocesses — resolve their tools.
|
||||
const nodePath = process.execPath
|
||||
const unitPath = `${dirname(nodePath)}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin`
|
||||
const baseAppExec = rawExec[0] === 'node' ? [nodePath, ...rawExec.slice(1)] : rawExec
|
||||
const baseAppEnvWithPath = { ...baseAppEnv, PATH: unitPath }
|
||||
// The agent unit runs `run`, which loadConfig()-validates ENROLL_URL(https)/RELAY_URL(wss) up front
|
||||
// (fail-fast). Without these in the unit env the supervisor exits 1 on every launch — so inject the
|
||||
// agent's own runtime config (NOT the base-app env) alongside PATH.
|
||||
const agentEnv: Record<string, string> = {
|
||||
PATH: unitPath,
|
||||
ENROLL_URL: cfg.enrollUrl,
|
||||
RELAY_URL: cfg.relayUrl,
|
||||
STATE_DIR: cfg.stateDir,
|
||||
LOCAL_TARGET_URL: cfg.localTargetUrl,
|
||||
}
|
||||
|
||||
if (platform === 'launchd') {
|
||||
const baseAppPath = launchdPlistPath(deps.homedir(), baseAppLabel())
|
||||
deps.writeFile(baseAppPath, buildLaunchdPlist(baseAppExec, baseAppEnv, baseAppLabel()))
|
||||
deps.writeFile(baseAppPath, buildLaunchdPlist(baseAppExec, baseAppEnvWithPath, baseAppLabel()))
|
||||
const agentPath = launchdPlistPath(deps.homedir(), agentLabel())
|
||||
deps.writeFile(agentPath, buildLaunchdPlist([bin, 'run'], {}, agentLabel()))
|
||||
deps.writeFile(agentPath, buildLaunchdPlist([nodePath, bin, 'run'], agentEnv, agentLabel()))
|
||||
for (const path of [baseAppPath, agentPath]) {
|
||||
const { cmd, args } = launchdLoadCommand(path)
|
||||
await deps.runCommand(cmd, args)
|
||||
@@ -217,8 +236,8 @@ export async function installService(
|
||||
}
|
||||
|
||||
const baseAppOptions: SystemdUnitOptions = options.envFile
|
||||
? { env: baseAppEnv, envFile: options.envFile }
|
||||
: { env: baseAppEnv }
|
||||
? { env: baseAppEnvWithPath, envFile: options.envFile }
|
||||
: { env: baseAppEnvWithPath }
|
||||
const baseAppPath = systemdUnitPath(deps.homedir(), baseAppUnitName())
|
||||
deps.writeFile(
|
||||
baseAppPath,
|
||||
@@ -227,7 +246,7 @@ export async function installService(
|
||||
const agentPath = systemdUnitPath(deps.homedir(), agentUnitName())
|
||||
deps.writeFile(
|
||||
agentPath,
|
||||
buildSystemdUnit(`${bin} run`, deps.username(), {}, 'web-terminal host agent (frpc supervisor)'),
|
||||
buildSystemdUnit(`${nodePath} ${bin} run`, deps.username(), { env: agentEnv }, 'web-terminal host agent (frpc supervisor)'),
|
||||
)
|
||||
for (const unit of [baseAppUnitName(), agentUnitName()]) {
|
||||
const { cmd, args } = systemdEnableCommand(unit)
|
||||
|
||||
Reference in New Issue
Block a user