File size: 3,125 Bytes
5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb a0d0463 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb 5683c00 2cad6bb a0d0463 5683c00 2cad6bb a0d0463 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | const { spawn, spawnSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const PORT = String(process.env.PORT || 7860);
const HOST = "0.0.0.0";
// Default workspace: use HOME so /home/node/chat temp project 会在 UI 里直接可见
const HOME = process.env.HOME || "/home/node";
const WORKSPACE_DIR = process.env.WORKSPACE_DIR || HOME;
function resolveOpencodeBin() {
const localBin = path.join(__dirname, "node_modules", ".bin", "opencode");
if (fs.existsSync(localBin)) return localBin;
return "opencode";
}
function spawnSyncSafe(cmd, args) {
const r = spawnSync(cmd, args, { encoding: "utf-8" });
if (r.error) return "not found";
return (r.stdout || r.stderr || "").trim().split("\n")[0] || "ok";
}
function buildEnv() {
const env = { ...process.env };
// Map OPENCODE_PASSWORD -> OPENCODE_SERVER_PASSWORD (official var used by server basic-auth)
if (!env.OPENCODE_SERVER_PASSWORD && env.OPENCODE_PASSWORD) {
env.OPENCODE_SERVER_PASSWORD = env.OPENCODE_PASSWORD;
}
if (!env.OPENCODE_SERVER_USERNAME) {
env.OPENCODE_SERVER_USERNAME = env.OPENCODE_USERNAME || "opencode";
}
// Ensure PATH always contains local bin and bun
const prefix = "/home/node/app/node_modules/.bin:/usr/local/bun/bin";
env.PATH = env.PATH ? `${prefix}:${env.PATH}` : prefix;
// Keep your concurrency limits
env.OPENCODE_BACKGROUND_CONCURRENCY = env.OPENCODE_BACKGROUND_CONCURRENCY || "1";
env.OPENCODE_MODEL_CONCURRENCY = env.OPENCODE_MODEL_CONCURRENCY || "1";
return env;
}
function ensureUserDirs() {
// Your requested project folders under /home/node
for (const d of ["chat", "temp", "project"]) {
fs.mkdirSync(path.join(HOME, d), { recursive: true });
}
}
let child = null;
function startOpenCode() {
ensureUserDirs();
fs.mkdirSync(WORKSPACE_DIR, { recursive: true });
const opencodeBin = resolveOpencodeBin();
const env = buildEnv();
console.log(`[start] HOME=${HOME}`);
console.log(`[start] workspace=${WORKSPACE_DIR}`);
console.log(`[start] opencode=${opencodeBin}`);
console.log(`[start] git=${spawnSyncSafe("git", ["--version"])}`);
console.log(`[start] curl=${spawnSyncSafe("curl", ["--version"])}`);
console.log(`[start] rg=${spawnSyncSafe("rg", ["--version"])}`);
console.log(`[start] jq=${spawnSyncSafe("jq", ["--version"])}`);
console.log(`[start] python3=${spawnSyncSafe("python3", ["--version"])}`);
console.log(`[start] bun=${spawnSyncSafe("bun", ["--version"])}`);
if (!env.OPENCODE_SERVER_PASSWORD) {
console.log("[start] WARNING: OPENCODE_SERVER_PASSWORD is NOT set (public access).");
}
console.log(`[start] Starting OpenCode web on ${HOST}:${PORT}`);
child = spawn(opencodeBin, ["web", "--port", PORT, "--hostname", HOST], {
env,
cwd: WORKSPACE_DIR,
stdio: "inherit",
});
child.on("close", (code) => {
console.log(`[start] OpenCode exited with code ${code}. Restarting in 5 seconds...`);
setTimeout(startOpenCode, 5000);
});
}
process.on("SIGTERM", () => child?.kill("SIGTERM"));
process.on("SIGINT", () => child?.kill("SIGINT"));
startOpenCode(); |