review: surface ping-test cleanup failures + restore copy

Routes the post-ping `_ping-test` cleanup through `spawnQuiet` +
`setupLog.step` so a non-zero exit from `delete-cli-agent.ts` lands
in `logs/setup-steps/cleanup-cli-agent.log` and the progression log,
and prints a one-line warn to the user. Previously the spawnSync was
fire-and-forget with `stdio: 'ignore'`, leaving an orphan agent group
silently if cleanup failed.

Restores the original copy on the cli-agent step labels, the ping
explainer paragraph, and the post-ping spinner stop line — those
copy changes are out of scope for this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-30 23:16:34 +03:00
parent 3dc772cca0
commit 3d6a9b74f3

View File

@@ -51,10 +51,11 @@ import { pollHealth } from './onecli.js';
import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js'; import { getLaunchdLabel, getSystemdUnit } from '../src/install-slug.js';
import { claudeCliAvailable, resolveTimezoneViaClaude } from './lib/tz-from-claude.js'; import { claudeCliAvailable, resolveTimezoneViaClaude } from './lib/tz-from-claude.js';
import * as setupLog from './logs.js'; import * as setupLog from './logs.js';
import { ensureAnswer, fail, runQuietChild, runQuietStep } from './lib/runner.js'; import { ensureAnswer, fail, runQuietChild, runQuietStep, spawnQuiet } from './lib/runner.js';
import { emit as phEmit } from './lib/diagnostics.js'; import { emit as phEmit } from './lib/diagnostics.js';
import { accentGreen, brandBody, brandBold, brandChip, dimWrap, fitToWidth, fmtDuration, note, wrapForGutter } from './lib/theme.js'; import { accentGreen, brandBody, brandBold, brandChip, dimWrap, fitToWidth, fmtDuration, note, wrapForGutter } from './lib/theme.js';
import { isValidTimezone } from '../src/timezone.js'; import { isValidTimezone } from '../src/timezone.js';
const CLI_AGENT_NAME = 'Terminal Agent'; const CLI_AGENT_NAME = 'Terminal Agent';
const RUN_START = Date.now(); const RUN_START = Date.now();
@@ -320,8 +321,8 @@ async function main(): Promise<void> {
const res = await runQuietStep( const res = await runQuietStep(
'cli-agent', 'cli-agent',
{ {
running: 'Preparing connection test…', running: 'Bringing your assistant online…',
done: 'Ready to test.', done: 'Assistant wired up.',
}, },
['--display-name', displayName!, '--agent-name', CLI_AGENT_NAME, '--folder', '_ping-test'], ['--display-name', displayName!, '--agent-name', CLI_AGENT_NAME, '--folder', '_ping-test'],
); );
@@ -336,7 +337,7 @@ async function main(): Promise<void> {
p.log.message( p.log.message(
brandBody( brandBody(
dimWrap( dimWrap(
'Checking your assistant can respond — first startup takes 3060 seconds.', "Your assistant runs in an isolated sandbox. I'm going to send it a quick test message (ping) and wait for a reply (pong) to confirm it's responding. First startup typically takes 3060 seconds while the sandbox warms up.",
4, 4,
), ),
), ),
@@ -344,9 +345,27 @@ async function main(): Promise<void> {
const ping = await confirmAssistantResponds(); const ping = await confirmAssistantResponds();
if (ping === 'ok') { if (ping === 'ok') {
phEmit('first_chat_ready'); phEmit('first_chat_ready');
spawnSync('pnpm', ['exec', 'tsx', 'scripts/delete-cli-agent.ts', '--folder', '_ping-test'], { const cleanupRawLog = setupLog.stepRawLog('cleanup-cli-agent');
stdio: 'ignore', const cleanupStart = Date.now();
}); const cleanup = await spawnQuiet(
'pnpm',
['exec', 'tsx', 'scripts/delete-cli-agent.ts', '--folder', '_ping-test'],
cleanupRawLog,
);
setupLog.step(
'cleanup-cli-agent',
cleanup.ok ? 'success' : 'failed',
Date.now() - cleanupStart,
{ exit_code: cleanup.exitCode },
cleanupRawLog,
);
if (!cleanup.ok) {
p.log.warn(
brandBody(
`Couldn't clean up the test agent — it may still appear in your agent list. See ${cleanupRawLog} for details.`,
),
);
}
const next = ensureAnswer( const next = ensureAnswer(
await brightSelect<'continue' | 'chat'>({ await brightSelect<'continue' | 'chat'>({
message: 'What next?', message: 'What next?',
@@ -580,7 +599,7 @@ async function confirmAssistantResponds(): Promise<PingResult> {
clearInterval(tick); clearInterval(tick);
const suffix = ` (${fmtDuration(Date.now() - start)})`; const suffix = ` (${fmtDuration(Date.now() - start)})`;
if (result === 'ok') { if (result === 'ok') {
s.stop(`${k.bold(fitToWidth('Connection verified.', suffix))}${k.dim(suffix)}`); s.stop(`${k.bold(fitToWidth('Your assistant is ready.', suffix))}${k.dim(suffix)}`);
} else { } else {
const msg = const msg =
result === 'socket_error' ? "Couldn't reach the NanoClaw service." : "Your assistant didn't reply in time."; result === 'socket_error' ? "Couldn't reach the NanoClaw service." : "Your assistant didn't reply in time.";