diff --git a/script/infra-setup.sh b/script/infra-setup.sh index a921226..4fc99d2 100755 --- a/script/infra-setup.sh +++ b/script/infra-setup.sh @@ -293,9 +293,26 @@ Remaining one-time steps (operator, on the target host): The OAuth flow is interactive and must be completed *as the service account*, because Claude Code reads credentials from $HOME: - sudo -u tireless -H /usr/bin/npx -y @anthropic-ai/claude-code@2.1.220 + sudo -u tireless -H bash -c 'cd /var/lib/tireless && \ + npx -y @anthropic-ai/claude-code@2.1.220' # then: /login, and complete the browser flow + The `cd` is load-bearing. `-H` sets HOME, but sudo leaves the working + directory where you invoked it — typically your own 0700 home, which + `tireless` cannot read. npx then fails spawning its `sh -c claude` there: + + npm error Error: spawn sh EACCES + npm error path: '/home/grenade' + + which reads like a broken install rather than a directory permission. + + `bash -c` rather than `sudo -iu tireless`, because the account's shell is + nologin by design and a login shell will not start. + + Keep the pinned version in step with `CLAUDE_PACKAGE` in + crates/tireless-agent/src/claude.rs — the runner npx's that exact version, + and logging in with a different one warms the wrong cache. + This writes /var/lib/tireless/.claude.json. The token refreshes in place, which is why the unit declares StateDirectory=tireless (systemd creates it, owns it as the service account, and keeps it writable).