@katyella/legio 0.1.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.
- package/CHANGELOG.md +422 -0
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/agents/builder.md +141 -0
- package/agents/coordinator.md +351 -0
- package/agents/cto.md +196 -0
- package/agents/gateway.md +276 -0
- package/agents/lead.md +281 -0
- package/agents/merger.md +156 -0
- package/agents/monitor.md +212 -0
- package/agents/reviewer.md +142 -0
- package/agents/scout.md +131 -0
- package/agents/supervisor.md +416 -0
- package/bin/legio.mjs +38 -0
- package/package.json +77 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +102 -0
- package/src/agents/hooks-deployer.test.ts +1820 -0
- package/src/agents/hooks-deployer.ts +574 -0
- package/src/agents/identity.test.ts +614 -0
- package/src/agents/identity.ts +385 -0
- package/src/agents/lifecycle.test.ts +202 -0
- package/src/agents/lifecycle.ts +184 -0
- package/src/agents/manifest.test.ts +558 -0
- package/src/agents/manifest.ts +297 -0
- package/src/agents/overlay.test.ts +592 -0
- package/src/agents/overlay.ts +316 -0
- package/src/beads/client.test.ts +210 -0
- package/src/beads/client.ts +227 -0
- package/src/beads/molecules.test.ts +320 -0
- package/src/beads/molecules.ts +209 -0
- package/src/commands/agents.test.ts +325 -0
- package/src/commands/agents.ts +286 -0
- package/src/commands/clean.test.ts +730 -0
- package/src/commands/clean.ts +653 -0
- package/src/commands/completions.test.ts +346 -0
- package/src/commands/completions.ts +950 -0
- package/src/commands/coordinator.test.ts +1524 -0
- package/src/commands/coordinator.ts +880 -0
- package/src/commands/costs.test.ts +1015 -0
- package/src/commands/costs.ts +473 -0
- package/src/commands/dashboard.test.ts +94 -0
- package/src/commands/dashboard.ts +607 -0
- package/src/commands/doctor.test.ts +295 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/down.test.ts +308 -0
- package/src/commands/down.ts +124 -0
- package/src/commands/errors.test.ts +648 -0
- package/src/commands/errors.ts +255 -0
- package/src/commands/feed.test.ts +579 -0
- package/src/commands/feed.ts +368 -0
- package/src/commands/gateway.test.ts +698 -0
- package/src/commands/gateway.ts +419 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +539 -0
- package/src/commands/hooks.test.ts +292 -0
- package/src/commands/hooks.ts +210 -0
- package/src/commands/init.test.ts +211 -0
- package/src/commands/init.ts +622 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +455 -0
- package/src/commands/log.test.ts +1556 -0
- package/src/commands/log.ts +752 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +544 -0
- package/src/commands/mail.test.ts +1726 -0
- package/src/commands/mail.ts +926 -0
- package/src/commands/merge.test.ts +676 -0
- package/src/commands/merge.ts +374 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +150 -0
- package/src/commands/monitor.test.ts +151 -0
- package/src/commands/monitor.ts +394 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +373 -0
- package/src/commands/prime.test.ts +467 -0
- package/src/commands/prime.ts +386 -0
- package/src/commands/replay.test.ts +742 -0
- package/src/commands/replay.ts +367 -0
- package/src/commands/run.test.ts +443 -0
- package/src/commands/run.ts +365 -0
- package/src/commands/server.test.ts +626 -0
- package/src/commands/server.ts +298 -0
- package/src/commands/sling.test.ts +810 -0
- package/src/commands/sling.ts +700 -0
- package/src/commands/spec.test.ts +206 -0
- package/src/commands/spec.ts +171 -0
- package/src/commands/status.test.ts +276 -0
- package/src/commands/status.ts +339 -0
- package/src/commands/stop.test.ts +357 -0
- package/src/commands/stop.ts +119 -0
- package/src/commands/supervisor.test.ts +186 -0
- package/src/commands/supervisor.ts +544 -0
- package/src/commands/trace.test.ts +746 -0
- package/src/commands/trace.ts +332 -0
- package/src/commands/up.test.ts +597 -0
- package/src/commands/up.ts +275 -0
- package/src/commands/watch.test.ts +152 -0
- package/src/commands/watch.ts +238 -0
- package/src/commands/worktree.test.ts +648 -0
- package/src/commands/worktree.ts +266 -0
- package/src/config.test.ts +496 -0
- package/src/config.ts +616 -0
- package/src/doctor/agents.test.ts +448 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +184 -0
- package/src/doctor/config-check.ts +185 -0
- package/src/doctor/consistency.test.ts +645 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +284 -0
- package/src/doctor/databases.ts +211 -0
- package/src/doctor/dependencies.test.ts +150 -0
- package/src/doctor/dependencies.ts +179 -0
- package/src/doctor/logs.test.ts +244 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +210 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +285 -0
- package/src/doctor/structure.ts +195 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +130 -0
- package/src/doctor/version.ts +131 -0
- package/src/e2e/chat-flow.test.ts +346 -0
- package/src/e2e/init-sling-lifecycle.test.ts +288 -0
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +246 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +344 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/global-setup.ts +14 -0
- package/src/index.ts +339 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +118 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +812 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +258 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +873 -0
- package/src/mail/client.ts +236 -0
- package/src/mail/store.test.ts +815 -0
- package/src/mail/store.ts +402 -0
- package/src/merge/queue.test.ts +449 -0
- package/src/merge/queue.ts +262 -0
- package/src/merge/resolver.test.ts +1453 -0
- package/src/merge/resolver.ts +759 -0
- package/src/metrics/store.test.ts +1167 -0
- package/src/metrics/store.ts +511 -0
- package/src/metrics/summary.test.ts +397 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +643 -0
- package/src/metrics/transcript.ts +351 -0
- package/src/mulch/client.test.ts +547 -0
- package/src/mulch/client.ts +416 -0
- package/src/server/audit-store.test.ts +384 -0
- package/src/server/audit-store.ts +257 -0
- package/src/server/headless.test.ts +180 -0
- package/src/server/headless.ts +151 -0
- package/src/server/index.test.ts +241 -0
- package/src/server/index.ts +317 -0
- package/src/server/public/app.js +187 -0
- package/src/server/public/apple-touch-icon.png +0 -0
- package/src/server/public/components/agent-badge.js +37 -0
- package/src/server/public/components/data-table.js +114 -0
- package/src/server/public/components/gateway-chat.js +256 -0
- package/src/server/public/components/issue-card.js +96 -0
- package/src/server/public/components/layout.js +88 -0
- package/src/server/public/components/message-bubble.js +120 -0
- package/src/server/public/components/stat-card.js +26 -0
- package/src/server/public/components/terminal-panel.js +140 -0
- package/src/server/public/favicon-16.png +0 -0
- package/src/server/public/favicon-32.png +0 -0
- package/src/server/public/favicon.ico +0 -0
- package/src/server/public/favicon.png +0 -0
- package/src/server/public/index.html +64 -0
- package/src/server/public/lib/api.js +35 -0
- package/src/server/public/lib/markdown.js +8 -0
- package/src/server/public/lib/preact-setup.js +8 -0
- package/src/server/public/lib/state.js +99 -0
- package/src/server/public/lib/utils.js +309 -0
- package/src/server/public/lib/ws.js +79 -0
- package/src/server/public/views/chat.js +983 -0
- package/src/server/public/views/costs.js +692 -0
- package/src/server/public/views/dashboard.js +781 -0
- package/src/server/public/views/gateway-chat.js +622 -0
- package/src/server/public/views/inspect.js +399 -0
- package/src/server/public/views/issues.js +470 -0
- package/src/server/public/views/setup.js +94 -0
- package/src/server/public/views/task-detail.js +422 -0
- package/src/server/routes.test.ts +3816 -0
- package/src/server/routes.ts +1964 -0
- package/src/server/websocket.test.ts +288 -0
- package/src/server/websocket.ts +196 -0
- package/src/sessions/compat.test.ts +109 -0
- package/src/sessions/compat.ts +17 -0
- package/src/sessions/store.test.ts +969 -0
- package/src/sessions/store.ts +480 -0
- package/src/test-helpers.test.ts +97 -0
- package/src/test-helpers.ts +143 -0
- package/src/types.ts +708 -0
- package/src/watchdog/daemon.test.ts +1233 -0
- package/src/watchdog/daemon.ts +533 -0
- package/src/watchdog/health.test.ts +371 -0
- package/src/watchdog/health.ts +248 -0
- package/src/watchdog/triage.test.ts +162 -0
- package/src/watchdog/triage.ts +193 -0
- package/src/worktree/manager.test.ts +444 -0
- package/src/worktree/manager.ts +224 -0
- package/src/worktree/tmux.test.ts +1238 -0
- package/src/worktree/tmux.ts +644 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +132 -0
- package/templates/overlay.md.tmpl +79 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: legio server <subcommand>
|
|
3
|
+
*
|
|
4
|
+
* Starts the local web UI server for project monitoring.
|
|
5
|
+
* The actual server implementation lives in src/server/index.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type SpawnOptions, spawn } from "node:child_process";
|
|
9
|
+
import { access, readFile, unlink, writeFile } from "node:fs/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { loadConfig } from "../config.ts";
|
|
12
|
+
import { ValidationError } from "../errors.ts";
|
|
13
|
+
import { isProcessRunning } from "../watchdog/health.ts";
|
|
14
|
+
|
|
15
|
+
function getFlag(args: string[], flag: string): string | undefined {
|
|
16
|
+
const idx = args.indexOf(flag);
|
|
17
|
+
if (idx === -1 || idx + 1 >= args.length) return undefined;
|
|
18
|
+
return args[idx + 1];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasFlag(args: string[], flag: string): boolean {
|
|
22
|
+
return args.includes(flag);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Dependency injection for testing. Uses real implementations when omitted. */
|
|
26
|
+
export interface ServerDeps {
|
|
27
|
+
_isProcessRunning?: (pid: number) => boolean;
|
|
28
|
+
_spawn?: (cmd: string, args: string[], opts: SpawnOptions) => { pid?: number; unref: () => void };
|
|
29
|
+
_sleep?: (ms: number) => Promise<void>;
|
|
30
|
+
_startServer?: (opts: {
|
|
31
|
+
port: number;
|
|
32
|
+
host: string;
|
|
33
|
+
root: string;
|
|
34
|
+
shouldOpen: boolean;
|
|
35
|
+
autoStartCoordinator: boolean;
|
|
36
|
+
}) => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const SERVER_HELP = `legio server <subcommand>
|
|
40
|
+
|
|
41
|
+
Subcommands:
|
|
42
|
+
start Start the local web UI server
|
|
43
|
+
stop Stop the daemon server
|
|
44
|
+
status Show daemon server status
|
|
45
|
+
|
|
46
|
+
Options (start):
|
|
47
|
+
--port <n> Port to listen on (default: 4173)
|
|
48
|
+
--host <addr> Bind address (default: 127.0.0.1)
|
|
49
|
+
--open Auto-open browser after server starts
|
|
50
|
+
--daemon Run server as a background daemon
|
|
51
|
+
|
|
52
|
+
Options (status):
|
|
53
|
+
--json JSON output
|
|
54
|
+
|
|
55
|
+
--help, -h Show this help
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if a file exists at the given path.
|
|
60
|
+
*/
|
|
61
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
62
|
+
try {
|
|
63
|
+
await access(path);
|
|
64
|
+
return true;
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns the path to the server PID file.
|
|
72
|
+
*/
|
|
73
|
+
function serverPidPath(projectRoot: string): string {
|
|
74
|
+
return join(projectRoot, ".legio", "server.pid");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Read the PID from the server PID file.
|
|
79
|
+
* Returns null if the file doesn't exist or can't be parsed.
|
|
80
|
+
*/
|
|
81
|
+
export async function readServerPid(projectRoot: string): Promise<number | null> {
|
|
82
|
+
const pidPath = serverPidPath(projectRoot);
|
|
83
|
+
if (!(await fileExists(pidPath))) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const text = await readFile(pidPath, "utf-8");
|
|
88
|
+
const pid = Number.parseInt(text.trim(), 10);
|
|
89
|
+
if (Number.isNaN(pid) || pid <= 0) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return pid;
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Write the server PID to the PID file.
|
|
100
|
+
*/
|
|
101
|
+
export async function writeServerPid(projectRoot: string, pid: number): Promise<void> {
|
|
102
|
+
const pidPath = serverPidPath(projectRoot);
|
|
103
|
+
await writeFile(pidPath, String(pid), "utf-8");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Remove the server PID file.
|
|
108
|
+
*/
|
|
109
|
+
export async function removeServerPid(projectRoot: string): Promise<void> {
|
|
110
|
+
const pidPath = serverPidPath(projectRoot);
|
|
111
|
+
try {
|
|
112
|
+
await unlink(pidPath);
|
|
113
|
+
} catch {
|
|
114
|
+
// File may already be gone — not an error
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function serverCommand(args: string[], deps: ServerDeps = {}): Promise<void> {
|
|
119
|
+
if (args.includes("--help") || args.includes("-h") || args.length === 0) {
|
|
120
|
+
process.stdout.write(`${SERVER_HELP}\n`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const subcommand = args[0];
|
|
125
|
+
const subArgs = args.slice(1);
|
|
126
|
+
|
|
127
|
+
switch (subcommand) {
|
|
128
|
+
case "start":
|
|
129
|
+
await startServer(subArgs, deps);
|
|
130
|
+
break;
|
|
131
|
+
case "stop":
|
|
132
|
+
await stopServer(subArgs, deps);
|
|
133
|
+
break;
|
|
134
|
+
case "status":
|
|
135
|
+
await statusServer(subArgs, deps);
|
|
136
|
+
break;
|
|
137
|
+
default:
|
|
138
|
+
process.stderr.write(`Unknown server subcommand: ${subcommand}\n`);
|
|
139
|
+
process.stderr.write("Run 'legio server --help' for usage.\n");
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function startServer(args: string[], deps: ServerDeps): Promise<void> {
|
|
145
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
146
|
+
process.stdout.write(`${SERVER_HELP}\n`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const portStr = getFlag(args, "--port");
|
|
151
|
+
const host = getFlag(args, "--host") ?? "127.0.0.1";
|
|
152
|
+
const shouldOpen = hasFlag(args, "--open");
|
|
153
|
+
const isDaemon = hasFlag(args, "--daemon");
|
|
154
|
+
|
|
155
|
+
const port = portStr ? Number.parseInt(portStr, 10) : 4173;
|
|
156
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
157
|
+
throw new ValidationError("--port must be a valid port number (1-65535)", {
|
|
158
|
+
field: "port",
|
|
159
|
+
value: portStr,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Resolve project root and validate .legio exists
|
|
164
|
+
const root = process.cwd();
|
|
165
|
+
const config = await loadConfig(root);
|
|
166
|
+
const projectRoot = config.project.root || root;
|
|
167
|
+
|
|
168
|
+
if (isDaemon) {
|
|
169
|
+
// Check if already running
|
|
170
|
+
const existingPid = await readServerPid(projectRoot);
|
|
171
|
+
const checkRunning = deps._isProcessRunning ?? isProcessRunning;
|
|
172
|
+
if (existingPid !== null && checkRunning(existingPid)) {
|
|
173
|
+
process.stdout.write(`Server already running (PID ${existingPid})\n`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// Clean up stale PID file if present
|
|
177
|
+
if (existingPid !== null) {
|
|
178
|
+
await removeServerPid(projectRoot);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Spawn detached child without --daemon, with LEGIO_SERVER_DAEMON=1.
|
|
182
|
+
// Strip __LEGIO_TSX_LOADED so the shim re-execs with --import tsx:
|
|
183
|
+
// the daemon child inherits the parent env, and on Node v23+ the shim
|
|
184
|
+
// must re-exec to load TypeScript — it cannot strip types without tsx active.
|
|
185
|
+
const spawnFn = deps._spawn ?? spawn;
|
|
186
|
+
const childArgs = ["server", "start", "--port", String(port), "--host", host];
|
|
187
|
+
if (shouldOpen) childArgs.push("--open");
|
|
188
|
+
|
|
189
|
+
const childEnv: NodeJS.ProcessEnv = { ...process.env, LEGIO_SERVER_DAEMON: "1" };
|
|
190
|
+
delete childEnv.__LEGIO_TSX_LOADED;
|
|
191
|
+
|
|
192
|
+
const child = spawnFn("legio", childArgs, {
|
|
193
|
+
detached: true,
|
|
194
|
+
stdio: "ignore",
|
|
195
|
+
env: childEnv,
|
|
196
|
+
});
|
|
197
|
+
child.unref();
|
|
198
|
+
|
|
199
|
+
if (child.pid !== undefined) {
|
|
200
|
+
await writeServerPid(projectRoot, child.pid);
|
|
201
|
+
|
|
202
|
+
// Wait for daemon to attempt port binding, then verify it survived
|
|
203
|
+
const sleep = deps._sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));
|
|
204
|
+
await sleep(500);
|
|
205
|
+
|
|
206
|
+
if (!checkRunning(child.pid)) {
|
|
207
|
+
await removeServerPid(projectRoot);
|
|
208
|
+
process.stderr.write("Daemon process exited immediately — port may already be in use\n");
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
process.stdout.write(
|
|
213
|
+
`Server started as daemon (PID ${child.pid}) at http://${host}:${port}\n`,
|
|
214
|
+
);
|
|
215
|
+
} else {
|
|
216
|
+
process.stderr.write("Failed to spawn daemon process\n");
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Import the server module dynamically to avoid circular deps
|
|
223
|
+
const autoStartCoordinator = process.env.LEGIO_SERVER_DAEMON === "1";
|
|
224
|
+
const start =
|
|
225
|
+
deps._startServer ??
|
|
226
|
+
(async (opts) => {
|
|
227
|
+
const { startServer: serverStart } = await import("../server/index.ts");
|
|
228
|
+
await serverStart(opts);
|
|
229
|
+
});
|
|
230
|
+
await start({ port, host, root, shouldOpen, autoStartCoordinator });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function stopServer(_args: string[], deps: ServerDeps): Promise<void> {
|
|
234
|
+
const root = process.cwd();
|
|
235
|
+
const config = await loadConfig(root);
|
|
236
|
+
const projectRoot = config.project.root || root;
|
|
237
|
+
|
|
238
|
+
const pid = await readServerPid(projectRoot);
|
|
239
|
+
if (pid === null) {
|
|
240
|
+
process.stdout.write("Server not running\n");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const checkRunning = deps._isProcessRunning ?? isProcessRunning;
|
|
245
|
+
if (!checkRunning(pid)) {
|
|
246
|
+
await removeServerPid(projectRoot);
|
|
247
|
+
process.stdout.write("Server not running (stale PID file cleaned up)\n");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
process.kill(pid, "SIGTERM");
|
|
253
|
+
} catch {
|
|
254
|
+
// Process may have just died
|
|
255
|
+
}
|
|
256
|
+
await removeServerPid(projectRoot);
|
|
257
|
+
process.stdout.write(`Server stopped (PID ${pid})\n`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function statusServer(args: string[], deps: ServerDeps): Promise<void> {
|
|
261
|
+
const jsonMode = hasFlag(args, "--json");
|
|
262
|
+
|
|
263
|
+
const root = process.cwd();
|
|
264
|
+
const config = await loadConfig(root);
|
|
265
|
+
const projectRoot = config.project.root || root;
|
|
266
|
+
|
|
267
|
+
const pid = await readServerPid(projectRoot);
|
|
268
|
+
const checkRunning = deps._isProcessRunning ?? isProcessRunning;
|
|
269
|
+
|
|
270
|
+
if (pid === null) {
|
|
271
|
+
if (jsonMode) {
|
|
272
|
+
process.stdout.write(`${JSON.stringify({ running: false, pid: null })}\n`);
|
|
273
|
+
} else {
|
|
274
|
+
process.stdout.write("Server not running\n");
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const alive = checkRunning(pid);
|
|
280
|
+
if (!alive) {
|
|
281
|
+
// Clean up stale PID file
|
|
282
|
+
await removeServerPid(projectRoot);
|
|
283
|
+
if (jsonMode) {
|
|
284
|
+
process.stdout.write(`${JSON.stringify({ running: false, pid: null, stale: true })}\n`);
|
|
285
|
+
} else {
|
|
286
|
+
process.stdout.write("Server not running (stale PID file cleaned up)\n");
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Determine port from config or default
|
|
292
|
+
const port = 4173;
|
|
293
|
+
if (jsonMode) {
|
|
294
|
+
process.stdout.write(`${JSON.stringify({ running: true, pid, port })}\n`);
|
|
295
|
+
} else {
|
|
296
|
+
process.stdout.write(`Server running (PID ${pid}) on port ${port}\n`);
|
|
297
|
+
}
|
|
298
|
+
}
|