Safe isolated Docker launcher for OpenCode.
# Build and verify
npm run build # Compiles src/ → dist/ via tsc
npm run typecheck # tsc --noEmit
npm test # vitest run (all tests)
npm test -- --run # Same, explicit flag
npm run test:watch # vitest watch mode
# Run the CLI locally (direct execution)
node dist/cli.js init --tool opencode --yes
node dist/cli.js doctor
node dist/cli.js launch --dry-run
node dist/cli.js clean --yes
# Run via installed binary
capsule init --tool opencode --yes
capsule doctor
capsule launch --dry-run
capsule clean --yes
codecapsuleremains available as a backwards-compatible alias.
- Entry:
src/cli.tswires Commander.js subcommands (init,doctor,launch,clean). - Commands:
src/commands/*.ts— each exports aCommandand arun*function. - Core:
src/core/types.ts+src/core/schemas.ts— Zod-validatedProfileandLocalConfig. - Docker:
src/docker/runner.tsbuildsdocker runargs;src/docker/dockerfile.tsgenerates the Dockerfile. - Adapters:
src/adapters/opencode.tsdefines import categories and path mappings for OpenCode. - Config:
.codecapsule/profile.json(committed) +.codecapsule/local.json(gitignored, machine-specific).
- ESM only:
"type": "module"in package.json. All imports use.jsextension. - Project-local state:
statePath,cachePath,configPathunder.codecapsule/(not Docker named volumes). - Host UID/GID: Dockerfile builds with
--build-arg USER_ID/GROUP_IDfor Linux write compatibility. - Image tags: Scoped by project slug + UID/GID (e.g.
codecapsule/opencode:safe-code-uid501-gid20). - Security defaults: No privileged, no docker socket, no host home mount, no SSH agent.
- Import model: Opt-in via
--import <category>; auth requires--confirm-auth-import. - Clean safety:
clean --yesremoves cache+image only; state requires--include-state.
- Framework: Vitest (no special config needed).
- Pattern:
test/**/*.test.ts. - Docker tests: E2E tests check
docker --version; skip if unavailable. - Mocking:
vi.doMock('node:child_process')for launch/doctor/clean tests. - Fixtures: JSON profiles in
test/fixtures/.
- All source under
src/, compiled todist/. tsconfig.json: strict, NodeNext module resolution,noEmitOnError: true.vitest.config.ts: minimal — justenvironment: 'node'..codecapsule/directory (created byinit) contains:profile.json— committed, shareablelocal.json— gitignored, machine-specific pathsDockerfile.opencode— committed, generated.gitignore— generated, ignoreslocal.json,state/,cache/,config/
- Containers run as non-root
codecapsuleuser withHOME=/home/codecapsule. - Bind mounts: workspace, state, cache, config (all project-local paths).
- Import mounts (read-only): Only when
--importflags are passed. --rmflag: Container is removed after exit (disposable by design).
- Tilde expansion:
resolveHostPath()insrc/core/import.tsexpands~/tohomedir()for Docker volume mounts. Never pass raw~to Docker. - Missing build:
launchwithout--buildexpects image to exist; runlaunch --buildfirst or afterclean. - State persistence: OpenCode writes auth/sessions to
.codecapsule/state/, config to.codecapsule/config/, cache to.codecapsule/cache/. All are bind-mounted into the container.
commander— CLI frameworkzod— Schema validation (Profile, LocalConfig)vitest— Testing
Node ≥22 required.