@synkro-sh/cli 1.7.92 → 1.7.94
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/bootstrap.js +497 -831
- package/dist/bootstrap.js.map +1 -1
- package/package.json +16 -15
package/dist/bootstrap.js
CHANGED
|
@@ -147,7 +147,7 @@ function getIdentity() {
|
|
|
147
147
|
if (cached2) return cached2;
|
|
148
148
|
let cliVersion = "0.0.0";
|
|
149
149
|
try {
|
|
150
|
-
cliVersion = "1.7.
|
|
150
|
+
cliVersion = "1.7.94";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -1735,18 +1735,6 @@ function installCCHooks(settingsPath, config) {
|
|
|
1735
1735
|
],
|
|
1736
1736
|
[SYNKRO_MARKER]: true
|
|
1737
1737
|
});
|
|
1738
|
-
if (config.promptRouteScriptPath) {
|
|
1739
|
-
settings.hooks.UserPromptSubmit.push({
|
|
1740
|
-
hooks: [
|
|
1741
|
-
{
|
|
1742
|
-
type: "command",
|
|
1743
|
-
command: config.promptRouteScriptPath,
|
|
1744
|
-
timeout: 5
|
|
1745
|
-
}
|
|
1746
|
-
],
|
|
1747
|
-
[SYNKRO_MARKER]: true
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
1738
|
settings.hooks.Stop = settings.hooks.Stop ?? [];
|
|
1751
1739
|
removeSynkroEntries(settings.hooks, "Stop");
|
|
1752
1740
|
settings.hooks.Stop.push({
|
|
@@ -2112,7 +2100,6 @@ function installCodexHooks(hooksJsonPath, config) {
|
|
|
2112
2100
|
if (config.taskActivateIntentScriptPath) push(h, "PermissionRequest", [cmd(config.taskActivateIntentScriptPath, 5)], M_ACTIVATE);
|
|
2113
2101
|
push(h, "PostToolUse", [cmd(config.bashFollowupScriptPath, 10)], M_BASH);
|
|
2114
2102
|
push(h, "UserPromptSubmit", [cmd(config.userPromptSubmitScriptPath, 5)]);
|
|
2115
|
-
if (config.promptRouteScriptPath) push(h, "UserPromptSubmit", [cmd(config.promptRouteScriptPath, 5)]);
|
|
2116
2103
|
push(h, "SessionStart", [cmd(config.sessionStartScriptPath, 5)]);
|
|
2117
2104
|
push(h, "SubagentStart", [cmd(config.subagentStartScriptPath, 5)]);
|
|
2118
2105
|
if (!config.skipTranscriptSync) push(h, "SubagentStop", [cmd(config.subagentStopScriptPath, 5)]);
|
|
@@ -2141,13 +2128,14 @@ function uninstallCodexHooks(hooksJsonPath) {
|
|
|
2141
2128
|
writeHooksFileAtomic2(hooksJsonPath, file);
|
|
2142
2129
|
return true;
|
|
2143
2130
|
}
|
|
2144
|
-
var SYNKRO_MARKER3, M_BASH, CODEX_EDIT_MATCHER, M_EDIT, M_AGENT, M_MCP, M_ACTIVATE, ALL_EVENTS2;
|
|
2131
|
+
var SYNKRO_MARKER3, CODEX_BASH_MATCHER, M_BASH, CODEX_EDIT_MATCHER, M_EDIT, M_AGENT, M_MCP, M_ACTIVATE, ALL_EVENTS2;
|
|
2145
2132
|
var init_codexHookConfig = __esm({
|
|
2146
2133
|
"cli/installer/codexHookConfig.ts"() {
|
|
2147
2134
|
"use strict";
|
|
2148
2135
|
init_platform();
|
|
2149
2136
|
SYNKRO_MARKER3 = "__synkro_managed__";
|
|
2150
|
-
|
|
2137
|
+
CODEX_BASH_MATCHER = "^(?:Bash|exec_command|functions[._]exec_command)$";
|
|
2138
|
+
M_BASH = CODEX_BASH_MATCHER;
|
|
2151
2139
|
CODEX_EDIT_MATCHER = "^(?:apply_patch|ApplyPatch|Edit|Write|functions[._]apply_patch)$";
|
|
2152
2140
|
M_EDIT = CODEX_EDIT_MATCHER;
|
|
2153
2141
|
M_AGENT = "Agent";
|
|
@@ -2157,6 +2145,175 @@ var init_codexHookConfig = __esm({
|
|
|
2157
2145
|
}
|
|
2158
2146
|
});
|
|
2159
2147
|
|
|
2148
|
+
// cli/installer/codexHookTrust.ts
|
|
2149
|
+
import { spawn as spawn2 } from "child_process";
|
|
2150
|
+
function parseCodexHookTrustRecords(stdout) {
|
|
2151
|
+
for (const line of stdout.split("\n")) {
|
|
2152
|
+
let message;
|
|
2153
|
+
try {
|
|
2154
|
+
message = JSON.parse(line);
|
|
2155
|
+
} catch {
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2158
|
+
const data = message?.result?.data;
|
|
2159
|
+
if (!Array.isArray(data)) continue;
|
|
2160
|
+
const hooks = data.flatMap((entry) => Array.isArray(entry?.hooks) ? entry.hooks : []).filter((hook) => typeof hook.command === "string" && isSynkroHookCommand(hook.command));
|
|
2161
|
+
if (!hooks.length) return null;
|
|
2162
|
+
return hooks;
|
|
2163
|
+
}
|
|
2164
|
+
return null;
|
|
2165
|
+
}
|
|
2166
|
+
function summarizeCodexHookTrust(hooks) {
|
|
2167
|
+
return {
|
|
2168
|
+
total: hooks.length,
|
|
2169
|
+
trusted: hooks.filter((hook) => hook.trustStatus === "trusted" || hook.trustStatus === "managed").length,
|
|
2170
|
+
needsReview: hooks.filter((hook) => hook.trustStatus === "modified" || hook.trustStatus === "untrusted").length,
|
|
2171
|
+
disabled: hooks.filter((hook) => hook.enabled === false).length
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
function parseCodexHookTrustOutput(stdout) {
|
|
2175
|
+
const hooks = parseCodexHookTrustRecords(stdout);
|
|
2176
|
+
return hooks ? summarizeCodexHookTrust(hooks) : null;
|
|
2177
|
+
}
|
|
2178
|
+
function buildCodexHookTrustEdits(hooks) {
|
|
2179
|
+
const edits = /* @__PURE__ */ new Map();
|
|
2180
|
+
for (const hook of hooks) {
|
|
2181
|
+
if (typeof hook.command !== "string" || !isSynkroHookCommand(hook.command) || typeof hook.key !== "string" || !hook.key || typeof hook.currentHash !== "string" || !/^sha256:[0-9a-f]{64}$/i.test(hook.currentHash)) continue;
|
|
2182
|
+
const edit = {
|
|
2183
|
+
keyPath: `hooks.state.${JSON.stringify(hook.key)}.trusted_hash`,
|
|
2184
|
+
value: hook.currentHash,
|
|
2185
|
+
mergeStrategy: "upsert"
|
|
2186
|
+
};
|
|
2187
|
+
edits.set(edit.keyPath, edit);
|
|
2188
|
+
}
|
|
2189
|
+
return [...edits.values()];
|
|
2190
|
+
}
|
|
2191
|
+
function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTrust = false) {
|
|
2192
|
+
return new Promise((resolve6) => {
|
|
2193
|
+
let settled = false;
|
|
2194
|
+
let stdout = "";
|
|
2195
|
+
let pending = "";
|
|
2196
|
+
let hooks = null;
|
|
2197
|
+
let child;
|
|
2198
|
+
const finish = (summary) => {
|
|
2199
|
+
if (settled) return;
|
|
2200
|
+
settled = true;
|
|
2201
|
+
clearTimeout(timer);
|
|
2202
|
+
try {
|
|
2203
|
+
child.stdin.end();
|
|
2204
|
+
} catch {
|
|
2205
|
+
}
|
|
2206
|
+
try {
|
|
2207
|
+
child.kill();
|
|
2208
|
+
} catch {
|
|
2209
|
+
}
|
|
2210
|
+
resolve6(summary);
|
|
2211
|
+
};
|
|
2212
|
+
const timer = setTimeout(() => finish(null), 1e4);
|
|
2213
|
+
try {
|
|
2214
|
+
child = spawn2(codexBinary, ["app-server", "--stdio"], {
|
|
2215
|
+
cwd,
|
|
2216
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
2217
|
+
windowsHide: true
|
|
2218
|
+
});
|
|
2219
|
+
child.once("error", () => finish(null));
|
|
2220
|
+
child.once("exit", () => finish(parseCodexHookTrustOutput(stdout)));
|
|
2221
|
+
child.stdout.setEncoding("utf8");
|
|
2222
|
+
child.stdout.on("data", (chunk) => {
|
|
2223
|
+
stdout += chunk;
|
|
2224
|
+
pending += chunk;
|
|
2225
|
+
const lines = pending.split("\n");
|
|
2226
|
+
pending = lines.pop() || "";
|
|
2227
|
+
for (const line of lines) {
|
|
2228
|
+
let message;
|
|
2229
|
+
try {
|
|
2230
|
+
message = JSON.parse(line);
|
|
2231
|
+
} catch {
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
if (message?.id === 1 && message?.result) {
|
|
2235
|
+
child.stdin.write(JSON.stringify({
|
|
2236
|
+
id: 2,
|
|
2237
|
+
method: "hooks/list",
|
|
2238
|
+
params: { cwds: [cwd] }
|
|
2239
|
+
}) + "\n");
|
|
2240
|
+
} else if (message?.id === 2) {
|
|
2241
|
+
hooks = parseCodexHookTrustRecords(line);
|
|
2242
|
+
if (!hooks || !autoTrust) {
|
|
2243
|
+
finish(hooks ? summarizeCodexHookTrust(hooks) : null);
|
|
2244
|
+
continue;
|
|
2245
|
+
}
|
|
2246
|
+
const edits = buildCodexHookTrustEdits(hooks);
|
|
2247
|
+
if (!edits.length) {
|
|
2248
|
+
finish(summarizeCodexHookTrust(hooks));
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
child.stdin.write(JSON.stringify({
|
|
2252
|
+
id: 3,
|
|
2253
|
+
method: "config/batchWrite",
|
|
2254
|
+
params: { edits, reloadUserConfig: true }
|
|
2255
|
+
}) + "\n");
|
|
2256
|
+
} else if (message?.id === 3) {
|
|
2257
|
+
if (message?.result?.status !== "ok" || !hooks) {
|
|
2258
|
+
finish(hooks ? summarizeCodexHookTrust(hooks) : null);
|
|
2259
|
+
continue;
|
|
2260
|
+
}
|
|
2261
|
+
finish({
|
|
2262
|
+
total: hooks.length,
|
|
2263
|
+
trusted: hooks.length,
|
|
2264
|
+
needsReview: 0,
|
|
2265
|
+
disabled: hooks.filter((hook) => hook.enabled === false).length
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
child.stdin.write(JSON.stringify({
|
|
2271
|
+
id: 1,
|
|
2272
|
+
method: "initialize",
|
|
2273
|
+
params: {
|
|
2274
|
+
clientInfo: { name: "synkro-cli", version: "1" },
|
|
2275
|
+
capabilities: { experimentalApi: true }
|
|
2276
|
+
}
|
|
2277
|
+
}) + "\n");
|
|
2278
|
+
} catch {
|
|
2279
|
+
finish(null);
|
|
2280
|
+
}
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
function trustCodexHooks(codexBinary = "codex", cwd = process.cwd()) {
|
|
2284
|
+
return queryCodexHookTrust(codexBinary, cwd, true);
|
|
2285
|
+
}
|
|
2286
|
+
function codexHookTrustLines(summary) {
|
|
2287
|
+
if (!summary) {
|
|
2288
|
+
return [
|
|
2289
|
+
" \u26A0 Codex hook trust could not be verified.",
|
|
2290
|
+
" Restart Codex, run /hooks, and review the Synkro hooks before relying on enforcement."
|
|
2291
|
+
];
|
|
2292
|
+
}
|
|
2293
|
+
if (summary.needsReview === 0 && summary.disabled === 0 && summary.trusted === summary.total) {
|
|
2294
|
+
return [` \u2713 Codex hook trust confirmed (${summary.trusted}/${summary.total} active)`];
|
|
2295
|
+
}
|
|
2296
|
+
const issues = [
|
|
2297
|
+
summary.needsReview > 0 ? `${summary.needsReview} need review` : "",
|
|
2298
|
+
summary.disabled > 0 ? `${summary.disabled} disabled` : ""
|
|
2299
|
+
].filter(Boolean).join(", ");
|
|
2300
|
+
return [
|
|
2301
|
+
` \u26A0 Codex security enforcement is not active (${issues}).`,
|
|
2302
|
+
" Restart Codex, run /hooks, and trust only the Synkro hooks before relying on enforcement."
|
|
2303
|
+
];
|
|
2304
|
+
}
|
|
2305
|
+
async function reportCodexHookTrust(codexBinary, cwd) {
|
|
2306
|
+
const summary = await trustCodexHooks(codexBinary || "codex", cwd);
|
|
2307
|
+
for (const line of codexHookTrustLines(summary)) console.log(line);
|
|
2308
|
+
return summary;
|
|
2309
|
+
}
|
|
2310
|
+
var init_codexHookTrust = __esm({
|
|
2311
|
+
"cli/installer/codexHookTrust.ts"() {
|
|
2312
|
+
"use strict";
|
|
2313
|
+
init_platform();
|
|
2314
|
+
}
|
|
2315
|
+
});
|
|
2316
|
+
|
|
2160
2317
|
// cli/installer/mcpConfig.ts
|
|
2161
2318
|
import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
2162
2319
|
import { homedir as homedir12 } from "os";
|
|
@@ -2320,9 +2477,54 @@ function removeCodexManagedBlock(content, path) {
|
|
|
2320
2477
|
if (inside) throw new Error(`Incomplete Synkro MCP marker block in ${path}`);
|
|
2321
2478
|
return { content: out.join("\n").replace(/\n{3,}$/g, "\n\n"), removed };
|
|
2322
2479
|
}
|
|
2323
|
-
function
|
|
2324
|
-
const section = /^\s*\[\s*mcp_servers\s*\.\s*(?:"synkro-guardrails"|'synkro-guardrails'|synkro-guardrails)\s*\]\s*(?:#.*)?$/
|
|
2325
|
-
|
|
2480
|
+
function findCodexMcpSection(content) {
|
|
2481
|
+
const section = /^\s*\[\s*mcp_servers\s*\.\s*(?:"synkro-guardrails"|'synkro-guardrails'|synkro-guardrails)\s*\]\s*(?:#.*)?$/gm;
|
|
2482
|
+
const match = section.exec(content);
|
|
2483
|
+
if (!match) return null;
|
|
2484
|
+
const nextSection = /^\s*\[{1,2}[^\r\n]+?\]{1,2}\s*(?:#.*)?$/gm;
|
|
2485
|
+
nextSection.lastIndex = match.index + match[0].length;
|
|
2486
|
+
const next = nextSection.exec(content);
|
|
2487
|
+
return {
|
|
2488
|
+
start: match.index,
|
|
2489
|
+
end: next?.index ?? content.length,
|
|
2490
|
+
body: content.slice(match.index, next?.index ?? content.length)
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
function readTomlString(block, key) {
|
|
2494
|
+
const match = block.match(new RegExp(`^\\s*${key}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")\\s*$`, "m"));
|
|
2495
|
+
if (!match) return null;
|
|
2496
|
+
try {
|
|
2497
|
+
return JSON.parse(match[1]);
|
|
2498
|
+
} catch {
|
|
2499
|
+
return null;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
function isRecognizedCodexEntry(block, expectedUrl) {
|
|
2503
|
+
const command = readTomlString(block, "command");
|
|
2504
|
+
const argsLine = block.match(/^\s*args\s*=\s*(\[[^\r\n]*\])\s*$/m);
|
|
2505
|
+
if (command && argsLine) {
|
|
2506
|
+
try {
|
|
2507
|
+
const args2 = JSON.parse(argsLine[1]);
|
|
2508
|
+
if (/(?:^|[\\/])bun(?:\.exe)?$/i.test(command) && Array.isArray(args2) && args2.length === 2 && args2[0] === "run" && args2[1] === MCP_STDIO_PROXY_PATH) return true;
|
|
2509
|
+
} catch {
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
const url = readTomlString(block, "url");
|
|
2513
|
+
return Boolean(expectedUrl && url === expectedUrl);
|
|
2514
|
+
}
|
|
2515
|
+
function stripDetachedCodexMarkers(content) {
|
|
2516
|
+
return content.split("\n").filter((line) => line.trim() !== CODEX_MCP_BEGIN && line.trim() !== CODEX_MCP_END).join("\n");
|
|
2517
|
+
}
|
|
2518
|
+
function removeRecognizedCodexEntry(content, path, expectedUrl) {
|
|
2519
|
+
const range = findCodexMcpSection(content);
|
|
2520
|
+
if (!range) return { content, removed: false };
|
|
2521
|
+
if (!isRecognizedCodexEntry(range.body, expectedUrl)) {
|
|
2522
|
+
throw new Error(
|
|
2523
|
+
`${path} already defines ${CODEX_MCP_SECTION}; remove or rename that unmanaged entry before installing Synkro`
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
const next = `${content.slice(0, range.start)}${content.slice(range.end)}`;
|
|
2527
|
+
return { content: next.replace(/\n{3,}$/g, "\n\n"), removed: true };
|
|
2326
2528
|
}
|
|
2327
2529
|
function tomlString(value) {
|
|
2328
2530
|
return JSON.stringify(value);
|
|
@@ -2330,16 +2532,17 @@ function tomlString(value) {
|
|
|
2330
2532
|
function installCodexMcpConfig(opts) {
|
|
2331
2533
|
const path = codexConfigPath(opts.configPath);
|
|
2332
2534
|
const current = readCodexToml(path);
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2535
|
+
const targetUrl = opts.local ? `stdio://${MCP_STDIO_PROXY_PATH}` : `${opts.gatewayUrl.replace(/\/$/, "")}/api/v1/mcp/guardrails`;
|
|
2536
|
+
const withoutManaged = stripDetachedCodexMarkers(removeCodexManagedBlock(current, path).content);
|
|
2537
|
+
const withoutExisting = removeRecognizedCodexEntry(
|
|
2538
|
+
withoutManaged,
|
|
2539
|
+
path,
|
|
2540
|
+
opts.local ? void 0 : targetUrl
|
|
2541
|
+
).content;
|
|
2339
2542
|
let url;
|
|
2340
2543
|
let body;
|
|
2341
2544
|
if (opts.local) {
|
|
2342
|
-
url =
|
|
2545
|
+
url = targetUrl;
|
|
2343
2546
|
body = [
|
|
2344
2547
|
`[${CODEX_MCP_SECTION}]`,
|
|
2345
2548
|
`command = ${tomlString(opts.bunBin || resolveBunBin2())}`,
|
|
@@ -2348,7 +2551,7 @@ function installCodexMcpConfig(opts) {
|
|
|
2348
2551
|
];
|
|
2349
2552
|
} else {
|
|
2350
2553
|
if (!opts.bearerToken) throw new Error("Codex cloud MCP registration requires a bearer token");
|
|
2351
|
-
url =
|
|
2554
|
+
url = targetUrl;
|
|
2352
2555
|
body = [
|
|
2353
2556
|
`[${CODEX_MCP_SECTION}]`,
|
|
2354
2557
|
`url = ${tomlString(url)}`,
|
|
@@ -2356,7 +2559,7 @@ function installCodexMcpConfig(opts) {
|
|
|
2356
2559
|
"enabled = true"
|
|
2357
2560
|
];
|
|
2358
2561
|
}
|
|
2359
|
-
const prefix =
|
|
2562
|
+
const prefix = withoutExisting.trimEnd();
|
|
2360
2563
|
const managed = [CODEX_MCP_BEGIN, ...body, CODEX_MCP_END].join("\n");
|
|
2361
2564
|
writeCodexTomlAtomic(path, `${prefix}${prefix ? "\n\n" : ""}${managed}
|
|
2362
2565
|
`);
|
|
@@ -2367,8 +2570,19 @@ function uninstallCodexMcpConfig(configPath) {
|
|
|
2367
2570
|
if (!existsSync13(path)) return false;
|
|
2368
2571
|
const current = readCodexToml(path);
|
|
2369
2572
|
const next = removeCodexManagedBlock(current, path);
|
|
2370
|
-
|
|
2371
|
-
|
|
2573
|
+
let content = stripDetachedCodexMarkers(next.content);
|
|
2574
|
+
let removed = next.removed;
|
|
2575
|
+
if (!removed) {
|
|
2576
|
+
try {
|
|
2577
|
+
const recognized = removeRecognizedCodexEntry(content, path);
|
|
2578
|
+
content = recognized.content;
|
|
2579
|
+
removed = recognized.removed;
|
|
2580
|
+
} catch {
|
|
2581
|
+
return false;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
if (!removed) return false;
|
|
2585
|
+
writeCodexTomlAtomic(path, content.trimEnd() ? `${content.trimEnd()}
|
|
2372
2586
|
` : "");
|
|
2373
2587
|
return true;
|
|
2374
2588
|
}
|
|
@@ -2982,6 +3196,26 @@ export function normalizeCodexApplyPatch(payload: any): void {
|
|
|
2982
3196
|
} catch { /* leave payload untouched on any parse error */ }
|
|
2983
3197
|
}
|
|
2984
3198
|
|
|
3199
|
+
/**
|
|
3200
|
+
* Normalize Codex app/local-function command aliases into the Bash hook shape
|
|
3201
|
+
* consumed by scanRouter. Codex CLI already emits Bash + command; bridges may
|
|
3202
|
+
* retain exec_command (optionally namespaced) + cmd.
|
|
3203
|
+
*/
|
|
3204
|
+
export function normalizeCodexExecCommand(payload: any): void {
|
|
3205
|
+
const toolName = String(payload?.tool_name || '');
|
|
3206
|
+
if (!/^(?:exec_command|functions[._]exec_command)$/i.test(toolName)) return;
|
|
3207
|
+
const toolInput = payload?.tool_input && typeof payload.tool_input === 'object'
|
|
3208
|
+
? payload.tool_input
|
|
3209
|
+
: {};
|
|
3210
|
+
payload.tool_name = 'Bash';
|
|
3211
|
+
payload.tool_input = {
|
|
3212
|
+
...toolInput,
|
|
3213
|
+
command: typeof toolInput.command === 'string'
|
|
3214
|
+
? toolInput.command
|
|
3215
|
+
: String(toolInput.cmd || ''),
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
|
|
2985
3219
|
type RecoveredCodexEdit = { payload: Record<string, any>; baseContent?: string };
|
|
2986
3220
|
|
|
2987
3221
|
function completedCodexToolRecord(entry: any): any {
|
|
@@ -3295,9 +3529,12 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
|
|
|
3295
3529
|
const input = await readStdin();
|
|
3296
3530
|
if (!input.trim()) { out(failOpen(harness)); return; }
|
|
3297
3531
|
const payload = JSON.parse(input);
|
|
3298
|
-
// Codex: rewrite
|
|
3299
|
-
// surface reads
|
|
3300
|
-
if (harness === 'codex')
|
|
3532
|
+
// Codex: rewrite bridged tool aliases into the shared CC shapes before any
|
|
3533
|
+
// surface reads them. No-op for CC/Cursor and already-canonical tools.
|
|
3534
|
+
if (harness === 'codex') {
|
|
3535
|
+
normalizeCodexApplyPatch(payload);
|
|
3536
|
+
normalizeCodexExecCommand(payload);
|
|
3537
|
+
}
|
|
3301
3538
|
if (harness === 'codex' && opts.subagent) {
|
|
3302
3539
|
const parentSessionId = String(payload?.session_id || '');
|
|
3303
3540
|
const childSessionId = String(payload?.agent_id || '');
|
|
@@ -3763,165 +4000,8 @@ function emitStubTelemetry(
|
|
|
3763
4000
|
STUB_USER_PROMPT_SUBMIT_TS = stubHook("prompt-submit", "{ telemetry: true }");
|
|
3764
4001
|
STUB_BASH_FOLLOWUP_TS = stubHook("bash-followup", "{ telemetry: true }");
|
|
3765
4002
|
STUB_PROMPT_ROUTE_TS = `#!/usr/bin/env bun
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
import { join } from 'node:path';
|
|
3769
|
-
import { spawnSync, spawn } from 'node:child_process';
|
|
3770
|
-
|
|
3771
|
-
const HOME = homedir();
|
|
3772
|
-
const MODEL_RE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
3773
|
-
|
|
3774
|
-
function cliBin(): string {
|
|
3775
|
-
try {
|
|
3776
|
-
const txt = readFileSync(join(HOME, '.synkro', 'config.env'), 'utf-8');
|
|
3777
|
-
for (const line of txt.split('\\n')) {
|
|
3778
|
-
const t = line.trim();
|
|
3779
|
-
if (t.startsWith('SYNKRO_CLI_BIN=')) {
|
|
3780
|
-
let v = t.slice('SYNKRO_CLI_BIN='.length).trim();
|
|
3781
|
-
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
3782
|
-
return v;
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
} catch {}
|
|
3786
|
-
return process.env.SYNKRO_CLI_BIN || '';
|
|
3787
|
-
}
|
|
3788
|
-
|
|
3789
|
-
function mcpJwt(): string {
|
|
3790
|
-
try { return readFileSync(join(HOME, '.synkro', '.mcp-jwt'), 'utf-8').trim(); } catch { return ''; }
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
// The caller's own tmux session, from the inherited $TMUX \u2014 so routing targets
|
|
3794
|
-
// THIS session, not whichever shim last wrote the shared active-session file.
|
|
3795
|
-
function currentTmuxSession(): string {
|
|
3796
|
-
if (!process.env.TMUX) return '';
|
|
3797
|
-
try {
|
|
3798
|
-
const pane = process.env.TMUX_PANE || '';
|
|
3799
|
-
const args = pane
|
|
3800
|
-
? ['display-message', '-p', '-t', pane, '#{session_name}']
|
|
3801
|
-
: ['display-message', '-p', '#{session_name}'];
|
|
3802
|
-
const r = spawnSync('tmux', args, { encoding: 'utf-8' });
|
|
3803
|
-
if (r.status === 0) return (r.stdout || '').trim();
|
|
3804
|
-
} catch {}
|
|
3805
|
-
return '';
|
|
3806
|
-
}
|
|
3807
|
-
|
|
3808
|
-
const chunks: Buffer[] = [];
|
|
3809
|
-
for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3810
|
-
|
|
3811
|
-
// Verdict: allow ({}) unless we decide to BLOCK this prompt for routing. We must
|
|
3812
|
-
// emit exactly one JSON line at the end (not up-front) so the block path can win.
|
|
3813
|
-
let blockForRouting = false;
|
|
3814
|
-
try {
|
|
3815
|
-
const payload = JSON.parse(Buffer.concat(chunks).toString('utf-8') || '{}');
|
|
3816
|
-
const sid = String(payload.session_id || payload.conversation_id || '');
|
|
3817
|
-
if (sid) {
|
|
3818
|
-
const safeSid = sid.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
3819
|
-
const tmuxSession = currentTmuxSession();
|
|
3820
|
-
const prompt = String(payload.prompt || '');
|
|
3821
|
-
|
|
3822
|
-
// Session registry: record THIS live session (heartbeat \u2014 ts refreshed every
|
|
3823
|
-
// prompt) so a router or a human can enumerate live sessions via
|
|
3824
|
-
// 'synkro sessions' and target exactly one by session_id. Wrapped + best-effort
|
|
3825
|
-
// so it never delays or breaks the prompt.
|
|
3826
|
-
try {
|
|
3827
|
-
const sessDir = join(HOME, '.synkro', 'pty', 'sessions');
|
|
3828
|
-
mkdirSync(sessDir, { recursive: true });
|
|
3829
|
-
const rec = {
|
|
3830
|
-
session_id: sid,
|
|
3831
|
-
tmux_session: tmuxSession,
|
|
3832
|
-
cwd: String(payload.cwd || ''),
|
|
3833
|
-
last_prompt: prompt.slice(0, 200),
|
|
3834
|
-
transcript_path: String(payload.transcript_path || ''),
|
|
3835
|
-
ts: Date.now(),
|
|
3836
|
-
};
|
|
3837
|
-
writeFileSync(join(sessDir, safeSid + '.json'), JSON.stringify(rec));
|
|
3838
|
-
// TTL-prune stale records (24h) \u2014 cheap, no per-record process spawn. Live
|
|
3839
|
-
// sessions refresh their ts every prompt, so only ended ones age out.
|
|
3840
|
-
const TTL = 24 * 60 * 60 * 1000;
|
|
3841
|
-
const now = Date.now();
|
|
3842
|
-
for (const f of readdirSync(sessDir)) {
|
|
3843
|
-
if (!f.endsWith('.json')) continue;
|
|
3844
|
-
try {
|
|
3845
|
-
const r = JSON.parse(readFileSync(join(sessDir, f), 'utf-8'));
|
|
3846
|
-
if (!r || typeof r.ts !== 'number' || now - r.ts > TTL) unlinkSync(join(sessDir, f));
|
|
3847
|
-
} catch { try { unlinkSync(join(sessDir, f)); } catch {} }
|
|
3848
|
-
}
|
|
3849
|
-
} catch {}
|
|
3850
|
-
|
|
3851
|
-
// \u2500\u2500 Routing: block + re-inject \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3852
|
-
// Loop-guard: a re-injected prompt carries route-guard-<sid>; clear it and
|
|
3853
|
-
// pass straight through (never route the re-injected prompt \u2014 that would loop).
|
|
3854
|
-
const guardFile = join(HOME, '.synkro', 'pty', 'route-guard-' + safeSid);
|
|
3855
|
-
let isReinjected = false;
|
|
3856
|
-
try {
|
|
3857
|
-
if (existsSync(guardFile)) { isReinjected = true; try { unlinkSync(guardFile); } catch {} }
|
|
3858
|
-
} catch {}
|
|
3859
|
-
|
|
3860
|
-
// Only route real task prompts. Skip empty input, slash-commands (/\u2026), bash
|
|
3861
|
-
// lines (!\u2026), and common conversational turns \u2014 those are never something to
|
|
3862
|
-
// pick a coding model for, and blocking them would add a needless pause. The
|
|
3863
|
-
// haiku 'keep' verdict is the backstop for anything this quick list misses.
|
|
3864
|
-
const t = prompt.trim();
|
|
3865
|
-
const norm = t.toLowerCase().replace(/[.!?,;:\\s]+$/, '').trim();
|
|
3866
|
-
const CONVERSATIONAL = new Set([
|
|
3867
|
-
'ok', 'okay', 'k', 'kk', 'yes', 'yep', 'yeah', 'ya', 'y', 'no', 'nope', 'n',
|
|
3868
|
-
'sure', 'thanks', 'thank you', 'thx', 'ty', 'cool', 'nice', 'great', 'awesome',
|
|
3869
|
-
'perfect', 'got it', 'gotcha', 'right', 'makes sense', 'sounds good', 'lgtm',
|
|
3870
|
-
'continue', 'keep going', 'go on', 'go ahead', 'proceed', 'next', 'more',
|
|
3871
|
-
'again', 'retry', 'stop', 'wait', 'hmm', 'hm', 'oh', 'i see', 'done', 'good',
|
|
3872
|
-
]);
|
|
3873
|
-
const routable = t.length > 0 && !t.startsWith('/') && !t.startsWith('!') && !CONVERSATIONAL.has(norm);
|
|
3874
|
-
|
|
3875
|
-
// Routing is opt-in via a marker file \u2014 either global ('routing-on', every
|
|
3876
|
-
// session) or per-session ('routing-on-<sid>', just this one). Per-session lets
|
|
3877
|
-
// us enable routing for one session (e.g. a test) without touching the others.
|
|
3878
|
-
let routingEnabled = false;
|
|
3879
|
-
try {
|
|
3880
|
-
routingEnabled = existsSync(join(HOME, '.synkro', 'pty', 'routing-on'))
|
|
3881
|
-
|| existsSync(join(HOME, '.synkro', 'pty', 'routing-on-' + safeSid));
|
|
3882
|
-
} catch {}
|
|
3883
|
-
|
|
3884
|
-
if (!isReinjected && routable && routingEnabled) {
|
|
3885
|
-
// ESCALATION-ONLY: classify in-place (fast local head, 1s cap). Block +
|
|
3886
|
-
// escalate to Opus ONLY for a confident opus verdict; never downgrade. Any
|
|
3887
|
-
// timeout / non-opus / error \u2192 run on the current model (no block). This
|
|
3888
|
-
// makes the classifier's weakness (under-firing opus) harmless: worst case
|
|
3889
|
-
// is a MISSED escalation = status quo, never a broken output on a weak model.
|
|
3890
|
-
let escalate = false;
|
|
3891
|
-
try {
|
|
3892
|
-
const resp = await fetch('http://127.0.0.1:' + (process.env.SYNKRO_GRADER_HOST_PORT || '18929') + '/submit', {
|
|
3893
|
-
method: 'POST',
|
|
3894
|
-
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + mcpJwt() },
|
|
3895
|
-
body: JSON.stringify({ role: 'route-classify', payload: t.slice(0, 1000), content: 'x', hedge: false }),
|
|
3896
|
-
signal: AbortSignal.timeout(1000),
|
|
3897
|
-
});
|
|
3898
|
-
if (resp.ok) {
|
|
3899
|
-
const data = await resp.json();
|
|
3900
|
-
if (/<model>\\s*claude-opus-4-8\\s*<\\/model>/i.test(String(data.result || ''))) escalate = true;
|
|
3901
|
-
}
|
|
3902
|
-
} catch {}
|
|
3903
|
-
if (escalate) {
|
|
3904
|
-
try {
|
|
3905
|
-
const bin = cliBin();
|
|
3906
|
-
if (bin) {
|
|
3907
|
-
// Orchestrator applies the pre-decided escalation (skip re-classify).
|
|
3908
|
-
const child = spawn('node', [bin, 'route-and-resubmit', sid, t.slice(0, 1000), tmuxSession || '', 'claude-opus-4-8'], { detached: true, stdio: 'ignore' });
|
|
3909
|
-
child.unref();
|
|
3910
|
-
blockForRouting = true; // hold this prompt; the orchestrator re-submits it on Opus
|
|
3911
|
-
}
|
|
3912
|
-
} catch {}
|
|
3913
|
-
}
|
|
3914
|
-
}
|
|
3915
|
-
}
|
|
3916
|
-
} catch {}
|
|
3917
|
-
|
|
3918
|
-
// Emit the single verdict line. Block (with a visible reason) when routing is
|
|
3919
|
-
// taking over the prompt; otherwise allow. Fail-open: any throw above \u2192 allow.
|
|
3920
|
-
if (blockForRouting) {
|
|
3921
|
-
process.stdout.write(JSON.stringify({ decision: 'block', reason: 'Synkro is routing this to the best model and will resubmit it automatically\u2026' }) + '\\n');
|
|
3922
|
-
} else {
|
|
3923
|
-
process.stdout.write('{}\\n');
|
|
3924
|
-
}
|
|
4003
|
+
for await (const _chunk of process.stdin) {}
|
|
4004
|
+
process.stdout.write('{}\\n');
|
|
3925
4005
|
`;
|
|
3926
4006
|
STUB_TASK_ACTIVATE_INTENT_TS = `#!/usr/bin/env bun
|
|
3927
4007
|
import { readFileSync } from 'node:fs';
|
|
@@ -4560,7 +4640,7 @@ __export(claudeDesktopTap_exports, {
|
|
|
4560
4640
|
claudeDesktopInstalled: () => claudeDesktopInstalled,
|
|
4561
4641
|
runClaudeDesktopTap: () => runClaudeDesktopTap
|
|
4562
4642
|
});
|
|
4563
|
-
import { spawn as
|
|
4643
|
+
import { spawn as spawn3 } from "child_process";
|
|
4564
4644
|
import { writeFileSync as writeFileSync12, mkdtempSync, mkdirSync as mkdirSync10, readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
|
|
4565
4645
|
import { join as join13 } from "path";
|
|
4566
4646
|
import { homedir as homedir15 } from "os";
|
|
@@ -4709,7 +4789,7 @@ async function runClaudeDesktopTap(opts = {}) {
|
|
|
4709
4789
|
const runnerPath = join13(sessionDir, "run.sh");
|
|
4710
4790
|
writeFileSync12(runnerPath, buildRunner(sessionDir), { mode: 493 });
|
|
4711
4791
|
await new Promise((resolve6) => {
|
|
4712
|
-
const child =
|
|
4792
|
+
const child = spawn3("bash", [runnerPath], {
|
|
4713
4793
|
stdio: "inherit",
|
|
4714
4794
|
env: { ...process.env, SYNKRO_CAPTURE_URL: CAPTURE_URL, SYNKRO_SCAN_URL: SCAN_URL, SYNKRO_SCAN_TURN_URL: SCAN_TURN_URL, SYNKRO_DLP_POLICY_URL: DLP_POLICY_URL, SYNKRO_TURN_VERDICTS_URL: TURN_VERDICTS_URL, SYNKRO_TURN_VERDICT_URL: TURN_VERDICT_URL, SYNKRO_MCP_EVENT_URL: MCP_EVENT_URL, SYNKRO_TAP_TOKEN: token, SYNKRO_TAP_TOKEN_FILE: JWT_PATH, SYNKRO_CD_BACKFILL: opts.backfill ? "1" : "" }
|
|
4715
4795
|
});
|
|
@@ -6093,6 +6173,7 @@ __export(dockerInstall_exports, {
|
|
|
6093
6173
|
dockerUpdate: () => dockerUpdate,
|
|
6094
6174
|
ensurePgliteProxyCredentials: () => ensurePgliteProxyCredentials,
|
|
6095
6175
|
imageTag: () => imageTag,
|
|
6176
|
+
isContainerActiveState: () => isContainerActiveState,
|
|
6096
6177
|
normalizeProvider: () => normalizeProvider,
|
|
6097
6178
|
poolLabel: () => poolLabel,
|
|
6098
6179
|
readContainerConfig: () => readContainerConfig,
|
|
@@ -6126,6 +6207,9 @@ function resolveContainerName(raw = process.env.SYNKRO_CONTAINER_NAME) {
|
|
|
6126
6207
|
}
|
|
6127
6208
|
return value;
|
|
6128
6209
|
}
|
|
6210
|
+
function isContainerActiveState(status) {
|
|
6211
|
+
return status === "running" || status === "restarting" || status === "paused";
|
|
6212
|
+
}
|
|
6129
6213
|
function createPgliteScramVerifier(password, salt = randomBytes2(16)) {
|
|
6130
6214
|
const iterations = 4096;
|
|
6131
6215
|
const saltedPassword = pbkdf2Sync(password, salt, iterations, 32, "sha256");
|
|
@@ -6134,27 +6218,29 @@ function createPgliteScramVerifier(password, salt = randomBytes2(16)) {
|
|
|
6134
6218
|
const serverKey = createHmac("sha256", saltedPassword).update("Server Key").digest();
|
|
6135
6219
|
return `SCRAM-SHA-256$${iterations}:${salt.toString("base64")}$${storedKey.toString("base64")}:${serverKey.toString("base64")}`;
|
|
6136
6220
|
}
|
|
6137
|
-
function ensurePgliteProxyCredentials() {
|
|
6138
|
-
const
|
|
6139
|
-
const
|
|
6221
|
+
function ensurePgliteProxyCredentials(paths = {}) {
|
|
6222
|
+
const passwordPath = paths.passwordPath ?? PGLITE_PASSWORD_PATH;
|
|
6223
|
+
const userlistPath = paths.userlistPath ?? PGLITE_USERLIST_PATH;
|
|
6224
|
+
const hasPassword = existsSync19(passwordPath) && readFileSync17(passwordPath, "utf-8").trim().length > 0;
|
|
6225
|
+
const hasUserlist = existsSync19(userlistPath) && readFileSync17(userlistPath, "utf-8").trim().length > 0;
|
|
6140
6226
|
if (hasPassword && hasUserlist) {
|
|
6141
|
-
chmodSync3(
|
|
6142
|
-
chmodSync3(
|
|
6227
|
+
chmodSync3(passwordPath, 384);
|
|
6228
|
+
chmodSync3(userlistPath, 384);
|
|
6143
6229
|
return;
|
|
6144
6230
|
}
|
|
6145
6231
|
const password = randomBytes2(24).toString("base64url");
|
|
6146
6232
|
const verifier = createPgliteScramVerifier(password);
|
|
6147
6233
|
const suffix = `${process.pid}.${Date.now()}.tmp`;
|
|
6148
|
-
const passwordTmp = `${
|
|
6149
|
-
const userlistTmp = `${
|
|
6234
|
+
const passwordTmp = `${passwordPath}.${suffix}`;
|
|
6235
|
+
const userlistTmp = `${userlistPath}.${suffix}`;
|
|
6150
6236
|
writeFileSync14(passwordTmp, `${password}
|
|
6151
6237
|
`, { mode: 384 });
|
|
6152
6238
|
writeFileSync14(userlistTmp, `"synkro" "${verifier}"
|
|
6153
6239
|
`, { mode: 384 });
|
|
6154
|
-
renameSync7(passwordTmp,
|
|
6155
|
-
renameSync7(userlistTmp,
|
|
6156
|
-
chmodSync3(
|
|
6157
|
-
chmodSync3(
|
|
6240
|
+
renameSync7(passwordTmp, passwordPath);
|
|
6241
|
+
renameSync7(userlistTmp, userlistPath);
|
|
6242
|
+
chmodSync3(passwordPath, 384);
|
|
6243
|
+
chmodSync3(userlistPath, 384);
|
|
6158
6244
|
}
|
|
6159
6245
|
function resolveConductorProvider(pool, counts) {
|
|
6160
6246
|
if (pool !== "auto") return pool;
|
|
@@ -6615,7 +6701,7 @@ function dockerStatus() {
|
|
|
6615
6701
|
timeout: 5e3
|
|
6616
6702
|
});
|
|
6617
6703
|
const status = (r.stdout || "").trim();
|
|
6618
|
-
if (status
|
|
6704
|
+
if (!isContainerActiveState(status)) return { running: false };
|
|
6619
6705
|
return {
|
|
6620
6706
|
running: true,
|
|
6621
6707
|
image: imageTag(),
|
|
@@ -6785,7 +6871,7 @@ var init_dockerInstall = __esm({
|
|
|
6785
6871
|
HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
|
|
6786
6872
|
CONTAINER_NAME = resolveContainerName();
|
|
6787
6873
|
defaultImageVersion = () => {
|
|
6788
|
-
if (true) return "1.7.
|
|
6874
|
+
if (true) return "1.7.94";
|
|
6789
6875
|
try {
|
|
6790
6876
|
const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
|
|
6791
6877
|
if (pkg.version) return pkg.version;
|
|
@@ -7022,6 +7108,7 @@ __export(ptyShim_exports, {
|
|
|
7022
7108
|
injectModel: () => injectModel,
|
|
7023
7109
|
installPtyShim: () => installPtyShim,
|
|
7024
7110
|
listSessions: () => listSessions,
|
|
7111
|
+
restoreLegacyClaudeShim: () => restoreLegacyClaudeShim,
|
|
7025
7112
|
uninstallPtyShim: () => uninstallPtyShim
|
|
7026
7113
|
});
|
|
7027
7114
|
import {
|
|
@@ -7038,48 +7125,11 @@ import {
|
|
|
7038
7125
|
} from "fs";
|
|
7039
7126
|
import { homedir as homedir20 } from "os";
|
|
7040
7127
|
import { join as join18 } from "path";
|
|
7041
|
-
import { spawnSync as spawnSync5, spawn as
|
|
7128
|
+
import { spawnSync as spawnSync5, spawn as spawn4 } from "child_process";
|
|
7042
7129
|
function rcFiles() {
|
|
7043
7130
|
const h = homedir20();
|
|
7044
7131
|
return [join18(h, ".zshrc"), join18(h, ".bashrc"), join18(h, ".bash_profile")];
|
|
7045
7132
|
}
|
|
7046
|
-
function resolveRealClaude() {
|
|
7047
|
-
const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
|
|
7048
|
-
const p = (r.stdout || "").trim();
|
|
7049
|
-
if (p) {
|
|
7050
|
-
try {
|
|
7051
|
-
return realpathSync(p);
|
|
7052
|
-
} catch {
|
|
7053
|
-
return p;
|
|
7054
|
-
}
|
|
7055
|
-
}
|
|
7056
|
-
for (const c of [join18(homedir20(), ".local", "bin", "claude"), "/usr/local/bin/claude", "/opt/homebrew/bin/claude"]) {
|
|
7057
|
-
if (existsSync21(c)) {
|
|
7058
|
-
try {
|
|
7059
|
-
return realpathSync(c);
|
|
7060
|
-
} catch {
|
|
7061
|
-
return c;
|
|
7062
|
-
}
|
|
7063
|
-
}
|
|
7064
|
-
}
|
|
7065
|
-
return "claude";
|
|
7066
|
-
}
|
|
7067
|
-
function findClaudeLink() {
|
|
7068
|
-
const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
|
|
7069
|
-
let linkPath = (r.stdout || "").trim();
|
|
7070
|
-
if (!linkPath) {
|
|
7071
|
-
const c = join18(homedir20(), ".local", "bin", "claude");
|
|
7072
|
-
if (existsSync21(c)) linkPath = c;
|
|
7073
|
-
else return null;
|
|
7074
|
-
}
|
|
7075
|
-
if (linkPath === SHIM_PATH) return null;
|
|
7076
|
-
let realTarget = linkPath;
|
|
7077
|
-
try {
|
|
7078
|
-
realTarget = realpathSync(linkPath);
|
|
7079
|
-
} catch {
|
|
7080
|
-
}
|
|
7081
|
-
return { linkPath, realTarget };
|
|
7082
|
-
}
|
|
7083
7133
|
function isOurShim(path) {
|
|
7084
7134
|
try {
|
|
7085
7135
|
return readFileSync20(path, "utf-8").slice(0, 300).includes("Synkro pty shim");
|
|
@@ -7087,44 +7137,39 @@ function isOurShim(path) {
|
|
|
7087
7137
|
return false;
|
|
7088
7138
|
}
|
|
7089
7139
|
}
|
|
7090
|
-
function
|
|
7091
|
-
|
|
7092
|
-
if (!found) {
|
|
7093
|
-
console.log(" \xB7 no claude binary to shadow \u2014 PATH shim only");
|
|
7094
|
-
return;
|
|
7095
|
-
}
|
|
7096
|
-
const { linkPath, realTarget } = found;
|
|
7097
|
-
if (isOurShim(linkPath)) {
|
|
7098
|
-
console.log(` \xB7 ${linkPath.replace(homedir20(), "~")} already shadowed`);
|
|
7099
|
-
return;
|
|
7100
|
-
}
|
|
7101
|
-
let wasSymlink = false;
|
|
7102
|
-
try {
|
|
7103
|
-
wasSymlink = lstatSync(linkPath).isSymbolicLink();
|
|
7104
|
-
} catch {
|
|
7105
|
-
}
|
|
7106
|
-
const state = { linkPath, realTarget, wasSymlink };
|
|
7140
|
+
function latestVersionedClaude(versionsDir) {
|
|
7141
|
+
let versions = [];
|
|
7107
7142
|
try {
|
|
7108
|
-
|
|
7143
|
+
versions = readdirSync3(versionsDir).map((entry) => join18(versionsDir, entry)).filter((entry) => existsSync21(entry)).sort((a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" }));
|
|
7109
7144
|
} catch {
|
|
7110
7145
|
}
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7146
|
+
return versions.at(-1) || "";
|
|
7147
|
+
}
|
|
7148
|
+
function restoreLegacyClaudeShim(linkPath, versionsDir) {
|
|
7149
|
+
if (!isOurShim(linkPath)) return false;
|
|
7150
|
+
const realTarget = latestVersionedClaude(versionsDir);
|
|
7151
|
+
if (!realTarget) {
|
|
7152
|
+
throw new Error(`no Claude version found under ${versionsDir}`);
|
|
7118
7153
|
}
|
|
7154
|
+
rmSync2(linkPath, { force: true });
|
|
7155
|
+
symlinkSync(realTarget, linkPath);
|
|
7156
|
+
return true;
|
|
7119
7157
|
}
|
|
7120
7158
|
function unshadowClaude() {
|
|
7121
7159
|
let state;
|
|
7122
7160
|
try {
|
|
7123
7161
|
state = JSON.parse(readFileSync20(SHADOW_STATE_FILE, "utf-8"));
|
|
7124
7162
|
} catch {
|
|
7163
|
+
}
|
|
7164
|
+
if (!state?.linkPath) {
|
|
7165
|
+
const legacyLink = join18(homedir20(), ".local", "bin", "claude");
|
|
7166
|
+
const versionsDir = join18(homedir20(), ".local", "share", "claude", "versions");
|
|
7167
|
+
if (restoreLegacyClaudeShim(legacyLink, versionsDir)) {
|
|
7168
|
+
const target = realpathSync(legacyLink);
|
|
7169
|
+
console.log(`\u2713 restored ${legacyLink.replace(homedir20(), "~")} \u2192 ${target.replace(homedir20(), "~")}`);
|
|
7170
|
+
}
|
|
7125
7171
|
return;
|
|
7126
7172
|
}
|
|
7127
|
-
if (!state?.linkPath) return;
|
|
7128
7173
|
try {
|
|
7129
7174
|
if (existsSync21(state.linkPath) && !isOurShim(state.linkPath)) return;
|
|
7130
7175
|
rmSync2(state.linkPath, { force: true });
|
|
@@ -7134,25 +7179,6 @@ function unshadowClaude() {
|
|
|
7134
7179
|
console.warn(` \u26A0 could not restore claude: ${e.message} \u2014 run: ln -sf ${state.realTarget} ${state.linkPath}`);
|
|
7135
7180
|
}
|
|
7136
7181
|
}
|
|
7137
|
-
function addPathBlock() {
|
|
7138
|
-
const touched = [];
|
|
7139
|
-
for (const rc of rcFiles()) {
|
|
7140
|
-
if (!existsSync21(rc) && rc.endsWith(".bash_profile")) continue;
|
|
7141
|
-
let body = "";
|
|
7142
|
-
try {
|
|
7143
|
-
body = readFileSync20(rc, "utf-8");
|
|
7144
|
-
} catch {
|
|
7145
|
-
}
|
|
7146
|
-
const cleaned = stripBlock(body);
|
|
7147
|
-
const next = cleaned.replace(/\n*$/, "") + (cleaned ? "\n\n" : "") + RC_BLOCK + "\n";
|
|
7148
|
-
try {
|
|
7149
|
-
writeFileSync16(rc, next, "utf-8");
|
|
7150
|
-
touched.push(rc);
|
|
7151
|
-
} catch {
|
|
7152
|
-
}
|
|
7153
|
-
}
|
|
7154
|
-
return touched;
|
|
7155
|
-
}
|
|
7156
7182
|
function stripBlock(body) {
|
|
7157
7183
|
if (!body.includes(RC_BEGIN)) return body;
|
|
7158
7184
|
const re = new RegExp(`\\n?${escapeRe(RC_BEGIN)}[\\s\\S]*?${escapeRe(RC_END)}\\n?`, "g");
|
|
@@ -7162,28 +7188,14 @@ function escapeRe(s) {
|
|
|
7162
7188
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7163
7189
|
}
|
|
7164
7190
|
function installPtyShim() {
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
const real = resolveRealClaude();
|
|
7168
|
-
writeFileSync16(SHIM_PATH, SHIM_SOURCE.replace("__BAKED_CLAUDE__", real), "utf-8");
|
|
7169
|
-
chmodSync4(SHIM_PATH, 493);
|
|
7170
|
-
const touched = addPathBlock();
|
|
7171
|
-
console.log(` \u2713 pty routing shim installed (real claude: ${real})`);
|
|
7172
|
-
shadowClaude();
|
|
7173
|
-
if (touched.length) console.log(` added ~/.synkro/bin to PATH in: ${touched.map((t) => t.replace(homedir20(), "~")).join(", ")}`);
|
|
7191
|
+
console.warn("Synkro host model routing has been removed; cleaning up any legacy pty shim.");
|
|
7192
|
+
uninstallPtyShim();
|
|
7174
7193
|
}
|
|
7175
7194
|
function uninstallPtyShim() {
|
|
7176
7195
|
try {
|
|
7177
7196
|
unshadowClaude();
|
|
7178
7197
|
} catch {
|
|
7179
7198
|
}
|
|
7180
|
-
try {
|
|
7181
|
-
const ls = spawnSync5("tmux", ["ls", "-F", "#{session_name}"], { encoding: "utf-8" });
|
|
7182
|
-
for (const s of (ls.stdout || "").split("\n")) {
|
|
7183
|
-
if (s.startsWith(SHIM_SESSION_PREFIX)) spawnSync5("tmux", ["kill-session", "-t", `=${s}`], { encoding: "utf-8" });
|
|
7184
|
-
}
|
|
7185
|
-
} catch {
|
|
7186
|
-
}
|
|
7187
7199
|
const cleaned = [];
|
|
7188
7200
|
for (const rc of rcFiles()) {
|
|
7189
7201
|
if (!existsSync21(rc)) continue;
|
|
@@ -7284,7 +7296,7 @@ function injectModel(model, sessionOverride) {
|
|
|
7284
7296
|
} catch {
|
|
7285
7297
|
}
|
|
7286
7298
|
const poll = `old=$(cat '${pidFile}' 2>/dev/null); if [ -n "$old" ]; then kill "$old" 2>/dev/null; fi; rm -f '${pidFile}'; set -o noclobber; echo $$ > '${pidFile}' 2>/dev/null || exit 1; set +o noclobber; trap 'rm -f "${pidFile}"' EXIT; for i in $(seq 1 40); do if tmux capture-pane -t ${session} -p -S -25 2>/dev/null | grep -qiE 'switch model|yes, switch'; then tmux send-keys -t ${session} -l '1'; sleep 0.1; tmux send-keys -t ${session} Enter; break; fi; sleep 0.1; done`;
|
|
7287
|
-
const child =
|
|
7299
|
+
const child = spawn4("bash", ["-c", poll], { detached: true, stdio: "ignore" });
|
|
7288
7300
|
child.unref();
|
|
7289
7301
|
return true;
|
|
7290
7302
|
}
|
|
@@ -7570,7 +7582,7 @@ __export(install_exports, {
|
|
|
7570
7582
|
import { existsSync as existsSync22, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17, chmodSync as chmodSync5, readFileSync as readFileSync21, readdirSync as readdirSync4, unlinkSync as unlinkSync7, statSync as statSync2 } from "fs";
|
|
7571
7583
|
import { homedir as homedir21 } from "os";
|
|
7572
7584
|
import { join as join19, isAbsolute, resolve as resolve4 } from "path";
|
|
7573
|
-
import { execSync as execSync4, spawn as
|
|
7585
|
+
import { execSync as execSync4, spawn as spawn5 } from "child_process";
|
|
7574
7586
|
import { createInterface as createInterface2 } from "readline";
|
|
7575
7587
|
import { createHash as createHash4 } from "crypto";
|
|
7576
7588
|
function resolvePersistedHookMode() {
|
|
@@ -7698,7 +7710,6 @@ async function promptTranscriptSources(wantCC, wantCursor, wantCodex) {
|
|
|
7698
7710
|
function ensureSynkroDir() {
|
|
7699
7711
|
mkdirSync15(SYNKRO_DIR11, { recursive: true });
|
|
7700
7712
|
mkdirSync15(HOOKS_DIR, { recursive: true });
|
|
7701
|
-
mkdirSync15(BIN_DIR, { recursive: true });
|
|
7702
7713
|
mkdirSync15(OFFSETS_DIR, { recursive: true });
|
|
7703
7714
|
mkdirSync15(join19(SYNKRO_DIR11, "sessions"), { recursive: true });
|
|
7704
7715
|
}
|
|
@@ -7722,7 +7733,7 @@ function writeHookScripts() {
|
|
|
7722
7733
|
const subagentStartScriptPath = join19(HOOKS_DIR, "codex-subagent-start.ts");
|
|
7723
7734
|
const subagentStopScriptPath = join19(HOOKS_DIR, "codex-subagent-stop.ts");
|
|
7724
7735
|
const userPromptSubmitScriptPath = join19(HOOKS_DIR, "cc-user-prompt-submit.ts");
|
|
7725
|
-
const
|
|
7736
|
+
const legacyPromptRouteScriptPath = join19(HOOKS_DIR, "cc-prompt-route.ts");
|
|
7726
7737
|
const commonScriptPath = join19(HOOKS_DIR, "_synkro-common.ts");
|
|
7727
7738
|
const commonBashScriptPath = join19(HOOKS_DIR, "_synkro-common.sh");
|
|
7728
7739
|
const installScanScriptPath = join19(HOOKS_DIR, "cc-install-scan.ts");
|
|
@@ -7753,7 +7764,7 @@ function writeHookScripts() {
|
|
|
7753
7764
|
[subagentStartScriptPath, STUB_SUBAGENT_START_TS],
|
|
7754
7765
|
[subagentStopScriptPath, STUB_SUBAGENT_STOP_TS],
|
|
7755
7766
|
[userPromptSubmitScriptPath, STUB_USER_PROMPT_SUBMIT_TS],
|
|
7756
|
-
[
|
|
7767
|
+
[legacyPromptRouteScriptPath, STUB_PROMPT_ROUTE_TS],
|
|
7757
7768
|
[installScanScriptPath, STUB_INSTALL_SCAN_TS],
|
|
7758
7769
|
[taskActivateIntentScriptPath, STUB_TASK_ACTIVATE_INTENT_TS],
|
|
7759
7770
|
[mcpGateScriptPath, STUB_MCP_GATE_TS],
|
|
@@ -7792,7 +7803,6 @@ function writeHookScripts() {
|
|
|
7792
7803
|
subagentStartScript: subagentStartScriptPath,
|
|
7793
7804
|
subagentStopScript: subagentStopScriptPath,
|
|
7794
7805
|
userPromptSubmitScript: userPromptSubmitScriptPath,
|
|
7795
|
-
promptRouteScript: promptRouteScriptPath,
|
|
7796
7806
|
installScanScript: installScanScriptPath,
|
|
7797
7807
|
cursorBashJudgeScript: cursorBashJudgePath,
|
|
7798
7808
|
cursorEditCaptureScript: cursorEditCapturePath,
|
|
@@ -7830,7 +7840,7 @@ function writeConfigEnv(opts) {
|
|
|
7830
7840
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
7831
7841
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
7832
7842
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
7833
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.
|
|
7843
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.94")}`
|
|
7834
7844
|
];
|
|
7835
7845
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
7836
7846
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -8572,7 +8582,7 @@ async function installCommand(opts = {}) {
|
|
|
8572
8582
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
8573
8583
|
emit("install", {
|
|
8574
8584
|
phase: "started",
|
|
8575
|
-
cli_version_to: "1.7.
|
|
8585
|
+
cli_version_to: "1.7.94",
|
|
8576
8586
|
agents_detected: agents.map((a) => a.kind),
|
|
8577
8587
|
with_github: false,
|
|
8578
8588
|
with_local_cc: false,
|
|
@@ -8613,7 +8623,6 @@ async function installCommand(opts = {}) {
|
|
|
8613
8623
|
sessionStartScriptPath: scripts.sessionStartScript,
|
|
8614
8624
|
transcriptSyncScriptPath: scripts.transcriptSyncScript,
|
|
8615
8625
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
8616
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
8617
8626
|
installScanScriptPath: scripts.installScanScript,
|
|
8618
8627
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
8619
8628
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
@@ -8661,25 +8670,22 @@ async function installCommand(opts = {}) {
|
|
|
8661
8670
|
subagentStartScriptPath: scripts.subagentStartScript,
|
|
8662
8671
|
subagentStopScriptPath: scripts.subagentStopScript,
|
|
8663
8672
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
8664
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
8665
8673
|
installScanScriptPath: scripts.installScanScript,
|
|
8666
8674
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
8667
8675
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
8668
8676
|
skipTranscriptSync: !transcriptCodex
|
|
8669
8677
|
});
|
|
8670
8678
|
console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);
|
|
8671
|
-
|
|
8679
|
+
await reportCodexHookTrust(agent.binaryPath, process.cwd());
|
|
8672
8680
|
}
|
|
8673
8681
|
}
|
|
8674
8682
|
console.log();
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
console.warn(` \u26A0 pty shim install skipped: ${e.message}`);
|
|
8680
|
-
}
|
|
8681
|
-
console.log();
|
|
8683
|
+
try {
|
|
8684
|
+
uninstallPtyShim();
|
|
8685
|
+
} catch (e) {
|
|
8686
|
+
console.warn(` \u26A0 legacy pty shim cleanup skipped: ${e.message}`);
|
|
8682
8687
|
}
|
|
8688
|
+
console.log();
|
|
8683
8689
|
let userId;
|
|
8684
8690
|
let orgId;
|
|
8685
8691
|
let email;
|
|
@@ -9093,7 +9099,7 @@ async function installCommand(opts = {}) {
|
|
|
9093
9099
|
}
|
|
9094
9100
|
}
|
|
9095
9101
|
try {
|
|
9096
|
-
const child =
|
|
9102
|
+
const child = spawn5(process.execPath, [process.argv[1], "reachability-scan", "--quiet"], {
|
|
9097
9103
|
detached: true,
|
|
9098
9104
|
stdio: "ignore",
|
|
9099
9105
|
cwd: process.cwd()
|
|
@@ -9274,7 +9280,6 @@ function reconcileHarness() {
|
|
|
9274
9280
|
sessionStartScriptPath: scripts.sessionStartScript,
|
|
9275
9281
|
transcriptSyncScriptPath: scripts.transcriptSyncScript,
|
|
9276
9282
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
9277
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
9278
9283
|
installScanScriptPath: scripts.installScanScript,
|
|
9279
9284
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
9280
9285
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
@@ -9344,14 +9349,13 @@ function reconcileHarness() {
|
|
|
9344
9349
|
subagentStartScriptPath: scripts.subagentStartScript,
|
|
9345
9350
|
subagentStopScriptPath: scripts.subagentStopScript,
|
|
9346
9351
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
9347
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
9348
9352
|
installScanScriptPath: scripts.installScanScript,
|
|
9349
9353
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
9350
9354
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
9351
9355
|
skipTranscriptSync: !persistedTranscriptConsent("CODEX")
|
|
9352
9356
|
});
|
|
9353
9357
|
console.log(" \u2713 Codex hooks registered");
|
|
9354
|
-
console.log("
|
|
9358
|
+
console.log(" Restart Codex and review /hooks whenever Codex marks a Synkro hook modified.");
|
|
9355
9359
|
try {
|
|
9356
9360
|
installCodexMcpConfig({ gatewayUrl: "", bearerToken: "", local: true });
|
|
9357
9361
|
console.log(" \u2713 Codex MCP registered");
|
|
@@ -10119,7 +10123,7 @@ async function syncCodexTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
10119
10123
|
}
|
|
10120
10124
|
return { sessions: totalSessions, messages: totalMessages };
|
|
10121
10125
|
}
|
|
10122
|
-
var SYNKRO_DIR11, HOOKS_DIR,
|
|
10126
|
+
var SYNKRO_DIR11, HOOKS_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
|
|
10123
10127
|
var init_install = __esm({
|
|
10124
10128
|
"cli/commands/install.ts"() {
|
|
10125
10129
|
"use strict";
|
|
@@ -10127,6 +10131,7 @@ var init_install = __esm({
|
|
|
10127
10131
|
init_ccHookConfig();
|
|
10128
10132
|
init_cursorHookConfig();
|
|
10129
10133
|
init_codexHookConfig();
|
|
10134
|
+
init_codexHookTrust();
|
|
10130
10135
|
init_mcpConfig();
|
|
10131
10136
|
init_synkroCommand();
|
|
10132
10137
|
init_skillParser();
|
|
@@ -10146,7 +10151,6 @@ var init_install = __esm({
|
|
|
10146
10151
|
init_codexTranscriptMessages();
|
|
10147
10152
|
SYNKRO_DIR11 = join19(homedir21(), ".synkro");
|
|
10148
10153
|
HOOKS_DIR = join19(SYNKRO_DIR11, "hooks");
|
|
10149
|
-
BIN_DIR = join19(SYNKRO_DIR11, "bin");
|
|
10150
10154
|
CONFIG_PATH4 = join19(SYNKRO_DIR11, "config.env");
|
|
10151
10155
|
MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
|
|
10152
10156
|
import { readFileSync } from 'node:fs';
|
|
@@ -11202,7 +11206,7 @@ var scanPr_exports = {};
|
|
|
11202
11206
|
__export(scanPr_exports, {
|
|
11203
11207
|
scanPrCommand: () => scanPrCommand
|
|
11204
11208
|
});
|
|
11205
|
-
import { execSync as execSync5, spawn as
|
|
11209
|
+
import { execSync as execSync5, spawn as spawn6 } from "child_process";
|
|
11206
11210
|
import { readFileSync as readFileSync24, existsSync as existsSync26 } from "fs";
|
|
11207
11211
|
import { join as join23 } from "path";
|
|
11208
11212
|
function parseMatchSpec(condition) {
|
|
@@ -11413,7 +11417,7 @@ ${hunks}`;
|
|
|
11413
11417
|
const fullPrompt = promptHeader + userMessage;
|
|
11414
11418
|
return new Promise((resolve6) => {
|
|
11415
11419
|
const t0 = Date.now();
|
|
11416
|
-
const proc =
|
|
11420
|
+
const proc = spawn6(
|
|
11417
11421
|
"claude",
|
|
11418
11422
|
["--print", "--model", "claude-sonnet-4-6", "--output-format", "json", "--no-session-persistence"],
|
|
11419
11423
|
{
|
|
@@ -11512,7 +11516,7 @@ ${JSON.stringify(findings, null, 2)}
|
|
|
11512
11516
|
function spawnOpusConsolidator(findings, claudeToken) {
|
|
11513
11517
|
return new Promise((resolve6) => {
|
|
11514
11518
|
const prompt = buildConsolidationPrompt(findings);
|
|
11515
|
-
const proc =
|
|
11519
|
+
const proc = spawn6(
|
|
11516
11520
|
"claude",
|
|
11517
11521
|
["--print", "--model", "claude-opus-4-7", "--output-format", "json", "--no-session-persistence"],
|
|
11518
11522
|
{
|
|
@@ -11925,296 +11929,20 @@ var init_scanPr = __esm({
|
|
|
11925
11929
|
}
|
|
11926
11930
|
});
|
|
11927
11931
|
|
|
11928
|
-
// cli/local-cc/
|
|
11929
|
-
|
|
11930
|
-
__export(routeDecide_exports, {
|
|
11931
|
-
routeDecide: () => routeDecide
|
|
11932
|
-
});
|
|
11933
|
-
import { readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "fs";
|
|
11932
|
+
// cli/local-cc/pueue.ts
|
|
11933
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync9, spawn as spawn7 } from "child_process";
|
|
11934
11934
|
import { homedir as homedir25 } from "os";
|
|
11935
11935
|
import { join as join24 } from "path";
|
|
11936
|
-
function safeSid(sid) {
|
|
11937
|
-
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
11938
|
-
}
|
|
11939
|
-
function loadMcpJwt() {
|
|
11940
|
-
try {
|
|
11941
|
-
return readFileSync25(join24(SYNKRO_DIR13, ".mcp-jwt"), "utf-8").trim();
|
|
11942
|
-
} catch {
|
|
11943
|
-
return "";
|
|
11944
|
-
}
|
|
11945
|
-
}
|
|
11946
|
-
async function routeDecide(sessionId) {
|
|
11947
|
-
if (!sessionId) return;
|
|
11948
|
-
const sid = safeSid(sessionId);
|
|
11949
|
-
let prompt = "";
|
|
11950
|
-
try {
|
|
11951
|
-
const rec = JSON.parse(readFileSync25(join24(SESSIONS_DIR2, sid + ".json"), "utf-8"));
|
|
11952
|
-
prompt = String(rec.last_prompt || "").trim();
|
|
11953
|
-
} catch {
|
|
11954
|
-
return;
|
|
11955
|
-
}
|
|
11956
|
-
if (!prompt) return;
|
|
11957
|
-
let model = "";
|
|
11958
|
-
try {
|
|
11959
|
-
const resp = await fetch(`http://127.0.0.1:${GRADER_HOST_PORT}/submit`, {
|
|
11960
|
-
method: "POST",
|
|
11961
|
-
headers: { "Content-Type": "application/json", Authorization: "Bearer " + loadMcpJwt() },
|
|
11962
|
-
body: JSON.stringify({ role: "route-classify", payload: prompt, content: prompt, hedge: false }),
|
|
11963
|
-
// First call may spin up the route lane (a haiku worker boot), so allow headroom.
|
|
11964
|
-
signal: AbortSignal.timeout(12e4)
|
|
11965
|
-
});
|
|
11966
|
-
if (!resp.ok) return;
|
|
11967
|
-
const data = await resp.json();
|
|
11968
|
-
const m = (data.result || "").match(/<model>\s*([A-Za-z0-9._:-]+)\s*<\/model>/i);
|
|
11969
|
-
model = m ? m[1].trim() : "";
|
|
11970
|
-
} catch {
|
|
11971
|
-
return;
|
|
11972
|
-
}
|
|
11973
|
-
if (!model || !VALID_MODELS.has(model)) return;
|
|
11974
|
-
const lastFile = join24(PTY_DIR, "route-last-" + sid);
|
|
11975
|
-
let last = "";
|
|
11976
|
-
try {
|
|
11977
|
-
last = readFileSync25(lastFile, "utf-8").trim();
|
|
11978
|
-
} catch {
|
|
11979
|
-
}
|
|
11980
|
-
if (model === last) return;
|
|
11981
|
-
try {
|
|
11982
|
-
writeFileSync19(lastFile, model);
|
|
11983
|
-
} catch {
|
|
11984
|
-
}
|
|
11985
|
-
try {
|
|
11986
|
-
writeFileSync19(join24(PTY_DIR, "route-" + sid), model);
|
|
11987
|
-
} catch {
|
|
11988
|
-
}
|
|
11989
|
-
}
|
|
11990
|
-
var SYNKRO_DIR13, PTY_DIR, SESSIONS_DIR2, VALID_MODELS, GRADER_HOST_PORT;
|
|
11991
|
-
var init_routeDecide = __esm({
|
|
11992
|
-
"cli/local-cc/routeDecide.ts"() {
|
|
11993
|
-
"use strict";
|
|
11994
|
-
SYNKRO_DIR13 = join24(homedir25(), ".synkro");
|
|
11995
|
-
PTY_DIR = join24(SYNKRO_DIR13, "pty");
|
|
11996
|
-
SESSIONS_DIR2 = join24(PTY_DIR, "sessions");
|
|
11997
|
-
VALID_MODELS = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
|
|
11998
|
-
GRADER_HOST_PORT = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
|
|
11999
|
-
}
|
|
12000
|
-
});
|
|
12001
|
-
|
|
12002
|
-
// cli/local-cc/routeOrchestrate.ts
|
|
12003
|
-
var routeOrchestrate_exports = {};
|
|
12004
|
-
__export(routeOrchestrate_exports, {
|
|
12005
|
-
routeAndResubmit: () => routeAndResubmit
|
|
12006
|
-
});
|
|
12007
|
-
import { readFileSync as readFileSync26, writeFileSync as writeFileSync20 } from "fs";
|
|
12008
|
-
import { homedir as homedir26 } from "os";
|
|
12009
|
-
import { join as join25 } from "path";
|
|
12010
|
-
import { spawnSync as spawnSync9 } from "child_process";
|
|
12011
|
-
function safeSid2(sid) {
|
|
12012
|
-
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
12013
|
-
}
|
|
12014
|
-
function safeSession(s) {
|
|
12015
|
-
return /^[A-Za-z0-9._:-]{1,64}$/.test(s);
|
|
12016
|
-
}
|
|
12017
|
-
function loadMcpJwt2() {
|
|
12018
|
-
try {
|
|
12019
|
-
return readFileSync26(join25(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
12020
|
-
} catch {
|
|
12021
|
-
return "";
|
|
12022
|
-
}
|
|
12023
|
-
}
|
|
12024
|
-
async function classifyTask(prompt) {
|
|
12025
|
-
try {
|
|
12026
|
-
const resp = await fetch(`http://127.0.0.1:${GRADER_HOST_PORT2}/submit`, {
|
|
12027
|
-
method: "POST",
|
|
12028
|
-
headers: { "Content-Type": "application/json", Authorization: "Bearer " + loadMcpJwt2() },
|
|
12029
|
-
body: JSON.stringify({ role: "route-classify", payload: prompt, content: prompt, hedge: false }),
|
|
12030
|
-
signal: AbortSignal.timeout(6e4)
|
|
12031
|
-
});
|
|
12032
|
-
if (!resp.ok) return null;
|
|
12033
|
-
const data = await resp.json();
|
|
12034
|
-
const m = (data.result || "").match(/<model>\s*([A-Za-z0-9._:-]+)\s*<\/model>/i);
|
|
12035
|
-
if (!m) return null;
|
|
12036
|
-
const v = m[1].trim();
|
|
12037
|
-
if (v.toLowerCase() === "keep") return "keep";
|
|
12038
|
-
return VALID_MODELS2.has(v) ? v : null;
|
|
12039
|
-
} catch {
|
|
12040
|
-
return null;
|
|
12041
|
-
}
|
|
12042
|
-
}
|
|
12043
|
-
function lastRoutedModel(sid) {
|
|
12044
|
-
try {
|
|
12045
|
-
const v = readFileSync26(join25(PTY_DIR2, "route-last-" + sid), "utf-8").trim();
|
|
12046
|
-
return VALID_MODELS2.has(v) ? v : "";
|
|
12047
|
-
} catch {
|
|
12048
|
-
return "";
|
|
12049
|
-
}
|
|
12050
|
-
}
|
|
12051
|
-
function resolveSession(sid, tmuxSession) {
|
|
12052
|
-
const candidates = [];
|
|
12053
|
-
if (tmuxSession) candidates.push(tmuxSession);
|
|
12054
|
-
try {
|
|
12055
|
-
const rec = JSON.parse(readFileSync26(join25(SESSIONS_DIR3, safeSid2(sid) + ".json"), "utf-8"));
|
|
12056
|
-
if (rec.tmux_session) candidates.push(rec.tmux_session);
|
|
12057
|
-
} catch {
|
|
12058
|
-
}
|
|
12059
|
-
try {
|
|
12060
|
-
candidates.push(readFileSync26(ACTIVE_SESSION_FILE2, "utf-8").trim());
|
|
12061
|
-
} catch {
|
|
12062
|
-
}
|
|
12063
|
-
for (const c of candidates) if (c && safeSession(c)) return c;
|
|
12064
|
-
return "";
|
|
12065
|
-
}
|
|
12066
|
-
async function routeAndResubmit(sessionId, task, tmuxSession, forceModel) {
|
|
12067
|
-
const trimmed = (task || "").trim();
|
|
12068
|
-
if (!sessionId || !trimmed) return;
|
|
12069
|
-
const sid = safeSid2(sessionId);
|
|
12070
|
-
const session = resolveSession(sid, tmuxSession);
|
|
12071
|
-
if (!session) return;
|
|
12072
|
-
if (spawnSync9("tmux", ["has-session", "-t", `=${session}`], { encoding: "utf-8" }).status !== 0) return;
|
|
12073
|
-
const current = lastRoutedModel(sid);
|
|
12074
|
-
const picked = forceModel && VALID_MODELS2.has(forceModel) ? forceModel : await classifyTask(trimmed);
|
|
12075
|
-
const doSwap = !!picked && picked !== "keep" && picked !== current;
|
|
12076
|
-
const sk = (...a) => spawnSync9("tmux", ["send-keys", "-t", session, ...a], { encoding: "utf-8" });
|
|
12077
|
-
const capture = () => spawnSync9("tmux", ["capture-pane", "-t", session, "-p", "-S", "-25"], { encoding: "utf-8" }).stdout || "";
|
|
12078
|
-
await wait(400);
|
|
12079
|
-
if (doSwap) {
|
|
12080
|
-
sk("C-u");
|
|
12081
|
-
await wait(80);
|
|
12082
|
-
sk("-l", `/model ${picked}`);
|
|
12083
|
-
sk("Enter");
|
|
12084
|
-
for (let i = 0; i < 50; i++) {
|
|
12085
|
-
await wait(100);
|
|
12086
|
-
if (/switch model|yes, switch/i.test(capture())) {
|
|
12087
|
-
sk("-l", "1");
|
|
12088
|
-
await wait(120);
|
|
12089
|
-
sk("Enter");
|
|
12090
|
-
break;
|
|
12091
|
-
}
|
|
12092
|
-
}
|
|
12093
|
-
await wait(500);
|
|
12094
|
-
try {
|
|
12095
|
-
writeFileSync20(join25(PTY_DIR2, "route-last-" + sid), picked);
|
|
12096
|
-
} catch {
|
|
12097
|
-
}
|
|
12098
|
-
}
|
|
12099
|
-
try {
|
|
12100
|
-
writeFileSync20(join25(PTY_DIR2, "route-guard-" + sid), "1");
|
|
12101
|
-
} catch {
|
|
12102
|
-
}
|
|
12103
|
-
sk("C-u");
|
|
12104
|
-
await wait(80);
|
|
12105
|
-
spawnSync9("tmux", ["set-buffer", "-b", "synkro-route", trimmed], { encoding: "utf-8" });
|
|
12106
|
-
spawnSync9("tmux", ["paste-buffer", "-t", session, "-b", "synkro-route", "-d"], { encoding: "utf-8" });
|
|
12107
|
-
await wait(120);
|
|
12108
|
-
sk("Enter");
|
|
12109
|
-
}
|
|
12110
|
-
var SYNKRO_DIR14, PTY_DIR2, SESSIONS_DIR3, ACTIVE_SESSION_FILE2, VALID_MODELS2, GRADER_HOST_PORT2, wait;
|
|
12111
|
-
var init_routeOrchestrate = __esm({
|
|
12112
|
-
"cli/local-cc/routeOrchestrate.ts"() {
|
|
12113
|
-
"use strict";
|
|
12114
|
-
SYNKRO_DIR14 = join25(homedir26(), ".synkro");
|
|
12115
|
-
PTY_DIR2 = join25(SYNKRO_DIR14, "pty");
|
|
12116
|
-
SESSIONS_DIR3 = join25(PTY_DIR2, "sessions");
|
|
12117
|
-
ACTIVE_SESSION_FILE2 = join25(PTY_DIR2, "active");
|
|
12118
|
-
VALID_MODELS2 = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
|
|
12119
|
-
GRADER_HOST_PORT2 = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
|
|
12120
|
-
wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12121
|
-
}
|
|
12122
|
-
});
|
|
12123
|
-
|
|
12124
|
-
// cli/local-cc/routingToggle.ts
|
|
12125
|
-
var routingToggle_exports = {};
|
|
12126
|
-
__export(routingToggle_exports, {
|
|
12127
|
-
routingCommand: () => routingCommand
|
|
12128
|
-
});
|
|
12129
|
-
import { writeFileSync as writeFileSync21, unlinkSync as unlinkSync9, existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
12130
|
-
import { homedir as homedir27 } from "os";
|
|
12131
|
-
import { join as join26 } from "path";
|
|
12132
|
-
function safeSid3(sid) {
|
|
12133
|
-
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
12134
|
-
}
|
|
12135
|
-
function markerFor(session) {
|
|
12136
|
-
return session ? join26(PTY_DIR3, "routing-on-" + safeSid3(session)) : join26(PTY_DIR3, "routing-on");
|
|
12137
|
-
}
|
|
12138
|
-
function routingCommand(args2) {
|
|
12139
|
-
const sub = (args2[0] || "status").trim();
|
|
12140
|
-
let session;
|
|
12141
|
-
const si = args2.indexOf("--session");
|
|
12142
|
-
if (si >= 0 && args2[si + 1]) session = args2[si + 1].trim();
|
|
12143
|
-
if (sub === "on") {
|
|
12144
|
-
try {
|
|
12145
|
-
writeFileSync21(markerFor(session), "1");
|
|
12146
|
-
} catch (e) {
|
|
12147
|
-
console.error("routing on failed:", String(e));
|
|
12148
|
-
return;
|
|
12149
|
-
}
|
|
12150
|
-
console.log(session ? `Routing ON for session ${session}.` : "Routing ON (all sessions).");
|
|
12151
|
-
console.log(" New task prompts will be classified and run on the best model automatically.");
|
|
12152
|
-
return;
|
|
12153
|
-
}
|
|
12154
|
-
if (sub === "off") {
|
|
12155
|
-
let removed = 0;
|
|
12156
|
-
if (session) {
|
|
12157
|
-
if (existsSync27(markerFor(session))) {
|
|
12158
|
-
try {
|
|
12159
|
-
unlinkSync9(markerFor(session));
|
|
12160
|
-
removed++;
|
|
12161
|
-
} catch {
|
|
12162
|
-
}
|
|
12163
|
-
}
|
|
12164
|
-
} else {
|
|
12165
|
-
for (const f of safeList()) {
|
|
12166
|
-
if (f === "routing-on" || f.startsWith("routing-on-")) {
|
|
12167
|
-
try {
|
|
12168
|
-
unlinkSync9(join26(PTY_DIR3, f));
|
|
12169
|
-
removed++;
|
|
12170
|
-
} catch {
|
|
12171
|
-
}
|
|
12172
|
-
}
|
|
12173
|
-
}
|
|
12174
|
-
}
|
|
12175
|
-
console.log(`Routing OFF${session ? ` for session ${session}` : ""} (${removed} marker${removed === 1 ? "" : "s"} cleared).`);
|
|
12176
|
-
return;
|
|
12177
|
-
}
|
|
12178
|
-
const markers = safeList().filter((f) => f === "routing-on" || f.startsWith("routing-on-"));
|
|
12179
|
-
if (markers.length === 0) {
|
|
12180
|
-
console.log("Routing is OFF (no markers).");
|
|
12181
|
-
return;
|
|
12182
|
-
}
|
|
12183
|
-
console.log("Routing is ON:");
|
|
12184
|
-
for (const m of markers) {
|
|
12185
|
-
if (m === "routing-on") console.log(" \u2022 global (all sessions)");
|
|
12186
|
-
else console.log(" \u2022 session " + m.slice("routing-on-".length));
|
|
12187
|
-
}
|
|
12188
|
-
}
|
|
12189
|
-
function safeList() {
|
|
12190
|
-
try {
|
|
12191
|
-
return readdirSync6(PTY_DIR3);
|
|
12192
|
-
} catch {
|
|
12193
|
-
return [];
|
|
12194
|
-
}
|
|
12195
|
-
}
|
|
12196
|
-
var PTY_DIR3;
|
|
12197
|
-
var init_routingToggle = __esm({
|
|
12198
|
-
"cli/local-cc/routingToggle.ts"() {
|
|
12199
|
-
"use strict";
|
|
12200
|
-
PTY_DIR3 = join26(homedir27(), ".synkro", "pty");
|
|
12201
|
-
}
|
|
12202
|
-
});
|
|
12203
|
-
|
|
12204
|
-
// cli/local-cc/pueue.ts
|
|
12205
|
-
import { execFileSync as execFileSync4, spawnSync as spawnSync10, spawn as spawn6 } from "child_process";
|
|
12206
|
-
import { homedir as homedir28 } from "os";
|
|
12207
|
-
import { join as join27 } from "path";
|
|
12208
11936
|
import { connect as connect2 } from "net";
|
|
12209
11937
|
function pueueAvailable() {
|
|
12210
|
-
const r =
|
|
11938
|
+
const r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
|
|
12211
11939
|
if (r.status !== 0) {
|
|
12212
11940
|
throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
|
|
12213
11941
|
}
|
|
12214
11942
|
}
|
|
12215
11943
|
function statusJson() {
|
|
12216
11944
|
pueueAvailable();
|
|
12217
|
-
const r =
|
|
11945
|
+
const r = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8" });
|
|
12218
11946
|
if (r.status !== 0) {
|
|
12219
11947
|
throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
|
|
12220
11948
|
}
|
|
@@ -12259,18 +11987,18 @@ function startTask(opts = {}) {
|
|
|
12259
11987
|
let existing = findTask(ch);
|
|
12260
11988
|
while (existing) {
|
|
12261
11989
|
if (existing.status === "Running" || existing.status === "Queued") {
|
|
12262
|
-
|
|
12263
|
-
|
|
11990
|
+
spawnSync9("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
|
|
11991
|
+
spawnSync9("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
|
|
12264
11992
|
for (let i = 0; i < 10; i++) {
|
|
12265
11993
|
const check = findTask(ch);
|
|
12266
11994
|
if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
|
|
12267
|
-
|
|
11995
|
+
spawnSync9("sleep", ["0.5"], { encoding: "utf-8" });
|
|
12268
11996
|
}
|
|
12269
11997
|
}
|
|
12270
|
-
|
|
11998
|
+
spawnSync9("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
|
|
12271
11999
|
existing = findTask(ch);
|
|
12272
12000
|
}
|
|
12273
|
-
const runScript =
|
|
12001
|
+
const runScript = join24(cwd, "run-claude.sh");
|
|
12274
12002
|
const args2 = [
|
|
12275
12003
|
"add",
|
|
12276
12004
|
"--label",
|
|
@@ -12281,7 +12009,7 @@ function startTask(opts = {}) {
|
|
|
12281
12009
|
"bash",
|
|
12282
12010
|
runScript
|
|
12283
12011
|
];
|
|
12284
|
-
const r =
|
|
12012
|
+
const r = spawnSync9("pueue", args2, { encoding: "utf-8" });
|
|
12285
12013
|
if (r.status !== 0) {
|
|
12286
12014
|
throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
|
|
12287
12015
|
}
|
|
@@ -12292,25 +12020,25 @@ function startTask(opts = {}) {
|
|
|
12292
12020
|
return created;
|
|
12293
12021
|
}
|
|
12294
12022
|
function stopTask(channel = CHANNEL_PRIMARY) {
|
|
12295
|
-
|
|
12023
|
+
spawnSync9("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
|
|
12296
12024
|
let t = findTask(channel);
|
|
12297
12025
|
while (t) {
|
|
12298
12026
|
if (t.status === "Running" || t.status === "Queued") {
|
|
12299
|
-
|
|
12027
|
+
spawnSync9("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
|
|
12300
12028
|
for (let i = 0; i < 10; i++) {
|
|
12301
12029
|
const check = findTask(channel);
|
|
12302
12030
|
if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
|
|
12303
|
-
|
|
12031
|
+
spawnSync9("sleep", ["0.5"], { encoding: "utf-8" });
|
|
12304
12032
|
}
|
|
12305
12033
|
}
|
|
12306
|
-
|
|
12034
|
+
spawnSync9("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
|
|
12307
12035
|
t = findTask(channel);
|
|
12308
12036
|
}
|
|
12309
12037
|
}
|
|
12310
12038
|
function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
|
|
12311
12039
|
const t = findTask(channel);
|
|
12312
12040
|
if (!t) return `(no ${channel.taskLabel} task)`;
|
|
12313
|
-
const r =
|
|
12041
|
+
const r = spawnSync9("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
|
|
12314
12042
|
return r.stdout || r.stderr || "(no output)";
|
|
12315
12043
|
}
|
|
12316
12044
|
function ensureRunning(opts = {}) {
|
|
@@ -12335,8 +12063,8 @@ function probePort(host, port, timeoutMs = 500) {
|
|
|
12335
12063
|
});
|
|
12336
12064
|
}
|
|
12337
12065
|
function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
|
|
12338
|
-
|
|
12339
|
-
|
|
12066
|
+
spawnSync9("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
|
|
12067
|
+
spawnSync9("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
|
|
12340
12068
|
}
|
|
12341
12069
|
async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
|
|
12342
12070
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -12348,46 +12076,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
|
|
|
12348
12076
|
return probePort(host, port);
|
|
12349
12077
|
}
|
|
12350
12078
|
function brewInstall(pkg) {
|
|
12351
|
-
const brew =
|
|
12079
|
+
const brew = spawnSync9("brew", ["--version"], { encoding: "utf-8" });
|
|
12352
12080
|
if (brew.status !== 0) return false;
|
|
12353
12081
|
console.log(` Installing ${pkg} via brew...`);
|
|
12354
|
-
const r =
|
|
12082
|
+
const r = spawnSync9("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
12355
12083
|
return r.status === 0;
|
|
12356
12084
|
}
|
|
12357
12085
|
function assertPueueInstalled() {
|
|
12358
|
-
let r =
|
|
12086
|
+
let r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
|
|
12359
12087
|
if (r.status !== 0) {
|
|
12360
12088
|
if (process.platform === "darwin" && brewInstall("pueue")) {
|
|
12361
|
-
r =
|
|
12089
|
+
r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
|
|
12362
12090
|
if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
|
|
12363
12091
|
} else {
|
|
12364
12092
|
throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
|
|
12365
12093
|
}
|
|
12366
12094
|
}
|
|
12367
|
-
const status =
|
|
12095
|
+
const status = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
|
|
12368
12096
|
if (status.status !== 0) {
|
|
12369
12097
|
console.log(" Starting pueued daemon...");
|
|
12370
|
-
const child =
|
|
12098
|
+
const child = spawn7("pueued", ["-d"], { stdio: "ignore", detached: true });
|
|
12371
12099
|
child.unref();
|
|
12372
|
-
|
|
12373
|
-
const retry =
|
|
12100
|
+
spawnSync9("sleep", ["1"]);
|
|
12101
|
+
const retry = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
|
|
12374
12102
|
if (retry.status !== 0) {
|
|
12375
12103
|
throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
|
|
12376
12104
|
}
|
|
12377
12105
|
}
|
|
12378
|
-
|
|
12106
|
+
spawnSync9("pueue", ["parallel", "2"], { encoding: "utf-8" });
|
|
12379
12107
|
}
|
|
12380
12108
|
function assertClaudeInstalled() {
|
|
12381
|
-
const r =
|
|
12109
|
+
const r = spawnSync9("claude", ["--version"], { encoding: "utf-8" });
|
|
12382
12110
|
if (r.status !== 0) {
|
|
12383
12111
|
throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
|
|
12384
12112
|
}
|
|
12385
12113
|
}
|
|
12386
12114
|
function assertTmuxInstalled() {
|
|
12387
|
-
let r =
|
|
12115
|
+
let r = spawnSync9("tmux", ["-V"], { encoding: "utf-8" });
|
|
12388
12116
|
if (r.status !== 0) {
|
|
12389
12117
|
if (process.platform === "darwin" && brewInstall("tmux")) {
|
|
12390
|
-
r =
|
|
12118
|
+
r = spawnSync9("tmux", ["-V"], { encoding: "utf-8" });
|
|
12391
12119
|
if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
|
|
12392
12120
|
} else {
|
|
12393
12121
|
throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
|
|
@@ -12400,12 +12128,12 @@ var init_pueue = __esm({
|
|
|
12400
12128
|
"use strict";
|
|
12401
12129
|
TASK_LABEL = "synkro-local-cc";
|
|
12402
12130
|
TMUX_SESSION = "synkro-local-cc";
|
|
12403
|
-
SESSION_DIR2 =
|
|
12131
|
+
SESSION_DIR2 = join24(homedir25(), ".synkro", "cc_sessions");
|
|
12404
12132
|
TASK_LABEL_2 = "synkro-local-cc-2";
|
|
12405
12133
|
TMUX_SESSION_2 = "synkro-local-cc-2";
|
|
12406
|
-
SESSION_DIR_22 =
|
|
12407
|
-
SESSION_DIR_32 =
|
|
12408
|
-
SESSION_DIR_42 =
|
|
12134
|
+
SESSION_DIR_22 = join24(homedir25(), ".synkro", "cc_sessions_2");
|
|
12135
|
+
SESSION_DIR_32 = join24(homedir25(), ".synkro", "cc_sessions_3");
|
|
12136
|
+
SESSION_DIR_42 = join24(homedir25(), ".synkro", "cc_sessions_4");
|
|
12409
12137
|
PueueError = class extends Error {
|
|
12410
12138
|
constructor(message, cause) {
|
|
12411
12139
|
super(message);
|
|
@@ -12420,13 +12148,13 @@ var init_pueue = __esm({
|
|
|
12420
12148
|
});
|
|
12421
12149
|
|
|
12422
12150
|
// cli/local-cc/settings.ts
|
|
12423
|
-
import { existsSync as
|
|
12424
|
-
import { homedir as
|
|
12425
|
-
import { join as
|
|
12151
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
|
|
12152
|
+
import { homedir as homedir26 } from "os";
|
|
12153
|
+
import { join as join25 } from "path";
|
|
12426
12154
|
function isLocalCCEnabled() {
|
|
12427
|
-
if (!
|
|
12155
|
+
if (!existsSync27(CONFIG_PATH5)) return false;
|
|
12428
12156
|
try {
|
|
12429
|
-
const content =
|
|
12157
|
+
const content = readFileSync25(CONFIG_PATH5, "utf-8");
|
|
12430
12158
|
const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
|
|
12431
12159
|
return match?.[1] === "yes";
|
|
12432
12160
|
} catch {
|
|
@@ -12437,7 +12165,7 @@ var CONFIG_PATH5;
|
|
|
12437
12165
|
var init_settings = __esm({
|
|
12438
12166
|
"cli/local-cc/settings.ts"() {
|
|
12439
12167
|
"use strict";
|
|
12440
|
-
CONFIG_PATH5 =
|
|
12168
|
+
CONFIG_PATH5 = join25(homedir26(), ".synkro", "config.env");
|
|
12441
12169
|
}
|
|
12442
12170
|
});
|
|
12443
12171
|
|
|
@@ -12446,11 +12174,11 @@ var localCc_exports = {};
|
|
|
12446
12174
|
__export(localCc_exports, {
|
|
12447
12175
|
localCcCommand: () => localCcCommand
|
|
12448
12176
|
});
|
|
12449
|
-
import { spawnSync as
|
|
12450
|
-
import { homedir as
|
|
12451
|
-
import { join as
|
|
12177
|
+
import { spawnSync as spawnSync10 } from "child_process";
|
|
12178
|
+
import { homedir as homedir27 } from "os";
|
|
12179
|
+
import { join as join26 } from "path";
|
|
12452
12180
|
import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
|
|
12453
|
-
import { existsSync as
|
|
12181
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync19 } from "fs";
|
|
12454
12182
|
function deploymentMode() {
|
|
12455
12183
|
const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
|
|
12456
12184
|
if (env === "docker") return "docker";
|
|
@@ -12556,15 +12284,15 @@ TROUBLESHOOTING
|
|
|
12556
12284
|
`);
|
|
12557
12285
|
}
|
|
12558
12286
|
function readGatewayUrl() {
|
|
12559
|
-
if (
|
|
12560
|
-
const m =
|
|
12287
|
+
if (existsSync28(CONFIG_PATH6)) {
|
|
12288
|
+
const m = readFileSync26(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
|
|
12561
12289
|
if (m) return m[1];
|
|
12562
12290
|
}
|
|
12563
12291
|
return "https://api.synkro.sh";
|
|
12564
12292
|
}
|
|
12565
12293
|
function updateLocalInferenceFlag(enabled) {
|
|
12566
|
-
if (!
|
|
12567
|
-
let content =
|
|
12294
|
+
if (!existsSync28(CONFIG_PATH6)) return;
|
|
12295
|
+
let content = readFileSync26(CONFIG_PATH6, "utf-8");
|
|
12568
12296
|
const flag = enabled ? "yes" : "no";
|
|
12569
12297
|
if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
|
|
12570
12298
|
content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
|
|
@@ -12573,7 +12301,7 @@ function updateLocalInferenceFlag(enabled) {
|
|
|
12573
12301
|
SYNKRO_LOCAL_INFERENCE='${flag}'
|
|
12574
12302
|
`;
|
|
12575
12303
|
}
|
|
12576
|
-
|
|
12304
|
+
writeFileSync19(CONFIG_PATH6, content, "utf-8");
|
|
12577
12305
|
}
|
|
12578
12306
|
async function setServerGradingProvider(provider) {
|
|
12579
12307
|
await ensureValidToken();
|
|
@@ -12627,7 +12355,7 @@ async function cmdStatus() {
|
|
|
12627
12355
|
}
|
|
12628
12356
|
const ch1Up = await isChannelAvailable();
|
|
12629
12357
|
console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
|
|
12630
|
-
const tmux1 =
|
|
12358
|
+
const tmux1 = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
|
|
12631
12359
|
console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
|
|
12632
12360
|
const t2 = findTask(CHANNEL_SECONDARY);
|
|
12633
12361
|
if (!t2) {
|
|
@@ -12637,7 +12365,7 @@ async function cmdStatus() {
|
|
|
12637
12365
|
}
|
|
12638
12366
|
const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
|
|
12639
12367
|
console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
|
|
12640
|
-
const tmux2 =
|
|
12368
|
+
const tmux2 = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
|
|
12641
12369
|
console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
|
|
12642
12370
|
}
|
|
12643
12371
|
async function cmdEnable() {
|
|
@@ -12845,7 +12573,7 @@ function cmdLogs(rest) {
|
|
|
12845
12573
|
}
|
|
12846
12574
|
return "200";
|
|
12847
12575
|
})();
|
|
12848
|
-
|
|
12576
|
+
spawnSync10("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
|
|
12849
12577
|
return;
|
|
12850
12578
|
}
|
|
12851
12579
|
for (const arg of rest) {
|
|
@@ -12893,7 +12621,7 @@ function cmdLogs(rest) {
|
|
|
12893
12621
|
function cmdAttach(rest) {
|
|
12894
12622
|
assertTmuxInstalled();
|
|
12895
12623
|
const readonly = rest.some((a) => a === "--readonly" || a === "-r");
|
|
12896
|
-
const has =
|
|
12624
|
+
const has = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
|
|
12897
12625
|
if (has.status !== 0) {
|
|
12898
12626
|
console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
|
|
12899
12627
|
process.exit(1);
|
|
@@ -12906,7 +12634,7 @@ function cmdAttach(rest) {
|
|
|
12906
12634
|
console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
|
|
12907
12635
|
console.log();
|
|
12908
12636
|
const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
|
|
12909
|
-
const r =
|
|
12637
|
+
const r = spawnSync10("tmux", args2, { stdio: "inherit" });
|
|
12910
12638
|
process.exit(r.status ?? 0);
|
|
12911
12639
|
}
|
|
12912
12640
|
async function cmdTest() {
|
|
@@ -13007,8 +12735,8 @@ var init_localCc = __esm({
|
|
|
13007
12735
|
init_install();
|
|
13008
12736
|
init_client2();
|
|
13009
12737
|
init_stub();
|
|
13010
|
-
SYNKRO_CONFIG_PATH =
|
|
13011
|
-
CONFIG_PATH6 =
|
|
12738
|
+
SYNKRO_CONFIG_PATH = join26(homedir27(), ".synkro", "config.env");
|
|
12739
|
+
CONFIG_PATH6 = join26(homedir27(), ".synkro", "config.env");
|
|
13012
12740
|
}
|
|
13013
12741
|
});
|
|
13014
12742
|
|
|
@@ -13017,14 +12745,14 @@ var import_exports = {};
|
|
|
13017
12745
|
__export(import_exports, {
|
|
13018
12746
|
importCommand: () => importCommand
|
|
13019
12747
|
});
|
|
13020
|
-
import { existsSync as
|
|
13021
|
-
import { homedir as
|
|
13022
|
-
import { join as
|
|
12748
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6 } from "fs";
|
|
12749
|
+
import { homedir as homedir28 } from "os";
|
|
12750
|
+
import { join as join27 } from "path";
|
|
13023
12751
|
import { execSync as execSync6 } from "child_process";
|
|
13024
12752
|
import { createInterface as createInterface4 } from "readline";
|
|
13025
12753
|
function readMcpJwt() {
|
|
13026
12754
|
try {
|
|
13027
|
-
return
|
|
12755
|
+
return readFileSync27(join27(homedir28(), ".synkro", ".mcp-jwt"), "utf-8").trim();
|
|
13028
12756
|
} catch {
|
|
13029
12757
|
return "";
|
|
13030
12758
|
}
|
|
@@ -13032,7 +12760,7 @@ function readMcpJwt() {
|
|
|
13032
12760
|
function readConfigEnv2() {
|
|
13033
12761
|
const out = {};
|
|
13034
12762
|
try {
|
|
13035
|
-
for (const line of
|
|
12763
|
+
for (const line of readFileSync27(CONFIG_PATH7, "utf-8").split("\n")) {
|
|
13036
12764
|
const t = line.trim();
|
|
13037
12765
|
if (!t || t.startsWith("#")) continue;
|
|
13038
12766
|
const eq = t.indexOf("=");
|
|
@@ -13044,8 +12772,8 @@ function readConfigEnv2() {
|
|
|
13044
12772
|
}
|
|
13045
12773
|
function projectsFolder() {
|
|
13046
12774
|
const sanitized = process.cwd().replace(/\//g, "-");
|
|
13047
|
-
const dir =
|
|
13048
|
-
return
|
|
12775
|
+
const dir = join27(homedir28(), ".claude", "projects", sanitized);
|
|
12776
|
+
return existsSync29(dir) ? dir : null;
|
|
13049
12777
|
}
|
|
13050
12778
|
function repoName() {
|
|
13051
12779
|
try {
|
|
@@ -13084,7 +12812,7 @@ function extractToolResultText(content, e) {
|
|
|
13084
12812
|
return t;
|
|
13085
12813
|
}
|
|
13086
12814
|
function parseSession(filePath, sessionId) {
|
|
13087
|
-
const lines =
|
|
12815
|
+
const lines = readFileSync27(filePath, "utf-8").split("\n").filter(Boolean);
|
|
13088
12816
|
const messages = [];
|
|
13089
12817
|
const actions = [];
|
|
13090
12818
|
let step = 0;
|
|
@@ -13151,7 +12879,7 @@ async function importCommand() {
|
|
|
13151
12879
|
console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
|
|
13152
12880
|
return;
|
|
13153
12881
|
}
|
|
13154
|
-
const files =
|
|
12882
|
+
const files = readdirSync6(dir).filter((f) => f.endsWith(".jsonl"));
|
|
13155
12883
|
if (!files.length) {
|
|
13156
12884
|
console.log("No sessions to import.");
|
|
13157
12885
|
return;
|
|
@@ -13164,7 +12892,7 @@ async function importCommand() {
|
|
|
13164
12892
|
return;
|
|
13165
12893
|
}
|
|
13166
12894
|
}
|
|
13167
|
-
const sessions = files.map((f) => parseSession(
|
|
12895
|
+
const sessions = files.map((f) => parseSession(join27(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
|
|
13168
12896
|
const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
|
|
13169
12897
|
let ok = 0, fail = 0;
|
|
13170
12898
|
if (isCloud) {
|
|
@@ -13239,7 +12967,7 @@ var init_import = __esm({
|
|
|
13239
12967
|
"cli/commands/import.ts"() {
|
|
13240
12968
|
"use strict";
|
|
13241
12969
|
init_stub();
|
|
13242
|
-
CONFIG_PATH7 =
|
|
12970
|
+
CONFIG_PATH7 = join27(homedir28(), ".synkro", "config.env");
|
|
13243
12971
|
}
|
|
13244
12972
|
});
|
|
13245
12973
|
|
|
@@ -13281,10 +13009,10 @@ var init_packVerify = __esm({
|
|
|
13281
13009
|
});
|
|
13282
13010
|
|
|
13283
13011
|
// cli/installer/lockfile.ts
|
|
13284
|
-
import { existsSync as
|
|
13285
|
-
import { join as
|
|
13012
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync20 } from "fs";
|
|
13013
|
+
import { join as join28 } from "path";
|
|
13286
13014
|
function lockPath(repoRoot2) {
|
|
13287
|
-
return
|
|
13015
|
+
return join28(repoRoot2, LOCK_FILE);
|
|
13288
13016
|
}
|
|
13289
13017
|
function writeLockfile(repoRoot2, entries) {
|
|
13290
13018
|
const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
|
|
@@ -13302,7 +13030,7 @@ function writeLockfile(repoRoot2, entries) {
|
|
|
13302
13030
|
""
|
|
13303
13031
|
])
|
|
13304
13032
|
].join("\n");
|
|
13305
|
-
|
|
13033
|
+
writeFileSync20(lockPath(repoRoot2), body, "utf-8");
|
|
13306
13034
|
}
|
|
13307
13035
|
var LOCK_FILE;
|
|
13308
13036
|
var init_lockfile = __esm({
|
|
@@ -13317,9 +13045,9 @@ var sync_exports = {};
|
|
|
13317
13045
|
__export(sync_exports, {
|
|
13318
13046
|
syncCommand: () => syncCommand
|
|
13319
13047
|
});
|
|
13320
|
-
import { existsSync as
|
|
13321
|
-
import { homedir as
|
|
13322
|
-
import { join as
|
|
13048
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync18, readdirSync as readdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync21 } from "fs";
|
|
13049
|
+
import { homedir as homedir29 } from "os";
|
|
13050
|
+
import { join as join29 } from "path";
|
|
13323
13051
|
function cacheKey(ref, version) {
|
|
13324
13052
|
return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
|
|
13325
13053
|
}
|
|
@@ -13350,7 +13078,7 @@ async function syncCommand(_args = []) {
|
|
|
13350
13078
|
}
|
|
13351
13079
|
const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
13352
13080
|
const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
|
|
13353
|
-
const cacheDir =
|
|
13081
|
+
const cacheDir = join29(homedir29(), ".synkro", "cache", "packs");
|
|
13354
13082
|
if (!cloud) mkdirSync18(cacheDir, { recursive: true });
|
|
13355
13083
|
console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
|
|
13356
13084
|
const lock = [];
|
|
@@ -13379,7 +13107,7 @@ async function syncCommand(_args = []) {
|
|
|
13379
13107
|
if (!cloud) {
|
|
13380
13108
|
const fname = cacheKey(ref, data.version);
|
|
13381
13109
|
keptCacheFiles.add(fname);
|
|
13382
|
-
|
|
13110
|
+
writeFileSync21(join29(cacheDir, fname), JSON.stringify({
|
|
13383
13111
|
ref,
|
|
13384
13112
|
version: data.version,
|
|
13385
13113
|
digest: data.digest,
|
|
@@ -13391,11 +13119,11 @@ async function syncCommand(_args = []) {
|
|
|
13391
13119
|
const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
|
|
13392
13120
|
console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
|
|
13393
13121
|
}
|
|
13394
|
-
if (!cloud &&
|
|
13395
|
-
for (const f of
|
|
13122
|
+
if (!cloud && existsSync31(cacheDir)) {
|
|
13123
|
+
for (const f of readdirSync7(cacheDir)) {
|
|
13396
13124
|
if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
|
|
13397
13125
|
try {
|
|
13398
|
-
rmSync4(
|
|
13126
|
+
rmSync4(join29(cacheDir, f));
|
|
13399
13127
|
} catch {
|
|
13400
13128
|
}
|
|
13401
13129
|
}
|
|
@@ -13424,13 +13152,13 @@ var whoami_exports = {};
|
|
|
13424
13152
|
__export(whoami_exports, {
|
|
13425
13153
|
whoamiCommand: () => whoamiCommand
|
|
13426
13154
|
});
|
|
13427
|
-
import { readFileSync as
|
|
13428
|
-
import { join as
|
|
13429
|
-
import { homedir as
|
|
13155
|
+
import { readFileSync as readFileSync29, existsSync as existsSync32 } from "fs";
|
|
13156
|
+
import { join as join30 } from "path";
|
|
13157
|
+
import { homedir as homedir30 } from "os";
|
|
13430
13158
|
function readConfigEnv3() {
|
|
13431
|
-
if (!
|
|
13159
|
+
if (!existsSync32(CONFIG_PATH8)) return {};
|
|
13432
13160
|
const out = {};
|
|
13433
|
-
for (const line of
|
|
13161
|
+
for (const line of readFileSync29(CONFIG_PATH8, "utf-8").split("\n")) {
|
|
13434
13162
|
const t = line.trim();
|
|
13435
13163
|
if (!t || t.startsWith("#")) continue;
|
|
13436
13164
|
const eq = t.indexOf("=");
|
|
@@ -13440,8 +13168,8 @@ function readConfigEnv3() {
|
|
|
13440
13168
|
}
|
|
13441
13169
|
function jwtStatus() {
|
|
13442
13170
|
try {
|
|
13443
|
-
if (!
|
|
13444
|
-
const jwt2 =
|
|
13171
|
+
if (!existsSync32(JWT_PATH2)) return { status: "none" };
|
|
13172
|
+
const jwt2 = readFileSync29(JWT_PATH2, "utf-8").trim();
|
|
13445
13173
|
if (!jwt2) return { status: "none" };
|
|
13446
13174
|
const payload = jwt2.split(".")[1];
|
|
13447
13175
|
if (!payload) return { status: "valid" };
|
|
@@ -13499,13 +13227,13 @@ async function whoamiCommand(args2 = []) {
|
|
|
13499
13227
|
console.log("synkro identity");
|
|
13500
13228
|
for (const [k, v] of rows) console.log(` ${k.padEnd(width)} ${v}`);
|
|
13501
13229
|
}
|
|
13502
|
-
var
|
|
13230
|
+
var SYNKRO_DIR13, CONFIG_PATH8, JWT_PATH2, GRADING_LABEL;
|
|
13503
13231
|
var init_whoami = __esm({
|
|
13504
13232
|
"cli/commands/whoami.ts"() {
|
|
13505
13233
|
"use strict";
|
|
13506
|
-
|
|
13507
|
-
CONFIG_PATH8 =
|
|
13508
|
-
JWT_PATH2 =
|
|
13234
|
+
SYNKRO_DIR13 = join30(homedir30(), ".synkro");
|
|
13235
|
+
CONFIG_PATH8 = join30(SYNKRO_DIR13, "config.env");
|
|
13236
|
+
JWT_PATH2 = join30(SYNKRO_DIR13, ".mcp-jwt");
|
|
13509
13237
|
GRADING_LABEL = {
|
|
13510
13238
|
local: "on-device worker pool",
|
|
13511
13239
|
cloud: "Synkro Cloud worker pool",
|
|
@@ -13550,12 +13278,12 @@ __export(linear_exports, {
|
|
|
13550
13278
|
formatLinks: () => formatLinks,
|
|
13551
13279
|
linearCommand: () => linearCommand
|
|
13552
13280
|
});
|
|
13553
|
-
import { readFileSync as
|
|
13554
|
-
import { homedir as
|
|
13555
|
-
import { join as
|
|
13281
|
+
import { readFileSync as readFileSync30 } from "fs";
|
|
13282
|
+
import { homedir as homedir31 } from "os";
|
|
13283
|
+
import { join as join31 } from "path";
|
|
13556
13284
|
function mcpJwt() {
|
|
13557
13285
|
try {
|
|
13558
|
-
return
|
|
13286
|
+
return readFileSync30(join31(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
13559
13287
|
} catch {
|
|
13560
13288
|
return "";
|
|
13561
13289
|
}
|
|
@@ -13590,11 +13318,11 @@ async function linearCommand(_args = []) {
|
|
|
13590
13318
|
}
|
|
13591
13319
|
console.log(formatLinks(links));
|
|
13592
13320
|
}
|
|
13593
|
-
var
|
|
13321
|
+
var SYNKRO_DIR14, PORT2, BASE;
|
|
13594
13322
|
var init_linear = __esm({
|
|
13595
13323
|
"cli/commands/linear.ts"() {
|
|
13596
13324
|
"use strict";
|
|
13597
|
-
|
|
13325
|
+
SYNKRO_DIR14 = join31(homedir31(), ".synkro");
|
|
13598
13326
|
PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
|
|
13599
13327
|
BASE = `http://127.0.0.1:${PORT2}`;
|
|
13600
13328
|
}
|
|
@@ -13602,7 +13330,7 @@ var init_linear = __esm({
|
|
|
13602
13330
|
|
|
13603
13331
|
// cli/scanning/cveReachability.ts
|
|
13604
13332
|
import { parse } from "@babel/parser";
|
|
13605
|
-
import { readFileSync as
|
|
13333
|
+
import { readFileSync as readFileSync31 } from "fs";
|
|
13606
13334
|
function walk(node, visit) {
|
|
13607
13335
|
if (!node || typeof node.type !== "string") return;
|
|
13608
13336
|
visit(node);
|
|
@@ -13743,10 +13471,10 @@ var init_cveReachability = __esm({
|
|
|
13743
13471
|
});
|
|
13744
13472
|
|
|
13745
13473
|
// cli/reachability/reachabilityScan.ts
|
|
13746
|
-
import { spawnSync as
|
|
13747
|
-
import { readFileSync as
|
|
13748
|
-
import { join as
|
|
13749
|
-
import { homedir as
|
|
13474
|
+
import { spawnSync as spawnSync11, execFileSync as execFileSync5 } from "child_process";
|
|
13475
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync22, existsSync as existsSync33, readdirSync as readdirSync8 } from "fs";
|
|
13476
|
+
import { join as join32 } from "path";
|
|
13477
|
+
import { homedir as homedir32 } from "os";
|
|
13750
13478
|
import { createRequire } from "module";
|
|
13751
13479
|
function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
13752
13480
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".turbo", "out", ".cache", ".synkro", ".claude", "vendor", "__tests__", "test-results"]);
|
|
@@ -13757,13 +13485,13 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
13757
13485
|
const dir = stack.pop();
|
|
13758
13486
|
let ents;
|
|
13759
13487
|
try {
|
|
13760
|
-
ents =
|
|
13488
|
+
ents = readdirSync8(dir, { withFileTypes: true });
|
|
13761
13489
|
} catch {
|
|
13762
13490
|
continue;
|
|
13763
13491
|
}
|
|
13764
13492
|
for (const e of ents) {
|
|
13765
13493
|
if (files.length >= maxFiles) break;
|
|
13766
|
-
const full =
|
|
13494
|
+
const full = join32(dir, e.name);
|
|
13767
13495
|
if (e.isDirectory()) {
|
|
13768
13496
|
if (!SKIP.has(e.name) && !e.name.startsWith(".")) stack.push(full);
|
|
13769
13497
|
continue;
|
|
@@ -13771,7 +13499,7 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
13771
13499
|
if (!EXT.test(e.name) || e.name.endsWith(".d.ts")) continue;
|
|
13772
13500
|
const rel = full.startsWith(repoRoot2 + "/") ? full.slice(repoRoot2.length + 1) : full;
|
|
13773
13501
|
try {
|
|
13774
|
-
const content =
|
|
13502
|
+
const content = readFileSync32(full, "utf8");
|
|
13775
13503
|
if (content.length <= maxBytes) files.push({ path: rel, content });
|
|
13776
13504
|
} catch {
|
|
13777
13505
|
}
|
|
@@ -13790,12 +13518,12 @@ function cleanVersion(spec) {
|
|
|
13790
13518
|
function gatherManifestVersions(repoRoot2) {
|
|
13791
13519
|
const out = {};
|
|
13792
13520
|
const dirs = [repoRoot2];
|
|
13793
|
-
const pkgsDir =
|
|
13794
|
-
if (
|
|
13521
|
+
const pkgsDir = join32(repoRoot2, "packages");
|
|
13522
|
+
if (existsSync33(pkgsDir)) {
|
|
13795
13523
|
try {
|
|
13796
|
-
for (const d of
|
|
13797
|
-
const pd =
|
|
13798
|
-
if (
|
|
13524
|
+
for (const d of readdirSync8(pkgsDir)) {
|
|
13525
|
+
const pd = join32(pkgsDir, d);
|
|
13526
|
+
if (existsSync33(join32(pd, "package.json"))) dirs.push(pd);
|
|
13799
13527
|
}
|
|
13800
13528
|
} catch {
|
|
13801
13529
|
}
|
|
@@ -13804,7 +13532,7 @@ function gatherManifestVersions(repoRoot2) {
|
|
|
13804
13532
|
for (const dir of dirs) {
|
|
13805
13533
|
let pkg;
|
|
13806
13534
|
try {
|
|
13807
|
-
pkg = JSON.parse(
|
|
13535
|
+
pkg = JSON.parse(readFileSync32(join32(dir, "package.json"), "utf8"));
|
|
13808
13536
|
} catch {
|
|
13809
13537
|
continue;
|
|
13810
13538
|
}
|
|
@@ -13824,28 +13552,28 @@ function findJelly(repoRoot2) {
|
|
|
13824
13552
|
try {
|
|
13825
13553
|
const pkgJson = require2.resolve("@cs-au-dk/jelly/package.json");
|
|
13826
13554
|
const dir = pkgJson.slice(0, pkgJson.length - "package.json".length);
|
|
13827
|
-
const pkg = JSON.parse(
|
|
13555
|
+
const pkg = JSON.parse(readFileSync32(pkgJson, "utf8"));
|
|
13828
13556
|
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
|
|
13829
13557
|
if (bin) {
|
|
13830
|
-
const p =
|
|
13831
|
-
if (
|
|
13558
|
+
const p = join32(dir, bin);
|
|
13559
|
+
if (existsSync33(p)) return p;
|
|
13832
13560
|
}
|
|
13833
13561
|
} catch {
|
|
13834
13562
|
}
|
|
13835
13563
|
for (const base of [repoRoot2, process.cwd()]) {
|
|
13836
|
-
const b =
|
|
13837
|
-
if (
|
|
13564
|
+
const b = join32(base, "node_modules", ".bin", "jelly");
|
|
13565
|
+
if (existsSync33(b)) return b;
|
|
13838
13566
|
}
|
|
13839
13567
|
return null;
|
|
13840
13568
|
}
|
|
13841
13569
|
function findEntries(repoRoot2) {
|
|
13842
13570
|
const dirs = [repoRoot2];
|
|
13843
|
-
const pkgsDir =
|
|
13844
|
-
if (
|
|
13571
|
+
const pkgsDir = join32(repoRoot2, "packages");
|
|
13572
|
+
if (existsSync33(pkgsDir)) {
|
|
13845
13573
|
try {
|
|
13846
|
-
for (const d of
|
|
13847
|
-
const pd =
|
|
13848
|
-
if (
|
|
13574
|
+
for (const d of readdirSync8(pkgsDir)) {
|
|
13575
|
+
const pd = join32(pkgsDir, d);
|
|
13576
|
+
if (existsSync33(join32(pd, "package.json"))) dirs.push(pd);
|
|
13849
13577
|
}
|
|
13850
13578
|
} catch {
|
|
13851
13579
|
}
|
|
@@ -13853,12 +13581,12 @@ function findEntries(repoRoot2) {
|
|
|
13853
13581
|
const entries = [];
|
|
13854
13582
|
for (const dir of dirs) {
|
|
13855
13583
|
try {
|
|
13856
|
-
const pkg = JSON.parse(
|
|
13584
|
+
const pkg = JSON.parse(readFileSync32(join32(dir, "package.json"), "utf8"));
|
|
13857
13585
|
const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
|
|
13858
13586
|
for (const c of cands) {
|
|
13859
13587
|
if (typeof c !== "string") continue;
|
|
13860
|
-
const f =
|
|
13861
|
-
if (
|
|
13588
|
+
const f = join32(dir, c);
|
|
13589
|
+
if (existsSync33(f)) {
|
|
13862
13590
|
entries.push(f);
|
|
13863
13591
|
break;
|
|
13864
13592
|
}
|
|
@@ -13891,9 +13619,9 @@ function parseApiUsage(log) {
|
|
|
13891
13619
|
}
|
|
13892
13620
|
function runReachabilityScan(repoRoot2, opts = {}) {
|
|
13893
13621
|
const commit = currentCommit(repoRoot2);
|
|
13894
|
-
if (!opts.force && commit &&
|
|
13622
|
+
if (!opts.force && commit && existsSync33(REACHABILITY_PATH)) {
|
|
13895
13623
|
try {
|
|
13896
|
-
const prev = JSON.parse(
|
|
13624
|
+
const prev = JSON.parse(readFileSync32(REACHABILITY_PATH, "utf8"));
|
|
13897
13625
|
if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
|
|
13898
13626
|
} catch {
|
|
13899
13627
|
}
|
|
@@ -13944,7 +13672,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
|
|
|
13944
13672
|
if (jelly) {
|
|
13945
13673
|
const entries = findEntries(repoRoot2);
|
|
13946
13674
|
if (entries.length > 0) {
|
|
13947
|
-
const r =
|
|
13675
|
+
const r = spawnSync11(
|
|
13948
13676
|
process.execPath,
|
|
13949
13677
|
[jelly, "-b", repoRoot2, "--api-usage", ...entries],
|
|
13950
13678
|
{ encoding: "utf8", timeout: opts.timeoutMs ?? 18e4, maxBuffer: 2e8 }
|
|
@@ -13982,7 +13710,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
|
|
|
13982
13710
|
if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
|
|
13983
13711
|
const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot2) };
|
|
13984
13712
|
try {
|
|
13985
|
-
|
|
13713
|
+
writeFileSync22(REACHABILITY_PATH, JSON.stringify(file, null, 2));
|
|
13986
13714
|
} catch (e) {
|
|
13987
13715
|
return { ok: false, reason: "write failed: " + String(e.message || e) };
|
|
13988
13716
|
}
|
|
@@ -13994,7 +13722,7 @@ var init_reachabilityScan = __esm({
|
|
|
13994
13722
|
"use strict";
|
|
13995
13723
|
init_cveReachability();
|
|
13996
13724
|
require2 = createRequire(import.meta.url);
|
|
13997
|
-
REACHABILITY_PATH =
|
|
13725
|
+
REACHABILITY_PATH = join32(homedir32(), ".synkro", "reachability.json");
|
|
13998
13726
|
}
|
|
13999
13727
|
});
|
|
14000
13728
|
|
|
@@ -14003,15 +13731,15 @@ var reachabilityScan_exports = {};
|
|
|
14003
13731
|
__export(reachabilityScan_exports, {
|
|
14004
13732
|
reachabilityScanCommand: () => reachabilityScanCommand
|
|
14005
13733
|
});
|
|
14006
|
-
import { readFileSync as
|
|
14007
|
-
import { join as
|
|
14008
|
-
import { homedir as
|
|
13734
|
+
import { readFileSync as readFileSync33, existsSync as existsSync34 } from "fs";
|
|
13735
|
+
import { join as join33 } from "path";
|
|
13736
|
+
import { homedir as homedir33 } from "os";
|
|
14009
13737
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
14010
13738
|
function readConfigEnv4() {
|
|
14011
|
-
const p =
|
|
14012
|
-
if (!
|
|
13739
|
+
const p = join33(SYNKRO_DIR15, "config.env");
|
|
13740
|
+
if (!existsSync34(p)) return {};
|
|
14013
13741
|
const out = {};
|
|
14014
|
-
for (const line of
|
|
13742
|
+
for (const line of readFileSync33(p, "utf-8").split("\n")) {
|
|
14015
13743
|
const t = line.trim();
|
|
14016
13744
|
if (!t || t.startsWith("#")) continue;
|
|
14017
13745
|
const eq = t.indexOf("=");
|
|
@@ -14043,11 +13771,11 @@ async function pushToCloud(cfg, repo) {
|
|
|
14043
13771
|
while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
|
|
14044
13772
|
let jwt2 = "";
|
|
14045
13773
|
try {
|
|
14046
|
-
jwt2 =
|
|
13774
|
+
jwt2 = readFileSync33(join33(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
|
|
14047
13775
|
} catch {
|
|
14048
13776
|
}
|
|
14049
|
-
if (!jwt2 || !
|
|
14050
|
-
const body =
|
|
13777
|
+
if (!jwt2 || !existsSync34(REACHABILITY_PATH)) return;
|
|
13778
|
+
const body = readFileSync33(REACHABILITY_PATH, "utf-8");
|
|
14051
13779
|
try {
|
|
14052
13780
|
const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
|
|
14053
13781
|
method: "POST",
|
|
@@ -14074,12 +13802,12 @@ async function reachabilityScanCommand(args2 = []) {
|
|
|
14074
13802
|
const isCloud = cfg.SYNKRO_DEPLOY_LOCATION === "cloud" || cfg.SYNKRO_STORAGE_MODE === "cloud";
|
|
14075
13803
|
if (isCloud) await pushToCloud(cfg, cfg.SYNKRO_CONNECTED_REPO || repoSlug(root));
|
|
14076
13804
|
}
|
|
14077
|
-
var
|
|
13805
|
+
var SYNKRO_DIR15;
|
|
14078
13806
|
var init_reachabilityScan2 = __esm({
|
|
14079
13807
|
"cli/commands/reachabilityScan.ts"() {
|
|
14080
13808
|
"use strict";
|
|
14081
13809
|
init_reachabilityScan();
|
|
14082
|
-
|
|
13810
|
+
SYNKRO_DIR15 = join33(homedir33(), ".synkro");
|
|
14083
13811
|
}
|
|
14084
13812
|
});
|
|
14085
13813
|
|
|
@@ -14209,13 +13937,13 @@ var config_exports = {};
|
|
|
14209
13937
|
__export(config_exports, {
|
|
14210
13938
|
configCommand: () => configCommand
|
|
14211
13939
|
});
|
|
14212
|
-
import { readFileSync as
|
|
14213
|
-
import { join as
|
|
14214
|
-
import { homedir as
|
|
13940
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync23, existsSync as existsSync35 } from "fs";
|
|
13941
|
+
import { join as join34 } from "path";
|
|
13942
|
+
import { homedir as homedir34 } from "os";
|
|
14215
13943
|
function readConfigEnv5() {
|
|
14216
|
-
if (!
|
|
13944
|
+
if (!existsSync35(CONFIG_PATH9)) return {};
|
|
14217
13945
|
const out = {};
|
|
14218
|
-
for (const line of
|
|
13946
|
+
for (const line of readFileSync34(CONFIG_PATH9, "utf-8").split("\n")) {
|
|
14219
13947
|
const t = line.trim();
|
|
14220
13948
|
if (!t || t.startsWith("#")) continue;
|
|
14221
13949
|
const eq = t.indexOf("=");
|
|
@@ -14224,11 +13952,11 @@ function readConfigEnv5() {
|
|
|
14224
13952
|
return out;
|
|
14225
13953
|
}
|
|
14226
13954
|
function updateConfigValue(key, value) {
|
|
14227
|
-
if (!
|
|
13955
|
+
if (!existsSync35(CONFIG_PATH9)) {
|
|
14228
13956
|
console.error("No config found. Run `synkro install` first.");
|
|
14229
13957
|
process.exit(1);
|
|
14230
13958
|
}
|
|
14231
|
-
const lines =
|
|
13959
|
+
const lines = readFileSync34(CONFIG_PATH9, "utf-8").split("\n");
|
|
14232
13960
|
const pattern = new RegExp(`^${key}=`);
|
|
14233
13961
|
let found = false;
|
|
14234
13962
|
const updated = lines.map((line) => {
|
|
@@ -14239,7 +13967,7 @@ function updateConfigValue(key, value) {
|
|
|
14239
13967
|
return line;
|
|
14240
13968
|
});
|
|
14241
13969
|
if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
|
|
14242
|
-
|
|
13970
|
+
writeFileSync23(CONFIG_PATH9, updated.join("\n"), "utf-8");
|
|
14243
13971
|
}
|
|
14244
13972
|
function resolveInferenceMode(cfg) {
|
|
14245
13973
|
if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
|
|
@@ -14391,14 +14119,14 @@ To change:`);
|
|
|
14391
14119
|
}
|
|
14392
14120
|
if (inferenceValue !== "cloud") await reconcileContainer();
|
|
14393
14121
|
}
|
|
14394
|
-
var
|
|
14122
|
+
var SYNKRO_DIR16, CONFIG_PATH9;
|
|
14395
14123
|
var init_config = __esm({
|
|
14396
14124
|
"cli/commands/config.ts"() {
|
|
14397
14125
|
"use strict";
|
|
14398
14126
|
init_stub();
|
|
14399
14127
|
init_optout();
|
|
14400
|
-
|
|
14401
|
-
CONFIG_PATH9 =
|
|
14128
|
+
SYNKRO_DIR16 = join34(homedir34(), ".synkro");
|
|
14129
|
+
CONFIG_PATH9 = join34(SYNKRO_DIR16, "config.env");
|
|
14402
14130
|
}
|
|
14403
14131
|
});
|
|
14404
14132
|
|
|
@@ -14587,14 +14315,14 @@ Usage:
|
|
|
14587
14315
|
});
|
|
14588
14316
|
|
|
14589
14317
|
// cli/bootstrap.js
|
|
14590
|
-
import { readFileSync as
|
|
14318
|
+
import { readFileSync as readFileSync35, existsSync as existsSync36 } from "fs";
|
|
14591
14319
|
import { resolve as resolve5 } from "path";
|
|
14592
14320
|
var envCandidates = [
|
|
14593
14321
|
resolve5(process.env.HOME ?? "", ".synkro", "config.env")
|
|
14594
14322
|
];
|
|
14595
14323
|
for (const envPath of envCandidates) {
|
|
14596
|
-
if (!
|
|
14597
|
-
const envContent =
|
|
14324
|
+
if (!existsSync36(envPath)) continue;
|
|
14325
|
+
const envContent = readFileSync35(envPath, "utf-8");
|
|
14598
14326
|
for (const line of envContent.split("\n")) {
|
|
14599
14327
|
const trimmed = line.trim();
|
|
14600
14328
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -14611,7 +14339,7 @@ var subArgs = args.slice(1);
|
|
|
14611
14339
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
14612
14340
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
14613
14341
|
function printVersion() {
|
|
14614
|
-
console.log("1.7.
|
|
14342
|
+
console.log("1.7.94");
|
|
14615
14343
|
}
|
|
14616
14344
|
function printHelp2() {
|
|
14617
14345
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|
|
@@ -14703,79 +14431,17 @@ async function main() {
|
|
|
14703
14431
|
await scanPrCommand2();
|
|
14704
14432
|
break;
|
|
14705
14433
|
}
|
|
14706
|
-
case "route":
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
if (model) {
|
|
14711
|
-
try {
|
|
14712
|
-
injectModel2(model, sessionOverride);
|
|
14713
|
-
} catch {
|
|
14714
|
-
}
|
|
14715
|
-
}
|
|
14716
|
-
break;
|
|
14717
|
-
}
|
|
14718
|
-
case "route-decide": {
|
|
14719
|
-
const { routeDecide: routeDecide2 } = await Promise.resolve().then(() => (init_routeDecide(), routeDecide_exports));
|
|
14720
|
-
const sid = (subArgs[0] || "").trim();
|
|
14721
|
-
if (sid) {
|
|
14722
|
-
try {
|
|
14723
|
-
await routeDecide2(sid);
|
|
14724
|
-
} catch {
|
|
14725
|
-
}
|
|
14726
|
-
}
|
|
14727
|
-
break;
|
|
14728
|
-
}
|
|
14729
|
-
case "route-and-resubmit": {
|
|
14730
|
-
const { routeAndResubmit: routeAndResubmit2 } = await Promise.resolve().then(() => (init_routeOrchestrate(), routeOrchestrate_exports));
|
|
14731
|
-
const sid = (subArgs[0] || "").trim();
|
|
14732
|
-
const task = (subArgs[1] || "").trim();
|
|
14733
|
-
const tmuxSession = (subArgs[2] || "").trim() || void 0;
|
|
14734
|
-
const forceModel = (subArgs[3] || "").trim() || void 0;
|
|
14735
|
-
if (sid && task) {
|
|
14736
|
-
try {
|
|
14737
|
-
await routeAndResubmit2(sid, task, tmuxSession, forceModel);
|
|
14738
|
-
} catch {
|
|
14739
|
-
}
|
|
14740
|
-
}
|
|
14741
|
-
break;
|
|
14742
|
-
}
|
|
14743
|
-
case "routing": {
|
|
14744
|
-
const { routingCommand: routingCommand2 } = await Promise.resolve().then(() => (init_routingToggle(), routingToggle_exports));
|
|
14745
|
-
routingCommand2(subArgs);
|
|
14746
|
-
break;
|
|
14747
|
-
}
|
|
14434
|
+
case "route":
|
|
14435
|
+
case "route-decide":
|
|
14436
|
+
case "route-and-resubmit":
|
|
14437
|
+
case "routing":
|
|
14748
14438
|
case "sessions": {
|
|
14749
|
-
|
|
14750
|
-
const rows = listSessions2({ liveOnly: !subArgs.includes("--all") });
|
|
14751
|
-
if (rows.length === 0) {
|
|
14752
|
-
console.log("No active Synkro-wrapped Claude Code sessions.");
|
|
14753
|
-
console.log("(Start one with `claude`; it registers on your first prompt.)");
|
|
14754
|
-
break;
|
|
14755
|
-
}
|
|
14756
|
-
const now = Date.now();
|
|
14757
|
-
const age = (ts) => {
|
|
14758
|
-
const s = Math.max(0, Math.round((now - (ts || 0)) / 1e3));
|
|
14759
|
-
if (s < 60) return `${s}s`;
|
|
14760
|
-
if (s < 3600) return `${Math.round(s / 60)}m`;
|
|
14761
|
-
return `${Math.round(s / 3600)}h`;
|
|
14762
|
-
};
|
|
14763
|
-
console.log(`${rows.length} session${rows.length === 1 ? "" : "s"}:
|
|
14764
|
-
`);
|
|
14765
|
-
for (const r of rows) {
|
|
14766
|
-
console.log(` ${r.session_id} (${age(r.ts)} ago)`);
|
|
14767
|
-
if (r.cwd) console.log(` cwd: ${r.cwd}`);
|
|
14768
|
-
if (r.last_prompt) console.log(` prompt: ${r.last_prompt.replace(/\s+/g, " ").slice(0, 80)}`);
|
|
14769
|
-
console.log(` route: echo <model> > ~/.synkro/pty/route-${r.session_id}`);
|
|
14770
|
-
console.log();
|
|
14771
|
-
}
|
|
14439
|
+
console.error("Synkro host model routing has been removed.");
|
|
14772
14440
|
break;
|
|
14773
14441
|
}
|
|
14774
14442
|
case "pty-shim": {
|
|
14775
|
-
const {
|
|
14776
|
-
|
|
14777
|
-
if (sub === "uninstall" || sub === "remove") uninstallPtyShim2();
|
|
14778
|
-
else installPtyShim2();
|
|
14443
|
+
const { uninstallPtyShim: uninstallPtyShim2 } = await Promise.resolve().then(() => (init_ptyShim(), ptyShim_exports));
|
|
14444
|
+
uninstallPtyShim2();
|
|
14779
14445
|
break;
|
|
14780
14446
|
}
|
|
14781
14447
|
case "local-cc": {
|