@synkro-sh/cli 1.7.86 → 1.7.88
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 +808 -552
- 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.88";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -2500,7 +2500,7 @@ function telemCreds(): TelemCreds {
|
|
|
2500
2500
|
}
|
|
2501
2501
|
|
|
2502
2502
|
interface HookEmitOpts {
|
|
2503
|
-
agent_kind?: 'claude_code' | 'cursor';
|
|
2503
|
+
agent_kind?: 'claude_code' | 'cursor' | 'codex';
|
|
2504
2504
|
cc_session_id?: string;
|
|
2505
2505
|
cc_tool_use_id?: string;
|
|
2506
2506
|
cc_model?: string;
|
|
@@ -5478,6 +5478,7 @@ __export(dockerInstall_exports, {
|
|
|
5478
5478
|
normalizeProvider: () => normalizeProvider,
|
|
5479
5479
|
poolLabel: () => poolLabel,
|
|
5480
5480
|
readContainerConfig: () => readContainerConfig,
|
|
5481
|
+
resolveConductorProvider: () => resolveConductorProvider,
|
|
5481
5482
|
resolveGraderPool: () => resolveGraderPool,
|
|
5482
5483
|
resolveWorkerConfig: () => resolveWorkerConfig,
|
|
5483
5484
|
splitWorkers: () => splitWorkers,
|
|
@@ -5488,21 +5489,35 @@ import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync12, rea
|
|
|
5488
5489
|
import { homedir as homedir16 } from "os";
|
|
5489
5490
|
import { join as join14 } from "path";
|
|
5490
5491
|
import { execSync as execSync3, spawnSync as spawnSync3 } from "child_process";
|
|
5492
|
+
function resolveConductorProvider(pool, counts) {
|
|
5493
|
+
if (pool !== "auto") return pool;
|
|
5494
|
+
const ranked = [
|
|
5495
|
+
["claude_code", counts.claudeWorkers],
|
|
5496
|
+
["cursor", counts.cursorWorkers],
|
|
5497
|
+
["codex", counts.codexWorkers]
|
|
5498
|
+
];
|
|
5499
|
+
return ranked.reduce((best, candidate) => candidate[1] > best[1] ? candidate : best, ranked[0])[0];
|
|
5500
|
+
}
|
|
5491
5501
|
function splitWorkers(total, providers) {
|
|
5492
5502
|
const t = Math.max(0, Math.floor(total));
|
|
5493
|
-
const
|
|
5494
|
-
const
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5503
|
+
const selected = ["claude_code", "cursor", "codex"].filter((provider) => providers.includes(provider));
|
|
5504
|
+
const active = selected.length > 0 ? selected : ["claude_code"];
|
|
5505
|
+
const base = Math.floor(t / active.length);
|
|
5506
|
+
let remainder = t % active.length;
|
|
5507
|
+
const counts = { claudeWorkers: 0, cursorWorkers: 0, codexWorkers: 0 };
|
|
5508
|
+
for (const provider of active) {
|
|
5509
|
+
const count = base + (remainder-- > 0 ? 1 : 0);
|
|
5510
|
+
if (provider === "claude_code") counts.claudeWorkers = count;
|
|
5511
|
+
else if (provider === "cursor") counts.cursorWorkers = count;
|
|
5512
|
+
else counts.codexWorkers = count;
|
|
5513
|
+
}
|
|
5514
|
+
return counts;
|
|
5501
5515
|
}
|
|
5502
5516
|
function normalizeProvider(p) {
|
|
5503
5517
|
const v = p.trim().toLowerCase();
|
|
5504
5518
|
if (v === "claude" || v === "claude-code" || v === "claude_code" || v === "cc") return "claude_code";
|
|
5505
5519
|
if (v === "cursor") return "cursor";
|
|
5520
|
+
if (v === "codex" || v === "openai-codex" || v === "openai_codex") return "codex";
|
|
5506
5521
|
return null;
|
|
5507
5522
|
}
|
|
5508
5523
|
function resolveGraderPool(parsed) {
|
|
@@ -5516,8 +5531,9 @@ function resolveGraderPool(parsed) {
|
|
|
5516
5531
|
const np = normalizeProvider(k);
|
|
5517
5532
|
if (np === "claude_code") counts.claude_code = Math.max(0, Math.floor(v));
|
|
5518
5533
|
else if (np === "cursor") counts.cursor = Math.max(0, Math.floor(v));
|
|
5534
|
+
else if (np === "codex") counts.codex = Math.max(0, Math.floor(v));
|
|
5519
5535
|
else if (flagUnknown) {
|
|
5520
|
-
warnings.push(`synkro.toml: ${where}.${k} is not a known provider (use claude-code or
|
|
5536
|
+
warnings.push(`synkro.toml: ${where}.${k} is not a known provider (use claude-code, cursor, or codex) \u2014 ignored`);
|
|
5521
5537
|
}
|
|
5522
5538
|
}
|
|
5523
5539
|
};
|
|
@@ -5528,10 +5544,10 @@ function resolveGraderPool(parsed) {
|
|
|
5528
5544
|
if (rawPool != null && rawPool !== "auto") {
|
|
5529
5545
|
const np = normalizeProvider(String(rawPool));
|
|
5530
5546
|
if (np) pool = np;
|
|
5531
|
-
else warnings.push(`synkro.toml: grader.pool="${rawPool}" is not recognized (use auto | claude-code | cursor) \u2014 falling back to auto`);
|
|
5547
|
+
else warnings.push(`synkro.toml: grader.pool="${rawPool}" is not recognized (use auto | claude-code | cursor | codex) \u2014 falling back to auto`);
|
|
5532
5548
|
}
|
|
5533
|
-
if (counts.claude_code != null || counts.cursor != null) {
|
|
5534
|
-
return { claudeWorkers: counts.claude_code, cursorWorkers: counts.cursor, pool, warnings };
|
|
5549
|
+
if (counts.claude_code != null || counts.cursor != null || counts.codex != null) {
|
|
5550
|
+
return { claudeWorkers: counts.claude_code, cursorWorkers: counts.cursor, codexWorkers: counts.codex, pool, warnings };
|
|
5535
5551
|
}
|
|
5536
5552
|
return { pool, warnings };
|
|
5537
5553
|
}
|
|
@@ -5624,21 +5640,29 @@ function resolveWorkerConfig(rest) {
|
|
|
5624
5640
|
if (provs.length === 0) {
|
|
5625
5641
|
const sc = readSynkroFileConfig();
|
|
5626
5642
|
for (const w of sc.warnings) console.warn(` \u26A0 ${w}`);
|
|
5627
|
-
if (sc.claudeWorkers != null || sc.cursorWorkers != null) {
|
|
5643
|
+
if (sc.claudeWorkers != null || sc.cursorWorkers != null || sc.codexWorkers != null) {
|
|
5628
5644
|
const cw = sc.claudeWorkers || 0;
|
|
5629
5645
|
const curw = sc.cursorWorkers || 0;
|
|
5630
|
-
|
|
5646
|
+
const codw = sc.codexWorkers || 0;
|
|
5647
|
+
if (cw + curw + codw > 0) {
|
|
5648
|
+
const counts2 = { claudeWorkers: cw, cursorWorkers: curw, codexWorkers: codw };
|
|
5649
|
+
return { ...counts2, conductorProvider: resolveConductorProvider(sc.pool, counts2), explicit };
|
|
5650
|
+
}
|
|
5631
5651
|
}
|
|
5632
5652
|
if (sc.pool === "cursor") {
|
|
5633
5653
|
provs = ["cursor"];
|
|
5634
5654
|
} else if (sc.pool === "claude_code") {
|
|
5635
5655
|
provs = ["claude_code"];
|
|
5656
|
+
} else if (sc.pool === "codex") {
|
|
5657
|
+
provs = ["codex"];
|
|
5636
5658
|
} else {
|
|
5637
5659
|
provs = detectAgents().map((a) => a.kind);
|
|
5638
5660
|
if (provs.length === 0) provs = ["claude_code"];
|
|
5639
5661
|
}
|
|
5640
5662
|
}
|
|
5641
|
-
|
|
5663
|
+
const counts = splitWorkers(workers, provs);
|
|
5664
|
+
const pool = provs.length === 1 ? provs[0] : "auto";
|
|
5665
|
+
return { ...counts, conductorProvider: resolveConductorProvider(pool, counts), explicit };
|
|
5642
5666
|
}
|
|
5643
5667
|
function imageTag() {
|
|
5644
5668
|
const registry = process.env.SYNKRO_IMAGE_REGISTRY || "";
|
|
@@ -5710,7 +5734,19 @@ async function dockerInstall(opts = {}) {
|
|
|
5710
5734
|
const image = imageTag();
|
|
5711
5735
|
const claudeWorkers = opts.claudeWorkers ?? opts.workersPerPool ?? 8;
|
|
5712
5736
|
const cursorWorkers = opts.cursorWorkers ?? 0;
|
|
5713
|
-
const
|
|
5737
|
+
const codexWorkers = opts.codexWorkers ?? 0;
|
|
5738
|
+
const totalWorkers = claudeWorkers + cursorWorkers + codexWorkers;
|
|
5739
|
+
const workerCounts = { claudeWorkers, cursorWorkers, codexWorkers };
|
|
5740
|
+
const conductorProvider = opts.conductorProvider ?? resolveConductorProvider("auto", workerCounts);
|
|
5741
|
+
const usesClaude = claudeWorkers > 0 || conductorProvider === "claude_code";
|
|
5742
|
+
const usesCursor = cursorWorkers > 0 || conductorProvider === "cursor";
|
|
5743
|
+
const usesCodex = codexWorkers > 0 || conductorProvider === "codex";
|
|
5744
|
+
const codexHomeDir = opts.codexHomeDir ?? join14(SYNKRO_DIR7, "codex-local-session");
|
|
5745
|
+
if (usesCodex && !existsSync19(join14(codexHomeDir, "auth.json"))) {
|
|
5746
|
+
throw new DockerInstallError(
|
|
5747
|
+
"Codex grader credentials are missing. Re-run `synkro install` to authorize an isolated Codex session."
|
|
5748
|
+
);
|
|
5749
|
+
}
|
|
5714
5750
|
mkdirSync12(PGDATA_PATH, { recursive: true });
|
|
5715
5751
|
mkdirSync12(BACKUP_DIR, { recursive: true });
|
|
5716
5752
|
mkdirSync12(CLAUDE_HOST_STATE_DIR, { recursive: true });
|
|
@@ -5727,14 +5763,14 @@ async function dockerInstall(opts = {}) {
|
|
|
5727
5763
|
if (needsKeychainBridge()) {
|
|
5728
5764
|
const claudeCredsPath = exportKeychainCreds();
|
|
5729
5765
|
if (!claudeCredsPath) {
|
|
5730
|
-
if (
|
|
5766
|
+
if (usesClaude) {
|
|
5731
5767
|
throw new DockerInstallError(
|
|
5732
5768
|
"Claude Code keychain entry not found. Run `claude login` (or open Claude Code and sign in) before installing the container."
|
|
5733
5769
|
);
|
|
5734
5770
|
}
|
|
5735
5771
|
console.warn(" \u26A0 Claude Code keychain entry not found \u2014 live usage telemetry stays off until you sign in to Claude Code (grading is unaffected).");
|
|
5736
5772
|
}
|
|
5737
|
-
if (
|
|
5773
|
+
if (usesCursor && !cursorApiKeyConfigured()) {
|
|
5738
5774
|
console.warn(" \u26A0 No Cursor API key found \u2014 Cursor grader workers will be idle.");
|
|
5739
5775
|
console.warn(" Generate a key at cursor.com \u2192 Settings \u2192 API Keys, then:");
|
|
5740
5776
|
console.warn(` echo 'YOUR_KEY' > ~/.synkro/cursor-creds/api-key && chmod 600 ~/.synkro/cursor-creds/api-key`);
|
|
@@ -5805,13 +5841,18 @@ async function dockerInstall(opts = {}) {
|
|
|
5805
5841
|
`${CLAUDE_HOST_STATE_DIR}:/data/claude-host-state:ro`,
|
|
5806
5842
|
// Cursor creds — mounted RW so the in-container refresher can rotate the
|
|
5807
5843
|
// access token in place. Only mounted when the install includes Cursor.
|
|
5808
|
-
...
|
|
5844
|
+
...usesCursor ? ["-v", `${CURSOR_CREDS_DIR}:/home/synkro/.cursor-creds:rw`] : [],
|
|
5845
|
+
...usesCodex ? ["-v", `${codexHomeDir}:/home/synkro/.codex:rw`] : [],
|
|
5809
5846
|
"-e",
|
|
5810
5847
|
`WORKERS_PER_POOL=${totalWorkers}`,
|
|
5811
5848
|
"-e",
|
|
5812
5849
|
`CLAUDE_WORKERS=${claudeWorkers}`,
|
|
5813
5850
|
"-e",
|
|
5814
5851
|
`CURSOR_WORKERS=${cursorWorkers}`,
|
|
5852
|
+
"-e",
|
|
5853
|
+
`CODEX_WORKERS=${codexWorkers}`,
|
|
5854
|
+
"-e",
|
|
5855
|
+
`SYNKRO_CONDUCTOR_PROVIDER=${conductorProvider}`,
|
|
5815
5856
|
// Pass through the batch-size lever if the operator set it. Defaults
|
|
5816
5857
|
// inside the container to 5; clamped to [1, 20] by synkro-server.ts.
|
|
5817
5858
|
...process.env.SYNKRO_MAX_BATCH_SIZE ? ["-e", `SYNKRO_MAX_BATCH_SIZE=${process.env.SYNKRO_MAX_BATCH_SIZE}`] : [],
|
|
@@ -5929,6 +5970,8 @@ function readContainerConfig() {
|
|
|
5929
5970
|
return {
|
|
5930
5971
|
claudeWorkers: num(get("CLAUDE_WORKERS")),
|
|
5931
5972
|
cursorWorkers: num(get("CURSOR_WORKERS")),
|
|
5973
|
+
codexWorkers: num(get("CODEX_WORKERS")),
|
|
5974
|
+
conductorProvider: normalizeProvider(get("SYNKRO_CONDUCTOR_PROVIDER") || "") || void 0,
|
|
5932
5975
|
connectedRepo: get("SYNKRO_CONNECTED_REPO") || void 0
|
|
5933
5976
|
};
|
|
5934
5977
|
}
|
|
@@ -6165,6 +6208,119 @@ var init_setupToken = __esm({
|
|
|
6165
6208
|
}
|
|
6166
6209
|
});
|
|
6167
6210
|
|
|
6211
|
+
// cli/local-cc/codexCloudSetup.ts
|
|
6212
|
+
import { spawn as nodeSpawn2, spawnSync as spawnSync4 } from "child_process";
|
|
6213
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13, rmSync, existsSync as existsSync20 } from "fs";
|
|
6214
|
+
import { homedir as homedir18 } from "os";
|
|
6215
|
+
import { join as join16 } from "path";
|
|
6216
|
+
function findCodexBinary() {
|
|
6217
|
+
if (process.env.SYNKRO_CODEX_BIN) return process.env.SYNKRO_CODEX_BIN;
|
|
6218
|
+
const r = spawnSync4("which", ["codex"], { encoding: "utf-8" });
|
|
6219
|
+
const p = (r.stdout || "").trim().split("\n")[0];
|
|
6220
|
+
return p || null;
|
|
6221
|
+
}
|
|
6222
|
+
function runCodexLogin(codexBin, codexHome) {
|
|
6223
|
+
mkdirSync13(codexHome, { recursive: true, mode: 448 });
|
|
6224
|
+
writeFileSync14(join16(codexHome, "config.toml"), 'cli_auth_credentials_store = "file"\n', { mode: 384 });
|
|
6225
|
+
return new Promise((resolve6, reject) => {
|
|
6226
|
+
const proc = nodeSpawn2(codexBin, ["login"], {
|
|
6227
|
+
stdio: "inherit",
|
|
6228
|
+
env: { ...process.env, CODEX_HOME: codexHome }
|
|
6229
|
+
});
|
|
6230
|
+
proc.on("error", (err) => reject(new Error(`failed to spawn codex login: ${err.message}`)));
|
|
6231
|
+
proc.on("close", (code) => code === 0 ? resolve6() : reject(new Error(`codex login exited with code ${code}`)));
|
|
6232
|
+
});
|
|
6233
|
+
}
|
|
6234
|
+
async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
|
|
6235
|
+
try {
|
|
6236
|
+
const status = await fetch(`${gatewayUrl.replace(/\/+$/, "")}/codex/status`, {
|
|
6237
|
+
headers: { Authorization: `Bearer ${bearerToken}` }
|
|
6238
|
+
});
|
|
6239
|
+
const body = await status.json().catch(() => ({}));
|
|
6240
|
+
if (status.ok && body.connected) {
|
|
6241
|
+
onStatus?.("\u2713 Existing Codex cloud session is connected.");
|
|
6242
|
+
return { ok: true };
|
|
6243
|
+
}
|
|
6244
|
+
} catch {
|
|
6245
|
+
}
|
|
6246
|
+
const codexBin = findCodexBinary();
|
|
6247
|
+
if (!codexBin) {
|
|
6248
|
+
return { ok: false, error: "Codex CLI not found on PATH. Install Codex (https://developers.openai.com/codex), then re-run `synkro install`. (Set SYNKRO_CODEX_BIN to override the binary path.)" };
|
|
6249
|
+
}
|
|
6250
|
+
onStatus?.("Opening your browser to authorize your Codex subscription for Synkro Cloud\u2026");
|
|
6251
|
+
try {
|
|
6252
|
+
await runCodexLogin(codexBin, CODEX_CLOUD_HOME);
|
|
6253
|
+
} catch (e) {
|
|
6254
|
+
return { ok: false, error: `Codex login failed: ${e.message}` };
|
|
6255
|
+
}
|
|
6256
|
+
const authPath = join16(CODEX_CLOUD_HOME, "auth.json");
|
|
6257
|
+
if (!existsSync20(authPath)) {
|
|
6258
|
+
return { ok: false, error: "codex login completed but no auth.json was written \u2014 did the browser approval finish?" };
|
|
6259
|
+
}
|
|
6260
|
+
let auth;
|
|
6261
|
+
try {
|
|
6262
|
+
auth = JSON.parse(readFileSync18(authPath, "utf-8"));
|
|
6263
|
+
} catch (e) {
|
|
6264
|
+
return { ok: false, error: `could not read codex auth.json: ${e.message}` };
|
|
6265
|
+
}
|
|
6266
|
+
if (!auth || typeof auth !== "object" || !auth.tokens?.refresh_token) {
|
|
6267
|
+
return { ok: false, error: "codex auth.json has no refresh token \u2014 cloud refresh would fail. Re-run login." };
|
|
6268
|
+
}
|
|
6269
|
+
onStatus?.("Connecting your Codex session to Synkro Cloud\u2026");
|
|
6270
|
+
let resp;
|
|
6271
|
+
try {
|
|
6272
|
+
resp = await fetch(`${gatewayUrl.replace(/\/+$/, "")}/codex/seed`, {
|
|
6273
|
+
method: "POST",
|
|
6274
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${bearerToken}` },
|
|
6275
|
+
body: JSON.stringify({ auth })
|
|
6276
|
+
});
|
|
6277
|
+
} catch (e) {
|
|
6278
|
+
return { ok: false, error: `failed to reach Synkro API: ${e.message}` };
|
|
6279
|
+
}
|
|
6280
|
+
if (!resp.ok) {
|
|
6281
|
+
const detail = await resp.text().catch(() => "");
|
|
6282
|
+
return { ok: false, error: `seed rejected (${resp.status}): ${detail.slice(0, 200)}` };
|
|
6283
|
+
}
|
|
6284
|
+
try {
|
|
6285
|
+
rmSync(CODEX_CLOUD_HOME, { recursive: true, force: true });
|
|
6286
|
+
} catch {
|
|
6287
|
+
}
|
|
6288
|
+
onStatus?.("\u2713 Codex subscription connected to Synkro Cloud.");
|
|
6289
|
+
return { ok: true };
|
|
6290
|
+
}
|
|
6291
|
+
async function setupCodexLocal(onStatus) {
|
|
6292
|
+
const codexBin = findCodexBinary();
|
|
6293
|
+
if (!codexBin) {
|
|
6294
|
+
return { ok: false, error: "Codex CLI not found on PATH. Install Codex, then re-run `synkro install`." };
|
|
6295
|
+
}
|
|
6296
|
+
const authPath = join16(CODEX_LOCAL_HOME, "auth.json");
|
|
6297
|
+
if (!existsSync20(authPath)) {
|
|
6298
|
+
onStatus?.("Opening your browser to authorize an isolated Codex session for the local grader\u2026");
|
|
6299
|
+
try {
|
|
6300
|
+
await runCodexLogin(codexBin, CODEX_LOCAL_HOME);
|
|
6301
|
+
} catch (e) {
|
|
6302
|
+
return { ok: false, error: `Codex login failed: ${e.message}` };
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
try {
|
|
6306
|
+
const auth = JSON.parse(readFileSync18(authPath, "utf-8"));
|
|
6307
|
+
if (!auth.tokens?.refresh_token) throw new Error("auth.json has no refresh token");
|
|
6308
|
+
} catch (e) {
|
|
6309
|
+
return { ok: false, error: `Codex local grader auth is invalid: ${e.message}` };
|
|
6310
|
+
}
|
|
6311
|
+
onStatus?.("\u2713 Codex session ready for the local grader.");
|
|
6312
|
+
return { ok: true, home: CODEX_LOCAL_HOME };
|
|
6313
|
+
}
|
|
6314
|
+
var SYNKRO_DIR9, CODEX_CLOUD_HOME, CODEX_LOCAL_HOME;
|
|
6315
|
+
var init_codexCloudSetup = __esm({
|
|
6316
|
+
"cli/local-cc/codexCloudSetup.ts"() {
|
|
6317
|
+
"use strict";
|
|
6318
|
+
SYNKRO_DIR9 = join16(homedir18(), ".synkro");
|
|
6319
|
+
CODEX_CLOUD_HOME = join16(SYNKRO_DIR9, "codex-cloud-session");
|
|
6320
|
+
CODEX_LOCAL_HOME = join16(SYNKRO_DIR9, "codex-local-session");
|
|
6321
|
+
}
|
|
6322
|
+
});
|
|
6323
|
+
|
|
6168
6324
|
// cli/local-cc/ptyShim.ts
|
|
6169
6325
|
var ptyShim_exports = {};
|
|
6170
6326
|
__export(ptyShim_exports, {
|
|
@@ -6181,26 +6337,26 @@ __export(ptyShim_exports, {
|
|
|
6181
6337
|
uninstallPtyShim: () => uninstallPtyShim
|
|
6182
6338
|
});
|
|
6183
6339
|
import {
|
|
6184
|
-
existsSync as
|
|
6185
|
-
mkdirSync as
|
|
6186
|
-
writeFileSync as
|
|
6340
|
+
existsSync as existsSync21,
|
|
6341
|
+
mkdirSync as mkdirSync14,
|
|
6342
|
+
writeFileSync as writeFileSync15,
|
|
6187
6343
|
chmodSync as chmodSync3,
|
|
6188
|
-
readFileSync as
|
|
6189
|
-
rmSync,
|
|
6344
|
+
readFileSync as readFileSync19,
|
|
6345
|
+
rmSync as rmSync2,
|
|
6190
6346
|
realpathSync,
|
|
6191
6347
|
symlinkSync,
|
|
6192
6348
|
lstatSync,
|
|
6193
6349
|
readdirSync as readdirSync3
|
|
6194
6350
|
} from "fs";
|
|
6195
|
-
import { homedir as
|
|
6196
|
-
import { join as
|
|
6197
|
-
import { spawnSync as
|
|
6351
|
+
import { homedir as homedir19 } from "os";
|
|
6352
|
+
import { join as join17 } from "path";
|
|
6353
|
+
import { spawnSync as spawnSync5, spawn as spawn3 } from "child_process";
|
|
6198
6354
|
function rcFiles() {
|
|
6199
|
-
const h =
|
|
6200
|
-
return [
|
|
6355
|
+
const h = homedir19();
|
|
6356
|
+
return [join17(h, ".zshrc"), join17(h, ".bashrc"), join17(h, ".bash_profile")];
|
|
6201
6357
|
}
|
|
6202
6358
|
function resolveRealClaude() {
|
|
6203
|
-
const r =
|
|
6359
|
+
const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
|
|
6204
6360
|
const p = (r.stdout || "").trim();
|
|
6205
6361
|
if (p) {
|
|
6206
6362
|
try {
|
|
@@ -6209,8 +6365,8 @@ function resolveRealClaude() {
|
|
|
6209
6365
|
return p;
|
|
6210
6366
|
}
|
|
6211
6367
|
}
|
|
6212
|
-
for (const c of [
|
|
6213
|
-
if (
|
|
6368
|
+
for (const c of [join17(homedir19(), ".local", "bin", "claude"), "/usr/local/bin/claude", "/opt/homebrew/bin/claude"]) {
|
|
6369
|
+
if (existsSync21(c)) {
|
|
6214
6370
|
try {
|
|
6215
6371
|
return realpathSync(c);
|
|
6216
6372
|
} catch {
|
|
@@ -6221,11 +6377,11 @@ function resolveRealClaude() {
|
|
|
6221
6377
|
return "claude";
|
|
6222
6378
|
}
|
|
6223
6379
|
function findClaudeLink() {
|
|
6224
|
-
const r =
|
|
6380
|
+
const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
|
|
6225
6381
|
let linkPath = (r.stdout || "").trim();
|
|
6226
6382
|
if (!linkPath) {
|
|
6227
|
-
const c =
|
|
6228
|
-
if (
|
|
6383
|
+
const c = join17(homedir19(), ".local", "bin", "claude");
|
|
6384
|
+
if (existsSync21(c)) linkPath = c;
|
|
6229
6385
|
else return null;
|
|
6230
6386
|
}
|
|
6231
6387
|
if (linkPath === SHIM_PATH) return null;
|
|
@@ -6238,7 +6394,7 @@ function findClaudeLink() {
|
|
|
6238
6394
|
}
|
|
6239
6395
|
function isOurShim(path) {
|
|
6240
6396
|
try {
|
|
6241
|
-
return
|
|
6397
|
+
return readFileSync19(path, "utf-8").slice(0, 300).includes("Synkro pty shim");
|
|
6242
6398
|
} catch {
|
|
6243
6399
|
return false;
|
|
6244
6400
|
}
|
|
@@ -6251,7 +6407,7 @@ function shadowClaude() {
|
|
|
6251
6407
|
}
|
|
6252
6408
|
const { linkPath, realTarget } = found;
|
|
6253
6409
|
if (isOurShim(linkPath)) {
|
|
6254
|
-
console.log(` \xB7 ${linkPath.replace(
|
|
6410
|
+
console.log(` \xB7 ${linkPath.replace(homedir19(), "~")} already shadowed`);
|
|
6255
6411
|
return;
|
|
6256
6412
|
}
|
|
6257
6413
|
let wasSymlink = false;
|
|
@@ -6261,14 +6417,14 @@ function shadowClaude() {
|
|
|
6261
6417
|
}
|
|
6262
6418
|
const state = { linkPath, realTarget, wasSymlink };
|
|
6263
6419
|
try {
|
|
6264
|
-
|
|
6420
|
+
writeFileSync15(SHADOW_STATE_FILE, JSON.stringify(state), "utf-8");
|
|
6265
6421
|
} catch {
|
|
6266
6422
|
}
|
|
6267
6423
|
try {
|
|
6268
|
-
|
|
6269
|
-
|
|
6424
|
+
rmSync2(linkPath, { force: true });
|
|
6425
|
+
writeFileSync15(linkPath, SHIM_SOURCE.replace("__BAKED_CLAUDE__", realTarget), "utf-8");
|
|
6270
6426
|
chmodSync3(linkPath, 493);
|
|
6271
|
-
console.log(` \u2713 shadowed ${linkPath.replace(
|
|
6427
|
+
console.log(` \u2713 shadowed ${linkPath.replace(homedir19(), "~")} \u2192 shim (real: ${realTarget.replace(homedir19(), "~")})`);
|
|
6272
6428
|
} catch (e) {
|
|
6273
6429
|
console.warn(` \u26A0 could not shadow ${linkPath}: ${e.message}`);
|
|
6274
6430
|
}
|
|
@@ -6276,16 +6432,16 @@ function shadowClaude() {
|
|
|
6276
6432
|
function unshadowClaude() {
|
|
6277
6433
|
let state;
|
|
6278
6434
|
try {
|
|
6279
|
-
state = JSON.parse(
|
|
6435
|
+
state = JSON.parse(readFileSync19(SHADOW_STATE_FILE, "utf-8"));
|
|
6280
6436
|
} catch {
|
|
6281
6437
|
return;
|
|
6282
6438
|
}
|
|
6283
6439
|
if (!state?.linkPath) return;
|
|
6284
6440
|
try {
|
|
6285
|
-
if (
|
|
6286
|
-
|
|
6441
|
+
if (existsSync21(state.linkPath) && !isOurShim(state.linkPath)) return;
|
|
6442
|
+
rmSync2(state.linkPath, { force: true });
|
|
6287
6443
|
symlinkSync(state.realTarget, state.linkPath);
|
|
6288
|
-
console.log(`\u2713 restored ${state.linkPath.replace(
|
|
6444
|
+
console.log(`\u2713 restored ${state.linkPath.replace(homedir19(), "~")} \u2192 ${state.realTarget.replace(homedir19(), "~")}`);
|
|
6289
6445
|
} catch (e) {
|
|
6290
6446
|
console.warn(` \u26A0 could not restore claude: ${e.message} \u2014 run: ln -sf ${state.realTarget} ${state.linkPath}`);
|
|
6291
6447
|
}
|
|
@@ -6293,16 +6449,16 @@ function unshadowClaude() {
|
|
|
6293
6449
|
function addPathBlock() {
|
|
6294
6450
|
const touched = [];
|
|
6295
6451
|
for (const rc of rcFiles()) {
|
|
6296
|
-
if (!
|
|
6452
|
+
if (!existsSync21(rc) && rc.endsWith(".bash_profile")) continue;
|
|
6297
6453
|
let body = "";
|
|
6298
6454
|
try {
|
|
6299
|
-
body =
|
|
6455
|
+
body = readFileSync19(rc, "utf-8");
|
|
6300
6456
|
} catch {
|
|
6301
6457
|
}
|
|
6302
6458
|
const cleaned = stripBlock(body);
|
|
6303
6459
|
const next = cleaned.replace(/\n*$/, "") + (cleaned ? "\n\n" : "") + RC_BLOCK + "\n";
|
|
6304
6460
|
try {
|
|
6305
|
-
|
|
6461
|
+
writeFileSync15(rc, next, "utf-8");
|
|
6306
6462
|
touched.push(rc);
|
|
6307
6463
|
} catch {
|
|
6308
6464
|
}
|
|
@@ -6318,15 +6474,15 @@ function escapeRe(s) {
|
|
|
6318
6474
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6319
6475
|
}
|
|
6320
6476
|
function installPtyShim() {
|
|
6321
|
-
|
|
6322
|
-
|
|
6477
|
+
mkdirSync14(SHIM_BIN_DIR, { recursive: true });
|
|
6478
|
+
mkdirSync14(PTY_STATE_DIR, { recursive: true });
|
|
6323
6479
|
const real = resolveRealClaude();
|
|
6324
|
-
|
|
6480
|
+
writeFileSync15(SHIM_PATH, SHIM_SOURCE.replace("__BAKED_CLAUDE__", real), "utf-8");
|
|
6325
6481
|
chmodSync3(SHIM_PATH, 493);
|
|
6326
6482
|
const touched = addPathBlock();
|
|
6327
6483
|
console.log(` \u2713 pty routing shim installed (real claude: ${real})`);
|
|
6328
6484
|
shadowClaude();
|
|
6329
|
-
if (touched.length) console.log(` added ~/.synkro/bin to PATH in: ${touched.map((t) => t.replace(
|
|
6485
|
+
if (touched.length) console.log(` added ~/.synkro/bin to PATH in: ${touched.map((t) => t.replace(homedir19(), "~")).join(", ")}`);
|
|
6330
6486
|
}
|
|
6331
6487
|
function uninstallPtyShim() {
|
|
6332
6488
|
try {
|
|
@@ -6334,30 +6490,30 @@ function uninstallPtyShim() {
|
|
|
6334
6490
|
} catch {
|
|
6335
6491
|
}
|
|
6336
6492
|
try {
|
|
6337
|
-
const ls =
|
|
6493
|
+
const ls = spawnSync5("tmux", ["ls", "-F", "#{session_name}"], { encoding: "utf-8" });
|
|
6338
6494
|
for (const s of (ls.stdout || "").split("\n")) {
|
|
6339
|
-
if (s.startsWith(SHIM_SESSION_PREFIX))
|
|
6495
|
+
if (s.startsWith(SHIM_SESSION_PREFIX)) spawnSync5("tmux", ["kill-session", "-t", `=${s}`], { encoding: "utf-8" });
|
|
6340
6496
|
}
|
|
6341
6497
|
} catch {
|
|
6342
6498
|
}
|
|
6343
6499
|
const cleaned = [];
|
|
6344
6500
|
for (const rc of rcFiles()) {
|
|
6345
|
-
if (!
|
|
6501
|
+
if (!existsSync21(rc)) continue;
|
|
6346
6502
|
try {
|
|
6347
|
-
const body =
|
|
6503
|
+
const body = readFileSync19(rc, "utf-8");
|
|
6348
6504
|
if (body.includes(RC_BEGIN)) {
|
|
6349
|
-
|
|
6350
|
-
cleaned.push(rc.replace(
|
|
6505
|
+
writeFileSync15(rc, stripBlock(body).replace(/\n{3,}/g, "\n\n"), "utf-8");
|
|
6506
|
+
cleaned.push(rc.replace(homedir19(), "~"));
|
|
6351
6507
|
}
|
|
6352
6508
|
} catch {
|
|
6353
6509
|
}
|
|
6354
6510
|
}
|
|
6355
6511
|
try {
|
|
6356
|
-
|
|
6512
|
+
rmSync2(SHIM_BIN_DIR, { recursive: true, force: true });
|
|
6357
6513
|
} catch {
|
|
6358
6514
|
}
|
|
6359
6515
|
try {
|
|
6360
|
-
|
|
6516
|
+
rmSync2(PTY_STATE_DIR, { recursive: true, force: true });
|
|
6361
6517
|
} catch {
|
|
6362
6518
|
}
|
|
6363
6519
|
console.log(`\u2713 removed pty routing shim${cleaned.length ? ` (cleaned PATH in: ${cleaned.join(", ")})` : ""}`);
|
|
@@ -6379,12 +6535,12 @@ function listSessions(opts = {}) {
|
|
|
6379
6535
|
const out = [];
|
|
6380
6536
|
for (const f of files) {
|
|
6381
6537
|
try {
|
|
6382
|
-
const r = JSON.parse(
|
|
6538
|
+
const r = JSON.parse(readFileSync19(join17(SESSIONS_DIR, f), "utf-8"));
|
|
6383
6539
|
if (!r || !r.session_id) continue;
|
|
6384
6540
|
if (liveOnly) {
|
|
6385
6541
|
const s = r.tmux_session;
|
|
6386
6542
|
if (!s || !isOurSession(s)) continue;
|
|
6387
|
-
if (
|
|
6543
|
+
if (spawnSync5("tmux", ["has-session", "-t", `=${s}`], { encoding: "utf-8" }).status !== 0) continue;
|
|
6388
6544
|
}
|
|
6389
6545
|
out.push(r);
|
|
6390
6546
|
} catch {
|
|
@@ -6398,7 +6554,7 @@ function currentTmuxSession() {
|
|
|
6398
6554
|
try {
|
|
6399
6555
|
const pane = process.env.TMUX_PANE || "";
|
|
6400
6556
|
const args2 = pane ? ["display-message", "-p", "-t", pane, "#{session_name}"] : ["display-message", "-p", "#{session_name}"];
|
|
6401
|
-
const r =
|
|
6557
|
+
const r = spawnSync5("tmux", args2, { encoding: "utf-8" });
|
|
6402
6558
|
if (r.status === 0) return (r.stdout || "").trim();
|
|
6403
6559
|
} catch {
|
|
6404
6560
|
}
|
|
@@ -6408,7 +6564,7 @@ function resolveTargetSession(override) {
|
|
|
6408
6564
|
const own = currentTmuxSession();
|
|
6409
6565
|
const fromFile = (() => {
|
|
6410
6566
|
try {
|
|
6411
|
-
return
|
|
6567
|
+
return readFileSync19(ACTIVE_SESSION_FILE, "utf-8").trim();
|
|
6412
6568
|
} catch {
|
|
6413
6569
|
return "";
|
|
6414
6570
|
}
|
|
@@ -6429,14 +6585,14 @@ function injectModel(model, sessionOverride) {
|
|
|
6429
6585
|
console.error(`synkro route: refusing suspicious session token: ${session}`);
|
|
6430
6586
|
return false;
|
|
6431
6587
|
}
|
|
6432
|
-
if (
|
|
6433
|
-
const sendKeys = (...a) =>
|
|
6588
|
+
if (spawnSync5("tmux", ["has-session", "-t", `=${session}`], { encoding: "utf-8" }).status !== 0) return false;
|
|
6589
|
+
const sendKeys = (...a) => spawnSync5("tmux", ["send-keys", "-t", session, ...a], { encoding: "utf-8" });
|
|
6434
6590
|
sendKeys("Escape");
|
|
6435
6591
|
sendKeys("-l", `/model ${model}`);
|
|
6436
6592
|
sendKeys("Enter");
|
|
6437
|
-
const pidFile =
|
|
6593
|
+
const pidFile = join17(PTY_STATE_DIR, `poll-${session}.pid`);
|
|
6438
6594
|
try {
|
|
6439
|
-
|
|
6595
|
+
mkdirSync14(PTY_STATE_DIR, { recursive: true });
|
|
6440
6596
|
} catch {
|
|
6441
6597
|
}
|
|
6442
6598
|
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`;
|
|
@@ -6444,17 +6600,17 @@ function injectModel(model, sessionOverride) {
|
|
|
6444
6600
|
child.unref();
|
|
6445
6601
|
return true;
|
|
6446
6602
|
}
|
|
6447
|
-
var
|
|
6603
|
+
var SYNKRO_DIR10, SHIM_BIN_DIR, SHIM_PATH, PTY_STATE_DIR, ACTIVE_SESSION_FILE, SHADOW_STATE_FILE, SESSIONS_DIR, SHIM_SESSION_PREFIX, RC_BEGIN, RC_END, RC_BLOCK, SHIM_SOURCE;
|
|
6448
6604
|
var init_ptyShim = __esm({
|
|
6449
6605
|
"cli/local-cc/ptyShim.ts"() {
|
|
6450
6606
|
"use strict";
|
|
6451
|
-
|
|
6452
|
-
SHIM_BIN_DIR =
|
|
6453
|
-
SHIM_PATH =
|
|
6454
|
-
PTY_STATE_DIR =
|
|
6455
|
-
ACTIVE_SESSION_FILE =
|
|
6456
|
-
SHADOW_STATE_FILE =
|
|
6457
|
-
SESSIONS_DIR =
|
|
6607
|
+
SYNKRO_DIR10 = join17(homedir19(), ".synkro");
|
|
6608
|
+
SHIM_BIN_DIR = join17(SYNKRO_DIR10, "bin");
|
|
6609
|
+
SHIM_PATH = join17(SHIM_BIN_DIR, "claude");
|
|
6610
|
+
PTY_STATE_DIR = join17(SYNKRO_DIR10, "pty");
|
|
6611
|
+
ACTIVE_SESSION_FILE = join17(PTY_STATE_DIR, "active");
|
|
6612
|
+
SHADOW_STATE_FILE = join17(PTY_STATE_DIR, "shadow.json");
|
|
6613
|
+
SESSIONS_DIR = join17(PTY_STATE_DIR, "sessions");
|
|
6458
6614
|
SHIM_SESSION_PREFIX = "synkro-cc-";
|
|
6459
6615
|
RC_BEGIN = "# >>> synkro pty shim (managed \u2014 do not edit) >>>";
|
|
6460
6616
|
RC_END = "# <<< synkro pty shim <<<";
|
|
@@ -6512,6 +6668,19 @@ exit $RC
|
|
|
6512
6668
|
}
|
|
6513
6669
|
});
|
|
6514
6670
|
|
|
6671
|
+
// cli/installer/graderSmoke.ts
|
|
6672
|
+
function isPrimerAckVerdict(value) {
|
|
6673
|
+
return /<category>\s*primer_ack\s*<\/category>/i.test(value) || /<reason>\s*(?:batch )?primer received\s*<\/reason>/i.test(value);
|
|
6674
|
+
}
|
|
6675
|
+
function isValidRiskyEditSmokeVerdict(value) {
|
|
6676
|
+
return /<synkro-verdict(?:\s[^>]*)?>[\s\S]*<\/synkro-verdict>/i.test(value) && /<ok>\s*false\s*<\/ok>/i.test(value) && !isPrimerAckVerdict(value);
|
|
6677
|
+
}
|
|
6678
|
+
var init_graderSmoke = __esm({
|
|
6679
|
+
"cli/installer/graderSmoke.ts"() {
|
|
6680
|
+
"use strict";
|
|
6681
|
+
}
|
|
6682
|
+
});
|
|
6683
|
+
|
|
6515
6684
|
// cli/commands/install.ts
|
|
6516
6685
|
var install_exports = {};
|
|
6517
6686
|
__export(install_exports, {
|
|
@@ -6527,9 +6696,9 @@ __export(install_exports, {
|
|
|
6527
6696
|
syncSkillFiles: () => syncSkillFiles,
|
|
6528
6697
|
writeHookScripts: () => writeHookScripts
|
|
6529
6698
|
});
|
|
6530
|
-
import { existsSync as
|
|
6531
|
-
import { homedir as
|
|
6532
|
-
import { join as
|
|
6699
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync15, writeFileSync as writeFileSync16, chmodSync as chmodSync4, readFileSync as readFileSync20, readdirSync as readdirSync4, unlinkSync as unlinkSync7, statSync as statSync2 } from "fs";
|
|
6700
|
+
import { homedir as homedir20 } from "os";
|
|
6701
|
+
import { join as join18, isAbsolute, resolve as resolve4 } from "path";
|
|
6533
6702
|
import { execSync as execSync4, spawn as spawn4 } from "child_process";
|
|
6534
6703
|
import { createInterface as createInterface2 } from "readline";
|
|
6535
6704
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -6625,7 +6794,7 @@ async function promptDeployLocation(current = "local") {
|
|
|
6625
6794
|
`Where should Synkro run?
|
|
6626
6795
|
local \u2014 a grading container on this machine (Docker)
|
|
6627
6796
|
cloud \u2014 a private container hosted by Synkro
|
|
6628
|
-
|
|
6797
|
+
Each worker uses the account credentials you authorize. Choose [${current}] / ${other}: `,
|
|
6629
6798
|
(answer) => {
|
|
6630
6799
|
rl.close();
|
|
6631
6800
|
const a = answer.trim().toLowerCase();
|
|
@@ -6656,38 +6825,38 @@ async function promptTranscriptSources(wantCC, wantCursor, wantCodex) {
|
|
|
6656
6825
|
}
|
|
6657
6826
|
}
|
|
6658
6827
|
function ensureSynkroDir() {
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6828
|
+
mkdirSync15(SYNKRO_DIR11, { recursive: true });
|
|
6829
|
+
mkdirSync15(HOOKS_DIR, { recursive: true });
|
|
6830
|
+
mkdirSync15(BIN_DIR, { recursive: true });
|
|
6831
|
+
mkdirSync15(OFFSETS_DIR, { recursive: true });
|
|
6832
|
+
mkdirSync15(join18(SYNKRO_DIR11, "sessions"), { recursive: true });
|
|
6664
6833
|
}
|
|
6665
6834
|
function writeHookScripts() {
|
|
6666
|
-
const installExtractCorePath =
|
|
6667
|
-
const bashScriptPath =
|
|
6668
|
-
const skillJudgeScriptPath =
|
|
6669
|
-
const cursorSkillJudgePath =
|
|
6670
|
-
const bashFollowupScriptPath =
|
|
6671
|
-
const editPrecheckScriptPath =
|
|
6672
|
-
const cwePrecheckScriptPath =
|
|
6673
|
-
const cvePrecheckScriptPath =
|
|
6674
|
-
const planJudgeScriptPath =
|
|
6675
|
-
const agentJudgeScriptPath =
|
|
6676
|
-
const stopSummaryScriptPath =
|
|
6677
|
-
const sessionStartScriptPath =
|
|
6678
|
-
const transcriptSyncScriptPath =
|
|
6679
|
-
const userPromptSubmitScriptPath =
|
|
6680
|
-
const promptRouteScriptPath =
|
|
6681
|
-
const commonScriptPath =
|
|
6682
|
-
const commonBashScriptPath =
|
|
6683
|
-
const installScanScriptPath =
|
|
6684
|
-
const cursorBashJudgePath =
|
|
6685
|
-
const cursorEditCapturePath =
|
|
6686
|
-
const cursorAgentCapturePath =
|
|
6687
|
-
const mcpStdioProxyPath =
|
|
6688
|
-
const taskActivateIntentScriptPath =
|
|
6689
|
-
const mcpGateScriptPath =
|
|
6690
|
-
const stubCommonPath =
|
|
6835
|
+
const installExtractCorePath = join18(HOOKS_DIR, "installExtractCore.ts");
|
|
6836
|
+
const bashScriptPath = join18(HOOKS_DIR, "cc-bash-judge.ts");
|
|
6837
|
+
const skillJudgeScriptPath = join18(HOOKS_DIR, "cc-skill-judge.ts");
|
|
6838
|
+
const cursorSkillJudgePath = join18(HOOKS_DIR, "cursor-skill-judge.ts");
|
|
6839
|
+
const bashFollowupScriptPath = join18(HOOKS_DIR, "cc-bash-followup.ts");
|
|
6840
|
+
const editPrecheckScriptPath = join18(HOOKS_DIR, "cc-edit-precheck.ts");
|
|
6841
|
+
const cwePrecheckScriptPath = join18(HOOKS_DIR, "cc-cwe-precheck.ts");
|
|
6842
|
+
const cvePrecheckScriptPath = join18(HOOKS_DIR, "cc-cve-precheck.ts");
|
|
6843
|
+
const planJudgeScriptPath = join18(HOOKS_DIR, "cc-plan-judge.ts");
|
|
6844
|
+
const agentJudgeScriptPath = join18(HOOKS_DIR, "cc-agent-judge.ts");
|
|
6845
|
+
const stopSummaryScriptPath = join18(HOOKS_DIR, "cc-stop-summary.ts");
|
|
6846
|
+
const sessionStartScriptPath = join18(HOOKS_DIR, "cc-session-start.ts");
|
|
6847
|
+
const transcriptSyncScriptPath = join18(HOOKS_DIR, "cc-transcript-sync.ts");
|
|
6848
|
+
const userPromptSubmitScriptPath = join18(HOOKS_DIR, "cc-user-prompt-submit.ts");
|
|
6849
|
+
const promptRouteScriptPath = join18(HOOKS_DIR, "cc-prompt-route.ts");
|
|
6850
|
+
const commonScriptPath = join18(HOOKS_DIR, "_synkro-common.ts");
|
|
6851
|
+
const commonBashScriptPath = join18(HOOKS_DIR, "_synkro-common.sh");
|
|
6852
|
+
const installScanScriptPath = join18(HOOKS_DIR, "cc-install-scan.ts");
|
|
6853
|
+
const cursorBashJudgePath = join18(HOOKS_DIR, "cursor-bash-judge.ts");
|
|
6854
|
+
const cursorEditCapturePath = join18(HOOKS_DIR, "cursor-edit-capture.ts");
|
|
6855
|
+
const cursorAgentCapturePath = join18(HOOKS_DIR, "cursor-agent-capture.ts");
|
|
6856
|
+
const mcpStdioProxyPath = join18(HOOKS_DIR, "mcp-stdio-proxy.ts");
|
|
6857
|
+
const taskActivateIntentScriptPath = join18(HOOKS_DIR, "cc-task-activate-intent.ts");
|
|
6858
|
+
const mcpGateScriptPath = join18(HOOKS_DIR, "cc-mcp-gate.ts");
|
|
6859
|
+
const stubCommonPath = join18(HOOKS_DIR, "_synkro-stub-common.ts");
|
|
6691
6860
|
const stubFiles = [
|
|
6692
6861
|
[stubCommonPath, STUB_COMMON_TS],
|
|
6693
6862
|
[bashScriptPath, STUB_BASH_JUDGE_TS],
|
|
@@ -6712,14 +6881,14 @@ function writeHookScripts() {
|
|
|
6712
6881
|
[cursorAgentCapturePath, STUB_CURSOR_AGENT_CAPTURE_TS]
|
|
6713
6882
|
];
|
|
6714
6883
|
for (const [p, content] of stubFiles) {
|
|
6715
|
-
|
|
6884
|
+
writeFileSync16(p, content, "utf-8");
|
|
6716
6885
|
chmodSync4(p, 493);
|
|
6717
6886
|
}
|
|
6718
|
-
|
|
6887
|
+
writeFileSync16(mcpStdioProxyPath, MCP_STDIO_PROXY_SRC, "utf-8");
|
|
6719
6888
|
chmodSync4(mcpStdioProxyPath, 493);
|
|
6720
6889
|
for (const stale of ["_synkro-common.ts", "_synkro-common.sh", "installExtractCore.ts"]) {
|
|
6721
6890
|
try {
|
|
6722
|
-
unlinkSync7(
|
|
6891
|
+
unlinkSync7(join18(HOOKS_DIR, stale));
|
|
6723
6892
|
} catch {
|
|
6724
6893
|
}
|
|
6725
6894
|
}
|
|
@@ -6755,11 +6924,11 @@ function shellQuoteSingle2(value) {
|
|
|
6755
6924
|
}
|
|
6756
6925
|
function resolveSynkroBundle() {
|
|
6757
6926
|
const scriptPath = process.argv[1];
|
|
6758
|
-
if (scriptPath &&
|
|
6927
|
+
if (scriptPath && existsSync22(scriptPath)) return scriptPath;
|
|
6759
6928
|
return null;
|
|
6760
6929
|
}
|
|
6761
6930
|
function writeConfigEnv(opts) {
|
|
6762
|
-
const credsPath =
|
|
6931
|
+
const credsPath = join18(SYNKRO_DIR11, "credentials.json");
|
|
6763
6932
|
const safeGateway = sanitizeConfigValue(opts.gatewayUrl);
|
|
6764
6933
|
const safeUserId = sanitizeConfigValue(opts.userId);
|
|
6765
6934
|
const safeOrgId = sanitizeConfigValue(opts.orgId);
|
|
@@ -6775,7 +6944,7 @@ function writeConfigEnv(opts) {
|
|
|
6775
6944
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
6776
6945
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
6777
6946
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
6778
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.
|
|
6947
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.88")}`
|
|
6779
6948
|
];
|
|
6780
6949
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
6781
6950
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -6795,12 +6964,12 @@ function writeConfigEnv(opts) {
|
|
|
6795
6964
|
lines.push(`SYNKRO_DEPLOY_LOCATION=${shellQuoteSingle2(sanitizeConfigValue(opts.deployLocation ?? "local", 16))}`);
|
|
6796
6965
|
lines.push(`SYNKRO_HOOK_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.hookMode ?? "stub", 8))}`);
|
|
6797
6966
|
lines.push("");
|
|
6798
|
-
|
|
6967
|
+
writeFileSync16(CONFIG_PATH4, lines.join("\n"), "utf-8");
|
|
6799
6968
|
chmodSync4(CONFIG_PATH4, 384);
|
|
6800
6969
|
}
|
|
6801
6970
|
function persistedTranscriptConsent(source) {
|
|
6802
6971
|
try {
|
|
6803
|
-
const env =
|
|
6972
|
+
const env = readFileSync20(CONFIG_PATH4, "utf-8");
|
|
6804
6973
|
const specific = env.match(new RegExp(`^SYNKRO_TRANSCRIPT_CONSENT_${source}='(yes|no)'`, "m"));
|
|
6805
6974
|
if (specific) return specific[1] === "yes";
|
|
6806
6975
|
if (source !== "CODEX") {
|
|
@@ -6823,7 +6992,7 @@ async function getOrMintCloudToken(gatewayUrl) {
|
|
|
6823
6992
|
assertGatewayAllowed(gatewayUrl);
|
|
6824
6993
|
let stored = "";
|
|
6825
6994
|
try {
|
|
6826
|
-
stored =
|
|
6995
|
+
stored = readFileSync20(CLOUD_JWT_PATH, "utf-8").trim();
|
|
6827
6996
|
} catch {
|
|
6828
6997
|
}
|
|
6829
6998
|
if (stored && !jwtExpired(stored)) return stored;
|
|
@@ -6839,25 +7008,53 @@ async function getOrMintCloudToken(gatewayUrl) {
|
|
|
6839
7008
|
throw new Error(`cloud-token mint failed (${resp.status}): ${t.slice(0, 200)}`);
|
|
6840
7009
|
}
|
|
6841
7010
|
const { token } = await resp.json();
|
|
6842
|
-
|
|
7011
|
+
writeFileSync16(CLOUD_JWT_PATH, token + "\n", { mode: 384 });
|
|
6843
7012
|
return token;
|
|
6844
7013
|
}
|
|
6845
7014
|
async function provisionCloudContainer(opts) {
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
7015
|
+
const sf = readFullSynkroFile();
|
|
7016
|
+
const envWorkers = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "", 10);
|
|
7017
|
+
const totalWorkers = Number.isFinite(envWorkers) && envWorkers > 0 ? envWorkers : 4;
|
|
7018
|
+
let claudeWorkers = 0;
|
|
7019
|
+
let cursorWorkers = 0;
|
|
7020
|
+
let codexWorkers = 0;
|
|
7021
|
+
if (sf && (sf.workers.claude != null || sf.workers.cursor != null || sf.workers.codex != null)) {
|
|
7022
|
+
claudeWorkers = Math.max(0, Math.floor(sf.workers.claude || 0));
|
|
7023
|
+
cursorWorkers = Math.max(0, Math.floor(sf.workers.cursor || 0));
|
|
7024
|
+
codexWorkers = Math.max(0, Math.floor(sf.workers.codex || 0));
|
|
7025
|
+
} else {
|
|
7026
|
+
const providers = [];
|
|
7027
|
+
if (sf?.grader.pool && sf.grader.pool !== "auto") providers.push(sf.grader.pool);
|
|
7028
|
+
else {
|
|
7029
|
+
if (opts.hasClaudeCode) providers.push("claude_code");
|
|
7030
|
+
if (opts.hasCursor) providers.push("cursor");
|
|
7031
|
+
if (opts.hasCodex) providers.push("codex");
|
|
7032
|
+
}
|
|
7033
|
+
({ claudeWorkers, cursorWorkers, codexWorkers } = splitWorkers(totalWorkers, providers));
|
|
7034
|
+
}
|
|
7035
|
+
if (claudeWorkers + cursorWorkers + codexWorkers === 0) codexWorkers = totalWorkers;
|
|
7036
|
+
const selectedKind = resolveConductorProvider(sf?.grader.pool || "auto", {
|
|
7037
|
+
claudeWorkers,
|
|
7038
|
+
cursorWorkers,
|
|
7039
|
+
codexWorkers
|
|
7040
|
+
});
|
|
7041
|
+
let setupToken = "";
|
|
7042
|
+
if (claudeWorkers > 0 || selectedKind === "claude_code") {
|
|
7043
|
+
try {
|
|
7044
|
+
console.log(" Authorize your Claude account for the hosted Claude worker \u2014");
|
|
7045
|
+
console.log(" a browser window will open for approval...\n");
|
|
7046
|
+
setupToken = await captureClaudeSetupToken();
|
|
7047
|
+
} catch (err) {
|
|
7048
|
+
console.error(` \u2717 Could not capture Claude setup-token: ${err.message}`);
|
|
7049
|
+
console.error(" Run `claude setup-token` manually, then re-run install.");
|
|
7050
|
+
process.exit(1);
|
|
7051
|
+
}
|
|
6855
7052
|
}
|
|
6856
|
-
console.log(" Validating Claude token...");
|
|
7053
|
+
if (setupToken) console.log(" Validating Claude token...");
|
|
6857
7054
|
let validated = false;
|
|
6858
7055
|
let authRejected = null;
|
|
6859
7056
|
let lastErr = "";
|
|
6860
|
-
for (let attempt = 1; attempt <= 2 && !validated; attempt++) {
|
|
7057
|
+
for (let attempt = 1; setupToken && attempt <= 2 && !validated; attempt++) {
|
|
6861
7058
|
try {
|
|
6862
7059
|
const out = execSync4('claude --print --output-format json "say ok"', {
|
|
6863
7060
|
env: { ...process.env, CLAUDE_CODE_OAUTH_TOKEN: setupToken },
|
|
@@ -6883,22 +7080,19 @@ async function provisionCloudContainer(opts) {
|
|
|
6883
7080
|
}
|
|
6884
7081
|
if (validated) {
|
|
6885
7082
|
console.log(" \u2713 Claude token validated.\n");
|
|
6886
|
-
} else {
|
|
7083
|
+
} else if (setupToken) {
|
|
6887
7084
|
console.warn(` \u26A0 Couldn't confirm the token locally in time (${lastErr.slice(0, 50)}).`);
|
|
6888
7085
|
console.warn(" Proceeding \u2014 the cloud smoke-grade below verifies it end-to-end in the container.\n");
|
|
6889
7086
|
}
|
|
6890
7087
|
const repo = detectGitRepo2() || void 0;
|
|
6891
|
-
const sf = readFullSynkroFile();
|
|
6892
7088
|
const harness = [];
|
|
6893
7089
|
if (opts.hasClaudeCode) harness.push("claude-code");
|
|
6894
7090
|
if (opts.hasCursor) harness.push("cursor");
|
|
6895
|
-
|
|
6896
|
-
const total = sf?.workers?.claude && sf.workers.claude > 0 ? sf.workers.claude : Number.isFinite(envWorkers) && envWorkers > 0 ? envWorkers : 4;
|
|
6897
|
-
const cursorTotal = sf?.workers?.cursor && sf.workers.cursor > 0 ? sf.workers.cursor : 0;
|
|
7091
|
+
if (opts.hasCodex) harness.push("codex");
|
|
6898
7092
|
let cursorApiKey = "";
|
|
6899
|
-
if (
|
|
7093
|
+
if (cursorWorkers > 0 || selectedKind === "cursor") {
|
|
6900
7094
|
try {
|
|
6901
|
-
cursorApiKey =
|
|
7095
|
+
cursorApiKey = readFileSync20(join18(SYNKRO_DIR11, "cursor-creds", "api-key"), "utf-8").trim();
|
|
6902
7096
|
} catch {
|
|
6903
7097
|
}
|
|
6904
7098
|
}
|
|
@@ -6908,6 +7102,13 @@ async function provisionCloudContainer(opts) {
|
|
|
6908
7102
|
} catch (e) {
|
|
6909
7103
|
console.warn(` (cloud token unavailable, using session token: ${e.message})`);
|
|
6910
7104
|
}
|
|
7105
|
+
if (codexWorkers > 0 || selectedKind === "codex") {
|
|
7106
|
+
const codex = await setupCodexCloud(opts.gatewayUrl, opts.jwt, (message) => console.log(` ${message}`));
|
|
7107
|
+
if (!codex.ok) {
|
|
7108
|
+
console.error(` \u2717 ${codex.error || "Codex cloud authorization failed"}`);
|
|
7109
|
+
process.exit(1);
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
6911
7112
|
try {
|
|
6912
7113
|
const resp = await fetch(`${opts.gatewayUrl}/api/v1/cli/cloud-provision`, {
|
|
6913
7114
|
method: "POST",
|
|
@@ -6917,8 +7118,10 @@ async function provisionCloudContainer(opts) {
|
|
|
6917
7118
|
user_id: opts.userId,
|
|
6918
7119
|
email: opts.email,
|
|
6919
7120
|
harness,
|
|
6920
|
-
claude_workers:
|
|
6921
|
-
cursor_workers:
|
|
7121
|
+
claude_workers: claudeWorkers,
|
|
7122
|
+
cursor_workers: cursorWorkers,
|
|
7123
|
+
codex_workers: codexWorkers,
|
|
7124
|
+
conductor_provider: selectedKind,
|
|
6922
7125
|
cursor_api_key: cursorApiKey,
|
|
6923
7126
|
// never logged; gateway stores it as the org secret
|
|
6924
7127
|
connected_repo: repo,
|
|
@@ -6933,11 +7136,25 @@ async function provisionCloudContainer(opts) {
|
|
|
6933
7136
|
}
|
|
6934
7137
|
const out = await resp.json().catch(() => ({}));
|
|
6935
7138
|
console.log(` \u2713 cloud container provisioned${out.container_id ? ` (${out.container_id})` : ""}`);
|
|
7139
|
+
const containerBase = (process.env.SYNKRO_CONTAINERS_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
7140
|
+
try {
|
|
7141
|
+
const recycle = await fetch(`${containerBase}/recycle`, {
|
|
7142
|
+
method: "POST",
|
|
7143
|
+
headers: { Authorization: `Bearer ${opts.jwt}` },
|
|
7144
|
+
signal: AbortSignal.timeout(3e4)
|
|
7145
|
+
});
|
|
7146
|
+
if (recycle.ok) console.log(" \u2713 cloud grader recycled onto the new pool");
|
|
7147
|
+
else console.warn(` \u26A0 cloud grader recycle returned HTTP ${recycle.status}; warmup will retry the current container`);
|
|
7148
|
+
} catch {
|
|
7149
|
+
console.warn(" \u26A0 cloud grader recycle was unreachable; warmup will retry");
|
|
7150
|
+
}
|
|
6936
7151
|
console.log();
|
|
7152
|
+
return selectedKind;
|
|
6937
7153
|
} catch (err) {
|
|
6938
7154
|
console.error(` \u2717 Cloud provisioning failed: ${err.message}`);
|
|
6939
7155
|
console.error(" Hooks + MCP are installed; re-run `synkro install` \u2192 cloud to retry.");
|
|
6940
7156
|
console.log();
|
|
7157
|
+
throw err;
|
|
6941
7158
|
} finally {
|
|
6942
7159
|
setupToken = "";
|
|
6943
7160
|
}
|
|
@@ -6978,17 +7195,21 @@ async function printWorkerDebug(base, jwt2) {
|
|
|
6978
7195
|
console.warn(" (could not fetch worker debug)\n");
|
|
6979
7196
|
}
|
|
6980
7197
|
}
|
|
6981
|
-
async function verifyCloudGrader(jwt2) {
|
|
7198
|
+
async function verifyCloudGrader(jwt2, requestedKind) {
|
|
6982
7199
|
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
6983
7200
|
console.log(" Warming the cloud grader (booting container + checking workers)...");
|
|
6984
7201
|
const deadline = Date.now() + 18e4;
|
|
6985
7202
|
let healthy = 0;
|
|
7203
|
+
let agentKind = requestedKind || "claude_code";
|
|
6986
7204
|
while (Date.now() < deadline) {
|
|
6987
7205
|
try {
|
|
6988
7206
|
const r = await fetch(`${base}/diag`, { headers: { Authorization: `Bearer ${jwt2}` }, signal: AbortSignal.timeout(6e4) });
|
|
6989
7207
|
if (r.ok) {
|
|
6990
7208
|
const j = await r.json();
|
|
6991
|
-
|
|
7209
|
+
if (!requestedKind) {
|
|
7210
|
+
agentKind = ["codex", "cursor", "claude_code"].find((kind) => (j[kind]?.healthy ?? 0) > 0) || "claude_code";
|
|
7211
|
+
}
|
|
7212
|
+
healthy = j[agentKind]?.healthy ?? 0;
|
|
6992
7213
|
if (healthy >= 1) break;
|
|
6993
7214
|
}
|
|
6994
7215
|
} catch {
|
|
@@ -7000,7 +7221,7 @@ async function verifyCloudGrader(jwt2) {
|
|
|
7000
7221
|
console.warn(" Grading is NOT verified \u2014 check the container or re-run `synkro install`.\n");
|
|
7001
7222
|
return false;
|
|
7002
7223
|
}
|
|
7003
|
-
console.log(` \u2713 ${healthy}
|
|
7224
|
+
console.log(` \u2713 ${healthy} ${poolLabel(agentKind)} worker(s) healthy`);
|
|
7004
7225
|
console.log(" Running smoke grade (a worker scores a known-risky edit; up to 120s)...");
|
|
7005
7226
|
const started = Date.now();
|
|
7006
7227
|
const ticker = setInterval(() => {
|
|
@@ -7016,7 +7237,7 @@ async function verifyCloudGrader(jwt2) {
|
|
|
7016
7237
|
const r = await fetch(`${base}/grade/submit`, {
|
|
7017
7238
|
method: "POST",
|
|
7018
7239
|
headers: { Authorization: `Bearer ${jwt2}`, "Content-Type": "application/json" },
|
|
7019
|
-
body: JSON.stringify({ role: "grade-edit", payload: probe, content: probe, agent_kind:
|
|
7240
|
+
body: JSON.stringify({ role: "grade-edit", payload: probe, content: probe, agent_kind: agentKind }),
|
|
7020
7241
|
signal: AbortSignal.timeout(12e4)
|
|
7021
7242
|
});
|
|
7022
7243
|
stopTicker();
|
|
@@ -7026,11 +7247,12 @@ async function verifyCloudGrader(jwt2) {
|
|
|
7026
7247
|
body = JSON.parse(raw);
|
|
7027
7248
|
} catch {
|
|
7028
7249
|
}
|
|
7029
|
-
if (r.ok && typeof body.result === "string" && body.result
|
|
7250
|
+
if (r.ok && typeof body.result === "string" && isValidRiskyEditSmokeVerdict(body.result)) {
|
|
7030
7251
|
console.log(" \u2713 smoke grade passed \u2014 cloud grading is live\n");
|
|
7031
7252
|
return true;
|
|
7032
7253
|
}
|
|
7033
|
-
const
|
|
7254
|
+
const invalidVerdict = r.ok && typeof body.result === "string" ? `invalid smoke verdict: ${body.result}` : "";
|
|
7255
|
+
const detail = (body.error || invalidVerdict || raw || "").trim().slice(0, 300) || "(empty response)";
|
|
7034
7256
|
console.warn(` \u2717 smoke grade FAILED (HTTP ${r.status}): ${detail}`);
|
|
7035
7257
|
if (body.error_code) {
|
|
7036
7258
|
console.warn(` \u25B8 ${body.error_code}: ${body.hint || ""}`);
|
|
@@ -7055,8 +7277,8 @@ async function verifyCloudGrader(jwt2) {
|
|
|
7055
7277
|
}
|
|
7056
7278
|
function readPersistedDeployLocation() {
|
|
7057
7279
|
try {
|
|
7058
|
-
if (
|
|
7059
|
-
const m =
|
|
7280
|
+
if (existsSync22(CONFIG_PATH4)) {
|
|
7281
|
+
const m = readFileSync20(CONFIG_PATH4, "utf-8").match(/^SYNKRO_DEPLOY_LOCATION='([^']*)'/m);
|
|
7060
7282
|
if (m?.[1] === "cloud") return "cloud";
|
|
7061
7283
|
}
|
|
7062
7284
|
} catch {
|
|
@@ -7064,8 +7286,8 @@ function readPersistedDeployLocation() {
|
|
|
7064
7286
|
return "local";
|
|
7065
7287
|
}
|
|
7066
7288
|
function updateConfigEnvLocation(location) {
|
|
7067
|
-
if (!
|
|
7068
|
-
let env =
|
|
7289
|
+
if (!existsSync22(CONFIG_PATH4)) return;
|
|
7290
|
+
let env = readFileSync20(CONFIG_PATH4, "utf-8");
|
|
7069
7291
|
const set = (k, v) => {
|
|
7070
7292
|
const re = new RegExp(`^${k}=.*$`, "m");
|
|
7071
7293
|
const line = `${k}='${v}'`;
|
|
@@ -7073,14 +7295,14 @@ function updateConfigEnvLocation(location) {
|
|
|
7073
7295
|
};
|
|
7074
7296
|
set("SYNKRO_DEPLOY_LOCATION", location);
|
|
7075
7297
|
set("SYNKRO_STORAGE_MODE", location === "cloud" ? "cloud" : "local");
|
|
7076
|
-
|
|
7298
|
+
writeFileSync16(CONFIG_PATH4, env, "utf-8");
|
|
7077
7299
|
chmodSync4(CONFIG_PATH4, 384);
|
|
7078
7300
|
}
|
|
7079
7301
|
async function applyMcpConfig(opts) {
|
|
7080
7302
|
if (!opts.hasClaudeCode && !opts.hasCursor && !opts.hasCodex) return;
|
|
7081
7303
|
let mcpJwt2 = "";
|
|
7082
7304
|
try {
|
|
7083
|
-
mcpJwt2 =
|
|
7305
|
+
mcpJwt2 = readFileSync20(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
|
|
7084
7306
|
} catch {
|
|
7085
7307
|
}
|
|
7086
7308
|
if (!mcpJwt2) {
|
|
@@ -7172,10 +7394,10 @@ Deploy location: ${current || "(none)"} \u2192 ${desired}
|
|
|
7172
7394
|
email = info.email;
|
|
7173
7395
|
} catch {
|
|
7174
7396
|
}
|
|
7175
|
-
await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
|
|
7397
|
+
const agentKind = await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor, hasCodex });
|
|
7176
7398
|
updateConfigEnvLocation("cloud");
|
|
7177
7399
|
await applyMcpConfig({ gatewayUrl, jwt: token, hasClaudeCode, hasCursor, hasCodex, local: false });
|
|
7178
|
-
await verifyCloudGrader(token);
|
|
7400
|
+
await verifyCloudGrader(token, agentKind);
|
|
7179
7401
|
console.log(" \u2713 grading + rules/MCP now served from Synkro Cloud\n");
|
|
7180
7402
|
} else {
|
|
7181
7403
|
updateConfigEnvLocation("local");
|
|
@@ -7210,8 +7432,8 @@ function resolveDeploymentMode() {
|
|
|
7210
7432
|
const envOverride = process.env.SYNKRO_DEPLOYMENT_MODE?.toLowerCase();
|
|
7211
7433
|
if (envOverride === "bare-host" || envOverride === "docker") return envOverride;
|
|
7212
7434
|
try {
|
|
7213
|
-
if (
|
|
7214
|
-
const m =
|
|
7435
|
+
if (existsSync22(CONFIG_PATH4)) {
|
|
7436
|
+
const m = readFileSync20(CONFIG_PATH4, "utf-8").match(/^SYNKRO_DEPLOYMENT_MODE='([^']*)'/m);
|
|
7215
7437
|
const val = m?.[1]?.toLowerCase();
|
|
7216
7438
|
if (val === "bare-host" || val === "docker") return val;
|
|
7217
7439
|
}
|
|
@@ -7238,16 +7460,16 @@ function collectLocalMetadata(includeClaudeCode = true) {
|
|
|
7238
7460
|
meta.cc_version = execSync4("claude --version", { encoding: "utf-8", timeout: 5e3 }).trim().split("\n")[0];
|
|
7239
7461
|
} catch {
|
|
7240
7462
|
}
|
|
7241
|
-
const claudeDir =
|
|
7463
|
+
const claudeDir = join18(homedir20(), ".claude");
|
|
7242
7464
|
try {
|
|
7243
|
-
const settings = JSON.parse(
|
|
7465
|
+
const settings = JSON.parse(readFileSync20(join18(claudeDir, "settings.json"), "utf-8"));
|
|
7244
7466
|
const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
|
|
7245
7467
|
if (plugins.length) meta.enabled_plugins = plugins;
|
|
7246
7468
|
if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
|
|
7247
7469
|
} catch {
|
|
7248
7470
|
}
|
|
7249
7471
|
try {
|
|
7250
|
-
const mcpCache = JSON.parse(
|
|
7472
|
+
const mcpCache = JSON.parse(readFileSync20(join18(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
|
|
7251
7473
|
const mcpNames = Object.keys(mcpCache);
|
|
7252
7474
|
if (mcpNames.length) meta.mcp_servers = mcpNames;
|
|
7253
7475
|
} catch {
|
|
@@ -7259,10 +7481,10 @@ function collectLocalMetadata(includeClaudeCode = true) {
|
|
|
7259
7481
|
} catch {
|
|
7260
7482
|
}
|
|
7261
7483
|
try {
|
|
7262
|
-
const sessionsDir =
|
|
7484
|
+
const sessionsDir = join18(claudeDir, "sessions");
|
|
7263
7485
|
const files = readdirSync4(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
|
|
7264
7486
|
for (const f of files) {
|
|
7265
|
-
const s = JSON.parse(
|
|
7487
|
+
const s = JSON.parse(readFileSync20(join18(sessionsDir, f), "utf-8"));
|
|
7266
7488
|
if (s.version) {
|
|
7267
7489
|
meta.cc_version = meta.cc_version || s.version;
|
|
7268
7490
|
break;
|
|
@@ -7464,7 +7686,7 @@ async function installCommand(opts = {}) {
|
|
|
7464
7686
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
7465
7687
|
emit("install", {
|
|
7466
7688
|
phase: "started",
|
|
7467
|
-
cli_version_to: "1.7.
|
|
7689
|
+
cli_version_to: "1.7.88",
|
|
7468
7690
|
agents_detected: agents.map((a) => a.kind),
|
|
7469
7691
|
with_github: false,
|
|
7470
7692
|
with_local_cc: false,
|
|
@@ -7476,9 +7698,9 @@ async function installCommand(opts = {}) {
|
|
|
7476
7698
|
const scripts = writeHookScripts();
|
|
7477
7699
|
console.log("Wrote hook scripts to ~/.synkro/hooks/\n");
|
|
7478
7700
|
for (const mode of ["edit", "bash"]) {
|
|
7479
|
-
const pidFile =
|
|
7701
|
+
const pidFile = join18(SYNKRO_DIR11, "daemon", mode, "daemon.pid");
|
|
7480
7702
|
try {
|
|
7481
|
-
const pid = parseInt(
|
|
7703
|
+
const pid = parseInt(readFileSync20(pidFile, "utf-8").trim(), 10);
|
|
7482
7704
|
if (pid > 0) {
|
|
7483
7705
|
process.kill(pid, "SIGTERM");
|
|
7484
7706
|
console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
|
|
@@ -7596,7 +7818,7 @@ async function installCommand(opts = {}) {
|
|
|
7596
7818
|
if (mintResp.ok) {
|
|
7597
7819
|
const minted = await mintResp.json();
|
|
7598
7820
|
mcpJwt2 = minted.token;
|
|
7599
|
-
|
|
7821
|
+
writeFileSync16(join18(SYNKRO_DIR11, ".mcp-jwt"), mcpJwt2 + "\n", { mode: 384 });
|
|
7600
7822
|
} else {
|
|
7601
7823
|
console.warn(" \u26A0 Could not mint MCP token \u2014 local server will reject requests until re-installed.");
|
|
7602
7824
|
}
|
|
@@ -7624,7 +7846,7 @@ async function installCommand(opts = {}) {
|
|
|
7624
7846
|
throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
|
|
7625
7847
|
}
|
|
7626
7848
|
const minted = await mintResp.json();
|
|
7627
|
-
|
|
7849
|
+
writeFileSync16(join18(SYNKRO_DIR11, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
|
|
7628
7850
|
const mcp = installMcpConfig({ gatewayUrl, bearerToken: minted.token });
|
|
7629
7851
|
console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
|
|
7630
7852
|
console.log(` url: ${mcp.url}`);
|
|
@@ -7646,8 +7868,8 @@ async function installCommand(opts = {}) {
|
|
|
7646
7868
|
if (hasCursor && !opts.noMcp) {
|
|
7647
7869
|
try {
|
|
7648
7870
|
if (useLocalMcp) {
|
|
7649
|
-
const jwtPath =
|
|
7650
|
-
if (!
|
|
7871
|
+
const jwtPath = join18(SYNKRO_DIR11, ".mcp-jwt");
|
|
7872
|
+
if (!existsSync22(jwtPath)) {
|
|
7651
7873
|
const mintResp = await fetch(`${gatewayUrl}/api/v1/cli/mcp-token`, {
|
|
7652
7874
|
method: "POST",
|
|
7653
7875
|
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
@@ -7655,7 +7877,7 @@ async function installCommand(opts = {}) {
|
|
|
7655
7877
|
});
|
|
7656
7878
|
if (mintResp.ok) {
|
|
7657
7879
|
const minted = await mintResp.json();
|
|
7658
|
-
|
|
7880
|
+
writeFileSync16(jwtPath, minted.token + "\n", { mode: 384 });
|
|
7659
7881
|
}
|
|
7660
7882
|
}
|
|
7661
7883
|
const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: "", local: true });
|
|
@@ -7675,7 +7897,7 @@ async function installCommand(opts = {}) {
|
|
|
7675
7897
|
throw new Error(`mcp-token mint failed (${mintResp.status}): ${errText.slice(0, 200)}`);
|
|
7676
7898
|
}
|
|
7677
7899
|
const minted = await mintResp.json();
|
|
7678
|
-
|
|
7900
|
+
writeFileSync16(join18(SYNKRO_DIR11, ".mcp-jwt"), minted.token + "\n", { mode: 384 });
|
|
7679
7901
|
const mcp = installCursorMcpConfig({ gatewayUrl, bearerToken: minted.token });
|
|
7680
7902
|
console.log(`Registered Synkro guardrails MCP server in ${mcp.path}`);
|
|
7681
7903
|
console.log(` url: ${mcp.url}`);
|
|
@@ -7688,10 +7910,10 @@ async function installCommand(opts = {}) {
|
|
|
7688
7910
|
}
|
|
7689
7911
|
if (hasCodex && !opts.noMcp) {
|
|
7690
7912
|
try {
|
|
7691
|
-
const jwtPath =
|
|
7913
|
+
const jwtPath = join18(SYNKRO_DIR11, ".mcp-jwt");
|
|
7692
7914
|
let mcpJwt2 = "";
|
|
7693
7915
|
try {
|
|
7694
|
-
mcpJwt2 =
|
|
7916
|
+
mcpJwt2 = readFileSync20(jwtPath, "utf-8").trim();
|
|
7695
7917
|
} catch {
|
|
7696
7918
|
}
|
|
7697
7919
|
if (!mcpJwt2) {
|
|
@@ -7709,7 +7931,7 @@ async function installCommand(opts = {}) {
|
|
|
7709
7931
|
}
|
|
7710
7932
|
const minted = await mintResp.json();
|
|
7711
7933
|
mcpJwt2 = minted.token;
|
|
7712
|
-
|
|
7934
|
+
writeFileSync16(jwtPath, mcpJwt2 + "\n", { mode: 384 });
|
|
7713
7935
|
}
|
|
7714
7936
|
const mcp = installCodexMcpConfig({
|
|
7715
7937
|
gatewayUrl,
|
|
@@ -7756,7 +7978,7 @@ async function installCommand(opts = {}) {
|
|
|
7756
7978
|
}
|
|
7757
7979
|
const pool = sfp?.grader?.pool || "auto";
|
|
7758
7980
|
if (pool === "cursor") return true;
|
|
7759
|
-
if (pool === "claude_code") return false;
|
|
7981
|
+
if (pool === "claude_code" || pool === "codex") return false;
|
|
7760
7982
|
return hasCursor;
|
|
7761
7983
|
})();
|
|
7762
7984
|
if (useLocalMcp) {
|
|
@@ -7776,14 +7998,17 @@ async function installCommand(opts = {}) {
|
|
|
7776
7998
|
for (const w of sf?.warnings || []) console.warn(` \u26A0 ${w}`);
|
|
7777
7999
|
let claudeWorkers;
|
|
7778
8000
|
let cursorWorkers;
|
|
7779
|
-
|
|
8001
|
+
let codexWorkers;
|
|
8002
|
+
if (sf && (sf.workers.claude != null || sf.workers.cursor != null || sf.workers.codex != null)) {
|
|
7780
8003
|
claudeWorkers = Math.max(0, Math.floor(sf.workers.claude || 0));
|
|
7781
8004
|
cursorWorkers = Math.max(0, Math.floor(sf.workers.cursor || 0));
|
|
7782
|
-
|
|
8005
|
+
codexWorkers = Math.max(0, Math.floor(sf.workers.codex || 0));
|
|
8006
|
+
if (claudeWorkers + cursorWorkers + codexWorkers === 0) {
|
|
7783
8007
|
claudeWorkers = 0;
|
|
7784
|
-
cursorWorkers =
|
|
8008
|
+
cursorWorkers = 0;
|
|
8009
|
+
codexWorkers = 8;
|
|
7785
8010
|
}
|
|
7786
|
-
console.log(` synkro.toml: explicit workers \u2014 ${claudeWorkers} claude + ${cursorWorkers} cursor`);
|
|
8011
|
+
console.log(` synkro.toml: explicit workers \u2014 ${claudeWorkers} claude + ${cursorWorkers} cursor + ${codexWorkers} codex`);
|
|
7787
8012
|
} else {
|
|
7788
8013
|
const totalWorkers = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
|
|
7789
8014
|
const synkroFilePool = sf?.grader?.pool || "auto";
|
|
@@ -7792,23 +8017,40 @@ async function installCommand(opts = {}) {
|
|
|
7792
8017
|
providers = ["cursor"];
|
|
7793
8018
|
} else if (synkroFilePool === "claude_code") {
|
|
7794
8019
|
providers = ["claude_code"];
|
|
8020
|
+
} else if (synkroFilePool === "codex") {
|
|
8021
|
+
providers = ["codex"];
|
|
7795
8022
|
} else {
|
|
7796
8023
|
if (hasClaudeCode) providers.push("claude_code");
|
|
7797
8024
|
if (hasCursor) providers.push("cursor");
|
|
8025
|
+
if (hasCodex) providers.push("codex");
|
|
7798
8026
|
}
|
|
7799
|
-
({ claudeWorkers, cursorWorkers } = splitWorkers(totalWorkers, providers));
|
|
8027
|
+
({ claudeWorkers, cursorWorkers, codexWorkers } = splitWorkers(totalWorkers, providers));
|
|
7800
8028
|
if (synkroFilePool !== "auto") console.log(` synkro.toml: grader pool set to ${poolLabel(synkroFilePool)}`);
|
|
7801
8029
|
}
|
|
7802
|
-
|
|
8030
|
+
const conductorProvider = resolveConductorProvider(sf?.grader.pool || "auto", {
|
|
8031
|
+
claudeWorkers,
|
|
8032
|
+
cursorWorkers,
|
|
8033
|
+
codexWorkers
|
|
8034
|
+
});
|
|
8035
|
+
let codexHomeDir;
|
|
8036
|
+
if (codexWorkers > 0 || conductorProvider === "codex") {
|
|
8037
|
+
const codexSetup = await setupCodexLocal((message) => console.log(` ${message}`));
|
|
8038
|
+
if (!codexSetup.ok || !codexSetup.home) {
|
|
8039
|
+
console.error(` \u2717 ${codexSetup.error || "Codex grader authorization failed"}`);
|
|
8040
|
+
process.exit(1);
|
|
8041
|
+
}
|
|
8042
|
+
codexHomeDir = codexSetup.home;
|
|
8043
|
+
}
|
|
8044
|
+
console.log(` worker pool: ${claudeWorkers} claude + ${cursorWorkers} cursor + ${codexWorkers} codex`);
|
|
7803
8045
|
const connectedRepo = detectGitRepo2() || void 0;
|
|
7804
|
-
const { image, hostMcpPort, hostGraderPort, hostCwePort, hostPglitePort } = await dockerInstall({ claudeWorkers, cursorWorkers, connectedRepo });
|
|
8046
|
+
const { image, hostMcpPort, hostGraderPort, hostCwePort, hostPglitePort } = await dockerInstall({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider, codexHomeDir, connectedRepo });
|
|
7805
8047
|
console.log(` \u2713 pulled ${image}`);
|
|
7806
8048
|
console.log(` container started \u2014 MCP=${hostMcpPort} general=${hostGraderPort} CWE=${hostCwePort} pglite=${hostPglitePort}`);
|
|
7807
8049
|
console.log(" waiting for container to be ready...");
|
|
7808
8050
|
const ready = await waitForContainerReady(6e4);
|
|
7809
8051
|
if (ready) {
|
|
7810
8052
|
console.log(" \u2713 container ready");
|
|
7811
|
-
const mcpJwt2 =
|
|
8053
|
+
const mcpJwt2 = readFileSync20(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
|
|
7812
8054
|
try {
|
|
7813
8055
|
const ingestResp = await fetch(`http://127.0.0.1:${hostMcpPort}/api/ingest`, {
|
|
7814
8056
|
method: "POST",
|
|
@@ -7841,8 +8083,8 @@ async function installCommand(opts = {}) {
|
|
|
7841
8083
|
console.log();
|
|
7842
8084
|
} else if (target === "cloud-container") {
|
|
7843
8085
|
if (graderUsesCursor) await promptCursorApiKey(opts);
|
|
7844
|
-
await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor });
|
|
7845
|
-
cloudGradeOk = await verifyCloudGrader(token);
|
|
8086
|
+
const agentKind = await provisionCloudContainer({ gatewayUrl, jwt: token, orgId, userId, email, hasClaudeCode, hasCursor, hasCodex });
|
|
8087
|
+
cloudGradeOk = await verifyCloudGrader(token, agentKind);
|
|
7846
8088
|
}
|
|
7847
8089
|
if (transcriptConsent) {
|
|
7848
8090
|
const repo = detectGitRepo2();
|
|
@@ -7851,7 +8093,7 @@ async function installCommand(opts = {}) {
|
|
|
7851
8093
|
try {
|
|
7852
8094
|
let mcpToken = "";
|
|
7853
8095
|
try {
|
|
7854
|
-
mcpToken =
|
|
8096
|
+
mcpToken = readFileSync20(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
|
|
7855
8097
|
} catch {
|
|
7856
8098
|
}
|
|
7857
8099
|
if (mcpToken) {
|
|
@@ -8019,8 +8261,8 @@ function writeSynkroFileIfMissing(opts) {
|
|
|
8019
8261
|
try {
|
|
8020
8262
|
const root = execSync4("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8021
8263
|
if (!root) return;
|
|
8022
|
-
if (root ===
|
|
8023
|
-
const fp =
|
|
8264
|
+
if (root === homedir20()) return;
|
|
8265
|
+
const fp = join18(root, "synkro.toml");
|
|
8024
8266
|
let hasFile = false;
|
|
8025
8267
|
try {
|
|
8026
8268
|
hasFile = statSync2(fp).isFile();
|
|
@@ -8032,15 +8274,10 @@ function writeSynkroFileIfMissing(opts) {
|
|
|
8032
8274
|
}
|
|
8033
8275
|
let pool = "auto";
|
|
8034
8276
|
const harness = [];
|
|
8035
|
-
if (opts.hasClaudeCode)
|
|
8036
|
-
|
|
8037
|
-
if (!opts.hasCursor) pool = "claude";
|
|
8038
|
-
}
|
|
8039
|
-
if (opts.hasCursor) {
|
|
8040
|
-
harness.push("cursor");
|
|
8041
|
-
if (!opts.hasClaudeCode) pool = "cursor";
|
|
8042
|
-
}
|
|
8277
|
+
if (opts.hasClaudeCode) harness.push("claude-code");
|
|
8278
|
+
if (opts.hasCursor) harness.push("cursor");
|
|
8043
8279
|
if (opts.hasCodex) harness.push("codex");
|
|
8280
|
+
if (harness.length === 1) pool = harness[0] === "claude-code" ? "claude-code" : harness[0];
|
|
8044
8281
|
const mode = opts.gradingMode === "byok" ? "byok" : "local";
|
|
8045
8282
|
const toml = [
|
|
8046
8283
|
"version = 1",
|
|
@@ -8057,7 +8294,7 @@ function writeSynkroFileIfMissing(opts) {
|
|
|
8057
8294
|
"cve = true",
|
|
8058
8295
|
""
|
|
8059
8296
|
].join("\n");
|
|
8060
|
-
|
|
8297
|
+
writeFileSync16(fp, toml, "utf-8");
|
|
8061
8298
|
console.log(` synkro.toml: wrote ${fp} (pool=${pool}, mode=${mode})`);
|
|
8062
8299
|
} catch {
|
|
8063
8300
|
}
|
|
@@ -8065,12 +8302,12 @@ function writeSynkroFileIfMissing(opts) {
|
|
|
8065
8302
|
function updateSynkroTomlLocation(location) {
|
|
8066
8303
|
try {
|
|
8067
8304
|
const root = execSync4("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8068
|
-
if (!root || root ===
|
|
8069
|
-
const fp =
|
|
8305
|
+
if (!root || root === homedir20()) return;
|
|
8306
|
+
const fp = join18(root, "synkro.toml");
|
|
8070
8307
|
let txt = "";
|
|
8071
8308
|
try {
|
|
8072
8309
|
if (!statSync2(fp).isFile()) return;
|
|
8073
|
-
txt =
|
|
8310
|
+
txt = readFileSync20(fp, "utf-8");
|
|
8074
8311
|
} catch {
|
|
8075
8312
|
return;
|
|
8076
8313
|
}
|
|
@@ -8078,7 +8315,7 @@ function updateSynkroTomlLocation(location) {
|
|
|
8078
8315
|
if (!re.test(txt)) return;
|
|
8079
8316
|
const next = txt.replace(re, `$1"${location}"`);
|
|
8080
8317
|
if (next !== txt) {
|
|
8081
|
-
|
|
8318
|
+
writeFileSync16(fp, next, "utf-8");
|
|
8082
8319
|
console.log(` synkro.toml: [grader] location = "${location}"`);
|
|
8083
8320
|
}
|
|
8084
8321
|
} catch {
|
|
@@ -8088,9 +8325,9 @@ function readFullSynkroFile() {
|
|
|
8088
8325
|
try {
|
|
8089
8326
|
const root = execSync4("git rev-parse --show-toplevel", { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8090
8327
|
if (!root) return null;
|
|
8091
|
-
const fp =
|
|
8092
|
-
if (!
|
|
8093
|
-
const parsed = parseSynkroToml2(
|
|
8328
|
+
const fp = join18(root, "synkro.toml");
|
|
8329
|
+
if (!existsSync22(fp)) return null;
|
|
8330
|
+
const parsed = parseSynkroToml2(readFileSync20(fp, "utf-8"));
|
|
8094
8331
|
const valid = ["claude-code", "cursor", "codex"];
|
|
8095
8332
|
const harness = Array.isArray(parsed.harness) ? parsed.harness.filter((h) => valid.includes(h)) : ["claude-code", "cursor"];
|
|
8096
8333
|
const resolved = resolveGraderPool(parsed);
|
|
@@ -8103,7 +8340,8 @@ function readFullSynkroFile() {
|
|
|
8103
8340
|
},
|
|
8104
8341
|
workers: {
|
|
8105
8342
|
...resolved.claudeWorkers != null ? { claude: resolved.claudeWorkers } : {},
|
|
8106
|
-
...resolved.cursorWorkers != null ? { cursor: resolved.cursorWorkers } : {}
|
|
8343
|
+
...resolved.cursorWorkers != null ? { cursor: resolved.cursorWorkers } : {},
|
|
8344
|
+
...resolved.codexWorkers != null ? { codex: resolved.codexWorkers } : {}
|
|
8107
8345
|
},
|
|
8108
8346
|
warnings: resolved.warnings,
|
|
8109
8347
|
ruleset: parsed.ruleset || "default",
|
|
@@ -8129,7 +8367,7 @@ function reconcileHarness() {
|
|
|
8129
8367
|
console.log(`synkro.toml: harness=[${sf.harness.join(", ")}] pool=${poolLabel(sf.grader.pool)} mode=${sf.grader.mode}`);
|
|
8130
8368
|
const scripts = writeHookScripts();
|
|
8131
8369
|
console.log("Wrote hook scripts to ~/.synkro/hooks/");
|
|
8132
|
-
const ccSettings =
|
|
8370
|
+
const ccSettings = join18(homedir20(), ".claude", "settings.json");
|
|
8133
8371
|
if (wantCC) {
|
|
8134
8372
|
installCCHooks(ccSettings, {
|
|
8135
8373
|
bashJudgeScriptPath: scripts.bashScript,
|
|
@@ -8152,7 +8390,7 @@ function reconcileHarness() {
|
|
|
8152
8390
|
});
|
|
8153
8391
|
console.log(" \u2713 Claude Code hooks registered");
|
|
8154
8392
|
try {
|
|
8155
|
-
const mcpJwt2 =
|
|
8393
|
+
const mcpJwt2 = readFileSync20(join18(SYNKRO_DIR11, ".mcp-jwt"), "utf-8").trim();
|
|
8156
8394
|
if (mcpJwt2) {
|
|
8157
8395
|
installMcpConfig({ gatewayUrl: "", bearerToken: mcpJwt2, local: true });
|
|
8158
8396
|
console.log(" \u2713 Claude Code MCP registered");
|
|
@@ -8164,7 +8402,7 @@ function reconcileHarness() {
|
|
|
8164
8402
|
if (uninstallMcpConfig()) console.log(" \u2717 Claude Code MCP removed");
|
|
8165
8403
|
if (uninstallClaudeDesktopMcpConfig()) console.log(" \u2717 Claude Desktop MCP removed");
|
|
8166
8404
|
}
|
|
8167
|
-
const cursorHooks =
|
|
8405
|
+
const cursorHooks = join18(homedir20(), ".cursor", "hooks.json");
|
|
8168
8406
|
if (wantCursor) {
|
|
8169
8407
|
installCursorHooks(cursorHooks, {
|
|
8170
8408
|
bashJudgeScriptPath: scripts.cursorBashJudgeScript,
|
|
@@ -8195,7 +8433,7 @@ function reconcileHarness() {
|
|
|
8195
8433
|
if (uninstallCursorHooks(cursorHooks)) console.log(" \u2717 Cursor hooks removed");
|
|
8196
8434
|
if (uninstallCursorMcpConfig()) console.log(" \u2717 Cursor MCP removed");
|
|
8197
8435
|
}
|
|
8198
|
-
const codexHooks =
|
|
8436
|
+
const codexHooks = join18(process.env.CODEX_HOME || join18(homedir20(), ".codex"), "hooks.json");
|
|
8199
8437
|
if (wantCodex) {
|
|
8200
8438
|
installCodexHooks(codexHooks, {
|
|
8201
8439
|
bashJudgeScriptPath: scripts.bashScript,
|
|
@@ -8226,10 +8464,14 @@ function reconcileHarness() {
|
|
|
8226
8464
|
if (uninstallCodexHooks(codexHooks)) console.log(" \u2717 Codex hooks removed");
|
|
8227
8465
|
if (uninstallCodexMcpConfig()) console.log(" \u2717 Codex MCP removed");
|
|
8228
8466
|
}
|
|
8229
|
-
if (sf.workers.claude != null || sf.workers.cursor != null) {
|
|
8467
|
+
if (sf.workers.claude != null || sf.workers.cursor != null || sf.workers.codex != null) {
|
|
8230
8468
|
const cw = Math.max(0, Math.floor(sf.workers.claude || 0));
|
|
8231
8469
|
const curw = Math.max(0, Math.floor(sf.workers.cursor || 0));
|
|
8232
|
-
|
|
8470
|
+
const codw = Math.max(0, Math.floor(sf.workers.codex || 0));
|
|
8471
|
+
if (cw + curw + codw > 0) {
|
|
8472
|
+
const counts2 = { claudeWorkers: cw, cursorWorkers: curw, codexWorkers: codw };
|
|
8473
|
+
return { ...counts2, conductorProvider: resolveConductorProvider(sf.grader.pool, counts2) };
|
|
8474
|
+
}
|
|
8233
8475
|
}
|
|
8234
8476
|
const total = parseInt(process.env.SYNKRO_WORKERS_PER_POOL || "8", 10);
|
|
8235
8477
|
const providers = [];
|
|
@@ -8237,12 +8479,16 @@ function reconcileHarness() {
|
|
|
8237
8479
|
providers.push("cursor");
|
|
8238
8480
|
} else if (sf.grader.pool === "claude_code") {
|
|
8239
8481
|
providers.push("claude_code");
|
|
8482
|
+
} else if (sf.grader.pool === "codex") {
|
|
8483
|
+
providers.push("codex");
|
|
8240
8484
|
} else {
|
|
8241
8485
|
if (wantCC) providers.push("claude_code");
|
|
8242
8486
|
if (wantCursor) providers.push("cursor");
|
|
8487
|
+
if (wantCodex) providers.push("codex");
|
|
8243
8488
|
}
|
|
8244
8489
|
if (providers.length === 0) providers.push("claude_code");
|
|
8245
|
-
|
|
8490
|
+
const counts = splitWorkers(total, providers);
|
|
8491
|
+
return { ...counts, conductorProvider: resolveConductorProvider(sf.grader.pool, counts) };
|
|
8246
8492
|
}
|
|
8247
8493
|
async function ingestSkillTasks(tasks, mcpPort) {
|
|
8248
8494
|
const summary = { ingested: 0, rules: 0, rejected: [] };
|
|
@@ -8279,7 +8525,7 @@ async function syncSkillFiles() {
|
|
|
8279
8525
|
if (resolved.length === 0) return;
|
|
8280
8526
|
const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
|
|
8281
8527
|
const tasks = resolved.map((fp) => {
|
|
8282
|
-
const content =
|
|
8528
|
+
const content = readFileSync20(fp, "utf-8");
|
|
8283
8529
|
const source = `skill:${fp.split("/").pop()}`;
|
|
8284
8530
|
if (!content.trim()) {
|
|
8285
8531
|
console.log(` \u2298 skill ${source}: empty file, skipped`);
|
|
@@ -8300,15 +8546,15 @@ function discoverSkillFiles(repoRoot2, excludeHashes, ingestedHashes, ingestedNa
|
|
|
8300
8546
|
const roots = [];
|
|
8301
8547
|
const add = (p) => {
|
|
8302
8548
|
try {
|
|
8303
|
-
if (p &&
|
|
8549
|
+
if (p && existsSync22(p) && statSync2(p).isDirectory()) roots.push(p);
|
|
8304
8550
|
} catch {
|
|
8305
8551
|
}
|
|
8306
8552
|
};
|
|
8307
|
-
add(
|
|
8308
|
-
add(
|
|
8553
|
+
add(join18(homedir20(), ".claude", "skills"));
|
|
8554
|
+
add(join18(homedir20(), ".agents", "skills"));
|
|
8309
8555
|
if (repoRoot2) {
|
|
8310
|
-
add(
|
|
8311
|
-
add(
|
|
8556
|
+
add(join18(repoRoot2, ".claude", "skills"));
|
|
8557
|
+
add(join18(repoRoot2, ".agents", "skills"));
|
|
8312
8558
|
}
|
|
8313
8559
|
const out = [];
|
|
8314
8560
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8319,7 +8565,7 @@ function discoverSkillFiles(repoRoot2, excludeHashes, ingestedHashes, ingestedNa
|
|
|
8319
8565
|
try {
|
|
8320
8566
|
const st = statSync2(file);
|
|
8321
8567
|
if (!st.isFile() || st.size > 2e5) return;
|
|
8322
|
-
content =
|
|
8568
|
+
content = readFileSync20(file, "utf-8");
|
|
8323
8569
|
} catch {
|
|
8324
8570
|
return;
|
|
8325
8571
|
}
|
|
@@ -8339,7 +8585,7 @@ function discoverSkillFiles(repoRoot2, excludeHashes, ingestedHashes, ingestedNa
|
|
|
8339
8585
|
}
|
|
8340
8586
|
for (const entry of entries) {
|
|
8341
8587
|
if (entry.startsWith(".")) continue;
|
|
8342
|
-
const full =
|
|
8588
|
+
const full = join18(root, entry);
|
|
8343
8589
|
let st;
|
|
8344
8590
|
try {
|
|
8345
8591
|
st = statSync2(full);
|
|
@@ -8347,8 +8593,8 @@ function discoverSkillFiles(repoRoot2, excludeHashes, ingestedHashes, ingestedNa
|
|
|
8347
8593
|
continue;
|
|
8348
8594
|
}
|
|
8349
8595
|
if (st.isDirectory()) {
|
|
8350
|
-
const skillMd =
|
|
8351
|
-
if (
|
|
8596
|
+
const skillMd = join18(full, "SKILL.md");
|
|
8597
|
+
if (existsSync22(skillMd)) consider(skillMd, entry);
|
|
8352
8598
|
} else if (/\.mdx?$/i.test(entry) && entry.toUpperCase() !== "README.MD") {
|
|
8353
8599
|
consider(full, entry.replace(/\.mdx?$/i, ""));
|
|
8354
8600
|
}
|
|
@@ -8397,7 +8643,7 @@ async function discoverAndIngestSkills() {
|
|
|
8397
8643
|
if (sf?.skills?.length) {
|
|
8398
8644
|
for (const fp of resolveSkillPaths(sf.skills, sf._repoRoot)) {
|
|
8399
8645
|
try {
|
|
8400
|
-
excludeHashes.add(createHash2("sha256").update(
|
|
8646
|
+
excludeHashes.add(createHash2("sha256").update(readFileSync20(fp, "utf-8")).digest("hex"));
|
|
8401
8647
|
} catch {
|
|
8402
8648
|
}
|
|
8403
8649
|
}
|
|
@@ -8428,13 +8674,13 @@ async function discoverAndIngestSkills() {
|
|
|
8428
8674
|
const setHash = discoverySetHash(selectable);
|
|
8429
8675
|
let prev = "";
|
|
8430
8676
|
try {
|
|
8431
|
-
prev =
|
|
8677
|
+
prev = readFileSync20(SKILLS_DISCOVERED_PATH, "utf-8").trim();
|
|
8432
8678
|
} catch {
|
|
8433
8679
|
}
|
|
8434
8680
|
if (prev === setHash) return;
|
|
8435
8681
|
const picks = await promptSkillDiscovery(found);
|
|
8436
8682
|
try {
|
|
8437
|
-
|
|
8683
|
+
writeFileSync16(SKILLS_DISCOVERED_PATH, setHash);
|
|
8438
8684
|
} catch {
|
|
8439
8685
|
}
|
|
8440
8686
|
if (picks.length === 0) {
|
|
@@ -8473,9 +8719,9 @@ function ensureReachabilityGitHook() {
|
|
|
8473
8719
|
const root = run("git rev-parse --show-toplevel");
|
|
8474
8720
|
if (!root) return null;
|
|
8475
8721
|
let hooksDir = run("git config --get core.hooksPath");
|
|
8476
|
-
hooksDir = hooksDir ? isAbsolute(hooksDir) ? hooksDir :
|
|
8477
|
-
if (!
|
|
8478
|
-
const hookPath =
|
|
8722
|
+
hooksDir = hooksDir ? isAbsolute(hooksDir) ? hooksDir : join18(root, hooksDir) : join18(root, ".git", "hooks");
|
|
8723
|
+
if (!existsSync22(hooksDir)) mkdirSync15(hooksDir, { recursive: true });
|
|
8724
|
+
const hookPath = join18(hooksDir, "post-commit");
|
|
8479
8725
|
const resolvedBin = resolveSynkroBinPath();
|
|
8480
8726
|
const invoke = resolvedBin ? `"${resolvedBin}" reachability-scan --quiet` : "true";
|
|
8481
8727
|
const START = "# >>> synkro reachability (managed) >>>";
|
|
@@ -8488,14 +8734,14 @@ function ensureReachabilityGitHook() {
|
|
|
8488
8734
|
END
|
|
8489
8735
|
].join("\n");
|
|
8490
8736
|
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8491
|
-
if (!
|
|
8492
|
-
|
|
8737
|
+
if (!existsSync22(hookPath)) {
|
|
8738
|
+
writeFileSync16(hookPath, "#!/bin/sh\n" + block + "\n", { mode: 493 });
|
|
8493
8739
|
return "installed";
|
|
8494
8740
|
}
|
|
8495
|
-
let cur =
|
|
8741
|
+
let cur = readFileSync20(hookPath, "utf-8");
|
|
8496
8742
|
if (cur.includes(START)) {
|
|
8497
8743
|
cur = cur.replace(new RegExp(esc(START) + "[\\s\\S]*?" + esc(END), "m"), block);
|
|
8498
|
-
|
|
8744
|
+
writeFileSync16(hookPath, cur);
|
|
8499
8745
|
try {
|
|
8500
8746
|
chmodSync4(hookPath, 493);
|
|
8501
8747
|
} catch {
|
|
@@ -8503,7 +8749,7 @@ function ensureReachabilityGitHook() {
|
|
|
8503
8749
|
return "updated";
|
|
8504
8750
|
}
|
|
8505
8751
|
const sep = cur.endsWith("\n") ? "" : "\n";
|
|
8506
|
-
|
|
8752
|
+
writeFileSync16(hookPath, cur + sep + "\n" + block + "\n");
|
|
8507
8753
|
try {
|
|
8508
8754
|
chmodSync4(hookPath, 493);
|
|
8509
8755
|
} catch {
|
|
@@ -8531,17 +8777,17 @@ function detectGitRepo2() {
|
|
|
8531
8777
|
function getClaudeProjectsFolder() {
|
|
8532
8778
|
const cwd = process.cwd();
|
|
8533
8779
|
const sanitized = "-" + cwd.replace(/\//g, "-");
|
|
8534
|
-
const projectsDir =
|
|
8535
|
-
return
|
|
8780
|
+
const projectsDir = join18(homedir20(), ".claude", "projects", sanitized);
|
|
8781
|
+
return existsSync22(projectsDir) ? projectsDir : null;
|
|
8536
8782
|
}
|
|
8537
8783
|
function extractSessionInsights(projectsDir) {
|
|
8538
8784
|
const insights = [];
|
|
8539
8785
|
const files = readdirSync4(projectsDir).filter((f) => f.endsWith(".jsonl"));
|
|
8540
8786
|
for (const file of files) {
|
|
8541
8787
|
const sessionId = file.replace(".jsonl", "");
|
|
8542
|
-
const filePath =
|
|
8788
|
+
const filePath = join18(projectsDir, file);
|
|
8543
8789
|
try {
|
|
8544
|
-
const content =
|
|
8790
|
+
const content = readFileSync20(filePath, "utf-8");
|
|
8545
8791
|
const lines = content.split("\n").filter(Boolean);
|
|
8546
8792
|
for (let i = 0; i < lines.length; i++) {
|
|
8547
8793
|
try {
|
|
@@ -8617,17 +8863,17 @@ function extractTextContent(content) {
|
|
|
8617
8863
|
return "";
|
|
8618
8864
|
}
|
|
8619
8865
|
function getCodexTranscriptFiles(repo) {
|
|
8620
|
-
const sessionsDir =
|
|
8621
|
-
if (!
|
|
8866
|
+
const sessionsDir = join18(process.env.CODEX_HOME || join18(homedir20(), ".codex"), "sessions");
|
|
8867
|
+
if (!existsSync22(sessionsDir)) return [];
|
|
8622
8868
|
let relative = [];
|
|
8623
8869
|
try {
|
|
8624
8870
|
relative = readdirSync4(sessionsDir, { recursive: true, encoding: "utf-8" });
|
|
8625
8871
|
} catch {
|
|
8626
8872
|
return [];
|
|
8627
8873
|
}
|
|
8628
|
-
return relative.filter((p) => p.endsWith(".jsonl")).map((p) =>
|
|
8874
|
+
return relative.filter((p) => p.endsWith(".jsonl")).map((p) => join18(sessionsDir, p)).filter((filePath) => {
|
|
8629
8875
|
try {
|
|
8630
|
-
const first =
|
|
8876
|
+
const first = readFileSync20(filePath, "utf-8").split("\n", 1)[0];
|
|
8631
8877
|
const meta = JSON.parse(first);
|
|
8632
8878
|
const cwd = typeof meta?.payload?.cwd === "string" ? resolve4(meta.payload.cwd) : "";
|
|
8633
8879
|
const root = resolve4(repo);
|
|
@@ -8638,7 +8884,7 @@ function getCodexTranscriptFiles(repo) {
|
|
|
8638
8884
|
});
|
|
8639
8885
|
}
|
|
8640
8886
|
function parseCodexTranscriptFile(filePath) {
|
|
8641
|
-
const lines =
|
|
8887
|
+
const lines = readFileSync20(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8642
8888
|
let sessionId = "";
|
|
8643
8889
|
let model = "";
|
|
8644
8890
|
const messages = [];
|
|
@@ -8693,7 +8939,7 @@ async function syncCodexTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8693
8939
|
totalSessions++;
|
|
8694
8940
|
totalMessages += result.ingested ?? messages.length;
|
|
8695
8941
|
}
|
|
8696
|
-
|
|
8942
|
+
writeFileSync16(join18(OFFSETS_DIR, parsed.sessionId), String(readFileSync20(files[i], "utf-8").split("\n").filter(Boolean).length), "utf-8");
|
|
8697
8943
|
} catch {
|
|
8698
8944
|
}
|
|
8699
8945
|
if ((i + 1) % 10 === 0 || i === files.length - 1) {
|
|
@@ -8707,14 +8953,14 @@ function cursorProjectSlug(workspaceRoot) {
|
|
|
8707
8953
|
return workspaceRoot.replace(/^[/]+/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8708
8954
|
}
|
|
8709
8955
|
function getCursorTranscriptsDir() {
|
|
8710
|
-
const dir =
|
|
8711
|
-
return
|
|
8956
|
+
const dir = join18(homedir20(), ".cursor", "projects", cursorProjectSlug(process.cwd()), "agent-transcripts");
|
|
8957
|
+
return existsSync22(dir) ? dir : null;
|
|
8712
8958
|
}
|
|
8713
8959
|
function isSafeConvId(id) {
|
|
8714
8960
|
return /^[A-Za-z0-9_-]+$/.test(id);
|
|
8715
8961
|
}
|
|
8716
8962
|
function parseCursorTranscriptFile(filePath) {
|
|
8717
|
-
const content =
|
|
8963
|
+
const content = readFileSync20(filePath, "utf-8");
|
|
8718
8964
|
const lines = content.split("\n").filter(Boolean);
|
|
8719
8965
|
const messages = [];
|
|
8720
8966
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -8746,8 +8992,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8746
8992
|
for (let i = 0; i < convDirs.length; i++) {
|
|
8747
8993
|
const convId = convDirs[i];
|
|
8748
8994
|
if (!isSafeConvId(convId)) continue;
|
|
8749
|
-
const filePath =
|
|
8750
|
-
if (!
|
|
8995
|
+
const filePath = join18(dir, convId, `${convId}.jsonl`);
|
|
8996
|
+
if (!existsSync22(filePath)) continue;
|
|
8751
8997
|
try {
|
|
8752
8998
|
const all = parseCursorTranscriptFile(filePath);
|
|
8753
8999
|
const messages = all.length > 500 ? all.slice(-500) : all;
|
|
@@ -8769,8 +9015,8 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8769
9015
|
process.stdout.write(`\r Progress: ${i + 1}/${convDirs.length} sessions (${totalMessages} messages embedded) `);
|
|
8770
9016
|
}
|
|
8771
9017
|
try {
|
|
8772
|
-
const lc =
|
|
8773
|
-
|
|
9018
|
+
const lc = readFileSync20(filePath, "utf-8").split("\n").filter(Boolean).length;
|
|
9019
|
+
writeFileSync16(join18(OFFSETS_DIR, convId), String(lc), "utf-8");
|
|
8774
9020
|
} catch {
|
|
8775
9021
|
}
|
|
8776
9022
|
}
|
|
@@ -8778,7 +9024,7 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8778
9024
|
return { sessions: totalSessions, messages: totalMessages };
|
|
8779
9025
|
}
|
|
8780
9026
|
function parseTranscriptFile(filePath) {
|
|
8781
|
-
const content =
|
|
9027
|
+
const content = readFileSync20(filePath, "utf-8");
|
|
8782
9028
|
const lines = content.split("\n").filter(Boolean);
|
|
8783
9029
|
const messages = [];
|
|
8784
9030
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -8826,7 +9072,7 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8826
9072
|
for (let i = 0; i < files.length; i++) {
|
|
8827
9073
|
const file = files[i];
|
|
8828
9074
|
const sessionId = file.replace(".jsonl", "");
|
|
8829
|
-
const filePath =
|
|
9075
|
+
const filePath = join18(projectsDir, file);
|
|
8830
9076
|
try {
|
|
8831
9077
|
const allMessages = parseTranscriptFile(filePath);
|
|
8832
9078
|
const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
|
|
@@ -8848,9 +9094,9 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
8848
9094
|
process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
|
|
8849
9095
|
}
|
|
8850
9096
|
try {
|
|
8851
|
-
const content =
|
|
9097
|
+
const content = readFileSync20(join18(projectsDir, file), "utf-8");
|
|
8852
9098
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
8853
|
-
|
|
9099
|
+
writeFileSync16(join18(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
|
|
8854
9100
|
} catch {
|
|
8855
9101
|
}
|
|
8856
9102
|
}
|
|
@@ -8871,7 +9117,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
8871
9117
|
const sessions = [];
|
|
8872
9118
|
for (const file of batch) {
|
|
8873
9119
|
const sessionId = file.replace(".jsonl", "");
|
|
8874
|
-
const filePath =
|
|
9120
|
+
const filePath = join18(projectsDir, file);
|
|
8875
9121
|
try {
|
|
8876
9122
|
const allMessages = parseTranscriptFile(filePath);
|
|
8877
9123
|
const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
|
|
@@ -8900,11 +9146,11 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
8900
9146
|
}
|
|
8901
9147
|
for (const file of batch) {
|
|
8902
9148
|
const sessionId = file.replace(".jsonl", "");
|
|
8903
|
-
const filePath =
|
|
9149
|
+
const filePath = join18(projectsDir, file);
|
|
8904
9150
|
try {
|
|
8905
|
-
const content =
|
|
9151
|
+
const content = readFileSync20(filePath, "utf-8");
|
|
8906
9152
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
8907
|
-
|
|
9153
|
+
writeFileSync16(join18(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
|
|
8908
9154
|
} catch {
|
|
8909
9155
|
}
|
|
8910
9156
|
}
|
|
@@ -8944,7 +9190,7 @@ async function syncCodexTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
8944
9190
|
try {
|
|
8945
9191
|
const parsed = parseCodexTranscriptFile(filePath);
|
|
8946
9192
|
if (parsed.sessionId) {
|
|
8947
|
-
|
|
9193
|
+
writeFileSync16(join18(OFFSETS_DIR, parsed.sessionId), String(readFileSync20(filePath, "utf-8").split("\n").filter(Boolean).length), "utf-8");
|
|
8948
9194
|
}
|
|
8949
9195
|
} catch {
|
|
8950
9196
|
}
|
|
@@ -8952,7 +9198,7 @@ async function syncCodexTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
8952
9198
|
}
|
|
8953
9199
|
return { sessions: totalSessions, messages: totalMessages };
|
|
8954
9200
|
}
|
|
8955
|
-
var
|
|
9201
|
+
var SYNKRO_DIR11, HOOKS_DIR, BIN_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
|
|
8956
9202
|
var init_install = __esm({
|
|
8957
9203
|
"cli/commands/install.ts"() {
|
|
8958
9204
|
"use strict";
|
|
@@ -8972,11 +9218,13 @@ var init_install = __esm({
|
|
|
8972
9218
|
init_claudeDesktopTap();
|
|
8973
9219
|
init_dockerInstall();
|
|
8974
9220
|
init_setupToken();
|
|
9221
|
+
init_codexCloudSetup();
|
|
8975
9222
|
init_ptyShim();
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
9223
|
+
init_graderSmoke();
|
|
9224
|
+
SYNKRO_DIR11 = join18(homedir20(), ".synkro");
|
|
9225
|
+
HOOKS_DIR = join18(SYNKRO_DIR11, "hooks");
|
|
9226
|
+
BIN_DIR = join18(SYNKRO_DIR11, "bin");
|
|
9227
|
+
CONFIG_PATH4 = join18(SYNKRO_DIR11, "config.env");
|
|
8980
9228
|
MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
|
|
8981
9229
|
import { readFileSync } from 'node:fs';
|
|
8982
9230
|
import { homedir } from 'node:os';
|
|
@@ -9087,23 +9335,23 @@ rl.on('line', async (line) => {
|
|
|
9087
9335
|
}
|
|
9088
9336
|
});
|
|
9089
9337
|
`;
|
|
9090
|
-
OFFSETS_DIR =
|
|
9091
|
-
CLOUD_JWT_PATH =
|
|
9092
|
-
SKILLS_DISCOVERED_PATH =
|
|
9338
|
+
OFFSETS_DIR = join18(SYNKRO_DIR11, ".transcript-offsets");
|
|
9339
|
+
CLOUD_JWT_PATH = join18(SYNKRO_DIR11, ".cloud-jwt");
|
|
9340
|
+
SKILLS_DISCOVERED_PATH = join18(SYNKRO_DIR11, ".skills-discovered");
|
|
9093
9341
|
}
|
|
9094
9342
|
});
|
|
9095
9343
|
|
|
9096
9344
|
// cli/local-cc/install.ts
|
|
9097
|
-
import { existsSync as
|
|
9098
|
-
import { join as
|
|
9099
|
-
import { homedir as
|
|
9100
|
-
import { spawnSync as
|
|
9345
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync16, writeFileSync as writeFileSync17, readFileSync as readFileSync21, chmodSync as chmodSync5, copyFileSync as copyFileSync2, renameSync as renameSync7, unlinkSync as unlinkSync8, openSync as openSync2, fsyncSync, closeSync as closeSync2 } from "fs";
|
|
9346
|
+
import { join as join19 } from "path";
|
|
9347
|
+
import { homedir as homedir21 } from "os";
|
|
9348
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
9101
9349
|
function writePluginFiles() {
|
|
9102
9350
|
for (const c of CHANNELS) {
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9351
|
+
mkdirSync16(c.sessionDir, { recursive: true });
|
|
9352
|
+
mkdirSync16(c.pluginSettingsDir, { recursive: true });
|
|
9353
|
+
writeFileSync17(c.pluginPkgPath, PLUGIN_PACKAGE_JSON, "utf-8");
|
|
9354
|
+
writeFileSync17(
|
|
9107
9355
|
c.pluginSettingsPath,
|
|
9108
9356
|
JSON.stringify({
|
|
9109
9357
|
fastMode: true,
|
|
@@ -9118,13 +9366,13 @@ function writePluginFiles() {
|
|
|
9118
9366
|
}, null, 2) + "\n",
|
|
9119
9367
|
"utf-8"
|
|
9120
9368
|
);
|
|
9121
|
-
|
|
9369
|
+
writeFileSync17(c.runScriptPath, c.runScriptSource, "utf-8");
|
|
9122
9370
|
chmodSync5(c.runScriptPath, 493);
|
|
9123
9371
|
}
|
|
9124
9372
|
}
|
|
9125
9373
|
function runBunInstall() {
|
|
9126
9374
|
for (const c of CHANNELS) {
|
|
9127
|
-
const r =
|
|
9375
|
+
const r = spawnSync7("bun", ["install", "--silent"], {
|
|
9128
9376
|
cwd: c.sessionDir,
|
|
9129
9377
|
encoding: "utf-8",
|
|
9130
9378
|
timeout: 12e4
|
|
@@ -9137,10 +9385,10 @@ function runBunInstall() {
|
|
|
9137
9385
|
}
|
|
9138
9386
|
}
|
|
9139
9387
|
function safelyMutateClaudeJson(mutator) {
|
|
9140
|
-
if (!
|
|
9388
|
+
if (!existsSync23(CLAUDE_JSON_PATH)) {
|
|
9141
9389
|
return;
|
|
9142
9390
|
}
|
|
9143
|
-
const originalText =
|
|
9391
|
+
const originalText = readFileSync21(CLAUDE_JSON_PATH, "utf-8");
|
|
9144
9392
|
let parsed;
|
|
9145
9393
|
try {
|
|
9146
9394
|
parsed = JSON.parse(originalText);
|
|
@@ -9172,7 +9420,7 @@ function safelyMutateClaudeJson(mutator) {
|
|
|
9172
9420
|
copyFileSync2(CLAUDE_JSON_PATH, CLAUDE_JSON_BACKUP_PATH);
|
|
9173
9421
|
const tmpPath = `${CLAUDE_JSON_PATH}.synkro-tmp.${process.pid}`;
|
|
9174
9422
|
try {
|
|
9175
|
-
|
|
9423
|
+
writeFileSync17(tmpPath, newText, "utf-8");
|
|
9176
9424
|
const fd = openSync2(tmpPath, "r");
|
|
9177
9425
|
try {
|
|
9178
9426
|
fsyncSync(fd);
|
|
@@ -9205,7 +9453,7 @@ function writeProjectMcpJson() {
|
|
|
9205
9453
|
}
|
|
9206
9454
|
}
|
|
9207
9455
|
};
|
|
9208
|
-
|
|
9456
|
+
writeFileSync17(c.projectMcpPath, JSON.stringify(mcp, null, 2) + "\n", "utf-8");
|
|
9209
9457
|
}
|
|
9210
9458
|
}
|
|
9211
9459
|
function patchClaudeJson() {
|
|
@@ -9240,15 +9488,15 @@ function patchClaudeJson() {
|
|
|
9240
9488
|
});
|
|
9241
9489
|
}
|
|
9242
9490
|
function installLocalCC() {
|
|
9243
|
-
let bunCheck =
|
|
9491
|
+
let bunCheck = spawnSync7("bun", ["--version"], { encoding: "utf-8" });
|
|
9244
9492
|
if (bunCheck.status !== 0) {
|
|
9245
9493
|
if (process.platform === "darwin") {
|
|
9246
9494
|
console.log(" Installing bun via brew...");
|
|
9247
|
-
const brewR =
|
|
9495
|
+
const brewR = spawnSync7("brew", ["install", "oven-sh/bun/bun"], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
9248
9496
|
if (brewR.status !== 0) {
|
|
9249
9497
|
throw new LocalCCInstallError("bun auto-install failed. Install manually: curl -fsSL https://bun.sh/install | bash");
|
|
9250
9498
|
}
|
|
9251
|
-
bunCheck =
|
|
9499
|
+
bunCheck = spawnSync7("bun", ["--version"], { encoding: "utf-8" });
|
|
9252
9500
|
if (bunCheck.status !== 0) {
|
|
9253
9501
|
throw new LocalCCInstallError("bun installed but not found on PATH. Restart your terminal and re-run install.");
|
|
9254
9502
|
}
|
|
@@ -9282,42 +9530,42 @@ var CLAUDE_JSON_BACKUP_PATH, SESSION_DIR, PLUGIN_PATH, PLUGIN_PKG_PATH, PLUGIN_S
|
|
|
9282
9530
|
var init_install2 = __esm({
|
|
9283
9531
|
"cli/local-cc/install.ts"() {
|
|
9284
9532
|
"use strict";
|
|
9285
|
-
CLAUDE_JSON_BACKUP_PATH =
|
|
9286
|
-
SESSION_DIR =
|
|
9287
|
-
PLUGIN_PATH =
|
|
9288
|
-
PLUGIN_PKG_PATH =
|
|
9289
|
-
PLUGIN_SETTINGS_DIR =
|
|
9290
|
-
PLUGIN_SETTINGS_PATH =
|
|
9291
|
-
PROJECT_MCP_PATH =
|
|
9292
|
-
CLAUDE_JSON_PATH =
|
|
9293
|
-
RUN_SCRIPT_PATH =
|
|
9533
|
+
CLAUDE_JSON_BACKUP_PATH = join19(homedir21(), ".claude.json.synkro-bak");
|
|
9534
|
+
SESSION_DIR = join19(homedir21(), ".synkro", "cc_sessions");
|
|
9535
|
+
PLUGIN_PATH = join19(SESSION_DIR, "synkro-channel.ts");
|
|
9536
|
+
PLUGIN_PKG_PATH = join19(SESSION_DIR, "package.json");
|
|
9537
|
+
PLUGIN_SETTINGS_DIR = join19(SESSION_DIR, ".claude");
|
|
9538
|
+
PLUGIN_SETTINGS_PATH = join19(PLUGIN_SETTINGS_DIR, "settings.json");
|
|
9539
|
+
PROJECT_MCP_PATH = join19(SESSION_DIR, ".mcp.json");
|
|
9540
|
+
CLAUDE_JSON_PATH = join19(homedir21(), ".claude.json");
|
|
9541
|
+
RUN_SCRIPT_PATH = join19(SESSION_DIR, "run-claude.sh");
|
|
9294
9542
|
TMUX_SESSION_NAME = "synkro-local-cc";
|
|
9295
9543
|
CHANNEL_1_PORT = 8941;
|
|
9296
|
-
SESSION_DIR_2 =
|
|
9297
|
-
PLUGIN_PATH_2 =
|
|
9298
|
-
PLUGIN_PKG_PATH_2 =
|
|
9299
|
-
PLUGIN_SETTINGS_DIR_2 =
|
|
9300
|
-
PLUGIN_SETTINGS_PATH_2 =
|
|
9301
|
-
PROJECT_MCP_PATH_2 =
|
|
9302
|
-
RUN_SCRIPT_PATH_2 =
|
|
9544
|
+
SESSION_DIR_2 = join19(homedir21(), ".synkro", "cc_sessions_2");
|
|
9545
|
+
PLUGIN_PATH_2 = join19(SESSION_DIR_2, "synkro-channel.ts");
|
|
9546
|
+
PLUGIN_PKG_PATH_2 = join19(SESSION_DIR_2, "package.json");
|
|
9547
|
+
PLUGIN_SETTINGS_DIR_2 = join19(SESSION_DIR_2, ".claude");
|
|
9548
|
+
PLUGIN_SETTINGS_PATH_2 = join19(PLUGIN_SETTINGS_DIR_2, "settings.json");
|
|
9549
|
+
PROJECT_MCP_PATH_2 = join19(SESSION_DIR_2, ".mcp.json");
|
|
9550
|
+
RUN_SCRIPT_PATH_2 = join19(SESSION_DIR_2, "run-claude.sh");
|
|
9303
9551
|
TMUX_SESSION_NAME_2 = "synkro-local-cc-2";
|
|
9304
9552
|
CHANNEL_2_PORT = 8951;
|
|
9305
|
-
SESSION_DIR_3 =
|
|
9306
|
-
PLUGIN_PATH_3 =
|
|
9307
|
-
PLUGIN_PKG_PATH_3 =
|
|
9308
|
-
PLUGIN_SETTINGS_DIR_3 =
|
|
9309
|
-
PLUGIN_SETTINGS_PATH_3 =
|
|
9310
|
-
PROJECT_MCP_PATH_3 =
|
|
9311
|
-
RUN_SCRIPT_PATH_3 =
|
|
9553
|
+
SESSION_DIR_3 = join19(homedir21(), ".synkro", "cc_sessions_3");
|
|
9554
|
+
PLUGIN_PATH_3 = join19(SESSION_DIR_3, "synkro-channel.ts");
|
|
9555
|
+
PLUGIN_PKG_PATH_3 = join19(SESSION_DIR_3, "package.json");
|
|
9556
|
+
PLUGIN_SETTINGS_DIR_3 = join19(SESSION_DIR_3, ".claude");
|
|
9557
|
+
PLUGIN_SETTINGS_PATH_3 = join19(PLUGIN_SETTINGS_DIR_3, "settings.json");
|
|
9558
|
+
PROJECT_MCP_PATH_3 = join19(SESSION_DIR_3, ".mcp.json");
|
|
9559
|
+
RUN_SCRIPT_PATH_3 = join19(SESSION_DIR_3, "run-claude.sh");
|
|
9312
9560
|
TMUX_SESSION_NAME_3 = "synkro-local-cc-3";
|
|
9313
9561
|
CHANNEL_3_PORT = 8942;
|
|
9314
|
-
SESSION_DIR_4 =
|
|
9315
|
-
PLUGIN_PATH_4 =
|
|
9316
|
-
PLUGIN_PKG_PATH_4 =
|
|
9317
|
-
PLUGIN_SETTINGS_DIR_4 =
|
|
9318
|
-
PLUGIN_SETTINGS_PATH_4 =
|
|
9319
|
-
PROJECT_MCP_PATH_4 =
|
|
9320
|
-
RUN_SCRIPT_PATH_4 =
|
|
9562
|
+
SESSION_DIR_4 = join19(homedir21(), ".synkro", "cc_sessions_4");
|
|
9563
|
+
PLUGIN_PATH_4 = join19(SESSION_DIR_4, "synkro-channel.ts");
|
|
9564
|
+
PLUGIN_PKG_PATH_4 = join19(SESSION_DIR_4, "package.json");
|
|
9565
|
+
PLUGIN_SETTINGS_DIR_4 = join19(SESSION_DIR_4, ".claude");
|
|
9566
|
+
PLUGIN_SETTINGS_PATH_4 = join19(PLUGIN_SETTINGS_DIR_4, "settings.json");
|
|
9567
|
+
PROJECT_MCP_PATH_4 = join19(SESSION_DIR_4, ".mcp.json");
|
|
9568
|
+
RUN_SCRIPT_PATH_4 = join19(SESSION_DIR_4, "run-claude.sh");
|
|
9321
9569
|
TMUX_SESSION_NAME_4 = "synkro-local-cc-4";
|
|
9322
9570
|
CHANNEL_4_PORT = 8952;
|
|
9323
9571
|
RUN_SCRIPT_SOURCE = `#!/usr/bin/env bash
|
|
@@ -9591,10 +9839,10 @@ var disconnect_exports = {};
|
|
|
9591
9839
|
__export(disconnect_exports, {
|
|
9592
9840
|
disconnectCommand: () => disconnectCommand
|
|
9593
9841
|
});
|
|
9594
|
-
import { existsSync as
|
|
9595
|
-
import { homedir as
|
|
9596
|
-
import { join as
|
|
9597
|
-
import { spawnSync as
|
|
9842
|
+
import { existsSync as existsSync24, rmSync as rmSync3, readdirSync as readdirSync5 } from "fs";
|
|
9843
|
+
import { homedir as homedir22 } from "os";
|
|
9844
|
+
import { join as join20 } from "path";
|
|
9845
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
9598
9846
|
import { createInterface as createInterface3 } from "readline";
|
|
9599
9847
|
async function tearDownLocalCC() {
|
|
9600
9848
|
const docker = dockerStatus();
|
|
@@ -9608,7 +9856,7 @@ async function tearDownLocalCC() {
|
|
|
9608
9856
|
console.log("\u2713 removed synkro-server container");
|
|
9609
9857
|
try {
|
|
9610
9858
|
const image = imageTag();
|
|
9611
|
-
const r =
|
|
9859
|
+
const r = spawnSync8("docker", ["rmi", "-f", image], { encoding: "utf-8", timeout: 3e4 });
|
|
9612
9860
|
console.log(r.status === 0 ? `\u2713 removed Docker image ${image}` : "\xB7 no Docker image to remove");
|
|
9613
9861
|
} catch {
|
|
9614
9862
|
}
|
|
@@ -9694,7 +9942,7 @@ async function disconnectCommand(args2 = [], opts = {}) {
|
|
|
9694
9942
|
if (desktopMcpRemoved) removed.mcp = true;
|
|
9695
9943
|
console.log(`${desktopMcpRemoved ? "\u2713" : "\xB7"} MCP guardrails (Claude Desktop): ${desktopMcpRemoved ? "removed from claude_desktop_config.json" : "no entry found"}`);
|
|
9696
9944
|
}
|
|
9697
|
-
if (
|
|
9945
|
+
if (existsSync24(SYNKRO_DIR12)) {
|
|
9698
9946
|
if (purge2) {
|
|
9699
9947
|
emit("uninstall", {
|
|
9700
9948
|
flavor,
|
|
@@ -9703,41 +9951,41 @@ async function disconnectCommand(args2 = [], opts = {}) {
|
|
|
9703
9951
|
duration_ms: Date.now() - startedAt
|
|
9704
9952
|
});
|
|
9705
9953
|
await flush({ force: true });
|
|
9706
|
-
|
|
9954
|
+
rmSync3(SYNKRO_DIR12, { recursive: true, force: true });
|
|
9707
9955
|
removed.config = true;
|
|
9708
|
-
console.log(`\u2713 wiped ${
|
|
9956
|
+
console.log(`\u2713 wiped ${SYNKRO_DIR12} entirely \u2014 including all scan data and backups`);
|
|
9709
9957
|
} else {
|
|
9710
9958
|
const keep = /* @__PURE__ */ new Set([
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9959
|
+
join20(SYNKRO_DIR12, "pgdata"),
|
|
9960
|
+
join20(SYNKRO_DIR12, "pgdata-backups"),
|
|
9961
|
+
join20(SYNKRO_DIR12, ".transcript-offsets")
|
|
9714
9962
|
]);
|
|
9715
9963
|
const preserved = [];
|
|
9716
|
-
for (const entry of readdirSync5(
|
|
9717
|
-
const full =
|
|
9964
|
+
for (const entry of readdirSync5(SYNKRO_DIR12)) {
|
|
9965
|
+
const full = join20(SYNKRO_DIR12, entry);
|
|
9718
9966
|
if (keep.has(full)) {
|
|
9719
9967
|
preserved.push(entry);
|
|
9720
9968
|
continue;
|
|
9721
9969
|
}
|
|
9722
|
-
|
|
9970
|
+
rmSync3(full, { recursive: true, force: true });
|
|
9723
9971
|
}
|
|
9724
9972
|
removed.config = true;
|
|
9725
9973
|
if (preserved.length > 0) {
|
|
9726
|
-
console.log(`\u2713 removed Synkro config from ${
|
|
9974
|
+
console.log(`\u2713 removed Synkro config from ${SYNKRO_DIR12} (kept your scan data: ${preserved.join(", ")})`);
|
|
9727
9975
|
console.log(" run `synkro uninstall --purge` to delete that too");
|
|
9728
9976
|
} else {
|
|
9729
|
-
console.log(`\u2713 removed ${
|
|
9977
|
+
console.log(`\u2713 removed ${SYNKRO_DIR12}`);
|
|
9730
9978
|
}
|
|
9731
9979
|
}
|
|
9732
9980
|
} else {
|
|
9733
|
-
console.log(`\xB7 ${
|
|
9981
|
+
console.log(`\xB7 ${SYNKRO_DIR12} already gone`);
|
|
9734
9982
|
}
|
|
9735
9983
|
if (!purge2) {
|
|
9736
9984
|
emit("uninstall", { flavor, purge: purge2, removed, duration_ms: Date.now() - startedAt });
|
|
9737
9985
|
}
|
|
9738
9986
|
console.log(purge2 ? "\nSynkro fully removed \u2014 this machine is clean, as if it was never installed." : "\nSynkro uninstalled. Your scan data is preserved.");
|
|
9739
9987
|
}
|
|
9740
|
-
var
|
|
9988
|
+
var SYNKRO_DIR12;
|
|
9741
9989
|
var init_disconnect = __esm({
|
|
9742
9990
|
"cli/commands/disconnect.ts"() {
|
|
9743
9991
|
"use strict";
|
|
@@ -9752,14 +10000,14 @@ var init_disconnect = __esm({
|
|
|
9752
10000
|
init_dockerInstall();
|
|
9753
10001
|
init_macKeychain();
|
|
9754
10002
|
init_telemetry();
|
|
9755
|
-
|
|
10003
|
+
SYNKRO_DIR12 = join20(homedir22(), ".synkro");
|
|
9756
10004
|
}
|
|
9757
10005
|
});
|
|
9758
10006
|
|
|
9759
10007
|
// cli/local-cc/turnLog.ts
|
|
9760
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
9761
|
-
import { dirname as dirname7, join as
|
|
9762
|
-
import { homedir as
|
|
10008
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync25, mkdirSync as mkdirSync17, openSync as openSync3, readFileSync as readFileSync22, readSync, closeSync as closeSync3, statSync as statSync3, watchFile, unwatchFile } from "fs";
|
|
10009
|
+
import { dirname as dirname7, join as join21 } from "path";
|
|
10010
|
+
import { homedir as homedir23 } from "os";
|
|
9763
10011
|
function truncate(s, max = PREVIEW_MAX) {
|
|
9764
10012
|
if (s.length <= max) return s;
|
|
9765
10013
|
return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
|
|
@@ -9779,7 +10027,7 @@ function extractSeverity(result) {
|
|
|
9779
10027
|
}
|
|
9780
10028
|
function appendTurn(args2) {
|
|
9781
10029
|
try {
|
|
9782
|
-
|
|
10030
|
+
mkdirSync17(dirname7(TURN_LOG_PATH), { recursive: true });
|
|
9783
10031
|
const entry = {
|
|
9784
10032
|
ts: new Date(args2.startedAt).toISOString(),
|
|
9785
10033
|
role: args2.role,
|
|
@@ -9795,11 +10043,11 @@ function appendTurn(args2) {
|
|
|
9795
10043
|
}
|
|
9796
10044
|
}
|
|
9797
10045
|
function readRecentTurns(n = 20) {
|
|
9798
|
-
if (!
|
|
10046
|
+
if (!existsSync25(TURN_LOG_PATH)) return [];
|
|
9799
10047
|
try {
|
|
9800
10048
|
const size = statSync3(TURN_LOG_PATH).size;
|
|
9801
10049
|
if (size === 0) return [];
|
|
9802
|
-
const text =
|
|
10050
|
+
const text = readFileSync22(TURN_LOG_PATH, "utf-8");
|
|
9803
10051
|
const lines = text.split("\n").filter(Boolean);
|
|
9804
10052
|
const lastN = lines.slice(-n).reverse();
|
|
9805
10053
|
return lastN.map((line) => {
|
|
@@ -9815,8 +10063,8 @@ function readRecentTurns(n = 20) {
|
|
|
9815
10063
|
}
|
|
9816
10064
|
function followTurns(onEntry) {
|
|
9817
10065
|
try {
|
|
9818
|
-
|
|
9819
|
-
if (!
|
|
10066
|
+
mkdirSync17(dirname7(TURN_LOG_PATH), { recursive: true });
|
|
10067
|
+
if (!existsSync25(TURN_LOG_PATH)) {
|
|
9820
10068
|
appendFileSync3(TURN_LOG_PATH, "", "utf-8");
|
|
9821
10069
|
}
|
|
9822
10070
|
} catch {
|
|
@@ -9878,7 +10126,7 @@ var TURN_LOG_PATH, PREVIEW_MAX;
|
|
|
9878
10126
|
var init_turnLog = __esm({
|
|
9879
10127
|
"cli/local-cc/turnLog.ts"() {
|
|
9880
10128
|
"use strict";
|
|
9881
|
-
TURN_LOG_PATH =
|
|
10129
|
+
TURN_LOG_PATH = join21(homedir23(), ".synkro", "cc_sessions", "turns.log");
|
|
9882
10130
|
PREVIEW_MAX = 400;
|
|
9883
10131
|
}
|
|
9884
10132
|
});
|
|
@@ -10032,8 +10280,8 @@ __export(scanPr_exports, {
|
|
|
10032
10280
|
scanPrCommand: () => scanPrCommand
|
|
10033
10281
|
});
|
|
10034
10282
|
import { execSync as execSync5, spawn as spawn5 } from "child_process";
|
|
10035
|
-
import { readFileSync as
|
|
10036
|
-
import { join as
|
|
10283
|
+
import { readFileSync as readFileSync23, existsSync as existsSync26 } from "fs";
|
|
10284
|
+
import { join as join22 } from "path";
|
|
10037
10285
|
function parseMatchSpec(condition) {
|
|
10038
10286
|
if (!condition.startsWith("match_spec:")) return null;
|
|
10039
10287
|
try {
|
|
@@ -10512,10 +10760,10 @@ function shouldFail(findings, threshold) {
|
|
|
10512
10760
|
return findings.some((f) => order.indexOf(f.severity) >= thresholdIdx);
|
|
10513
10761
|
}
|
|
10514
10762
|
function readRepoDeps() {
|
|
10515
|
-
const pkgPath =
|
|
10516
|
-
if (!
|
|
10763
|
+
const pkgPath = join22(process.cwd(), "package.json");
|
|
10764
|
+
if (!existsSync26(pkgPath)) return {};
|
|
10517
10765
|
try {
|
|
10518
|
-
const pkg = JSON.parse(
|
|
10766
|
+
const pkg = JSON.parse(readFileSync23(pkgPath, "utf-8"));
|
|
10519
10767
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
10520
10768
|
} catch {
|
|
10521
10769
|
return {};
|
|
@@ -10759,15 +11007,15 @@ var routeDecide_exports = {};
|
|
|
10759
11007
|
__export(routeDecide_exports, {
|
|
10760
11008
|
routeDecide: () => routeDecide
|
|
10761
11009
|
});
|
|
10762
|
-
import { readFileSync as
|
|
10763
|
-
import { homedir as
|
|
10764
|
-
import { join as
|
|
11010
|
+
import { readFileSync as readFileSync24, writeFileSync as writeFileSync18 } from "fs";
|
|
11011
|
+
import { homedir as homedir24 } from "os";
|
|
11012
|
+
import { join as join23 } from "path";
|
|
10765
11013
|
function safeSid(sid) {
|
|
10766
11014
|
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
10767
11015
|
}
|
|
10768
11016
|
function loadMcpJwt() {
|
|
10769
11017
|
try {
|
|
10770
|
-
return
|
|
11018
|
+
return readFileSync24(join23(SYNKRO_DIR13, ".mcp-jwt"), "utf-8").trim();
|
|
10771
11019
|
} catch {
|
|
10772
11020
|
return "";
|
|
10773
11021
|
}
|
|
@@ -10777,7 +11025,7 @@ async function routeDecide(sessionId) {
|
|
|
10777
11025
|
const sid = safeSid(sessionId);
|
|
10778
11026
|
let prompt = "";
|
|
10779
11027
|
try {
|
|
10780
|
-
const rec = JSON.parse(
|
|
11028
|
+
const rec = JSON.parse(readFileSync24(join23(SESSIONS_DIR2, sid + ".json"), "utf-8"));
|
|
10781
11029
|
prompt = String(rec.last_prompt || "").trim();
|
|
10782
11030
|
} catch {
|
|
10783
11031
|
return;
|
|
@@ -10800,29 +11048,29 @@ async function routeDecide(sessionId) {
|
|
|
10800
11048
|
return;
|
|
10801
11049
|
}
|
|
10802
11050
|
if (!model || !VALID_MODELS.has(model)) return;
|
|
10803
|
-
const lastFile =
|
|
11051
|
+
const lastFile = join23(PTY_DIR, "route-last-" + sid);
|
|
10804
11052
|
let last = "";
|
|
10805
11053
|
try {
|
|
10806
|
-
last =
|
|
11054
|
+
last = readFileSync24(lastFile, "utf-8").trim();
|
|
10807
11055
|
} catch {
|
|
10808
11056
|
}
|
|
10809
11057
|
if (model === last) return;
|
|
10810
11058
|
try {
|
|
10811
|
-
|
|
11059
|
+
writeFileSync18(lastFile, model);
|
|
10812
11060
|
} catch {
|
|
10813
11061
|
}
|
|
10814
11062
|
try {
|
|
10815
|
-
|
|
11063
|
+
writeFileSync18(join23(PTY_DIR, "route-" + sid), model);
|
|
10816
11064
|
} catch {
|
|
10817
11065
|
}
|
|
10818
11066
|
}
|
|
10819
|
-
var
|
|
11067
|
+
var SYNKRO_DIR13, PTY_DIR, SESSIONS_DIR2, VALID_MODELS, GRADER_HOST_PORT;
|
|
10820
11068
|
var init_routeDecide = __esm({
|
|
10821
11069
|
"cli/local-cc/routeDecide.ts"() {
|
|
10822
11070
|
"use strict";
|
|
10823
|
-
|
|
10824
|
-
PTY_DIR =
|
|
10825
|
-
SESSIONS_DIR2 =
|
|
11071
|
+
SYNKRO_DIR13 = join23(homedir24(), ".synkro");
|
|
11072
|
+
PTY_DIR = join23(SYNKRO_DIR13, "pty");
|
|
11073
|
+
SESSIONS_DIR2 = join23(PTY_DIR, "sessions");
|
|
10826
11074
|
VALID_MODELS = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
|
|
10827
11075
|
GRADER_HOST_PORT = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
|
|
10828
11076
|
}
|
|
@@ -10833,10 +11081,10 @@ var routeOrchestrate_exports = {};
|
|
|
10833
11081
|
__export(routeOrchestrate_exports, {
|
|
10834
11082
|
routeAndResubmit: () => routeAndResubmit
|
|
10835
11083
|
});
|
|
10836
|
-
import { readFileSync as
|
|
10837
|
-
import { homedir as
|
|
10838
|
-
import { join as
|
|
10839
|
-
import { spawnSync as
|
|
11084
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "fs";
|
|
11085
|
+
import { homedir as homedir25 } from "os";
|
|
11086
|
+
import { join as join24 } from "path";
|
|
11087
|
+
import { spawnSync as spawnSync9 } from "child_process";
|
|
10840
11088
|
function safeSid2(sid) {
|
|
10841
11089
|
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
10842
11090
|
}
|
|
@@ -10845,7 +11093,7 @@ function safeSession(s) {
|
|
|
10845
11093
|
}
|
|
10846
11094
|
function loadMcpJwt2() {
|
|
10847
11095
|
try {
|
|
10848
|
-
return
|
|
11096
|
+
return readFileSync25(join24(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
10849
11097
|
} catch {
|
|
10850
11098
|
return "";
|
|
10851
11099
|
}
|
|
@@ -10871,7 +11119,7 @@ async function classifyTask(prompt) {
|
|
|
10871
11119
|
}
|
|
10872
11120
|
function lastRoutedModel(sid) {
|
|
10873
11121
|
try {
|
|
10874
|
-
const v =
|
|
11122
|
+
const v = readFileSync25(join24(PTY_DIR2, "route-last-" + sid), "utf-8").trim();
|
|
10875
11123
|
return VALID_MODELS2.has(v) ? v : "";
|
|
10876
11124
|
} catch {
|
|
10877
11125
|
return "";
|
|
@@ -10881,12 +11129,12 @@ function resolveSession(sid, tmuxSession) {
|
|
|
10881
11129
|
const candidates = [];
|
|
10882
11130
|
if (tmuxSession) candidates.push(tmuxSession);
|
|
10883
11131
|
try {
|
|
10884
|
-
const rec = JSON.parse(
|
|
11132
|
+
const rec = JSON.parse(readFileSync25(join24(SESSIONS_DIR3, safeSid2(sid) + ".json"), "utf-8"));
|
|
10885
11133
|
if (rec.tmux_session) candidates.push(rec.tmux_session);
|
|
10886
11134
|
} catch {
|
|
10887
11135
|
}
|
|
10888
11136
|
try {
|
|
10889
|
-
candidates.push(
|
|
11137
|
+
candidates.push(readFileSync25(ACTIVE_SESSION_FILE2, "utf-8").trim());
|
|
10890
11138
|
} catch {
|
|
10891
11139
|
}
|
|
10892
11140
|
for (const c of candidates) if (c && safeSession(c)) return c;
|
|
@@ -10898,12 +11146,12 @@ async function routeAndResubmit(sessionId, task, tmuxSession, forceModel) {
|
|
|
10898
11146
|
const sid = safeSid2(sessionId);
|
|
10899
11147
|
const session = resolveSession(sid, tmuxSession);
|
|
10900
11148
|
if (!session) return;
|
|
10901
|
-
if (
|
|
11149
|
+
if (spawnSync9("tmux", ["has-session", "-t", `=${session}`], { encoding: "utf-8" }).status !== 0) return;
|
|
10902
11150
|
const current = lastRoutedModel(sid);
|
|
10903
11151
|
const picked = forceModel && VALID_MODELS2.has(forceModel) ? forceModel : await classifyTask(trimmed);
|
|
10904
11152
|
const doSwap = !!picked && picked !== "keep" && picked !== current;
|
|
10905
|
-
const sk = (...a) =>
|
|
10906
|
-
const capture = () =>
|
|
11153
|
+
const sk = (...a) => spawnSync9("tmux", ["send-keys", "-t", session, ...a], { encoding: "utf-8" });
|
|
11154
|
+
const capture = () => spawnSync9("tmux", ["capture-pane", "-t", session, "-p", "-S", "-25"], { encoding: "utf-8" }).stdout || "";
|
|
10907
11155
|
await wait(400);
|
|
10908
11156
|
if (doSwap) {
|
|
10909
11157
|
sk("C-u");
|
|
@@ -10921,29 +11169,29 @@ async function routeAndResubmit(sessionId, task, tmuxSession, forceModel) {
|
|
|
10921
11169
|
}
|
|
10922
11170
|
await wait(500);
|
|
10923
11171
|
try {
|
|
10924
|
-
|
|
11172
|
+
writeFileSync19(join24(PTY_DIR2, "route-last-" + sid), picked);
|
|
10925
11173
|
} catch {
|
|
10926
11174
|
}
|
|
10927
11175
|
}
|
|
10928
11176
|
try {
|
|
10929
|
-
|
|
11177
|
+
writeFileSync19(join24(PTY_DIR2, "route-guard-" + sid), "1");
|
|
10930
11178
|
} catch {
|
|
10931
11179
|
}
|
|
10932
11180
|
sk("C-u");
|
|
10933
11181
|
await wait(80);
|
|
10934
|
-
|
|
10935
|
-
|
|
11182
|
+
spawnSync9("tmux", ["set-buffer", "-b", "synkro-route", trimmed], { encoding: "utf-8" });
|
|
11183
|
+
spawnSync9("tmux", ["paste-buffer", "-t", session, "-b", "synkro-route", "-d"], { encoding: "utf-8" });
|
|
10936
11184
|
await wait(120);
|
|
10937
11185
|
sk("Enter");
|
|
10938
11186
|
}
|
|
10939
|
-
var
|
|
11187
|
+
var SYNKRO_DIR14, PTY_DIR2, SESSIONS_DIR3, ACTIVE_SESSION_FILE2, VALID_MODELS2, GRADER_HOST_PORT2, wait;
|
|
10940
11188
|
var init_routeOrchestrate = __esm({
|
|
10941
11189
|
"cli/local-cc/routeOrchestrate.ts"() {
|
|
10942
11190
|
"use strict";
|
|
10943
|
-
|
|
10944
|
-
PTY_DIR2 =
|
|
10945
|
-
SESSIONS_DIR3 =
|
|
10946
|
-
ACTIVE_SESSION_FILE2 =
|
|
11191
|
+
SYNKRO_DIR14 = join24(homedir25(), ".synkro");
|
|
11192
|
+
PTY_DIR2 = join24(SYNKRO_DIR14, "pty");
|
|
11193
|
+
SESSIONS_DIR3 = join24(PTY_DIR2, "sessions");
|
|
11194
|
+
ACTIVE_SESSION_FILE2 = join24(PTY_DIR2, "active");
|
|
10947
11195
|
VALID_MODELS2 = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
|
|
10948
11196
|
GRADER_HOST_PORT2 = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
|
|
10949
11197
|
wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -10955,14 +11203,14 @@ var routingToggle_exports = {};
|
|
|
10955
11203
|
__export(routingToggle_exports, {
|
|
10956
11204
|
routingCommand: () => routingCommand
|
|
10957
11205
|
});
|
|
10958
|
-
import { writeFileSync as
|
|
10959
|
-
import { homedir as
|
|
10960
|
-
import { join as
|
|
11206
|
+
import { writeFileSync as writeFileSync20, unlinkSync as unlinkSync9, existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
11207
|
+
import { homedir as homedir26 } from "os";
|
|
11208
|
+
import { join as join25 } from "path";
|
|
10961
11209
|
function safeSid3(sid) {
|
|
10962
11210
|
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
10963
11211
|
}
|
|
10964
11212
|
function markerFor(session) {
|
|
10965
|
-
return session ?
|
|
11213
|
+
return session ? join25(PTY_DIR3, "routing-on-" + safeSid3(session)) : join25(PTY_DIR3, "routing-on");
|
|
10966
11214
|
}
|
|
10967
11215
|
function routingCommand(args2) {
|
|
10968
11216
|
const sub = (args2[0] || "status").trim();
|
|
@@ -10971,7 +11219,7 @@ function routingCommand(args2) {
|
|
|
10971
11219
|
if (si >= 0 && args2[si + 1]) session = args2[si + 1].trim();
|
|
10972
11220
|
if (sub === "on") {
|
|
10973
11221
|
try {
|
|
10974
|
-
|
|
11222
|
+
writeFileSync20(markerFor(session), "1");
|
|
10975
11223
|
} catch (e) {
|
|
10976
11224
|
console.error("routing on failed:", String(e));
|
|
10977
11225
|
return;
|
|
@@ -10983,7 +11231,7 @@ function routingCommand(args2) {
|
|
|
10983
11231
|
if (sub === "off") {
|
|
10984
11232
|
let removed = 0;
|
|
10985
11233
|
if (session) {
|
|
10986
|
-
if (
|
|
11234
|
+
if (existsSync27(markerFor(session))) {
|
|
10987
11235
|
try {
|
|
10988
11236
|
unlinkSync9(markerFor(session));
|
|
10989
11237
|
removed++;
|
|
@@ -10994,7 +11242,7 @@ function routingCommand(args2) {
|
|
|
10994
11242
|
for (const f of safeList()) {
|
|
10995
11243
|
if (f === "routing-on" || f.startsWith("routing-on-")) {
|
|
10996
11244
|
try {
|
|
10997
|
-
unlinkSync9(
|
|
11245
|
+
unlinkSync9(join25(PTY_DIR3, f));
|
|
10998
11246
|
removed++;
|
|
10999
11247
|
} catch {
|
|
11000
11248
|
}
|
|
@@ -11026,24 +11274,24 @@ var PTY_DIR3;
|
|
|
11026
11274
|
var init_routingToggle = __esm({
|
|
11027
11275
|
"cli/local-cc/routingToggle.ts"() {
|
|
11028
11276
|
"use strict";
|
|
11029
|
-
PTY_DIR3 =
|
|
11277
|
+
PTY_DIR3 = join25(homedir26(), ".synkro", "pty");
|
|
11030
11278
|
}
|
|
11031
11279
|
});
|
|
11032
11280
|
|
|
11033
11281
|
// cli/local-cc/pueue.ts
|
|
11034
|
-
import { execFileSync as execFileSync4, spawnSync as
|
|
11035
|
-
import { homedir as
|
|
11036
|
-
import { join as
|
|
11282
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync10, spawn as spawn6 } from "child_process";
|
|
11283
|
+
import { homedir as homedir27 } from "os";
|
|
11284
|
+
import { join as join26 } from "path";
|
|
11037
11285
|
import { connect as connect2 } from "net";
|
|
11038
11286
|
function pueueAvailable() {
|
|
11039
|
-
const r =
|
|
11287
|
+
const r = spawnSync10("pueue", ["--version"], { encoding: "utf-8" });
|
|
11040
11288
|
if (r.status !== 0) {
|
|
11041
11289
|
throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
|
|
11042
11290
|
}
|
|
11043
11291
|
}
|
|
11044
11292
|
function statusJson() {
|
|
11045
11293
|
pueueAvailable();
|
|
11046
|
-
const r =
|
|
11294
|
+
const r = spawnSync10("pueue", ["status", "--json"], { encoding: "utf-8" });
|
|
11047
11295
|
if (r.status !== 0) {
|
|
11048
11296
|
throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
|
|
11049
11297
|
}
|
|
@@ -11088,18 +11336,18 @@ function startTask(opts = {}) {
|
|
|
11088
11336
|
let existing = findTask(ch);
|
|
11089
11337
|
while (existing) {
|
|
11090
11338
|
if (existing.status === "Running" || existing.status === "Queued") {
|
|
11091
|
-
|
|
11092
|
-
|
|
11339
|
+
spawnSync10("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
|
|
11340
|
+
spawnSync10("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
|
|
11093
11341
|
for (let i = 0; i < 10; i++) {
|
|
11094
11342
|
const check = findTask(ch);
|
|
11095
11343
|
if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
|
|
11096
|
-
|
|
11344
|
+
spawnSync10("sleep", ["0.5"], { encoding: "utf-8" });
|
|
11097
11345
|
}
|
|
11098
11346
|
}
|
|
11099
|
-
|
|
11347
|
+
spawnSync10("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
|
|
11100
11348
|
existing = findTask(ch);
|
|
11101
11349
|
}
|
|
11102
|
-
const runScript =
|
|
11350
|
+
const runScript = join26(cwd, "run-claude.sh");
|
|
11103
11351
|
const args2 = [
|
|
11104
11352
|
"add",
|
|
11105
11353
|
"--label",
|
|
@@ -11110,7 +11358,7 @@ function startTask(opts = {}) {
|
|
|
11110
11358
|
"bash",
|
|
11111
11359
|
runScript
|
|
11112
11360
|
];
|
|
11113
|
-
const r =
|
|
11361
|
+
const r = spawnSync10("pueue", args2, { encoding: "utf-8" });
|
|
11114
11362
|
if (r.status !== 0) {
|
|
11115
11363
|
throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
|
|
11116
11364
|
}
|
|
@@ -11121,25 +11369,25 @@ function startTask(opts = {}) {
|
|
|
11121
11369
|
return created;
|
|
11122
11370
|
}
|
|
11123
11371
|
function stopTask(channel = CHANNEL_PRIMARY) {
|
|
11124
|
-
|
|
11372
|
+
spawnSync10("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
|
|
11125
11373
|
let t = findTask(channel);
|
|
11126
11374
|
while (t) {
|
|
11127
11375
|
if (t.status === "Running" || t.status === "Queued") {
|
|
11128
|
-
|
|
11376
|
+
spawnSync10("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
|
|
11129
11377
|
for (let i = 0; i < 10; i++) {
|
|
11130
11378
|
const check = findTask(channel);
|
|
11131
11379
|
if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
|
|
11132
|
-
|
|
11380
|
+
spawnSync10("sleep", ["0.5"], { encoding: "utf-8" });
|
|
11133
11381
|
}
|
|
11134
11382
|
}
|
|
11135
|
-
|
|
11383
|
+
spawnSync10("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
|
|
11136
11384
|
t = findTask(channel);
|
|
11137
11385
|
}
|
|
11138
11386
|
}
|
|
11139
11387
|
function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
|
|
11140
11388
|
const t = findTask(channel);
|
|
11141
11389
|
if (!t) return `(no ${channel.taskLabel} task)`;
|
|
11142
|
-
const r =
|
|
11390
|
+
const r = spawnSync10("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
|
|
11143
11391
|
return r.stdout || r.stderr || "(no output)";
|
|
11144
11392
|
}
|
|
11145
11393
|
function ensureRunning(opts = {}) {
|
|
@@ -11164,8 +11412,8 @@ function probePort(host, port, timeoutMs = 500) {
|
|
|
11164
11412
|
});
|
|
11165
11413
|
}
|
|
11166
11414
|
function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
|
|
11167
|
-
|
|
11168
|
-
|
|
11415
|
+
spawnSync10("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
|
|
11416
|
+
spawnSync10("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
|
|
11169
11417
|
}
|
|
11170
11418
|
async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
|
|
11171
11419
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -11177,46 +11425,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
|
|
|
11177
11425
|
return probePort(host, port);
|
|
11178
11426
|
}
|
|
11179
11427
|
function brewInstall(pkg) {
|
|
11180
|
-
const brew =
|
|
11428
|
+
const brew = spawnSync10("brew", ["--version"], { encoding: "utf-8" });
|
|
11181
11429
|
if (brew.status !== 0) return false;
|
|
11182
11430
|
console.log(` Installing ${pkg} via brew...`);
|
|
11183
|
-
const r =
|
|
11431
|
+
const r = spawnSync10("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
11184
11432
|
return r.status === 0;
|
|
11185
11433
|
}
|
|
11186
11434
|
function assertPueueInstalled() {
|
|
11187
|
-
let r =
|
|
11435
|
+
let r = spawnSync10("pueue", ["--version"], { encoding: "utf-8" });
|
|
11188
11436
|
if (r.status !== 0) {
|
|
11189
11437
|
if (process.platform === "darwin" && brewInstall("pueue")) {
|
|
11190
|
-
r =
|
|
11438
|
+
r = spawnSync10("pueue", ["--version"], { encoding: "utf-8" });
|
|
11191
11439
|
if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
|
|
11192
11440
|
} else {
|
|
11193
11441
|
throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
|
|
11194
11442
|
}
|
|
11195
11443
|
}
|
|
11196
|
-
const status =
|
|
11444
|
+
const status = spawnSync10("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
|
|
11197
11445
|
if (status.status !== 0) {
|
|
11198
11446
|
console.log(" Starting pueued daemon...");
|
|
11199
11447
|
const child = spawn6("pueued", ["-d"], { stdio: "ignore", detached: true });
|
|
11200
11448
|
child.unref();
|
|
11201
|
-
|
|
11202
|
-
const retry =
|
|
11449
|
+
spawnSync10("sleep", ["1"]);
|
|
11450
|
+
const retry = spawnSync10("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
|
|
11203
11451
|
if (retry.status !== 0) {
|
|
11204
11452
|
throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
|
|
11205
11453
|
}
|
|
11206
11454
|
}
|
|
11207
|
-
|
|
11455
|
+
spawnSync10("pueue", ["parallel", "2"], { encoding: "utf-8" });
|
|
11208
11456
|
}
|
|
11209
11457
|
function assertClaudeInstalled() {
|
|
11210
|
-
const r =
|
|
11458
|
+
const r = spawnSync10("claude", ["--version"], { encoding: "utf-8" });
|
|
11211
11459
|
if (r.status !== 0) {
|
|
11212
11460
|
throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
|
|
11213
11461
|
}
|
|
11214
11462
|
}
|
|
11215
11463
|
function assertTmuxInstalled() {
|
|
11216
|
-
let r =
|
|
11464
|
+
let r = spawnSync10("tmux", ["-V"], { encoding: "utf-8" });
|
|
11217
11465
|
if (r.status !== 0) {
|
|
11218
11466
|
if (process.platform === "darwin" && brewInstall("tmux")) {
|
|
11219
|
-
r =
|
|
11467
|
+
r = spawnSync10("tmux", ["-V"], { encoding: "utf-8" });
|
|
11220
11468
|
if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
|
|
11221
11469
|
} else {
|
|
11222
11470
|
throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
|
|
@@ -11229,12 +11477,12 @@ var init_pueue = __esm({
|
|
|
11229
11477
|
"use strict";
|
|
11230
11478
|
TASK_LABEL = "synkro-local-cc";
|
|
11231
11479
|
TMUX_SESSION = "synkro-local-cc";
|
|
11232
|
-
SESSION_DIR2 =
|
|
11480
|
+
SESSION_DIR2 = join26(homedir27(), ".synkro", "cc_sessions");
|
|
11233
11481
|
TASK_LABEL_2 = "synkro-local-cc-2";
|
|
11234
11482
|
TMUX_SESSION_2 = "synkro-local-cc-2";
|
|
11235
|
-
SESSION_DIR_22 =
|
|
11236
|
-
SESSION_DIR_32 =
|
|
11237
|
-
SESSION_DIR_42 =
|
|
11483
|
+
SESSION_DIR_22 = join26(homedir27(), ".synkro", "cc_sessions_2");
|
|
11484
|
+
SESSION_DIR_32 = join26(homedir27(), ".synkro", "cc_sessions_3");
|
|
11485
|
+
SESSION_DIR_42 = join26(homedir27(), ".synkro", "cc_sessions_4");
|
|
11238
11486
|
PueueError = class extends Error {
|
|
11239
11487
|
constructor(message, cause) {
|
|
11240
11488
|
super(message);
|
|
@@ -11249,13 +11497,13 @@ var init_pueue = __esm({
|
|
|
11249
11497
|
});
|
|
11250
11498
|
|
|
11251
11499
|
// cli/local-cc/settings.ts
|
|
11252
|
-
import { existsSync as
|
|
11253
|
-
import { homedir as
|
|
11254
|
-
import { join as
|
|
11500
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26 } from "fs";
|
|
11501
|
+
import { homedir as homedir28 } from "os";
|
|
11502
|
+
import { join as join27 } from "path";
|
|
11255
11503
|
function isLocalCCEnabled() {
|
|
11256
|
-
if (!
|
|
11504
|
+
if (!existsSync28(CONFIG_PATH5)) return false;
|
|
11257
11505
|
try {
|
|
11258
|
-
const content =
|
|
11506
|
+
const content = readFileSync26(CONFIG_PATH5, "utf-8");
|
|
11259
11507
|
const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
|
|
11260
11508
|
return match?.[1] === "yes";
|
|
11261
11509
|
} catch {
|
|
@@ -11266,7 +11514,7 @@ var CONFIG_PATH5;
|
|
|
11266
11514
|
var init_settings = __esm({
|
|
11267
11515
|
"cli/local-cc/settings.ts"() {
|
|
11268
11516
|
"use strict";
|
|
11269
|
-
CONFIG_PATH5 =
|
|
11517
|
+
CONFIG_PATH5 = join27(homedir28(), ".synkro", "config.env");
|
|
11270
11518
|
}
|
|
11271
11519
|
});
|
|
11272
11520
|
|
|
@@ -11275,11 +11523,11 @@ var localCc_exports = {};
|
|
|
11275
11523
|
__export(localCc_exports, {
|
|
11276
11524
|
localCcCommand: () => localCcCommand
|
|
11277
11525
|
});
|
|
11278
|
-
import { spawnSync as
|
|
11279
|
-
import { homedir as
|
|
11280
|
-
import { join as
|
|
11526
|
+
import { spawnSync as spawnSync11 } from "child_process";
|
|
11527
|
+
import { homedir as homedir29 } from "os";
|
|
11528
|
+
import { join as join28 } from "path";
|
|
11281
11529
|
import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
|
|
11282
|
-
import { existsSync as
|
|
11530
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
|
|
11283
11531
|
function deploymentMode() {
|
|
11284
11532
|
const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
|
|
11285
11533
|
if (env === "docker") return "docker";
|
|
@@ -11385,15 +11633,15 @@ TROUBLESHOOTING
|
|
|
11385
11633
|
`);
|
|
11386
11634
|
}
|
|
11387
11635
|
function readGatewayUrl() {
|
|
11388
|
-
if (
|
|
11389
|
-
const m =
|
|
11636
|
+
if (existsSync29(CONFIG_PATH6)) {
|
|
11637
|
+
const m = readFileSync27(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
|
|
11390
11638
|
if (m) return m[1];
|
|
11391
11639
|
}
|
|
11392
11640
|
return "https://api.synkro.sh";
|
|
11393
11641
|
}
|
|
11394
11642
|
function updateLocalInferenceFlag(enabled) {
|
|
11395
|
-
if (!
|
|
11396
|
-
let content =
|
|
11643
|
+
if (!existsSync29(CONFIG_PATH6)) return;
|
|
11644
|
+
let content = readFileSync27(CONFIG_PATH6, "utf-8");
|
|
11397
11645
|
const flag = enabled ? "yes" : "no";
|
|
11398
11646
|
if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
|
|
11399
11647
|
content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
|
|
@@ -11402,7 +11650,7 @@ function updateLocalInferenceFlag(enabled) {
|
|
|
11402
11650
|
SYNKRO_LOCAL_INFERENCE='${flag}'
|
|
11403
11651
|
`;
|
|
11404
11652
|
}
|
|
11405
|
-
|
|
11653
|
+
writeFileSync21(CONFIG_PATH6, content, "utf-8");
|
|
11406
11654
|
}
|
|
11407
11655
|
async function setServerGradingProvider(provider) {
|
|
11408
11656
|
await ensureValidToken();
|
|
@@ -11456,7 +11704,7 @@ async function cmdStatus() {
|
|
|
11456
11704
|
}
|
|
11457
11705
|
const ch1Up = await isChannelAvailable();
|
|
11458
11706
|
console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
|
|
11459
|
-
const tmux1 =
|
|
11707
|
+
const tmux1 = spawnSync11("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
|
|
11460
11708
|
console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
|
|
11461
11709
|
const t2 = findTask(CHANNEL_SECONDARY);
|
|
11462
11710
|
if (!t2) {
|
|
@@ -11466,7 +11714,7 @@ async function cmdStatus() {
|
|
|
11466
11714
|
}
|
|
11467
11715
|
const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
|
|
11468
11716
|
console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
|
|
11469
|
-
const tmux2 =
|
|
11717
|
+
const tmux2 = spawnSync11("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
|
|
11470
11718
|
console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
|
|
11471
11719
|
}
|
|
11472
11720
|
async function cmdEnable() {
|
|
@@ -11523,9 +11771,9 @@ async function warmChannels(ready1, ready2) {
|
|
|
11523
11771
|
async function cmdStart(rest = []) {
|
|
11524
11772
|
if (inDockerMode()) {
|
|
11525
11773
|
if (rest.length > 0) {
|
|
11526
|
-
const { claudeWorkers, cursorWorkers } = resolveWorkerConfig(rest);
|
|
11527
|
-
console.log(`Starting synkro-server container (${claudeWorkers} claude + ${cursorWorkers} cursor)...`);
|
|
11528
|
-
await dockerUpdate({ claudeWorkers, cursorWorkers });
|
|
11774
|
+
const { claudeWorkers, cursorWorkers, codexWorkers, conductorProvider } = resolveWorkerConfig(rest);
|
|
11775
|
+
console.log(`Starting synkro-server container (${claudeWorkers} claude + ${cursorWorkers} cursor + ${codexWorkers} codex)...`);
|
|
11776
|
+
await dockerUpdate({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider });
|
|
11529
11777
|
const ready3 = await waitForContainerReady(6e4);
|
|
11530
11778
|
console.log(ready3 ? "\u2713 container ready" : "\u26A0 container did not pass /healthz within 60s");
|
|
11531
11779
|
return;
|
|
@@ -11571,18 +11819,20 @@ async function cmdRestart(rest = []) {
|
|
|
11571
11819
|
const { explicit } = resolveWorkerConfig(rest);
|
|
11572
11820
|
let claudeWorkers;
|
|
11573
11821
|
let cursorWorkers;
|
|
11822
|
+
let codexWorkers;
|
|
11823
|
+
let conductorProvider;
|
|
11574
11824
|
if (explicit) {
|
|
11575
|
-
({ claudeWorkers, cursorWorkers } = resolveWorkerConfig(rest));
|
|
11825
|
+
({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider } = resolveWorkerConfig(rest));
|
|
11576
11826
|
} else {
|
|
11577
11827
|
const reconciled = reconcileHarness();
|
|
11578
11828
|
if (reconciled) {
|
|
11579
|
-
({ claudeWorkers, cursorWorkers } = reconciled);
|
|
11829
|
+
({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider } = reconciled);
|
|
11580
11830
|
} else {
|
|
11581
|
-
({ claudeWorkers, cursorWorkers } = resolveWorkerConfig(rest));
|
|
11831
|
+
({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider } = resolveWorkerConfig(rest));
|
|
11582
11832
|
}
|
|
11583
11833
|
}
|
|
11584
|
-
console.log(`Restarting synkro-server container (${claudeWorkers} claude + ${cursorWorkers} cursor, pulling latest image)...`);
|
|
11585
|
-
await dockerUpdate({ claudeWorkers, cursorWorkers });
|
|
11834
|
+
console.log(`Restarting synkro-server container (${claudeWorkers} claude + ${cursorWorkers} cursor + ${codexWorkers} codex, pulling latest image)...`);
|
|
11835
|
+
await dockerUpdate({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider });
|
|
11586
11836
|
const ready = await waitForContainerReady(6e4);
|
|
11587
11837
|
console.log(ready ? "\u2713 container ready" : "\u26A0 container did not pass /healthz within 60s");
|
|
11588
11838
|
if (ready) {
|
|
@@ -11672,7 +11922,7 @@ function cmdLogs(rest) {
|
|
|
11672
11922
|
}
|
|
11673
11923
|
return "200";
|
|
11674
11924
|
})();
|
|
11675
|
-
|
|
11925
|
+
spawnSync11("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
|
|
11676
11926
|
return;
|
|
11677
11927
|
}
|
|
11678
11928
|
for (const arg of rest) {
|
|
@@ -11720,7 +11970,7 @@ function cmdLogs(rest) {
|
|
|
11720
11970
|
function cmdAttach(rest) {
|
|
11721
11971
|
assertTmuxInstalled();
|
|
11722
11972
|
const readonly = rest.some((a) => a === "--readonly" || a === "-r");
|
|
11723
|
-
const has =
|
|
11973
|
+
const has = spawnSync11("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
|
|
11724
11974
|
if (has.status !== 0) {
|
|
11725
11975
|
console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
|
|
11726
11976
|
process.exit(1);
|
|
@@ -11733,7 +11983,7 @@ function cmdAttach(rest) {
|
|
|
11733
11983
|
console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
|
|
11734
11984
|
console.log();
|
|
11735
11985
|
const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
|
|
11736
|
-
const r =
|
|
11986
|
+
const r = spawnSync11("tmux", args2, { stdio: "inherit" });
|
|
11737
11987
|
process.exit(r.status ?? 0);
|
|
11738
11988
|
}
|
|
11739
11989
|
async function cmdTest() {
|
|
@@ -11834,8 +12084,8 @@ var init_localCc = __esm({
|
|
|
11834
12084
|
init_install();
|
|
11835
12085
|
init_client2();
|
|
11836
12086
|
init_stub();
|
|
11837
|
-
SYNKRO_CONFIG_PATH =
|
|
11838
|
-
CONFIG_PATH6 =
|
|
12087
|
+
SYNKRO_CONFIG_PATH = join28(homedir29(), ".synkro", "config.env");
|
|
12088
|
+
CONFIG_PATH6 = join28(homedir29(), ".synkro", "config.env");
|
|
11839
12089
|
}
|
|
11840
12090
|
});
|
|
11841
12091
|
|
|
@@ -11844,14 +12094,14 @@ var import_exports = {};
|
|
|
11844
12094
|
__export(import_exports, {
|
|
11845
12095
|
importCommand: () => importCommand
|
|
11846
12096
|
});
|
|
11847
|
-
import { existsSync as
|
|
11848
|
-
import { homedir as
|
|
11849
|
-
import { join as
|
|
12097
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28, readdirSync as readdirSync7 } from "fs";
|
|
12098
|
+
import { homedir as homedir30 } from "os";
|
|
12099
|
+
import { join as join29 } from "path";
|
|
11850
12100
|
import { execSync as execSync6 } from "child_process";
|
|
11851
12101
|
import { createInterface as createInterface4 } from "readline";
|
|
11852
12102
|
function readMcpJwt() {
|
|
11853
12103
|
try {
|
|
11854
|
-
return
|
|
12104
|
+
return readFileSync28(join29(homedir30(), ".synkro", ".mcp-jwt"), "utf-8").trim();
|
|
11855
12105
|
} catch {
|
|
11856
12106
|
return "";
|
|
11857
12107
|
}
|
|
@@ -11859,7 +12109,7 @@ function readMcpJwt() {
|
|
|
11859
12109
|
function readConfigEnv2() {
|
|
11860
12110
|
const out = {};
|
|
11861
12111
|
try {
|
|
11862
|
-
for (const line of
|
|
12112
|
+
for (const line of readFileSync28(CONFIG_PATH7, "utf-8").split("\n")) {
|
|
11863
12113
|
const t = line.trim();
|
|
11864
12114
|
if (!t || t.startsWith("#")) continue;
|
|
11865
12115
|
const eq = t.indexOf("=");
|
|
@@ -11871,8 +12121,8 @@ function readConfigEnv2() {
|
|
|
11871
12121
|
}
|
|
11872
12122
|
function projectsFolder() {
|
|
11873
12123
|
const sanitized = process.cwd().replace(/\//g, "-");
|
|
11874
|
-
const dir =
|
|
11875
|
-
return
|
|
12124
|
+
const dir = join29(homedir30(), ".claude", "projects", sanitized);
|
|
12125
|
+
return existsSync30(dir) ? dir : null;
|
|
11876
12126
|
}
|
|
11877
12127
|
function repoName() {
|
|
11878
12128
|
try {
|
|
@@ -11911,7 +12161,7 @@ function extractToolResultText(content, e) {
|
|
|
11911
12161
|
return t;
|
|
11912
12162
|
}
|
|
11913
12163
|
function parseSession(filePath, sessionId) {
|
|
11914
|
-
const lines =
|
|
12164
|
+
const lines = readFileSync28(filePath, "utf-8").split("\n").filter(Boolean);
|
|
11915
12165
|
const messages = [];
|
|
11916
12166
|
const actions = [];
|
|
11917
12167
|
let step = 0;
|
|
@@ -11991,7 +12241,7 @@ async function importCommand() {
|
|
|
11991
12241
|
return;
|
|
11992
12242
|
}
|
|
11993
12243
|
}
|
|
11994
|
-
const sessions = files.map((f) => parseSession(
|
|
12244
|
+
const sessions = files.map((f) => parseSession(join29(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
|
|
11995
12245
|
const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
|
|
11996
12246
|
let ok = 0, fail = 0;
|
|
11997
12247
|
if (isCloud) {
|
|
@@ -12066,7 +12316,7 @@ var init_import = __esm({
|
|
|
12066
12316
|
"cli/commands/import.ts"() {
|
|
12067
12317
|
"use strict";
|
|
12068
12318
|
init_stub();
|
|
12069
|
-
CONFIG_PATH7 =
|
|
12319
|
+
CONFIG_PATH7 = join29(homedir30(), ".synkro", "config.env");
|
|
12070
12320
|
}
|
|
12071
12321
|
});
|
|
12072
12322
|
|
|
@@ -12108,10 +12358,10 @@ var init_packVerify = __esm({
|
|
|
12108
12358
|
});
|
|
12109
12359
|
|
|
12110
12360
|
// cli/installer/lockfile.ts
|
|
12111
|
-
import { existsSync as
|
|
12112
|
-
import { join as
|
|
12361
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29, writeFileSync as writeFileSync22 } from "fs";
|
|
12362
|
+
import { join as join30 } from "path";
|
|
12113
12363
|
function lockPath(repoRoot2) {
|
|
12114
|
-
return
|
|
12364
|
+
return join30(repoRoot2, LOCK_FILE);
|
|
12115
12365
|
}
|
|
12116
12366
|
function writeLockfile(repoRoot2, entries) {
|
|
12117
12367
|
const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
|
|
@@ -12129,7 +12379,7 @@ function writeLockfile(repoRoot2, entries) {
|
|
|
12129
12379
|
""
|
|
12130
12380
|
])
|
|
12131
12381
|
].join("\n");
|
|
12132
|
-
|
|
12382
|
+
writeFileSync22(lockPath(repoRoot2), body, "utf-8");
|
|
12133
12383
|
}
|
|
12134
12384
|
var LOCK_FILE;
|
|
12135
12385
|
var init_lockfile = __esm({
|
|
@@ -12144,9 +12394,9 @@ var sync_exports = {};
|
|
|
12144
12394
|
__export(sync_exports, {
|
|
12145
12395
|
syncCommand: () => syncCommand
|
|
12146
12396
|
});
|
|
12147
|
-
import { existsSync as
|
|
12148
|
-
import { homedir as
|
|
12149
|
-
import { join as
|
|
12397
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync18, readdirSync as readdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync23 } from "fs";
|
|
12398
|
+
import { homedir as homedir31 } from "os";
|
|
12399
|
+
import { join as join31 } from "path";
|
|
12150
12400
|
function cacheKey(ref, version) {
|
|
12151
12401
|
return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
|
|
12152
12402
|
}
|
|
@@ -12177,8 +12427,8 @@ async function syncCommand(_args = []) {
|
|
|
12177
12427
|
}
|
|
12178
12428
|
const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
12179
12429
|
const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
|
|
12180
|
-
const cacheDir =
|
|
12181
|
-
if (!cloud)
|
|
12430
|
+
const cacheDir = join31(homedir31(), ".synkro", "cache", "packs");
|
|
12431
|
+
if (!cloud) mkdirSync18(cacheDir, { recursive: true });
|
|
12182
12432
|
console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
|
|
12183
12433
|
const lock = [];
|
|
12184
12434
|
const keptCacheFiles = /* @__PURE__ */ new Set();
|
|
@@ -12206,7 +12456,7 @@ async function syncCommand(_args = []) {
|
|
|
12206
12456
|
if (!cloud) {
|
|
12207
12457
|
const fname = cacheKey(ref, data.version);
|
|
12208
12458
|
keptCacheFiles.add(fname);
|
|
12209
|
-
|
|
12459
|
+
writeFileSync23(join31(cacheDir, fname), JSON.stringify({
|
|
12210
12460
|
ref,
|
|
12211
12461
|
version: data.version,
|
|
12212
12462
|
digest: data.digest,
|
|
@@ -12218,11 +12468,11 @@ async function syncCommand(_args = []) {
|
|
|
12218
12468
|
const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
|
|
12219
12469
|
console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
|
|
12220
12470
|
}
|
|
12221
|
-
if (!cloud &&
|
|
12471
|
+
if (!cloud && existsSync32(cacheDir)) {
|
|
12222
12472
|
for (const f of readdirSync8(cacheDir)) {
|
|
12223
12473
|
if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
|
|
12224
12474
|
try {
|
|
12225
|
-
|
|
12475
|
+
rmSync4(join31(cacheDir, f));
|
|
12226
12476
|
} catch {
|
|
12227
12477
|
}
|
|
12228
12478
|
}
|
|
@@ -12251,13 +12501,13 @@ var whoami_exports = {};
|
|
|
12251
12501
|
__export(whoami_exports, {
|
|
12252
12502
|
whoamiCommand: () => whoamiCommand
|
|
12253
12503
|
});
|
|
12254
|
-
import { readFileSync as
|
|
12255
|
-
import { join as
|
|
12256
|
-
import { homedir as
|
|
12504
|
+
import { readFileSync as readFileSync30, existsSync as existsSync33 } from "fs";
|
|
12505
|
+
import { join as join32 } from "path";
|
|
12506
|
+
import { homedir as homedir32 } from "os";
|
|
12257
12507
|
function readConfigEnv3() {
|
|
12258
|
-
if (!
|
|
12508
|
+
if (!existsSync33(CONFIG_PATH8)) return {};
|
|
12259
12509
|
const out = {};
|
|
12260
|
-
for (const line of
|
|
12510
|
+
for (const line of readFileSync30(CONFIG_PATH8, "utf-8").split("\n")) {
|
|
12261
12511
|
const t = line.trim();
|
|
12262
12512
|
if (!t || t.startsWith("#")) continue;
|
|
12263
12513
|
const eq = t.indexOf("=");
|
|
@@ -12267,8 +12517,8 @@ function readConfigEnv3() {
|
|
|
12267
12517
|
}
|
|
12268
12518
|
function jwtStatus() {
|
|
12269
12519
|
try {
|
|
12270
|
-
if (!
|
|
12271
|
-
const jwt2 =
|
|
12520
|
+
if (!existsSync33(JWT_PATH2)) return { status: "none" };
|
|
12521
|
+
const jwt2 = readFileSync30(JWT_PATH2, "utf-8").trim();
|
|
12272
12522
|
if (!jwt2) return { status: "none" };
|
|
12273
12523
|
const payload = jwt2.split(".")[1];
|
|
12274
12524
|
if (!payload) return { status: "valid" };
|
|
@@ -12326,13 +12576,13 @@ async function whoamiCommand(args2 = []) {
|
|
|
12326
12576
|
console.log("synkro identity");
|
|
12327
12577
|
for (const [k, v] of rows) console.log(` ${k.padEnd(width)} ${v}`);
|
|
12328
12578
|
}
|
|
12329
|
-
var
|
|
12579
|
+
var SYNKRO_DIR15, CONFIG_PATH8, JWT_PATH2, GRADING_LABEL;
|
|
12330
12580
|
var init_whoami = __esm({
|
|
12331
12581
|
"cli/commands/whoami.ts"() {
|
|
12332
12582
|
"use strict";
|
|
12333
|
-
|
|
12334
|
-
CONFIG_PATH8 =
|
|
12335
|
-
JWT_PATH2 =
|
|
12583
|
+
SYNKRO_DIR15 = join32(homedir32(), ".synkro");
|
|
12584
|
+
CONFIG_PATH8 = join32(SYNKRO_DIR15, "config.env");
|
|
12585
|
+
JWT_PATH2 = join32(SYNKRO_DIR15, ".mcp-jwt");
|
|
12336
12586
|
GRADING_LABEL = {
|
|
12337
12587
|
local: "on-device worker pool",
|
|
12338
12588
|
cloud: "Synkro Cloud worker pool",
|
|
@@ -12377,12 +12627,12 @@ __export(linear_exports, {
|
|
|
12377
12627
|
formatLinks: () => formatLinks,
|
|
12378
12628
|
linearCommand: () => linearCommand
|
|
12379
12629
|
});
|
|
12380
|
-
import { readFileSync as
|
|
12381
|
-
import { homedir as
|
|
12382
|
-
import { join as
|
|
12630
|
+
import { readFileSync as readFileSync31 } from "fs";
|
|
12631
|
+
import { homedir as homedir33 } from "os";
|
|
12632
|
+
import { join as join33 } from "path";
|
|
12383
12633
|
function mcpJwt() {
|
|
12384
12634
|
try {
|
|
12385
|
-
return
|
|
12635
|
+
return readFileSync31(join33(SYNKRO_DIR16, ".mcp-jwt"), "utf-8").trim();
|
|
12386
12636
|
} catch {
|
|
12387
12637
|
return "";
|
|
12388
12638
|
}
|
|
@@ -12417,11 +12667,11 @@ async function linearCommand(_args = []) {
|
|
|
12417
12667
|
}
|
|
12418
12668
|
console.log(formatLinks(links));
|
|
12419
12669
|
}
|
|
12420
|
-
var
|
|
12670
|
+
var SYNKRO_DIR16, PORT2, BASE;
|
|
12421
12671
|
var init_linear = __esm({
|
|
12422
12672
|
"cli/commands/linear.ts"() {
|
|
12423
12673
|
"use strict";
|
|
12424
|
-
|
|
12674
|
+
SYNKRO_DIR16 = join33(homedir33(), ".synkro");
|
|
12425
12675
|
PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
|
|
12426
12676
|
BASE = `http://127.0.0.1:${PORT2}`;
|
|
12427
12677
|
}
|
|
@@ -12429,7 +12679,7 @@ var init_linear = __esm({
|
|
|
12429
12679
|
|
|
12430
12680
|
// cli/scanning/cveReachability.ts
|
|
12431
12681
|
import { parse } from "@babel/parser";
|
|
12432
|
-
import { readFileSync as
|
|
12682
|
+
import { readFileSync as readFileSync32 } from "fs";
|
|
12433
12683
|
function walk(node, visit) {
|
|
12434
12684
|
if (!node || typeof node.type !== "string") return;
|
|
12435
12685
|
visit(node);
|
|
@@ -12570,10 +12820,10 @@ var init_cveReachability = __esm({
|
|
|
12570
12820
|
});
|
|
12571
12821
|
|
|
12572
12822
|
// cli/reachability/reachabilityScan.ts
|
|
12573
|
-
import { spawnSync as
|
|
12574
|
-
import { readFileSync as
|
|
12575
|
-
import { join as
|
|
12576
|
-
import { homedir as
|
|
12823
|
+
import { spawnSync as spawnSync12, execFileSync as execFileSync5 } from "child_process";
|
|
12824
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync24, existsSync as existsSync34, readdirSync as readdirSync9 } from "fs";
|
|
12825
|
+
import { join as join34 } from "path";
|
|
12826
|
+
import { homedir as homedir34 } from "os";
|
|
12577
12827
|
import { createRequire } from "module";
|
|
12578
12828
|
function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
12579
12829
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".turbo", "out", ".cache", ".synkro", ".claude", "vendor", "__tests__", "test-results"]);
|
|
@@ -12590,7 +12840,7 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
12590
12840
|
}
|
|
12591
12841
|
for (const e of ents) {
|
|
12592
12842
|
if (files.length >= maxFiles) break;
|
|
12593
|
-
const full =
|
|
12843
|
+
const full = join34(dir, e.name);
|
|
12594
12844
|
if (e.isDirectory()) {
|
|
12595
12845
|
if (!SKIP.has(e.name) && !e.name.startsWith(".")) stack.push(full);
|
|
12596
12846
|
continue;
|
|
@@ -12598,7 +12848,7 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
12598
12848
|
if (!EXT.test(e.name) || e.name.endsWith(".d.ts")) continue;
|
|
12599
12849
|
const rel = full.startsWith(repoRoot2 + "/") ? full.slice(repoRoot2.length + 1) : full;
|
|
12600
12850
|
try {
|
|
12601
|
-
const content =
|
|
12851
|
+
const content = readFileSync33(full, "utf8");
|
|
12602
12852
|
if (content.length <= maxBytes) files.push({ path: rel, content });
|
|
12603
12853
|
} catch {
|
|
12604
12854
|
}
|
|
@@ -12617,12 +12867,12 @@ function cleanVersion(spec) {
|
|
|
12617
12867
|
function gatherManifestVersions(repoRoot2) {
|
|
12618
12868
|
const out = {};
|
|
12619
12869
|
const dirs = [repoRoot2];
|
|
12620
|
-
const pkgsDir =
|
|
12621
|
-
if (
|
|
12870
|
+
const pkgsDir = join34(repoRoot2, "packages");
|
|
12871
|
+
if (existsSync34(pkgsDir)) {
|
|
12622
12872
|
try {
|
|
12623
12873
|
for (const d of readdirSync9(pkgsDir)) {
|
|
12624
|
-
const pd =
|
|
12625
|
-
if (
|
|
12874
|
+
const pd = join34(pkgsDir, d);
|
|
12875
|
+
if (existsSync34(join34(pd, "package.json"))) dirs.push(pd);
|
|
12626
12876
|
}
|
|
12627
12877
|
} catch {
|
|
12628
12878
|
}
|
|
@@ -12631,7 +12881,7 @@ function gatherManifestVersions(repoRoot2) {
|
|
|
12631
12881
|
for (const dir of dirs) {
|
|
12632
12882
|
let pkg;
|
|
12633
12883
|
try {
|
|
12634
|
-
pkg = JSON.parse(
|
|
12884
|
+
pkg = JSON.parse(readFileSync33(join34(dir, "package.json"), "utf8"));
|
|
12635
12885
|
} catch {
|
|
12636
12886
|
continue;
|
|
12637
12887
|
}
|
|
@@ -12651,28 +12901,28 @@ function findJelly(repoRoot2) {
|
|
|
12651
12901
|
try {
|
|
12652
12902
|
const pkgJson = require2.resolve("@cs-au-dk/jelly/package.json");
|
|
12653
12903
|
const dir = pkgJson.slice(0, pkgJson.length - "package.json".length);
|
|
12654
|
-
const pkg = JSON.parse(
|
|
12904
|
+
const pkg = JSON.parse(readFileSync33(pkgJson, "utf8"));
|
|
12655
12905
|
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
|
|
12656
12906
|
if (bin) {
|
|
12657
|
-
const p =
|
|
12658
|
-
if (
|
|
12907
|
+
const p = join34(dir, bin);
|
|
12908
|
+
if (existsSync34(p)) return p;
|
|
12659
12909
|
}
|
|
12660
12910
|
} catch {
|
|
12661
12911
|
}
|
|
12662
12912
|
for (const base of [repoRoot2, process.cwd()]) {
|
|
12663
|
-
const b =
|
|
12664
|
-
if (
|
|
12913
|
+
const b = join34(base, "node_modules", ".bin", "jelly");
|
|
12914
|
+
if (existsSync34(b)) return b;
|
|
12665
12915
|
}
|
|
12666
12916
|
return null;
|
|
12667
12917
|
}
|
|
12668
12918
|
function findEntries(repoRoot2) {
|
|
12669
12919
|
const dirs = [repoRoot2];
|
|
12670
|
-
const pkgsDir =
|
|
12671
|
-
if (
|
|
12920
|
+
const pkgsDir = join34(repoRoot2, "packages");
|
|
12921
|
+
if (existsSync34(pkgsDir)) {
|
|
12672
12922
|
try {
|
|
12673
12923
|
for (const d of readdirSync9(pkgsDir)) {
|
|
12674
|
-
const pd =
|
|
12675
|
-
if (
|
|
12924
|
+
const pd = join34(pkgsDir, d);
|
|
12925
|
+
if (existsSync34(join34(pd, "package.json"))) dirs.push(pd);
|
|
12676
12926
|
}
|
|
12677
12927
|
} catch {
|
|
12678
12928
|
}
|
|
@@ -12680,12 +12930,12 @@ function findEntries(repoRoot2) {
|
|
|
12680
12930
|
const entries = [];
|
|
12681
12931
|
for (const dir of dirs) {
|
|
12682
12932
|
try {
|
|
12683
|
-
const pkg = JSON.parse(
|
|
12933
|
+
const pkg = JSON.parse(readFileSync33(join34(dir, "package.json"), "utf8"));
|
|
12684
12934
|
const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
|
|
12685
12935
|
for (const c of cands) {
|
|
12686
12936
|
if (typeof c !== "string") continue;
|
|
12687
|
-
const f =
|
|
12688
|
-
if (
|
|
12937
|
+
const f = join34(dir, c);
|
|
12938
|
+
if (existsSync34(f)) {
|
|
12689
12939
|
entries.push(f);
|
|
12690
12940
|
break;
|
|
12691
12941
|
}
|
|
@@ -12718,9 +12968,9 @@ function parseApiUsage(log) {
|
|
|
12718
12968
|
}
|
|
12719
12969
|
function runReachabilityScan(repoRoot2, opts = {}) {
|
|
12720
12970
|
const commit = currentCommit(repoRoot2);
|
|
12721
|
-
if (!opts.force && commit &&
|
|
12971
|
+
if (!opts.force && commit && existsSync34(REACHABILITY_PATH)) {
|
|
12722
12972
|
try {
|
|
12723
|
-
const prev = JSON.parse(
|
|
12973
|
+
const prev = JSON.parse(readFileSync33(REACHABILITY_PATH, "utf8"));
|
|
12724
12974
|
if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
|
|
12725
12975
|
} catch {
|
|
12726
12976
|
}
|
|
@@ -12771,7 +13021,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
|
|
|
12771
13021
|
if (jelly) {
|
|
12772
13022
|
const entries = findEntries(repoRoot2);
|
|
12773
13023
|
if (entries.length > 0) {
|
|
12774
|
-
const r =
|
|
13024
|
+
const r = spawnSync12(
|
|
12775
13025
|
process.execPath,
|
|
12776
13026
|
[jelly, "-b", repoRoot2, "--api-usage", ...entries],
|
|
12777
13027
|
{ encoding: "utf8", timeout: opts.timeoutMs ?? 18e4, maxBuffer: 2e8 }
|
|
@@ -12809,7 +13059,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
|
|
|
12809
13059
|
if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
|
|
12810
13060
|
const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot2) };
|
|
12811
13061
|
try {
|
|
12812
|
-
|
|
13062
|
+
writeFileSync24(REACHABILITY_PATH, JSON.stringify(file, null, 2));
|
|
12813
13063
|
} catch (e) {
|
|
12814
13064
|
return { ok: false, reason: "write failed: " + String(e.message || e) };
|
|
12815
13065
|
}
|
|
@@ -12821,7 +13071,7 @@ var init_reachabilityScan = __esm({
|
|
|
12821
13071
|
"use strict";
|
|
12822
13072
|
init_cveReachability();
|
|
12823
13073
|
require2 = createRequire(import.meta.url);
|
|
12824
|
-
REACHABILITY_PATH =
|
|
13074
|
+
REACHABILITY_PATH = join34(homedir34(), ".synkro", "reachability.json");
|
|
12825
13075
|
}
|
|
12826
13076
|
});
|
|
12827
13077
|
|
|
@@ -12830,15 +13080,15 @@ var reachabilityScan_exports = {};
|
|
|
12830
13080
|
__export(reachabilityScan_exports, {
|
|
12831
13081
|
reachabilityScanCommand: () => reachabilityScanCommand
|
|
12832
13082
|
});
|
|
12833
|
-
import { readFileSync as
|
|
12834
|
-
import { join as
|
|
12835
|
-
import { homedir as
|
|
13083
|
+
import { readFileSync as readFileSync34, existsSync as existsSync35 } from "fs";
|
|
13084
|
+
import { join as join35 } from "path";
|
|
13085
|
+
import { homedir as homedir35 } from "os";
|
|
12836
13086
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
12837
13087
|
function readConfigEnv4() {
|
|
12838
|
-
const p =
|
|
12839
|
-
if (!
|
|
13088
|
+
const p = join35(SYNKRO_DIR17, "config.env");
|
|
13089
|
+
if (!existsSync35(p)) return {};
|
|
12840
13090
|
const out = {};
|
|
12841
|
-
for (const line of
|
|
13091
|
+
for (const line of readFileSync34(p, "utf-8").split("\n")) {
|
|
12842
13092
|
const t = line.trim();
|
|
12843
13093
|
if (!t || t.startsWith("#")) continue;
|
|
12844
13094
|
const eq = t.indexOf("=");
|
|
@@ -12870,11 +13120,11 @@ async function pushToCloud(cfg, repo) {
|
|
|
12870
13120
|
while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
|
|
12871
13121
|
let jwt2 = "";
|
|
12872
13122
|
try {
|
|
12873
|
-
jwt2 =
|
|
13123
|
+
jwt2 = readFileSync34(join35(SYNKRO_DIR17, ".mcp-jwt"), "utf-8").trim();
|
|
12874
13124
|
} catch {
|
|
12875
13125
|
}
|
|
12876
|
-
if (!jwt2 || !
|
|
12877
|
-
const body =
|
|
13126
|
+
if (!jwt2 || !existsSync35(REACHABILITY_PATH)) return;
|
|
13127
|
+
const body = readFileSync34(REACHABILITY_PATH, "utf-8");
|
|
12878
13128
|
try {
|
|
12879
13129
|
const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
|
|
12880
13130
|
method: "POST",
|
|
@@ -12901,12 +13151,12 @@ async function reachabilityScanCommand(args2 = []) {
|
|
|
12901
13151
|
const isCloud = cfg.SYNKRO_DEPLOY_LOCATION === "cloud" || cfg.SYNKRO_STORAGE_MODE === "cloud";
|
|
12902
13152
|
if (isCloud) await pushToCloud(cfg, cfg.SYNKRO_CONNECTED_REPO || repoSlug(root));
|
|
12903
13153
|
}
|
|
12904
|
-
var
|
|
13154
|
+
var SYNKRO_DIR17;
|
|
12905
13155
|
var init_reachabilityScan2 = __esm({
|
|
12906
13156
|
"cli/commands/reachabilityScan.ts"() {
|
|
12907
13157
|
"use strict";
|
|
12908
13158
|
init_reachabilityScan();
|
|
12909
|
-
|
|
13159
|
+
SYNKRO_DIR17 = join35(homedir35(), ".synkro");
|
|
12910
13160
|
}
|
|
12911
13161
|
});
|
|
12912
13162
|
|
|
@@ -12935,9 +13185,9 @@ async function startCommand(rest = []) {
|
|
|
12935
13185
|
assertDockerAvailable();
|
|
12936
13186
|
const cfg = resolveWorkerConfig(rest);
|
|
12937
13187
|
if (cfg.explicit) {
|
|
12938
|
-
console.log(`Synkro: starting server (${cfg.claudeWorkers} claude + ${cfg.cursorWorkers} cursor)
|
|
13188
|
+
console.log(`Synkro: starting server (${cfg.claudeWorkers} claude + ${cfg.cursorWorkers} cursor + ${cfg.codexWorkers} codex)
|
|
12939
13189
|
`);
|
|
12940
|
-
await dockerUpdate({ claudeWorkers: cfg.claudeWorkers, cursorWorkers: cfg.cursorWorkers, connectedRepo: resolveConnectedRepo() });
|
|
13190
|
+
await dockerUpdate({ claudeWorkers: cfg.claudeWorkers, cursorWorkers: cfg.cursorWorkers, codexWorkers: cfg.codexWorkers, conductorProvider: cfg.conductorProvider, connectedRepo: resolveConnectedRepo() });
|
|
12941
13191
|
const ready = await waitForContainerReady(6e4);
|
|
12942
13192
|
if (!ready) {
|
|
12943
13193
|
console.error("\n\u26A0 container did not pass /healthz within 60s");
|
|
@@ -12964,10 +13214,12 @@ async function updateCommand() {
|
|
|
12964
13214
|
}
|
|
12965
13215
|
const claudeWorkers = cfg.claudeWorkers ?? 8;
|
|
12966
13216
|
const cursorWorkers = cfg.cursorWorkers ?? 0;
|
|
13217
|
+
const codexWorkers = cfg.codexWorkers ?? 0;
|
|
13218
|
+
const conductorProvider = cfg.conductorProvider;
|
|
12967
13219
|
console.log("Synkro: updating to the latest container image");
|
|
12968
|
-
console.log(` preserving pool: ${claudeWorkers} claude + ${cursorWorkers} cursor worker(s)
|
|
13220
|
+
console.log(` preserving pool: ${claudeWorkers} claude + ${cursorWorkers} cursor + ${codexWorkers} codex worker(s)
|
|
12969
13221
|
`);
|
|
12970
|
-
await dockerUpdate({ claudeWorkers, cursorWorkers, connectedRepo: resolveConnectedRepo() });
|
|
13222
|
+
await dockerUpdate({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider, connectedRepo: resolveConnectedRepo() });
|
|
12971
13223
|
const ready = await waitForContainerReady(9e4);
|
|
12972
13224
|
if (!ready) {
|
|
12973
13225
|
console.error("\n\u26A0 container did not pass its health check within 90s \u2014 check: docker logs synkro-server");
|
|
@@ -12991,16 +13243,20 @@ async function restartCommand(rest = []) {
|
|
|
12991
13243
|
const cfg = resolveWorkerConfig(rest);
|
|
12992
13244
|
let claudeWorkers = cfg.claudeWorkers;
|
|
12993
13245
|
let cursorWorkers = cfg.cursorWorkers;
|
|
13246
|
+
let codexWorkers = cfg.codexWorkers;
|
|
13247
|
+
let conductorProvider = cfg.conductorProvider;
|
|
12994
13248
|
if (!cfg.explicit) {
|
|
12995
13249
|
const reconciled = reconcileHarness();
|
|
12996
13250
|
if (reconciled) {
|
|
12997
13251
|
claudeWorkers = reconciled.claudeWorkers;
|
|
12998
13252
|
cursorWorkers = reconciled.cursorWorkers;
|
|
13253
|
+
codexWorkers = reconciled.codexWorkers;
|
|
13254
|
+
conductorProvider = reconciled.conductorProvider;
|
|
12999
13255
|
}
|
|
13000
13256
|
}
|
|
13001
|
-
console.log(`Synkro: restarting server (${claudeWorkers} claude + ${cursorWorkers} cursor)
|
|
13257
|
+
console.log(`Synkro: restarting server (${claudeWorkers} claude + ${cursorWorkers} cursor + ${codexWorkers} codex)
|
|
13002
13258
|
`);
|
|
13003
|
-
await dockerUpdate({ claudeWorkers, cursorWorkers, connectedRepo: resolveConnectedRepo() });
|
|
13259
|
+
await dockerUpdate({ claudeWorkers, cursorWorkers, codexWorkers, conductorProvider, connectedRepo: resolveConnectedRepo() });
|
|
13004
13260
|
const ready = await waitForContainerReady(6e4);
|
|
13005
13261
|
if (!ready) {
|
|
13006
13262
|
console.error("\n\u26A0 container did not pass /healthz within 60s");
|
|
@@ -13028,13 +13284,13 @@ var config_exports = {};
|
|
|
13028
13284
|
__export(config_exports, {
|
|
13029
13285
|
configCommand: () => configCommand
|
|
13030
13286
|
});
|
|
13031
|
-
import { readFileSync as
|
|
13032
|
-
import { join as
|
|
13033
|
-
import { homedir as
|
|
13287
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync25, existsSync as existsSync36 } from "fs";
|
|
13288
|
+
import { join as join36 } from "path";
|
|
13289
|
+
import { homedir as homedir36 } from "os";
|
|
13034
13290
|
function readConfigEnv5() {
|
|
13035
|
-
if (!
|
|
13291
|
+
if (!existsSync36(CONFIG_PATH9)) return {};
|
|
13036
13292
|
const out = {};
|
|
13037
|
-
for (const line of
|
|
13293
|
+
for (const line of readFileSync35(CONFIG_PATH9, "utf-8").split("\n")) {
|
|
13038
13294
|
const t = line.trim();
|
|
13039
13295
|
if (!t || t.startsWith("#")) continue;
|
|
13040
13296
|
const eq = t.indexOf("=");
|
|
@@ -13043,11 +13299,11 @@ function readConfigEnv5() {
|
|
|
13043
13299
|
return out;
|
|
13044
13300
|
}
|
|
13045
13301
|
function updateConfigValue(key, value) {
|
|
13046
|
-
if (!
|
|
13302
|
+
if (!existsSync36(CONFIG_PATH9)) {
|
|
13047
13303
|
console.error("No config found. Run `synkro install` first.");
|
|
13048
13304
|
process.exit(1);
|
|
13049
13305
|
}
|
|
13050
|
-
const lines =
|
|
13306
|
+
const lines = readFileSync35(CONFIG_PATH9, "utf-8").split("\n");
|
|
13051
13307
|
const pattern = new RegExp(`^${key}=`);
|
|
13052
13308
|
let found = false;
|
|
13053
13309
|
const updated = lines.map((line) => {
|
|
@@ -13058,7 +13314,7 @@ function updateConfigValue(key, value) {
|
|
|
13058
13314
|
return line;
|
|
13059
13315
|
});
|
|
13060
13316
|
if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
|
|
13061
|
-
|
|
13317
|
+
writeFileSync25(CONFIG_PATH9, updated.join("\n"), "utf-8");
|
|
13062
13318
|
}
|
|
13063
13319
|
function resolveInferenceMode(cfg) {
|
|
13064
13320
|
if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
|
|
@@ -13210,14 +13466,14 @@ To change:`);
|
|
|
13210
13466
|
}
|
|
13211
13467
|
if (inferenceValue !== "cloud") await reconcileContainer();
|
|
13212
13468
|
}
|
|
13213
|
-
var
|
|
13469
|
+
var SYNKRO_DIR18, CONFIG_PATH9;
|
|
13214
13470
|
var init_config = __esm({
|
|
13215
13471
|
"cli/commands/config.ts"() {
|
|
13216
13472
|
"use strict";
|
|
13217
13473
|
init_stub();
|
|
13218
13474
|
init_optout();
|
|
13219
|
-
|
|
13220
|
-
CONFIG_PATH9 =
|
|
13475
|
+
SYNKRO_DIR18 = join36(homedir36(), ".synkro");
|
|
13476
|
+
CONFIG_PATH9 = join36(SYNKRO_DIR18, "config.env");
|
|
13221
13477
|
}
|
|
13222
13478
|
});
|
|
13223
13479
|
|
|
@@ -13406,14 +13662,14 @@ Usage:
|
|
|
13406
13662
|
});
|
|
13407
13663
|
|
|
13408
13664
|
// cli/bootstrap.js
|
|
13409
|
-
import { readFileSync as
|
|
13665
|
+
import { readFileSync as readFileSync36, existsSync as existsSync37 } from "fs";
|
|
13410
13666
|
import { resolve as resolve5 } from "path";
|
|
13411
13667
|
var envCandidates = [
|
|
13412
13668
|
resolve5(process.env.HOME ?? "", ".synkro", "config.env")
|
|
13413
13669
|
];
|
|
13414
13670
|
for (const envPath of envCandidates) {
|
|
13415
|
-
if (!
|
|
13416
|
-
const envContent =
|
|
13671
|
+
if (!existsSync37(envPath)) continue;
|
|
13672
|
+
const envContent = readFileSync36(envPath, "utf-8");
|
|
13417
13673
|
for (const line of envContent.split("\n")) {
|
|
13418
13674
|
const trimmed = line.trim();
|
|
13419
13675
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -13430,7 +13686,7 @@ var subArgs = args.slice(1);
|
|
|
13430
13686
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
13431
13687
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
13432
13688
|
function printVersion() {
|
|
13433
|
-
console.log("1.7.
|
|
13689
|
+
console.log("1.7.88");
|
|
13434
13690
|
}
|
|
13435
13691
|
function printHelp2() {
|
|
13436
13692
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|