Skip to content

docs: add macOS setup gotchas (npm prefix, ffmpeg, Groq key format) - #45

Open
impactworks-dev wants to merge 182 commits into
earlyaidopters:mainfrom
impactworks-dev:main
Open

docs: add macOS setup gotchas (npm prefix, ffmpeg, Groq key format)#45
impactworks-dev wants to merge 182 commits into
earlyaidopters:mainfrom
impactworks-dev:main

Conversation

@impactworks-dev

Copy link
Copy Markdown

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 -g fails with EACCES on /usr/local/... — macOS ships an npm prefix owned by root. Docs the user-writable $HOME/.npm-global workaround so @anthropic-ai/claude-code installs without sudo.
  • Local voice output needs ffmpeg — the say + ffmpeg TTS fallback silently falls back to text without it. Adds brew install ffmpeg.
  • Groq API key format — only gsk_... values work; an org_... identifier returns HTTP 401 invalid_api_key.
  • Secrets belong in .env, not the shell — pasting a key at the zsh prompt produces command not found, easy to misread as a setup failure.
  • launchd PATH must include the Claude CLI dir — otherwise the bot starts but can't spawn Claude Code subprocesses (common when claude lives in $HOME/.npm-global/bin).

Also refreshes package-lock.json to match a current npm 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

  • Ran npm install, npm run build, npm run setup equivalent config generation, launchd install, and npm run status — reports "All systems go."
  • Verified Groq STT end-to-end via transcribeAudio() in src/voice.ts (HTTP 200, correct transcript).
  • Verified local TTS end-to-end via 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)

dante-crescenzi and others added 7 commits April 20, 2026 08:35
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 rubening left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. 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.
  2. Unrelated code changes -- src/bot.ts, src/db.ts, src/index.ts modifications for voice persistence, and an entire warroom/ Python package, are bundled with what's labeled as a docs PR.
  3. 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.

dante-crescenzi and others added 22 commits May 19, 2026 20:13
- 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.
dante-crescenzi and others added 30 commits July 10, 2026 08:09
… 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
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants