@synkro-sh/cli 1.7.93 → 1.7.95
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 +373 -56
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
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.95";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -2147,7 +2147,7 @@ var init_codexHookConfig = __esm({
|
|
|
2147
2147
|
|
|
2148
2148
|
// cli/installer/codexHookTrust.ts
|
|
2149
2149
|
import { spawn as spawn2 } from "child_process";
|
|
2150
|
-
function
|
|
2150
|
+
function parseCodexHookTrustRecords(stdout) {
|
|
2151
2151
|
for (const line of stdout.split("\n")) {
|
|
2152
2152
|
let message;
|
|
2153
2153
|
try {
|
|
@@ -2159,20 +2159,41 @@ function parseCodexHookTrustOutput(stdout) {
|
|
|
2159
2159
|
if (!Array.isArray(data)) continue;
|
|
2160
2160
|
const hooks = data.flatMap((entry) => Array.isArray(entry?.hooks) ? entry.hooks : []).filter((hook) => typeof hook.command === "string" && isSynkroHookCommand(hook.command));
|
|
2161
2161
|
if (!hooks.length) return null;
|
|
2162
|
-
return
|
|
2163
|
-
total: hooks.length,
|
|
2164
|
-
trusted: hooks.filter((hook) => hook.trustStatus === "trusted" || hook.trustStatus === "managed").length,
|
|
2165
|
-
needsReview: hooks.filter((hook) => hook.trustStatus === "modified" || hook.trustStatus === "untrusted").length,
|
|
2166
|
-
disabled: hooks.filter((hook) => hook.enabled === false).length
|
|
2167
|
-
};
|
|
2162
|
+
return hooks;
|
|
2168
2163
|
}
|
|
2169
2164
|
return null;
|
|
2170
2165
|
}
|
|
2171
|
-
function
|
|
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) {
|
|
2172
2192
|
return new Promise((resolve6) => {
|
|
2173
2193
|
let settled = false;
|
|
2174
2194
|
let stdout = "";
|
|
2175
2195
|
let pending = "";
|
|
2196
|
+
let hooks = null;
|
|
2176
2197
|
let child;
|
|
2177
2198
|
const finish = (summary) => {
|
|
2178
2199
|
if (settled) return;
|
|
@@ -2217,7 +2238,32 @@ function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
|
|
|
2217
2238
|
params: { cwds: [cwd] }
|
|
2218
2239
|
}) + "\n");
|
|
2219
2240
|
} else if (message?.id === 2) {
|
|
2220
|
-
|
|
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
|
+
});
|
|
2221
2267
|
}
|
|
2222
2268
|
}
|
|
2223
2269
|
});
|
|
@@ -2234,6 +2280,9 @@ function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
|
|
|
2234
2280
|
}
|
|
2235
2281
|
});
|
|
2236
2282
|
}
|
|
2283
|
+
function trustCodexHooks(codexBinary = "codex", cwd = process.cwd()) {
|
|
2284
|
+
return queryCodexHookTrust(codexBinary, cwd, true);
|
|
2285
|
+
}
|
|
2237
2286
|
function codexHookTrustLines(summary) {
|
|
2238
2287
|
if (!summary) {
|
|
2239
2288
|
return [
|
|
@@ -2254,7 +2303,7 @@ function codexHookTrustLines(summary) {
|
|
|
2254
2303
|
];
|
|
2255
2304
|
}
|
|
2256
2305
|
async function reportCodexHookTrust(codexBinary, cwd) {
|
|
2257
|
-
const summary = await
|
|
2306
|
+
const summary = await trustCodexHooks(codexBinary || "codex", cwd);
|
|
2258
2307
|
for (const line of codexHookTrustLines(summary)) console.log(line);
|
|
2259
2308
|
return summary;
|
|
2260
2309
|
}
|
|
@@ -2428,9 +2477,54 @@ function removeCodexManagedBlock(content, path) {
|
|
|
2428
2477
|
if (inside) throw new Error(`Incomplete Synkro MCP marker block in ${path}`);
|
|
2429
2478
|
return { content: out.join("\n").replace(/\n{3,}$/g, "\n\n"), removed };
|
|
2430
2479
|
}
|
|
2431
|
-
function
|
|
2432
|
-
const section = /^\s*\[\s*mcp_servers\s*\.\s*(?:"synkro-guardrails"|'synkro-guardrails'|synkro-guardrails)\s*\]\s*(?:#.*)?$/
|
|
2433
|
-
|
|
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 };
|
|
2434
2528
|
}
|
|
2435
2529
|
function tomlString(value) {
|
|
2436
2530
|
return JSON.stringify(value);
|
|
@@ -2438,16 +2532,17 @@ function tomlString(value) {
|
|
|
2438
2532
|
function installCodexMcpConfig(opts) {
|
|
2439
2533
|
const path = codexConfigPath(opts.configPath);
|
|
2440
2534
|
const current = readCodexToml(path);
|
|
2441
|
-
const
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
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;
|
|
2447
2542
|
let url;
|
|
2448
2543
|
let body;
|
|
2449
2544
|
if (opts.local) {
|
|
2450
|
-
url =
|
|
2545
|
+
url = targetUrl;
|
|
2451
2546
|
body = [
|
|
2452
2547
|
`[${CODEX_MCP_SECTION}]`,
|
|
2453
2548
|
`command = ${tomlString(opts.bunBin || resolveBunBin2())}`,
|
|
@@ -2456,7 +2551,7 @@ function installCodexMcpConfig(opts) {
|
|
|
2456
2551
|
];
|
|
2457
2552
|
} else {
|
|
2458
2553
|
if (!opts.bearerToken) throw new Error("Codex cloud MCP registration requires a bearer token");
|
|
2459
|
-
url =
|
|
2554
|
+
url = targetUrl;
|
|
2460
2555
|
body = [
|
|
2461
2556
|
`[${CODEX_MCP_SECTION}]`,
|
|
2462
2557
|
`url = ${tomlString(url)}`,
|
|
@@ -2464,7 +2559,7 @@ function installCodexMcpConfig(opts) {
|
|
|
2464
2559
|
"enabled = true"
|
|
2465
2560
|
];
|
|
2466
2561
|
}
|
|
2467
|
-
const prefix =
|
|
2562
|
+
const prefix = withoutExisting.trimEnd();
|
|
2468
2563
|
const managed = [CODEX_MCP_BEGIN, ...body, CODEX_MCP_END].join("\n");
|
|
2469
2564
|
writeCodexTomlAtomic(path, `${prefix}${prefix ? "\n\n" : ""}${managed}
|
|
2470
2565
|
`);
|
|
@@ -2475,8 +2570,19 @@ function uninstallCodexMcpConfig(configPath) {
|
|
|
2475
2570
|
if (!existsSync13(path)) return false;
|
|
2476
2571
|
const current = readCodexToml(path);
|
|
2477
2572
|
const next = removeCodexManagedBlock(current, path);
|
|
2478
|
-
|
|
2479
|
-
|
|
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()}
|
|
2480
2586
|
` : "");
|
|
2481
2587
|
return true;
|
|
2482
2588
|
}
|
|
@@ -6506,8 +6612,16 @@ async function dockerInstall(opts = {}) {
|
|
|
6506
6612
|
...process.env.SYNKRO_MAX_BATCH_SIZE ? ["-e", `SYNKRO_MAX_BATCH_SIZE=${process.env.SYNKRO_MAX_BATCH_SIZE}`] : [],
|
|
6507
6613
|
// Full verifier prompt/response tracing is explicit opt-in because it contains source code.
|
|
6508
6614
|
...process.env.SYNKRO_VERIFY_TRACE === "1" ? ["-e", "SYNKRO_VERIFY_TRACE=1"] : [],
|
|
6509
|
-
//
|
|
6510
|
-
|
|
6615
|
+
// Explicit model overrides are preserved across local install/update for
|
|
6616
|
+
// every provider and isolated lane. The server reports the resolved values
|
|
6617
|
+
// in /healthz so operators can audit exactly what is spending tokens.
|
|
6618
|
+
...[
|
|
6619
|
+
"SYNKRO_CLAUDE_MODEL",
|
|
6620
|
+
"SYNKRO_CURSOR_MODEL",
|
|
6621
|
+
"SYNKRO_CODEX_MODEL",
|
|
6622
|
+
"SYNKRO_CONDUCTOR_MODEL",
|
|
6623
|
+
"SYNKRO_ROUTE_MODEL"
|
|
6624
|
+
].flatMap((key) => process.env[key] ? ["-e", `${key}=${process.env[key]}`] : []),
|
|
6511
6625
|
// Fix-poll kill switch. Default ON in the image; a benchmark/headless run
|
|
6512
6626
|
// (e.g. sec-code-bench) sets SYNKRO_FIX_POLL=0 so ask-mode violations skip the
|
|
6513
6627
|
// interactive AskUserQuestion poll and fall through to generate-the-fix. Only
|
|
@@ -6765,7 +6879,7 @@ var init_dockerInstall = __esm({
|
|
|
6765
6879
|
HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
|
|
6766
6880
|
CONTAINER_NAME = resolveContainerName();
|
|
6767
6881
|
defaultImageVersion = () => {
|
|
6768
|
-
if (true) return "1.7.
|
|
6882
|
+
if (true) return "1.7.95";
|
|
6769
6883
|
try {
|
|
6770
6884
|
const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
|
|
6771
6885
|
if (pkg.version) return pkg.version;
|
|
@@ -7458,6 +7572,94 @@ var init_codexTranscriptMessages = __esm({
|
|
|
7458
7572
|
}
|
|
7459
7573
|
});
|
|
7460
7574
|
|
|
7575
|
+
// cli/scanning/claudeTranscriptUsage.ts
|
|
7576
|
+
function tokenCount(value) {
|
|
7577
|
+
const parsed = Number(value);
|
|
7578
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
7579
|
+
}
|
|
7580
|
+
function isoDay(value, fallbackDay) {
|
|
7581
|
+
if (typeof value === "string") {
|
|
7582
|
+
const parsed = new Date(value);
|
|
7583
|
+
if (Number.isFinite(parsed.getTime())) return parsed.toISOString().slice(0, 10);
|
|
7584
|
+
}
|
|
7585
|
+
return fallbackDay;
|
|
7586
|
+
}
|
|
7587
|
+
function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), options = {}) {
|
|
7588
|
+
const seen = options.seenStableIds ?? /* @__PURE__ */ new Set();
|
|
7589
|
+
const rollups = /* @__PURE__ */ new Map();
|
|
7590
|
+
const usage = {
|
|
7591
|
+
input_tokens: 0,
|
|
7592
|
+
output_tokens: 0,
|
|
7593
|
+
cache_creation_input_tokens: 0,
|
|
7594
|
+
cache_read_input_tokens: 0
|
|
7595
|
+
};
|
|
7596
|
+
let model = "";
|
|
7597
|
+
let turns = 0;
|
|
7598
|
+
let lineIndex = 0;
|
|
7599
|
+
for (const line of transcript.split("\n")) {
|
|
7600
|
+
lineIndex += 1;
|
|
7601
|
+
const text = line.trim();
|
|
7602
|
+
if (!text) continue;
|
|
7603
|
+
try {
|
|
7604
|
+
const entry = JSON.parse(text);
|
|
7605
|
+
const message = entry?.message;
|
|
7606
|
+
if (message?.role !== "assistant" || !message.usage || typeof message.usage !== "object") {
|
|
7607
|
+
continue;
|
|
7608
|
+
}
|
|
7609
|
+
const stableId = typeof entry.uuid === "string" && entry.uuid ? `uuid:${entry.uuid}` : typeof message.id === "string" && message.id ? `message:${message.id}:${String(entry.timestamp || "")}` : `${options.sourceId || "transcript"}:line:${lineIndex}`;
|
|
7610
|
+
if (seen.has(stableId)) continue;
|
|
7611
|
+
seen.add(stableId);
|
|
7612
|
+
const counts = {
|
|
7613
|
+
input_tokens: tokenCount(message.usage.input_tokens),
|
|
7614
|
+
output_tokens: tokenCount(message.usage.output_tokens),
|
|
7615
|
+
cache_creation_input_tokens: tokenCount(message.usage.cache_creation_input_tokens),
|
|
7616
|
+
cache_read_input_tokens: tokenCount(message.usage.cache_read_input_tokens)
|
|
7617
|
+
};
|
|
7618
|
+
const countTotal = counts.input_tokens + counts.output_tokens + counts.cache_creation_input_tokens + counts.cache_read_input_tokens;
|
|
7619
|
+
if (countTotal === 0) continue;
|
|
7620
|
+
const entryModel = typeof message.model === "string" && message.model ? message.model : "unknown";
|
|
7621
|
+
if (entryModel !== "<synthetic>") model = entryModel;
|
|
7622
|
+
const day = isoDay(entry.timestamp, fallbackDay);
|
|
7623
|
+
const key = `${day}\0${entryModel}`;
|
|
7624
|
+
const row = rollups.get(key) ?? {
|
|
7625
|
+
day,
|
|
7626
|
+
model: entryModel,
|
|
7627
|
+
turns: 0,
|
|
7628
|
+
input_tokens: 0,
|
|
7629
|
+
output_tokens: 0,
|
|
7630
|
+
cache_creation_input_tokens: 0,
|
|
7631
|
+
cache_read_input_tokens: 0
|
|
7632
|
+
};
|
|
7633
|
+
row.turns += 1;
|
|
7634
|
+
row.input_tokens += counts.input_tokens;
|
|
7635
|
+
row.output_tokens += counts.output_tokens;
|
|
7636
|
+
row.cache_creation_input_tokens += counts.cache_creation_input_tokens;
|
|
7637
|
+
row.cache_read_input_tokens += counts.cache_read_input_tokens;
|
|
7638
|
+
rollups.set(key, row);
|
|
7639
|
+
turns += 1;
|
|
7640
|
+
usage.input_tokens += counts.input_tokens;
|
|
7641
|
+
usage.output_tokens += counts.output_tokens;
|
|
7642
|
+
usage.cache_creation_input_tokens += counts.cache_creation_input_tokens;
|
|
7643
|
+
usage.cache_read_input_tokens += counts.cache_read_input_tokens;
|
|
7644
|
+
} catch {
|
|
7645
|
+
}
|
|
7646
|
+
}
|
|
7647
|
+
if (turns === 0) return null;
|
|
7648
|
+
return {
|
|
7649
|
+
usage,
|
|
7650
|
+
model: model || "unknown",
|
|
7651
|
+
rollups: [...rollups.values()].sort(
|
|
7652
|
+
(a, b) => a.day.localeCompare(b.day) || a.model.localeCompare(b.model)
|
|
7653
|
+
),
|
|
7654
|
+
turns
|
|
7655
|
+
};
|
|
7656
|
+
}
|
|
7657
|
+
var init_claudeTranscriptUsage = __esm({
|
|
7658
|
+
"cli/scanning/claudeTranscriptUsage.ts"() {
|
|
7659
|
+
"use strict";
|
|
7660
|
+
}
|
|
7661
|
+
});
|
|
7662
|
+
|
|
7461
7663
|
// cli/commands/install.ts
|
|
7462
7664
|
var install_exports = {};
|
|
7463
7665
|
__export(install_exports, {
|
|
@@ -7475,7 +7677,7 @@ __export(install_exports, {
|
|
|
7475
7677
|
});
|
|
7476
7678
|
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";
|
|
7477
7679
|
import { homedir as homedir21 } from "os";
|
|
7478
|
-
import { join as join19, isAbsolute, resolve as resolve4 } from "path";
|
|
7680
|
+
import { basename, join as join19, isAbsolute, resolve as resolve4, sep } from "path";
|
|
7479
7681
|
import { execSync as execSync4, spawn as spawn5 } from "child_process";
|
|
7480
7682
|
import { createInterface as createInterface2 } from "readline";
|
|
7481
7683
|
import { createHash as createHash4 } from "crypto";
|
|
@@ -7734,7 +7936,7 @@ function writeConfigEnv(opts) {
|
|
|
7734
7936
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
7735
7937
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
7736
7938
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
7737
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.
|
|
7939
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.95")}`
|
|
7738
7940
|
];
|
|
7739
7941
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
7740
7942
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -7912,6 +8114,11 @@ async function provisionCloudContainer(opts) {
|
|
|
7912
8114
|
cursor_workers: cursorWorkers,
|
|
7913
8115
|
codex_workers: codexWorkers,
|
|
7914
8116
|
conductor_provider: selectedKind,
|
|
8117
|
+
claude_model: process.env.SYNKRO_CLAUDE_MODEL || "",
|
|
8118
|
+
cursor_model: process.env.SYNKRO_CURSOR_MODEL || "",
|
|
8119
|
+
codex_model: process.env.SYNKRO_CODEX_MODEL || "",
|
|
8120
|
+
conductor_model: process.env.SYNKRO_CONDUCTOR_MODEL || "",
|
|
8121
|
+
route_model: process.env.SYNKRO_ROUTE_MODEL || "",
|
|
7915
8122
|
cursor_api_key: cursorApiKey,
|
|
7916
8123
|
// never logged; gateway stores it as the org secret
|
|
7917
8124
|
connected_repo: repo,
|
|
@@ -8476,7 +8683,7 @@ async function installCommand(opts = {}) {
|
|
|
8476
8683
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
8477
8684
|
emit("install", {
|
|
8478
8685
|
phase: "started",
|
|
8479
|
-
cli_version_to: "1.7.
|
|
8686
|
+
cli_version_to: "1.7.95",
|
|
8480
8687
|
agents_detected: agents.map((a) => a.kind),
|
|
8481
8688
|
with_github: false,
|
|
8482
8689
|
with_local_cc: false,
|
|
@@ -9543,8 +9750,8 @@ function ensureReachabilityGitHook() {
|
|
|
9543
9750
|
}
|
|
9544
9751
|
return "updated";
|
|
9545
9752
|
}
|
|
9546
|
-
const
|
|
9547
|
-
writeFileSync17(hookPath, cur +
|
|
9753
|
+
const sep3 = cur.endsWith("\n") ? "" : "\n";
|
|
9754
|
+
writeFileSync17(hookPath, cur + sep3 + "\n" + block + "\n");
|
|
9548
9755
|
try {
|
|
9549
9756
|
chmodSync5(hookPath, 493);
|
|
9550
9757
|
} catch {
|
|
@@ -9571,10 +9778,33 @@ function detectGitRepo2() {
|
|
|
9571
9778
|
}
|
|
9572
9779
|
function getClaudeProjectsFolder() {
|
|
9573
9780
|
const cwd = process.cwd();
|
|
9574
|
-
const sanitized =
|
|
9781
|
+
const sanitized = cwd.replace(/\//g, "-");
|
|
9575
9782
|
const projectsDir = join19(homedir21(), ".claude", "projects", sanitized);
|
|
9576
9783
|
return existsSync22(projectsDir) ? projectsDir : null;
|
|
9577
9784
|
}
|
|
9785
|
+
function getClaudeTranscriptFileEntries(projectsDir) {
|
|
9786
|
+
let relativeFiles = [];
|
|
9787
|
+
try {
|
|
9788
|
+
relativeFiles = readdirSync4(projectsDir, { recursive: true, encoding: "utf-8" });
|
|
9789
|
+
} catch {
|
|
9790
|
+
return [];
|
|
9791
|
+
}
|
|
9792
|
+
return relativeFiles.filter((file) => file.endsWith(".jsonl")).map((file) => {
|
|
9793
|
+
const parts = file.split(sep);
|
|
9794
|
+
const subagentsIndex = parts.lastIndexOf("subagents");
|
|
9795
|
+
if (subagentsIndex > 0) {
|
|
9796
|
+
return {
|
|
9797
|
+
filePath: join19(projectsDir, file),
|
|
9798
|
+
sessionId: basename(file, ".jsonl"),
|
|
9799
|
+
parentSessionId: parts[subagentsIndex - 1]
|
|
9800
|
+
};
|
|
9801
|
+
}
|
|
9802
|
+
return {
|
|
9803
|
+
filePath: join19(projectsDir, file),
|
|
9804
|
+
sessionId: basename(file, ".jsonl")
|
|
9805
|
+
};
|
|
9806
|
+
});
|
|
9807
|
+
}
|
|
9578
9808
|
function extractSessionInsights(projectsDir) {
|
|
9579
9809
|
const insights = [];
|
|
9580
9810
|
const files = readdirSync4(projectsDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -9874,23 +10104,40 @@ function parseTranscriptFile(filePath) {
|
|
|
9874
10104
|
async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
9875
10105
|
const projectsDir = getClaudeProjectsFolder();
|
|
9876
10106
|
if (!projectsDir) return { sessions: 0, messages: 0 };
|
|
9877
|
-
const files =
|
|
10107
|
+
const files = getClaudeTranscriptFileEntries(projectsDir);
|
|
9878
10108
|
if (files.length === 0) return { sessions: 0, messages: 0 };
|
|
9879
10109
|
console.log(` Found ${files.length} CC session transcripts, importing + embedding...`);
|
|
9880
10110
|
let totalSessions = 0;
|
|
9881
10111
|
let totalMessages = 0;
|
|
10112
|
+
const seenStableIds = /* @__PURE__ */ new Set();
|
|
9882
10113
|
for (let i = 0; i < files.length; i++) {
|
|
9883
10114
|
const file = files[i];
|
|
9884
|
-
const sessionId = file.
|
|
9885
|
-
const filePath =
|
|
10115
|
+
const sessionId = file.sessionId;
|
|
10116
|
+
const filePath = file.filePath;
|
|
9886
10117
|
try {
|
|
10118
|
+
const transcript = readFileSync21(filePath, "utf-8");
|
|
10119
|
+
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
10120
|
+
transcript,
|
|
10121
|
+
statSync2(filePath).mtime.toISOString().slice(0, 10),
|
|
10122
|
+
{ seenStableIds, sourceId: file.parentSessionId ? `${file.parentSessionId}:subagent:${sessionId}` : sessionId }
|
|
10123
|
+
);
|
|
9887
10124
|
const allMessages = parseTranscriptFile(filePath);
|
|
9888
10125
|
const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
|
|
9889
10126
|
if (messages.length === 0) continue;
|
|
9890
10127
|
const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/conversation-sync`, {
|
|
9891
10128
|
method: "POST",
|
|
9892
10129
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpToken}` },
|
|
9893
|
-
body: JSON.stringify({
|
|
10130
|
+
body: JSON.stringify({
|
|
10131
|
+
session_id: sessionId,
|
|
10132
|
+
parent_session_id: file.parentSessionId,
|
|
10133
|
+
repo,
|
|
10134
|
+
messages,
|
|
10135
|
+
session_usage: transcriptUsage?.usage,
|
|
10136
|
+
usage_rollups: transcriptUsage?.rollups ?? [],
|
|
10137
|
+
model: transcriptUsage?.model,
|
|
10138
|
+
harness: "claude-code",
|
|
10139
|
+
usage_cumulative: true
|
|
10140
|
+
}),
|
|
9894
10141
|
signal: AbortSignal.timeout(15e3)
|
|
9895
10142
|
});
|
|
9896
10143
|
if (resp.ok) {
|
|
@@ -9904,9 +10151,10 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
9904
10151
|
process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
|
|
9905
10152
|
}
|
|
9906
10153
|
try {
|
|
9907
|
-
const content = readFileSync21(
|
|
10154
|
+
const content = readFileSync21(filePath, "utf-8");
|
|
9908
10155
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
9909
|
-
|
|
10156
|
+
const offsetId = file.parentSessionId ? `${file.parentSessionId}_${sessionId}` : sessionId;
|
|
10157
|
+
writeFileSync17(join19(OFFSETS_DIR, offsetId), String(lineCount), "utf-8");
|
|
9910
10158
|
} catch {
|
|
9911
10159
|
}
|
|
9912
10160
|
}
|
|
@@ -9916,23 +10164,39 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
9916
10164
|
async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
9917
10165
|
const projectsDir = getClaudeProjectsFolder();
|
|
9918
10166
|
if (!projectsDir) return { sessions: 0, messages: 0 };
|
|
9919
|
-
const files =
|
|
10167
|
+
const files = getClaudeTranscriptFileEntries(projectsDir);
|
|
9920
10168
|
if (files.length === 0) return { sessions: 0, messages: 0 };
|
|
9921
10169
|
console.log(`Found ${files.length} CC session transcripts, syncing...`);
|
|
9922
10170
|
const maxMessagesPerSession = 500;
|
|
9923
10171
|
let totalSessions = 0;
|
|
9924
10172
|
let totalMessages = 0;
|
|
10173
|
+
const seenStableIds = /* @__PURE__ */ new Set();
|
|
9925
10174
|
for (let i = 0; i < files.length; i += 5) {
|
|
9926
10175
|
const batch = files.slice(i, i + 5);
|
|
9927
10176
|
const sessions = [];
|
|
9928
10177
|
for (const file of batch) {
|
|
9929
|
-
const sessionId = file.
|
|
9930
|
-
const filePath =
|
|
10178
|
+
const sessionId = file.sessionId;
|
|
10179
|
+
const filePath = file.filePath;
|
|
9931
10180
|
try {
|
|
10181
|
+
const transcript = readFileSync21(filePath, "utf-8");
|
|
10182
|
+
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
10183
|
+
transcript,
|
|
10184
|
+
statSync2(filePath).mtime.toISOString().slice(0, 10),
|
|
10185
|
+
{ seenStableIds, sourceId: file.parentSessionId ? `${file.parentSessionId}:subagent:${sessionId}` : sessionId }
|
|
10186
|
+
);
|
|
9932
10187
|
const allMessages = parseTranscriptFile(filePath);
|
|
9933
10188
|
const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
|
|
9934
10189
|
if (messages.length > 0) {
|
|
9935
|
-
sessions.push({
|
|
10190
|
+
sessions.push({
|
|
10191
|
+
cc_session_id: sessionId,
|
|
10192
|
+
parent_session_id: file.parentSessionId,
|
|
10193
|
+
messages,
|
|
10194
|
+
model: transcriptUsage?.model,
|
|
10195
|
+
session_usage: transcriptUsage?.usage,
|
|
10196
|
+
usage_rollups: transcriptUsage?.rollups ?? [],
|
|
10197
|
+
harness: "claude-code",
|
|
10198
|
+
usage_cumulative: true
|
|
10199
|
+
});
|
|
9936
10200
|
}
|
|
9937
10201
|
} catch {
|
|
9938
10202
|
}
|
|
@@ -9955,12 +10219,13 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
9955
10219
|
} catch {
|
|
9956
10220
|
}
|
|
9957
10221
|
for (const file of batch) {
|
|
9958
|
-
const sessionId = file.
|
|
9959
|
-
const filePath =
|
|
10222
|
+
const sessionId = file.sessionId;
|
|
10223
|
+
const filePath = file.filePath;
|
|
9960
10224
|
try {
|
|
9961
10225
|
const content = readFileSync21(filePath, "utf-8");
|
|
9962
10226
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
9963
|
-
|
|
10227
|
+
const offsetId = file.parentSessionId ? `${file.parentSessionId}_${sessionId}` : sessionId;
|
|
10228
|
+
writeFileSync17(join19(OFFSETS_DIR, offsetId), String(lineCount), "utf-8");
|
|
9964
10229
|
} catch {
|
|
9965
10230
|
}
|
|
9966
10231
|
}
|
|
@@ -10043,6 +10308,7 @@ var init_install = __esm({
|
|
|
10043
10308
|
init_graderSmoke();
|
|
10044
10309
|
init_codexTranscriptUsage();
|
|
10045
10310
|
init_codexTranscriptMessages();
|
|
10311
|
+
init_claudeTranscriptUsage();
|
|
10046
10312
|
SYNKRO_DIR11 = join19(homedir21(), ".synkro");
|
|
10047
10313
|
HOOKS_DIR = join19(SYNKRO_DIR11, "hooks");
|
|
10048
10314
|
CONFIG_PATH4 = join19(SYNKRO_DIR11, "config.env");
|
|
@@ -12639,9 +12905,9 @@ var import_exports = {};
|
|
|
12639
12905
|
__export(import_exports, {
|
|
12640
12906
|
importCommand: () => importCommand
|
|
12641
12907
|
});
|
|
12642
|
-
import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6 } from "fs";
|
|
12908
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
12643
12909
|
import { homedir as homedir28 } from "os";
|
|
12644
|
-
import { join as join27 } from "path";
|
|
12910
|
+
import { basename as basename2, join as join27, sep as sep2 } from "path";
|
|
12645
12911
|
import { execSync as execSync6 } from "child_process";
|
|
12646
12912
|
import { createInterface as createInterface4 } from "readline";
|
|
12647
12913
|
function readMcpJwt() {
|
|
@@ -12669,6 +12935,23 @@ function projectsFolder() {
|
|
|
12669
12935
|
const dir = join27(homedir28(), ".claude", "projects", sanitized);
|
|
12670
12936
|
return existsSync29(dir) ? dir : null;
|
|
12671
12937
|
}
|
|
12938
|
+
function transcriptFiles(projectsDir) {
|
|
12939
|
+
let relativeFiles = [];
|
|
12940
|
+
try {
|
|
12941
|
+
relativeFiles = readdirSync6(projectsDir, { recursive: true, encoding: "utf-8" });
|
|
12942
|
+
} catch {
|
|
12943
|
+
return [];
|
|
12944
|
+
}
|
|
12945
|
+
return relativeFiles.filter((file) => file.endsWith(".jsonl")).map((file) => {
|
|
12946
|
+
const parts = file.split(sep2);
|
|
12947
|
+
const subagentsIndex = parts.lastIndexOf("subagents");
|
|
12948
|
+
return {
|
|
12949
|
+
filePath: join27(projectsDir, file),
|
|
12950
|
+
sessionId: basename2(file, ".jsonl"),
|
|
12951
|
+
parentSessionId: subagentsIndex > 0 ? parts[subagentsIndex - 1] : void 0
|
|
12952
|
+
};
|
|
12953
|
+
});
|
|
12954
|
+
}
|
|
12672
12955
|
function repoName() {
|
|
12673
12956
|
try {
|
|
12674
12957
|
const url = execSync6("git config --get remote.origin.url", { encoding: "utf-8" }).trim();
|
|
@@ -12705,8 +12988,18 @@ function extractToolResultText(content, e) {
|
|
|
12705
12988
|
}
|
|
12706
12989
|
return t;
|
|
12707
12990
|
}
|
|
12708
|
-
function parseSession(
|
|
12709
|
-
const
|
|
12991
|
+
function parseSession(file, seenStableIds) {
|
|
12992
|
+
const { filePath, sessionId, parentSessionId } = file;
|
|
12993
|
+
const transcript = readFileSync27(filePath, "utf-8");
|
|
12994
|
+
const lines = transcript.split("\n").filter(Boolean);
|
|
12995
|
+
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
12996
|
+
transcript,
|
|
12997
|
+
statSync4(filePath).mtime.toISOString().slice(0, 10),
|
|
12998
|
+
{
|
|
12999
|
+
seenStableIds,
|
|
13000
|
+
sourceId: parentSessionId ? `${parentSessionId}:subagent:${sessionId}` : sessionId
|
|
13001
|
+
}
|
|
13002
|
+
);
|
|
12710
13003
|
const messages = [];
|
|
12711
13004
|
const actions = [];
|
|
12712
13005
|
let step = 0;
|
|
@@ -12755,7 +13048,17 @@ function parseSession(filePath, sessionId) {
|
|
|
12755
13048
|
}
|
|
12756
13049
|
messages.push(msg);
|
|
12757
13050
|
}
|
|
12758
|
-
return {
|
|
13051
|
+
return {
|
|
13052
|
+
cc_session_id: sessionId,
|
|
13053
|
+
parent_session_id: parentSessionId,
|
|
13054
|
+
messages,
|
|
13055
|
+
actions,
|
|
13056
|
+
model: transcriptUsage?.model,
|
|
13057
|
+
session_usage: transcriptUsage?.usage,
|
|
13058
|
+
usage_rollups: transcriptUsage?.rollups ?? [],
|
|
13059
|
+
harness: "claude-code",
|
|
13060
|
+
usage_cumulative: true
|
|
13061
|
+
};
|
|
12759
13062
|
}
|
|
12760
13063
|
function ask2(q) {
|
|
12761
13064
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
@@ -12773,7 +13076,7 @@ async function importCommand() {
|
|
|
12773
13076
|
console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
|
|
12774
13077
|
return;
|
|
12775
13078
|
}
|
|
12776
|
-
const files =
|
|
13079
|
+
const files = transcriptFiles(dir);
|
|
12777
13080
|
if (!files.length) {
|
|
12778
13081
|
console.log("No sessions to import.");
|
|
12779
13082
|
return;
|
|
@@ -12786,7 +13089,8 @@ async function importCommand() {
|
|
|
12786
13089
|
return;
|
|
12787
13090
|
}
|
|
12788
13091
|
}
|
|
12789
|
-
const
|
|
13092
|
+
const seenStableIds = /* @__PURE__ */ new Set();
|
|
13093
|
+
const sessions = files.map((file) => parseSession(file, seenStableIds)).filter((s) => s.messages.length > 0);
|
|
12790
13094
|
const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
|
|
12791
13095
|
let ok = 0, fail = 0;
|
|
12792
13096
|
if (isCloud) {
|
|
@@ -12832,7 +13136,19 @@ async function importCommand() {
|
|
|
12832
13136
|
const r = await fetch(`http://127.0.0.1:${port}/api/ingest`, {
|
|
12833
13137
|
method: "POST",
|
|
12834
13138
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpJwt2}` },
|
|
12835
|
-
body: JSON.stringify({
|
|
13139
|
+
body: JSON.stringify({
|
|
13140
|
+
capture_type: "transcript_sync",
|
|
13141
|
+
session_id: s.cc_session_id,
|
|
13142
|
+
parent_session_id: s.parent_session_id,
|
|
13143
|
+
repo,
|
|
13144
|
+
messages: s.messages,
|
|
13145
|
+
actions: s.actions,
|
|
13146
|
+
model: s.model,
|
|
13147
|
+
session_usage: s.session_usage,
|
|
13148
|
+
usage_rollups: s.usage_rollups,
|
|
13149
|
+
harness: s.harness,
|
|
13150
|
+
usage_cumulative: true
|
|
13151
|
+
}),
|
|
12836
13152
|
// A full session can carry thousands of turns — 15s timed out mid-import.
|
|
12837
13153
|
signal: AbortSignal.timeout(12e4)
|
|
12838
13154
|
});
|
|
@@ -12861,6 +13177,7 @@ var init_import = __esm({
|
|
|
12861
13177
|
"cli/commands/import.ts"() {
|
|
12862
13178
|
"use strict";
|
|
12863
13179
|
init_stub();
|
|
13180
|
+
init_claudeTranscriptUsage();
|
|
12864
13181
|
CONFIG_PATH7 = join27(homedir28(), ".synkro", "config.env");
|
|
12865
13182
|
}
|
|
12866
13183
|
});
|
|
@@ -14233,7 +14550,7 @@ var subArgs = args.slice(1);
|
|
|
14233
14550
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
14234
14551
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
14235
14552
|
function printVersion() {
|
|
14236
|
-
console.log("1.7.
|
|
14553
|
+
console.log("1.7.95");
|
|
14237
14554
|
}
|
|
14238
14555
|
function printHelp2() {
|
|
14239
14556
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|