@adhdev/daemon-core 0.9.77-rc.43 → 0.9.77-rc.45
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/dist/commands/router.d.ts +1 -0
- package/dist/git/git-worktree.d.ts +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +946 -182
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +942 -183
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +10 -0
- package/dist/mesh/p2p-relay-failure.d.ts +35 -0
- package/package.json +1 -1
- package/src/commands/router.ts +446 -11
- package/src/git/git-worktree.ts +35 -1
- package/src/index.d.ts +3 -0
- package/src/index.ts +14 -0
- package/src/mesh/coordinator-prompt.ts +16 -8
- package/src/mesh/mesh-events.ts +292 -5
- package/src/mesh/mesh-ledger.ts +1 -0
- package/src/mesh/mesh-work-queue.ts +27 -0
- package/src/mesh/p2p-relay-failure.ts +152 -0
package/dist/index.mjs
CHANGED
|
@@ -123,7 +123,33 @@ async function removeWorktree(repoRoot, worktreePath, opts = {}) {
|
|
|
123
123
|
});
|
|
124
124
|
} catch (error) {
|
|
125
125
|
const stderr = typeof error.stderr === "string" ? error.stderr : "";
|
|
126
|
-
|
|
126
|
+
const stdout = typeof error.stdout === "string" ? error.stdout : "";
|
|
127
|
+
const detail = `${stderr}
|
|
128
|
+
${stdout}
|
|
129
|
+
${error.message || ""}`;
|
|
130
|
+
if (opts.allowSubmoduleForceFallback && SUBMODULE_WORKTREE_REMOVE_RE.test(detail)) {
|
|
131
|
+
try {
|
|
132
|
+
await execFileAsync2("git", ["worktree", "remove", "--force", worktreePath], {
|
|
133
|
+
cwd: repoRoot,
|
|
134
|
+
encoding: "utf8",
|
|
135
|
+
timeout: GIT_TIMEOUT_MS,
|
|
136
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
137
|
+
windowsHide: true
|
|
138
|
+
});
|
|
139
|
+
} catch (forceError) {
|
|
140
|
+
const forceStderr = typeof forceError.stderr === "string" ? forceError.stderr : "";
|
|
141
|
+
const forceStdout = typeof forceError.stdout === "string" ? forceError.stdout : "";
|
|
142
|
+
throw new Error(`git worktree remove --force fallback failed: ${forceStderr.trim() || forceStdout.trim() || forceError.message}`);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
success: true,
|
|
146
|
+
removedPath: worktreePath,
|
|
147
|
+
fallback: "git_worktree_remove_force_submodule",
|
|
148
|
+
forced: true,
|
|
149
|
+
reason: "working_trees_containing_submodules"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`git worktree remove failed: ${stderr.trim() || stdout.trim() || error.message}`);
|
|
127
153
|
}
|
|
128
154
|
return { success: true, removedPath: worktreePath };
|
|
129
155
|
}
|
|
@@ -173,7 +199,7 @@ async function pruneWorktrees(repoRoot) {
|
|
|
173
199
|
} catch {
|
|
174
200
|
}
|
|
175
201
|
}
|
|
176
|
-
var execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER;
|
|
202
|
+
var execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, SUBMODULE_WORKTREE_REMOVE_RE;
|
|
177
203
|
var init_git_worktree = __esm({
|
|
178
204
|
"src/git/git-worktree.ts"() {
|
|
179
205
|
"use strict";
|
|
@@ -181,6 +207,7 @@ var init_git_worktree = __esm({
|
|
|
181
207
|
WORKTREE_DIR_NAME = ".adhdev-worktrees";
|
|
182
208
|
GIT_TIMEOUT_MS = 3e4;
|
|
183
209
|
GIT_MAX_BUFFER = 4 * 1024 * 1024;
|
|
210
|
+
SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
|
|
184
211
|
}
|
|
185
212
|
});
|
|
186
213
|
|
|
@@ -684,7 +711,8 @@ function buildRulesSection(coordinatorCliType) {
|
|
|
684
711
|
- **Minimize coordinator context.** The coordinator's job is routing, not implementing. Do not read source files, run commands, or analyze code directly \u2014 delegate all of that to node agents. Your context should stay lean.
|
|
685
712
|
- **Delegate analysis too.** If you need to understand a bug or explore the codebase, send that investigation as a task to the queue or a node. Do not do it yourself.
|
|
686
713
|
- **Respect explicit provider requests.** If the user names an agent/provider, pass the matching provider type to \`mesh_launch_session\`: Hermes \u2192 \`hermes-cli\`, Claude Code/Claude \u2192 \`claude-cli\`, Codex \u2192 \`codex-cli\`, Gemini \u2192 \`gemini-cli\`. Never substitute \`claude-cli\` just because the coordinator itself is Claude Code.
|
|
687
|
-
- **Front-load
|
|
714
|
+
- **Front-load new task messages.** When calling \`mesh_enqueue_task\` or \`mesh_send_task\` for a new task, include everything the agent needs: what files to touch, what the problem is, what the fix should look like. The agent won't ask follow-up questions.
|
|
715
|
+
- **Avoid context-wasting restarts.** For follow-up, retry, commit/push, preview, or cleanup work on the same issue, prefer the existing idle session and send only the delta from its last verified state. Start a fresh chat/session only for genuinely independent work, explicit provider/user request, unsafe transcript contamination, or required branch/worktree isolation.
|
|
688
716
|
- **Don't inspect code.** Treat delegated agent summaries as self-reports, not verification. Verify side effects via \`mesh_git_status\` (including related repo freshness when configured), not by reading source files.
|
|
689
717
|
- **Don't over-parallelize.** Start with 1-2 concurrent tasks. Scale up if they succeed. Never launch a duplicate session or second worker solely because \`mesh_read_chat\` has no final assistant message while the delegated session is still showing tool/terminal activity.
|
|
690
718
|
- **Handle failures with context.** If a task fails, check \`mesh_task_history\` first to see if this task was attempted before and how it failed. Read the chat to understand why, then decide: retry on the same node, reassign to a different node, or escalate to the user.
|
|
@@ -705,18 +733,24 @@ var init_coordinator_prompt = __esm({
|
|
|
705
733
|
|
|
706
734
|
| Tool | Purpose |
|
|
707
735
|
|------|---------|
|
|
708
|
-
| \`mesh_status\` | Check all nodes' health, git state,
|
|
736
|
+
| \`mesh_status\` | Check all nodes' health, git state, active sessions, and branch convergence |
|
|
709
737
|
| \`mesh_list_nodes\` | List nodes with workspace paths |
|
|
738
|
+
| \`mesh_enqueue_task\` | Add a task to the pull-based work queue; idle nodes auto-claim |
|
|
739
|
+
| \`mesh_view_queue\` | View queue status \u2014 pending, assigned, completed, failed, cancelled tasks |
|
|
740
|
+
| \`mesh_queue_cancel\` | Cancel a queue task without deleting audit history |
|
|
741
|
+
| \`mesh_queue_requeue\` | Return a task to pending for retry; clears stale session targets |
|
|
742
|
+
| \`mesh_send_task\` | Legacy push: enqueue a task targeted at a specific node |
|
|
710
743
|
| \`mesh_launch_session\` | Start a new agent session on a node |
|
|
711
|
-
| \`
|
|
712
|
-
| \`
|
|
744
|
+
| \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
|
|
745
|
+
| \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
|
|
713
746
|
| \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
|
|
714
747
|
| \`mesh_git_status\` | Check git status on a specific node |
|
|
715
748
|
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
716
749
|
| \`mesh_approve\` | Approve/reject a pending agent action |
|
|
717
750
|
| \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
|
|
718
751
|
| \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
|
|
719
|
-
| \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable)
|
|
752
|
+
| \`mesh_remove_node\` | Remove a node (cleans up worktree if applicable) |
|
|
753
|
+
| \`mesh_cleanup_sessions\` | Manually clean up delegated session records for a node |`;
|
|
720
754
|
TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
|
|
721
755
|
|
|
722
756
|
Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
|
|
@@ -726,9 +760,10 @@ Before doing any coordinator work, confirm that the actual callable tool list in
|
|
|
726
760
|
2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign.
|
|
727
761
|
3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
|
|
728
762
|
a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
|
|
729
|
-
b. **Node Preparation**: Call \`mesh_launch_session\`
|
|
763
|
+
b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. If you need branch isolation for parallel work, call \`mesh_clone_node\` to create a worktree node first.
|
|
730
764
|
c. **Targeted Tasks**: Use \`mesh_send_task\` only when you need to bypass the queue and force a specific node to execute a task immediately.
|
|
731
|
-
d.
|
|
765
|
+
d. For the first dispatch of a new task, provide a **complete, self-contained** instruction that includes all context the agent needs (file paths, line numbers, what to change, why). Do not send partial instructions expecting future follow-up.
|
|
766
|
+
e. For a continuation of the same issue in an existing session, send a concise **delta instruction**: current verified state, the exact failed/blocked step, the newly approved action, and final reporting requirements. Do not resend the full original task or open a new chat solely to continue the same work; that wastes coordinator and worker context.
|
|
732
767
|
4. **Monitor** \u2014 Prefer event-driven completion/status notifications. Do **not** poll \`mesh_read_chat\` repeatedly. Use \`mesh_view_queue\` to see the status of all pending, assigned, completed, and failed tasks. Do not call \`mesh_read_chat\` again within a few seconds for the same generating session. Use at most one compact \`mesh_read_chat\` check after a completion/approval signal. Handle approvals via \`mesh_approve\`.
|
|
733
768
|
5. **Verify** \u2014 When a task reports completion or git work is visible, call \`mesh_git_status\` to verify changes were made.
|
|
734
769
|
6. **Checkpoint** \u2014 Call \`mesh_checkpoint\` to save the work.
|
|
@@ -744,7 +779,7 @@ When a node agent stops unexpectedly, the daemon automatically enriches the syst
|
|
|
744
779
|
- A recommendation: **retry**, **reassign**, or **escalate**
|
|
745
780
|
|
|
746
781
|
Follow these recovery rules:
|
|
747
|
-
1. **If "Retry recommended"**:
|
|
782
|
+
1. **If "Retry recommended"**: Check \`mesh_view_queue\` first \u2014 the daemon may have auto-requeued. If not, re-launch the session on the same node (\`mesh_launch_session\`), then resend the original task (\`mesh_send_task\`). The system message includes the original task text.
|
|
748
783
|
2. **If "Max retries exceeded"**: Do NOT retry on the same node. Either reassign the task to a different node, or inform the user that the task requires manual intervention.
|
|
749
784
|
3. **If no recovery context**: The stop may be intentional (normal completion). Use \`mesh_read_chat\` once to verify, then move on.
|
|
750
785
|
4. **Always record what happened**: After handling a failure, briefly note the outcome in your report to the user.`;
|
|
@@ -996,6 +1031,7 @@ __export(mesh_work_queue_exports, {
|
|
|
996
1031
|
enqueueTask: () => enqueueTask,
|
|
997
1032
|
getMeshQueueStats: () => getMeshQueueStats,
|
|
998
1033
|
getQueue: () => getQueue,
|
|
1034
|
+
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
999
1035
|
requeueTask: () => requeueTask,
|
|
1000
1036
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
1001
1037
|
updateTaskStatus: () => updateTaskStatus
|
|
@@ -1074,6 +1110,19 @@ function updateTaskStatus(meshId, taskId, status) {
|
|
|
1074
1110
|
writeQueue(meshId, queue);
|
|
1075
1111
|
return queue[idx];
|
|
1076
1112
|
}
|
|
1113
|
+
function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
|
|
1114
|
+
const queue = readQueue(meshId);
|
|
1115
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1116
|
+
if (idx === -1) return null;
|
|
1117
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1118
|
+
queue[idx].autoLaunch = {
|
|
1119
|
+
...autoLaunch,
|
|
1120
|
+
updatedAt: now
|
|
1121
|
+
};
|
|
1122
|
+
queue[idx].updatedAt = now;
|
|
1123
|
+
writeQueue(meshId, queue);
|
|
1124
|
+
return queue[idx];
|
|
1125
|
+
}
|
|
1077
1126
|
function cancelTask(meshId, taskId, opts) {
|
|
1078
1127
|
const queue = readQueue(meshId);
|
|
1079
1128
|
const idx = queue.findIndex((q) => q.id === taskId);
|
|
@@ -1143,10 +1192,144 @@ var init_mesh_work_queue = __esm({
|
|
|
1143
1192
|
}
|
|
1144
1193
|
});
|
|
1145
1194
|
|
|
1195
|
+
// src/detection/cli-detector.ts
|
|
1196
|
+
import { exec } from "child_process";
|
|
1197
|
+
import * as os2 from "os";
|
|
1198
|
+
import * as path8 from "path";
|
|
1199
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1200
|
+
function parseVersion(raw) {
|
|
1201
|
+
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
1202
|
+
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
1203
|
+
}
|
|
1204
|
+
function shellQuote(value) {
|
|
1205
|
+
if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
1206
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
1207
|
+
}
|
|
1208
|
+
function expandHome(value) {
|
|
1209
|
+
const trimmed = value.trim();
|
|
1210
|
+
if (!trimmed.startsWith("~")) return trimmed;
|
|
1211
|
+
return path8.join(os2.homedir(), trimmed.slice(1));
|
|
1212
|
+
}
|
|
1213
|
+
function isExplicitCommandPath(command) {
|
|
1214
|
+
const trimmed = command.trim();
|
|
1215
|
+
return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
1216
|
+
}
|
|
1217
|
+
function resolveCommandPath(command) {
|
|
1218
|
+
const trimmed = command.trim();
|
|
1219
|
+
if (!trimmed) return null;
|
|
1220
|
+
if (isExplicitCommandPath(trimmed)) {
|
|
1221
|
+
const expanded = expandHome(trimmed);
|
|
1222
|
+
const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
|
|
1223
|
+
return existsSync7(candidate) ? candidate : null;
|
|
1224
|
+
}
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
function execAsync(cmd, timeoutMs = 5e3) {
|
|
1228
|
+
return new Promise((resolve16) => {
|
|
1229
|
+
const child = exec(cmd, {
|
|
1230
|
+
encoding: "utf-8",
|
|
1231
|
+
timeout: timeoutMs,
|
|
1232
|
+
...process.platform === "win32" ? { windowsHide: true } : {}
|
|
1233
|
+
}, (err, stdout) => {
|
|
1234
|
+
if (err || !stdout?.trim()) {
|
|
1235
|
+
resolve16(null);
|
|
1236
|
+
} else {
|
|
1237
|
+
resolve16(stdout.trim());
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
child.on("error", () => resolve16(null));
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
async function detectCLIs(providerLoader, options) {
|
|
1244
|
+
const platform10 = os2.platform();
|
|
1245
|
+
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
1246
|
+
const includeVersion = options?.includeVersion !== false;
|
|
1247
|
+
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
1248
|
+
const results = await Promise.all(
|
|
1249
|
+
cliList.map(async (cli) => {
|
|
1250
|
+
try {
|
|
1251
|
+
const explicitPath = resolveCommandPath(cli.command);
|
|
1252
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
|
|
1253
|
+
if (!pathResult) return { ...cli, installed: false };
|
|
1254
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
1255
|
+
let version;
|
|
1256
|
+
if (includeVersion) {
|
|
1257
|
+
const versionCommands = [
|
|
1258
|
+
`"${firstPath}" --version`,
|
|
1259
|
+
`"${firstPath}" -V`,
|
|
1260
|
+
`"${firstPath}" -v`,
|
|
1261
|
+
cli.versionCommand
|
|
1262
|
+
].filter((v) => !!v);
|
|
1263
|
+
try {
|
|
1264
|
+
for (const versionCommand of versionCommands) {
|
|
1265
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
1266
|
+
if (versionResult) {
|
|
1267
|
+
version = parseVersion(versionResult);
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return { ...cli, installed: true, version, path: firstPath };
|
|
1275
|
+
} catch {
|
|
1276
|
+
return { ...cli, installed: false };
|
|
1277
|
+
}
|
|
1278
|
+
})
|
|
1279
|
+
);
|
|
1280
|
+
return results;
|
|
1281
|
+
}
|
|
1282
|
+
async function detectCLI(cliId, providerLoader, options) {
|
|
1283
|
+
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
1284
|
+
if (providerLoader) {
|
|
1285
|
+
const cliList = providerLoader.getCliDetectionList();
|
|
1286
|
+
const target = cliList.find((c) => c.id === resolvedId);
|
|
1287
|
+
if (target) {
|
|
1288
|
+
const platform10 = os2.platform();
|
|
1289
|
+
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
1290
|
+
try {
|
|
1291
|
+
const explicitPath = resolveCommandPath(target.command);
|
|
1292
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
|
|
1293
|
+
if (!pathResult) return null;
|
|
1294
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
1295
|
+
let version;
|
|
1296
|
+
if (options?.includeVersion !== false) {
|
|
1297
|
+
const versionCommands = [
|
|
1298
|
+
`"${firstPath}" --version`,
|
|
1299
|
+
`"${firstPath}" -V`,
|
|
1300
|
+
`"${firstPath}" -v`,
|
|
1301
|
+
target.versionCommand
|
|
1302
|
+
].filter((v) => !!v);
|
|
1303
|
+
try {
|
|
1304
|
+
for (const versionCommand of versionCommands) {
|
|
1305
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
1306
|
+
if (versionResult) {
|
|
1307
|
+
version = parseVersion(versionResult);
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
} catch {
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return { ...target, installed: true, version, path: firstPath };
|
|
1315
|
+
} catch {
|
|
1316
|
+
return null;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
const all = await detectCLIs(providerLoader, options);
|
|
1321
|
+
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
1322
|
+
}
|
|
1323
|
+
var init_cli_detector = __esm({
|
|
1324
|
+
"src/detection/cli-detector.ts"() {
|
|
1325
|
+
"use strict";
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1146
1329
|
// src/logging/logger.ts
|
|
1147
1330
|
import * as fs2 from "fs";
|
|
1148
|
-
import * as
|
|
1149
|
-
import * as
|
|
1331
|
+
import * as path9 from "path";
|
|
1332
|
+
import * as os3 from "os";
|
|
1150
1333
|
function setLogLevel(level) {
|
|
1151
1334
|
currentLevel = level;
|
|
1152
1335
|
daemonLog("Logger", `Log level set to: ${level}`, "info");
|
|
@@ -1161,13 +1344,13 @@ function getDaemonLogDir() {
|
|
|
1161
1344
|
return LOG_DIR;
|
|
1162
1345
|
}
|
|
1163
1346
|
function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
|
|
1164
|
-
return
|
|
1347
|
+
return path9.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
|
|
1165
1348
|
}
|
|
1166
1349
|
function checkDateRotation() {
|
|
1167
1350
|
const today = getDateStr();
|
|
1168
1351
|
if (today !== currentDate) {
|
|
1169
1352
|
currentDate = today;
|
|
1170
|
-
currentLogFile =
|
|
1353
|
+
currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
1171
1354
|
cleanOldLogs();
|
|
1172
1355
|
}
|
|
1173
1356
|
}
|
|
@@ -1181,7 +1364,7 @@ function cleanOldLogs() {
|
|
|
1181
1364
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
1182
1365
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
1183
1366
|
try {
|
|
1184
|
-
fs2.unlinkSync(
|
|
1367
|
+
fs2.unlinkSync(path9.join(LOG_DIR, file));
|
|
1185
1368
|
} catch {
|
|
1186
1369
|
}
|
|
1187
1370
|
}
|
|
@@ -1304,7 +1487,7 @@ var init_logger = __esm({
|
|
|
1304
1487
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
1305
1488
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
1306
1489
|
currentLevel = "info";
|
|
1307
|
-
LOG_DIR = process.platform === "win32" ?
|
|
1490
|
+
LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os3.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os3.homedir(), "Library", "Logs", "adhdev") : path9.join(os3.homedir(), ".local", "share", "adhdev", "logs");
|
|
1308
1491
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
1309
1492
|
MAX_LOG_DAYS = 7;
|
|
1310
1493
|
try {
|
|
@@ -1312,16 +1495,16 @@ var init_logger = __esm({
|
|
|
1312
1495
|
} catch {
|
|
1313
1496
|
}
|
|
1314
1497
|
currentDate = getDateStr();
|
|
1315
|
-
currentLogFile =
|
|
1498
|
+
currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
1316
1499
|
cleanOldLogs();
|
|
1317
1500
|
try {
|
|
1318
|
-
const oldLog =
|
|
1501
|
+
const oldLog = path9.join(LOG_DIR, "daemon.log");
|
|
1319
1502
|
if (fs2.existsSync(oldLog)) {
|
|
1320
1503
|
const stat2 = fs2.statSync(oldLog);
|
|
1321
1504
|
const oldDate = stat2.mtime.toISOString().slice(0, 10);
|
|
1322
|
-
fs2.renameSync(oldLog,
|
|
1505
|
+
fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
1323
1506
|
}
|
|
1324
|
-
const oldLogBackup =
|
|
1507
|
+
const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
|
|
1325
1508
|
if (fs2.existsSync(oldLogBackup)) {
|
|
1326
1509
|
fs2.unlinkSync(oldLogBackup);
|
|
1327
1510
|
}
|
|
@@ -1353,7 +1536,7 @@ var init_logger = __esm({
|
|
|
1353
1536
|
}
|
|
1354
1537
|
};
|
|
1355
1538
|
interceptorInstalled = false;
|
|
1356
|
-
LOG_PATH =
|
|
1539
|
+
LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
1357
1540
|
}
|
|
1358
1541
|
});
|
|
1359
1542
|
|
|
@@ -1425,7 +1608,235 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
1425
1608
|
});
|
|
1426
1609
|
return true;
|
|
1427
1610
|
}
|
|
1428
|
-
function
|
|
1611
|
+
function normalizeProviderPriority(policy) {
|
|
1612
|
+
const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
|
|
1613
|
+
if (!Array.isArray(raw)) return [];
|
|
1614
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1615
|
+
return raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean).filter((type) => {
|
|
1616
|
+
if (seen.has(type)) return false;
|
|
1617
|
+
seen.add(type);
|
|
1618
|
+
return true;
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
function isTerminalSessionStatus(status) {
|
|
1622
|
+
return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
|
|
1623
|
+
}
|
|
1624
|
+
function isIdleSessionState(state) {
|
|
1625
|
+
const status = readNonEmptyString(state?.status).toLowerCase();
|
|
1626
|
+
if (isTerminalSessionStatus(status)) return false;
|
|
1627
|
+
return status === "idle" || state?.activeChat?.status === "waiting_input";
|
|
1628
|
+
}
|
|
1629
|
+
function isDirtyNode(node) {
|
|
1630
|
+
return node?.health === "dirty" || node?.git?.dirty === true;
|
|
1631
|
+
}
|
|
1632
|
+
function isLaunchableNode(node) {
|
|
1633
|
+
if (!node || node.status === "disabled" || node.status === "removed") return false;
|
|
1634
|
+
const health = readNonEmptyString(node.health).toLowerCase();
|
|
1635
|
+
if (!health) return true;
|
|
1636
|
+
return health === "online" || health === "unknown";
|
|
1637
|
+
}
|
|
1638
|
+
function localAutoLaunchSkipReason(node) {
|
|
1639
|
+
const daemonId = readNonEmptyString(node?.daemonId);
|
|
1640
|
+
const machineId = readNonEmptyString(node?.machineId);
|
|
1641
|
+
const appConfig = loadConfig();
|
|
1642
|
+
const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
|
|
1643
|
+
const cloudDaemonId = localMachineId ? `daemon_${localMachineId}` : "";
|
|
1644
|
+
const standaloneDaemonId = localMachineId ? `standalone_${localMachineId}` : "";
|
|
1645
|
+
const daemonMatchesLocal = !daemonId || daemonId === cloudDaemonId || daemonId === standaloneDaemonId;
|
|
1646
|
+
const machineMatchesLocal = !machineId || localMachineId && machineId === localMachineId;
|
|
1647
|
+
if (node?.isLocalWorktree === true) {
|
|
1648
|
+
return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
|
|
1649
|
+
}
|
|
1650
|
+
if (daemonId || machineId) {
|
|
1651
|
+
return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
|
|
1652
|
+
}
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
function activeAssignedCount(meshId) {
|
|
1656
|
+
return getQueue(meshId, { status: ["assigned"] }).length;
|
|
1657
|
+
}
|
|
1658
|
+
function nodeHasActiveAssignment(meshId, nodeId) {
|
|
1659
|
+
return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
|
|
1660
|
+
}
|
|
1661
|
+
function liveSessionCountForNode(components, meshId, nodeId) {
|
|
1662
|
+
return components.instanceManager.getByCategory("cli").filter((inst) => {
|
|
1663
|
+
const state = inst.getState();
|
|
1664
|
+
const settings = state.settings || {};
|
|
1665
|
+
if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
|
|
1666
|
+
const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
1667
|
+
if (instNodeId !== nodeId) return false;
|
|
1668
|
+
const status = readNonEmptyString(state.status).toLowerCase();
|
|
1669
|
+
return !isTerminalSessionStatus(status);
|
|
1670
|
+
}).length;
|
|
1671
|
+
}
|
|
1672
|
+
function recordAutoLaunchEvent(meshId, args) {
|
|
1673
|
+
try {
|
|
1674
|
+
appendLedgerEntry(meshId, {
|
|
1675
|
+
kind: "session_auto_launch",
|
|
1676
|
+
nodeId: args.nodeId,
|
|
1677
|
+
sessionId: args.sessionId,
|
|
1678
|
+
providerType: args.providerType,
|
|
1679
|
+
payload: {
|
|
1680
|
+
phase: args.phase,
|
|
1681
|
+
taskId: args.taskId,
|
|
1682
|
+
reason: args.reason,
|
|
1683
|
+
error: args.error
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
} catch (e) {
|
|
1687
|
+
LOG.warn("MeshQueue", `Failed to record auto-launch ledger event: ${e?.message || e}`);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
function markAutoLaunch(meshId, taskId, args) {
|
|
1691
|
+
recordTaskAutoLaunch(meshId, taskId, {
|
|
1692
|
+
status: args.status,
|
|
1693
|
+
reason: args.reason || args.error,
|
|
1694
|
+
nodeId: args.nodeId,
|
|
1695
|
+
providerType: args.providerType,
|
|
1696
|
+
sessionId: args.sessionId
|
|
1697
|
+
});
|
|
1698
|
+
recordAutoLaunchEvent(meshId, {
|
|
1699
|
+
phase: args.status,
|
|
1700
|
+
taskId,
|
|
1701
|
+
nodeId: args.nodeId,
|
|
1702
|
+
providerType: args.providerType,
|
|
1703
|
+
sessionId: args.sessionId,
|
|
1704
|
+
reason: args.reason,
|
|
1705
|
+
error: args.error
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
async function resolveUsableProvider(components, nodeId, node) {
|
|
1709
|
+
const providerPriority = normalizeProviderPriority(node?.policy);
|
|
1710
|
+
if (!providerPriority.length) return { reason: "missing_provider_priority" };
|
|
1711
|
+
const providerLoader = components.providerLoader;
|
|
1712
|
+
if (!providerLoader) return { reason: "provider_loader_unavailable" };
|
|
1713
|
+
const failed = [];
|
|
1714
|
+
for (const requestedType of providerPriority) {
|
|
1715
|
+
const normalizedType = typeof providerLoader.resolveAlias === "function" ? providerLoader.resolveAlias(requestedType) : requestedType;
|
|
1716
|
+
if (typeof providerLoader.isMachineProviderEnabled === "function" && !providerLoader.isMachineProviderEnabled(normalizedType)) {
|
|
1717
|
+
failed.push(`${requestedType}: disabled`);
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
let detected;
|
|
1721
|
+
try {
|
|
1722
|
+
detected = await detectCLI(normalizedType, providerLoader, { includeVersion: false });
|
|
1723
|
+
} catch (e) {
|
|
1724
|
+
failed.push(`${requestedType}: detect failed: ${e?.message || e}`);
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
if (typeof providerLoader.setCliDetectionResults === "function") {
|
|
1728
|
+
providerLoader.setCliDetectionResults([{
|
|
1729
|
+
id: normalizedType,
|
|
1730
|
+
installed: !!detected,
|
|
1731
|
+
path: detected?.path
|
|
1732
|
+
}], false);
|
|
1733
|
+
}
|
|
1734
|
+
components.onStatusChange?.();
|
|
1735
|
+
if (detected) return { providerType: normalizedType };
|
|
1736
|
+
failed.push(`${requestedType}: not detected`);
|
|
1737
|
+
}
|
|
1738
|
+
return { reason: `provider_priority_unusable: ${failed.join("; ") || nodeId}` };
|
|
1739
|
+
}
|
|
1740
|
+
async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
1741
|
+
const queue = getQueue(meshId);
|
|
1742
|
+
const pending = queue.filter((task) => task.status === "pending");
|
|
1743
|
+
if (!pending.length) return false;
|
|
1744
|
+
const maxParallelTasks = Math.max(1, Math.floor(Number(mesh?.policy?.maxParallelTasks) || 2));
|
|
1745
|
+
for (const task of pending) {
|
|
1746
|
+
if (activeAssignedCount(meshId) >= maxParallelTasks) {
|
|
1747
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
if (task.targetSessionId) {
|
|
1751
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => task.targetNodeId ? node?.id === task.targetNodeId : true) : [];
|
|
1755
|
+
if (!candidateNodes.length) {
|
|
1756
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_matching_node", nodeId: task.targetNodeId });
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1759
|
+
for (const node of candidateNodes) {
|
|
1760
|
+
const nodeId = readNonEmptyString(node?.id);
|
|
1761
|
+
if (!nodeId) continue;
|
|
1762
|
+
const launchKey = `${meshId}:${nodeId}`;
|
|
1763
|
+
const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
|
|
1764
|
+
if (autoLaunchInProgress.has(launchKey)) {
|
|
1765
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_in_progress", nodeId });
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
if (Date.now() < cooldownUntil) {
|
|
1769
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_cooldown", nodeId });
|
|
1770
|
+
continue;
|
|
1771
|
+
}
|
|
1772
|
+
if (isDirtyNode(node)) {
|
|
1773
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dirty_workspace", nodeId });
|
|
1774
|
+
continue;
|
|
1775
|
+
}
|
|
1776
|
+
if (!isLaunchableNode(node)) {
|
|
1777
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
|
|
1778
|
+
continue;
|
|
1779
|
+
}
|
|
1780
|
+
const localSkipReason = localAutoLaunchSkipReason(node);
|
|
1781
|
+
if (localSkipReason) {
|
|
1782
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: localSkipReason, nodeId });
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
if (nodeHasActiveAssignment(meshId, nodeId)) {
|
|
1786
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
|
|
1787
|
+
continue;
|
|
1788
|
+
}
|
|
1789
|
+
const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
|
|
1790
|
+
if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
|
|
1791
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_concurrent_sessions_reached", nodeId });
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
autoLaunchInProgress.add(launchKey);
|
|
1795
|
+
try {
|
|
1796
|
+
const resolved = await resolveUsableProvider(components, nodeId, node);
|
|
1797
|
+
if (!resolved.providerType) {
|
|
1798
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: resolved.reason || "provider_unusable", nodeId });
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType });
|
|
1802
|
+
const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
|
|
1803
|
+
cliType: resolved.providerType,
|
|
1804
|
+
dir: node.workspace,
|
|
1805
|
+
settings: {
|
|
1806
|
+
meshNodeFor: meshId,
|
|
1807
|
+
meshNodeId: nodeId,
|
|
1808
|
+
spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
|
|
1809
|
+
launchedByCoordinator: true,
|
|
1810
|
+
autoLaunchedForQueueTaskId: task.id
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
if (!launchResult?.success) {
|
|
1814
|
+
const reason = launchResult?.error || "launch_cli_failed";
|
|
1815
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
|
|
1816
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
|
|
1820
|
+
if (!sessionId) {
|
|
1821
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
|
|
1822
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
1823
|
+
return false;
|
|
1824
|
+
}
|
|
1825
|
+
markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId });
|
|
1826
|
+
tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType);
|
|
1827
|
+
return true;
|
|
1828
|
+
} catch (e) {
|
|
1829
|
+
markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
|
|
1830
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
1831
|
+
return false;
|
|
1832
|
+
} finally {
|
|
1833
|
+
autoLaunchInProgress.delete(launchKey);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
return false;
|
|
1838
|
+
}
|
|
1839
|
+
async function triggerMeshQueue(components, meshId) {
|
|
1429
1840
|
const mesh = getMeshWithCache(components, meshId);
|
|
1430
1841
|
if (!mesh) return;
|
|
1431
1842
|
const cliInstances = components.instanceManager.getByCategory("cli");
|
|
@@ -1436,9 +1847,7 @@ function triggerMeshQueue(components, meshId) {
|
|
|
1436
1847
|
if (instMeshId !== meshId) continue;
|
|
1437
1848
|
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
1438
1849
|
if (!nodeId) continue;
|
|
1439
|
-
|
|
1440
|
-
if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
|
|
1441
|
-
if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
|
|
1850
|
+
if (!isIdleSessionState(state)) continue;
|
|
1442
1851
|
const sessionId = state.instanceId;
|
|
1443
1852
|
const providerType = state.type || readNonEmptyString(settings.providerType);
|
|
1444
1853
|
if (providerType) {
|
|
@@ -1454,6 +1863,7 @@ function triggerMeshQueue(components, meshId) {
|
|
|
1454
1863
|
}
|
|
1455
1864
|
}
|
|
1456
1865
|
}
|
|
1866
|
+
await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
|
|
1457
1867
|
}
|
|
1458
1868
|
function buildMeshSystemMessage(args) {
|
|
1459
1869
|
const metadata = formatCompletionMetadata(args.metadataEvent);
|
|
@@ -1730,11 +2140,13 @@ function setupMeshEventForwarding(components) {
|
|
|
1730
2140
|
});
|
|
1731
2141
|
});
|
|
1732
2142
|
}
|
|
1733
|
-
var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
|
|
2143
|
+
var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
|
|
1734
2144
|
var init_mesh_events = __esm({
|
|
1735
2145
|
"src/mesh/mesh-events.ts"() {
|
|
1736
2146
|
"use strict";
|
|
2147
|
+
init_config();
|
|
1737
2148
|
init_mesh_config();
|
|
2149
|
+
init_cli_detector();
|
|
1738
2150
|
init_logger();
|
|
1739
2151
|
init_mesh_ledger();
|
|
1740
2152
|
init_mesh_work_queue();
|
|
@@ -1755,6 +2167,9 @@ var init_mesh_events = __esm({
|
|
|
1755
2167
|
"agent:stopped": "task_failed",
|
|
1756
2168
|
"monitor:long_generating": "task_stalled"
|
|
1757
2169
|
};
|
|
2170
|
+
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
2171
|
+
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
2172
|
+
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
1758
2173
|
}
|
|
1759
2174
|
});
|
|
1760
2175
|
|
|
@@ -6721,10 +7136,120 @@ init_mesh_ledger();
|
|
|
6721
7136
|
init_mesh_work_queue();
|
|
6722
7137
|
init_mesh_events();
|
|
6723
7138
|
|
|
7139
|
+
// src/mesh/p2p-relay-failure.ts
|
|
7140
|
+
var NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
|
|
7141
|
+
var P2P_NEXT_ACTION = "Check daemon/P2P health, wait briefly for connection establishment, then do one bounded retry or requeue the mesh task after clearing stale target session metadata.";
|
|
7142
|
+
var NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
|
|
7143
|
+
function messageFromError(error) {
|
|
7144
|
+
if (error instanceof Error) return error.message;
|
|
7145
|
+
if (typeof error === "string") return error;
|
|
7146
|
+
if (error && typeof error === "object") {
|
|
7147
|
+
const candidate = error.error ?? error.message ?? error.reason;
|
|
7148
|
+
if (typeof candidate === "string") return candidate;
|
|
7149
|
+
}
|
|
7150
|
+
return String(error || "mesh relay command failed");
|
|
7151
|
+
}
|
|
7152
|
+
function classifyP2pRelayFailure(error, _context = {}) {
|
|
7153
|
+
const message = messageFromError(error);
|
|
7154
|
+
const lower = message.toLowerCase();
|
|
7155
|
+
const hasP2pSignal = /p2p|datachannel|node-datachannel|webrtc|ice|mesh_relay_command|daemon_mesh_p2p_transport/i.test(message);
|
|
7156
|
+
const hasFailureSignal = /unavailable|missing|failed|failure|timeout|timed out|not connected|closed|disconnected|offline|no route|route unavailable|cannot send|cannot establish/i.test(message);
|
|
7157
|
+
if (/requires targetdaemonid and command|providerpriority|no inference provider|permission denied|read-only|not a member/i.test(message)) {
|
|
7158
|
+
return {
|
|
7159
|
+
code: "mesh_logic_or_provider_failure",
|
|
7160
|
+
reason: "mesh_logic_or_provider_failure",
|
|
7161
|
+
transport: "unknown",
|
|
7162
|
+
recoverable: false,
|
|
7163
|
+
retryRecommended: false,
|
|
7164
|
+
nextAction: NON_P2P_NEXT_ACTION,
|
|
7165
|
+
noFallbackReason: NO_FALLBACK_REASON
|
|
7166
|
+
};
|
|
7167
|
+
}
|
|
7168
|
+
let code = null;
|
|
7169
|
+
let reason = "";
|
|
7170
|
+
if (/timeout|timed out/i.test(message) && (hasP2pSignal || /mesh transport/i.test(message))) {
|
|
7171
|
+
code = "p2p_timeout";
|
|
7172
|
+
reason = "daemon_mesh_p2p_timeout";
|
|
7173
|
+
} else if (/no route|route unavailable/i.test(message)) {
|
|
7174
|
+
code = "p2p_no_route";
|
|
7175
|
+
reason = "daemon_mesh_p2p_no_route";
|
|
7176
|
+
} else if (/offline|not owned|not found|not connected to server/i.test(message) && /daemon|peer|target/i.test(message)) {
|
|
7177
|
+
code = "p2p_daemon_offline";
|
|
7178
|
+
reason = "daemon_mesh_target_offline";
|
|
7179
|
+
} else if (/closed|disconnected/i.test(message) && (hasP2pSignal || /state changed/i.test(message))) {
|
|
7180
|
+
code = "p2p_datachannel_closed";
|
|
7181
|
+
reason = "daemon_mesh_p2p_datachannel_closed";
|
|
7182
|
+
} else if (/not connected|cannot send|cannot establish/i.test(message) && hasP2pSignal) {
|
|
7183
|
+
code = "p2p_not_connected";
|
|
7184
|
+
reason = "daemon_mesh_p2p_not_connected";
|
|
7185
|
+
} else if (hasP2pSignal && hasFailureSignal) {
|
|
7186
|
+
code = "p2p_unavailable";
|
|
7187
|
+
reason = "daemon_mesh_p2p_transport_unavailable";
|
|
7188
|
+
}
|
|
7189
|
+
if (!code) {
|
|
7190
|
+
return {
|
|
7191
|
+
code: "mesh_logic_or_provider_failure",
|
|
7192
|
+
reason: "mesh_logic_or_provider_failure",
|
|
7193
|
+
transport: "unknown",
|
|
7194
|
+
recoverable: false,
|
|
7195
|
+
retryRecommended: false,
|
|
7196
|
+
nextAction: NON_P2P_NEXT_ACTION,
|
|
7197
|
+
noFallbackReason: NO_FALLBACK_REASON
|
|
7198
|
+
};
|
|
7199
|
+
}
|
|
7200
|
+
return {
|
|
7201
|
+
code,
|
|
7202
|
+
reason,
|
|
7203
|
+
transport: "p2p",
|
|
7204
|
+
recoverable: true,
|
|
7205
|
+
retryRecommended: true,
|
|
7206
|
+
nextAction: P2P_NEXT_ACTION,
|
|
7207
|
+
noFallbackReason: NO_FALLBACK_REASON
|
|
7208
|
+
};
|
|
7209
|
+
}
|
|
7210
|
+
function isP2pRelayTransportFailure(error) {
|
|
7211
|
+
return classifyP2pRelayFailure(error).recoverable === true;
|
|
7212
|
+
}
|
|
7213
|
+
function buildP2pRelayFailurePayload(error, context = {}) {
|
|
7214
|
+
const classification = classifyP2pRelayFailure(error, context);
|
|
7215
|
+
return {
|
|
7216
|
+
success: false,
|
|
7217
|
+
...classification,
|
|
7218
|
+
error: messageFromError(error),
|
|
7219
|
+
...context.command ? { command: context.command } : {},
|
|
7220
|
+
...context.targetDaemonId ? { targetDaemonId: context.targetDaemonId } : {}
|
|
7221
|
+
};
|
|
7222
|
+
}
|
|
7223
|
+
var P2pRelayFailureError = class extends Error {
|
|
7224
|
+
code;
|
|
7225
|
+
reason;
|
|
7226
|
+
transport;
|
|
7227
|
+
recoverable;
|
|
7228
|
+
retryRecommended;
|
|
7229
|
+
nextAction;
|
|
7230
|
+
noFallbackReason;
|
|
7231
|
+
command;
|
|
7232
|
+
targetDaemonId;
|
|
7233
|
+
constructor(message, context = {}) {
|
|
7234
|
+
super(message);
|
|
7235
|
+
this.name = "P2pRelayFailureError";
|
|
7236
|
+
const payload = buildP2pRelayFailurePayload(message, context);
|
|
7237
|
+
this.code = payload.code;
|
|
7238
|
+
this.reason = payload.reason;
|
|
7239
|
+
this.transport = payload.transport;
|
|
7240
|
+
this.recoverable = payload.recoverable;
|
|
7241
|
+
this.retryRecommended = payload.retryRecommended;
|
|
7242
|
+
this.nextAction = payload.nextAction;
|
|
7243
|
+
this.noFallbackReason = payload.noFallbackReason;
|
|
7244
|
+
this.command = context.command;
|
|
7245
|
+
this.targetDaemonId = context.targetDaemonId;
|
|
7246
|
+
}
|
|
7247
|
+
};
|
|
7248
|
+
|
|
6724
7249
|
// src/config/state-store.ts
|
|
6725
7250
|
init_config();
|
|
6726
|
-
import { existsSync as
|
|
6727
|
-
import { join as
|
|
7251
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
7252
|
+
import { join as join9 } from "path";
|
|
6728
7253
|
var DEFAULT_STATE = {
|
|
6729
7254
|
recentActivity: [],
|
|
6730
7255
|
savedProviderSessions: [],
|
|
@@ -6737,7 +7262,7 @@ function isPlainObject2(value) {
|
|
|
6737
7262
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
6738
7263
|
}
|
|
6739
7264
|
function getStatePath() {
|
|
6740
|
-
return
|
|
7265
|
+
return join9(getConfigDir(), "state.json");
|
|
6741
7266
|
}
|
|
6742
7267
|
function normalizeState(raw) {
|
|
6743
7268
|
const parsed = isPlainObject2(raw) ? raw : {};
|
|
@@ -6773,7 +7298,7 @@ function normalizeState(raw) {
|
|
|
6773
7298
|
}
|
|
6774
7299
|
function loadState() {
|
|
6775
7300
|
const statePath = getStatePath();
|
|
6776
|
-
if (!
|
|
7301
|
+
if (!existsSync9(statePath)) {
|
|
6777
7302
|
return { ...DEFAULT_STATE };
|
|
6778
7303
|
}
|
|
6779
7304
|
try {
|
|
@@ -6794,9 +7319,9 @@ function resetState() {
|
|
|
6794
7319
|
|
|
6795
7320
|
// src/detection/ide-detector.ts
|
|
6796
7321
|
import { execSync } from "child_process";
|
|
6797
|
-
import { existsSync as
|
|
6798
|
-
import { platform, homedir as
|
|
6799
|
-
import * as
|
|
7322
|
+
import { existsSync as existsSync10 } from "fs";
|
|
7323
|
+
import { platform as platform2, homedir as homedir5 } from "os";
|
|
7324
|
+
import * as path10 from "path";
|
|
6800
7325
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
6801
7326
|
var registeredIDEs = /* @__PURE__ */ new Map();
|
|
6802
7327
|
function registerIDEDefinition(def) {
|
|
@@ -6815,14 +7340,14 @@ function getMergedDefinitions() {
|
|
|
6815
7340
|
function findCliCommand(command) {
|
|
6816
7341
|
const trimmed = String(command || "").trim();
|
|
6817
7342
|
if (!trimmed) return null;
|
|
6818
|
-
if (
|
|
6819
|
-
const candidate = trimmed.startsWith("~") ?
|
|
6820
|
-
const resolved =
|
|
6821
|
-
return
|
|
7343
|
+
if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
7344
|
+
const candidate = trimmed.startsWith("~") ? path10.join(homedir5(), trimmed.slice(1)) : trimmed;
|
|
7345
|
+
const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
|
|
7346
|
+
return existsSync10(resolved) ? resolved : null;
|
|
6822
7347
|
}
|
|
6823
7348
|
try {
|
|
6824
7349
|
const result = execSync(
|
|
6825
|
-
|
|
7350
|
+
platform2() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
|
|
6826
7351
|
{ encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
6827
7352
|
).trim();
|
|
6828
7353
|
return result.split("\n")[0] || null;
|
|
@@ -6843,21 +7368,21 @@ function getIdeVersion(cliCommand) {
|
|
|
6843
7368
|
}
|
|
6844
7369
|
}
|
|
6845
7370
|
function checkPathExists(paths) {
|
|
6846
|
-
const home =
|
|
7371
|
+
const home = homedir5();
|
|
6847
7372
|
for (const p of paths) {
|
|
6848
|
-
const normalized = p.startsWith("~") ?
|
|
7373
|
+
const normalized = p.startsWith("~") ? path10.join(home, p.slice(1)) : p;
|
|
6849
7374
|
if (normalized.includes("*")) {
|
|
6850
7375
|
const username = home.split(/[\\/]/).pop() || "";
|
|
6851
7376
|
const resolved = normalized.replace("*", username);
|
|
6852
|
-
if (
|
|
7377
|
+
if (existsSync10(resolved)) return resolved;
|
|
6853
7378
|
} else {
|
|
6854
|
-
if (
|
|
7379
|
+
if (existsSync10(normalized)) return normalized;
|
|
6855
7380
|
}
|
|
6856
7381
|
}
|
|
6857
7382
|
return null;
|
|
6858
7383
|
}
|
|
6859
7384
|
async function detectIDEs(providerLoader) {
|
|
6860
|
-
const os22 =
|
|
7385
|
+
const os22 = platform2();
|
|
6861
7386
|
const results = [];
|
|
6862
7387
|
for (const def of getMergedDefinitions()) {
|
|
6863
7388
|
const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
|
|
@@ -6865,7 +7390,7 @@ async function detectIDEs(providerLoader) {
|
|
|
6865
7390
|
let resolvedCli = cliPath;
|
|
6866
7391
|
if (!resolvedCli && appPath && os22 === "darwin") {
|
|
6867
7392
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
6868
|
-
if (
|
|
7393
|
+
if (existsSync10(bundledCli)) resolvedCli = bundledCli;
|
|
6869
7394
|
}
|
|
6870
7395
|
if (!resolvedCli && appPath && os22 === "win32") {
|
|
6871
7396
|
const { dirname: dirname9 } = await import("path");
|
|
@@ -6878,7 +7403,7 @@ async function detectIDEs(providerLoader) {
|
|
|
6878
7403
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
6879
7404
|
];
|
|
6880
7405
|
for (const c of candidates) {
|
|
6881
|
-
if (
|
|
7406
|
+
if (existsSync10(c)) {
|
|
6882
7407
|
resolvedCli = c;
|
|
6883
7408
|
break;
|
|
6884
7409
|
}
|
|
@@ -6900,134 +7425,8 @@ async function detectIDEs(providerLoader) {
|
|
|
6900
7425
|
return results;
|
|
6901
7426
|
}
|
|
6902
7427
|
|
|
6903
|
-
// src/
|
|
6904
|
-
|
|
6905
|
-
import * as os3 from "os";
|
|
6906
|
-
import * as path10 from "path";
|
|
6907
|
-
import { existsSync as existsSync10 } from "fs";
|
|
6908
|
-
function parseVersion(raw) {
|
|
6909
|
-
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
6910
|
-
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
6911
|
-
}
|
|
6912
|
-
function shellQuote(value) {
|
|
6913
|
-
if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
6914
|
-
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
6915
|
-
}
|
|
6916
|
-
function expandHome(value) {
|
|
6917
|
-
const trimmed = value.trim();
|
|
6918
|
-
if (!trimmed.startsWith("~")) return trimmed;
|
|
6919
|
-
return path10.join(os3.homedir(), trimmed.slice(1));
|
|
6920
|
-
}
|
|
6921
|
-
function isExplicitCommandPath(command) {
|
|
6922
|
-
const trimmed = command.trim();
|
|
6923
|
-
return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
6924
|
-
}
|
|
6925
|
-
function resolveCommandPath(command) {
|
|
6926
|
-
const trimmed = command.trim();
|
|
6927
|
-
if (!trimmed) return null;
|
|
6928
|
-
if (isExplicitCommandPath(trimmed)) {
|
|
6929
|
-
const expanded = expandHome(trimmed);
|
|
6930
|
-
const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
|
|
6931
|
-
return existsSync10(candidate) ? candidate : null;
|
|
6932
|
-
}
|
|
6933
|
-
return null;
|
|
6934
|
-
}
|
|
6935
|
-
function execAsync(cmd, timeoutMs = 5e3) {
|
|
6936
|
-
return new Promise((resolve16) => {
|
|
6937
|
-
const child = exec(cmd, {
|
|
6938
|
-
encoding: "utf-8",
|
|
6939
|
-
timeout: timeoutMs,
|
|
6940
|
-
...process.platform === "win32" ? { windowsHide: true } : {}
|
|
6941
|
-
}, (err, stdout) => {
|
|
6942
|
-
if (err || !stdout?.trim()) {
|
|
6943
|
-
resolve16(null);
|
|
6944
|
-
} else {
|
|
6945
|
-
resolve16(stdout.trim());
|
|
6946
|
-
}
|
|
6947
|
-
});
|
|
6948
|
-
child.on("error", () => resolve16(null));
|
|
6949
|
-
});
|
|
6950
|
-
}
|
|
6951
|
-
async function detectCLIs(providerLoader, options) {
|
|
6952
|
-
const platform10 = os3.platform();
|
|
6953
|
-
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
6954
|
-
const includeVersion = options?.includeVersion !== false;
|
|
6955
|
-
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
6956
|
-
const results = await Promise.all(
|
|
6957
|
-
cliList.map(async (cli) => {
|
|
6958
|
-
try {
|
|
6959
|
-
const explicitPath = resolveCommandPath(cli.command);
|
|
6960
|
-
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
|
|
6961
|
-
if (!pathResult) return { ...cli, installed: false };
|
|
6962
|
-
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
6963
|
-
let version;
|
|
6964
|
-
if (includeVersion) {
|
|
6965
|
-
const versionCommands = [
|
|
6966
|
-
`"${firstPath}" --version`,
|
|
6967
|
-
`"${firstPath}" -V`,
|
|
6968
|
-
`"${firstPath}" -v`,
|
|
6969
|
-
cli.versionCommand
|
|
6970
|
-
].filter((v) => !!v);
|
|
6971
|
-
try {
|
|
6972
|
-
for (const versionCommand of versionCommands) {
|
|
6973
|
-
const versionResult = await execAsync(versionCommand, 3e3);
|
|
6974
|
-
if (versionResult) {
|
|
6975
|
-
version = parseVersion(versionResult);
|
|
6976
|
-
break;
|
|
6977
|
-
}
|
|
6978
|
-
}
|
|
6979
|
-
} catch {
|
|
6980
|
-
}
|
|
6981
|
-
}
|
|
6982
|
-
return { ...cli, installed: true, version, path: firstPath };
|
|
6983
|
-
} catch {
|
|
6984
|
-
return { ...cli, installed: false };
|
|
6985
|
-
}
|
|
6986
|
-
})
|
|
6987
|
-
);
|
|
6988
|
-
return results;
|
|
6989
|
-
}
|
|
6990
|
-
async function detectCLI(cliId, providerLoader, options) {
|
|
6991
|
-
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
6992
|
-
if (providerLoader) {
|
|
6993
|
-
const cliList = providerLoader.getCliDetectionList();
|
|
6994
|
-
const target = cliList.find((c) => c.id === resolvedId);
|
|
6995
|
-
if (target) {
|
|
6996
|
-
const platform10 = os3.platform();
|
|
6997
|
-
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
6998
|
-
try {
|
|
6999
|
-
const explicitPath = resolveCommandPath(target.command);
|
|
7000
|
-
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
|
|
7001
|
-
if (!pathResult) return null;
|
|
7002
|
-
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
7003
|
-
let version;
|
|
7004
|
-
if (options?.includeVersion !== false) {
|
|
7005
|
-
const versionCommands = [
|
|
7006
|
-
`"${firstPath}" --version`,
|
|
7007
|
-
`"${firstPath}" -V`,
|
|
7008
|
-
`"${firstPath}" -v`,
|
|
7009
|
-
target.versionCommand
|
|
7010
|
-
].filter((v) => !!v);
|
|
7011
|
-
try {
|
|
7012
|
-
for (const versionCommand of versionCommands) {
|
|
7013
|
-
const versionResult = await execAsync(versionCommand, 3e3);
|
|
7014
|
-
if (versionResult) {
|
|
7015
|
-
version = parseVersion(versionResult);
|
|
7016
|
-
break;
|
|
7017
|
-
}
|
|
7018
|
-
}
|
|
7019
|
-
} catch {
|
|
7020
|
-
}
|
|
7021
|
-
}
|
|
7022
|
-
return { ...target, installed: true, version, path: firstPath };
|
|
7023
|
-
} catch {
|
|
7024
|
-
return null;
|
|
7025
|
-
}
|
|
7026
|
-
}
|
|
7027
|
-
}
|
|
7028
|
-
const all = await detectCLIs(providerLoader, options);
|
|
7029
|
-
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
7030
|
-
}
|
|
7428
|
+
// src/index.ts
|
|
7429
|
+
init_cli_detector();
|
|
7031
7430
|
|
|
7032
7431
|
// src/system/host-memory.ts
|
|
7033
7432
|
import * as os4 from "os";
|
|
@@ -16131,13 +16530,14 @@ var DaemonCommandHandler = class {
|
|
|
16131
16530
|
|
|
16132
16531
|
// src/commands/cli-manager.ts
|
|
16133
16532
|
init_provider_cli_adapter();
|
|
16533
|
+
init_cli_detector();
|
|
16534
|
+
init_config();
|
|
16134
16535
|
import * as os13 from "os";
|
|
16135
16536
|
import * as path18 from "path";
|
|
16136
16537
|
import * as crypto4 from "crypto";
|
|
16137
16538
|
import { existsSync as existsSync14, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
16138
16539
|
import { execFileSync } from "child_process";
|
|
16139
16540
|
import chalk from "chalk";
|
|
16140
|
-
init_config();
|
|
16141
16541
|
|
|
16142
16542
|
// src/providers/cli-provider-instance.ts
|
|
16143
16543
|
import * as os12 from "os";
|
|
@@ -21723,6 +22123,7 @@ function getAvailableIdeIds() {
|
|
|
21723
22123
|
|
|
21724
22124
|
// src/commands/router.ts
|
|
21725
22125
|
init_config();
|
|
22126
|
+
init_cli_detector();
|
|
21726
22127
|
init_logger();
|
|
21727
22128
|
|
|
21728
22129
|
// src/logging/command-log.ts
|
|
@@ -22869,6 +23270,209 @@ async function resolveProviderTypeFromPriority(args) {
|
|
|
22869
23270
|
}
|
|
22870
23271
|
return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
|
|
22871
23272
|
}
|
|
23273
|
+
var REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
23274
|
+
var REFINE_VALIDATION_TIMEOUT_MS = 12e4;
|
|
23275
|
+
var REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
23276
|
+
var REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
|
|
23277
|
+
var REFINE_VALIDATION_MAX_COMMANDS = 4;
|
|
23278
|
+
function truncateValidationOutput(value) {
|
|
23279
|
+
const text = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
23280
|
+
if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
|
|
23281
|
+
return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
|
|
23282
|
+
[truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
|
|
23283
|
+
}
|
|
23284
|
+
function readPackageScripts(workspace) {
|
|
23285
|
+
try {
|
|
23286
|
+
const packageJsonPath = pathJoin(workspace, "package.json");
|
|
23287
|
+
const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
|
|
23288
|
+
return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
|
|
23289
|
+
} catch {
|
|
23290
|
+
return {};
|
|
23291
|
+
}
|
|
23292
|
+
}
|
|
23293
|
+
function tokenizeValidationCommand(command) {
|
|
23294
|
+
const trimmed = command.trim();
|
|
23295
|
+
if (!trimmed) return null;
|
|
23296
|
+
if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
|
|
23297
|
+
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
23298
|
+
if (!tokens.length) return null;
|
|
23299
|
+
if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
|
|
23300
|
+
return tokens;
|
|
23301
|
+
}
|
|
23302
|
+
function scriptMatchesValidationCategory(scriptName, category) {
|
|
23303
|
+
return scriptName === category || scriptName.startsWith(`${category}:`);
|
|
23304
|
+
}
|
|
23305
|
+
function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
|
|
23306
|
+
const tokens = tokenizeValidationCommand(rawCommand);
|
|
23307
|
+
if (!tokens) {
|
|
23308
|
+
return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
|
|
23309
|
+
}
|
|
23310
|
+
const [binary, second, third, ...rest] = tokens;
|
|
23311
|
+
let scriptName = "";
|
|
23312
|
+
let command = binary;
|
|
23313
|
+
let args = [];
|
|
23314
|
+
if ((binary === "npm" || binary === "pnpm" || binary === "bun") && second === "run" && third) {
|
|
23315
|
+
scriptName = third;
|
|
23316
|
+
args = ["run", scriptName, ...rest];
|
|
23317
|
+
} else if (binary === "npm" && second === "test" && !third) {
|
|
23318
|
+
scriptName = "test";
|
|
23319
|
+
args = ["test"];
|
|
23320
|
+
} else if (binary === "yarn" && second === "run" && third) {
|
|
23321
|
+
scriptName = third;
|
|
23322
|
+
args = ["run", scriptName, ...rest];
|
|
23323
|
+
} else if (binary === "yarn" && second && !third) {
|
|
23324
|
+
scriptName = second;
|
|
23325
|
+
args = [scriptName];
|
|
23326
|
+
} else {
|
|
23327
|
+
return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
|
|
23328
|
+
}
|
|
23329
|
+
if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
|
|
23330
|
+
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
|
|
23331
|
+
}
|
|
23332
|
+
if (!scriptMatchesValidationCategory(scriptName, category)) {
|
|
23333
|
+
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
|
|
23334
|
+
}
|
|
23335
|
+
return {
|
|
23336
|
+
command: {
|
|
23337
|
+
command,
|
|
23338
|
+
args,
|
|
23339
|
+
displayCommand: [command, ...args].join(" "),
|
|
23340
|
+
category,
|
|
23341
|
+
source
|
|
23342
|
+
}
|
|
23343
|
+
};
|
|
23344
|
+
}
|
|
23345
|
+
function collectProjectContextValidationCandidates(mesh) {
|
|
23346
|
+
const commands = mesh?.projectContext?.commands;
|
|
23347
|
+
if (!commands || typeof commands !== "object" || Array.isArray(commands)) return [];
|
|
23348
|
+
const candidates = [];
|
|
23349
|
+
for (const category of REFINE_VALIDATION_CATEGORIES) {
|
|
23350
|
+
const entries = Array.isArray(commands[category]) ? commands[category] : [];
|
|
23351
|
+
for (const entry of entries) {
|
|
23352
|
+
if (typeof entry?.command !== "string") continue;
|
|
23353
|
+
candidates.push({
|
|
23354
|
+
command: entry.command,
|
|
23355
|
+
category,
|
|
23356
|
+
source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
|
|
23357
|
+
confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
|
|
23358
|
+
});
|
|
23359
|
+
}
|
|
23360
|
+
}
|
|
23361
|
+
return candidates.sort((a, b) => {
|
|
23362
|
+
const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
|
|
23363
|
+
return rank(a.confidence) - rank(b.confidence);
|
|
23364
|
+
});
|
|
23365
|
+
}
|
|
23366
|
+
function collectPolicyValidationCandidates(mesh) {
|
|
23367
|
+
const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
|
|
23368
|
+
const configured = Array.isArray(policy.validationCommands) ? policy.validationCommands : Array.isArray(policy.validationGate?.commands) ? policy.validationGate.commands : [];
|
|
23369
|
+
return configured.map((entry) => typeof entry === "string" ? { command: entry, category: "", source: "mesh.policy.validationCommands" } : entry).filter((entry) => entry && typeof entry.command === "string").map((entry) => {
|
|
23370
|
+
const commandText = entry.command.trim();
|
|
23371
|
+
const category = REFINE_VALIDATION_CATEGORIES.find((cat) => commandText.includes(` ${cat}`)) ?? "";
|
|
23372
|
+
return { command: commandText, category, source: "mesh.policy.validationCommands" };
|
|
23373
|
+
}).filter((entry) => !!entry.category);
|
|
23374
|
+
}
|
|
23375
|
+
function selectMeshRefineValidationCommands(mesh, workspace) {
|
|
23376
|
+
const scripts = readPackageScripts(workspace);
|
|
23377
|
+
const rejectedCommands = [];
|
|
23378
|
+
const selected = [];
|
|
23379
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23380
|
+
const candidates = [
|
|
23381
|
+
...collectPolicyValidationCandidates(mesh),
|
|
23382
|
+
...collectProjectContextValidationCandidates(mesh)
|
|
23383
|
+
];
|
|
23384
|
+
for (const candidate of candidates) {
|
|
23385
|
+
const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
|
|
23386
|
+
if (parsed.rejected) {
|
|
23387
|
+
rejectedCommands.push(parsed.rejected);
|
|
23388
|
+
continue;
|
|
23389
|
+
}
|
|
23390
|
+
if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
|
|
23391
|
+
selected.push(parsed.command);
|
|
23392
|
+
seen.add(parsed.command.displayCommand);
|
|
23393
|
+
if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
|
|
23394
|
+
}
|
|
23395
|
+
if (!selected.length && candidates.length === 0) {
|
|
23396
|
+
for (const category of REFINE_VALIDATION_CATEGORIES) {
|
|
23397
|
+
if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
|
|
23398
|
+
const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
|
|
23399
|
+
if (fallback.command && !seen.has(fallback.command.displayCommand)) {
|
|
23400
|
+
selected.push(fallback.command);
|
|
23401
|
+
seen.add(fallback.command.displayCommand);
|
|
23402
|
+
} else if (fallback.rejected) {
|
|
23403
|
+
rejectedCommands.push(fallback.rejected);
|
|
23404
|
+
}
|
|
23405
|
+
if (selected.length >= 2) break;
|
|
23406
|
+
}
|
|
23407
|
+
}
|
|
23408
|
+
return {
|
|
23409
|
+
commands: selected,
|
|
23410
|
+
rejectedCommands,
|
|
23411
|
+
source: selected.some((command) => command.source === "mesh.policy.validationCommands") ? "mesh_policy" : selected.some((command) => command.source !== "package.json:scripts") ? "project_context" : selected.length ? "package_json_scripts" : "unavailable"
|
|
23412
|
+
};
|
|
23413
|
+
}
|
|
23414
|
+
async function runMeshRefineValidationGate(mesh, workspace) {
|
|
23415
|
+
const { execFile: execFile3 } = await import("child_process");
|
|
23416
|
+
const { promisify: promisify3 } = await import("util");
|
|
23417
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
23418
|
+
const selection = selectMeshRefineValidationCommands(mesh, workspace);
|
|
23419
|
+
const summary = {
|
|
23420
|
+
status: "skipped",
|
|
23421
|
+
required: true,
|
|
23422
|
+
commandsRun: [],
|
|
23423
|
+
rejectedCommands: selection.rejectedCommands,
|
|
23424
|
+
skippedReason: void 0,
|
|
23425
|
+
timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
|
|
23426
|
+
outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
|
|
23427
|
+
};
|
|
23428
|
+
if (!selection.commands.length) {
|
|
23429
|
+
summary.skippedReason = "validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available";
|
|
23430
|
+
return summary;
|
|
23431
|
+
}
|
|
23432
|
+
for (const candidate of selection.commands) {
|
|
23433
|
+
const startedAt = Date.now();
|
|
23434
|
+
try {
|
|
23435
|
+
const result = await execFileAsync3(candidate.command, candidate.args, {
|
|
23436
|
+
cwd: workspace,
|
|
23437
|
+
encoding: "utf8",
|
|
23438
|
+
timeout: REFINE_VALIDATION_TIMEOUT_MS,
|
|
23439
|
+
maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
23440
|
+
env: { ...process.env, CI: process.env.CI || "1" }
|
|
23441
|
+
});
|
|
23442
|
+
summary.commandsRun.push({
|
|
23443
|
+
command: candidate.command,
|
|
23444
|
+
args: candidate.args,
|
|
23445
|
+
displayCommand: candidate.displayCommand,
|
|
23446
|
+
category: candidate.category,
|
|
23447
|
+
source: candidate.source,
|
|
23448
|
+
passed: true,
|
|
23449
|
+
exitCode: 0,
|
|
23450
|
+
durationMs: Date.now() - startedAt,
|
|
23451
|
+
stdout: truncateValidationOutput(result.stdout),
|
|
23452
|
+
stderr: truncateValidationOutput(result.stderr)
|
|
23453
|
+
});
|
|
23454
|
+
} catch (error) {
|
|
23455
|
+
summary.commandsRun.push({
|
|
23456
|
+
command: candidate.command,
|
|
23457
|
+
args: candidate.args,
|
|
23458
|
+
displayCommand: candidate.displayCommand,
|
|
23459
|
+
category: candidate.category,
|
|
23460
|
+
source: candidate.source,
|
|
23461
|
+
passed: false,
|
|
23462
|
+
exitCode: typeof error?.code === "number" ? error.code : null,
|
|
23463
|
+
signal: typeof error?.signal === "string" ? error.signal : null,
|
|
23464
|
+
timedOut: error?.killed === true || /timed out/i.test(String(error?.message || "")),
|
|
23465
|
+
durationMs: Date.now() - startedAt,
|
|
23466
|
+
stdout: truncateValidationOutput(error?.stdout),
|
|
23467
|
+
stderr: truncateValidationOutput(error?.stderr || error?.message)
|
|
23468
|
+
});
|
|
23469
|
+
summary.status = "failed";
|
|
23470
|
+
return summary;
|
|
23471
|
+
}
|
|
23472
|
+
}
|
|
23473
|
+
summary.status = "passed";
|
|
23474
|
+
return summary;
|
|
23475
|
+
}
|
|
22872
23476
|
function loadYamlModule() {
|
|
22873
23477
|
return yaml;
|
|
22874
23478
|
}
|
|
@@ -23152,20 +23756,98 @@ var DaemonCommandRouter = class {
|
|
|
23152
23756
|
recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
|
|
23153
23757
|
};
|
|
23154
23758
|
}
|
|
23759
|
+
const forceFallbackConvergence = await this.getWorktreeForceCleanupConvergence({
|
|
23760
|
+
repoRoot,
|
|
23761
|
+
workspace,
|
|
23762
|
+
node: args.node
|
|
23763
|
+
});
|
|
23155
23764
|
try {
|
|
23156
|
-
const result = await removeWorktree2(repoRoot, workspace, {
|
|
23157
|
-
|
|
23765
|
+
const result = await removeWorktree2(repoRoot, workspace, {
|
|
23766
|
+
requireClean: true,
|
|
23767
|
+
allowSubmoduleForceFallback: forceFallbackConvergence.allow
|
|
23768
|
+
});
|
|
23769
|
+
return {
|
|
23770
|
+
success: true,
|
|
23771
|
+
removedPath: result.removedPath,
|
|
23772
|
+
repoRoot,
|
|
23773
|
+
...result.fallback ? {
|
|
23774
|
+
fallback: result.fallback,
|
|
23775
|
+
forced: result.forced,
|
|
23776
|
+
reason: result.reason,
|
|
23777
|
+
convergence: forceFallbackConvergence
|
|
23778
|
+
} : {}
|
|
23779
|
+
};
|
|
23158
23780
|
} catch (e) {
|
|
23159
23781
|
const message = String(e?.message || e || "worktree cleanup failed");
|
|
23160
23782
|
const dirty = message.includes("dirty worktree") || message.includes("local changes");
|
|
23783
|
+
const submoduleForceBlocked = /working trees containing submodules cannot be moved or removed/i.test(message) && !forceFallbackConvergence.allow;
|
|
23161
23784
|
return {
|
|
23162
23785
|
success: false,
|
|
23163
|
-
code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
|
|
23164
|
-
error: message,
|
|
23165
|
-
recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure."
|
|
23786
|
+
code: dirty ? "mesh_worktree_cleanup_dirty" : submoduleForceBlocked ? "mesh_worktree_cleanup_force_fallback_blocked" : "mesh_worktree_cleanup_failed",
|
|
23787
|
+
error: submoduleForceBlocked ? `${message}; refusing --force fallback because convergence could not be verified: ${forceFallbackConvergence.error || "unknown convergence state"}` : message,
|
|
23788
|
+
recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : submoduleForceBlocked ? "Verify the worktree branch is merged/contained in the source default branch (for example origin/main) or mark the node with a safe branchConvergence final state before retrying. The mesh registry entry is preserved." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure.",
|
|
23789
|
+
...submoduleForceBlocked ? { convergence: forceFallbackConvergence } : {}
|
|
23166
23790
|
};
|
|
23167
23791
|
}
|
|
23168
23792
|
}
|
|
23793
|
+
async getWorktreeForceCleanupConvergence(args) {
|
|
23794
|
+
const metadataStatus = typeof args.node?.branchConvergence?.status === "string" ? args.node.branchConvergence.status : "";
|
|
23795
|
+
if (metadataStatus === "merged_to_main" || metadataStatus === "cleanup_candidate") {
|
|
23796
|
+
return { allow: true, status: metadataStatus, source: "node_branch_convergence" };
|
|
23797
|
+
}
|
|
23798
|
+
const { execFile: execFile3 } = await import("child_process");
|
|
23799
|
+
const { promisify: promisify3 } = await import("util");
|
|
23800
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
23801
|
+
const runGit2 = async (gitArgs, cwd) => {
|
|
23802
|
+
const { stdout } = await execFileAsync3("git", gitArgs, {
|
|
23803
|
+
cwd,
|
|
23804
|
+
encoding: "utf8",
|
|
23805
|
+
timeout: 3e4,
|
|
23806
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
23807
|
+
windowsHide: true
|
|
23808
|
+
});
|
|
23809
|
+
return String(stdout || "").trim();
|
|
23810
|
+
};
|
|
23811
|
+
let head = "";
|
|
23812
|
+
try {
|
|
23813
|
+
head = await runGit2(["rev-parse", "HEAD"], args.workspace);
|
|
23814
|
+
} catch (e) {
|
|
23815
|
+
return { allow: false, error: `could not resolve worktree HEAD: ${e?.message || e}` };
|
|
23816
|
+
}
|
|
23817
|
+
if (!head) return { allow: false, error: "worktree HEAD is empty" };
|
|
23818
|
+
const candidateRefs = [];
|
|
23819
|
+
try {
|
|
23820
|
+
const defaultBranch = await runGit2(["branch", "--show-current"], args.repoRoot);
|
|
23821
|
+
if (defaultBranch) {
|
|
23822
|
+
candidateRefs.push(defaultBranch, `origin/${defaultBranch}`);
|
|
23823
|
+
}
|
|
23824
|
+
} catch {
|
|
23825
|
+
}
|
|
23826
|
+
candidateRefs.push("origin/main", "origin/master", "main", "master");
|
|
23827
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23828
|
+
const checkedRefs = [];
|
|
23829
|
+
for (const ref of candidateRefs) {
|
|
23830
|
+
if (!ref || seen.has(ref)) continue;
|
|
23831
|
+
seen.add(ref);
|
|
23832
|
+
let commit = "";
|
|
23833
|
+
try {
|
|
23834
|
+
commit = await runGit2(["rev-parse", "--verify", `${ref}^{commit}`], args.repoRoot);
|
|
23835
|
+
} catch {
|
|
23836
|
+
continue;
|
|
23837
|
+
}
|
|
23838
|
+
checkedRefs.push(ref);
|
|
23839
|
+
try {
|
|
23840
|
+
await runGit2(["merge-base", "--is-ancestor", head, commit], args.repoRoot);
|
|
23841
|
+
return { allow: true, status: "merged_to_default_ref", source: "git_merge_base", ref };
|
|
23842
|
+
} catch {
|
|
23843
|
+
}
|
|
23844
|
+
}
|
|
23845
|
+
return {
|
|
23846
|
+
allow: false,
|
|
23847
|
+
status: metadataStatus || void 0,
|
|
23848
|
+
error: checkedRefs.length ? `worktree HEAD is not contained in checked refs: ${checkedRefs.join(", ")}` : "no default/main refs were available for convergence verification"
|
|
23849
|
+
};
|
|
23850
|
+
}
|
|
23169
23851
|
isCompletedHostedSession(record) {
|
|
23170
23852
|
return record?.lifecycle === "stopped" || record?.lifecycle === "failed" || record?.lifecycle === "interrupted";
|
|
23171
23853
|
}
|
|
@@ -24125,10 +24807,61 @@ var DaemonCommandRouter = class {
|
|
|
24125
24807
|
if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
|
|
24126
24808
|
const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
24127
24809
|
const baseBranch = baseBranchStdout.trim();
|
|
24810
|
+
const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
|
|
24811
|
+
if (validationSummary.status === "failed") {
|
|
24812
|
+
return {
|
|
24813
|
+
success: false,
|
|
24814
|
+
code: "validation_failed",
|
|
24815
|
+
convergenceStatus: "blocked_review",
|
|
24816
|
+
error: "Refinery validation gate failed; merge/refine was not attempted.",
|
|
24817
|
+
branch,
|
|
24818
|
+
into: baseBranch,
|
|
24819
|
+
validationSummary,
|
|
24820
|
+
finalBranchConvergenceState: {
|
|
24821
|
+
branch,
|
|
24822
|
+
baseBranch,
|
|
24823
|
+
merged: false,
|
|
24824
|
+
removed: false,
|
|
24825
|
+
validation: "failed",
|
|
24826
|
+
status: "blocked_review"
|
|
24827
|
+
}
|
|
24828
|
+
};
|
|
24829
|
+
}
|
|
24830
|
+
if (validationSummary.status === "skipped") {
|
|
24831
|
+
return {
|
|
24832
|
+
success: false,
|
|
24833
|
+
code: "validation_unavailable",
|
|
24834
|
+
convergenceStatus: "blocked_review",
|
|
24835
|
+
error: "Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.",
|
|
24836
|
+
branch,
|
|
24837
|
+
into: baseBranch,
|
|
24838
|
+
validationSummary,
|
|
24839
|
+
finalBranchConvergenceState: {
|
|
24840
|
+
branch,
|
|
24841
|
+
baseBranch,
|
|
24842
|
+
merged: false,
|
|
24843
|
+
removed: false,
|
|
24844
|
+
validation: "unavailable",
|
|
24845
|
+
status: "blocked_review"
|
|
24846
|
+
}
|
|
24847
|
+
};
|
|
24848
|
+
}
|
|
24128
24849
|
try {
|
|
24129
24850
|
await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
24130
24851
|
} catch (e) {
|
|
24131
|
-
return {
|
|
24852
|
+
return {
|
|
24853
|
+
success: false,
|
|
24854
|
+
error: `Merge failed (conflicts?): ${e.message}`,
|
|
24855
|
+
validationSummary,
|
|
24856
|
+
finalBranchConvergenceState: {
|
|
24857
|
+
branch,
|
|
24858
|
+
baseBranch,
|
|
24859
|
+
merged: false,
|
|
24860
|
+
removed: false,
|
|
24861
|
+
validation: "passed",
|
|
24862
|
+
status: "not_mergeable"
|
|
24863
|
+
}
|
|
24864
|
+
};
|
|
24132
24865
|
}
|
|
24133
24866
|
const removeResult = await this.execute("remove_mesh_node", {
|
|
24134
24867
|
meshId,
|
|
@@ -24141,11 +24874,27 @@ var DaemonCommandRouter = class {
|
|
|
24141
24874
|
appendLedgerEntry2(meshId, {
|
|
24142
24875
|
kind: "node_removed",
|
|
24143
24876
|
nodeId,
|
|
24144
|
-
payload: { refined: true, mergedBranch: branch, into: baseBranch }
|
|
24877
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
|
|
24145
24878
|
});
|
|
24146
24879
|
} catch {
|
|
24147
24880
|
}
|
|
24148
|
-
return {
|
|
24881
|
+
return {
|
|
24882
|
+
success: true,
|
|
24883
|
+
merged: true,
|
|
24884
|
+
branch,
|
|
24885
|
+
into: baseBranch,
|
|
24886
|
+
removeResult,
|
|
24887
|
+
validationSummary,
|
|
24888
|
+
finalBranchConvergenceState: {
|
|
24889
|
+
branch: baseBranch,
|
|
24890
|
+
mergedBranch: branch,
|
|
24891
|
+
baseBranch,
|
|
24892
|
+
merged: true,
|
|
24893
|
+
removed: removeResult?.success !== false,
|
|
24894
|
+
validation: "passed",
|
|
24895
|
+
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
|
|
24896
|
+
}
|
|
24897
|
+
};
|
|
24149
24898
|
} catch (e) {
|
|
24150
24899
|
return { success: false, error: e.message };
|
|
24151
24900
|
}
|
|
@@ -24200,7 +24949,10 @@ var DaemonCommandRouter = class {
|
|
|
24200
24949
|
sessionCleanupMode,
|
|
24201
24950
|
workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
|
|
24202
24951
|
daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
|
|
24203
|
-
worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
|
|
24952
|
+
worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
|
|
24953
|
+
worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
|
|
24954
|
+
forced: worktreeCleanup?.forced === true ? true : void 0,
|
|
24955
|
+
forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0
|
|
24204
24956
|
}
|
|
24205
24957
|
});
|
|
24206
24958
|
} catch {
|
|
@@ -32337,6 +33089,9 @@ function launchIDE(ide, workspacePath) {
|
|
|
32337
33089
|
}
|
|
32338
33090
|
}
|
|
32339
33091
|
|
|
33092
|
+
// src/boot/daemon-lifecycle.ts
|
|
33093
|
+
init_cli_detector();
|
|
33094
|
+
|
|
32340
33095
|
// src/sessions/registry.ts
|
|
32341
33096
|
var SessionRegistry = class {
|
|
32342
33097
|
bySessionId = /* @__PURE__ */ new Map();
|
|
@@ -32673,6 +33428,7 @@ export {
|
|
|
32673
33428
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
32674
33429
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
32675
33430
|
NodePtyTransportFactory,
|
|
33431
|
+
P2pRelayFailureError,
|
|
32676
33432
|
ProviderCliAdapter,
|
|
32677
33433
|
ProviderInstanceManager,
|
|
32678
33434
|
ProviderLoader,
|
|
@@ -32689,6 +33445,7 @@ export {
|
|
|
32689
33445
|
buildChatTailDeliverySignature,
|
|
32690
33446
|
buildCoordinatorSystemPrompt,
|
|
32691
33447
|
buildMachineInfo,
|
|
33448
|
+
buildP2pRelayFailurePayload,
|
|
32692
33449
|
buildPinnedGlobalInstallCommand,
|
|
32693
33450
|
buildRuntimeSystemChatMessage,
|
|
32694
33451
|
buildSessionEntries,
|
|
@@ -32703,6 +33460,7 @@ export {
|
|
|
32703
33460
|
claimNextTask,
|
|
32704
33461
|
classifyChatMessageVisibility,
|
|
32705
33462
|
classifyHotChatSessionsForSubscriptionFlush,
|
|
33463
|
+
classifyP2pRelayFailure,
|
|
32706
33464
|
clearDebugTrace,
|
|
32707
33465
|
compareGitSnapshots,
|
|
32708
33466
|
configureDebugTraceStore,
|
|
@@ -32770,6 +33528,7 @@ export {
|
|
|
32770
33528
|
isInternalChatMessage,
|
|
32771
33529
|
isManagedStatusWaiting,
|
|
32772
33530
|
isManagedStatusWorking,
|
|
33531
|
+
isP2pRelayTransportFailure,
|
|
32773
33532
|
isPathInside,
|
|
32774
33533
|
isSessionHostLiveRuntime,
|
|
32775
33534
|
isSessionHostRecoverySnapshot,
|