When corepack enable fails with EACCES (common when Node is installed to a system-writable prefix like /usr/local that the user doesn't own), we fall back to `npm install -g pnpm`. But npm's global prefix isn't always on the shell's PATH — users often set `npm config set prefix ~/.npm-global` to avoid sudo, and the resulting bin dir isn't picked up by `command -v`. Install succeeded, but pnpm "wasn't there" for the follow-up `pnpm install`.
Now after the npm fallback we query `npm config get prefix` and prepend `<prefix>/bin` to PATH. Mirror the same lookup in nanoclaw.sh right before `exec pnpm run setup:auto` — setup.sh's PATH mutation doesn't propagate back, and the hand-off needs pnpm visible too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Some Node installs (older nvm, node@22 keg-only on brew, minimal distro packages) don't ship corepack, so the bootstrap was dying with "corepack: command not found" before pnpm could land on PATH. Now guards the corepack call and falls back to `npm install -g pnpm@<pinned>`, reading the version from package.json's packageManager field.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shared bash + node emitter in setup/lib/diagnostics.{sh,ts} reads/writes data/install-id so every event from a single install shares one distinct_id — bash-side setup_launched/setup_start, node-side auto_started, per-step started/completed, auth_method_chosen, channel_chosen, first_chat_ready/failed, setup_incomplete, setup_aborted, setup_completed. Opt-out via NANOCLAW_NO_DIAGNOSTICS=1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents and implements the output contract from docs/setup-flow.md:
Level 1: clack UI — branded, concise, product content
Level 2: logs/setup.log — append-only, linear, structured entries for
humans + AI agents reviewing a run
Level 3: logs/setup-steps/NN-name.log — full raw stdout+stderr per step
Every scripted sub-step, including bootstrap, emits at all three levels.
Bootstrap now runs under a bash-side clack-alike spinner with live elapsed
time; its apt/pnpm output is captured to 01-bootstrap.log and summarised
as a progression entry. setup.sh's legacy log() routes to the raw log
instead of contaminating the progression log.
Telegram install becomes fully branded: setup/auto.ts owns the BotFather
instructions (clack note), token paste (clack password with format
validation), and getMe check (clack spinner). add-telegram.sh drops to a
non-interactive installer that reads TELEGRAM_BOT_TOKEN from env, logs to
stderr, and emits a single ADD_TELEGRAM status block on stdout.
The Anthropic credential flow is the one intentional break — register-
claude-token.sh still inherits the TTY for claude setup-token's browser
dance; it logs as an 'interactive' progression entry with the method.
setup/logs.ts centralises the level 2/3 formatting: reset, header, step,
userInput, complete, abort, stepRawLog. User answers (display name, agent
name, channel choice, telegram_token preview) log as their own entries so
the setup path is reconstructable from the progression log alone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Node check now triggers setup/install-node.sh when missing/too old, and
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 prevents the first-use prompt from
hanging the script when stdout is redirected to the log.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes to the fresh-install path:
1. setup.sh: when `corepack enable` runs as a non-root user against a
system-wide Node install (apt-installed to /usr/bin), it fails EACCES
trying to symlink /usr/bin/pnpm, leaving pnpm off PATH. Retry with
sudo when pnpm is still missing — gated to Linux/WSL so macOS
Homebrew prefixes aren't polluted with root-owned shims.
2. SKILL.md step 1: if the probe reports STATUS: unavailable (Node not
installed), install Node BEFORE invoking `bash setup.sh`. The old
flow ran setup.sh first as a diagnostic, which always failed fast,
installed Node, then re-ran — two bootstraps for no reason.
Combined: fresh Linux box now goes Node install -> single setup.sh run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setup.sh ran npm install which modified package-lock.json, causing
git merge to refuse during channel skill installation. Switch to
npm ci (deterministic, doesn't modify lockfile) and clean up stale
peer flags in the lockfile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: migrate setup from bash scripts to cross-platform Node.js modules
Replace 9 bash scripts + qr-auth.html with a two-phase setup system:
a bash bootstrap (setup.sh) for Node.js/npm verification, and TypeScript
modules (src/setup/) for everything else. Resolves cross-platform issues:
sed -i replaced with fs operations, sqlite3 CLI replaced with better-sqlite3,
browser opening made cross-platform, service management supports launchd/
systemd/WSL nohup fallback, SQL injection prevented with parameterized queries.
Add Linux systemctl equivalents alongside macOS launchctl commands in 8 skill
files and CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: setup migration issues — pairing code, systemd fallback, nohup escaping
- Emit WhatsApp pairing code immediately when received, before polling
for auth completion. Previously the code was only shown in the final
status block after auth succeeded — a catch-22 since the user needs
the code to authenticate. (whatsapp-auth.ts)
- Add systemd user session pre-check before attempting to write the
user-level service unit. Falls back to nohup wrapper when user-level
systemd is unavailable (e.g. su session without login/D-Bus). (service.ts)
- Rewrite nohup wrapper template using array join instead of template
literal to fix shell variable escaping (\\$ → $). (service.ts)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: detect stale docker group and kill
orphaned processes on Linux systemd
* fix: remove redundant shell option from execSync to fix TS2769
execSync already runs in a shell by default; the explicit `shell: true`
caused a type error with @types/node which expects string, not boolean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: hide QR browser auth option on headless Linux
Emit IS_HEADLESS from environment step and condition SKILL.md to
only show pairing code + QR terminal when no display server is
available (headless Linux without WSL). WSL is excluded from the
headless gate because browser opening works via Windows interop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>