From b0cae1ba4cef1fa4a21beae5907f36334184adfc Mon Sep 17 00:00:00 2001 From: gavrielc Date: Tue, 21 Apr 2026 17:41:29 +0300 Subject: [PATCH] feat(setup): chain register-claude-token.sh into setup:auto Runs after the OneCLI install step and before mounts/service. Skips silently when `onecli secrets list` already reports an Anthropic secret, so re-running setup:auto on a configured install is a no-op. Child process uses stdio:inherit so the menu + browser sign-in flow work normally. Co-Authored-By: Claude Opus 4.7 (1M context) --- setup/auto.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/setup/auto.ts b/setup/auto.ts index 84db937..66d6880 100644 --- a/setup/auto.ts +++ b/setup/auto.ts @@ -9,14 +9,15 @@ * Config via env: * NANOCLAW_TZ IANA zone override (skip autodetect) * NANOCLAW_SKIP comma-separated step names to skip - * (environment|timezone|container|onecli|mounts|service|verify) + * (environment|timezone|container|onecli|auth|mounts|service|verify) * - * OneCLI is installed and configured here, but secret registration (the - * Anthropic token or API key), channel auth, and `/manage-channels` stay - * interactive — they need human input. Finish those with `/setup` §4 - * onwards, `/add-`, and `/manage-channels`. + * Anthropic credential registration runs via setup/register-claude-token.sh + * (the only step that truly requires human input — browser sign-in or a + * pasted token/key). Channel auth and `/manage-channels` remain separate + * because they're platform-specific and typically handled via `/add-` + * and `/manage-channels` after this driver completes. */ -import { spawn } from 'child_process'; +import { spawn, spawnSync } from 'child_process'; type Fields = Record; type StepResult = { ok: boolean; fields: Fields; exitCode: number }; @@ -67,6 +68,26 @@ function runStep(name: string, extra: string[] = []): Promise { }); } +function anthropicSecretExists(): boolean { + try { + const res = spawnSync('onecli', ['secrets', 'list'], { + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'pipe'], + }); + if (res.status !== 0) return false; + return /anthropic/i.test(res.stdout ?? ''); + } catch { + return false; + } +} + +function runBashScript(relPath: string): Promise { + return new Promise((resolve) => { + const child = spawn('bash', [relPath], { stdio: 'inherit' }); + child.on('close', (code) => resolve(code ?? 1)); + }); +} + function fail(msg: string, hint?: string): never { console.error(`\n[setup:auto] ${msg}`); if (hint) console.error(` ${hint}`); @@ -131,6 +152,24 @@ async function main(): Promise { } } + if (!skip.has('auth')) { + if (anthropicSecretExists()) { + console.log( + '\n── auth ────────────────────────────────────\n' + + '[setup:auto] OneCLI already has an Anthropic secret — skipping.', + ); + } else { + console.log('\n── auth ────────────────────────────────────'); + const code = await runBashScript('setup/register-claude-token.sh'); + if (code !== 0) { + fail( + 'Anthropic credential registration failed or was aborted.', + 'Re-run `bash setup/register-claude-token.sh` or handle via `/setup` §4.', + ); + } + } + } + if (!skip.has('mounts')) { const res = await runStep('mounts', ['--empty']); if (!res.ok && res.fields.STATUS !== 'skipped') { @@ -160,7 +199,7 @@ async function main(): Promise { if (!res.ok) { console.log('\n[setup:auto] Scripted steps done. Remaining (interactive):'); if (res.fields.CREDENTIALS !== 'configured') { - console.log(' • Register an Anthropic secret in OneCLI — see `/setup` §4'); + console.log(' • Anthropic secret not detected — re-run `bash setup/register-claude-token.sh`'); } if (!res.fields.CONFIGURED_CHANNELS) { console.log(' • Install a channel: `/add-discord`, `/add-slack`, `/add-telegram`, …');