feat(setup): rewrite copy for first-time users + split auth flow

Content pass: every user-facing line is rewritten from the perspective
of someone trying NanoClaw for the first time. Phase labels and devops
framing are gone. Examples:

  "Environment OK"            → "Your system looks good."
  "Container image ready"     → "Sandbox ready."
  "OneCLI installed"          → "OneCLI vault ready."
  "Anthropic credential"      → "Claude account"
  "Mount allowlist in place"  → "Access rules set."
  "Service installed/running" → "NanoClaw is running."
  "Wiring the terminal agent" → "Setting up your terminal chat…"
  "Setup complete"            → "You're ready! Enjoy NanoClaw."

Long-running steps get a one-sentence "why" that teaches a NanoClaw
differentiator while the user waits:

  bootstrap → "NanoClaw is small and runs entirely on your machine.
              Yours to modify."
  container → "Your assistant lives in its own sandbox. It can only
              see what you explicitly share."
  onecli    → "Your assistant never gets your API keys directly. The
              vault adds them to approved requests as they leave the
              sandbox."

OneCLI is now named explicitly and framed as "your agent's vault" in
the install step, the paste-auth save step, the subscription-auth
banner, and their associated failure hints.

Auth split (option b: explicit step name on fail): the auth-method
choice moves from the bash menu in register-claude-token.sh into a
clack select. Only the subscription path still breaks out to the
interactive TTY for `claude setup-token`; paste-based OAuth tokens and
API keys stay in clack via p.password() and register directly via
`onecli secrets create`. register-claude-token.sh is scoped down to
the subscription flow only.

nanoclaw.sh: dropped the "Phase 1 / Phase 2" labels. The wordmark and
subtitle now print bash-side so setup:auto skips repeating them and
the flow reads as one continuous sequence. Bootstrap label is
"Installing the basics" with a dim gutter-line "why" preamble. pnpm's
`> nanoclaw@X setup:auto` preamble is suppressed via --silent.

Em-dash pass on user-facing copy: every em-dash that functions as an
em-dash in a user-visible string is replaced with period, semicolon,
comma, or parens. Code comments and JSDoc are untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-22 02:57:20 +03:00
parent 9b7d4d50e4
commit 7d2081660b
4 changed files with 346 additions and 249 deletions

View File

