feat: add bare-metal runner — run straight from GitHub - #320
feat: add bare-metal runner — run straight from GitHub#320jelloeater-agent wants to merge 2 commits into
Conversation
Adds a one-liner entry point so OpenCode Manager can be started without Docker or cloning the repo: bunx opencode-manager The CLI bootstraps the full service: - Checks/install prerequisites (Bun, Git, OpenCode) - Creates data directory at ~/.opencode-manager/ - Auto-generates AUTH_SECRET on first run - Installs dependencies and builds frontend - Starts the backend server Also adds: - bin/ entry point (shell + Bun TypeScript) - scripts/build-npm.ts prepublish hook - package.json bin/files/prepack fields - README one-liner quick-start section
|
@chriswritescode-dev Hope this helps 👍🏻 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
📝 WalkthroughWalkthroughThe PR adds a Bun-based ChangesCLI packaging and runtime
Estimated code review effort: 3 (Moderate) | ~30 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant opencode_manager as opencode-manager.ts
participant bun as Bun
participant backend as Backend entry
opencode_manager->>bun: Install dependencies and build frontend
opencode_manager->>backend: Spawn backend with loaded environment
backend-->>opencode_manager: Close or error event
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (2)
scripts/build-npm.ts (2)
14-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse structured logging instead of raw console output.
This script adds
console.error/console.logcalls for operational output. Route these messages through the repository’s structured logger so packaging logs remain consistent and machine-readable.As per coding guidelines: do not use
console.log; use structured logging instead.Also applies to: 21-27
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-npm.ts` at line 14, Replace the raw console.error and console.log calls in the build script, including the command-failure output and the lines referenced around 21-27, with the repository’s structured logger. Preserve each message’s existing content and severity while routing operational packaging output through the logger.Source: Coding guidelines
7-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftShare the frontend-build orchestration with the CLI path.
This duplicates the
run/frontend-build flow already implemented inbin/opencode-manager.ts:186-199. Extract a shared helper so the npm packaging path and bare-metal startup cannot drift in their build and failure behavior.As per coding guidelines: follow DRY and SOLID principles, keeping responsibilities focused.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/build-npm.ts` around lines 7 - 25, Extract the duplicated frontend distribution build-and-failure flow from scripts/build-npm.ts and the corresponding CLI logic in bin/opencode-manager.ts into a shared helper. Reuse that helper from both paths so frontend existence checks, build invocation, inherited stdio, and nonzero-failure handling remain consistent; keep packaging and startup-specific responsibilities outside the helper.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/opencode-manager`:
- Around line 10-17: Update the command-selection logic in the opencode-manager
launcher so Bun installation is not gated on Node being available. Use curl
availability, or attempt the Bun installer directly, allowing fresh machines
without Bun or Node to install Bun; retain the existing error path only when the
installer cannot be run.
In `@bin/opencode-manager.ts`:
- Line 133: Remove the unused pkgDir parameter from the ensureDataDir function
signature and update its call site to invoke it without that argument. Preserve
the function’s existing dataDir and workspaceDir behavior.
- Line 85: Update the version check around the `ver` comparison to use numeric
semantic-version ordering rather than string lexicographic comparison, ensuring
versions such as `1.0.9` are correctly recognized as older than `1.0.137` and
upgraded when required.
- Line 69: Remove the added implementation comments in bin/opencode-manager.ts
at lines 69, 80, 129, 177, 204, and 219, including the installation re-check,
version-check, package-directory, fallback-install, env-loading, and
production-mode comments. Leave the surrounding self-documenting code and
behavior unchanged.
- Line 162: Update the writeFileSync call that creates the generated secret file
at envPath to explicitly set file mode 0o600, ensuring only the owner can read
or write the .env contents.
- Line 236: Update the child process close handler in bin/opencode-manager.ts so
signal termination does not map to exit status 0: use the close callback’s
signal information to return a non-zero failure status when signal is set, while
preserving the child exit code for normal exits.
In `@README.md`:
- Line 41: Update the bootstrap descriptions at README.md lines 41-41 and 51-51
to state that Git is required, not installed by the launcher; keep the existing
claims about installing Bun and OpenCode and starting the server.
In `@scripts/build-npm.ts`:
- Around line 19-25: The frontend packaging flow currently skips rebuilding
based only on frontendDist existence, which can publish stale assets. Update the
prepack/build logic around frontendDist and the frontend build command to always
rebuild during packaging, or replace the existence check with reliable source
freshness validation such as a hash or manifest.
---
Nitpick comments:
In `@scripts/build-npm.ts`:
- Line 14: Replace the raw console.error and console.log calls in the build
script, including the command-failure output and the lines referenced around
21-27, with the repository’s structured logger. Preserve each message’s existing
content and severity while routing operational packaging output through the
logger.
- Around line 7-25: Extract the duplicated frontend distribution
build-and-failure flow from scripts/build-npm.ts and the corresponding CLI logic
in bin/opencode-manager.ts into a shared helper. Reuse that helper from both
paths so frontend existence checks, build invocation, inherited stdio, and
nonzero-failure handling remain consistent; keep packaging and startup-specific
responsibilities outside the helper.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3a36829f-bd48-4399-88f6-ccfb49ef8fb9
📒 Files selected for processing (5)
README.mdbin/opencode-managerbin/opencode-manager.tspackage.jsonscripts/build-npm.ts
| elif command -v node &>/dev/null; then | ||
| echo "opencode-manager: Bun is required but not found. Installing..." | ||
| curl -fsSL https://bun.sh/install | bash | ||
| export PATH="$HOME/.bun/bin:$PATH" | ||
| exec bun run "$SCRIPT_DIR/opencode-manager.ts" "$@" | ||
| else | ||
| echo "opencode-manager: Neither bun nor node found. Please install Bun: https://bun.sh" >&2 | ||
| exit 1 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not require Node to install Bun.
On a fresh machine with neither Bun nor Node, this exits instead of running the Bun installer, even though the installer does not require Node. Gate this branch on curl (or attempt installation directly) instead.
Proposed fix
-elif command -v node &>/dev/null; then
+elif command -v curl &>/dev/null; then📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| elif command -v node &>/dev/null; then | |
| echo "opencode-manager: Bun is required but not found. Installing..." | |
| curl -fsSL https://bun.sh/install | bash | |
| export PATH="$HOME/.bun/bin:$PATH" | |
| exec bun run "$SCRIPT_DIR/opencode-manager.ts" "$@" | |
| else | |
| echo "opencode-manager: Neither bun nor node found. Please install Bun: https://bun.sh" >&2 | |
| exit 1 | |
| elif command -v curl &>/dev/null; then | |
| echo "opencode-manager: Bun is required but not found. Installing..." | |
| curl -fsSL https://bun.sh/install | bash | |
| export PATH="$HOME/.bun/bin:$PATH" | |
| exec bun run "$SCRIPT_DIR/opencode-manager.ts" "$@" | |
| else | |
| echo "opencode-manager: Neither bun nor node found. Please install Bun: https://bun.sh" >&2 | |
| exit 1 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/opencode-manager` around lines 10 - 17, Update the command-selection
logic in the opencode-manager launcher so Bun installation is not gated on Node
being available. Use curl availability, or attempt the Bun installer directly,
allowing fresh machines without Bun or Node to install Bun; retain the existing
error path only when the installer cannot be run.
| if (install.status !== 0) { | ||
| die('Failed to install Bun. Install manually: https://bun.sh') | ||
| } | ||
| // Re-check after install |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the added implementation comments.
The TypeScript rules require self-documenting code without comments.
bin/opencode-manager.ts#L69-L69: remove the installation re-check comment.bin/opencode-manager.ts#L80-L80: remove the version-check comment.bin/opencode-manager.ts#L129-L129: remove the package-directory context comment.bin/opencode-manager.ts#L177-L177: remove the fallback-install comment.bin/opencode-manager.ts#L204-L204: remove the env-loading comment.bin/opencode-manager.ts#L219-L219: remove the production-mode comment.
As per coding guidelines: “Do not add comments; code should be self-documenting.”
📍 Affects 1 file
bin/opencode-manager.ts#L69-L69(this comment)bin/opencode-manager.ts#L80-L80bin/opencode-manager.ts#L129-L129bin/opencode-manager.ts#L177-L177bin/opencode-manager.ts#L204-L204bin/opencode-manager.ts#L219-L219
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/opencode-manager.ts` at line 69, Remove the added implementation comments
in bin/opencode-manager.ts at lines 69, 80, 129, 177, 204, and 219, including
the installation re-check, version-check, package-directory, fallback-install,
env-loading, and production-mode comments. Leave the surrounding
self-documenting code and behavior unchanged.
Source: Coding guidelines
| const match = v.stdout.match(/(\d+\.\d+\.\d+)/) | ||
| if (match) { | ||
| const ver = match[1] | ||
| if (ver >= '1.0.137') { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Compare OpenCode versions numerically.
Line 85 accepts 1.0.9 as newer than 1.0.137 via lexicographic comparison, so an unsupported OpenCode version is not upgraded. Use a numeric semver comparison.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/opencode-manager.ts` at line 85, Update the version check around the
`ver` comparison to use numeric semantic-version ordering rather than string
lexicographic comparison, ensuring versions such as `1.0.9` are correctly
recognized as older than `1.0.137` and upgraded when required.
| return resolve(import.meta.dir, '..') | ||
| } | ||
|
|
||
| function ensureDataDir(pkgDir: string): { dataDir: string; workspaceDir: string } { |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the unused pkgDir parameter.
ensureDataDir never uses pkgDir; remove it from the function and its call site.
As per coding guidelines: “Do not leave dead code, commented-out blocks, unused variables, or unused imports.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/opencode-manager.ts` at line 133, Remove the unused pkgDir parameter from
the ensureDataDir function signature and update its call site to invoke it
without that argument. Preserve the function’s existing dataDir and workspaceDir
behavior.
Source: Coding guidelines
| `OPENCODE_HOST=127.0.0.1`, | ||
| ].join('\n') + '\n' | ||
|
|
||
| writeFileSync(envPath, content) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Restrict permissions on the generated secret file.
writeFileSync creates .env using the process umask, commonly making AUTH_SECRET readable by other local users. Write it with mode 0o600.
Proposed fix
- writeFileSync(envPath, content)
+ writeFileSync(envPath, content, { mode: 0o600 })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| writeFileSync(envPath, content) | |
| writeFileSync(envPath, content, { mode: 0o600 }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/opencode-manager.ts` at line 162, Update the writeFileSync call that
creates the generated secret file at envPath to explicitly set file mode 0o600,
ensuring only the owner can read or write the .env contents.
| env: { ...process.env }, | ||
| }) | ||
|
|
||
| child.on('close', (code) => process.exit(code ?? 0)) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Do not mask signal termination as success.
A backend killed by a signal produces code === null, and Line 236 exits the CLI with status 0. Preserve a non-zero failure status when signal is set.
Proposed fix
- child.on('close', (code) => process.exit(code ?? 0))
+ child.on('close', (code, signal) => process.exit(code ?? (signal ? 1 : 0)))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| child.on('close', (code) => process.exit(code ?? 0)) | |
| child.on('close', (code, signal) => process.exit(code ?? (signal ? 1 : 0))) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/opencode-manager.ts` at line 236, Update the child process close handler
in bin/opencode-manager.ts so signal termination does not map to exit status 0:
use the close callback’s signal information to return a non-zero failure status
when signal is set, while preserving the child exit code for normal exits.
| bunx opencode-manager | ||
| ``` | ||
|
|
||
| This installs prerequisites (Bun, OpenCode, Git), sets up `~/.opencode-manager/`, and starts the server. Open `http://localhost:5003`. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Correct the Git-installation claim.
The launcher does not install Git; ensureGit() exits when it is unavailable.
README.md#L41-L41: state that Git is required rather than installed.README.md#L51-L51: update the repeated bootstrap description accordingly.
📍 Affects 1 file
README.md#L41-L41(this comment)README.md#L51-L51
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` at line 41, Update the bootstrap descriptions at README.md lines
41-41 and 51-51 to state that Git is required, not installed by the launcher;
keep the existing claims about installing Bun and OpenCode and starting the
server.
| const frontendDist = resolve(root, 'frontend', 'dist') | ||
| if (!existsSync(frontendDist)) { | ||
| console.log('Building frontend for npm publish...') | ||
| run('bun', ['run', 'build:frontend']) | ||
| } else { | ||
| console.log('Frontend already built, skipping.') | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not use directory existence as the freshness check.
prepack can run after frontend source changes while frontend/dist still exists, causing npm to publish stale frontend assets. Always rebuild during packaging, or validate freshness with a source hash/manifest.
Proposed fix
-import { existsSync } from 'fs'
import { resolve } from 'path'
const root = resolve(import.meta.dir, '..')
@@
-const frontendDist = resolve(root, 'frontend', 'dist')
-if (!existsSync(frontendDist)) {
- console.log('Building frontend for npm publish...')
- run('bun', ['run', 'build:frontend'])
-} else {
- console.log('Frontend already built, skipping.')
-}
+console.log('Building frontend for npm publish...')
+run('bun', ['run', 'build:frontend'])📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const frontendDist = resolve(root, 'frontend', 'dist') | |
| if (!existsSync(frontendDist)) { | |
| console.log('Building frontend for npm publish...') | |
| run('bun', ['run', 'build:frontend']) | |
| } else { | |
| console.log('Frontend already built, skipping.') | |
| } | |
| console.log('Building frontend for npm publish...') | |
| run('bun', ['run', 'build:frontend']) |
🧰 Tools
🪛 ast-grep (0.44.1)
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from 'child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/build-npm.ts` around lines 19 - 25, The frontend packaging flow
currently skips rebuilding based only on frontendDist existence, which can
publish stale assets. Update the prepack/build logic around frontendDist and the
frontend build command to always rebuild during packaging, or replace the
existence check with reliable source freshness validation such as a hash or
manifest.
|
Closing — distribution strategy needs rework. Verified blockers: |
|
@chriswritescode-dev ... yeah, the package name being taken already is a big oof... |
What
Run OpenCode Manager with one command — no clone, no Docker, no npm publish:
How
The CLI bootstraps the full service on first run:
~/.opencode-manager/AUTH_SECRETon first run:5003Edit
~/.opencode-manager/.envto customize ports, auth, etc.Files
bin/opencode-managerbin/opencode-manager.tsscripts/build-npm.tsprepackhook — builds frontend for publishpackage.jsonbin,files,prepackfieldsREADME.mdWhy
Docker and clone-and-configure are friction.
bunx github:...should be the fastest path from zero to running.Summary by CodeRabbit
New Features
--helpand--versionoptions.Documentation