docs: add macOS setup gotchas (npm prefix, ffmpeg, Groq key format) - #45
Open
impactworks-dev wants to merge 182 commits into
Open
docs: add macOS setup gotchas (npm prefix, ffmpeg, Groq key format)#45impactworks-dev wants to merge 182 commits into
impactworks-dev wants to merge 182 commits into
Conversation
Captures real issues encountered during a fresh macOS setup walkthrough: - npm -g EACCES workaround via user-writable prefix - brew install ffmpeg required for local say + ffmpeg TTS fallback - Groq API keys are gsk_* (not org_*) to avoid 401 invalid_api_key - Clarify secrets live in .env, not the shell - launchd PATH must include Claude CLI dir to spawn subprocesses Also refreshes package-lock.json to match npm install. Co-Authored-By: Oz <oz-agent@warp.dev>
Document the one-time local setup for: - Obsidian Brain vault integration (path, registry, cleanup) - GitHub SSH ed25519 key with Keychain-backed auto-load - kepano/obsidian-skills install layout and user-level symlinks - Agent template vault configuration - launchd PATH requirements for npm-global binaries No secrets are recorded; machine-specific values stay in .env. Co-Authored-By: Oz <oz-agent@warp.dev>
Add a new chat_settings table to store per-chat preferences keyed by chat_id and key. Use it to persist the voice-reply toggle so the bot remembers each chat's voice mode across restarts. Changes: - db.ts: add chat_settings table and get/set/delete/getAllByKey helpers - bot.ts: load voice-enabled chats on startup; persist /voice toggle - index.ts: invoke loadVoiceEnabledChats() at startup and log result Co-Authored-By: Oz <oz-agent@warp.dev>
Add .gitignore entries for files that are either machine-specific or managed by tooling: - .agents/ and matching .claude/skills/* symlinks (managed by `skills`) - cli/ (third-party repo cloned for reference) - memory/ (personal knowledge base) - dashboard.html (generated from src/dashboard-html.ts) - TASKS.md (personal task tracker) - launchd/com.claudeclaw.tunnel.plist (absolute home paths) Also track skills-lock.json so the skill install set is reproducible. Co-Authored-By: Oz <oz-agent@warp.dev>
Introduces the warroom/ Python package: a WebSocket voice server that bridges browser audio to ClaudeClaw agents via either Gemini Live (default) or Deepgram STT + Cartesia TTS. Built against pipecat-ai 1.0.0: - WebsocketServerTransport owns the WebSocket server (host/port). - SileroVADAnalyzer is attached via WebsocketServerParams.vad_analyzer. - PipelineTask accepts params as a keyword-only argument. - LLMContext + LLMContextAggregatorPair replace the old OpenAILLMContext + llm.create_context_aggregator() pattern. - GeminiLiveLLMService replaces GeminiMultimodalLiveLLMService. - Cartesia/Deepgram service imports use their .tts / .stt submodules. - CartesiaTTSService uses Settings(voice=...) instead of deprecated voice_id kwarg. Also ignores warroom/.venv/ and warroom/__pycache__/ from VCS. Co-Authored-By: Oz <oz-agent@warp.dev>
The test 'truncates last_result to 500 chars' asserted toHaveLength(500) against a 1000-char input, but updateTaskAfterRun in src/db.ts truncates via result.slice(0, 4000). The input was therefore never truncated and the length check was unreachable. Align the test with the current runtime behavior: feed 5000 chars and assert the stored value is exactly 4000. Co-Authored-By: Oz <oz-agent@warp.dev>
Add War Room voice server on pipecat 1.0
rubening
reviewed
May 19, 2026
rubening
left a comment
There was a problem hiding this comment.
Review (Overnight Agent, Night #97)
Thanks for the setup docs content -- the macOS gotchas section is genuinely useful. However, this PR has several issues that make it unmergeable as-is:
- CLAUDE.md deletion -- This PR replaces the entire production CLAUDE.md with a generic template. CLAUDE.md contains the bot's personality, environment config, skills, special commands, and all operational instructions. Deleting it would break the bot.
- Unrelated code changes --
src/bot.ts,src/db.ts,src/index.tsmodifications for voice persistence, and an entirewarroom/Python package, are bundled with what's labeled as a docs PR. - Mixed commits -- 7 unrelated commits covering docs, code features, config changes, and a merge commit from your fork.
Recommendation: Please submit a clean PR with only the setup docs changes (a new docs/SETUP_NOTES.md and the relevant README additions). The voice persistence feature and warroom package are interesting but should be separate PRs with their own review.
The headRefName being main suggests these were committed directly to your fork's main -- consider using feature branches for future PRs to keep changes isolated.
- Add interactive dashboard with HTML UI and TypeScript backend - Add War Room client bundle, avatars, and HTML interface - Add Google OAuth authentication flow (scripts/google-auth.ts) - Update package dependencies (package.json, package-lock.json) - Add launchd service plists and restart-main.sh helper - Various src/ module updates and new TypeScript sources Co-Authored-By: Oz <oz-agent@warp.dev>
Ports five osrepo PRs onto the manual-port snapshot: - PR earlyaidopters#51 (security/dashboard): timing-safe token comparison + CORS Origin allowlist + War Room WebSocket upgrade auth gate - PR earlyaidopters#52 (security/dashboard-html): escape agent.name on 3 innerHTML render sites - PR earlyaidopters#57 (fix/scheduler): pass agentDefaultModel to runAgent for scheduled tasks (mission tasks already correct) - PR #67 (fix/orchestrator): pass target agent's model to runAgent in delegateToAgent (was undefined, ignored per-agent model) - PR #69 (fix/scheduler): wire memory ingestion into mission and scheduled task completion Skipped from PR earlyaidopters#51: CSRF middleware block (not present in snapshot), requireToken helper (not present in snapshot). Files: src/dashboard.ts, src/dashboard-html.ts, src/scheduler.ts, src/orchestrator.ts. +77 / -10.
Adds the full v2 dashboard frontend from osrepo/main:
- web/ (55 files): index.html, main.tsx, App.tsx, 14 pages, 19 components,
16 lib helpers, public/brain.glb (1.6MB)
- vite.config.ts: Vite root=web/, outDir=dist/web/, proxies /api+/ws+
/warroom-* to backend in dev, vendor chunk split
- package.json + package-lock.json: adds preact, vite, three,
monaco-editor, tailwindcss v4, dompurify, marked, lucide-preact,
wouter-preact, @preact/signals, @preact/preset-vite, @types/three,
@types/dompurify, @tailwindcss/vite
Backend wiring in src/dashboard.ts:
- Token middleware now gates only /api/*. SPA shell at / is public so
token-stripped URLs still load the app and the SPA reads ?token= from
window.location itself
- requireToken() helper for legacy HTML routes that embed DASHBOARD_TOKEN
- / route serves dist/web/index.html when present, falls back to legacy
inline HTML if DASHBOARD_LEGACY=true or bundle is missing
- /assets/* serves Vite-built JS/CSS/source-maps with immutable cache
- /:filename.{glb|gltf|bin|ktx2|wasm|svg|png|webp|ico} serves top-level
static files (e.g. /brain.glb for 3D Hive Mind)
- /warroom now calls requireToken() inline (was global middleware)
Known gaps vs osrepo/main (SPA pages will 404 on these endpoints):
- /api/dashboard/settings PATCH (Settings page)
- /api/security/kill-switch POST (Settings -> Security)
- /api/warroom/unpin POST (War Room)
These depend on src/kill-switches.ts and other osrepo modules not yet
ported. UI loads, most pages work, three buttons fail. Iterate later.
Files: src/dashboard.ts, package.json, package-lock.json, web/* (55),
vite.config.ts.
…, war room text) Dashboard fixes (earlier today): - token persisted in localStorage (no more 401 lockouts on fresh tabs/bookmarks) - SPA history fallback for deep routes (no more 404 on refresh of /mission etc.) - War Room voice WebSocket token fix (warroom-html.ts) Feature parity ported from cached osrepo/main: - /api/dashboard/settings (GET+PATCH), /api/security/kill-switch (POST) - /api/agents/suggestions (4 routes), /api/warroom/text/* (11 routes) + /warroom/text page - new modules: kill-switches, env-write, warroom-tool-policy, warroom-text-* - security.ts getScrubbedSdkEnv, state.ts abortByPrefix, memory-ingest extractViaClaude - buildMemoryContext opts param, AgentConfig.warroomTools (additive, backward compatible) db.ts: - new tables: dashboard_settings, agent_suggestions, agent_file_history - warroom_meetings meeting_type + chat_id migrations - 26 parity helper functions; getWarRoomTranscript/addWarRoomTranscript upgraded (back-compat) chore: harden .gitignore against untracked OAuth client_secret + personal working files
- Multi-stage Dockerfile with Python venv + Claude Code CLI baked in - docker-entrypoint.sh bridges Fly secrets to /app/.env, restores Claude creds - fly.toml: app config, persistent volume, single machine, no HTTP healthcheck - Run as non-root node user (Claude Code refuses --dangerously-skip-permissions as root) - agents/*/agent.yaml: obsidian vault path → /app/store/obsidian-brain (syncthing target) - .github/workflows/fly-deploy.yml: auto-deploy on push to main - scripts/fly-*.sh: setup, data migration, cutover, syncthing helpers - FLY-MIGRATION.md: end-to-end runbook
Includes daily brief, memory-to-tasks dispatcher, sidebar rebrand, dashboard updates that were running locally on Mac before cutover but missed the first CI commit.
Without this, every deploy wipes /home/node/.claude/projects/ and stored session IDs become orphans. Symlink the dir onto the Fly volume so sessions survive container restarts. No more manual DELETE FROM sessions needed.
Bumps machine to 2GB to fit 5 node processes + Gemini bursts. Each sub-agent uses its own Telegram bot token from /app/.env. When main exits, sub-agents die with the bash shell, supervisor restarts.
Mobile Safari ITP clears localStorage after 7 days of inactivity, causing all API calls to return 401 (dashboardToken becomes empty string). Frontend (api.ts): - On every load, persist token to BOTH localStorage AND a 30-day cookie (claw_token; Secure; SameSite=Lax). Cookie is also read as final fallback so cleared localStorage or private browsing never breaks auth. - All fetch helpers now send Authorization: Bearer <token> header in addition to the existing ?token= query param. Backend (dashboard.ts): - API auth middleware now accepts the token from (in order): 1. ?token= query param (original, always present when token is known) 2. Authorization: Bearer header (new - sent by every SPA fetch) 3. claw_token cookie (new - set by SPA on first authenticated load) This means once a mobile browser visits with ?token= once, subsequent visits work indefinitely without re-injecting the token.
Static token entry page at /auth.html. On any api 401/403, the SPA hops there with a return param. Form verifies the token via /api/health and persists to localStorage + cookie. Token already cached → silently redirects back. Force re-entry with ?forceLogin=1.
Adds refreshing state to useFetch so pages can distinguish first-load (loading) from background revalidation. Button now disables, swaps copy to Refreshing..., and spins the RefreshCw icon (Tailwinds animate-spin) while a fetch is in flight.
Claude Code SDK with OAuth (Pro/Max subscription) only populates usage on the final result event, not on per-assistant-message events. Without a fallback, lastCallCacheRead and lastCallInputTokens stayed 0 so the token_usage table got context_tokens=0 every turn, breaking /convolife percent calculation and the dashboard usage view. Fall back to the result event aggregate when per-message events were empty.
SPA calls /api/tokens, /api/health, /api/memories without ever sending a chatId. Backend filtered by empty string, returning zeros even though the DB has 254 rows for the real chat. Default to ALLOWED_CHAT_ID so the page shows the primary authorized chats data when no chatId is explicitly provided.
… nightly trend, date nav + sleep-history backend
…ly Vitals; metric-history backend + endpoint
…Metrics, 7-night trailing averages, retitle 90-day overview
… Daily Metrics sparklines, Movement goal; daily-log endpoint + jpg static serving
…ted text, not raw JSON
…ive, add PandaDoc + cross-links
…entry can't silently drop the whole history write
…onger matches apple_sleeping_wrist_temperature and drops all nights
Prime Reset is Dante's memoir-in-progress and practical field guide. This wires Nikki and the Content agent into it. - CLAUDE.md: scoped Prime Reset section — editorial rules (interview first, 5-8 voice-friendly questions then one follow-up at a time, preserve exact phrases, never invent memories/dialogue/emotions/medical details/beliefs/ tool usage/results, separate personal experience from medical advice, nothing publishes without Dante's approval, Substack canonical), the three content tracks, and the restrained Story Capture behaviour. - agents/content: Prime Reset drafting rules — first drafts only, from a completed source brief, Voice Guide binding, gaps return as questions rather than getting filled creatively, never publishes. Adds Prime Reset/ to the agent's Obsidian folders. Canonical spec lives in the Obsidian Brain at Prime Reset/Prime Reset Publishing System.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vPpZBLT8r9giMJ3VTfNZt
Work in progress since 2026-07-26, committed as-is. - Primary Contacts: new dashboard page, route and nav entry, contact data module, and three API endpoints (/api/primary-contacts, /apply, /retry-phones). - ClickUp connector: clickup_set_custom_field for writing custom field values to a task. - AGENTS.md: ImpactWorks brand standard — canonical palette, SF Pro type scale, logo usage rules, and the pointer to the November 2025 brand guidelines PDF. src/dashboard.ts also carries the model allowlist widening from the following commit; the two changes could not be separated cleanly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vPpZBLT8r9giMJ3VTfNZt
Nikki (main) and the Content agent both move to claude-opus-5 at Dante's direction. Content's path today was haiku-4-5 -> sonnet-4-6 -> opus-5. - agents/main/agent.yaml: model claude-opus-5. This is the real lever — bot.ts resolves the active model from agentDefaultModel, which config.ts populates from agent.yaml. - bot.ts / signal-bot.ts: /model opus alias now resolves to claude-opus-5, sonnet to claude-sonnet-4-6, and the hardcoded fallbacks follow. - dashboard: validModels widened to include claude-opus-5 and claude-sonnet-5, and the pickers now list them. Previously the dashboard rejected any model outside a stale four-item allowlist, so clicking a preset would silently knock an agent back to an older model. - dashboard-html: main defaults to Opus 5, sub-agents to Sonnet 5. Model ids verified against /v1/models before setting. Also excludes local scratch from the Docker build context — output/, outputs/, videos/, tmp/ and friends were adding ~220MB to every deploy. Adds the ImpactWorks agreement and Upwork scripts and docs that had been sitting untracked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vPpZBLT8r9giMJ3VTfNZt
Dante's call — two posts per week is now required, not a target. Replaces the earlier "one Book Journey is the floor, AI Recovery Lab is a target" wording, which contradicted the ClickUp spec. A mandatory cadence needs a defined failure mode or it just produces guilt, so the enforcement is a buffer rather than a streak: keep 1-2 approved-but- unpublished posts banked, publish from the bank when a slot comes due and nothing new is ready, and treat buffer-at-zero as the real warning sign rather than a missed draft. When the slot and the quality bar collide the slot wins — planned post, then a banked post, then a short Field Note from confirmed Story Bank material, then a clearly labelled research piece. What does not bend under deadline pressure: invent nothing, assert nothing unverified, keep personal experience separate from medical advice, and publish nothing without Dante's approval. If he is unreachable the slot slips; his approval is the one gate with no override. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vPpZBLT8r9giMJ3VTfNZt
Dante's corrections, 2026-08-03. - Cadence: replaces "two mandatory posts per week" with a launch ramp. One Book Journey post every Wednesday is the only mandatory slot. The Thursday AI Recovery Lab interview continues, but Friday publication is optional until two approved posts are banked AND four consecutive Wednesdays have published. A skipped Friday is explicitly not a miss right now. - Content agent identified as Claude Opus 5 (was still described as Sonnet 4.6 in prose after the model itself was changed). - Adds the ChatGPT/Codex review step to the documented order of hands: interview -> source brief -> Content agent draft -> Codex review -> Dante approves -> published. - Privacy boundary: this repo is PUBLIC. Personal stories, interview answers, medical or family material, source briefs, drafts, Story Bank contents and the Master Writing Sample live only in the private Obsidian Brain. The project description here is genericised accordingly; the repo keeps the mechanical workflow and nothing else. Article order changed in ClickUp and the Article Queue: "What Recovery Looks Like Three Years Later" leads, "The Proposal That Gave Me Back More Than My Work" moves to second. Those live in the private vault and ClickUp, not here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vPpZBLT8r9giMJ3VTfNZt
Dante's decisions, 2026-08-03. - No separate ClickUp status for ChatGPT/Codex review. An article stays in Draft while Codex reviews it and moves to Dante Review only once that editorial pass is complete. A card in Dante Review means Codex is done. - Public git history is accepted as-is. Do not rewrite history, force-push, or change repository visibility for this project. The forward rule is unchanged: detailed stories, interviews, medical and family material, source briefs and drafts stay out of this repo and live only in the private Obsidian Brain. The master writing sample was received today and saved verbatim to the private vault. It is not in this repo and must not be. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016vPpZBLT8r9giMJ3VTfNZt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a short "macOS setup notes" subsection in Step 1 of the README capturing the real issues a fresh Mac setup hits that aren't obvious from the current docs.
What's documented
npm i -gfails withEACCESon/usr/local/...— macOS ships an npm prefix owned by root. Docs the user-writable$HOME/.npm-globalworkaround so@anthropic-ai/claude-codeinstalls without sudo.say+ ffmpeg TTS fallback silently falls back to text without it. Addsbrew install ffmpeg.gsk_...values work; anorg_...identifier returnsHTTP 401 invalid_api_key..env, not the shell — pasting a key at the zsh prompt producescommand not found, easy to misread as a setup failure.claudelives in$HOME/.npm-global/bin).Also refreshes
package-lock.jsonto match a currentnpm install.Why
These were encountered during a real end-to-end setup on a clean Mac. Writing them in Step 1 up front saves newcomers from the same debug loop.
Testing
npm install,npm run build,npm run setupequivalent config generation, launchd install, andnpm run status— reports "All systems go."transcribeAudio()insrc/voice.ts(HTTP 200, correct transcript).synthesizeSpeech()(produces a valid OGG Opus buffer; round-trips back to text through Groq).Conversation
Generated with assistance from Warp (conversation: https://app.warp.dev/conversation/7f78a63a-b2fe-461c-b0e0-794ba9f23ae3)