@cluesmith/codev 3.3.2 → 3.3.4
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/dashboard-dist/assets/{index-D3XaKpZW.js → index-DZf6pNPE.js} +41 -41
- package/dashboard-dist/assets/index-DZf6pNPE.js.map +1 -0
- package/dashboard-dist/assets/index-DnYLy5C_.css +32 -0
- package/dashboard-dist/index.html +2 -2
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +48 -0
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/ide.d.ts +20 -0
- package/dist/agent-farm/commands/ide.d.ts.map +1 -0
- package/dist/agent-farm/commands/ide.js +138 -0
- package/dist/agent-farm/commands/ide.js.map +1 -0
- package/dist/agent-farm/commands/inbox.d.ts.map +1 -1
- package/dist/agent-farm/commands/inbox.js +79 -10
- package/dist/agent-farm/commands/inbox.js.map +1 -1
- package/dist/agent-farm/commands/interrupt.d.ts +4 -1
- package/dist/agent-farm/commands/interrupt.d.ts.map +1 -1
- package/dist/agent-farm/commands/interrupt.js +6 -3
- package/dist/agent-farm/commands/interrupt.js.map +1 -1
- package/dist/agent-farm/commands/reset.d.ts +4 -1
- package/dist/agent-farm/commands/reset.d.ts.map +1 -1
- package/dist/agent-farm/commands/reset.js +6 -3
- package/dist/agent-farm/commands/reset.js.map +1 -1
- package/dist/agent-farm/commands/send.d.ts +28 -0
- package/dist/agent-farm/commands/send.d.ts.map +1 -1
- package/dist/agent-farm/commands/send.js +79 -6
- package/dist/agent-farm/commands/send.js.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.js +16 -7
- package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
- package/dist/agent-farm/commands/tower.d.ts +21 -9
- package/dist/agent-farm/commands/tower.d.ts.map +1 -1
- package/dist/agent-farm/commands/tower.js +90 -30
- package/dist/agent-farm/commands/tower.js.map +1 -1
- package/dist/agent-farm/db/index.d.ts +3 -1
- package/dist/agent-farm/db/index.d.ts.map +1 -1
- package/dist/agent-farm/db/index.js +8 -443
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/mailbox.d.ts +47 -7
- package/dist/agent-farm/db/mailbox.d.ts.map +1 -1
- package/dist/agent-farm/db/mailbox.js +56 -12
- package/dist/agent-farm/db/mailbox.js.map +1 -1
- package/dist/agent-farm/db/migrations.d.ts +57 -0
- package/dist/agent-farm/db/migrations.d.ts.map +1 -0
- package/dist/agent-farm/db/migrations.js +530 -0
- package/dist/agent-farm/db/migrations.js.map +1 -0
- package/dist/agent-farm/db/schema.d.ts +1 -1
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +1 -0
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/tower-owner.d.ts +168 -0
- package/dist/agent-farm/db/tower-owner.d.ts.map +1 -0
- package/dist/agent-farm/db/tower-owner.js +369 -0
- package/dist/agent-farm/db/tower-owner.js.map +1 -0
- package/dist/agent-farm/db/types.d.ts +47 -0
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js.map +1 -1
- package/dist/agent-farm/lib/ide-record.d.ts +65 -0
- package/dist/agent-farm/lib/ide-record.d.ts.map +1 -0
- package/dist/agent-farm/lib/ide-record.js +118 -0
- package/dist/agent-farm/lib/ide-record.js.map +1 -0
- package/dist/agent-farm/lib/tunnel-client.d.ts +22 -0
- package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
- package/dist/agent-farm/lib/tunnel-client.js +96 -19
- package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
- package/dist/agent-farm/servers/cron-delivery.d.ts +7 -1
- package/dist/agent-farm/servers/cron-delivery.d.ts.map +1 -1
- package/dist/agent-farm/servers/cron-delivery.js +2 -1
- package/dist/agent-farm/servers/cron-delivery.js.map +1 -1
- package/dist/agent-farm/servers/gate-profiles.d.ts +44 -2
- package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -1
- package/dist/agent-farm/servers/gate-profiles.js +79 -6
- package/dist/agent-farm/servers/gate-profiles.js.map +1 -1
- package/dist/agent-farm/servers/ide-forward.d.ts +62 -0
- package/dist/agent-farm/servers/ide-forward.d.ts.map +1 -0
- package/dist/agent-farm/servers/ide-forward.js +274 -0
- package/dist/agent-farm/servers/ide-forward.js.map +1 -0
- package/dist/agent-farm/servers/ide-server.d.ts +68 -0
- package/dist/agent-farm/servers/ide-server.d.ts.map +1 -0
- package/dist/agent-farm/servers/ide-server.js +281 -0
- package/dist/agent-farm/servers/ide-server.js.map +1 -0
- package/dist/agent-farm/servers/mailbox-delivery.d.ts +296 -12
- package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -1
- package/dist/agent-farm/servers/mailbox-delivery.js +566 -69
- package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -1
- package/dist/agent-farm/servers/mailbox-wiring.d.ts +66 -19
- package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -1
- package/dist/agent-farm/servers/mailbox-wiring.js +204 -37
- package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -1
- package/dist/agent-farm/servers/message-write.d.ts +138 -11
- package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
- package/dist/agent-farm/servers/message-write.js +144 -35
- package/dist/agent-farm/servers/message-write.js.map +1 -1
- package/dist/agent-farm/servers/overview.d.ts +28 -12
- package/dist/agent-farm/servers/overview.d.ts.map +1 -1
- package/dist/agent-farm/servers/overview.js +130 -79
- package/dist/agent-farm/servers/overview.js.map +1 -1
- package/dist/agent-farm/servers/render-gate.d.ts +77 -1
- package/dist/agent-farm/servers/render-gate.d.ts.map +1 -1
- package/dist/agent-farm/servers/render-gate.js +174 -25
- package/dist/agent-farm/servers/render-gate.js.map +1 -1
- package/dist/agent-farm/servers/session-submit.d.ts +7 -4
- package/dist/agent-farm/servers/session-submit.d.ts.map +1 -1
- package/dist/agent-farm/servers/session-submit.js +7 -4
- package/dist/agent-farm/servers/session-submit.js.map +1 -1
- package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-cron.js +7 -3
- package/dist/agent-farm/servers/tower-cron.js.map +1 -1
- package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-instances.js +17 -10
- package/dist/agent-farm/servers/tower-instances.js.map +1 -1
- package/dist/agent-farm/servers/tower-routes.d.ts +32 -0
- package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-routes.js +186 -26
- package/dist/agent-farm/servers/tower-routes.js.map +1 -1
- package/dist/agent-farm/servers/tower-server.js +79 -9
- package/dist/agent-farm/servers/tower-server.js.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.d.ts +21 -0
- package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.js +106 -13
- package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
- package/dist/agent-farm/servers/tower-utils.d.ts +35 -0
- package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-utils.js +39 -0
- package/dist/agent-farm/servers/tower-utils.js.map +1 -1
- package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-websocket.js +26 -2
- package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
- package/dist/agent-farm/utils/message-format.d.ts +78 -2
- package/dist/agent-farm/utils/message-format.d.ts.map +1 -1
- package/dist/agent-farm/utils/message-format.js +107 -11
- package/dist/agent-farm/utils/message-format.js.map +1 -1
- package/dist/agent-farm/utils/port.d.ts +22 -0
- package/dist/agent-farm/utils/port.d.ts.map +1 -0
- package/dist/agent-farm/utils/port.js +35 -0
- package/dist/agent-farm/utils/port.js.map +1 -0
- package/dist/agent-farm/utils/server-utils.d.ts +26 -0
- package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
- package/dist/agent-farm/utils/server-utils.js +36 -0
- package/dist/agent-farm/utils/server-utils.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +61 -1
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/consult/tree-tripwire.d.ts +177 -0
- package/dist/commands/consult/tree-tripwire.d.ts.map +1 -0
- package/dist/commands/consult/tree-tripwire.js +356 -0
- package/dist/commands/consult/tree-tripwire.js.map +1 -0
- package/dist/commands/doctor.d.ts +69 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +165 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/generate-image.d.ts +25 -1
- package/dist/commands/generate-image.d.ts.map +1 -1
- package/dist/commands/generate-image.js +410 -2
- package/dist/commands/generate-image.js.map +1 -1
- package/dist/commands/porch/prompts.d.ts.map +1 -1
- package/dist/commands/porch/prompts.js +18 -2
- package/dist/commands/porch/prompts.js.map +1 -1
- package/dist/lib/agent-env.d.ts +86 -0
- package/dist/lib/agent-env.d.ts.map +1 -0
- package/dist/lib/agent-env.js +126 -0
- package/dist/lib/agent-env.js.map +1 -0
- package/dist/lib/forge-contracts.d.ts +76 -5
- package/dist/lib/forge-contracts.d.ts.map +1 -1
- package/dist/lib/forge-contracts.js.map +1 -1
- package/dist/lib/forge-rate-limit.d.ts +45 -0
- package/dist/lib/forge-rate-limit.d.ts.map +1 -0
- package/dist/lib/forge-rate-limit.js +89 -0
- package/dist/lib/forge-rate-limit.js.map +1 -0
- package/dist/lib/forge.d.ts +29 -1
- package/dist/lib/forge.d.ts.map +1 -1
- package/dist/lib/forge.js +96 -21
- package/dist/lib/forge.js.map +1 -1
- package/dist/lib/github.d.ts +13 -1
- package/dist/lib/github.d.ts.map +1 -1
- package/dist/lib/github.js +58 -15
- package/dist/lib/github.js.map +1 -1
- package/dist/lib/test-env.d.ts +17 -0
- package/dist/lib/test-env.d.ts.map +1 -1
- package/dist/lib/test-env.js +20 -0
- package/dist/lib/test-env.js.map +1 -1
- package/dist/terminal/pty-manager.d.ts +10 -1
- package/dist/terminal/pty-manager.d.ts.map +1 -1
- package/dist/terminal/pty-manager.js +38 -9
- package/dist/terminal/pty-manager.js.map +1 -1
- package/dist/terminal/pty-session.d.ts +174 -8
- package/dist/terminal/pty-session.d.ts.map +1 -1
- package/dist/terminal/pty-session.js +299 -25
- package/dist/terminal/pty-session.js.map +1 -1
- package/dist/terminal/session-manager.d.ts +23 -2
- package/dist/terminal/session-manager.d.ts.map +1 -1
- package/dist/terminal/session-manager.js +115 -36
- package/dist/terminal/session-manager.js.map +1 -1
- package/dist/terminal/session-screen.d.ts +34 -5
- package/dist/terminal/session-screen.d.ts.map +1 -1
- package/dist/terminal/session-screen.js +52 -6
- package/dist/terminal/session-screen.js.map +1 -1
- package/dist/terminal/shellper-client.d.ts +73 -0
- package/dist/terminal/shellper-client.d.ts.map +1 -1
- package/dist/terminal/shellper-client.js +151 -0
- package/dist/terminal/shellper-client.js.map +1 -1
- package/dist/terminal/shellper-main.js +20 -3
- package/dist/terminal/shellper-main.js.map +1 -1
- package/dist/terminal/shellper-process.d.ts +2 -0
- package/dist/terminal/shellper-process.d.ts.map +1 -1
- package/dist/terminal/shellper-process.js +17 -1
- package/dist/terminal/shellper-process.js.map +1 -1
- package/dist/terminal/shellper-protocol.d.ts +28 -0
- package/dist/terminal/shellper-protocol.d.ts.map +1 -1
- package/dist/terminal/shellper-protocol.js.map +1 -1
- package/dist/terminal/terminal-replies.d.ts +102 -0
- package/dist/terminal/terminal-replies.d.ts.map +1 -0
- package/dist/terminal/terminal-replies.js +181 -0
- package/dist/terminal/terminal-replies.js.map +1 -0
- package/package.json +4 -4
- package/scripts/forge/gitea/_lib.sh +214 -0
- package/scripts/forge/gitea/issue-comment.sh +8 -1
- package/scripts/forge/gitea/issue-view.sh +95 -4
- package/scripts/forge/gitea/pr-create.sh +155 -0
- package/scripts/forge/gitea/pr-exists.sh +32 -4
- package/scripts/forge/gitea/pr-list.sh +40 -31
- package/scripts/forge/gitea/pr-view.sh +65 -4
- package/scripts/forge/gitea/recently-merged.sh +91 -21
- package/scripts/forge/gitea/user-identity.sh +30 -1
- package/scripts/forge/github/issue-view.sh +5 -2
- package/scripts/forge/github/pr-create.sh +47 -0
- package/scripts/forge/github/pr-list.sh +6 -2
- package/scripts/forge/github/pr-search.sh +6 -2
- package/scripts/forge/gitlab/issue-search.sh +1 -0
- package/scripts/forge/gitlab/issue-view.sh +24 -4
- package/scripts/forge/gitlab/pr-create.sh +53 -0
- package/scripts/forge/gitlab/pr-search.sh +37 -2
- package/scripts/forge/linear/auth-status.sh +1 -0
- package/scripts/forge/linear/issue-comment.sh +1 -0
- package/scripts/forge/linear/issue-list.sh +1 -0
- package/scripts/forge/linear/issue-search.sh +1 -0
- package/scripts/forge/linear/issue-view.sh +14 -2
- package/scripts/forge/linear/recently-closed.sh +1 -0
- package/scripts/forge/linear/user-identity.sh +1 -0
- package/skeleton/.claude/skills/generate-image/SKILL.md +39 -1
- package/skeleton/.codex/skills/generate-image/SKILL.md +39 -1
- package/skeleton/protocols/air/prompts/pr.md +6 -1
- package/skeleton/protocols/aspir/prompts/review.md +6 -1
- package/skeleton/protocols/bugfix/prompts/pr.md +6 -1
- package/skeleton/protocols/bugfix/protocol.md +2 -1
- package/skeleton/protocols/maintain/prompts/review.md +6 -1
- package/skeleton/protocols/pir/prompts/review.md +10 -7
- package/skeleton/protocols/spir/prompts/review.md +6 -1
- package/skeleton/resources/commands/agent-farm.md +28 -3
- package/skeleton/roles/consultant.md +32 -0
- package/skeleton/templates/AGENTS.md +1 -1
- package/skeleton/templates/CLAUDE.md +1 -1
- package/templates/tower.html +16 -1
- package/dashboard-dist/assets/index-BZRTEMtH.css +0 -32
- package/dashboard-dist/assets/index-D3XaKpZW.js.map +0 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE server lifecycle, Tower-side (Issue #1668).
|
|
3
|
+
*
|
|
4
|
+
* Tower is the spawner: `afx ide start` drives this through the local-only
|
|
5
|
+
* `/api/ide` endpoint, and the same spawn path is reused by boot reconcile and
|
|
6
|
+
* by on-demand respawn when a `/ide/` request arrives and the server is down.
|
|
7
|
+
* Runtime state lives in the `~/.agent-farm/ide-server.json` record (Issue
|
|
8
|
+
* #1668 §D3) — no config file, no `global.db` change. Liveness is discovered by
|
|
9
|
+
* port (`getProcessesOnPort`), the same way Tower finds its own process.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { getProcessesOnPort } from '../utils/port.js';
|
|
14
|
+
import { sanitizeAgentEnv } from '../../lib/agent-env.js';
|
|
15
|
+
import { parseJsonBody } from '../utils/server-utils.js';
|
|
16
|
+
import { IDE_DEFAULT_PREFIX, IDE_DEFAULT_PORT, readIdeRecord, writeIdeRecord, deleteIdeRecord, ensureIdeConnectionToken, getIdeConnectionTokenPath, } from '../lib/ide-record.js';
|
|
17
|
+
const READY_TIMEOUT_MS = 15_000;
|
|
18
|
+
const READY_POLL_MS = 200;
|
|
19
|
+
const STOP_EXIT_TIMEOUT_MS = 5_000;
|
|
20
|
+
const STOP_POLL_MS = 200;
|
|
21
|
+
/**
|
|
22
|
+
* True if some process is listening on the port. Uses `lsof` (`getProcessesOnPort`),
|
|
23
|
+
* which is ~100 ms and synchronous, so it is a **COLD-path only** check — restart
|
|
24
|
+
* reconcile, `afx ide status`, start-collision detection, and respawn. The request
|
|
25
|
+
* forward must NEVER call this (Issue #1668): it reads the port from the record and
|
|
26
|
+
* lets the upstream connect error be the liveness truth-teller.
|
|
27
|
+
*/
|
|
28
|
+
function isPortLive(port) {
|
|
29
|
+
return getProcessesOnPort(port).length > 0;
|
|
30
|
+
}
|
|
31
|
+
/** True if a PID is alive (cheap, non-blocking). */
|
|
32
|
+
function isPidAlive(pid) {
|
|
33
|
+
try {
|
|
34
|
+
process.kill(pid, 0);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The recorded IDE server port, or null if none — the forward's hot-path target
|
|
43
|
+
* resolution. A plain record read, no port scan (Issue #1668): a dead server
|
|
44
|
+
* fails the proxy connect immediately, which is the only liveness check the
|
|
45
|
+
* forward needs.
|
|
46
|
+
*/
|
|
47
|
+
export function getRecordedIdePort() {
|
|
48
|
+
return readIdeRecord()?.port ?? null;
|
|
49
|
+
}
|
|
50
|
+
/** Poll until a process is listening on `port`, or the timeout elapses. */
|
|
51
|
+
async function waitForPort(port, timeoutMs) {
|
|
52
|
+
const deadline = Date.now() + timeoutMs;
|
|
53
|
+
while (Date.now() < deadline) {
|
|
54
|
+
if (isPortLive(port))
|
|
55
|
+
return true;
|
|
56
|
+
await new Promise((r) => setTimeout(r, READY_POLL_MS));
|
|
57
|
+
}
|
|
58
|
+
return isPortLive(port);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Spawn the IDE server detached (so it survives a Tower restart, like a
|
|
62
|
+
* shellper) and write the runtime record. Resolves to the record once the
|
|
63
|
+
* server is listening, or throws if the binary is missing or never binds.
|
|
64
|
+
*/
|
|
65
|
+
export async function spawnIdeServer(opts, log) {
|
|
66
|
+
const port = opts.port ?? IDE_DEFAULT_PORT;
|
|
67
|
+
if (!opts.serverPath) {
|
|
68
|
+
throw new Error('IDE server path is required (no default — set --server-path)');
|
|
69
|
+
}
|
|
70
|
+
if (!existsSync(opts.serverPath)) {
|
|
71
|
+
throw new Error(`IDE server binary not found at ${opts.serverPath}`);
|
|
72
|
+
}
|
|
73
|
+
// Start-collision: a prior server recorded on a DIFFERENT port would be orphaned
|
|
74
|
+
// (still running, no longer tracked) if we just overwrote the singleton record.
|
|
75
|
+
// Stop it first so "start on a new port" never leaves a detached stray (#1668).
|
|
76
|
+
const existing = readIdeRecord();
|
|
77
|
+
if (existing && existing.port !== port && isPortLive(existing.port)) {
|
|
78
|
+
log('WARN', `IDE server already recorded on port ${existing.port}; stopping it before starting on ${port}`);
|
|
79
|
+
await stopIdeServer(log);
|
|
80
|
+
}
|
|
81
|
+
// Already live on the port — idempotent: adopt and record, don't double-spawn.
|
|
82
|
+
if (isPortLive(port)) {
|
|
83
|
+
log('INFO', `IDE server already listening on port ${port}; adopting`);
|
|
84
|
+
const pid = getProcessesOnPort(port)[0] ?? 0;
|
|
85
|
+
const record = {
|
|
86
|
+
prefix: IDE_DEFAULT_PREFIX,
|
|
87
|
+
port,
|
|
88
|
+
serverPath: opts.serverPath,
|
|
89
|
+
pid,
|
|
90
|
+
defaultFolder: opts.defaultFolder,
|
|
91
|
+
startedAt: new Date().toISOString(),
|
|
92
|
+
};
|
|
93
|
+
writeIdeRecord(record);
|
|
94
|
+
return record;
|
|
95
|
+
}
|
|
96
|
+
const basePath = IDE_DEFAULT_PREFIX.replace(/\/$/, ''); // "/ide"
|
|
97
|
+
const tokenFile = getIdeConnectionTokenPath();
|
|
98
|
+
ensureIdeConnectionToken();
|
|
99
|
+
const args = [
|
|
100
|
+
'--host', '127.0.0.1',
|
|
101
|
+
'--port', String(port),
|
|
102
|
+
'--server-base-path', basePath,
|
|
103
|
+
'--connection-token-file', tokenFile,
|
|
104
|
+
'--accept-server-license-terms',
|
|
105
|
+
];
|
|
106
|
+
if (opts.defaultFolder) {
|
|
107
|
+
args.push('--default-folder', opts.defaultFolder);
|
|
108
|
+
}
|
|
109
|
+
log('INFO', `Spawning IDE server: ${opts.serverPath} ${args.join(' ')}`);
|
|
110
|
+
const child = spawn(opts.serverPath, args, {
|
|
111
|
+
cwd: process.cwd(),
|
|
112
|
+
env: sanitizeAgentEnv(process.env),
|
|
113
|
+
detached: true,
|
|
114
|
+
stdio: 'ignore',
|
|
115
|
+
});
|
|
116
|
+
if (!child.pid) {
|
|
117
|
+
throw new Error('Failed to spawn IDE server process');
|
|
118
|
+
}
|
|
119
|
+
child.unref();
|
|
120
|
+
const ready = await waitForPort(port, READY_TIMEOUT_MS);
|
|
121
|
+
if (!ready) {
|
|
122
|
+
throw new Error(`IDE server did not start listening on port ${port} within ${READY_TIMEOUT_MS / 1000}s`);
|
|
123
|
+
}
|
|
124
|
+
const record = {
|
|
125
|
+
prefix: IDE_DEFAULT_PREFIX,
|
|
126
|
+
port,
|
|
127
|
+
serverPath: opts.serverPath,
|
|
128
|
+
pid: child.pid,
|
|
129
|
+
defaultFolder: opts.defaultFolder,
|
|
130
|
+
startedAt: new Date().toISOString(),
|
|
131
|
+
};
|
|
132
|
+
writeIdeRecord(record);
|
|
133
|
+
log('INFO', `IDE server listening on port ${port} (pid ${child.pid})`);
|
|
134
|
+
return record;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Stop the IDE server on the recorded port: SIGTERM, wait for exit, escalate to
|
|
138
|
+
* SIGKILL for survivors (mirrors `towerStop`, #1668 plan §4), then delete the
|
|
139
|
+
* record. Cold path — `getProcessesOnPort` is fine here.
|
|
140
|
+
*/
|
|
141
|
+
export async function stopIdeServer(log) {
|
|
142
|
+
const record = readIdeRecord();
|
|
143
|
+
if (!record) {
|
|
144
|
+
return { stopped: false, port: null };
|
|
145
|
+
}
|
|
146
|
+
const pids = getProcessesOnPort(record.port);
|
|
147
|
+
for (const pid of pids) {
|
|
148
|
+
try {
|
|
149
|
+
process.kill(pid, 'SIGTERM');
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// already gone
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Wait for the SIGTERMed processes to actually exit before deleting the record;
|
|
156
|
+
// escalate any survivors to SIGKILL, so a lingering process can't be re-adopted.
|
|
157
|
+
const deadline = Date.now() + STOP_EXIT_TIMEOUT_MS;
|
|
158
|
+
let survivors = pids.filter(isPidAlive);
|
|
159
|
+
while (survivors.length > 0 && Date.now() < deadline) {
|
|
160
|
+
await new Promise((r) => setTimeout(r, STOP_POLL_MS));
|
|
161
|
+
survivors = survivors.filter(isPidAlive);
|
|
162
|
+
}
|
|
163
|
+
for (const pid of survivors) {
|
|
164
|
+
try {
|
|
165
|
+
process.kill(pid, 'SIGKILL');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// exited between the check and the kill
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
deleteIdeRecord();
|
|
172
|
+
const killedNote = survivors.length > 0 ? `, SIGKILL escalated for ${survivors.length}` : '';
|
|
173
|
+
log('INFO', `Stopped IDE server on port ${record.port} (${pids.length} process(es)${killedNote})`);
|
|
174
|
+
return { stopped: pids.length > 0, port: record.port };
|
|
175
|
+
}
|
|
176
|
+
/** Report the IDE server's status from the record plus a live port probe. */
|
|
177
|
+
export function ideServerStatus() {
|
|
178
|
+
const record = readIdeRecord();
|
|
179
|
+
if (!record)
|
|
180
|
+
return { record: null, alive: false, pid: null };
|
|
181
|
+
const pids = getProcessesOnPort(record.port);
|
|
182
|
+
return { record, alive: pids.length > 0, pid: pids[0] ?? null };
|
|
183
|
+
}
|
|
184
|
+
// Guards against two concurrent `/ide/` requests both triggering a respawn.
|
|
185
|
+
let respawnInFlight = null;
|
|
186
|
+
/**
|
|
187
|
+
* Ensure the IDE server is live and return its port, or null if it cannot be
|
|
188
|
+
* made live (no record, or a respawn that never bound). A live server is the
|
|
189
|
+
* fast path (no spawn). A recorded-but-dead server is respawned from the
|
|
190
|
+
* recorded `serverPath`; concurrent callers share one in-flight respawn.
|
|
191
|
+
*/
|
|
192
|
+
export async function ensureIdeServerLive(log = () => { }) {
|
|
193
|
+
const record = readIdeRecord();
|
|
194
|
+
if (!record)
|
|
195
|
+
return null;
|
|
196
|
+
if (isPortLive(record.port))
|
|
197
|
+
return record.port;
|
|
198
|
+
if (respawnInFlight)
|
|
199
|
+
return respawnInFlight;
|
|
200
|
+
respawnInFlight = (async () => {
|
|
201
|
+
try {
|
|
202
|
+
log('INFO', `IDE server on port ${record.port} is down; respawning from ${record.serverPath}`);
|
|
203
|
+
const fresh = await spawnIdeServer({ serverPath: record.serverPath, port: record.port, defaultFolder: record.defaultFolder }, log);
|
|
204
|
+
return fresh.port;
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
log('WARN', `IDE server respawn failed: ${err.message}`);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
respawnInFlight = null;
|
|
212
|
+
}
|
|
213
|
+
})();
|
|
214
|
+
return respawnInFlight;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Boot-time reconcile: adopt a live server (no orphan) or respawn a recorded
|
|
218
|
+
* but dead one. Best-effort — never throws into Tower boot.
|
|
219
|
+
*/
|
|
220
|
+
export async function reconcileIdeServer(log) {
|
|
221
|
+
const record = readIdeRecord();
|
|
222
|
+
if (!record)
|
|
223
|
+
return;
|
|
224
|
+
if (isPortLive(record.port)) {
|
|
225
|
+
log('INFO', `Adopted running IDE server on port ${record.port}`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
await ensureIdeServerLive(log);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Local-only `/api/ide` management endpoint driving the lifecycle above:
|
|
232
|
+
* `POST` starts (body `{ serverPath, port?, defaultFolder? }`), `DELETE` stops,
|
|
233
|
+
* `GET` reports status. Reached only post-`isRequestAllowed` (key required) and
|
|
234
|
+
* blocked from the tunnel like `/api/tunnel/*` — it spawns/kills a process.
|
|
235
|
+
*/
|
|
236
|
+
export async function handleIdeApi(req, res, log) {
|
|
237
|
+
if (req.method === 'GET') {
|
|
238
|
+
const status = ideServerStatus();
|
|
239
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
240
|
+
res.end(JSON.stringify(status));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (req.method === 'DELETE') {
|
|
244
|
+
const result = await stopIdeServer(log);
|
|
245
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
246
|
+
res.end(JSON.stringify(result));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (req.method === 'POST') {
|
|
250
|
+
let body;
|
|
251
|
+
try {
|
|
252
|
+
body = await parseJsonBody(req);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
256
|
+
res.end(JSON.stringify({ error: 'Invalid JSON body' }));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const serverPath = typeof body.serverPath === 'string' ? body.serverPath : '';
|
|
260
|
+
const port = typeof body.port === 'number' ? body.port : undefined;
|
|
261
|
+
const defaultFolder = typeof body.defaultFolder === 'string' ? body.defaultFolder : undefined;
|
|
262
|
+
if (!serverPath) {
|
|
263
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
264
|
+
res.end(JSON.stringify({ error: 'serverPath is required' }));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const record = await spawnIdeServer({ serverPath, port, defaultFolder }, log);
|
|
269
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
270
|
+
res.end(JSON.stringify({ ok: true, ...record }));
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
274
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
res.writeHead(405, { 'Content-Type': 'application/json' });
|
|
279
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=ide-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ide-server.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/ide-server.ts"],"names":[],"mappings":"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"}
|