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();