@@ -1,128 +1,88 @@
#!/usr/bin/env bash
set -euo pipefail
# Prefer bash 4+ (for `read -e -i` readline preload). macOS ships 3.2 in
# /bin/bash, but Homebrew users usually have 5.x first on PATH. The readline
# preload is optional — on 3.x we fall back to a plain confirmation prompt.
# Register an Anthropic credential with OneCLI. Three paths:
# 1) Claude subscription — run `claude setup-token` (browser sign-in)
# and capture the resulting OAuth token.
# 2) Paste an existing sk-ant-oat… OAuth token you already have.
# 3) Paste an Anthropic API key (sk-ant-api…).
# Register a Claude subscription OAuth token with OneCLI — the *only* auth
# path that needs a TTY break in the flow. Paste-based paths (existing
# OAuth token / API key) are handled in-process by setup/auto.ts using
# clack prompts, then onecli secrets create is invoked directly from TS.
#
# Flow:
# 1. Run `claude setup-token` under a PTY (via script(1)) so the browser
# OAuth dance works and its token is captured into a tempfile.
# 2. Regex the sk-ant-oat…AA token out of the ANSI-stripped capture.
# 3. Register it with OneCLI.
#
# Env overrides:
# SECRET_NAME OneCLI secret name (default: Anthropic)
# HOST_PATTERN OneCLI host pattern (default: api.anthropic.com)
# Prefer bash 4+ (for `read -e -i` readline preload). macOS ships 3.2 in
# /bin/bash, but Homebrew users usually have 5.x first on PATH. The
# readline preload is optional — on 3.x we fall back to a plain prompt.
SECRET_NAME="${SECRET_NAME:-Anthropic}"
HOST_PATTERN="${HOST_PATTERN:-api.anthropic.com}"
command -v onecli >/dev/null \
|| { echo "onecli not found. Install it first (see /setup §4)." >&2; exit 1; }
command -v claude >/dev/null \
|| { echo "claude CLI not found. Install from https://claude.ai/download" >&2; exit 1; }
command -v script >/dev/null \
|| { echo "script(1) is required for PTY capture." >&2; exit 1; }
TOKEN=""
tmpfile=$(mktemp -t claude-setup-token.XXXXXX)
trap 'rm -f "$tmpfile"' EXIT
capture_via_claude_setup_token() {
command -v claude >/dev/null \
|| { echo "claude CLI not found. Install from https://claude.ai/download" >&2; exit 1; }
command -v script >/dev/null \
|| { echo "script(1) is required for PTY capture." >&2; exit 1; }
cat <<'EOF'
A browser window will open for you to sign in with your Claude account.
When you finish, we'll save the token to your OneCLI vault automatically.
local tmpfile
tmpfile=$(mktemp -t claude-setup-token.XXXXXX)
trap 'rm -f "$tmpfile"' RETURN
cat <<'EOF'
A browser window will open for sign-in. Token is captured automatically.
Press Enter to run, or edit the command first.
Press Enter to continue, or edit the command first.
EOF
local cmd="claude setup-token"
if [[ ${BASH_VERSINFO[0]:-0} -ge 4 ]]; then
# bash 4+: pre-fill the readline buffer so Enter literally submits.
read -r -e -i "$cmd" -p "$ " cmd </dev/tty
else
# bash 3.x (macOS default /bin/bash): no readline preload. Fall back.
echo "$ $cmd"
read -r -p "Press Enter to run, Ctrl-C to abort. " _ </dev/tty
fi
cmd="claude setup-token"
if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
# bash 4+: pre-fill the readline buffer so Enter literally submits.
read -r -e -i "$cmd" -p "$ " cmd </dev/tty
else
# bash 3.x (macOS default /bin/bash): no readline preload. Fall back.
echo "$ $cmd"
read -r -p "Press Enter to run, Ctrl-C to abort. " _ </dev/tty
fi
# `script` arg order differs between BSD (macOS) and util-linux.
if script --version 2>/dev/null | grep -q util-linux; then
script -q -c "$cmd" "$tmpfile"
else
# BSD script: command is argv after the file, so let it word-split.
# shellcheck disable=SC2086
script -q "$tmpfile" $cmd
fi
# `script` arg order differs between BSD (macOS) and util-linux.
if script --version 2>/dev/null | grep -q util-linux; then
script -q -c "$cmd" "$tmpfile"
else
# BSD script: command is argv after the file, so let it word-split.
# shellcheck disable=SC2086
script -q "$tmpfile" $cmd
fi
# Strip ANSI codes + newlines (TTY wraps the token mid-string), then match
# the sk-ant-oat…AA token. perl because BSD grep caps {n,m} at 255.
TOKEN=$(sed $'s/\x1b\\[[0-9;]*[a-zA-Z]//g' "$tmpfile" \
| tr -d '\n\r' \
| perl -ne 'print "$1\n" while /(sk-ant-oat[A-Za-z0-9_-]{80,500}AA)/g' \
| tail -1 || true)
# Strip ANSI codes + newlines (TTY wraps the token mid-string), then match
# the sk-ant-oat…AA token. perl because BSD grep caps {n,m} at 255.
token=$(sed $'s/\x1b\\[[0-9;]*[a-zA-Z]//g' "$tmpfile" \
| tr -d '\n\r' \
| perl -ne 'print "$1\n" while /(sk-ant-oat[A-Za-z0-9_-]{80,500}AA)/g' \
| tail -1 || true)
if [[ -z "$TOKEN" ]]; then
local keep
keep=$(mktemp -t claude-setup-token-log.XXXXXX)
cp "$tmpfile" "$keep"
echo >&2
echo "No sk-ant-oat…AA token found. Raw log: $keep" >&2
exit 1
fi
}
prompt_for_pasted() {
local prefix="$1" # "oat" or "api"
local value
echo
echo "Paste your sk-ant-${prefix}… credential and press Enter."
echo "Nothing will appear on the screen as you paste — that's intentional."
echo "Paste once, then just press Enter to submit."
read -r -s -p "> " value </dev/tty
echo
if [[ -z "$value" ]]; then
echo "No input. Aborting." >&2
exit 1
fi
if [[ ! "$value" =~ ^sk-ant-${prefix} ]]; then
echo "Value does not start with sk-ant-${prefix}. Aborting." >&2
exit 1
fi
TOKEN="$value"
}
cat <<EOF
How would you like to authenticate?
1) Use Claude subscription — runs \`claude setup-token\` and saves the
resulting token in the Agent Vault.
2) I have my own OAuth token — paste an existing sk-ant-oat… token.
3) I have my own API key — paste an Anthropic API key (sk-ant-api…).
EOF
read -r -p "Choose [1/2/3]: " CHOICE </dev/tty
case "$CHOICE" in
1) capture_via_claude_setup_token ;;
2) prompt_for_pasted oat ;;
3) prompt_for_pasted api ;;
*) echo "Invalid choice." >&2; exit 1 ;;
esac
if [ -z "$token" ]; then
keep=$(mktemp -t claude-setup-token-log.XXXXXX)
cp "$tmpfile" "$keep"
echo >&2
echo "No sk-ant-oat…AA token found. Raw log: $keep" >&2
exit 1
fi
echo
echo "Got token: ${TOKEN:0:16}${TOKEN: -4}"
echo "Registering with OneCLI as '${SECRET_NAME}' (host pattern: ${HOST_PATTERN})…"
echo "Got token: ${token:0:16}${token: -4}"
echo "Saving it to your OneCLI vault as '${SECRET_NAME}' (host: ${HOST_PATTERN})…"
onecli secrets create \
--name "$SECRET_NAME" \
--type anthropic \
--value "$TOKEN" \
--value "$token" \
--host-pattern "$HOST_PATTERN"
echo "Done."