fix(setup): accept CLI-only verify success

This commit is contained in:
glifocat
2026-04-24 11:44:35 +02:00
parent a4346f566c
commit 1de5a0356b

View File

@@ -14,14 +14,9 @@ import Database from 'better-sqlite3';
import { DATA_DIR } from '../src/config.js'; import { DATA_DIR } from '../src/config.js';
import { readEnvFile } from '../src/env.js'; import { readEnvFile } from '../src/env.js';
import { log } from '../src/log.js'; import { log } from '../src/log.js';
import { pingCliAgent } from './lib/agent-ping.js'; import { pingCliAgent, type PingResult } from './lib/agent-ping.js';
import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js'; import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js';
import { import { getPlatform, getServiceManager, hasSystemd, isRoot } from './platform.js';
getPlatform,
getServiceManager,
hasSystemd,
isRoot,
} from './platform.js';
import { emitStatus } from './status.js'; import { emitStatus } from './status.js';
export async function run(_args: string[]): Promise<void> { export async function run(_args: string[]): Promise<void> {
@@ -38,11 +33,7 @@ export async function run(_args: string[]): Promise<void> {
// a sibling checkout (common for developers with multiple clones), this // a sibling checkout (common for developers with multiple clones), this
// repo's `data/cli.sock` won't exist — AGENT_PING would return a // repo's `data/cli.sock` won't exist — AGENT_PING would return a
// misleading `socket_error`. Surface the mismatch directly instead. // misleading `socket_error`. Surface the mismatch directly instead.
let service: let service: 'not_found' | 'stopped' | 'running' | 'running_other_checkout' = 'not_found';
| 'not_found'
| 'stopped'
| 'running'
| 'running_other_checkout' = 'not_found';
let runningFromPath: string | null = null; let runningFromPath: string | null = null;
const mgr = getServiceManager(); const mgr = getServiceManager();
@@ -74,10 +65,7 @@ export async function run(_args: string[]): Promise<void> {
execSync(`${prefix} is-active ${systemdUnit}`, { stdio: 'ignore' }); execSync(`${prefix} is-active ${systemdUnit}`, { stdio: 'ignore' });
service = 'running'; service = 'running';
try { try {
const pidStr = execSync( const pidStr = execSync(`${prefix} show ${systemdUnit} -p MainPID --value`, { encoding: 'utf-8' }).trim();
`${prefix} show ${systemdUnit} -p MainPID --value`,
{ encoding: 'utf-8' },
).trim();
const pid = Number(pidStr); const pid = Number(pidStr);
if (Number.isInteger(pid) && pid > 0) { if (Number.isInteger(pid) && pid > 0) {
runningFromPath = resolveBinaryScript(pid); runningFromPath = resolveBinaryScript(pid);
@@ -115,11 +103,7 @@ export async function run(_args: string[]): Promise<void> {
} }
} }
if ( if (service === 'running' && runningFromPath && !isPathInside(runningFromPath, projectRoot)) {
service === 'running' &&
runningFromPath &&
!isPathInside(runningFromPath, projectRoot)
) {
service = 'running_other_checkout'; service = 'running_other_checkout';
} }
@@ -210,11 +194,7 @@ export async function run(_args: string[]): Promise<void> {
// 6. Check mount allowlist // 6. Check mount allowlist
let mountAllowlist = 'missing'; let mountAllowlist = 'missing';
if ( if (fs.existsSync(path.join(homeDir, '.config', 'nanoclaw', 'mount-allowlist.json'))) {
fs.existsSync(
path.join(homeDir, '.config', 'nanoclaw', 'mount-allowlist.json'),
)
) {
mountAllowlist = 'configured'; mountAllowlist = 'configured';
} }
@@ -227,15 +207,15 @@ export async function run(_args: string[]): Promise<void> {
log.info('Agent ping result', { agentPing }); log.info('Agent ping result', { agentPing });
} }
// Determine overall status // Determine overall status. A CLI-only install is valid when the local
const status = // agent round-trip succeeds; messaging app credentials are optional.
service === 'running' && const status = determineVerifyStatus({
credentials !== 'missing' && service,
anyChannelConfigured && credentials,
registeredGroups > 0 && anyChannelConfigured,
(agentPing === 'ok' || agentPing === 'skipped') registeredGroups,
? 'success' agentPing,
: 'failed'; });
log.info('Verification complete', { status, channelAuth }); log.info('Verification complete', { status, channelAuth });
@@ -255,6 +235,25 @@ export async function run(_args: string[]): Promise<void> {
if (status === 'failed') process.exit(1); if (status === 'failed') process.exit(1);
} }
export function determineVerifyStatus(input: {
service: 'not_found' | 'stopped' | 'running' | 'running_other_checkout';
credentials: string;
anyChannelConfigured: boolean;
registeredGroups: number;
agentPing: PingResult | 'skipped';
}): 'success' | 'failed' {
const cliAgentResponds = input.agentPing === 'ok';
const hasUsableChannel = input.anyChannelConfigured || cliAgentResponds;
return input.service === 'running' &&
input.credentials !== 'missing' &&
hasUsableChannel &&
input.registeredGroups > 0 &&
(cliAgentResponds || input.agentPing === 'skipped')
? 'success'
: 'failed';
}
/** /**
* Given a PID, resolve the script path the process is executing (i.e. the * Given a PID, resolve the script path the process is executing (i.e. the
* first `.js` / `.ts` / `.mjs` arg after `node`). Returns null on any * first `.js` / `.ts` / `.mjs` arg after `node`). Returns null on any