great-cto 2.67.0 → 2.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +42 -11
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -41,6 +41,7 @@ function parseArgs(argv) {
41
41
  command: "init",
42
42
  boardPort: 3141,
43
43
  boardNoOpen: false,
44
+ consoleBind: null,
44
45
  dir: process.cwd(),
45
46
  yes: false,
46
47
  dryRun: false,
@@ -87,8 +88,14 @@ function parseArgs(argv) {
87
88
  const v = a.slice("--host=".length);
88
89
  args.host = (v === "codex" || v === "claude-code") ? v : null;
89
90
  }
91
+ else if (a === "--bind")
92
+ args.consoleBind = argv[++i] ?? null;
93
+ else if (a.startsWith("--bind="))
94
+ args.consoleBind = a.slice("--bind=".length) || null;
90
95
  else if (a === "board")
91
96
  args.command = "board";
97
+ else if (a === "console")
98
+ args.command = "console";
92
99
  else if (a === "register")
93
100
  args.command = "register";
94
101
  else if (a === "ci")
@@ -187,8 +194,10 @@ async function runRegister(args) {
187
194
  }
188
195
  // ── Board server lifecycle helpers ───────────────────────────────────────────
189
196
  /** Absolute path to the board PID file (persists across CLI invocations). */
190
- function boardPidFilePath() {
191
- return join(homedir(), ".great_cto", "board.pid");
197
+ function boardPidFilePath(surface) {
198
+ // The two surfaces are separate processes with separate lifecycles — a console
199
+ // start must never kill the builder board (and vice versa).
200
+ return join(homedir(), ".great_cto", surface === "console" ? "console.pid" : "board.pid");
192
201
  }
193
202
  /**
194
203
  * Locate the board server.mjs — checks dev layouts then plugin cache versions
@@ -217,8 +226,8 @@ function findBoardServerPath() {
217
226
  * Kill the board server recorded in the PID file.
218
227
  * Returns true if a live process was terminated.
219
228
  */
220
- async function killExistingBoard() {
221
- const pidFile = boardPidFilePath();
229
+ async function killExistingBoard(surface) {
230
+ const pidFile = boardPidFilePath(surface);
222
231
  if (!fsExistsSync(pidFile))
223
232
  return false;
224
233
  const raw = readFileSync(pidFile, "utf8").trim();
@@ -296,12 +305,12 @@ async function restartBoardAfterUpgrade(port) {
296
305
  log(` ${green("✓")} board restarted → http://localhost:${port}`);
297
306
  }
298
307
  // ─────────────────────────────────────────────────────────────────────────────
299
- async function runBoard(args) {
308
+ async function runBoard(args, surface) {
300
309
  const { spawn } = await import("node:child_process");
301
- // Stop any existing board server (e.g. old version left over after upgrade)
302
- const killed = await killExistingBoard();
310
+ // Stop any existing server OF THIS SURFACE (board and console run side by side)
311
+ const killed = await killExistingBoard(surface);
303
312
  if (killed) {
304
- log(` ${dim("stopped previous board server")}`);
313
+ log(` ${dim(surface === "console" ? "stopped previous console server" : "stopped previous board server")}`);
305
314
  }
306
315
  const serverPath = findBoardServerPath();
307
316
  if (!serverPath) {
@@ -311,20 +320,25 @@ async function runBoard(args) {
311
320
  const nodeArgs = [serverPath];
312
321
  if (args.boardNoOpen)
313
322
  nodeArgs.push("--no-open");
323
+ if (surface)
324
+ nodeArgs.push("--surface", surface);
325
+ const env = { ...process.env, BOARD_PORT: String(args.boardPort) };
326
+ if (surface === "console" && args.consoleBind)
327
+ env.GREAT_CTO_HOST = args.consoleBind;
314
328
  const child = spawn(process.execPath, nodeArgs, {
315
- env: { ...process.env, BOARD_PORT: String(args.boardPort) },
329
+ env,
316
330
  stdio: "inherit",
317
331
  detached: false,
318
332
  });
319
333
  // Write PID so future invocations (including init upgrades) can find us
320
334
  try {
321
335
  mkdirSync(join(homedir(), ".great_cto"), { recursive: true });
322
- writeFileSync(boardPidFilePath(), String(child.pid));
336
+ writeFileSync(boardPidFilePath(surface), String(child.pid));
323
337
  }
324
338
  catch { /* best-effort */ }
325
339
  child.on("exit", code => {
326
340
  try {
327
- unlinkSync(boardPidFilePath());
341
+ unlinkSync(boardPidFilePath(surface));
328
342
  }
329
343
  catch { /* ignore */ }
330
344
  process.exit(code ?? 0);
@@ -338,6 +352,7 @@ ${bold("Usage:")}
338
352
  npx great-cto install [options] Same as init
339
353
  npx great-cto [init] [options] Detect + bootstrap
340
354
  npx great-cto board [--port 3141] [--no-open]
355
+ npx great-cto console [--port 8788] [--bind 0.0.0.0] [--no-open]
341
356
  npx great-cto register [--dir PATH]
342
357
  npx great-cto ci [path] [--no-archetype] [--no-budget]
343
358
  npx great-cto mcp [--sse --port N]
@@ -352,6 +367,12 @@ ${bold("Board:")}
352
367
  great-cto board --port 4000 Use a different port
353
368
  great-cto board --no-open Start server without opening browser
354
369
 
370
+ ${bold("Operator console (the second surface — invite-only, hostable):")}
371
+ great-cto console Serve ONLY the operator console (no dev board)
372
+ great-cto console --port 8788 Different port
373
+ great-cto console --bind 0.0.0.0 Reachable beyond this machine (tunnel/hosting);
374
+ operators sign in via invite links
375
+
355
376
  ${bold("Register:")}
356
377
  great-cto register Add this repo to ~/.great_cto/projects.json
357
378
  (auto-discovered after /audit or /start, but
@@ -972,6 +993,16 @@ async function main() {
972
993
  process.exit(1);
973
994
  }
974
995
  }
996
+ if (args.command === "console") {
997
+ try {
998
+ const code = await runBoard(args, "console");
999
+ process.exit(code);
1000
+ }
1001
+ catch (e) {
1002
+ error(e.message);
1003
+ process.exit(1);
1004
+ }
1005
+ }
975
1006
  if (args.command === "register") {
976
1007
  try {
977
1008
  const code = await runRegister(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "2.67.0",
3
+ "version": "2.69.0",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",