fifony 0.1.42 → 0.1.43
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/app/dist/assets/{CommandPalette-DNR5umI1.js → CommandPalette-M4VAMxCU.js} +1 -1
- package/app/dist/assets/{KeyboardShortcutsHelp-Dpl19F20.js → KeyboardShortcutsHelp-DkvPUXQq.js} +1 -1
- package/app/dist/assets/OnboardingWizard-B7V9hoCR.js +1 -0
- package/app/dist/assets/analytics.lazy-zVJdF880.js +1 -0
- package/app/dist/assets/{api-ChEctgc5.js → api-CkVfYg_m.js} +1 -1
- package/app/dist/assets/{createLucideIcon-R47sXufx.js → createLucideIcon-Dfk_Hxud.js} +1 -1
- package/app/dist/assets/index-BpiCi7Ew.css +1 -0
- package/app/dist/assets/index-D2INW0zc.js +47 -0
- package/app/dist/assets/vendor-BEoYbFV1.js +9 -0
- package/app/dist/index.html +5 -5
- package/app/dist/service-worker.js +9 -4
- package/bin/fifony.js +3 -0
- package/dist/agent/pty-daemon.js +177 -0
- package/dist/agent/run-local.js +177 -43
- package/dist/{agent-NNGZEKZH.js → agent-RMQTTUEC.js} +37 -16
- package/dist/analytics-broadcaster-O6YBP66L.js +145 -0
- package/dist/chunk-3NE23NYW.js +82 -0
- package/dist/chunk-42AMQAJG.js +404 -0
- package/dist/{chunk-H5N7O5NP.js → chunk-AILXZ2TD.js} +79 -147
- package/dist/{chunk-I2UHVKHS.js → chunk-BRSR26VK.js} +2 -2
- package/dist/chunk-E2EWEYA4.js +1302 -0
- package/dist/chunk-ESWHDHH6.js +102 -0
- package/dist/{chunk-NB44PCD2.js → chunk-FJNH3G2Z.js} +1061 -1138
- package/dist/chunk-MVTGAKQK.js +493 -0
- package/dist/chunk-QQQLP3PL.js +155 -0
- package/dist/chunk-SOBLO4YZ.js +2016 -0
- package/dist/chunk-YRSH2CLW.js +13784 -0
- package/dist/cli.js +335 -44
- package/dist/{issue-state-machine-GPQNZYUZ.js → fsm-issue-YGGF7SIL.js} +9 -5
- package/dist/helpers-L7NYO5XS.js +53 -0
- package/dist/issue-log-broadcaster-WZAHISYB.js +84 -0
- package/dist/{issues-MZLRSXD6.js → issues-3QRR7KM6.js} +10 -8
- package/dist/log-analyzer-K7MXQB4T.js +287 -0
- package/dist/mcp/server.js +109 -137
- package/dist/parallel-executor-6INE6NDO.js +118 -0
- package/dist/pid-manager-UBWXVSMD.js +21 -0
- package/dist/queue-workers-XFZK3TT5.js +32 -0
- package/dist/replan-issue.command-4UCWYHGZ.js +15 -0
- package/dist/scheduler-ZP7GOZDW.js +26 -0
- package/dist/{settings-NGY33WQE.js → settings-ZAWDCFP2.js} +32 -8
- package/dist/settings.resource-5CW456AZ.js +24 -0
- package/dist/store-M6NCKMZY.js +97 -0
- package/dist/{web-push-CRVDJKWR.js → web-push-AX5IIK3P.js} +2 -2
- package/dist/{workspace-D3F3XGSI.js → workspace-CJTWFWTJ.js} +5 -4
- package/package.json +8 -7
- package/app/dist/assets/OnboardingWizard-CijMhJDW.js +0 -1
- package/app/dist/assets/analytics.lazy-Dq90a756.js +0 -1
- package/app/dist/assets/index-Dy_fM427.js +0 -54
- package/app/dist/assets/index-Q9jBP0Pz.css +0 -1
- package/app/dist/assets/vendor-DkWeBvNl.js +0 -9
- package/dist/chunk-2CVTK5F2.js +0 -288
- package/dist/chunk-37N5OFHM.js +0 -125
- package/dist/chunk-JTKUWIQD.js +0 -8406
- package/dist/chunk-RBDBGU2C.js +0 -303
- package/dist/issue-runner-CMZPSVC7.js +0 -16
- package/dist/queue-workers-XZ6DGH4W.js +0 -23
- package/dist/scheduler-NVE6L3P7.js +0 -22
- package/dist/store-4HCGBN4L.js +0 -65
package/app/dist/index.html
CHANGED
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
<link rel="icon" href="/assets/icon-32.png" sizes="32x32" type="image/png" />
|
|
21
21
|
<link rel="icon" href="/assets/icon-16.png" sizes="16x16" type="image/png" />
|
|
22
22
|
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png" />
|
|
23
|
-
<script type="module" crossorigin src="/assets/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/assets/index-D2INW0zc.js"></script>
|
|
24
24
|
<link rel="modulepreload" crossorigin href="/assets/assets/rolldown-runtime-Dw2cE7zH.js">
|
|
25
|
-
<link rel="modulepreload" crossorigin href="/assets/assets/api-
|
|
26
|
-
<link rel="modulepreload" crossorigin href="/assets/assets/vendor-
|
|
27
|
-
<link rel="modulepreload" crossorigin href="/assets/assets/createLucideIcon-
|
|
28
|
-
<link rel="stylesheet" crossorigin href="/assets/assets/index-
|
|
25
|
+
<link rel="modulepreload" crossorigin href="/assets/assets/api-CkVfYg_m.js">
|
|
26
|
+
<link rel="modulepreload" crossorigin href="/assets/assets/vendor-BEoYbFV1.js">
|
|
27
|
+
<link rel="modulepreload" crossorigin href="/assets/assets/createLucideIcon-Dfk_Hxud.js">
|
|
28
|
+
<link rel="stylesheet" crossorigin href="/assets/assets/index-BpiCi7Ew.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="root"></div>
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
const CACHE_VERSION = "
|
|
1
|
+
const CACHE_VERSION = "1774647226934";
|
|
2
2
|
const CORE_CACHE = `fifony-core-${CACHE_VERSION}`;
|
|
3
3
|
const ASSET_CACHE = `fifony-assets-${CACHE_VERSION}`;
|
|
4
4
|
const APP_SHELL_ROUTES = [
|
|
5
5
|
"/onboarding",
|
|
6
6
|
"/kanban",
|
|
7
|
+
"/milestones",
|
|
7
8
|
"/issues",
|
|
8
9
|
"/analytics",
|
|
9
10
|
"/agents",
|
|
11
|
+
"/services",
|
|
10
12
|
"/settings",
|
|
11
13
|
"/settings/project",
|
|
12
|
-
"/settings/
|
|
14
|
+
"/settings/system",
|
|
13
15
|
"/settings/agents",
|
|
14
16
|
"/settings/notifications",
|
|
15
|
-
"/settings/
|
|
16
|
-
"/settings/
|
|
17
|
+
"/settings/execution",
|
|
18
|
+
"/settings/quality",
|
|
19
|
+
"/settings/pipeline",
|
|
20
|
+
"/settings/services",
|
|
21
|
+
"/settings/appearance",
|
|
17
22
|
"/settings/providers",
|
|
18
23
|
];
|
|
19
24
|
const APP_SHELL_FILES = ["/offline.html", "/manifest.webmanifest", "/favicon.png", "/icon-192.png", "/icon-512.png"];
|
package/bin/fifony.js
CHANGED
|
@@ -9,6 +9,9 @@ const __dirname = dirname(__filename);
|
|
|
9
9
|
const packageRoot = resolve(__dirname, "..");
|
|
10
10
|
const workspaceRoot = env.FIFONY_WORKSPACE_ROOT ?? cwd();
|
|
11
11
|
|
|
12
|
+
// Make the package root available to all child processes (used by pty-daemon path resolution)
|
|
13
|
+
process.env.FIFONY_PKG_ROOT = packageRoot;
|
|
14
|
+
|
|
12
15
|
const distCli = resolve(packageRoot, "dist", "cli.js");
|
|
13
16
|
const srcCli = resolve(packageRoot, "src", "cli.ts");
|
|
14
17
|
const forceSource = argv.includes("--dev") || env.NODE_ENV === "development";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/agents/pty-daemon.ts
|
|
4
|
+
import { appendFileSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { createServer } from "net";
|
|
7
|
+
function appendTail(current, text, maxChars) {
|
|
8
|
+
const combined = current + text;
|
|
9
|
+
return combined.length > maxChars ? combined.slice(combined.length - maxChars) : combined;
|
|
10
|
+
}
|
|
11
|
+
async function main() {
|
|
12
|
+
const raw = process.argv[2];
|
|
13
|
+
if (!raw) {
|
|
14
|
+
process.stderr.write("[pty-daemon] Missing options argument\n");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
let opts;
|
|
18
|
+
try {
|
|
19
|
+
opts = JSON.parse(raw);
|
|
20
|
+
} catch {
|
|
21
|
+
process.stderr.write("[pty-daemon] Failed to parse options JSON\n");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const { command, workspacePath, issueId, startedAt, commandSlice } = opts;
|
|
25
|
+
const liveLogFile = join(workspacePath, "live-output.log");
|
|
26
|
+
const socketPath = join(workspacePath, "agent.sock");
|
|
27
|
+
const agentPidFile = join(workspacePath, "agent.pid");
|
|
28
|
+
const daemonPidFile = join(workspacePath, "daemon.pid");
|
|
29
|
+
const daemonExitFile = join(workspacePath, "daemon.exit.json");
|
|
30
|
+
writeFileSync(daemonPidFile, String(process.pid), "utf8");
|
|
31
|
+
writeFileSync(liveLogFile, "", "utf8");
|
|
32
|
+
const cleanupFiles = () => {
|
|
33
|
+
try {
|
|
34
|
+
rmSync(socketPath, { force: true });
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
rmSync(daemonPidFile, { force: true });
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
process.on("exit", cleanupFiles);
|
|
43
|
+
process.on("SIGTERM", () => {
|
|
44
|
+
cleanupFiles();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
});
|
|
47
|
+
process.on("SIGINT", () => {
|
|
48
|
+
cleanupFiles();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
const clients = /* @__PURE__ */ new Set();
|
|
52
|
+
let outputTail = "";
|
|
53
|
+
const OUTPUT_RING_CHARS = 3e5;
|
|
54
|
+
const MAX_LOG_BYTES = 10 * 1024 * 1024;
|
|
55
|
+
const TARGET_LOG_BYTES = 5 * 1024 * 1024;
|
|
56
|
+
let bytesWritten = 0;
|
|
57
|
+
const server = createServer((socket) => {
|
|
58
|
+
clients.add(socket);
|
|
59
|
+
socket.on("close", () => clients.delete(socket));
|
|
60
|
+
socket.on("error", () => clients.delete(socket));
|
|
61
|
+
let buf = "";
|
|
62
|
+
socket.on("data", (chunk) => {
|
|
63
|
+
buf += chunk.toString();
|
|
64
|
+
const lines = buf.split("\n");
|
|
65
|
+
buf = lines.pop() ?? "";
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
if (!line.trim()) continue;
|
|
68
|
+
try {
|
|
69
|
+
const msg = JSON.parse(line);
|
|
70
|
+
if (msg.t === "cancel") {
|
|
71
|
+
try {
|
|
72
|
+
ptyProcess.kill();
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
} else if (msg.t === "tail") {
|
|
76
|
+
const reply = JSON.stringify({ t: "tail", v: outputTail }) + "\n";
|
|
77
|
+
try {
|
|
78
|
+
socket.write(reply);
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
} else if (msg.t === "write" && typeof msg.v === "string") {
|
|
82
|
+
try {
|
|
83
|
+
ptyProcess.write(msg.v);
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
try {
|
|
93
|
+
rmSync(socketPath, { force: true });
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
server.listen(socketPath);
|
|
97
|
+
const nodePty = await import("node-pty");
|
|
98
|
+
const ptyProcess = nodePty.spawn("sh", ["-c", command], {
|
|
99
|
+
name: "xterm-256color",
|
|
100
|
+
cols: 220,
|
|
101
|
+
rows: 50,
|
|
102
|
+
cwd: workspacePath,
|
|
103
|
+
env: process.env
|
|
104
|
+
});
|
|
105
|
+
const agentPid = ptyProcess.pid;
|
|
106
|
+
if (agentPid) {
|
|
107
|
+
writeFileSync(agentPidFile, JSON.stringify({
|
|
108
|
+
pid: agentPid,
|
|
109
|
+
issueId,
|
|
110
|
+
startedAt,
|
|
111
|
+
command: commandSlice
|
|
112
|
+
}), "utf8");
|
|
113
|
+
}
|
|
114
|
+
const broadcast = (msg) => {
|
|
115
|
+
for (const client of clients) {
|
|
116
|
+
try {
|
|
117
|
+
client.write(msg);
|
|
118
|
+
} catch {
|
|
119
|
+
clients.delete(client);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
ptyProcess.onData((data) => {
|
|
124
|
+
outputTail = appendTail(outputTail, data, OUTPUT_RING_CHARS);
|
|
125
|
+
const encoded = Buffer.from(data);
|
|
126
|
+
try {
|
|
127
|
+
appendFileSync(liveLogFile, encoded);
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
bytesWritten += encoded.length;
|
|
131
|
+
if (bytesWritten >= MAX_LOG_BYTES) {
|
|
132
|
+
try {
|
|
133
|
+
const content = readFileSync(liveLogFile);
|
|
134
|
+
if (content.length > TARGET_LOG_BYTES) {
|
|
135
|
+
writeFileSync(liveLogFile, content.slice(content.length - TARGET_LOG_BYTES));
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
139
|
+
bytesWritten = 0;
|
|
140
|
+
}
|
|
141
|
+
broadcast(JSON.stringify({ t: "d", v: data }) + "\n");
|
|
142
|
+
});
|
|
143
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
144
|
+
const success = exitCode === 0;
|
|
145
|
+
const exitRecord = {
|
|
146
|
+
success,
|
|
147
|
+
code: exitCode ?? null,
|
|
148
|
+
outputPath: liveLogFile,
|
|
149
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
writeFileSync(daemonExitFile, JSON.stringify(exitRecord, null, 2), "utf8");
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
broadcast(JSON.stringify({ t: "x", c: exitCode ?? null, s: success }) + "\n");
|
|
156
|
+
try {
|
|
157
|
+
rmSync(agentPidFile, { force: true });
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
setTimeout(() => {
|
|
161
|
+
server.close();
|
|
162
|
+
for (const client of clients) {
|
|
163
|
+
try {
|
|
164
|
+
client.destroy();
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}, 2e3);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
main().catch((err) => {
|
|
173
|
+
process.stderr.write(`[pty-daemon] Fatal: ${String(err)}
|
|
174
|
+
`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
});
|
|
177
|
+
//# sourceMappingURL=pty-daemon.js.map
|
package/dist/agent/run-local.js
CHANGED
|
@@ -1,73 +1,92 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
addEvent,
|
|
4
3
|
applyPersistedSettings,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
buildRuntimeState,
|
|
4
|
+
broadcastToWebSocketClients,
|
|
5
|
+
cleanTerminalWorkspaces,
|
|
8
6
|
closeStateStore,
|
|
9
7
|
createContainer,
|
|
10
|
-
deriveConfig,
|
|
11
|
-
detectProjectName,
|
|
12
8
|
hasTerminalQueue,
|
|
13
|
-
|
|
9
|
+
initManagedServiceWatcher,
|
|
10
|
+
initQueueWorkers,
|
|
14
11
|
initStateStore,
|
|
15
12
|
installGracefulShutdown,
|
|
13
|
+
listServiceStatuses,
|
|
14
|
+
loadLegacyPersistedServices,
|
|
15
|
+
loadPersistedMilestones,
|
|
16
|
+
loadPersistedServices,
|
|
16
17
|
loadPersistedState,
|
|
18
|
+
loadPersistedVariables,
|
|
17
19
|
loadRuntimeSettings,
|
|
18
20
|
persistDetectedProvidersSetting,
|
|
19
21
|
persistSetting,
|
|
20
22
|
persistState,
|
|
21
23
|
persistStateFull,
|
|
24
|
+
reconcileAgentStateTransitions,
|
|
25
|
+
reconcileManagedServiceStates,
|
|
26
|
+
recoverOrphans,
|
|
22
27
|
recoverPlanningSession,
|
|
23
|
-
|
|
28
|
+
recoverState,
|
|
29
|
+
replaceAllServices,
|
|
24
30
|
startApiServer,
|
|
31
|
+
startAutoConfiguredServices,
|
|
32
|
+
startManagedAgentWatcher,
|
|
33
|
+
startServiceLogBroadcasting,
|
|
34
|
+
stopQueueWorkers,
|
|
35
|
+
stopServiceLogBroadcasting,
|
|
25
36
|
syncRuntimeConfigSettings,
|
|
26
|
-
|
|
27
|
-
} from "../chunk-
|
|
28
|
-
import {
|
|
29
|
-
cleanTerminalWorkspaces,
|
|
30
|
-
initQueueWorkers,
|
|
31
|
-
recoverOrphans,
|
|
32
|
-
recoverState,
|
|
33
|
-
stopQueueWorkers
|
|
34
|
-
} from "../chunk-RBDBGU2C.js";
|
|
37
|
+
upsertPersistedVariable
|
|
38
|
+
} from "../chunk-YRSH2CLW.js";
|
|
35
39
|
import {
|
|
36
40
|
initWebPush
|
|
37
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-BRSR26VK.js";
|
|
42
|
+
import "../chunk-QQQLP3PL.js";
|
|
43
|
+
import "../chunk-AILXZ2TD.js";
|
|
38
44
|
import {
|
|
39
|
-
computeMetrics
|
|
40
|
-
} from "../chunk-H5N7O5NP.js";
|
|
41
|
-
import {
|
|
42
|
-
detectAvailableProviders,
|
|
43
45
|
detectDefaultBranch,
|
|
44
46
|
getGitRepoStatus,
|
|
45
|
-
getProviderDefaultCommand,
|
|
46
|
-
resolveDefaultProvider,
|
|
47
47
|
setSkipSource
|
|
48
|
-
} from "../chunk-
|
|
48
|
+
} from "../chunk-SOBLO4YZ.js";
|
|
49
49
|
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
addEvent,
|
|
51
|
+
applyWorkflowConfig,
|
|
52
|
+
buildQueueTitle,
|
|
53
|
+
buildRuntimeState,
|
|
54
|
+
deriveConfig,
|
|
55
|
+
detectProjectName,
|
|
56
|
+
hydrate,
|
|
57
|
+
resolveProjectMetadata,
|
|
58
|
+
validateConfig
|
|
59
|
+
} from "../chunk-E2EWEYA4.js";
|
|
55
60
|
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
computeMetrics
|
|
62
|
+
} from "../chunk-MVTGAKQK.js";
|
|
63
|
+
import {
|
|
64
|
+
detectAvailableProviders,
|
|
65
|
+
getProviderDefaultCommand,
|
|
66
|
+
resolveDefaultProvider
|
|
67
|
+
} from "../chunk-FJNH3G2Z.js";
|
|
61
68
|
import {
|
|
62
69
|
initLogger,
|
|
63
70
|
logger
|
|
64
71
|
} from "../chunk-DVU3CXWA.js";
|
|
72
|
+
import "../chunk-ESWHDHH6.js";
|
|
73
|
+
import {
|
|
74
|
+
CLI_ARGS,
|
|
75
|
+
PACKAGE_ROOT,
|
|
76
|
+
STATE_ROOT,
|
|
77
|
+
TARGET_ROOT,
|
|
78
|
+
debugBoot,
|
|
79
|
+
fail,
|
|
80
|
+
now,
|
|
81
|
+
parseIntArg
|
|
82
|
+
} from "../chunk-42AMQAJG.js";
|
|
83
|
+
import "../chunk-3NE23NYW.js";
|
|
65
84
|
|
|
66
85
|
// src/boot.ts
|
|
67
86
|
import { mkdirSync, readFileSync } from "fs";
|
|
68
87
|
import { env, exit, argv } from "process";
|
|
69
88
|
|
|
70
|
-
// src/persistence/plugins/dev-
|
|
89
|
+
// src/persistence/plugins/dev-frontend.ts
|
|
71
90
|
import { resolve } from "path";
|
|
72
91
|
async function startDevFrontend(apiPort, devPort, options) {
|
|
73
92
|
const VITE_CONFIG_PATH = resolve(PACKAGE_ROOT, "app/vite.config.js");
|
|
@@ -143,7 +162,7 @@ async function startDevFrontend(apiPort, devPort, options) {
|
|
|
143
162
|
await server.listen();
|
|
144
163
|
logger.info(`Dev frontend available at http://localhost:${devPort}`);
|
|
145
164
|
} catch (error) {
|
|
146
|
-
logger.warn(`Failed to start Vite
|
|
165
|
+
logger.warn(`Failed to start Vite service: ${String(error)}`);
|
|
147
166
|
}
|
|
148
167
|
}
|
|
149
168
|
|
|
@@ -173,7 +192,7 @@ Options:
|
|
|
173
192
|
--attempts <n> Maximum attempts per issue
|
|
174
193
|
--poll <ms> Scheduler interval in ms
|
|
175
194
|
--timeout <ms> Agent command timeout in ms (default: 1800000)
|
|
176
|
-
--dev Start Vite dev
|
|
195
|
+
--dev Start Vite dev frontend alongside API (HMR on port+1)
|
|
177
196
|
--no-tls Disable HTTPS (use plain HTTP)
|
|
178
197
|
--once Process once and exit
|
|
179
198
|
--skip-source Skip source snapshot copy
|
|
@@ -184,6 +203,8 @@ Options:
|
|
|
184
203
|
);
|
|
185
204
|
}
|
|
186
205
|
async function main() {
|
|
206
|
+
let serviceWatcher = null;
|
|
207
|
+
let agentWatcher = null;
|
|
187
208
|
debugBoot("main:start");
|
|
188
209
|
const args = CLI_ARGS;
|
|
189
210
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -236,37 +257,79 @@ async function main() {
|
|
|
236
257
|
sourceRepoUrl: TARGET_ROOT,
|
|
237
258
|
sourceRef: "workspace",
|
|
238
259
|
config,
|
|
260
|
+
milestones: [],
|
|
239
261
|
issues: [],
|
|
240
262
|
events: [],
|
|
241
263
|
metrics: { total: 0, planning: 0, queued: 0, inProgress: 0, blocked: 0, done: 0, merged: 0, cancelled: 0, activeWorkers: 0 },
|
|
242
264
|
notes: [],
|
|
265
|
+
variables: [],
|
|
243
266
|
booting: true
|
|
244
267
|
};
|
|
245
268
|
let apiState = earlyState;
|
|
246
269
|
createContainer(apiState);
|
|
247
270
|
debugBoot("main:container-early-init");
|
|
248
271
|
if (dashboardPort) {
|
|
249
|
-
|
|
272
|
+
const devPort = devMode ? dashboardPort + 1 : void 0;
|
|
273
|
+
await startApiServer(apiState, dashboardPort, { devPort });
|
|
250
274
|
debugBoot("main:api-server-early-start");
|
|
251
|
-
if (devMode) {
|
|
252
|
-
const devPort = dashboardPort + 1;
|
|
275
|
+
if (devMode && devPort) {
|
|
253
276
|
await startDevFrontend(dashboardPort, devPort);
|
|
254
277
|
}
|
|
255
278
|
}
|
|
279
|
+
const extractLegacyServicesFromRuntimeState = (value) => {
|
|
280
|
+
if (!value || typeof value !== "object") return [];
|
|
281
|
+
const configValue = value.config;
|
|
282
|
+
if (!configValue || typeof configValue !== "object") return [];
|
|
283
|
+
const legacyServices = configValue.devServers;
|
|
284
|
+
if (!Array.isArray(legacyServices)) return [];
|
|
285
|
+
return legacyServices.filter(
|
|
286
|
+
(entry) => Boolean(entry && typeof entry === "object" && typeof entry.id === "string" && typeof entry.command === "string")
|
|
287
|
+
);
|
|
288
|
+
};
|
|
256
289
|
debugBoot("main:phase-c-start");
|
|
257
290
|
logger.debug("[Boot] Loading persisted state, settings, and recovering sessions");
|
|
258
|
-
const [previous, persistedSettings] = await Promise.all([
|
|
291
|
+
const [previous, persistedSettings, persistedServices, legacyPersistedServices, persistedMilestones, persistedVariables] = await Promise.all([
|
|
259
292
|
loadPersistedState(),
|
|
260
293
|
loadRuntimeSettings(),
|
|
294
|
+
loadPersistedServices(),
|
|
295
|
+
loadLegacyPersistedServices(),
|
|
296
|
+
loadPersistedMilestones(),
|
|
297
|
+
loadPersistedVariables(),
|
|
261
298
|
persistDetectedProvidersSetting(detectedProviders),
|
|
262
299
|
recoverPlanningSession()
|
|
263
300
|
]);
|
|
264
301
|
logger.info({ hadPreviousState: previous !== null, issueCount: previous?.issues?.length ?? 0, settingsCount: persistedSettings.length }, "[Boot] State loaded from persistence");
|
|
265
302
|
debugBoot("main:state-loaded");
|
|
303
|
+
const runtimeLegacyServices = extractLegacyServicesFromRuntimeState(previous);
|
|
304
|
+
const migratedServices = persistedServices.length > 0 ? persistedServices : legacyPersistedServices.length > 0 ? legacyPersistedServices : runtimeLegacyServices;
|
|
266
305
|
config = applyPersistedSettings(config, persistedSettings);
|
|
306
|
+
if (migratedServices.length > 0) {
|
|
307
|
+
config = { ...config, services: migratedServices };
|
|
308
|
+
if (persistedServices.length === 0) {
|
|
309
|
+
await replaceAllServices(migratedServices);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
267
312
|
await syncRuntimeConfigSettings(config, persistedSettings);
|
|
268
313
|
const projectMetadata = resolveProjectMetadata(persistedSettings, TARGET_ROOT);
|
|
269
|
-
const state = buildRuntimeState(previous, config, projectMetadata);
|
|
314
|
+
const state = buildRuntimeState(previous, config, projectMetadata, persistedMilestones);
|
|
315
|
+
state.variables = persistedVariables;
|
|
316
|
+
if (state.variables.length === 0) {
|
|
317
|
+
const migrated = [];
|
|
318
|
+
const globalEnv = config.serviceEnv ?? {};
|
|
319
|
+
for (const [key, value] of Object.entries(globalEnv)) {
|
|
320
|
+
migrated.push({ id: `global:${key}`, key, value: String(value ?? ""), scope: "global", updatedAt: now() });
|
|
321
|
+
}
|
|
322
|
+
for (const svc of config.services ?? []) {
|
|
323
|
+
for (const [key, value] of Object.entries(svc.env ?? {})) {
|
|
324
|
+
migrated.push({ id: `${svc.id}:${key}`, key, value: String(value ?? ""), scope: svc.id, updatedAt: now() });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (migrated.length > 0) {
|
|
328
|
+
await Promise.all(migrated.map((v) => upsertPersistedVariable(v)));
|
|
329
|
+
state.variables = migrated;
|
|
330
|
+
logger.info({ count: migrated.length }, "[Boot] Migrated legacy env vars to variables resource");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
270
333
|
debugBoot("main:state-merged");
|
|
271
334
|
state.config.dashboardPort = dashboardPort ? String(dashboardPort) : void 0;
|
|
272
335
|
state.updatedAt = now();
|
|
@@ -306,6 +369,32 @@ async function main() {
|
|
|
306
369
|
}
|
|
307
370
|
cleanTerminalWorkspaces();
|
|
308
371
|
if (!skipRecovery) await recoverOrphans();
|
|
372
|
+
try {
|
|
373
|
+
const agentTransitions = reconcileAgentStateTransitions(state.issues, STATE_ROOT);
|
|
374
|
+
if (agentTransitions.length > 0) {
|
|
375
|
+
logger.info({ count: agentTransitions.length }, "[Boot] Agent states reconciled");
|
|
376
|
+
}
|
|
377
|
+
} catch (err) {
|
|
378
|
+
logger.warn({ err }, "[Boot] Agent state reconciliation failed \u2014 continuing");
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
const services = state.config.services ?? [];
|
|
382
|
+
reconcileManagedServiceStates(services, STATE_ROOT);
|
|
383
|
+
const autoStartTransitions = startAutoConfiguredServices(
|
|
384
|
+
services,
|
|
385
|
+
TARGET_ROOT,
|
|
386
|
+
STATE_ROOT,
|
|
387
|
+
state.config.serviceEnv
|
|
388
|
+
);
|
|
389
|
+
for (const t of autoStartTransitions) {
|
|
390
|
+
logger.info({ id: t.id, command: t.to }, "[Boot] Service auto-started");
|
|
391
|
+
}
|
|
392
|
+
for (const status of listServiceStatuses(services, STATE_ROOT)) {
|
|
393
|
+
if (status.running) startServiceLogBroadcasting(status.id, STATE_ROOT);
|
|
394
|
+
}
|
|
395
|
+
} catch (err) {
|
|
396
|
+
logger.warn({ err }, "[Boot] Service init failed \u2014 continuing");
|
|
397
|
+
}
|
|
309
398
|
state.metrics = computeMetrics(state.issues);
|
|
310
399
|
if (dashboardPort) {
|
|
311
400
|
Object.assign(apiState, state);
|
|
@@ -319,6 +408,43 @@ async function main() {
|
|
|
319
408
|
} catch (error) {
|
|
320
409
|
logger.warn({ err: error }, "[Boot] Queue workers failed to initialize \u2014 continuing without queue-based dispatch");
|
|
321
410
|
}
|
|
411
|
+
serviceWatcher = initManagedServiceWatcher(
|
|
412
|
+
() => apiState.config.services ?? [],
|
|
413
|
+
() => apiState.config.serviceEnv ?? {},
|
|
414
|
+
STATE_ROOT,
|
|
415
|
+
TARGET_ROOT,
|
|
416
|
+
(t) => {
|
|
417
|
+
logger.info({ id: t.id, from: t.from, to: t.to, reason: t.reason }, "[Service] FSM transition");
|
|
418
|
+
broadcastToWebSocketClients({
|
|
419
|
+
type: "service",
|
|
420
|
+
id: t.id,
|
|
421
|
+
state: t.to,
|
|
422
|
+
running: t.to === "starting" || t.to === "running",
|
|
423
|
+
pid: t.pid ?? null
|
|
424
|
+
});
|
|
425
|
+
if (t.to === "starting") {
|
|
426
|
+
startServiceLogBroadcasting(t.id, STATE_ROOT);
|
|
427
|
+
} else if (t.to === "stopped" || t.to === "crashed") {
|
|
428
|
+
stopServiceLogBroadcasting(t.id);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
agentWatcher = startManagedAgentWatcher(
|
|
433
|
+
() => apiState.issues,
|
|
434
|
+
STATE_ROOT,
|
|
435
|
+
(t) => {
|
|
436
|
+
logger.info({ issueId: t.issueId, identifier: t.identifier, from: t.from, to: t.to, reason: t.reason }, "[AgentFSM] Transition");
|
|
437
|
+
broadcastToWebSocketClients({
|
|
438
|
+
type: "agent-fsm",
|
|
439
|
+
issueId: t.issueId,
|
|
440
|
+
identifier: t.identifier,
|
|
441
|
+
operation: t.operation,
|
|
442
|
+
state: t.to,
|
|
443
|
+
running: t.to === "running" || t.to === "preparing",
|
|
444
|
+
pid: t.pid ?? null
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
);
|
|
322
448
|
installGracefulShutdown(state);
|
|
323
449
|
try {
|
|
324
450
|
const settings = await loadRuntimeSettings();
|
|
@@ -362,6 +488,14 @@ async function main() {
|
|
|
362
488
|
state.updatedAt = now();
|
|
363
489
|
state.metrics = computeMetrics(state.issues);
|
|
364
490
|
await persistStateFull(state);
|
|
491
|
+
try {
|
|
492
|
+
serviceWatcher?.stop();
|
|
493
|
+
} catch {
|
|
494
|
+
}
|
|
495
|
+
try {
|
|
496
|
+
agentWatcher?.stop();
|
|
497
|
+
} catch {
|
|
498
|
+
}
|
|
365
499
|
try {
|
|
366
500
|
await stopQueueWorkers();
|
|
367
501
|
} catch {
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
addTokenUsage,
|
|
3
|
-
|
|
3
|
+
canDispatchManagedAgent,
|
|
4
4
|
extractTokenUsage,
|
|
5
|
-
isAgentStillRunning,
|
|
6
|
-
isProcessAlive,
|
|
7
5
|
issueHasResumableSession,
|
|
8
6
|
loadAgentPipelineSnapshotForIssue,
|
|
9
7
|
loadAgentPipelineState,
|
|
10
8
|
loadAgentSessionSnapshotsForIssue,
|
|
11
9
|
readAgentDirective,
|
|
12
|
-
readAgentPid,
|
|
13
10
|
runAgentPipeline,
|
|
14
11
|
runAgentSession,
|
|
15
|
-
|
|
12
|
+
runManagedExecuteJob,
|
|
13
|
+
runManagedReviewJob,
|
|
16
14
|
runPlanningJob,
|
|
17
15
|
tryParseJsonOutput
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-YRSH2CLW.js";
|
|
17
|
+
import "./chunk-BRSR26VK.js";
|
|
18
|
+
import "./chunk-QQQLP3PL.js";
|
|
19
|
+
import "./chunk-AILXZ2TD.js";
|
|
22
20
|
import {
|
|
21
|
+
attachToDaemon,
|
|
23
22
|
buildPrompt,
|
|
24
23
|
buildProviderBasePrompt,
|
|
25
24
|
buildTurnPrompt,
|
|
@@ -34,16 +33,32 @@ import {
|
|
|
34
33
|
prepareWorkspace,
|
|
35
34
|
runCommandWithTimeout,
|
|
36
35
|
runHook,
|
|
37
|
-
shouldSkipMergePath
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
import "./chunk-
|
|
36
|
+
shouldSkipMergePath,
|
|
37
|
+
writeToDaemon
|
|
38
|
+
} from "./chunk-SOBLO4YZ.js";
|
|
39
|
+
import "./chunk-E2EWEYA4.js";
|
|
40
|
+
import "./chunk-MVTGAKQK.js";
|
|
41
|
+
import "./chunk-FJNH3G2Z.js";
|
|
41
42
|
import "./chunk-DVU3CXWA.js";
|
|
43
|
+
import "./chunk-ESWHDHH6.js";
|
|
44
|
+
import "./chunk-42AMQAJG.js";
|
|
45
|
+
import {
|
|
46
|
+
cleanStalePidFile,
|
|
47
|
+
isAgentStillRunning,
|
|
48
|
+
isDaemonAlive,
|
|
49
|
+
isDaemonSocketReady,
|
|
50
|
+
isProcessAlive,
|
|
51
|
+
readAgentPid,
|
|
52
|
+
readDaemonExit,
|
|
53
|
+
readDaemonPid
|
|
54
|
+
} from "./chunk-3NE23NYW.js";
|
|
42
55
|
export {
|
|
43
56
|
addTokenUsage,
|
|
57
|
+
attachToDaemon,
|
|
44
58
|
buildPrompt,
|
|
45
59
|
buildProviderBasePrompt,
|
|
46
60
|
buildTurnPrompt,
|
|
61
|
+
canDispatchManagedAgent as canDispatchAgent,
|
|
47
62
|
cleanStalePidFile,
|
|
48
63
|
cleanWorkspace,
|
|
49
64
|
computeDiffStats,
|
|
@@ -53,6 +68,8 @@ export {
|
|
|
53
68
|
hydrateIssuePathsFromWorkspace,
|
|
54
69
|
inferChangedWorkspacePaths,
|
|
55
70
|
isAgentStillRunning,
|
|
71
|
+
isDaemonAlive,
|
|
72
|
+
isDaemonSocketReady,
|
|
56
73
|
isProcessAlive,
|
|
57
74
|
issueHasResumableSession,
|
|
58
75
|
loadAgentPipelineSnapshotForIssue,
|
|
@@ -63,13 +80,17 @@ export {
|
|
|
63
80
|
prepareWorkspace,
|
|
64
81
|
readAgentDirective,
|
|
65
82
|
readAgentPid,
|
|
83
|
+
readDaemonExit,
|
|
84
|
+
readDaemonPid,
|
|
66
85
|
runAgentPipeline,
|
|
67
86
|
runAgentSession,
|
|
68
87
|
runCommandWithTimeout,
|
|
88
|
+
runManagedExecuteJob as runExecutePhase,
|
|
69
89
|
runHook,
|
|
70
|
-
runIssueOnce,
|
|
71
90
|
runPlanningJob,
|
|
91
|
+
runManagedReviewJob as runReviewPhase,
|
|
72
92
|
shouldSkipMergePath,
|
|
73
|
-
tryParseJsonOutput
|
|
93
|
+
tryParseJsonOutput,
|
|
94
|
+
writeToDaemon
|
|
74
95
|
};
|
|
75
|
-
//# sourceMappingURL=agent-
|
|
96
|
+
//# sourceMappingURL=agent-RMQTTUEC.js.map
|