@synkro-sh/cli 1.10.6 → 1.10.8
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 +1202 -369
- package/dist/bootstrap.js.map +1 -1
- package/package.json +3 -1
package/dist/bootstrap.js
CHANGED
|
@@ -147,7 +147,7 @@ function getIdentity() {
|
|
|
147
147
|
if (cached2) return cached2;
|
|
148
148
|
let cliVersion2 = "0.0.0";
|
|
149
149
|
try {
|
|
150
|
-
cliVersion2 = "1.10.
|
|
150
|
+
cliVersion2 = "1.10.8";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -2351,7 +2351,7 @@ function buildCodexHookTrustEdits(hooks) {
|
|
|
2351
2351
|
return [...edits.values()];
|
|
2352
2352
|
}
|
|
2353
2353
|
function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTrust = false) {
|
|
2354
|
-
return new Promise((
|
|
2354
|
+
return new Promise((resolve8) => {
|
|
2355
2355
|
let settled = false;
|
|
2356
2356
|
let stdout = "";
|
|
2357
2357
|
let pending = "";
|
|
@@ -2369,7 +2369,7 @@ function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTru
|
|
|
2369
2369
|
child.kill();
|
|
2370
2370
|
} catch {
|
|
2371
2371
|
}
|
|
2372
|
-
|
|
2372
|
+
resolve8(summary);
|
|
2373
2373
|
};
|
|
2374
2374
|
const timer = setTimeout(() => finish(null), 1e4);
|
|
2375
2375
|
try {
|
|
@@ -5181,7 +5181,7 @@ function createCallbackServer() {
|
|
|
5181
5181
|
"Access-Control-Allow-Headers": "Content-Type",
|
|
5182
5182
|
"Vary": "Origin"
|
|
5183
5183
|
};
|
|
5184
|
-
return new Promise((
|
|
5184
|
+
return new Promise((resolve8, reject) => {
|
|
5185
5185
|
const server = createServer((req, res) => {
|
|
5186
5186
|
if (req.method === "OPTIONS") {
|
|
5187
5187
|
const origin = req.headers.origin;
|
|
@@ -5270,7 +5270,7 @@ function createCallbackServer() {
|
|
|
5270
5270
|
res.end(JSON.stringify({ ok: true }));
|
|
5271
5271
|
setTimeout(() => {
|
|
5272
5272
|
server.close();
|
|
5273
|
-
|
|
5273
|
+
resolve8(authData);
|
|
5274
5274
|
}, 200);
|
|
5275
5275
|
});
|
|
5276
5276
|
req.on("error", (e) => {
|
|
@@ -5606,7 +5606,7 @@ function detectSubdirRepos() {
|
|
|
5606
5606
|
}
|
|
5607
5607
|
}
|
|
5608
5608
|
function ask(rl, question) {
|
|
5609
|
-
return new Promise((
|
|
5609
|
+
return new Promise((resolve8) => rl.question(question, resolve8));
|
|
5610
5610
|
}
|
|
5611
5611
|
async function linkRepo(repo, linkedNames) {
|
|
5612
5612
|
try {
|
|
@@ -5845,7 +5845,7 @@ async function runClaudeDesktopTap(opts = {}) {
|
|
|
5845
5845
|
writeFileSync12(join13(sessionDir, "mcp_patch.py"), MCP_PATCH_PY, "utf-8");
|
|
5846
5846
|
const runnerPath = join13(sessionDir, "run.sh");
|
|
5847
5847
|
writeFileSync12(runnerPath, buildRunner(sessionDir), { mode: 493 });
|
|
5848
|
-
await new Promise((
|
|
5848
|
+
await new Promise((resolve8) => {
|
|
5849
5849
|
const child = spawn3("bash", [runnerPath], {
|
|
5850
5850
|
stdio: "inherit",
|
|
5851
5851
|
env: { ...process.env, SYNKRO_CAPTURE_URL: CAPTURE_URL, SYNKRO_SCAN_URL: SCAN_URL, SYNKRO_SCAN_TURN_URL: SCAN_TURN_URL, SYNKRO_DLP_POLICY_URL: DLP_POLICY_URL, SYNKRO_TURN_VERDICTS_URL: TURN_VERDICTS_URL, SYNKRO_TURN_VERDICT_URL: TURN_VERDICT_URL, SYNKRO_MCP_EVENT_URL: MCP_EVENT_URL, SYNKRO_TAP_TOKEN: token, SYNKRO_TAP_TOKEN_FILE: JWT_PATH, SYNKRO_CD_BACKFILL: opts.backfill ? "1" : "" }
|
|
@@ -5861,7 +5861,7 @@ async function runClaudeDesktopTap(opts = {}) {
|
|
|
5861
5861
|
child.on("exit", () => {
|
|
5862
5862
|
process.off("SIGINT", forward);
|
|
5863
5863
|
process.off("SIGTERM", forward);
|
|
5864
|
-
|
|
5864
|
+
resolve8();
|
|
5865
5865
|
});
|
|
5866
5866
|
});
|
|
5867
5867
|
}
|
|
@@ -7934,7 +7934,7 @@ var init_dockerInstall = __esm({
|
|
|
7934
7934
|
HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
|
|
7935
7935
|
CONTAINER_NAME = resolveContainerName();
|
|
7936
7936
|
defaultImageVersion = () => {
|
|
7937
|
-
if (true) return "1.10.
|
|
7937
|
+
if (true) return "1.10.8";
|
|
7938
7938
|
try {
|
|
7939
7939
|
const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
|
|
7940
7940
|
if (pkg.version) return pkg.version;
|
|
@@ -7966,7 +7966,7 @@ function captureClaudeSetupToken() {
|
|
|
7966
7966
|
const bin = "script";
|
|
7967
7967
|
const args2 = isMac ? ["-q", tmpFile, "claude", "setup-token"] : ["-qec", "claude setup-token", tmpFile];
|
|
7968
7968
|
const OAUTH_HINT = 'The browser approval did not return a token. This usually means claude.ai rejected the OAuth request (its "Authorization failed \u2014 Unsupported media type" page), most often because a browser extension (Grammarly, ad/script blockers, AI-assistant toolbars) stripped the request headers, or you approved on the wrong Claude account. Fix: retry in a clean incognito window with extensions disabled, and approve on the Claude account you want the cloud workers to use.';
|
|
7969
|
-
return new Promise((
|
|
7969
|
+
return new Promise((resolve8, reject) => {
|
|
7970
7970
|
const proc = nodeSpawn(bin, args2, {
|
|
7971
7971
|
stdio: "inherit",
|
|
7972
7972
|
env: { ...process.env, FORCE_COLOR: "3", COLORTERM: "truecolor", TERM: "xterm-256color" }
|
|
@@ -8033,7 +8033,7 @@ function captureClaudeSetupToken() {
|
|
|
8033
8033
|
reject(new Error(`Captured no setup token from claude setup-token output. ${reason}`));
|
|
8034
8034
|
return;
|
|
8035
8035
|
}
|
|
8036
|
-
|
|
8036
|
+
resolve8(token);
|
|
8037
8037
|
});
|
|
8038
8038
|
});
|
|
8039
8039
|
}
|
|
@@ -8059,13 +8059,13 @@ function findCodexBinary() {
|
|
|
8059
8059
|
function runCodexLogin(codexBin, codexHome) {
|
|
8060
8060
|
mkdirSync13(codexHome, { recursive: true, mode: 448 });
|
|
8061
8061
|
writeFileSync15(join17(codexHome, "config.toml"), 'cli_auth_credentials_store = "file"\n', { mode: 384 });
|
|
8062
|
-
return new Promise((
|
|
8062
|
+
return new Promise((resolve8, reject) => {
|
|
8063
8063
|
const proc = nodeSpawn2(codexBin, ["login"], {
|
|
8064
8064
|
stdio: "inherit",
|
|
8065
8065
|
env: { ...process.env, CODEX_HOME: codexHome }
|
|
8066
8066
|
});
|
|
8067
8067
|
proc.on("error", (err) => reject(new Error(`failed to spawn codex login: ${err.message}`)));
|
|
8068
|
-
proc.on("close", (code) => code === 0 ?
|
|
8068
|
+
proc.on("close", (code) => code === 0 ? resolve8() : reject(new Error(`codex login exited with code ${code}`)));
|
|
8069
8069
|
});
|
|
8070
8070
|
}
|
|
8071
8071
|
async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
|
|
@@ -8767,20 +8767,20 @@ async function promptAgentSelection(detected) {
|
|
|
8767
8767
|
detected.forEach((a, i) => console.log(` ${i + 1}. ${a.name}`));
|
|
8768
8768
|
console.log(` ${detected.length + 1}. Both / all (default)`);
|
|
8769
8769
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
8770
|
-
const ask3 = () => new Promise((
|
|
8770
|
+
const ask3 = () => new Promise((resolve8) => {
|
|
8771
8771
|
rl.question(`Pick [1-${detected.length + 1}] (default: all): `, (answer) => {
|
|
8772
8772
|
const t = answer.trim().toLowerCase();
|
|
8773
8773
|
if (t === "" || t === String(detected.length + 1) || t === "both" || t === "all") {
|
|
8774
8774
|
rl.close();
|
|
8775
|
-
return
|
|
8775
|
+
return resolve8(detected);
|
|
8776
8776
|
}
|
|
8777
8777
|
const n = parseInt(t, 10);
|
|
8778
8778
|
if (Number.isInteger(n) && n >= 1 && n <= detected.length) {
|
|
8779
8779
|
rl.close();
|
|
8780
|
-
return
|
|
8780
|
+
return resolve8([detected[n - 1]]);
|
|
8781
8781
|
}
|
|
8782
8782
|
console.log("Invalid choice. Try again.");
|
|
8783
|
-
|
|
8783
|
+
resolve8(ask3());
|
|
8784
8784
|
});
|
|
8785
8785
|
});
|
|
8786
8786
|
return ask3();
|
|
@@ -8803,12 +8803,12 @@ async function promptCursorApiKey(opts) {
|
|
|
8803
8803
|
return;
|
|
8804
8804
|
}
|
|
8805
8805
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
8806
|
-
const key = await new Promise((
|
|
8806
|
+
const key = await new Promise((resolve8) => {
|
|
8807
8807
|
rl.question(
|
|
8808
8808
|
"Cursor grading needs a Cursor API key (cursor.com \u2192 Settings \u2192 API Keys).\nPaste it now, or press Enter to skip (Cursor workers stay idle until set): ",
|
|
8809
8809
|
(answer) => {
|
|
8810
8810
|
rl.close();
|
|
8811
|
-
|
|
8811
|
+
resolve8(answer.trim());
|
|
8812
8812
|
}
|
|
8813
8813
|
);
|
|
8814
8814
|
});
|
|
@@ -8823,7 +8823,7 @@ async function promptDeployLocation(current = "local") {
|
|
|
8823
8823
|
if (!process.stdin.isTTY) return current;
|
|
8824
8824
|
const other = current === "cloud" ? "local" : "cloud";
|
|
8825
8825
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
8826
|
-
return new Promise((
|
|
8826
|
+
return new Promise((resolve8) => {
|
|
8827
8827
|
rl.question(
|
|
8828
8828
|
`Where should Synkro run?
|
|
8829
8829
|
local \u2014 a grading container on this machine (Docker)
|
|
@@ -8832,7 +8832,7 @@ Each worker uses the account credentials you authorize. Choose [${current}] / ${
|
|
|
8832
8832
|
(answer) => {
|
|
8833
8833
|
rl.close();
|
|
8834
8834
|
const a = answer.trim().toLowerCase();
|
|
8835
|
-
|
|
8835
|
+
resolve8(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
|
|
8836
8836
|
}
|
|
8837
8837
|
);
|
|
8838
8838
|
});
|
|
@@ -8994,7 +8994,7 @@ function writeConfigEnv(opts) {
|
|
|
8994
8994
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
8995
8995
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
8996
8996
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
8997
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.10.
|
|
8997
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.10.8")}`
|
|
8998
8998
|
];
|
|
8999
8999
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
9000
9000
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -9741,7 +9741,7 @@ async function installCommand(opts = {}) {
|
|
|
9741
9741
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
9742
9742
|
emit("install", {
|
|
9743
9743
|
phase: "started",
|
|
9744
|
-
cli_version_to: "1.10.
|
|
9744
|
+
cli_version_to: "1.10.8",
|
|
9745
9745
|
agents_detected: agents.map((a) => a.kind),
|
|
9746
9746
|
with_github: false,
|
|
9747
9747
|
with_local_cc: false,
|
|
@@ -10809,8 +10809,8 @@ function ensureReachabilityGitHook() {
|
|
|
10809
10809
|
}
|
|
10810
10810
|
return "updated";
|
|
10811
10811
|
}
|
|
10812
|
-
const
|
|
10813
|
-
writeFileSync17(hookPath, cur +
|
|
10812
|
+
const sep4 = cur.endsWith("\n") ? "" : "\n";
|
|
10813
|
+
writeFileSync17(hookPath, cur + sep4 + "\n" + block + "\n");
|
|
10814
10814
|
try {
|
|
10815
10815
|
chmodSync5(hookPath, 493);
|
|
10816
10816
|
} catch {
|
|
@@ -12030,10 +12030,10 @@ function confirmPurge() {
|
|
|
12030
12030
|
return Promise.resolve(false);
|
|
12031
12031
|
}
|
|
12032
12032
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
12033
|
-
return new Promise((
|
|
12033
|
+
return new Promise((resolve8) => {
|
|
12034
12034
|
rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
|
|
12035
12035
|
rl.close();
|
|
12036
|
-
|
|
12036
|
+
resolve8(answer.trim().toLowerCase() === "yes");
|
|
12037
12037
|
});
|
|
12038
12038
|
});
|
|
12039
12039
|
}
|
|
@@ -12286,7 +12286,7 @@ async function submitToChannel(role, payload, opts = {}) {
|
|
|
12286
12286
|
const port = opts.port ?? CHANNEL_PORT;
|
|
12287
12287
|
const startedAt = Date.now();
|
|
12288
12288
|
try {
|
|
12289
|
-
const result = await new Promise((
|
|
12289
|
+
const result = await new Promise((resolve8, reject) => {
|
|
12290
12290
|
const req = httpRequest({
|
|
12291
12291
|
host: CHANNEL_HOST,
|
|
12292
12292
|
port,
|
|
@@ -12312,7 +12312,7 @@ async function submitToChannel(role, payload, opts = {}) {
|
|
|
12312
12312
|
reject(new LocalCCError(parsed.error));
|
|
12313
12313
|
return;
|
|
12314
12314
|
}
|
|
12315
|
-
|
|
12315
|
+
resolve8(String(parsed.result ?? ""));
|
|
12316
12316
|
} catch (err) {
|
|
12317
12317
|
reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
|
|
12318
12318
|
}
|
|
@@ -12338,14 +12338,14 @@ async function submitToChannel(role, payload, opts = {}) {
|
|
|
12338
12338
|
}
|
|
12339
12339
|
}
|
|
12340
12340
|
function isChannelAvailable(port = CHANNEL_PORT, timeoutMs = 500) {
|
|
12341
|
-
return new Promise((
|
|
12341
|
+
return new Promise((resolve8) => {
|
|
12342
12342
|
const sock = connect(port, CHANNEL_HOST);
|
|
12343
12343
|
const done = (ok) => {
|
|
12344
12344
|
try {
|
|
12345
12345
|
sock.destroy();
|
|
12346
12346
|
} catch {
|
|
12347
12347
|
}
|
|
12348
|
-
|
|
12348
|
+
resolve8(ok);
|
|
12349
12349
|
};
|
|
12350
12350
|
sock.once("connect", () => done(true));
|
|
12351
12351
|
sock.once("error", () => done(false));
|
|
@@ -12377,10 +12377,10 @@ __export(grade_exports, {
|
|
|
12377
12377
|
gradeCommand: () => gradeCommand
|
|
12378
12378
|
});
|
|
12379
12379
|
async function readStdin() {
|
|
12380
|
-
return new Promise((
|
|
12380
|
+
return new Promise((resolve8, reject) => {
|
|
12381
12381
|
const chunks = [];
|
|
12382
12382
|
process.stdin.on("data", (c) => chunks.push(c));
|
|
12383
|
-
process.stdin.on("end", () =>
|
|
12383
|
+
process.stdin.on("end", () => resolve8(Buffer.concat(chunks).toString("utf-8")));
|
|
12384
12384
|
process.stdin.on("error", reject);
|
|
12385
12385
|
});
|
|
12386
12386
|
}
|
|
@@ -12634,7 +12634,7 @@ function spawnClaudeJudge(file, claudeToken, promptHeader) {
|
|
|
12634
12634
|
Diff:
|
|
12635
12635
|
${hunks}`;
|
|
12636
12636
|
const fullPrompt = promptHeader + userMessage;
|
|
12637
|
-
return new Promise((
|
|
12637
|
+
return new Promise((resolve8) => {
|
|
12638
12638
|
const t0 = Date.now();
|
|
12639
12639
|
const proc = spawn6(
|
|
12640
12640
|
"claude",
|
|
@@ -12662,7 +12662,7 @@ ${hunks}`;
|
|
|
12662
12662
|
const latencyMs = Date.now() - t0;
|
|
12663
12663
|
if (code !== 0) {
|
|
12664
12664
|
console.warn(` claude exited ${code}: ${(stderr || stdout).slice(0, 500)}`);
|
|
12665
|
-
|
|
12665
|
+
resolve8({ findings: [], latencyMs });
|
|
12666
12666
|
return;
|
|
12667
12667
|
}
|
|
12668
12668
|
try {
|
|
@@ -12681,10 +12681,10 @@ ${hunks}`;
|
|
|
12681
12681
|
description: f.description,
|
|
12682
12682
|
fix: f.fix
|
|
12683
12683
|
}));
|
|
12684
|
-
|
|
12684
|
+
resolve8({ findings, latencyMs });
|
|
12685
12685
|
} catch (parseErr) {
|
|
12686
12686
|
console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);
|
|
12687
|
-
|
|
12687
|
+
resolve8({ findings: [], latencyMs });
|
|
12688
12688
|
}
|
|
12689
12689
|
});
|
|
12690
12690
|
});
|
|
@@ -12733,7 +12733,7 @@ ${JSON.stringify(findings, null, 2)}
|
|
|
12733
12733
|
`;
|
|
12734
12734
|
}
|
|
12735
12735
|
function spawnOpusConsolidator(findings, claudeToken) {
|
|
12736
|
-
return new Promise((
|
|
12736
|
+
return new Promise((resolve8) => {
|
|
12737
12737
|
const prompt = buildConsolidationPrompt(findings);
|
|
12738
12738
|
const proc = spawn6(
|
|
12739
12739
|
"claude",
|
|
@@ -12760,7 +12760,7 @@ function spawnOpusConsolidator(findings, claudeToken) {
|
|
|
12760
12760
|
proc.on("close", (code) => {
|
|
12761
12761
|
if (code !== 0) {
|
|
12762
12762
|
console.warn(` opus consolidation exited ${code}: ${(stderr || stdout).slice(0, 300)}`);
|
|
12763
|
-
|
|
12763
|
+
resolve8(fallbackReview(findings));
|
|
12764
12764
|
return;
|
|
12765
12765
|
}
|
|
12766
12766
|
try {
|
|
@@ -12781,10 +12781,10 @@ function spawnOpusConsolidator(findings, claudeToken) {
|
|
|
12781
12781
|
const order = ["low", "medium", "high", "critical"];
|
|
12782
12782
|
return order.indexOf(f.severity) > order.indexOf(max) ? f.severity : max;
|
|
12783
12783
|
}, "low");
|
|
12784
|
-
|
|
12784
|
+
resolve8({ summary: review.summary || "", comments, severity: maxSeverity });
|
|
12785
12785
|
} catch {
|
|
12786
12786
|
console.warn(` failed to parse opus response, using fallback`);
|
|
12787
|
-
|
|
12787
|
+
resolve8(fallbackReview(findings));
|
|
12788
12788
|
}
|
|
12789
12789
|
});
|
|
12790
12790
|
});
|
|
@@ -13267,14 +13267,14 @@ function ensureRunning(opts = {}) {
|
|
|
13267
13267
|
return startTask(opts);
|
|
13268
13268
|
}
|
|
13269
13269
|
function probePort(host, port, timeoutMs = 500) {
|
|
13270
|
-
return new Promise((
|
|
13270
|
+
return new Promise((resolve8) => {
|
|
13271
13271
|
const sock = connect2(port, host);
|
|
13272
13272
|
const done = (ok) => {
|
|
13273
13273
|
try {
|
|
13274
13274
|
sock.destroy();
|
|
13275
13275
|
} catch {
|
|
13276
13276
|
}
|
|
13277
|
-
|
|
13277
|
+
resolve8(ok);
|
|
13278
13278
|
};
|
|
13279
13279
|
sock.once("connect", () => done(true));
|
|
13280
13280
|
sock.once("error", () => done(false));
|
|
@@ -13824,7 +13824,7 @@ function cmdLogs(rest) {
|
|
|
13824
13824
|
if (!raw) console.log(" " + colorize("(use --raw / -r to see full payloads, --live / -f to follow)", 90));
|
|
13825
13825
|
return;
|
|
13826
13826
|
}
|
|
13827
|
-
return new Promise((
|
|
13827
|
+
return new Promise((resolve8) => {
|
|
13828
13828
|
console.log(" " + colorize("\u2014 following new turns (Ctrl-C to exit) \u2014", 90));
|
|
13829
13829
|
const stop = followTurns((t) => {
|
|
13830
13830
|
console.log(" " + formatTurn(t, raw));
|
|
@@ -13832,7 +13832,7 @@ function cmdLogs(rest) {
|
|
|
13832
13832
|
const onSigint = () => {
|
|
13833
13833
|
stop();
|
|
13834
13834
|
process.removeListener("SIGINT", onSigint);
|
|
13835
|
-
|
|
13835
|
+
resolve8();
|
|
13836
13836
|
};
|
|
13837
13837
|
process.on("SIGINT", onSigint);
|
|
13838
13838
|
});
|
|
@@ -14121,9 +14121,9 @@ function parseSession(file, seenStableIds) {
|
|
|
14121
14121
|
}
|
|
14122
14122
|
function ask2(q) {
|
|
14123
14123
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
14124
|
-
return new Promise((
|
|
14124
|
+
return new Promise((resolve8) => rl.question(q, (a) => {
|
|
14125
14125
|
rl.close();
|
|
14126
|
-
|
|
14126
|
+
resolve8(/^y(es)?$/i.test(a.trim()));
|
|
14127
14127
|
}));
|
|
14128
14128
|
}
|
|
14129
14129
|
async function importCommand() {
|
|
@@ -14650,6 +14650,7 @@ function buildSpawnAgent(opts) {
|
|
|
14650
14650
|
// and its own history scrolls — without it only the last screen is
|
|
14651
14651
|
// reachable once the session is nested inside the layout.
|
|
14652
14652
|
["tmux", "set-option", "-t", session, "mouse", "on"],
|
|
14653
|
+
...buildClipboardBindings(session),
|
|
14653
14654
|
["tmux", "set-option", "-t", session, "-q", "@synkro_harness", opts.harness],
|
|
14654
14655
|
["tmux", "set-option", "-t", session, "-q", "@synkro_space", opts.space],
|
|
14655
14656
|
["tmux", "set-option", "-t", session, "-q", "@synkro_backend", opts.backend],
|
|
@@ -14658,6 +14659,13 @@ function buildSpawnAgent(opts) {
|
|
|
14658
14659
|
["tmux", "set-option", "-t", session, "remain-on-exit", "on"]
|
|
14659
14660
|
];
|
|
14660
14661
|
}
|
|
14662
|
+
function buildClipboardBindings(session) {
|
|
14663
|
+
return [
|
|
14664
|
+
["tmux", "set-option", "-t", session, "set-clipboard", "on"],
|
|
14665
|
+
["tmux", "bind-key", "-T", "copy-mode", "MouseDragEnd1Pane", "send-keys", "-X", "copy-pipe-and-cancel", SYSTEM_CLIPBOARD],
|
|
14666
|
+
["tmux", "bind-key", "-T", "copy-mode-vi", "MouseDragEnd1Pane", "send-keys", "-X", "copy-pipe-and-cancel", SYSTEM_CLIPBOARD]
|
|
14667
|
+
];
|
|
14668
|
+
}
|
|
14661
14669
|
function buildListAgents() {
|
|
14662
14670
|
return ["tmux", "list-sessions", "-F", ["#{session_name}", "#{?pane_dead,dead,alive}", "#{@synkro_harness}", "#{@synkro_space}", "#{@synkro_backend}", "#{@synkro_pueue}", "#{pane_current_path}"].join(FIELD_SEP)];
|
|
14663
14671
|
}
|
|
@@ -14701,6 +14709,9 @@ function buildSendText(session, text) {
|
|
|
14701
14709
|
function buildEnableMouse(session) {
|
|
14702
14710
|
return ["tmux", "set-option", "-t", session, "mouse", "on"];
|
|
14703
14711
|
}
|
|
14712
|
+
function buildKeepPaneAfterExit(pane) {
|
|
14713
|
+
return ["tmux", "set-option", "-p", "-t", pane, "remain-on-exit", "on"];
|
|
14714
|
+
}
|
|
14704
14715
|
function buildInterrupt(session) {
|
|
14705
14716
|
return ["tmux", "send-keys", "-t", session, "Escape"];
|
|
14706
14717
|
}
|
|
@@ -14767,7 +14778,7 @@ function parseWorktrees(porcelain) {
|
|
|
14767
14778
|
}
|
|
14768
14779
|
return rows;
|
|
14769
14780
|
}
|
|
14770
|
-
var execFileAsync, AGENT_PREFIX, UI_SESSION, CONTAINER_USER, FIELD_SEP;
|
|
14781
|
+
var execFileAsync, AGENT_PREFIX, UI_SESSION, CONTAINER_USER, SYSTEM_CLIPBOARD, FIELD_SEP;
|
|
14771
14782
|
var init_tmux = __esm({
|
|
14772
14783
|
"cli/ui/tmux.ts"() {
|
|
14773
14784
|
"use strict";
|
|
@@ -14775,6 +14786,7 @@ var init_tmux = __esm({
|
|
|
14775
14786
|
AGENT_PREFIX = "synkro-agent-";
|
|
14776
14787
|
UI_SESSION = "synkro-ui";
|
|
14777
14788
|
CONTAINER_USER = "synkro";
|
|
14789
|
+
SYSTEM_CLIPBOARD = "if command -v pbcopy >/dev/null 2>&1; then pbcopy; elif command -v wl-copy >/dev/null 2>&1; then wl-copy; elif command -v xclip >/dev/null 2>&1; then xclip -selection clipboard; else cat >/dev/null; fi";
|
|
14778
14790
|
FIELD_SEP = "|";
|
|
14779
14791
|
}
|
|
14780
14792
|
});
|
|
@@ -14907,6 +14919,10 @@ function visibleAgents(all, space, filter, grouped) {
|
|
|
14907
14919
|
if (grouped || filter === "all" || !space) return all;
|
|
14908
14920
|
return all.filter((agent) => agent.repo === space.path);
|
|
14909
14921
|
}
|
|
14922
|
+
function nextLiveAgent(all, preferredRepo = "") {
|
|
14923
|
+
const live = all.filter((agent) => agent.status !== "offline" && agent.status !== "done");
|
|
14924
|
+
return live.find((agent) => Boolean(preferredRepo) && agent.repo === preferredRepo) || live[0];
|
|
14925
|
+
}
|
|
14910
14926
|
async function discoverAgents(runner, backend, memory) {
|
|
14911
14927
|
const snapshot = await run(runner, buildAgentSnapshot());
|
|
14912
14928
|
const { list, captures } = parseAgentSnapshot(snapshot.ok ? snapshot.stdout : "");
|
|
@@ -15172,6 +15188,7 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
|
|
|
15172
15188
|
["bind-key", "-n", "M-s", "select-pane", "-L"]
|
|
15173
15189
|
];
|
|
15174
15190
|
for (const argv of style) await run(HOST, ["tmux", ...argv]);
|
|
15191
|
+
for (const argv of buildClipboardBindings(UI_SESSION)) await run(HOST, argv);
|
|
15175
15192
|
}
|
|
15176
15193
|
async function buildTab(bootPath, repoCwd, spec) {
|
|
15177
15194
|
await run(HOST, ["tmux", "set-option", "-g", "history-limit", "50000"]);
|
|
@@ -15214,6 +15231,7 @@ async function buildTab(bootPath, repoCwd, spec) {
|
|
|
15214
15231
|
const sidebarPane = split.stdout.trim();
|
|
15215
15232
|
const panes = await run(HOST, ["tmux", "list-panes", "-t", windowTarget, "-F", "#{pane_id}"]);
|
|
15216
15233
|
const centerPane = panes.stdout.split("\n").map((line) => line.trim()).filter(Boolean).find((id) => id !== sidebarPane) || "";
|
|
15234
|
+
await run(HOST, buildKeepPaneAfterExit(centerPane));
|
|
15217
15235
|
if (spec.agentSession) {
|
|
15218
15236
|
await run(HOST, ["tmux", "set-option", "-t", windowTarget, "-w", "-q", "@synkro_agent", spec.agentSession]);
|
|
15219
15237
|
}
|
|
@@ -15593,6 +15611,9 @@ async function detectContainerBackend() {
|
|
|
15593
15611
|
}
|
|
15594
15612
|
return { runner: { kind: "host" }, backend: "host", note: "runtime: host (container unavailable)" };
|
|
15595
15613
|
}
|
|
15614
|
+
function harnessCommand(request) {
|
|
15615
|
+
return request.resume ? resumeCommand(request.harness) : HARNESS_COMMANDS[request.harness] || "claude";
|
|
15616
|
+
}
|
|
15596
15617
|
async function provisionContainerWorkspace(runner, slug) {
|
|
15597
15618
|
const dir = CONTAINER_WORK + "/ui-" + slug;
|
|
15598
15619
|
await run(runner, [
|
|
@@ -15610,7 +15631,7 @@ async function spawnAgent(info, request) {
|
|
|
15610
15631
|
if (request.backend === "container") {
|
|
15611
15632
|
cwd = request.cwd.startsWith(CONTAINER_WORK) ? request.cwd : await provisionContainerWorkspace(runner, slug);
|
|
15612
15633
|
}
|
|
15613
|
-
const command =
|
|
15634
|
+
const command = harnessCommand(request);
|
|
15614
15635
|
for (const argv of buildSpawnAgent({ name: slug, cwd, command, harness: request.harness, space: cwd, backend: request.backend })) {
|
|
15615
15636
|
const result = await run(runner, argv);
|
|
15616
15637
|
if (!result.ok && argv[1] === "new-session") {
|
|
@@ -15901,6 +15922,24 @@ async function runSidebar() {
|
|
|
15901
15922
|
const agent = state.agents[state.agentIndex];
|
|
15902
15923
|
if (agent) await showAgent(agent);
|
|
15903
15924
|
}
|
|
15925
|
+
async function showWorkspaceTerminal(cwd) {
|
|
15926
|
+
if (!centerPane || !cwd) return;
|
|
15927
|
+
const command = makeTerminalCommand(bootPath);
|
|
15928
|
+
const argv = ["tmux", "respawn-pane", "-k", "-t", centerPane, "-c", cwd];
|
|
15929
|
+
if (command) argv.push(command);
|
|
15930
|
+
const respawned = await run(host, argv);
|
|
15931
|
+
if (!respawned.ok) {
|
|
15932
|
+
state.message = "could not restore workspace terminal";
|
|
15933
|
+
return;
|
|
15934
|
+
}
|
|
15935
|
+
const spaceName = cwd.split("/").filter(Boolean).pop() || "space";
|
|
15936
|
+
await run(host, ["tmux", "set-option", "-t", centerPane, "-w", "-u", "@synkro_agent"]);
|
|
15937
|
+
await run(host, ["tmux", "set-option", "-t", centerPane, "-w", "-q", "@synkro_cwd", cwd]);
|
|
15938
|
+
await run(host, ["tmux", "set-option", "-t", centerPane, "-w", "-q", "@synkro_kind", "terminal"]);
|
|
15939
|
+
await run(host, ["tmux", "rename-window", "-t", centerPane, tabTitle("terminal", spaceName)]);
|
|
15940
|
+
state.viewing = "";
|
|
15941
|
+
await snapshotTabs();
|
|
15942
|
+
}
|
|
15904
15943
|
async function showAgent(agent) {
|
|
15905
15944
|
if (!centerPane) return;
|
|
15906
15945
|
if (agent.status === "offline") {
|
|
@@ -15919,6 +15958,9 @@ async function runSidebar() {
|
|
|
15919
15958
|
return;
|
|
15920
15959
|
}
|
|
15921
15960
|
await run(runnerFor(agent.backend), buildEnableMouse(agent.session));
|
|
15961
|
+
for (const argv of buildClipboardBindings(agent.session)) {
|
|
15962
|
+
await run(runnerFor(agent.backend), argv);
|
|
15963
|
+
}
|
|
15922
15964
|
const command = buildCenterAttachCommand(runnerFor(agent.backend), agent.session);
|
|
15923
15965
|
await run(host, ["tmux", "respawn-pane", "-k", "-t", centerPane, command]);
|
|
15924
15966
|
const where = agent.space ? agent.space.split("/").filter(Boolean).pop() || "" : "";
|
|
@@ -15931,7 +15973,15 @@ async function runSidebar() {
|
|
|
15931
15973
|
async function confirmKillAgent() {
|
|
15932
15974
|
const agent = state.agents[state.agentIndex];
|
|
15933
15975
|
if (!agent) return;
|
|
15976
|
+
const wasViewing = state.viewing === agent.session;
|
|
15934
15977
|
await openDialog("kill", [agent.backend, agent.session]);
|
|
15978
|
+
await refresh();
|
|
15979
|
+
if (allAgents.some((candidate) => candidate.session === agent.session && candidate.status !== "offline")) return;
|
|
15980
|
+
state.message = "closed " + agent.name;
|
|
15981
|
+
if (!wasViewing) return;
|
|
15982
|
+
await showWorkspaceTerminal(agent.space || selectedSpace()?.path || repoCwd);
|
|
15983
|
+
const successor = nextLiveAgent(allAgents, agent.repo || "");
|
|
15984
|
+
if (successor) await showAgent(successor);
|
|
15935
15985
|
}
|
|
15936
15986
|
async function consent(action) {
|
|
15937
15987
|
const agent = state.agents[state.agentIndex];
|
|
@@ -15954,6 +16004,7 @@ async function runSidebar() {
|
|
|
15954
16004
|
const remembered = space ? lastAgentFor(space.path) : "";
|
|
15955
16005
|
const agent = allAgents.find((row2) => row2.session === remembered && row2.status !== "offline");
|
|
15956
16006
|
if (agent) await showAgent(agent);
|
|
16007
|
+
else if (space) await showWorkspaceTerminal(space.path);
|
|
15957
16008
|
} else if (target.kind === "agent") {
|
|
15958
16009
|
state.section = "agents";
|
|
15959
16010
|
state.agentIndex = target.index;
|
|
@@ -16155,6 +16206,9 @@ var init_repos = __esm({
|
|
|
16155
16206
|
|
|
16156
16207
|
// cli/ui/tabs.ts
|
|
16157
16208
|
import { execSync as execSync7 } from "child_process";
|
|
16209
|
+
function isLegacyCodexRenderer(command) {
|
|
16210
|
+
return /(?:^|\s)ui\s+--run\s+codex(?:\s|$)/.test(String(command || "").trim());
|
|
16211
|
+
}
|
|
16158
16212
|
function repoRoot() {
|
|
16159
16213
|
try {
|
|
16160
16214
|
return execSync7("git rev-parse --show-toplevel", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim() || process.cwd();
|
|
@@ -16175,16 +16229,6 @@ async function createTab(bootPath, kind, spacePath) {
|
|
|
16175
16229
|
});
|
|
16176
16230
|
return;
|
|
16177
16231
|
}
|
|
16178
|
-
if (kind === "cursor-synkro") {
|
|
16179
|
-
await buildTab(bootPath, repo, {
|
|
16180
|
-
cwd: spacePath,
|
|
16181
|
-
center: ["node", bootPath, "ui", "--run", "cursor", spacePath].map(shellQuote3).join(" "),
|
|
16182
|
-
title: tabTitle("cursor", space),
|
|
16183
|
-
kind: "cursor-synkro",
|
|
16184
|
-
focus: "center"
|
|
16185
|
-
});
|
|
16186
|
-
return;
|
|
16187
|
-
}
|
|
16188
16232
|
if (kind === "settings") {
|
|
16189
16233
|
await buildTab(bootPath, repo, {
|
|
16190
16234
|
cwd: spacePath,
|
|
@@ -16195,12 +16239,13 @@ async function createTab(bootPath, kind, spacePath) {
|
|
|
16195
16239
|
});
|
|
16196
16240
|
return;
|
|
16197
16241
|
}
|
|
16242
|
+
if (!["claude", "codex", "cursor"].includes(kind)) return;
|
|
16198
16243
|
const info = await detectContainerBackend();
|
|
16199
16244
|
const spaceName = space;
|
|
16200
16245
|
const stamp = String(process.pid % 1e4);
|
|
16201
|
-
const harness =
|
|
16246
|
+
const harness = kind;
|
|
16202
16247
|
const reachable = info.backend === "container" && (await run(info.runner, ["test", "-d", spacePath])).ok;
|
|
16203
|
-
const backend = reachable ? "container" : "host";
|
|
16248
|
+
const backend = harness === "codex" ? "host" : reachable ? "container" : "host";
|
|
16204
16249
|
const spawned = await spawnAgent(info, {
|
|
16205
16250
|
name: spaceName + "-" + harness + "-" + stamp,
|
|
16206
16251
|
harness,
|
|
@@ -16231,16 +16276,34 @@ async function openAgentTab(bootPath, session) {
|
|
|
16231
16276
|
]);
|
|
16232
16277
|
const [harness, space, backend] = (meta.stdout.trim() || "||").split("|");
|
|
16233
16278
|
const containerHosted = backend === "container";
|
|
16234
|
-
|
|
16235
|
-
|
|
16236
|
-
if (!alive) return;
|
|
16279
|
+
let runner = containerHosted ? info.runner : HOST2;
|
|
16280
|
+
let activeSession = session;
|
|
16237
16281
|
const spaceName = (space || "").split("/").filter(Boolean).pop() || "space";
|
|
16282
|
+
if (harness === "codex") {
|
|
16283
|
+
const command = await run(HOST2, ["tmux", "display-message", "-p", "-t", session, "#{pane_start_command}"]);
|
|
16284
|
+
if (command.ok && isLegacyCodexRenderer(command.stdout)) {
|
|
16285
|
+
await run(HOST2, buildKillSession(session));
|
|
16286
|
+
const restored = await spawnAgent(info, {
|
|
16287
|
+
name: session.replace(/^synkro-agent-/, ""),
|
|
16288
|
+
harness: "codex",
|
|
16289
|
+
spaceName,
|
|
16290
|
+
cwd: space || repoRoot(),
|
|
16291
|
+
backend: "host",
|
|
16292
|
+
resume: true
|
|
16293
|
+
});
|
|
16294
|
+
if (!restored.ok) return;
|
|
16295
|
+
activeSession = restored.session;
|
|
16296
|
+
runner = HOST2;
|
|
16297
|
+
}
|
|
16298
|
+
}
|
|
16299
|
+
const alive = (await run(runner, ["tmux", "has-session", "-t", activeSession])).ok;
|
|
16300
|
+
if (!alive) return;
|
|
16238
16301
|
await buildTab(bootPath, repoRoot(), {
|
|
16239
16302
|
cwd: space || repoRoot(),
|
|
16240
|
-
center: buildCenterAttachCommand(runner,
|
|
16303
|
+
center: buildCenterAttachCommand(runner, activeSession),
|
|
16241
16304
|
title: tabTitle(harness || "claude", spaceName),
|
|
16242
16305
|
kind: harness || "claude",
|
|
16243
|
-
agentSession:
|
|
16306
|
+
agentSession: activeSession,
|
|
16244
16307
|
focus: "center"
|
|
16245
16308
|
});
|
|
16246
16309
|
}
|
|
@@ -16256,11 +16319,23 @@ async function restoreTabs(bootPath, repoCwd) {
|
|
|
16256
16319
|
const space = tab.cwd.split("/").filter(Boolean).pop() || "space";
|
|
16257
16320
|
const containerHosted = info.backend === "container" && (await run(info.runner, ["test", "-d", tab.cwd])).ok;
|
|
16258
16321
|
const harness = ["claude", "codex", "cursor"].includes(tab.kind) ? tab.kind : "";
|
|
16259
|
-
|
|
16260
|
-
|
|
16322
|
+
const sessionRunner = harness === "codex" ? HOST2 : containerHosted ? info.runner : HOST2;
|
|
16323
|
+
let sessionAlive = Boolean(tab.agentSession && alive.has(tab.agentSession));
|
|
16324
|
+
if (sessionAlive && harness === "codex") {
|
|
16325
|
+
const command = await run(HOST2, ["tmux", "display-message", "-p", "-t", tab.agentSession, "#{pane_start_command}"]);
|
|
16326
|
+
if (command.ok && isLegacyCodexRenderer(command.stdout)) {
|
|
16327
|
+
await run(HOST2, buildKillSession(tab.agentSession));
|
|
16328
|
+
sessionAlive = false;
|
|
16329
|
+
}
|
|
16330
|
+
}
|
|
16331
|
+
if (tab.agentSession && sessionAlive) {
|
|
16332
|
+
await run(sessionRunner, buildEnableMouse(tab.agentSession));
|
|
16333
|
+
for (const argv of buildClipboardBindings(tab.agentSession)) {
|
|
16334
|
+
await run(sessionRunner, argv);
|
|
16335
|
+
}
|
|
16261
16336
|
await buildTab(bootPath, repoCwd, {
|
|
16262
16337
|
cwd: tab.cwd,
|
|
16263
|
-
center: buildCenterAttachCommand(
|
|
16338
|
+
center: buildCenterAttachCommand(sessionRunner, tab.agentSession),
|
|
16264
16339
|
title: tab.title,
|
|
16265
16340
|
kind: tab.kind,
|
|
16266
16341
|
agentSession: tab.agentSession,
|
|
@@ -16272,12 +16347,13 @@ async function restoreTabs(bootPath, repoCwd) {
|
|
|
16272
16347
|
harness,
|
|
16273
16348
|
spaceName: space,
|
|
16274
16349
|
cwd: tab.cwd,
|
|
16275
|
-
backend: containerHosted ? "container" : "host",
|
|
16350
|
+
backend: harness === "codex" ? "host" : containerHosted ? "container" : "host",
|
|
16276
16351
|
resume: true
|
|
16277
16352
|
});
|
|
16353
|
+
const restoredInContainer = harness !== "codex" && containerHosted;
|
|
16278
16354
|
await buildTab(bootPath, repoCwd, spawned.ok ? {
|
|
16279
16355
|
cwd: tab.cwd,
|
|
16280
|
-
center: buildCenterAttachCommand(
|
|
16356
|
+
center: buildCenterAttachCommand(restoredInContainer ? info.runner : HOST2, spawned.session),
|
|
16281
16357
|
title: tabTitle(harness, space),
|
|
16282
16358
|
kind: harness,
|
|
16283
16359
|
agentSession: spawned.session,
|
|
@@ -16370,7 +16446,7 @@ async function pick(opts) {
|
|
|
16370
16446
|
const perItem = opts.choices.some((choice) => choice.detail) ? 2 : 1;
|
|
16371
16447
|
const visibleItems = Math.max(1, Math.floor(Math.max(2, height - 5) / perItem));
|
|
16372
16448
|
const firstRow = 4;
|
|
16373
|
-
return new Promise((
|
|
16449
|
+
return new Promise((resolve8) => {
|
|
16374
16450
|
const view = () => opts.choices.filter((choice) => matches(choice, filter));
|
|
16375
16451
|
const draw = () => {
|
|
16376
16452
|
const shown = view();
|
|
@@ -16418,7 +16494,7 @@ async function pick(opts) {
|
|
|
16418
16494
|
const hit = shown.findIndex((_, index) => y >= rowOfItem(index) && y < rowOfItem(index) + perItem);
|
|
16419
16495
|
if (hit >= 0) {
|
|
16420
16496
|
process.stdin.off("data", onData);
|
|
16421
|
-
|
|
16497
|
+
resolve8(shown[hit].value);
|
|
16422
16498
|
return;
|
|
16423
16499
|
}
|
|
16424
16500
|
}
|
|
@@ -16426,13 +16502,13 @@ async function pick(opts) {
|
|
|
16426
16502
|
if (!sawMouse) {
|
|
16427
16503
|
if (input === KEY_ESC && !input.includes("[<") || input === KEY_CTRL_C2) {
|
|
16428
16504
|
process.stdin.off("data", onData);
|
|
16429
|
-
|
|
16505
|
+
resolve8(null);
|
|
16430
16506
|
return;
|
|
16431
16507
|
}
|
|
16432
16508
|
if (input === "\r") {
|
|
16433
16509
|
if (shown.length === 0) return;
|
|
16434
16510
|
process.stdin.off("data", onData);
|
|
16435
|
-
|
|
16511
|
+
resolve8(shown[selected].value);
|
|
16436
16512
|
return;
|
|
16437
16513
|
}
|
|
16438
16514
|
const down = input === CSI2 + "B" || opts.menu && (input === "j" || input === CSI2 + "C");
|
|
@@ -16456,7 +16532,7 @@ async function readLine(opts) {
|
|
|
16456
16532
|
let value = "";
|
|
16457
16533
|
let error = "";
|
|
16458
16534
|
const width = Math.max(20, Number(process.stdout.columns || 80));
|
|
16459
|
-
return new Promise((
|
|
16535
|
+
return new Promise((resolve8) => {
|
|
16460
16536
|
const draw = () => {
|
|
16461
16537
|
const lines = [
|
|
16462
16538
|
" " + S.title + opts.title + S.reset,
|
|
@@ -16475,7 +16551,7 @@ async function readLine(opts) {
|
|
|
16475
16551
|
const input = chunk.toString("utf8");
|
|
16476
16552
|
if (input === KEY_ESC || input === KEY_CTRL_C2) {
|
|
16477
16553
|
process.stdin.off("data", onData);
|
|
16478
|
-
|
|
16554
|
+
resolve8(null);
|
|
16479
16555
|
return;
|
|
16480
16556
|
}
|
|
16481
16557
|
if (input === "\r") {
|
|
@@ -16487,7 +16563,7 @@ async function readLine(opts) {
|
|
|
16487
16563
|
return;
|
|
16488
16564
|
}
|
|
16489
16565
|
process.stdin.off("data", onData);
|
|
16490
|
-
|
|
16566
|
+
resolve8(value.trim());
|
|
16491
16567
|
})();
|
|
16492
16568
|
return;
|
|
16493
16569
|
}
|
|
@@ -16754,9 +16830,6 @@ async function runDialog(kind, repoCwd, argA = "", argB = "") {
|
|
|
16754
16830
|
label: (TAB_GLYPHS[harness] || "") + " " + (HARNESS_LABELS[harness] || harness)
|
|
16755
16831
|
}))
|
|
16756
16832
|
];
|
|
16757
|
-
if (harnesses.includes("cursor")) {
|
|
16758
|
-
sessions.push({ value: "cursor-synkro", label: TAB_GLYPHS.cursor + " Cursor in Synkro UX" });
|
|
16759
|
-
}
|
|
16760
16833
|
for (; ; ) {
|
|
16761
16834
|
const session = await pick({
|
|
16762
16835
|
menu: true,
|
|
@@ -16815,21 +16888,418 @@ var init_dialog = __esm({
|
|
|
16815
16888
|
}
|
|
16816
16889
|
});
|
|
16817
16890
|
|
|
16891
|
+
// cli/harness/render.ts
|
|
16892
|
+
function spinnerFrame(tick) {
|
|
16893
|
+
return SPINNER[Math.abs(tick) % SPINNER.length];
|
|
16894
|
+
}
|
|
16895
|
+
function terminalText(text) {
|
|
16896
|
+
return String(text).replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
|
|
16897
|
+
}
|
|
16898
|
+
function clip3(text, max) {
|
|
16899
|
+
const value = String(text || "").replace(/\s+/g, " ").trim();
|
|
16900
|
+
if (max <= 1) return value;
|
|
16901
|
+
return value.length <= max ? value : value.slice(0, max - 1) + "\u2026";
|
|
16902
|
+
}
|
|
16903
|
+
function seconds(ms) {
|
|
16904
|
+
const total = Math.max(0, Math.round(ms / 1e3));
|
|
16905
|
+
if (total < 60) return total + "s";
|
|
16906
|
+
return Math.floor(total / 60) + "m" + String(total % 60).padStart(2, "0") + "s";
|
|
16907
|
+
}
|
|
16908
|
+
function statusLine(opts) {
|
|
16909
|
+
const hint = opts.hint ? " \xB7 " + opts.hint : "";
|
|
16910
|
+
const body = opts.text + " (" + seconds(opts.elapsedMs) + hint + ")";
|
|
16911
|
+
const width = Math.max(20, (opts.width || 100) - 4);
|
|
16912
|
+
return S2.think + " " + spinnerFrame(opts.tick) + " " + clip3(body, width) + S2.reset;
|
|
16913
|
+
}
|
|
16914
|
+
function renderEvent(event, width = 100) {
|
|
16915
|
+
const body = Math.max(30, width - 4);
|
|
16916
|
+
switch (event.type) {
|
|
16917
|
+
// The workspace and both accounts are already in the session header, so
|
|
16918
|
+
// this line carries only what the header could not know before the harness
|
|
16919
|
+
// started: which model answered, and whether a subscription or a key paid.
|
|
16920
|
+
case "session-start":
|
|
16921
|
+
return [
|
|
16922
|
+
"",
|
|
16923
|
+
S2.dim + " " + clip3(event.model, body - 20) + (event.authSource === "login" ? " \xB7 subscription" : " \xB7 api key") + S2.reset,
|
|
16924
|
+
""
|
|
16925
|
+
];
|
|
16926
|
+
case "user-message":
|
|
16927
|
+
return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset, ""];
|
|
16928
|
+
// Live state, not transcript. The runner promotes these to the status line.
|
|
16929
|
+
case "thinking":
|
|
16930
|
+
return [];
|
|
16931
|
+
case "assistant-message": {
|
|
16932
|
+
const lines = wrap(event.text, body - 2);
|
|
16933
|
+
return [
|
|
16934
|
+
"",
|
|
16935
|
+
...lines.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset),
|
|
16936
|
+
""
|
|
16937
|
+
];
|
|
16938
|
+
}
|
|
16939
|
+
case "tool-start": {
|
|
16940
|
+
const label = TOOL_LABEL[event.kind] || TOOL_LABEL.other;
|
|
16941
|
+
return [
|
|
16942
|
+
S2.tool + " " + BULLET + " " + label + S2.reset + S2.dim + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
|
|
16943
|
+
];
|
|
16944
|
+
}
|
|
16945
|
+
case "tool-end": {
|
|
16946
|
+
if (event.blocked) {
|
|
16947
|
+
return [
|
|
16948
|
+
S2.blocked + " " + BULLET + " Blocked" + S2.reset + S2.dim + " " + clip3(terminalText(event.target), body - 14) + S2.reset,
|
|
16949
|
+
...wrap(terminalText(event.reason), body - 6).map((line, index) => index === 0 ? S2.blocked + " " + ELBOW + " " + S2.reset + S2.rule + line + S2.reset : S2.rule + " " + line + S2.reset)
|
|
16950
|
+
];
|
|
16951
|
+
}
|
|
16952
|
+
const detail = event.ok ? event.output.trim() ? clip3(terminalText(event.output), body - 10) : "done" : "failed" + (event.exitCode === null ? "" : " (exit " + event.exitCode + ")");
|
|
16953
|
+
const tint = event.ok ? S2.ok : S2.blocked;
|
|
16954
|
+
return [tint + " " + ELBOW + " " + S2.reset + S2.dim + detail + S2.reset];
|
|
16955
|
+
}
|
|
16956
|
+
// A clean finish needs no announcement: the prompt returning IS the signal.
|
|
16957
|
+
case "turn-end":
|
|
16958
|
+
return event.ok ? [] : ["", S2.blocked + " " + BULLET + " turn ended with an error" + S2.reset, ""];
|
|
16959
|
+
case "notice":
|
|
16960
|
+
return [S2.dim + " " + clip3(event.text, body) + S2.reset];
|
|
16961
|
+
default:
|
|
16962
|
+
return [];
|
|
16963
|
+
}
|
|
16964
|
+
}
|
|
16965
|
+
function wrap(text, width) {
|
|
16966
|
+
const words = String(text || "").replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
|
|
16967
|
+
if (words.length === 0) return [];
|
|
16968
|
+
const lines = [];
|
|
16969
|
+
let line = "";
|
|
16970
|
+
for (const word of words) {
|
|
16971
|
+
if (!line) line = word;
|
|
16972
|
+
else if ((line + " " + word).length <= width) line += " " + word;
|
|
16973
|
+
else {
|
|
16974
|
+
lines.push(line);
|
|
16975
|
+
line = word;
|
|
16976
|
+
}
|
|
16977
|
+
}
|
|
16978
|
+
if (line) lines.push(line);
|
|
16979
|
+
return lines;
|
|
16980
|
+
}
|
|
16981
|
+
var ESC2, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
|
|
16982
|
+
var init_render2 = __esm({
|
|
16983
|
+
"cli/harness/render.ts"() {
|
|
16984
|
+
"use strict";
|
|
16985
|
+
ESC2 = "\x1B[";
|
|
16986
|
+
S2 = {
|
|
16987
|
+
reset: ESC2 + "0m",
|
|
16988
|
+
dim: ESC2 + "2m",
|
|
16989
|
+
bold: ESC2 + "1m",
|
|
16990
|
+
user: ESC2 + "38;5;111m",
|
|
16991
|
+
agent: ESC2 + "38;5;252m",
|
|
16992
|
+
think: ESC2 + "38;5;244m",
|
|
16993
|
+
tool: ESC2 + "38;5;180m",
|
|
16994
|
+
ok: ESC2 + "38;5;114m",
|
|
16995
|
+
blocked: ESC2 + "38;5;203m",
|
|
16996
|
+
rule: ESC2 + "38;5;211m"
|
|
16997
|
+
};
|
|
16998
|
+
CLEAR_LINE = "\r" + ESC2 + "2K";
|
|
16999
|
+
BULLET = "\u23FA";
|
|
17000
|
+
ELBOW = "\u23BF";
|
|
17001
|
+
SPINNER = ["\xB7", "\u2722", "\u2733", "\u2217", "\u273B", "\u273D"];
|
|
17002
|
+
TOOL_LABEL = {
|
|
17003
|
+
shell: "Shell",
|
|
17004
|
+
read: "Read",
|
|
17005
|
+
edit: "Edit",
|
|
17006
|
+
write: "Write",
|
|
17007
|
+
delete: "Delete",
|
|
17008
|
+
search: "Search",
|
|
17009
|
+
list: "List",
|
|
17010
|
+
todo: "Todo",
|
|
17011
|
+
other: "Tool"
|
|
17012
|
+
};
|
|
17013
|
+
}
|
|
17014
|
+
});
|
|
17015
|
+
|
|
17016
|
+
// cli/harness/composer.ts
|
|
17017
|
+
import { execFileSync as execFileSync5, spawnSync as spawnSync12 } from "child_process";
|
|
17018
|
+
import { existsSync as existsSync38, mkdtempSync as mkdtempSync2, rmSync as rmSync7, statSync as statSync5, writeFileSync as writeFileSync27 } from "fs";
|
|
17019
|
+
import { homedir as homedir38, tmpdir } from "os";
|
|
17020
|
+
import { basename as basename3, dirname as dirname12, extname, isAbsolute as isAbsolute2, join as join37, resolve as resolve5, sep as sep3 } from "path";
|
|
17021
|
+
import { createInterface as createInterface5 } from "readline";
|
|
17022
|
+
function markerCarryLength(input, marker) {
|
|
17023
|
+
for (let length = Math.min(input.length, marker.length - 1); length > 0; length -= 1) {
|
|
17024
|
+
if (marker.startsWith(input.slice(-length))) return length;
|
|
17025
|
+
}
|
|
17026
|
+
return 0;
|
|
17027
|
+
}
|
|
17028
|
+
function normalizePathToken(raw) {
|
|
17029
|
+
let value = raw.trim();
|
|
17030
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
17031
|
+
value = value.slice(1, -1);
|
|
17032
|
+
}
|
|
17033
|
+
value = value.replace(/\\([\\ "'()])/g, "$1");
|
|
17034
|
+
if (value === "~") return homedir38();
|
|
17035
|
+
if (value.startsWith("~/")) return join37(homedir38(), value.slice(2));
|
|
17036
|
+
return value;
|
|
17037
|
+
}
|
|
17038
|
+
function imagePathsInText(text, cwd) {
|
|
17039
|
+
const candidates = String(text || "").match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(?:\\.|[^\s])+/g) || [];
|
|
17040
|
+
const images = [];
|
|
17041
|
+
for (const candidate of candidates) {
|
|
17042
|
+
const token = normalizePathToken(candidate);
|
|
17043
|
+
if (!IMAGE_EXTENSIONS.has(extname(token).toLowerCase())) continue;
|
|
17044
|
+
const path = isAbsolute2(token) ? token : resolve5(cwd, token);
|
|
17045
|
+
try {
|
|
17046
|
+
if (statSync5(path).isFile() && !images.includes(path)) images.push(path);
|
|
17047
|
+
} catch {
|
|
17048
|
+
}
|
|
17049
|
+
}
|
|
17050
|
+
return images;
|
|
17051
|
+
}
|
|
17052
|
+
function codexUserInput(draft) {
|
|
17053
|
+
const input = [];
|
|
17054
|
+
if (draft.text.trim()) input.push({ type: "text", text: draft.text, text_elements: [] });
|
|
17055
|
+
for (const path of draft.images) input.push({ type: "localImage", path });
|
|
17056
|
+
return input;
|
|
17057
|
+
}
|
|
17058
|
+
function clipboardText() {
|
|
17059
|
+
try {
|
|
17060
|
+
if (process.platform === "darwin") return execFileSync5("pbpaste", [], { encoding: "utf8", timeout: 1500 });
|
|
17061
|
+
const wayland = spawnSync12("wl-paste", ["--no-newline"], { encoding: "utf8", timeout: 1500 });
|
|
17062
|
+
if (wayland.status === 0) return String(wayland.stdout || "");
|
|
17063
|
+
const x11 = spawnSync12("xclip", ["-selection", "clipboard", "-o"], { encoding: "utf8", timeout: 1500 });
|
|
17064
|
+
return x11.status === 0 ? String(x11.stdout || "") : "";
|
|
17065
|
+
} catch {
|
|
17066
|
+
return "";
|
|
17067
|
+
}
|
|
17068
|
+
}
|
|
17069
|
+
function macClipboardPng(target) {
|
|
17070
|
+
const script = [
|
|
17071
|
+
"on run argv",
|
|
17072
|
+
"set outputPath to item 1 of argv",
|
|
17073
|
+
"set imageData to the clipboard as \xABclass PNGf\xBB",
|
|
17074
|
+
"set outputFile to open for access POSIX file outputPath with write permission",
|
|
17075
|
+
"set eof outputFile to 0",
|
|
17076
|
+
"write imageData to outputFile",
|
|
17077
|
+
"close access outputFile",
|
|
17078
|
+
"end run"
|
|
17079
|
+
].join("\n");
|
|
17080
|
+
const result = spawnSync12("osascript", ["-e", script, target], { encoding: "utf8", timeout: 3e3 });
|
|
17081
|
+
return result.status === 0 && existsSync38(target);
|
|
17082
|
+
}
|
|
17083
|
+
function linuxClipboardPng(target) {
|
|
17084
|
+
const wayland = spawnSync12("wl-paste", ["--type", "image/png"], { encoding: null, timeout: 3e3 });
|
|
17085
|
+
if (wayland.status === 0 && Buffer.isBuffer(wayland.stdout) && wayland.stdout.length > 0) {
|
|
17086
|
+
writeFileSync27(target, wayland.stdout);
|
|
17087
|
+
return true;
|
|
17088
|
+
}
|
|
17089
|
+
const x11 = spawnSync12("xclip", ["-selection", "clipboard", "-t", "image/png", "-o"], { encoding: null, timeout: 3e3 });
|
|
17090
|
+
if (x11.status === 0 && Buffer.isBuffer(x11.stdout) && x11.stdout.length > 0) {
|
|
17091
|
+
writeFileSync27(target, x11.stdout);
|
|
17092
|
+
return true;
|
|
17093
|
+
}
|
|
17094
|
+
return false;
|
|
17095
|
+
}
|
|
17096
|
+
function clipboardImage() {
|
|
17097
|
+
const directory = mkdtempSync2(join37(tmpdir(), "synkro-paste-"));
|
|
17098
|
+
const target = join37(directory, "clipboard.png");
|
|
17099
|
+
try {
|
|
17100
|
+
const copied = process.platform === "darwin" ? macClipboardPng(target) : process.platform === "linux" && linuxClipboardPng(target);
|
|
17101
|
+
if (copied && statSync5(target).size > 0) return target;
|
|
17102
|
+
} catch {
|
|
17103
|
+
}
|
|
17104
|
+
rmSync7(directory, { recursive: true, force: true });
|
|
17105
|
+
return "";
|
|
17106
|
+
}
|
|
17107
|
+
function cleanupPromptDraft(draft) {
|
|
17108
|
+
for (const path of draft.temporaryImages) {
|
|
17109
|
+
const directory = dirname12(resolve5(path));
|
|
17110
|
+
const temporaryRoot = resolve5(tmpdir()) + sep3;
|
|
17111
|
+
if (!directory.startsWith(temporaryRoot) || !basename3(directory).startsWith("synkro-paste-")) continue;
|
|
17112
|
+
try {
|
|
17113
|
+
rmSync7(directory, { recursive: true, force: true });
|
|
17114
|
+
} catch {
|
|
17115
|
+
}
|
|
17116
|
+
}
|
|
17117
|
+
}
|
|
17118
|
+
function fallbackPrompt(cwd) {
|
|
17119
|
+
return new Promise((resolveDraft) => {
|
|
17120
|
+
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
17121
|
+
let settled = false;
|
|
17122
|
+
const finish = (value) => {
|
|
17123
|
+
if (settled) return;
|
|
17124
|
+
settled = true;
|
|
17125
|
+
rl.close();
|
|
17126
|
+
resolveDraft(value);
|
|
17127
|
+
};
|
|
17128
|
+
rl.once("close", () => finish(null));
|
|
17129
|
+
rl.once("SIGINT", () => finish(null));
|
|
17130
|
+
rl.question(S2.user + " \u276F " + S2.reset, (text) => {
|
|
17131
|
+
finish({ text, images: imagePathsInText(text, cwd), temporaryImages: [] });
|
|
17132
|
+
});
|
|
17133
|
+
});
|
|
17134
|
+
}
|
|
17135
|
+
function readPrompt(cwd) {
|
|
17136
|
+
const input = process.stdin;
|
|
17137
|
+
const output = process.stdout;
|
|
17138
|
+
if (!input.isTTY || !output.isTTY || !input.setRawMode) return fallbackPrompt(cwd);
|
|
17139
|
+
return new Promise((resolveDraft) => {
|
|
17140
|
+
let text = "";
|
|
17141
|
+
let cursor = 0;
|
|
17142
|
+
let stream = "";
|
|
17143
|
+
let pasting = false;
|
|
17144
|
+
let settled = false;
|
|
17145
|
+
const images = [];
|
|
17146
|
+
const temporaryImages = [];
|
|
17147
|
+
const wasRaw = Boolean(input.isRaw);
|
|
17148
|
+
const prompt = S2.user + " \u276F " + S2.reset;
|
|
17149
|
+
const redraw = () => {
|
|
17150
|
+
const safe = text.replace(/[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g, "").replace(/[\r\n]+/g, " / ");
|
|
17151
|
+
const attachmentLabel = images.length ? " [" + images.length + " image" + (images.length === 1 ? "" : "s") + "]" : "";
|
|
17152
|
+
const attachments = attachmentLabel ? S2.dim + attachmentLabel + S2.reset : "";
|
|
17153
|
+
output.write("\r\x1B[2K" + prompt + safe + attachments);
|
|
17154
|
+
const after = text.slice(cursor).replace(/[\r\n]+/g, " / ").length + attachmentLabel.length;
|
|
17155
|
+
if (after > 0) output.write("\x1B[" + after + "D");
|
|
17156
|
+
};
|
|
17157
|
+
const finish = (draft) => {
|
|
17158
|
+
if (settled) return;
|
|
17159
|
+
settled = true;
|
|
17160
|
+
input.off("data", onData);
|
|
17161
|
+
input.setRawMode?.(wasRaw);
|
|
17162
|
+
output.write("\x1B[?2004l\n");
|
|
17163
|
+
if (!draft) cleanupPromptDraft({ temporaryImages });
|
|
17164
|
+
resolveDraft(draft);
|
|
17165
|
+
};
|
|
17166
|
+
const insert = (value) => {
|
|
17167
|
+
text = text.slice(0, cursor) + value + text.slice(cursor);
|
|
17168
|
+
cursor += value.length;
|
|
17169
|
+
};
|
|
17170
|
+
const attachClipboard = () => {
|
|
17171
|
+
const image = clipboardImage();
|
|
17172
|
+
if (image) {
|
|
17173
|
+
images.push(image);
|
|
17174
|
+
temporaryImages.push(image);
|
|
17175
|
+
return;
|
|
17176
|
+
}
|
|
17177
|
+
insert(clipboardText());
|
|
17178
|
+
};
|
|
17179
|
+
const processStream = () => {
|
|
17180
|
+
while (stream && !settled) {
|
|
17181
|
+
if (pasting) {
|
|
17182
|
+
const end = stream.indexOf(PASTE_END);
|
|
17183
|
+
if (end < 0) {
|
|
17184
|
+
const keep = markerCarryLength(stream, PASTE_END);
|
|
17185
|
+
insert(stream.slice(0, stream.length - keep));
|
|
17186
|
+
stream = stream.slice(stream.length - keep);
|
|
17187
|
+
break;
|
|
17188
|
+
}
|
|
17189
|
+
insert(stream.slice(0, end));
|
|
17190
|
+
stream = stream.slice(end + PASTE_END.length);
|
|
17191
|
+
pasting = false;
|
|
17192
|
+
continue;
|
|
17193
|
+
}
|
|
17194
|
+
if (stream.startsWith(PASTE_START)) {
|
|
17195
|
+
stream = stream.slice(PASTE_START.length);
|
|
17196
|
+
pasting = true;
|
|
17197
|
+
continue;
|
|
17198
|
+
}
|
|
17199
|
+
if (PASTE_START.startsWith(stream)) break;
|
|
17200
|
+
if (stream.startsWith("\x1B[D")) {
|
|
17201
|
+
cursor = Math.max(0, cursor - 1);
|
|
17202
|
+
stream = stream.slice(3);
|
|
17203
|
+
continue;
|
|
17204
|
+
}
|
|
17205
|
+
if (stream.startsWith("\x1B[C")) {
|
|
17206
|
+
cursor = Math.min(text.length, cursor + 1);
|
|
17207
|
+
stream = stream.slice(3);
|
|
17208
|
+
continue;
|
|
17209
|
+
}
|
|
17210
|
+
if (stream.startsWith("\x1B[H")) {
|
|
17211
|
+
cursor = 0;
|
|
17212
|
+
stream = stream.slice(3);
|
|
17213
|
+
continue;
|
|
17214
|
+
}
|
|
17215
|
+
if (stream.startsWith("\x1B[F")) {
|
|
17216
|
+
cursor = text.length;
|
|
17217
|
+
stream = stream.slice(3);
|
|
17218
|
+
continue;
|
|
17219
|
+
}
|
|
17220
|
+
if (stream.startsWith("\x1B[A") || stream.startsWith("\x1B[B")) {
|
|
17221
|
+
stream = stream.slice(3);
|
|
17222
|
+
continue;
|
|
17223
|
+
}
|
|
17224
|
+
if (stream.startsWith("\x1B") && stream.length < 3) break;
|
|
17225
|
+
const char = stream[0];
|
|
17226
|
+
stream = stream.slice(1);
|
|
17227
|
+
if (char === "\r" || char === "\n") {
|
|
17228
|
+
const found = imagePathsInText(text, cwd);
|
|
17229
|
+
for (const path of found) if (!images.includes(path)) images.push(path);
|
|
17230
|
+
finish({ text, images, temporaryImages });
|
|
17231
|
+
} else if (char === "" || char === "" && !text && images.length === 0) {
|
|
17232
|
+
finish(null);
|
|
17233
|
+
} else if (char === "\x7F" || char === "\b") {
|
|
17234
|
+
if (cursor > 0) {
|
|
17235
|
+
text = text.slice(0, cursor - 1) + text.slice(cursor);
|
|
17236
|
+
cursor -= 1;
|
|
17237
|
+
}
|
|
17238
|
+
} else if (char === "") {
|
|
17239
|
+
text = text.slice(cursor);
|
|
17240
|
+
cursor = 0;
|
|
17241
|
+
} else if (char === "") {
|
|
17242
|
+
const before = text.slice(0, cursor).replace(/\s*\S+\s*$/, "");
|
|
17243
|
+
text = before + text.slice(cursor);
|
|
17244
|
+
cursor = before.length;
|
|
17245
|
+
} else if (char === "") {
|
|
17246
|
+
attachClipboard();
|
|
17247
|
+
} else if (char >= " " || char === " ") {
|
|
17248
|
+
insert(char);
|
|
17249
|
+
}
|
|
17250
|
+
}
|
|
17251
|
+
if (!settled) redraw();
|
|
17252
|
+
};
|
|
17253
|
+
let queue = Promise.resolve();
|
|
17254
|
+
const onData = (chunk) => {
|
|
17255
|
+
queue = queue.then(() => {
|
|
17256
|
+
stream += chunk.toString("utf8");
|
|
17257
|
+
processStream();
|
|
17258
|
+
});
|
|
17259
|
+
};
|
|
17260
|
+
input.setRawMode(true);
|
|
17261
|
+
input.resume();
|
|
17262
|
+
input.on("data", onData);
|
|
17263
|
+
output.write("\x1B[?2004h" + prompt);
|
|
17264
|
+
});
|
|
17265
|
+
}
|
|
17266
|
+
var PASTE_START, PASTE_END, IMAGE_EXTENSIONS;
|
|
17267
|
+
var init_composer = __esm({
|
|
17268
|
+
"cli/harness/composer.ts"() {
|
|
17269
|
+
"use strict";
|
|
17270
|
+
init_render2();
|
|
17271
|
+
PASTE_START = "\x1B[200~";
|
|
17272
|
+
PASTE_END = "\x1B[201~";
|
|
17273
|
+
IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
|
|
17274
|
+
}
|
|
17275
|
+
});
|
|
17276
|
+
|
|
16818
17277
|
// cli/harness/events.ts
|
|
17278
|
+
function fixPollId(raw) {
|
|
17279
|
+
const text = String(raw || "");
|
|
17280
|
+
return FIX_POLL_MARKER.exec(text)?.[1] || LEGACY_FIX_POLL_MARKER.exec(text)?.[1] || "";
|
|
17281
|
+
}
|
|
17282
|
+
function cleanGuardText(raw) {
|
|
17283
|
+
return String(raw || "").replace(FIX_POLL_MARKER, "").replace(/\n*\s*SYNKRO FIX POLL[\s\S]*$/i, "").replace(/\n?\d{4}-\d\d-\d\dT[^\n]*\sERROR\s+codex_core::tools::router:[^\n]*/gi, "").replace(/\s*Checking command\s*$/i, "").trim();
|
|
17284
|
+
}
|
|
16819
17285
|
function blockReason(raw) {
|
|
16820
|
-
const text =
|
|
17286
|
+
const text = cleanGuardText(raw);
|
|
16821
17287
|
if (!text) return "blocked by policy";
|
|
17288
|
+
const guardAt = text.lastIndexOf("Guard:");
|
|
17289
|
+
if (guardAt >= 0) return text.slice(guardAt + "Guard:".length).trim();
|
|
16822
17290
|
const afterTag = text.match(/\[synkro:[^\]]*\]\s*(.+)/is);
|
|
16823
17291
|
if (afterTag) return afterTag[1].trim();
|
|
16824
17292
|
const afterHook = text.match(/blocked by a hook:\s*(.+)/is);
|
|
16825
17293
|
if (afterHook) return afterHook[1].trim();
|
|
16826
17294
|
return text;
|
|
16827
17295
|
}
|
|
16828
|
-
var BLOCK_MARKER;
|
|
17296
|
+
var BLOCK_MARKER, FIX_POLL_MARKER, LEGACY_FIX_POLL_MARKER;
|
|
16829
17297
|
var init_events = __esm({
|
|
16830
17298
|
"cli/harness/events.ts"() {
|
|
16831
17299
|
"use strict";
|
|
16832
17300
|
BLOCK_MARKER = /blocked by a hook|\[synkro:/i;
|
|
17301
|
+
FIX_POLL_MARKER = /\[synkro:fix-poll\s+item_id=\\?["']?([A-Za-z0-9_-]+)\\?["']?\]/i;
|
|
17302
|
+
LEGACY_FIX_POLL_MARKER = /SYNKRO FIX POLL\s*\(item_id=([A-Za-z0-9_-]+)\)/i;
|
|
16833
17303
|
}
|
|
16834
17304
|
});
|
|
16835
17305
|
|
|
@@ -16910,6 +17380,7 @@ function parseCursorLine(line) {
|
|
|
16910
17380
|
ok: Boolean(success) && Number(success?.exitCode ?? 0) === 0,
|
|
16911
17381
|
blocked,
|
|
16912
17382
|
reason: rejected ? blocked ? blockReason(rawReason) : rawReason || "rejected" : "",
|
|
17383
|
+
pollId: blocked ? fixPollId(rawReason) : "",
|
|
16913
17384
|
exitCode: success ? Number(success.exitCode ?? 0) : null,
|
|
16914
17385
|
output: String(success?.stdout || success?.stderr || "")
|
|
16915
17386
|
}];
|
|
@@ -16981,128 +17452,6 @@ var init_cursor = __esm({
|
|
|
16981
17452
|
}
|
|
16982
17453
|
});
|
|
16983
17454
|
|
|
16984
|
-
// cli/harness/render.ts
|
|
16985
|
-
function spinnerFrame(tick) {
|
|
16986
|
-
return SPINNER[Math.abs(tick) % SPINNER.length];
|
|
16987
|
-
}
|
|
16988
|
-
function clip3(text, max) {
|
|
16989
|
-
const value = String(text || "").replace(/\s+/g, " ").trim();
|
|
16990
|
-
if (max <= 1) return value;
|
|
16991
|
-
return value.length <= max ? value : value.slice(0, max - 1) + "\u2026";
|
|
16992
|
-
}
|
|
16993
|
-
function seconds(ms) {
|
|
16994
|
-
const total = Math.max(0, Math.round(ms / 1e3));
|
|
16995
|
-
if (total < 60) return total + "s";
|
|
16996
|
-
return Math.floor(total / 60) + "m" + String(total % 60).padStart(2, "0") + "s";
|
|
16997
|
-
}
|
|
16998
|
-
function statusLine(opts) {
|
|
16999
|
-
const hint = opts.hint ? " \xB7 " + opts.hint : "";
|
|
17000
|
-
const body = opts.text + " (" + seconds(opts.elapsedMs) + hint + ")";
|
|
17001
|
-
const width = Math.max(20, (opts.width || 100) - 4);
|
|
17002
|
-
return S2.think + " " + spinnerFrame(opts.tick) + " " + clip3(body, width) + S2.reset;
|
|
17003
|
-
}
|
|
17004
|
-
function renderEvent(event, width = 100) {
|
|
17005
|
-
const body = Math.max(30, width - 4);
|
|
17006
|
-
switch (event.type) {
|
|
17007
|
-
// The workspace and both accounts are already in the session header, so
|
|
17008
|
-
// this line carries only what the header could not know before the harness
|
|
17009
|
-
// started: which model answered, and whether a subscription or a key paid.
|
|
17010
|
-
case "session-start":
|
|
17011
|
-
return [
|
|
17012
|
-
"",
|
|
17013
|
-
S2.dim + " " + clip3(event.model, body - 20) + (event.authSource === "login" ? " \xB7 subscription" : " \xB7 api key") + S2.reset,
|
|
17014
|
-
""
|
|
17015
|
-
];
|
|
17016
|
-
case "user-message":
|
|
17017
|
-
return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset, ""];
|
|
17018
|
-
// Live state, not transcript. The runner promotes these to the status line.
|
|
17019
|
-
case "thinking":
|
|
17020
|
-
return [];
|
|
17021
|
-
case "assistant-message": {
|
|
17022
|
-
const lines = wrap(event.text, body - 2);
|
|
17023
|
-
return [
|
|
17024
|
-
"",
|
|
17025
|
-
...lines.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset),
|
|
17026
|
-
""
|
|
17027
|
-
];
|
|
17028
|
-
}
|
|
17029
|
-
case "tool-start": {
|
|
17030
|
-
const label = TOOL_LABEL[event.kind] || TOOL_LABEL.other;
|
|
17031
|
-
return [
|
|
17032
|
-
S2.tool + " " + BULLET + " " + label + S2.reset + S2.dim + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
|
|
17033
|
-
];
|
|
17034
|
-
}
|
|
17035
|
-
case "tool-end": {
|
|
17036
|
-
if (event.blocked) {
|
|
17037
|
-
return [
|
|
17038
|
-
S2.blocked + " " + BULLET + " Blocked" + S2.reset + S2.dim + " " + clip3(event.target, body - 14) + S2.reset,
|
|
17039
|
-
...wrap(event.reason, body - 6).map((line, index) => index === 0 ? S2.blocked + " " + ELBOW + " " + S2.reset + S2.rule + line + S2.reset : S2.rule + " " + line + S2.reset)
|
|
17040
|
-
];
|
|
17041
|
-
}
|
|
17042
|
-
const detail = event.ok ? event.output.trim() ? clip3(event.output, body - 10) : "done" : "failed" + (event.exitCode === null ? "" : " (exit " + event.exitCode + ")");
|
|
17043
|
-
const tint = event.ok ? S2.ok : S2.blocked;
|
|
17044
|
-
return [tint + " " + ELBOW + " " + S2.reset + S2.dim + detail + S2.reset];
|
|
17045
|
-
}
|
|
17046
|
-
// A clean finish needs no announcement: the prompt returning IS the signal.
|
|
17047
|
-
case "turn-end":
|
|
17048
|
-
return event.ok ? [] : ["", S2.blocked + " " + BULLET + " turn ended with an error" + S2.reset, ""];
|
|
17049
|
-
case "notice":
|
|
17050
|
-
return [S2.dim + " " + clip3(event.text, body) + S2.reset];
|
|
17051
|
-
default:
|
|
17052
|
-
return [];
|
|
17053
|
-
}
|
|
17054
|
-
}
|
|
17055
|
-
function wrap(text, width) {
|
|
17056
|
-
const words = String(text || "").replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
|
|
17057
|
-
if (words.length === 0) return [];
|
|
17058
|
-
const lines = [];
|
|
17059
|
-
let line = "";
|
|
17060
|
-
for (const word of words) {
|
|
17061
|
-
if (!line) line = word;
|
|
17062
|
-
else if ((line + " " + word).length <= width) line += " " + word;
|
|
17063
|
-
else {
|
|
17064
|
-
lines.push(line);
|
|
17065
|
-
line = word;
|
|
17066
|
-
}
|
|
17067
|
-
}
|
|
17068
|
-
if (line) lines.push(line);
|
|
17069
|
-
return lines;
|
|
17070
|
-
}
|
|
17071
|
-
var ESC2, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
|
|
17072
|
-
var init_render2 = __esm({
|
|
17073
|
-
"cli/harness/render.ts"() {
|
|
17074
|
-
"use strict";
|
|
17075
|
-
ESC2 = "\x1B[";
|
|
17076
|
-
S2 = {
|
|
17077
|
-
reset: ESC2 + "0m",
|
|
17078
|
-
dim: ESC2 + "2m",
|
|
17079
|
-
bold: ESC2 + "1m",
|
|
17080
|
-
user: ESC2 + "38;5;111m",
|
|
17081
|
-
agent: ESC2 + "38;5;252m",
|
|
17082
|
-
think: ESC2 + "38;5;244m",
|
|
17083
|
-
tool: ESC2 + "38;5;180m",
|
|
17084
|
-
ok: ESC2 + "38;5;114m",
|
|
17085
|
-
blocked: ESC2 + "38;5;203m",
|
|
17086
|
-
rule: ESC2 + "38;5;211m"
|
|
17087
|
-
};
|
|
17088
|
-
CLEAR_LINE = "\r" + ESC2 + "2K";
|
|
17089
|
-
BULLET = "\u23FA";
|
|
17090
|
-
ELBOW = "\u23BF";
|
|
17091
|
-
SPINNER = ["\xB7", "\u2722", "\u2733", "\u2217", "\u273B", "\u273D"];
|
|
17092
|
-
TOOL_LABEL = {
|
|
17093
|
-
shell: "Shell",
|
|
17094
|
-
read: "Read",
|
|
17095
|
-
edit: "Edit",
|
|
17096
|
-
write: "Write",
|
|
17097
|
-
delete: "Delete",
|
|
17098
|
-
search: "Search",
|
|
17099
|
-
list: "List",
|
|
17100
|
-
todo: "Todo",
|
|
17101
|
-
other: "Tool"
|
|
17102
|
-
};
|
|
17103
|
-
}
|
|
17104
|
-
});
|
|
17105
|
-
|
|
17106
17455
|
// cli/harness/run.ts
|
|
17107
17456
|
import { spawn as spawn9 } from "child_process";
|
|
17108
17457
|
function replayKey(event) {
|
|
@@ -17131,6 +17480,7 @@ function createTurnSink(opts) {
|
|
|
17131
17480
|
let buffer = "";
|
|
17132
17481
|
let reconnecting = false;
|
|
17133
17482
|
let lastWasAnswer = false;
|
|
17483
|
+
const toolStartedAt = /* @__PURE__ */ new Map();
|
|
17134
17484
|
let statusText = "";
|
|
17135
17485
|
let statusKind = "progress";
|
|
17136
17486
|
let statusShown = false;
|
|
@@ -17233,7 +17583,17 @@ function createTurnSink(opts) {
|
|
|
17233
17583
|
opts.write(rendered.join("\n") + "\n");
|
|
17234
17584
|
drawStatus();
|
|
17235
17585
|
};
|
|
17236
|
-
const take = (
|
|
17586
|
+
const take = (incoming) => {
|
|
17587
|
+
let event = incoming;
|
|
17588
|
+
if (event.type === "tool-start" && event.id && !toolStartedAt.has(event.id)) {
|
|
17589
|
+
toolStartedAt.set(event.id, now());
|
|
17590
|
+
} else if (event.type === "tool-end") {
|
|
17591
|
+
const started = toolStartedAt.get(event.id);
|
|
17592
|
+
if ((event.durationMs === null || event.durationMs === void 0) && started !== void 0) {
|
|
17593
|
+
event = { ...event, durationMs: Math.max(0, now() - started) };
|
|
17594
|
+
}
|
|
17595
|
+
toolStartedAt.delete(event.id);
|
|
17596
|
+
}
|
|
17237
17597
|
const isRetry = event.type === "notice" && event.kind === "retry";
|
|
17238
17598
|
if (!isRetry) disarmStall();
|
|
17239
17599
|
if (isRetry) {
|
|
@@ -17266,6 +17626,9 @@ function createTurnSink(opts) {
|
|
|
17266
17626
|
notice(text) {
|
|
17267
17627
|
emit2({ type: "notice", text });
|
|
17268
17628
|
},
|
|
17629
|
+
event(event) {
|
|
17630
|
+
take(event);
|
|
17631
|
+
},
|
|
17269
17632
|
finish(exit, interrupted = false) {
|
|
17270
17633
|
disarmStall();
|
|
17271
17634
|
stopStatus();
|
|
@@ -17299,7 +17662,7 @@ async function runCursorTurn(opts) {
|
|
|
17299
17662
|
showHeader: opts.showHeader,
|
|
17300
17663
|
onGiveUp: () => stopHarness()
|
|
17301
17664
|
});
|
|
17302
|
-
return new Promise((
|
|
17665
|
+
return new Promise((resolve8) => {
|
|
17303
17666
|
const child = spawn9("cursor-agent", cursorArgs(opts.prompt), {
|
|
17304
17667
|
cwd: opts.cwd,
|
|
17305
17668
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -17328,11 +17691,11 @@ async function runCursorTurn(opts) {
|
|
|
17328
17691
|
});
|
|
17329
17692
|
child.on("close", (code) => {
|
|
17330
17693
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
17331
|
-
|
|
17694
|
+
resolve8(sink.finish(code ?? 0, interrupted));
|
|
17332
17695
|
});
|
|
17333
17696
|
child.on("error", (error) => {
|
|
17334
17697
|
sink.notice("failed to start cursor-agent: " + String(error));
|
|
17335
|
-
|
|
17698
|
+
resolve8(sink.finish(1, interrupted));
|
|
17336
17699
|
});
|
|
17337
17700
|
});
|
|
17338
17701
|
}
|
|
@@ -17346,10 +17709,474 @@ var init_run = __esm({
|
|
|
17346
17709
|
}
|
|
17347
17710
|
});
|
|
17348
17711
|
|
|
17712
|
+
// cli/harness/codex.ts
|
|
17713
|
+
import { spawn as spawn10 } from "child_process";
|
|
17714
|
+
function itemTarget(item) {
|
|
17715
|
+
if (item?.type === "commandExecution") {
|
|
17716
|
+
return { kind: "shell", target: String(item.command || ""), description: "" };
|
|
17717
|
+
}
|
|
17718
|
+
if (item?.type === "fileChange") {
|
|
17719
|
+
const paths = Array.isArray(item.changes) ? item.changes.map((change) => String(change?.path || "")).filter(Boolean) : [];
|
|
17720
|
+
return { kind: "edit", target: paths.join(", ") || "files", description: "" };
|
|
17721
|
+
}
|
|
17722
|
+
if (item?.type === "mcpToolCall") {
|
|
17723
|
+
return { kind: "other", target: String(item.server || "") + "::" + String(item.tool || ""), description: "MCP tool" };
|
|
17724
|
+
}
|
|
17725
|
+
if (item?.type === "dynamicToolCall") {
|
|
17726
|
+
return { kind: "other", target: [item.namespace, item.tool].filter(Boolean).join("::"), description: "tool" };
|
|
17727
|
+
}
|
|
17728
|
+
return { kind: "other", target: String(item?.type || "tool"), description: "" };
|
|
17729
|
+
}
|
|
17730
|
+
function itemOutput(item) {
|
|
17731
|
+
if (item?.type === "commandExecution") return String(item.aggregatedOutput || "");
|
|
17732
|
+
if (item?.type === "mcpToolCall") return item.error ? String(item.error?.message || JSON.stringify(item.error)) : item.result ? JSON.stringify(item.result) : "";
|
|
17733
|
+
if (item?.type === "dynamicToolCall") return item.contentItems ? JSON.stringify(item.contentItems) : "";
|
|
17734
|
+
return "";
|
|
17735
|
+
}
|
|
17736
|
+
function parseCodexNotification(method, params, startedAt = /* @__PURE__ */ new Map(), approvalReasons = /* @__PURE__ */ new Map()) {
|
|
17737
|
+
if (method === "hook/started" || method === "hook/completed") {
|
|
17738
|
+
const run2 = params?.run || {};
|
|
17739
|
+
if (run2.eventName !== "preToolUse") return [];
|
|
17740
|
+
const asMs = (value) => {
|
|
17741
|
+
const number = Number(value || 0);
|
|
17742
|
+
return number > 0 && number < 1e11 ? number * 1e3 : number;
|
|
17743
|
+
};
|
|
17744
|
+
const started = asMs(run2.startedAt);
|
|
17745
|
+
const priorStart = Number(approvalReasons.get("__pretool_started__") || 0);
|
|
17746
|
+
if (started && (!priorStart || started < priorStart)) approvalReasons.set("__pretool_started__", String(started));
|
|
17747
|
+
if (method === "hook/completed") {
|
|
17748
|
+
const completed = asMs(run2.completedAt) || started + Number(run2.durationMs || 0);
|
|
17749
|
+
const priorCompleted = Number(approvalReasons.get("__pretool_completed__") || 0);
|
|
17750
|
+
if (completed > priorCompleted) approvalReasons.set("__pretool_completed__", String(completed));
|
|
17751
|
+
const entries = Array.isArray(run2.entries) ? run2.entries : [];
|
|
17752
|
+
const pollEntry = entries.find((entry) => fixPollId(String(entry?.text || "")));
|
|
17753
|
+
const blockingEntry = entries.find((entry) => entry?.kind === "stop" || entry?.kind === "error");
|
|
17754
|
+
const text = String(pollEntry?.text || blockingEntry?.text || run2.statusMessage || "");
|
|
17755
|
+
if (text && (run2.status === "blocked" || run2.status === "failed" || BLOCK_MARKER.test(text))) {
|
|
17756
|
+
approvalReasons.set("__latest_hook__", text);
|
|
17757
|
+
approvalReasons.set("__latest_hook_id__", String(run2.id || "synkro-policy-block"));
|
|
17758
|
+
approvalReasons.set("__latest_hook_duration__", String(Number(run2.durationMs || Math.max(0, completed - started))));
|
|
17759
|
+
}
|
|
17760
|
+
}
|
|
17761
|
+
return [];
|
|
17762
|
+
}
|
|
17763
|
+
if (method === "item/started") {
|
|
17764
|
+
const item = params?.item;
|
|
17765
|
+
if (!item?.id) return [];
|
|
17766
|
+
startedAt.set(String(item.id), Number(params.startedAtMs || Date.now()));
|
|
17767
|
+
if (!["commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall"].includes(item.type)) return [];
|
|
17768
|
+
return [{ type: "tool-start", id: String(item.id), ...itemTarget(item) }];
|
|
17769
|
+
}
|
|
17770
|
+
if (method === "item/completed") {
|
|
17771
|
+
const item = params?.item;
|
|
17772
|
+
if (!item?.id) return [];
|
|
17773
|
+
if (item.type === "hookPrompt") {
|
|
17774
|
+
const text = Array.isArray(item.fragments) ? item.fragments.map((fragment) => String(fragment?.text || "")).filter(Boolean).join("\n") : "";
|
|
17775
|
+
if (text) {
|
|
17776
|
+
const start2 = startedAt.get(String(item.id));
|
|
17777
|
+
const completed2 = Number(params.completedAtMs || Date.now());
|
|
17778
|
+
approvalReasons.set("__latest_hook__", text);
|
|
17779
|
+
approvalReasons.set("__latest_hook_id__", String(item.id));
|
|
17780
|
+
approvalReasons.set("__latest_hook_duration__", String(start2 === void 0 ? 0 : Math.max(0, completed2 - start2)));
|
|
17781
|
+
}
|
|
17782
|
+
startedAt.delete(String(item.id));
|
|
17783
|
+
return [];
|
|
17784
|
+
}
|
|
17785
|
+
if (item.type === "agentMessage") {
|
|
17786
|
+
return item.text ? [{ type: "assistant-message", text: String(item.text) }] : [];
|
|
17787
|
+
}
|
|
17788
|
+
if (item.type === "reasoning") {
|
|
17789
|
+
const text = [...item.summary || [], ...item.content || []].join(" ").trim();
|
|
17790
|
+
return text ? [{ type: "thinking", text }] : [];
|
|
17791
|
+
}
|
|
17792
|
+
if (!["commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall"].includes(item.type)) return [];
|
|
17793
|
+
const output = itemOutput(item);
|
|
17794
|
+
const approvalReason = approvalReasons.get(String(item.id)) || "";
|
|
17795
|
+
const hookReason = approvalReasons.get("__latest_hook__") || "";
|
|
17796
|
+
const rawReason = [approvalReason, hookReason, output].filter(Boolean).join("\n");
|
|
17797
|
+
const status = String(item.status || "");
|
|
17798
|
+
const blocked = BLOCK_MARKER.test(rawReason) || /blocked by PreToolUse hook/i.test(rawReason) || Boolean(hookReason) && (status === "failed" || status === "declined");
|
|
17799
|
+
const itemStart = startedAt.get(String(item.id));
|
|
17800
|
+
const hookStart = Number(approvalReasons.get("__pretool_started__") || 0);
|
|
17801
|
+
const start = itemStart === void 0 ? hookStart || void 0 : hookStart ? Math.min(itemStart, hookStart) : itemStart;
|
|
17802
|
+
const completed = Number(params.completedAtMs || Date.now());
|
|
17803
|
+
const durationMs = start === void 0 ? item.durationMs ?? null : Math.max(0, completed - start);
|
|
17804
|
+
startedAt.delete(String(item.id));
|
|
17805
|
+
approvalReasons.delete(String(item.id));
|
|
17806
|
+
approvalReasons.delete("__latest_hook__");
|
|
17807
|
+
approvalReasons.delete("__latest_hook_id__");
|
|
17808
|
+
approvalReasons.delete("__latest_hook_duration__");
|
|
17809
|
+
approvalReasons.delete("__pretool_started__");
|
|
17810
|
+
approvalReasons.delete("__pretool_completed__");
|
|
17811
|
+
return [{
|
|
17812
|
+
type: "tool-end",
|
|
17813
|
+
id: String(item.id),
|
|
17814
|
+
...itemTarget(item),
|
|
17815
|
+
ok: status === "completed" && Number(item.exitCode ?? 0) === 0,
|
|
17816
|
+
blocked,
|
|
17817
|
+
reason: blocked ? blockReason(rawReason) : "",
|
|
17818
|
+
pollId: blocked ? fixPollId(rawReason) : "",
|
|
17819
|
+
exitCode: item.exitCode === null || item.exitCode === void 0 ? null : Number(item.exitCode),
|
|
17820
|
+
output: blocked ? "" : output,
|
|
17821
|
+
durationMs
|
|
17822
|
+
}];
|
|
17823
|
+
}
|
|
17824
|
+
if (method === "turn/completed") {
|
|
17825
|
+
const status = String(params?.turn?.status || "failed");
|
|
17826
|
+
const hookReason = approvalReasons.get("__latest_hook__") || "";
|
|
17827
|
+
const events = [];
|
|
17828
|
+
if (hookReason) {
|
|
17829
|
+
events.push({
|
|
17830
|
+
type: "tool-end",
|
|
17831
|
+
id: approvalReasons.get("__latest_hook_id__") || "synkro-policy-block",
|
|
17832
|
+
kind: "other",
|
|
17833
|
+
target: "policy check",
|
|
17834
|
+
ok: false,
|
|
17835
|
+
blocked: true,
|
|
17836
|
+
reason: blockReason(hookReason),
|
|
17837
|
+
pollId: fixPollId(hookReason),
|
|
17838
|
+
exitCode: null,
|
|
17839
|
+
output: "",
|
|
17840
|
+
durationMs: Number(approvalReasons.get("__latest_hook_duration__") || 0)
|
|
17841
|
+
});
|
|
17842
|
+
approvalReasons.delete("__latest_hook__");
|
|
17843
|
+
approvalReasons.delete("__latest_hook_id__");
|
|
17844
|
+
approvalReasons.delete("__latest_hook_duration__");
|
|
17845
|
+
}
|
|
17846
|
+
approvalReasons.delete("__pretool_started__");
|
|
17847
|
+
approvalReasons.delete("__pretool_completed__");
|
|
17848
|
+
events.push({ type: "turn-end", ok: status === "completed", text: String(params?.turn?.error?.message || "") });
|
|
17849
|
+
return events;
|
|
17850
|
+
}
|
|
17851
|
+
return [];
|
|
17852
|
+
}
|
|
17853
|
+
function codexStderrNotice(raw, initialized) {
|
|
17854
|
+
const text = String(raw || "").trim();
|
|
17855
|
+
if (!text || initialized) return "";
|
|
17856
|
+
return /fatal|panic|authentication failed|not logged in/i.test(text) ? text.split("\n")[0].slice(0, 300) : "";
|
|
17857
|
+
}
|
|
17858
|
+
async function askApproval(text) {
|
|
17859
|
+
const input = process.stdin;
|
|
17860
|
+
const output = process.stdout;
|
|
17861
|
+
if (!input.isTTY || !output.isTTY) return false;
|
|
17862
|
+
output.write("\n" + S2.rule + " Synkro approval required" + S2.reset + "\n " + text.replace(/[\u0000-\u001F\u007F-\u009F]/g, " ").slice(0, 600) + "\n");
|
|
17863
|
+
output.write(S2.dim + " Press y to allow once; any other key denies." + S2.reset + "\n");
|
|
17864
|
+
return new Promise((resolve8) => {
|
|
17865
|
+
const wasRaw = Boolean(input.isRaw);
|
|
17866
|
+
const onData = (chunk) => {
|
|
17867
|
+
input.off("data", onData);
|
|
17868
|
+
if (input.setRawMode) input.setRawMode(wasRaw);
|
|
17869
|
+
resolve8(chunk.toString("utf8").toLowerCase() === "y");
|
|
17870
|
+
};
|
|
17871
|
+
if (input.setRawMode) input.setRawMode(true);
|
|
17872
|
+
input.resume();
|
|
17873
|
+
input.on("data", onData);
|
|
17874
|
+
});
|
|
17875
|
+
}
|
|
17876
|
+
async function runCodexTurn(opts) {
|
|
17877
|
+
const session = new CodexAppSession();
|
|
17878
|
+
try {
|
|
17879
|
+
return await session.turn(opts);
|
|
17880
|
+
} finally {
|
|
17881
|
+
session.close();
|
|
17882
|
+
}
|
|
17883
|
+
}
|
|
17884
|
+
var CodexAppSession;
|
|
17885
|
+
var init_codex = __esm({
|
|
17886
|
+
"cli/harness/codex.ts"() {
|
|
17887
|
+
"use strict";
|
|
17888
|
+
init_composer();
|
|
17889
|
+
init_events();
|
|
17890
|
+
init_run();
|
|
17891
|
+
init_render2();
|
|
17892
|
+
CodexAppSession = class {
|
|
17893
|
+
child = null;
|
|
17894
|
+
buffer = "";
|
|
17895
|
+
nextId = 1;
|
|
17896
|
+
pending = /* @__PURE__ */ new Map();
|
|
17897
|
+
initialized = false;
|
|
17898
|
+
threadId = "";
|
|
17899
|
+
model = "Codex";
|
|
17900
|
+
cwd = "";
|
|
17901
|
+
active = null;
|
|
17902
|
+
send(message) {
|
|
17903
|
+
this.child?.stdin.write(JSON.stringify({ jsonrpc: "2.0", ...message }) + "\n");
|
|
17904
|
+
}
|
|
17905
|
+
request(method, params) {
|
|
17906
|
+
const id = this.nextId++;
|
|
17907
|
+
return new Promise((resolve8, reject) => {
|
|
17908
|
+
this.pending.set(id, { resolve: resolve8, reject });
|
|
17909
|
+
this.send({ id, method, params });
|
|
17910
|
+
});
|
|
17911
|
+
}
|
|
17912
|
+
respond(id, result) {
|
|
17913
|
+
this.send({ id, result });
|
|
17914
|
+
}
|
|
17915
|
+
async handleServerRequest(message) {
|
|
17916
|
+
const method = String(message.method || "");
|
|
17917
|
+
const params = message.params || {};
|
|
17918
|
+
if (params.itemId && params.reason) this.active?.approvalReasons.set(String(params.itemId), String(params.reason));
|
|
17919
|
+
if (method === "item/commandExecution/requestApproval" || method === "item/fileChange/requestApproval") {
|
|
17920
|
+
const summary = String(params.reason || params.command || params.grantRoot || "Allow this action?");
|
|
17921
|
+
const accepted = await askApproval(summary);
|
|
17922
|
+
this.respond(message.id, { decision: accepted ? "accept" : "decline" });
|
|
17923
|
+
return;
|
|
17924
|
+
}
|
|
17925
|
+
if (method === "item/tool/requestUserInput") {
|
|
17926
|
+
const answers = {};
|
|
17927
|
+
for (const question of params.questions || []) answers[String(question.id)] = { answers: [] };
|
|
17928
|
+
this.respond(message.id, { answers });
|
|
17929
|
+
return;
|
|
17930
|
+
}
|
|
17931
|
+
if (method === "mcpServer/elicitation/request") {
|
|
17932
|
+
this.respond(message.id, { action: "decline" });
|
|
17933
|
+
return;
|
|
17934
|
+
}
|
|
17935
|
+
this.respond(message.id, {});
|
|
17936
|
+
}
|
|
17937
|
+
handleLine(line) {
|
|
17938
|
+
let message;
|
|
17939
|
+
try {
|
|
17940
|
+
message = JSON.parse(line);
|
|
17941
|
+
} catch {
|
|
17942
|
+
return;
|
|
17943
|
+
}
|
|
17944
|
+
if (message.method && message.id !== void 0) {
|
|
17945
|
+
void this.handleServerRequest(message);
|
|
17946
|
+
return;
|
|
17947
|
+
}
|
|
17948
|
+
if (message.method) {
|
|
17949
|
+
const active = this.active;
|
|
17950
|
+
if (!active) return;
|
|
17951
|
+
const events = parseCodexNotification(message.method, message.params, active.startedAt, active.approvalReasons);
|
|
17952
|
+
for (const event of events) active.sink.event(event);
|
|
17953
|
+
if (message.method === "turn/started") active.turnId = String(message.params?.turn?.id || "");
|
|
17954
|
+
if (message.method === "turn/completed") {
|
|
17955
|
+
const result = active.sink.finish(message.params?.turn?.status === "completed" ? 0 : 1, active.interrupted);
|
|
17956
|
+
this.active = null;
|
|
17957
|
+
active.resolve(result);
|
|
17958
|
+
}
|
|
17959
|
+
return;
|
|
17960
|
+
}
|
|
17961
|
+
if (message.id !== void 0) {
|
|
17962
|
+
const pending = this.pending.get(Number(message.id));
|
|
17963
|
+
if (!pending) return;
|
|
17964
|
+
this.pending.delete(Number(message.id));
|
|
17965
|
+
if (message.error) pending.reject(new Error(String(message.error?.message || JSON.stringify(message.error))));
|
|
17966
|
+
else pending.resolve(message.result);
|
|
17967
|
+
}
|
|
17968
|
+
}
|
|
17969
|
+
async start(cwd) {
|
|
17970
|
+
if (this.child) return;
|
|
17971
|
+
this.cwd = cwd;
|
|
17972
|
+
this.child = spawn10("codex", ["app-server"], { cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
17973
|
+
this.child.stdout.on("data", (chunk) => {
|
|
17974
|
+
this.buffer += chunk.toString("utf8");
|
|
17975
|
+
let newline = this.buffer.indexOf("\n");
|
|
17976
|
+
while (newline !== -1) {
|
|
17977
|
+
const line = this.buffer.slice(0, newline).trim();
|
|
17978
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
17979
|
+
if (line) this.handleLine(line);
|
|
17980
|
+
newline = this.buffer.indexOf("\n");
|
|
17981
|
+
}
|
|
17982
|
+
});
|
|
17983
|
+
this.child.stderr.on("data", (chunk) => {
|
|
17984
|
+
const note = codexStderrNotice(chunk.toString("utf8"), this.initialized);
|
|
17985
|
+
if (note) this.active?.sink.notice(note);
|
|
17986
|
+
});
|
|
17987
|
+
this.child.on("close", (code) => {
|
|
17988
|
+
const active = this.active;
|
|
17989
|
+
if (active) {
|
|
17990
|
+
this.active = null;
|
|
17991
|
+
active.resolve(active.sink.finish(code ?? 1, active.interrupted));
|
|
17992
|
+
}
|
|
17993
|
+
for (const pending of this.pending.values()) pending.reject(new Error("codex app-server exited"));
|
|
17994
|
+
this.pending.clear();
|
|
17995
|
+
this.child = null;
|
|
17996
|
+
});
|
|
17997
|
+
this.child.on("error", (error) => {
|
|
17998
|
+
const failure = new Error("failed to start codex app-server: " + String(error));
|
|
17999
|
+
const active = this.active;
|
|
18000
|
+
if (active) {
|
|
18001
|
+
active.sink.notice(failure.message);
|
|
18002
|
+
this.active = null;
|
|
18003
|
+
active.resolve(active.sink.finish(1, active.interrupted));
|
|
18004
|
+
}
|
|
18005
|
+
for (const pending of this.pending.values()) pending.reject(failure);
|
|
18006
|
+
this.pending.clear();
|
|
18007
|
+
});
|
|
18008
|
+
const initialized = await this.request("initialize", { clientInfo: { name: "synkro-governed", version: "1.0.0" } });
|
|
18009
|
+
this.initialized = true;
|
|
18010
|
+
this.send({ method: "initialized", params: {} });
|
|
18011
|
+
const started = await this.request("thread/start", {
|
|
18012
|
+
cwd,
|
|
18013
|
+
approvalPolicy: "on-request",
|
|
18014
|
+
approvalsReviewer: "user",
|
|
18015
|
+
sandbox: "workspace-write",
|
|
18016
|
+
ephemeral: false
|
|
18017
|
+
});
|
|
18018
|
+
this.threadId = String(started?.thread?.id || "");
|
|
18019
|
+
this.model = String(started?.model || initialized?.userAgent || "Codex");
|
|
18020
|
+
if (!this.threadId) throw new Error("codex app-server did not return a thread id");
|
|
18021
|
+
}
|
|
18022
|
+
async turn(opts) {
|
|
18023
|
+
await this.start(opts.cwd);
|
|
18024
|
+
if (this.active) throw new Error("a Codex turn is already running");
|
|
18025
|
+
const sink = createTurnSink({
|
|
18026
|
+
write: opts.write || ((text) => process.stdout.write(text)),
|
|
18027
|
+
onEvent: opts.onEvent,
|
|
18028
|
+
width: opts.width || Number(process.stdout.columns || 100),
|
|
18029
|
+
animate: false,
|
|
18030
|
+
showPrompt: opts.showPrompt,
|
|
18031
|
+
showHeader: opts.showHeader
|
|
18032
|
+
});
|
|
18033
|
+
sink.event({ type: "session-start", sessionId: this.threadId, model: this.model, cwd: this.cwd, authSource: "login" });
|
|
18034
|
+
sink.event({
|
|
18035
|
+
type: "user-message",
|
|
18036
|
+
text: opts.prompt || (opts.images?.length ? "[" + opts.images.length + " image attached]" : "")
|
|
18037
|
+
});
|
|
18038
|
+
const result = new Promise((resolve8) => {
|
|
18039
|
+
this.active = { sink, resolve: resolve8, startedAt: /* @__PURE__ */ new Map(), approvalReasons: /* @__PURE__ */ new Map(), turnId: "", interrupted: false };
|
|
18040
|
+
});
|
|
18041
|
+
const onAbort = () => {
|
|
18042
|
+
if (!this.active) return;
|
|
18043
|
+
this.active.interrupted = true;
|
|
18044
|
+
if (this.active.turnId) void this.request("turn/interrupt", { threadId: this.threadId, turnId: this.active.turnId }).catch(() => {
|
|
18045
|
+
});
|
|
18046
|
+
};
|
|
18047
|
+
if (opts.signal?.aborted) onAbort();
|
|
18048
|
+
else opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
18049
|
+
try {
|
|
18050
|
+
await this.request("turn/start", {
|
|
18051
|
+
threadId: this.threadId,
|
|
18052
|
+
cwd: opts.cwd,
|
|
18053
|
+
input: codexUserInput({ text: opts.prompt, images: opts.images || [] })
|
|
18054
|
+
});
|
|
18055
|
+
return await result;
|
|
18056
|
+
} finally {
|
|
18057
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
18058
|
+
}
|
|
18059
|
+
}
|
|
18060
|
+
close() {
|
|
18061
|
+
this.child?.kill();
|
|
18062
|
+
this.child = null;
|
|
18063
|
+
}
|
|
18064
|
+
};
|
|
18065
|
+
}
|
|
18066
|
+
});
|
|
18067
|
+
|
|
18068
|
+
// cli/harness/fixPoll.ts
|
|
18069
|
+
function pollBaseUrl() {
|
|
18070
|
+
const port = String(process.env.SYNKRO_MCP_PORT || "18931");
|
|
18071
|
+
return "http://127.0.0.1:" + port + "/api/local/fix-poll";
|
|
18072
|
+
}
|
|
18073
|
+
function neutralizeTerminalControls(value) {
|
|
18074
|
+
return value.replace(/[\u0000-\u0008\u000B-\u001F\u007F-\u009F\u001B]/g, "");
|
|
18075
|
+
}
|
|
18076
|
+
async function loadFixPoll(itemId, fetchImpl = fetch) {
|
|
18077
|
+
const response = await fetchImpl(pollBaseUrl() + "?item_id=" + encodeURIComponent(itemId));
|
|
18078
|
+
if (!response.ok) return null;
|
|
18079
|
+
const body = await response.json();
|
|
18080
|
+
const candidates = Array.isArray(body.candidates) ? body.candidates.filter((candidate) => typeof candidate === "string").slice(0, 8) : [];
|
|
18081
|
+
if (!candidates.length || body.status !== "pending") return null;
|
|
18082
|
+
return {
|
|
18083
|
+
itemId,
|
|
18084
|
+
filePath: String(body.file_path || ""),
|
|
18085
|
+
ruleId: String(body.rule_id || ""),
|
|
18086
|
+
candidates
|
|
18087
|
+
};
|
|
18088
|
+
}
|
|
18089
|
+
async function recordFixPoll(itemId, chosenIdx, fetchImpl = fetch) {
|
|
18090
|
+
const response = await fetchImpl(pollBaseUrl() + "/record", {
|
|
18091
|
+
method: "POST",
|
|
18092
|
+
headers: { "content-type": "application/json" },
|
|
18093
|
+
body: JSON.stringify({ item_id: itemId, chosen_idx: chosenIdx })
|
|
18094
|
+
});
|
|
18095
|
+
return response.ok;
|
|
18096
|
+
}
|
|
18097
|
+
function fixPollLines(poll, selected, width = 100) {
|
|
18098
|
+
const max = Math.max(32, width - 10);
|
|
18099
|
+
const choices = [...poll.candidates, "None of the above"];
|
|
18100
|
+
const scope = [poll.ruleId, poll.filePath].filter(Boolean).join(" \xB7 ");
|
|
18101
|
+
const lines = [
|
|
18102
|
+
"",
|
|
18103
|
+
S2.bold + S2.rule + " Synkro needs your decision" + S2.reset,
|
|
18104
|
+
...scope ? [S2.dim + " " + neutralizeTerminalControls(scope) + S2.reset] : [],
|
|
18105
|
+
""
|
|
18106
|
+
];
|
|
18107
|
+
choices.forEach((choice, index) => {
|
|
18108
|
+
const prefix = index === selected ? S2.user + " \u276F " : S2.dim + " ";
|
|
18109
|
+
const text = wrap(index + 1 + ". " + neutralizeTerminalControls(choice), max);
|
|
18110
|
+
lines.push(prefix + (text[0] || "") + S2.reset);
|
|
18111
|
+
for (const continuation of text.slice(1)) lines.push(" " + continuation);
|
|
18112
|
+
});
|
|
18113
|
+
lines.push("", S2.dim + " \u2191/\u2193 select \xB7 Enter confirm \xB7 1-" + choices.length + " choose" + S2.reset);
|
|
18114
|
+
return lines;
|
|
18115
|
+
}
|
|
18116
|
+
async function pickFixPoll(poll, io = {}) {
|
|
18117
|
+
const input = io.input || process.stdin;
|
|
18118
|
+
const output = io.output || process.stdout;
|
|
18119
|
+
if (!input.isTTY || !output.isTTY) return -1;
|
|
18120
|
+
let selected = 0;
|
|
18121
|
+
let rendered = 0;
|
|
18122
|
+
const draw = () => {
|
|
18123
|
+
if (rendered) output.write("\x1B[" + rendered + "A\x1B[J");
|
|
18124
|
+
const lines = fixPollLines(poll, selected, Number(output.columns || 100));
|
|
18125
|
+
output.write(lines.join("\n") + "\n");
|
|
18126
|
+
rendered = lines.length;
|
|
18127
|
+
};
|
|
18128
|
+
const choice = await new Promise((resolve8) => {
|
|
18129
|
+
const choices = poll.candidates.length + 1;
|
|
18130
|
+
const wasRaw = Boolean(input.isRaw);
|
|
18131
|
+
const done = (index) => {
|
|
18132
|
+
input.off("data", onData);
|
|
18133
|
+
if (input.setRawMode) input.setRawMode(wasRaw);
|
|
18134
|
+
resolve8(index === poll.candidates.length ? -1 : index);
|
|
18135
|
+
};
|
|
18136
|
+
const onData = (chunk) => {
|
|
18137
|
+
const key = chunk.toString("utf8");
|
|
18138
|
+
if (key === "") return done(poll.candidates.length);
|
|
18139
|
+
if (key === "\x1B" || key.toLowerCase() === "n") return done(poll.candidates.length);
|
|
18140
|
+
if (key === "\r" || key === "\n") return done(selected);
|
|
18141
|
+
if (key === "\x1B[A" || key === "k") selected = (selected - 1 + choices) % choices;
|
|
18142
|
+
else if (key === "\x1B[B" || key === "j") selected = (selected + 1) % choices;
|
|
18143
|
+
else if (/^[1-9]$/.test(key)) {
|
|
18144
|
+
const index = Number(key) - 1;
|
|
18145
|
+
if (index < choices) return done(index);
|
|
18146
|
+
} else return;
|
|
18147
|
+
draw();
|
|
18148
|
+
};
|
|
18149
|
+
if (input.setRawMode) input.setRawMode(true);
|
|
18150
|
+
input.resume();
|
|
18151
|
+
input.on("data", onData);
|
|
18152
|
+
draw();
|
|
18153
|
+
});
|
|
18154
|
+
return choice;
|
|
18155
|
+
}
|
|
18156
|
+
async function resolveFixPolls(events, io = {}) {
|
|
18157
|
+
const ids = Array.from(new Set(events.filter((event) => event.type === "tool-end").map((event) => event.pollId || "").filter(Boolean)));
|
|
18158
|
+
const recorded = [];
|
|
18159
|
+
for (const itemId of ids) {
|
|
18160
|
+
const poll = await loadFixPoll(itemId, io.fetchImpl).catch(() => null);
|
|
18161
|
+
if (!poll) continue;
|
|
18162
|
+
const chosen = await pickFixPoll(poll, io);
|
|
18163
|
+
const ok = await recordFixPoll(itemId, chosen, io.fetchImpl).catch(() => false);
|
|
18164
|
+
if (ok) recorded.push(chosen);
|
|
18165
|
+
else (io.output || process.stdout).write(S2.blocked + " Could not record that Synkro decision.\n" + S2.reset);
|
|
18166
|
+
}
|
|
18167
|
+
return recorded;
|
|
18168
|
+
}
|
|
18169
|
+
var init_fixPoll = __esm({
|
|
18170
|
+
"cli/harness/fixPoll.ts"() {
|
|
18171
|
+
"use strict";
|
|
18172
|
+
init_render2();
|
|
18173
|
+
}
|
|
18174
|
+
});
|
|
18175
|
+
|
|
17349
18176
|
// cli/harness/identity.ts
|
|
17350
|
-
import { execFileSync as
|
|
17351
|
-
import { homedir as
|
|
17352
|
-
function shortenPath(path, home =
|
|
18177
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
18178
|
+
import { homedir as homedir39 } from "os";
|
|
18179
|
+
function shortenPath(path, home = homedir39()) {
|
|
17353
18180
|
const value = String(path || "");
|
|
17354
18181
|
if (home && value === home) return "~";
|
|
17355
18182
|
if (home && value.startsWith(home + "/")) return "~" + value.slice(home.length);
|
|
@@ -17361,7 +18188,7 @@ function parseCursorAccount(output) {
|
|
|
17361
18188
|
}
|
|
17362
18189
|
function cursorAccount() {
|
|
17363
18190
|
try {
|
|
17364
|
-
const out =
|
|
18191
|
+
const out = execFileSync6("cursor-agent", ["status"], {
|
|
17365
18192
|
encoding: "utf8",
|
|
17366
18193
|
timeout: 5e3,
|
|
17367
18194
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -17412,13 +18239,13 @@ var init_identity2 = __esm({
|
|
|
17412
18239
|
});
|
|
17413
18240
|
|
|
17414
18241
|
// cli/harness/session.ts
|
|
17415
|
-
import { createInterface as createInterface5 } from "readline";
|
|
17416
18242
|
async function runOnce(harness, cwd, prompt, echoPrompt = true, showHeader = true, signal) {
|
|
17417
|
-
if (harness !== "cursor") {
|
|
18243
|
+
if (harness !== "cursor" && harness !== "codex") {
|
|
17418
18244
|
process.stdout.write(S2.dim + " " + harness + " sessions are not embedded yet\n" + S2.reset);
|
|
17419
18245
|
return 1;
|
|
17420
18246
|
}
|
|
17421
|
-
const result = await runCursorTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal });
|
|
18247
|
+
const result = harness === "codex" ? await runCodexTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal }) : await runCursorTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal });
|
|
18248
|
+
await resolveFixPolls(result.events);
|
|
17422
18249
|
if (result.blocked > 0) {
|
|
17423
18250
|
process.stdout.write(
|
|
17424
18251
|
S2.blocked + " " + result.blocked + " action" + (result.blocked === 1 ? "" : "s") + " blocked by policy" + S2.reset + "\n"
|
|
@@ -17429,43 +18256,49 @@ async function runOnce(harness, cwd, prompt, echoPrompt = true, showHeader = tru
|
|
|
17429
18256
|
async function runGovernedSession(harness, cwd, prompt) {
|
|
17430
18257
|
if (prompt) return runOnce(harness, cwd, prompt);
|
|
17431
18258
|
process.stdout.write(identityHeader({ cwd, harness, identity: readIdentity(harness) }).join("\n") + "\n");
|
|
17432
|
-
const
|
|
17433
|
-
const ask3 = () => new Promise((resolve7) => {
|
|
17434
|
-
const onClose = () => resolve7(null);
|
|
17435
|
-
rl.once("close", onClose);
|
|
17436
|
-
rl.question(S2.user + " \u276F " + S2.reset, (answer) => {
|
|
17437
|
-
rl.removeListener("close", onClose);
|
|
17438
|
-
resolve7(answer);
|
|
17439
|
-
});
|
|
17440
|
-
});
|
|
18259
|
+
const codex = harness === "codex" ? new CodexAppSession() : null;
|
|
17441
18260
|
let turn = null;
|
|
17442
|
-
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
}
|
|
17447
|
-
process.stdout.write("\n");
|
|
17448
|
-
rl.close();
|
|
17449
|
-
});
|
|
18261
|
+
const interruptTurn = () => {
|
|
18262
|
+
turn?.abort();
|
|
18263
|
+
};
|
|
18264
|
+
process.on("SIGINT", interruptTurn);
|
|
17450
18265
|
let first = true;
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17456
|
-
|
|
17457
|
-
|
|
17458
|
-
|
|
17459
|
-
|
|
17460
|
-
|
|
18266
|
+
try {
|
|
18267
|
+
for (; ; ) {
|
|
18268
|
+
const draft = await readPrompt(cwd);
|
|
18269
|
+
if (draft === null) break;
|
|
18270
|
+
const line = draft.text.trim();
|
|
18271
|
+
if (!line && draft.images.length === 0) continue;
|
|
18272
|
+
if (draft.images.length === 0 && (line === "exit" || line === "quit")) break;
|
|
18273
|
+
try {
|
|
18274
|
+
turn = new AbortController();
|
|
18275
|
+
const cursorPrompt = draft.images.length ? [draft.text, "", "Attached image files:", ...draft.images].join("\n") : draft.text;
|
|
18276
|
+
const result = codex ? await codex.turn({ prompt: draft.text, images: draft.images, cwd, showPrompt: false, showHeader: first, signal: turn.signal }) : await runCursorTurn({ prompt: cursorPrompt, cwd, showPrompt: false, showHeader: first, signal: turn.signal });
|
|
18277
|
+
await resolveFixPolls(result.events);
|
|
18278
|
+
if (result.blocked > 0) {
|
|
18279
|
+
process.stdout.write(
|
|
18280
|
+
S2.blocked + " " + result.blocked + " action" + (result.blocked === 1 ? "" : "s") + " blocked by policy" + S2.reset + "\n"
|
|
18281
|
+
);
|
|
18282
|
+
}
|
|
18283
|
+
first = false;
|
|
18284
|
+
} finally {
|
|
18285
|
+
cleanupPromptDraft(draft);
|
|
18286
|
+
turn = null;
|
|
18287
|
+
}
|
|
18288
|
+
}
|
|
18289
|
+
} finally {
|
|
18290
|
+
process.off("SIGINT", interruptTurn);
|
|
18291
|
+
codex?.close();
|
|
17461
18292
|
}
|
|
17462
|
-
rl.close();
|
|
17463
18293
|
return 0;
|
|
17464
18294
|
}
|
|
17465
18295
|
var init_session = __esm({
|
|
17466
18296
|
"cli/harness/session.ts"() {
|
|
17467
18297
|
"use strict";
|
|
18298
|
+
init_composer();
|
|
17468
18299
|
init_run();
|
|
18300
|
+
init_codex();
|
|
18301
|
+
init_fixPoll();
|
|
17469
18302
|
init_identity2();
|
|
17470
18303
|
init_render2();
|
|
17471
18304
|
}
|
|
@@ -17489,7 +18322,7 @@ async function takeover(kind, cwd) {
|
|
|
17489
18322
|
const harness = ["claude", "codex", "cursor"].includes(kind) ? kind : "claude";
|
|
17490
18323
|
const info = await detectContainerBackend();
|
|
17491
18324
|
const reachable = info.backend === "container" ? (await run(info.runner, ["test", "-d", cwd])).ok : false;
|
|
17492
|
-
const backend = reachable ? "container" : "host";
|
|
18325
|
+
const backend = harness === "codex" ? "host" : reachable ? "container" : "host";
|
|
17493
18326
|
const spaceName = cwd.split("/").filter(Boolean).pop() || "space";
|
|
17494
18327
|
const spawned = await spawnAgent(info, {
|
|
17495
18328
|
name: spaceName + "-" + harness + "-" + String(process.pid % 1e4),
|
|
@@ -17517,7 +18350,7 @@ async function restoreSession(session) {
|
|
|
17517
18350
|
harness: record.harness,
|
|
17518
18351
|
spaceName: record.spaceName,
|
|
17519
18352
|
cwd: record.space,
|
|
17520
|
-
backend: record.backend,
|
|
18353
|
+
backend: record.harness === "codex" ? "host" : record.backend,
|
|
17521
18354
|
resume: true
|
|
17522
18355
|
});
|
|
17523
18356
|
}
|
|
@@ -17660,11 +18493,11 @@ __export(linear_exports, {
|
|
|
17660
18493
|
linearCommand: () => linearCommand
|
|
17661
18494
|
});
|
|
17662
18495
|
import { readFileSync as readFileSync33 } from "fs";
|
|
17663
|
-
import { homedir as
|
|
17664
|
-
import { join as
|
|
18496
|
+
import { homedir as homedir40 } from "os";
|
|
18497
|
+
import { join as join38 } from "path";
|
|
17665
18498
|
function mcpJwt() {
|
|
17666
18499
|
try {
|
|
17667
|
-
return readFileSync33(
|
|
18500
|
+
return readFileSync33(join38(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
17668
18501
|
} catch {
|
|
17669
18502
|
return "";
|
|
17670
18503
|
}
|
|
@@ -17703,7 +18536,7 @@ var SYNKRO_DIR14, PORT2, BASE;
|
|
|
17703
18536
|
var init_linear = __esm({
|
|
17704
18537
|
"cli/commands/linear.ts"() {
|
|
17705
18538
|
"use strict";
|
|
17706
|
-
SYNKRO_DIR14 =
|
|
18539
|
+
SYNKRO_DIR14 = join38(homedir40(), ".synkro");
|
|
17707
18540
|
PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
|
|
17708
18541
|
BASE = `http://127.0.0.1:${PORT2}`;
|
|
17709
18542
|
}
|
|
@@ -17852,10 +18685,10 @@ var init_cveReachability = __esm({
|
|
|
17852
18685
|
});
|
|
17853
18686
|
|
|
17854
18687
|
// cli/reachability/reachabilityScan.ts
|
|
17855
|
-
import { spawnSync as
|
|
17856
|
-
import { readFileSync as readFileSync35, writeFileSync as
|
|
17857
|
-
import { join as
|
|
17858
|
-
import { homedir as
|
|
18688
|
+
import { spawnSync as spawnSync13, execFileSync as execFileSync7 } from "child_process";
|
|
18689
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync28, existsSync as existsSync39, readdirSync as readdirSync10 } from "fs";
|
|
18690
|
+
import { join as join39 } from "path";
|
|
18691
|
+
import { homedir as homedir41 } from "os";
|
|
17859
18692
|
import { createRequire } from "module";
|
|
17860
18693
|
function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
|
|
17861
18694
|
const SKIP2 = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".turbo", "out", ".cache", ".synkro", ".claude", "vendor", "__tests__", "test-results"]);
|
|
@@ -17872,7 +18705,7 @@ function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
17872
18705
|
}
|
|
17873
18706
|
for (const e of ents) {
|
|
17874
18707
|
if (files.length >= maxFiles) break;
|
|
17875
|
-
const full =
|
|
18708
|
+
const full = join39(dir, e.name);
|
|
17876
18709
|
if (e.isDirectory()) {
|
|
17877
18710
|
if (!SKIP2.has(e.name) && !e.name.startsWith(".")) stack.push(full);
|
|
17878
18711
|
continue;
|
|
@@ -17899,12 +18732,12 @@ function cleanVersion(spec) {
|
|
|
17899
18732
|
function gatherManifestVersions(repoRoot3) {
|
|
17900
18733
|
const out = {};
|
|
17901
18734
|
const dirs = [repoRoot3];
|
|
17902
|
-
const pkgsDir =
|
|
17903
|
-
if (
|
|
18735
|
+
const pkgsDir = join39(repoRoot3, "packages");
|
|
18736
|
+
if (existsSync39(pkgsDir)) {
|
|
17904
18737
|
try {
|
|
17905
18738
|
for (const d of readdirSync10(pkgsDir)) {
|
|
17906
|
-
const pd =
|
|
17907
|
-
if (
|
|
18739
|
+
const pd = join39(pkgsDir, d);
|
|
18740
|
+
if (existsSync39(join39(pd, "package.json"))) dirs.push(pd);
|
|
17908
18741
|
}
|
|
17909
18742
|
} catch {
|
|
17910
18743
|
}
|
|
@@ -17913,7 +18746,7 @@ function gatherManifestVersions(repoRoot3) {
|
|
|
17913
18746
|
for (const dir of dirs) {
|
|
17914
18747
|
let pkg;
|
|
17915
18748
|
try {
|
|
17916
|
-
pkg = JSON.parse(readFileSync35(
|
|
18749
|
+
pkg = JSON.parse(readFileSync35(join39(dir, "package.json"), "utf8"));
|
|
17917
18750
|
} catch {
|
|
17918
18751
|
continue;
|
|
17919
18752
|
}
|
|
@@ -17936,25 +18769,25 @@ function findJelly(repoRoot3) {
|
|
|
17936
18769
|
const pkg = JSON.parse(readFileSync35(pkgJson, "utf8"));
|
|
17937
18770
|
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
|
|
17938
18771
|
if (bin) {
|
|
17939
|
-
const p =
|
|
17940
|
-
if (
|
|
18772
|
+
const p = join39(dir, bin);
|
|
18773
|
+
if (existsSync39(p)) return p;
|
|
17941
18774
|
}
|
|
17942
18775
|
} catch {
|
|
17943
18776
|
}
|
|
17944
18777
|
for (const base of [repoRoot3, process.cwd()]) {
|
|
17945
|
-
const b =
|
|
17946
|
-
if (
|
|
18778
|
+
const b = join39(base, "node_modules", ".bin", "jelly");
|
|
18779
|
+
if (existsSync39(b)) return b;
|
|
17947
18780
|
}
|
|
17948
18781
|
return null;
|
|
17949
18782
|
}
|
|
17950
18783
|
function findEntries(repoRoot3) {
|
|
17951
18784
|
const dirs = [repoRoot3];
|
|
17952
|
-
const pkgsDir =
|
|
17953
|
-
if (
|
|
18785
|
+
const pkgsDir = join39(repoRoot3, "packages");
|
|
18786
|
+
if (existsSync39(pkgsDir)) {
|
|
17954
18787
|
try {
|
|
17955
18788
|
for (const d of readdirSync10(pkgsDir)) {
|
|
17956
|
-
const pd =
|
|
17957
|
-
if (
|
|
18789
|
+
const pd = join39(pkgsDir, d);
|
|
18790
|
+
if (existsSync39(join39(pd, "package.json"))) dirs.push(pd);
|
|
17958
18791
|
}
|
|
17959
18792
|
} catch {
|
|
17960
18793
|
}
|
|
@@ -17962,12 +18795,12 @@ function findEntries(repoRoot3) {
|
|
|
17962
18795
|
const entries = [];
|
|
17963
18796
|
for (const dir of dirs) {
|
|
17964
18797
|
try {
|
|
17965
|
-
const pkg = JSON.parse(readFileSync35(
|
|
18798
|
+
const pkg = JSON.parse(readFileSync35(join39(dir, "package.json"), "utf8"));
|
|
17966
18799
|
const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
|
|
17967
18800
|
for (const c of cands) {
|
|
17968
18801
|
if (typeof c !== "string") continue;
|
|
17969
|
-
const f =
|
|
17970
|
-
if (
|
|
18802
|
+
const f = join39(dir, c);
|
|
18803
|
+
if (existsSync39(f)) {
|
|
17971
18804
|
entries.push(f);
|
|
17972
18805
|
break;
|
|
17973
18806
|
}
|
|
@@ -17979,7 +18812,7 @@ function findEntries(repoRoot3) {
|
|
|
17979
18812
|
}
|
|
17980
18813
|
function currentCommit(repoRoot3) {
|
|
17981
18814
|
try {
|
|
17982
|
-
return
|
|
18815
|
+
return execFileSync7("git", ["rev-parse", "HEAD"], { cwd: repoRoot3, encoding: "utf8" }).trim();
|
|
17983
18816
|
} catch {
|
|
17984
18817
|
return "";
|
|
17985
18818
|
}
|
|
@@ -18000,7 +18833,7 @@ function parseApiUsage(log) {
|
|
|
18000
18833
|
}
|
|
18001
18834
|
function runReachabilityScan(repoRoot3, opts = {}) {
|
|
18002
18835
|
const commit = currentCommit(repoRoot3);
|
|
18003
|
-
if (!opts.force && commit &&
|
|
18836
|
+
if (!opts.force && commit && existsSync39(REACHABILITY_PATH)) {
|
|
18004
18837
|
try {
|
|
18005
18838
|
const prev = JSON.parse(readFileSync35(REACHABILITY_PATH, "utf8"));
|
|
18006
18839
|
if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
|
|
@@ -18053,7 +18886,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
|
|
|
18053
18886
|
if (jelly) {
|
|
18054
18887
|
const entries = findEntries(repoRoot3);
|
|
18055
18888
|
if (entries.length > 0) {
|
|
18056
|
-
const r =
|
|
18889
|
+
const r = spawnSync13(
|
|
18057
18890
|
process.execPath,
|
|
18058
18891
|
[jelly, "-b", repoRoot3, "--api-usage", ...entries],
|
|
18059
18892
|
{ encoding: "utf8", timeout: opts.timeoutMs ?? 18e4, maxBuffer: 2e8 }
|
|
@@ -18091,7 +18924,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
|
|
|
18091
18924
|
if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
|
|
18092
18925
|
const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot3) };
|
|
18093
18926
|
try {
|
|
18094
|
-
|
|
18927
|
+
writeFileSync28(REACHABILITY_PATH, JSON.stringify(file, null, 2));
|
|
18095
18928
|
} catch (e) {
|
|
18096
18929
|
return { ok: false, reason: "write failed: " + String(e.message || e) };
|
|
18097
18930
|
}
|
|
@@ -18103,7 +18936,7 @@ var init_reachabilityScan = __esm({
|
|
|
18103
18936
|
"use strict";
|
|
18104
18937
|
init_cveReachability();
|
|
18105
18938
|
require2 = createRequire(import.meta.url);
|
|
18106
|
-
REACHABILITY_PATH =
|
|
18939
|
+
REACHABILITY_PATH = join39(homedir41(), ".synkro", "reachability.json");
|
|
18107
18940
|
}
|
|
18108
18941
|
});
|
|
18109
18942
|
|
|
@@ -18112,13 +18945,13 @@ var reachabilityScan_exports = {};
|
|
|
18112
18945
|
__export(reachabilityScan_exports, {
|
|
18113
18946
|
reachabilityScanCommand: () => reachabilityScanCommand
|
|
18114
18947
|
});
|
|
18115
|
-
import { readFileSync as readFileSync36, existsSync as
|
|
18116
|
-
import { join as
|
|
18117
|
-
import { homedir as
|
|
18118
|
-
import { execFileSync as
|
|
18948
|
+
import { readFileSync as readFileSync36, existsSync as existsSync40 } from "fs";
|
|
18949
|
+
import { join as join40 } from "path";
|
|
18950
|
+
import { homedir as homedir42 } from "os";
|
|
18951
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
18119
18952
|
function readConfigEnv4() {
|
|
18120
|
-
const p =
|
|
18121
|
-
if (!
|
|
18953
|
+
const p = join40(SYNKRO_DIR15, "config.env");
|
|
18954
|
+
if (!existsSync40(p)) return {};
|
|
18122
18955
|
const out = {};
|
|
18123
18956
|
for (const line of readFileSync36(p, "utf-8").split("\n")) {
|
|
18124
18957
|
const t = line.trim();
|
|
@@ -18130,7 +18963,7 @@ function readConfigEnv4() {
|
|
|
18130
18963
|
}
|
|
18131
18964
|
function repoRoot2() {
|
|
18132
18965
|
try {
|
|
18133
|
-
return
|
|
18966
|
+
return execFileSync8("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
|
|
18134
18967
|
} catch {
|
|
18135
18968
|
return process.cwd();
|
|
18136
18969
|
}
|
|
@@ -18138,7 +18971,7 @@ function repoRoot2() {
|
|
|
18138
18971
|
function repoSlug(root) {
|
|
18139
18972
|
const run2 = (a) => {
|
|
18140
18973
|
try {
|
|
18141
|
-
return
|
|
18974
|
+
return execFileSync8("git", a, { encoding: "utf-8" }).trim();
|
|
18142
18975
|
} catch {
|
|
18143
18976
|
return "";
|
|
18144
18977
|
}
|
|
@@ -18152,10 +18985,10 @@ async function pushToCloud(cfg, repo) {
|
|
|
18152
18985
|
while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
|
|
18153
18986
|
let jwt2 = "";
|
|
18154
18987
|
try {
|
|
18155
|
-
jwt2 = readFileSync36(
|
|
18988
|
+
jwt2 = readFileSync36(join40(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
|
|
18156
18989
|
} catch {
|
|
18157
18990
|
}
|
|
18158
|
-
if (!jwt2 || !
|
|
18991
|
+
if (!jwt2 || !existsSync40(REACHABILITY_PATH)) return;
|
|
18159
18992
|
const body = readFileSync36(REACHABILITY_PATH, "utf-8");
|
|
18160
18993
|
try {
|
|
18161
18994
|
const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
|
|
@@ -18188,7 +19021,7 @@ var init_reachabilityScan2 = __esm({
|
|
|
18188
19021
|
"cli/commands/reachabilityScan.ts"() {
|
|
18189
19022
|
"use strict";
|
|
18190
19023
|
init_reachabilityScan();
|
|
18191
|
-
SYNKRO_DIR15 =
|
|
19024
|
+
SYNKRO_DIR15 = join40(homedir42(), ".synkro");
|
|
18192
19025
|
}
|
|
18193
19026
|
});
|
|
18194
19027
|
|
|
@@ -18318,11 +19151,11 @@ var config_exports = {};
|
|
|
18318
19151
|
__export(config_exports, {
|
|
18319
19152
|
configCommand: () => configCommand
|
|
18320
19153
|
});
|
|
18321
|
-
import { readFileSync as readFileSync37, writeFileSync as
|
|
18322
|
-
import { join as
|
|
18323
|
-
import { homedir as
|
|
19154
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync29, existsSync as existsSync41 } from "fs";
|
|
19155
|
+
import { join as join41 } from "path";
|
|
19156
|
+
import { homedir as homedir43 } from "os";
|
|
18324
19157
|
function readConfigEnv5() {
|
|
18325
|
-
if (!
|
|
19158
|
+
if (!existsSync41(CONFIG_PATH9)) return {};
|
|
18326
19159
|
const out = {};
|
|
18327
19160
|
for (const line of readFileSync37(CONFIG_PATH9, "utf-8").split("\n")) {
|
|
18328
19161
|
const t = line.trim();
|
|
@@ -18333,7 +19166,7 @@ function readConfigEnv5() {
|
|
|
18333
19166
|
return out;
|
|
18334
19167
|
}
|
|
18335
19168
|
function updateConfigValue(key, value) {
|
|
18336
|
-
if (!
|
|
19169
|
+
if (!existsSync41(CONFIG_PATH9)) {
|
|
18337
19170
|
console.error("No config found. Run `synkro install` first.");
|
|
18338
19171
|
process.exit(1);
|
|
18339
19172
|
}
|
|
@@ -18348,7 +19181,7 @@ function updateConfigValue(key, value) {
|
|
|
18348
19181
|
return line;
|
|
18349
19182
|
});
|
|
18350
19183
|
if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
|
|
18351
|
-
|
|
19184
|
+
writeFileSync29(CONFIG_PATH9, updated.join("\n"), "utf-8");
|
|
18352
19185
|
}
|
|
18353
19186
|
function resolveInferenceMode(cfg) {
|
|
18354
19187
|
if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
|
|
@@ -18506,8 +19339,8 @@ var init_config = __esm({
|
|
|
18506
19339
|
"use strict";
|
|
18507
19340
|
init_stub();
|
|
18508
19341
|
init_optout();
|
|
18509
|
-
SYNKRO_DIR16 =
|
|
18510
|
-
CONFIG_PATH9 =
|
|
19342
|
+
SYNKRO_DIR16 = join41(homedir43(), ".synkro");
|
|
19343
|
+
CONFIG_PATH9 = join41(SYNKRO_DIR16, "config.env");
|
|
18511
19344
|
}
|
|
18512
19345
|
});
|
|
18513
19346
|
|
|
@@ -18597,12 +19430,12 @@ async function runExport(args2) {
|
|
|
18597
19430
|
}
|
|
18598
19431
|
function confirmYesNo(question) {
|
|
18599
19432
|
if (!process.stdin.isTTY) return Promise.resolve(false);
|
|
18600
|
-
return new Promise((
|
|
19433
|
+
return new Promise((resolve8) => {
|
|
18601
19434
|
const rl = createInterface6({ input: process.stdin, output: process.stdout });
|
|
18602
19435
|
rl.question(`${question} (y/N): `, (answer) => {
|
|
18603
19436
|
rl.close();
|
|
18604
19437
|
const t = answer.trim().toLowerCase();
|
|
18605
|
-
|
|
19438
|
+
resolve8(t === "y" || t === "yes");
|
|
18606
19439
|
});
|
|
18607
19440
|
});
|
|
18608
19441
|
}
|
|
@@ -18697,11 +19530,11 @@ Usage:
|
|
|
18697
19530
|
|
|
18698
19531
|
// cli/inventory/identity.ts
|
|
18699
19532
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18700
|
-
import { existsSync as
|
|
18701
|
-
import { homedir as
|
|
18702
|
-
import { dirname as
|
|
19533
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync24, readFileSync as readFileSync38, renameSync as renameSync9, writeFileSync as writeFileSync30 } from "fs";
|
|
19534
|
+
import { homedir as homedir44 } from "os";
|
|
19535
|
+
import { dirname as dirname13, join as join42 } from "path";
|
|
18703
19536
|
function operationalIdentityPath() {
|
|
18704
|
-
return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH ||
|
|
19537
|
+
return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH || join42(homedir44(), ".synkro", "installation.json");
|
|
18705
19538
|
}
|
|
18706
19539
|
function validIdentity(value) {
|
|
18707
19540
|
if (!value || typeof value !== "object") return false;
|
|
@@ -18709,15 +19542,15 @@ function validIdentity(value) {
|
|
|
18709
19542
|
return typeof row2.installation_id === "string" && UUID_RE.test(row2.installation_id) && typeof row2.created_at === "string" && Number.isFinite(Date.parse(row2.created_at));
|
|
18710
19543
|
}
|
|
18711
19544
|
function writeIdentity(path, identity) {
|
|
18712
|
-
mkdirSync24(
|
|
19545
|
+
mkdirSync24(dirname13(path), { recursive: true, mode: 448 });
|
|
18713
19546
|
const temp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
|
|
18714
|
-
|
|
19547
|
+
writeFileSync30(temp, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
18715
19548
|
renameSync9(temp, path);
|
|
18716
19549
|
}
|
|
18717
19550
|
function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
|
|
18718
19551
|
const prior = cached4.get(path);
|
|
18719
19552
|
if (prior) return prior;
|
|
18720
|
-
if (
|
|
19553
|
+
if (existsSync42(path)) {
|
|
18721
19554
|
try {
|
|
18722
19555
|
const parsed = JSON.parse(readFileSync38(path, "utf8"));
|
|
18723
19556
|
if (validIdentity(parsed)) {
|
|
@@ -18744,13 +19577,13 @@ var init_identity3 = __esm({
|
|
|
18744
19577
|
// cli/inventory/collector.ts
|
|
18745
19578
|
import { createHash as createHash5 } from "crypto";
|
|
18746
19579
|
import {
|
|
18747
|
-
existsSync as
|
|
19580
|
+
existsSync as existsSync43,
|
|
18748
19581
|
readFileSync as readFileSync39,
|
|
18749
19582
|
readdirSync as readdirSync11,
|
|
18750
|
-
statSync as
|
|
19583
|
+
statSync as statSync6
|
|
18751
19584
|
} from "fs";
|
|
18752
|
-
import { arch, homedir as
|
|
18753
|
-
import { basename as
|
|
19585
|
+
import { arch, homedir as homedir45, hostname as hostname2, platform as platform6, release as release2 } from "os";
|
|
19586
|
+
import { basename as basename4, join as join43, relative, resolve as resolve6 } from "path";
|
|
18754
19587
|
import { fileURLToPath } from "url";
|
|
18755
19588
|
function sha256(value) {
|
|
18756
19589
|
return createHash5("sha256").update(value).digest("hex");
|
|
@@ -18760,14 +19593,14 @@ function pseudonymousHostnameHash(installationId, host) {
|
|
|
18760
19593
|
}
|
|
18761
19594
|
function cliVersion() {
|
|
18762
19595
|
try {
|
|
18763
|
-
return "1.10.
|
|
19596
|
+
return "1.10.8";
|
|
18764
19597
|
} catch {
|
|
18765
19598
|
return "0.0.0";
|
|
18766
19599
|
}
|
|
18767
19600
|
}
|
|
18768
19601
|
function readJson(path) {
|
|
18769
19602
|
try {
|
|
18770
|
-
if (!
|
|
19603
|
+
if (!existsSync43(path)) return null;
|
|
18771
19604
|
const parsed = JSON.parse(readFileSync39(path, "utf8"));
|
|
18772
19605
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
18773
19606
|
} catch {
|
|
@@ -18776,7 +19609,7 @@ function readJson(path) {
|
|
|
18776
19609
|
}
|
|
18777
19610
|
function readText(path) {
|
|
18778
19611
|
try {
|
|
18779
|
-
if (!
|
|
19612
|
+
if (!existsSync43(path)) return "";
|
|
18780
19613
|
return readFileSync39(path, "utf8");
|
|
18781
19614
|
} catch {
|
|
18782
19615
|
return "";
|
|
@@ -18797,7 +19630,7 @@ function canonical(raw) {
|
|
|
18797
19630
|
}
|
|
18798
19631
|
function safePackageName(command, args2) {
|
|
18799
19632
|
if (typeof command !== "string" || !command.trim()) return void 0;
|
|
18800
|
-
const runner =
|
|
19633
|
+
const runner = basename4(command.trim()).replace(/\.exe$/i, "");
|
|
18801
19634
|
if (Array.isArray(args2) && ["npx", "bunx", "uvx"].includes(runner)) {
|
|
18802
19635
|
const pkg = args2.find((arg) => typeof arg === "string" && !arg.startsWith("-"));
|
|
18803
19636
|
if (typeof pkg === "string") {
|
|
@@ -18805,7 +19638,7 @@ function safePackageName(command, args2) {
|
|
|
18805
19638
|
if (/^(?:@[a-z0-9_.-]+\/)?[a-z0-9_.-]+(?:@[a-z0-9_.+~-]+)?$/i.test(candidate)) {
|
|
18806
19639
|
return candidate;
|
|
18807
19640
|
}
|
|
18808
|
-
return
|
|
19641
|
+
return basename4(candidate);
|
|
18809
19642
|
}
|
|
18810
19643
|
}
|
|
18811
19644
|
return runner;
|
|
@@ -18863,16 +19696,16 @@ function mcpArtifactsFromJson(harness, config, configScope = "user") {
|
|
|
18863
19696
|
}
|
|
18864
19697
|
function claudeDesktopConfigCandidates(home, targetPlatform) {
|
|
18865
19698
|
if (targetPlatform === "darwin") {
|
|
18866
|
-
return [
|
|
19699
|
+
return [join43(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
|
|
18867
19700
|
}
|
|
18868
19701
|
if (targetPlatform === "linux") {
|
|
18869
19702
|
return [
|
|
18870
|
-
|
|
18871
|
-
|
|
19703
|
+
join43(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
19704
|
+
join43(home, ".config", "claude", "claude_desktop_config.json")
|
|
18872
19705
|
];
|
|
18873
19706
|
}
|
|
18874
19707
|
if (targetPlatform === "win32" && process.env.APPDATA) {
|
|
18875
|
-
return [
|
|
19708
|
+
return [join43(process.env.APPDATA, "Claude", "claude_desktop_config.json")];
|
|
18876
19709
|
}
|
|
18877
19710
|
return [];
|
|
18878
19711
|
}
|
|
@@ -18880,7 +19713,7 @@ function claudeManagedMcpConfigCandidates(targetPlatform) {
|
|
|
18880
19713
|
if (targetPlatform === "darwin") return ["/Library/Application Support/ClaudeCode/managed-mcp.json"];
|
|
18881
19714
|
if (targetPlatform === "linux") return ["/etc/claude-code/managed-mcp.json"];
|
|
18882
19715
|
if (targetPlatform === "win32" && process.env.ProgramFiles) {
|
|
18883
|
-
return [
|
|
19716
|
+
return [join43(process.env.ProgramFiles, "ClaudeCode", "managed-mcp.json")];
|
|
18884
19717
|
}
|
|
18885
19718
|
return [];
|
|
18886
19719
|
}
|
|
@@ -18888,8 +19721,8 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
|
|
|
18888
19721
|
const roots = /* @__PURE__ */ new Set();
|
|
18889
19722
|
const add = (value) => {
|
|
18890
19723
|
if (typeof value !== "string" || !value.trim()) return;
|
|
18891
|
-
const path =
|
|
18892
|
-
if (
|
|
19724
|
+
const path = resolve6(value);
|
|
19725
|
+
if (existsSync43(path)) roots.add(path);
|
|
18893
19726
|
};
|
|
18894
19727
|
add(currentDirectory);
|
|
18895
19728
|
for (const path of explicit) add(path);
|
|
@@ -18900,17 +19733,17 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
|
|
|
18900
19733
|
return [...roots];
|
|
18901
19734
|
}
|
|
18902
19735
|
function cursorWorkspaceStorageCandidates(home, targetPlatform) {
|
|
18903
|
-
if (targetPlatform === "darwin") return [
|
|
18904
|
-
if (targetPlatform === "linux") return [
|
|
19736
|
+
if (targetPlatform === "darwin") return [join43(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage")];
|
|
19737
|
+
if (targetPlatform === "linux") return [join43(home, ".config", "Cursor", "User", "workspaceStorage")];
|
|
18905
19738
|
if (targetPlatform === "win32" && process.env.APPDATA) {
|
|
18906
|
-
return [
|
|
19739
|
+
return [join43(process.env.APPDATA, "Cursor", "User", "workspaceStorage")];
|
|
18907
19740
|
}
|
|
18908
19741
|
return [];
|
|
18909
19742
|
}
|
|
18910
19743
|
function cursorWorkspaceRoots(home, targetPlatform) {
|
|
18911
19744
|
const roots = /* @__PURE__ */ new Set();
|
|
18912
19745
|
for (const storage of cursorWorkspaceStorageCandidates(home, targetPlatform)) {
|
|
18913
|
-
if (!
|
|
19746
|
+
if (!existsSync43(storage)) continue;
|
|
18914
19747
|
let entries = [];
|
|
18915
19748
|
try {
|
|
18916
19749
|
entries = readdirSync11(storage, { withFileTypes: true });
|
|
@@ -18919,12 +19752,12 @@ function cursorWorkspaceRoots(home, targetPlatform) {
|
|
|
18919
19752
|
}
|
|
18920
19753
|
for (const entry of entries) {
|
|
18921
19754
|
if (!entry.isDirectory() || entry.isSymbolicLink?.()) continue;
|
|
18922
|
-
const state = readJson(
|
|
19755
|
+
const state = readJson(join43(storage, entry.name, "workspace.json"));
|
|
18923
19756
|
const raw = state?.folder;
|
|
18924
19757
|
if (typeof raw !== "string" || !raw.trim()) continue;
|
|
18925
19758
|
try {
|
|
18926
19759
|
const path = raw.startsWith("file:") ? fileURLToPath(raw) : raw;
|
|
18927
|
-
if (
|
|
19760
|
+
if (existsSync43(path)) roots.add(resolve6(path));
|
|
18928
19761
|
} catch {
|
|
18929
19762
|
}
|
|
18930
19763
|
}
|
|
@@ -19000,10 +19833,10 @@ function hookArtifacts(harness, config) {
|
|
|
19000
19833
|
harness,
|
|
19001
19834
|
type: "hook",
|
|
19002
19835
|
canonical_id: `${canonical(event)}:${commandHash.slice(0, 20)}`,
|
|
19003
|
-
display_name: `${event} \xB7 ${
|
|
19836
|
+
display_name: `${event} \xB7 ${basename4(String(entry.command).split(/\s+/)[0] || "hook")}`,
|
|
19004
19837
|
enabled: entry.enabled !== false,
|
|
19005
19838
|
config_scope: "user",
|
|
19006
|
-
package_name:
|
|
19839
|
+
package_name: basename4(String(entry.command).split(/\s+/)[0] || "") || void 0,
|
|
19007
19840
|
config_hash: commandHash,
|
|
19008
19841
|
metadata: { managed, events: [event] }
|
|
19009
19842
|
});
|
|
@@ -19018,7 +19851,7 @@ function parseFrontmatter(content) {
|
|
|
19018
19851
|
return { name: value("name"), version: value("version") };
|
|
19019
19852
|
}
|
|
19020
19853
|
function skillArtifacts(harness, root) {
|
|
19021
|
-
if (!
|
|
19854
|
+
if (!existsSync43(root)) return [];
|
|
19022
19855
|
const manifests = [];
|
|
19023
19856
|
const visit = (dir) => {
|
|
19024
19857
|
let entries;
|
|
@@ -19029,7 +19862,7 @@ function skillArtifacts(harness, root) {
|
|
|
19029
19862
|
}
|
|
19030
19863
|
for (const entry of entries) {
|
|
19031
19864
|
if (entry.isSymbolicLink?.()) continue;
|
|
19032
|
-
const path =
|
|
19865
|
+
const path = join43(dir, entry.name);
|
|
19033
19866
|
if (entry.isFile() && entry.name === "SKILL.md") manifests.push(path);
|
|
19034
19867
|
else if (entry.isDirectory()) visit(path);
|
|
19035
19868
|
}
|
|
@@ -19039,7 +19872,7 @@ function skillArtifacts(harness, root) {
|
|
|
19039
19872
|
const content = readText(path);
|
|
19040
19873
|
const frontmatter = parseFrontmatter(content);
|
|
19041
19874
|
const rel = relative(root, path).replaceAll("\\", "/");
|
|
19042
|
-
const name = frontmatter.name ||
|
|
19875
|
+
const name = frontmatter.name || basename4(join43(path, "..")) || "skill";
|
|
19043
19876
|
return {
|
|
19044
19877
|
harness,
|
|
19045
19878
|
type: "skill",
|
|
@@ -19054,7 +19887,7 @@ function skillArtifacts(harness, root) {
|
|
|
19054
19887
|
});
|
|
19055
19888
|
}
|
|
19056
19889
|
function cursorExtensionArtifacts(root) {
|
|
19057
|
-
if (!
|
|
19890
|
+
if (!existsSync43(root)) return [];
|
|
19058
19891
|
let dirs = [];
|
|
19059
19892
|
try {
|
|
19060
19893
|
dirs = readdirSync11(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink());
|
|
@@ -19063,7 +19896,7 @@ function cursorExtensionArtifacts(root) {
|
|
|
19063
19896
|
}
|
|
19064
19897
|
const artifacts = [];
|
|
19065
19898
|
for (const dir of dirs) {
|
|
19066
|
-
const pkg = readJson(
|
|
19899
|
+
const pkg = readJson(join43(root, dir.name, "package.json"));
|
|
19067
19900
|
if (!pkg) continue;
|
|
19068
19901
|
const publisher = typeof pkg.publisher === "string" ? pkg.publisher : void 0;
|
|
19069
19902
|
const name = typeof pkg.name === "string" ? pkg.name : dir.name;
|
|
@@ -19083,20 +19916,20 @@ function cursorExtensionArtifacts(root) {
|
|
|
19083
19916
|
return artifacts;
|
|
19084
19917
|
}
|
|
19085
19918
|
function deploymentMode2(home) {
|
|
19086
|
-
const raw = readText(
|
|
19919
|
+
const raw = readText(join43(home, ".synkro", "config.env"));
|
|
19087
19920
|
const value = (key) => raw.match(new RegExp(`^${key}=['"]?([^'"\\n]*)`, "m"))?.[1]?.toLowerCase();
|
|
19088
19921
|
if (value("SYNKRO_GRADING_MODE") === "byok") return "byok";
|
|
19089
19922
|
if (value("SYNKRO_STORAGE_MODE") === "cloud") return "cloud";
|
|
19090
19923
|
return "local";
|
|
19091
19924
|
}
|
|
19092
19925
|
function telemetryHealth(home) {
|
|
19093
|
-
const meta = readJson(
|
|
19926
|
+
const meta = readJson(join43(home, ".synkro", "telemetry-meta.json"));
|
|
19094
19927
|
const health = {};
|
|
19095
19928
|
if (meta?.last_flush_ok_at && Number.isFinite(Date.parse(meta.last_flush_ok_at))) health.telemetry_last_flush_at = meta.last_flush_ok_at;
|
|
19096
19929
|
if (meta?.last_flush_error) health.telemetry_last_error = "flush_failed";
|
|
19097
|
-
const queue =
|
|
19930
|
+
const queue = join43(home, ".synkro", "telemetry-pending.jsonl");
|
|
19098
19931
|
try {
|
|
19099
|
-
const size =
|
|
19932
|
+
const size = statSync6(queue).size;
|
|
19100
19933
|
health.telemetry_backlog = size <= 5 * 1024 * 1024 ? readFileSync39(queue, "utf8").split("\n").filter(Boolean).length : Math.ceil(size / 1024);
|
|
19101
19934
|
} catch {
|
|
19102
19935
|
}
|
|
@@ -19138,7 +19971,7 @@ function harnessSnapshot(agent) {
|
|
|
19138
19971
|
}
|
|
19139
19972
|
const config = readJson(agent.settingsPath);
|
|
19140
19973
|
const coverage = inspectCodexHooks(agent.settingsPath);
|
|
19141
|
-
const toml = readText(
|
|
19974
|
+
const toml = readText(join43(agent.configDir, "config.toml"));
|
|
19142
19975
|
const permission = toml.match(/^\s*approval_policy\s*=\s*["']([^"']+)/m)?.[1];
|
|
19143
19976
|
return {
|
|
19144
19977
|
row: {
|
|
@@ -19155,11 +19988,11 @@ function harnessSnapshot(agent) {
|
|
|
19155
19988
|
};
|
|
19156
19989
|
}
|
|
19157
19990
|
function collectOperationalInventory(options = {}) {
|
|
19158
|
-
const home = options.homeDir ??
|
|
19991
|
+
const home = options.homeDir ?? homedir45();
|
|
19159
19992
|
const detected = options.detectedAgents ?? detectAgents();
|
|
19160
19993
|
const identity = getOperationalInstallationIdentity(options.identityPath);
|
|
19161
19994
|
const targetPlatform = options.platformName ?? platform6();
|
|
19162
|
-
const codexHome = options.homeDir ?
|
|
19995
|
+
const codexHome = options.homeDir ? join43(home, ".codex") : process.env.CODEX_HOME || join43(home, ".codex");
|
|
19163
19996
|
const harnesses = [];
|
|
19164
19997
|
const artifacts = [];
|
|
19165
19998
|
for (const agent of detected) {
|
|
@@ -19167,7 +20000,7 @@ function collectOperationalInventory(options = {}) {
|
|
|
19167
20000
|
harnesses.push(row2);
|
|
19168
20001
|
artifacts.push(...hookArtifacts(row2.harness, config));
|
|
19169
20002
|
}
|
|
19170
|
-
const claudeJson = readJson(
|
|
20003
|
+
const claudeJson = readJson(join43(home, ".claude.json"));
|
|
19171
20004
|
artifacts.push(...mcpArtifactsFromJson("claude_code", claudeJson));
|
|
19172
20005
|
if (claudeJson?.projects && typeof claudeJson.projects === "object") {
|
|
19173
20006
|
for (const [projectPath, project] of Object.entries(claudeJson.projects)) {
|
|
@@ -19175,8 +20008,8 @@ function collectOperationalInventory(options = {}) {
|
|
|
19175
20008
|
artifacts.push(...mcpArtifactsFromJson("claude_code", project, `local:${sha256(projectPath).slice(0, 16)}`));
|
|
19176
20009
|
}
|
|
19177
20010
|
}
|
|
19178
|
-
artifacts.push(...mcpArtifactsFromJson("cursor", readJson(
|
|
19179
|
-
artifacts.push(...codexMcpArtifacts(readText(
|
|
20011
|
+
artifacts.push(...mcpArtifactsFromJson("cursor", readJson(join43(home, ".cursor", "mcp.json"))));
|
|
20012
|
+
artifacts.push(...codexMcpArtifacts(readText(join43(codexHome, "config.toml"))));
|
|
19180
20013
|
const projectRoots = discoveredProjectRoots(
|
|
19181
20014
|
claudeJson,
|
|
19182
20015
|
options.currentDirectory ?? process.cwd(),
|
|
@@ -19187,11 +20020,11 @@ function collectOperationalInventory(options = {}) {
|
|
|
19187
20020
|
const scopeHash = sha256(projectRoot).slice(0, 16);
|
|
19188
20021
|
artifacts.push(...mcpArtifactsFromJson(
|
|
19189
20022
|
"claude_code",
|
|
19190
|
-
readJson(
|
|
20023
|
+
readJson(join43(projectRoot, ".mcp.json")),
|
|
19191
20024
|
`project:${scopeHash}`
|
|
19192
20025
|
));
|
|
19193
|
-
const cursorProjectConfig =
|
|
19194
|
-
if (
|
|
20026
|
+
const cursorProjectConfig = join43(projectRoot, ".cursor", "mcp.json");
|
|
20027
|
+
if (resolve6(cursorProjectConfig) !== resolve6(join43(home, ".cursor", "mcp.json"))) {
|
|
19195
20028
|
artifacts.push(...mcpArtifactsFromJson(
|
|
19196
20029
|
"cursor",
|
|
19197
20030
|
readJson(cursorProjectConfig),
|
|
@@ -19202,7 +20035,7 @@ function collectOperationalInventory(options = {}) {
|
|
|
19202
20035
|
for (const managedPath of claudeManagedMcpConfigCandidates(targetPlatform)) {
|
|
19203
20036
|
artifacts.push(...mcpArtifactsFromJson("claude_code", readJson(managedPath), "managed"));
|
|
19204
20037
|
}
|
|
19205
|
-
const desktopConfigPath = claudeDesktopConfigCandidates(home, targetPlatform).find((path) =>
|
|
20038
|
+
const desktopConfigPath = claudeDesktopConfigCandidates(home, targetPlatform).find((path) => existsSync43(path));
|
|
19206
20039
|
if (desktopConfigPath) {
|
|
19207
20040
|
const desktopConfig = readJson(desktopConfigPath);
|
|
19208
20041
|
harnesses.push({
|
|
@@ -19213,7 +20046,7 @@ function collectOperationalInventory(options = {}) {
|
|
|
19213
20046
|
});
|
|
19214
20047
|
artifacts.push(...mcpArtifactsFromJson("claude_desktop", desktopConfig));
|
|
19215
20048
|
}
|
|
19216
|
-
const claudeSettings = readJson(
|
|
20049
|
+
const claudeSettings = readJson(join43(home, ".claude", "settings.json"));
|
|
19217
20050
|
if (claudeSettings?.enabledPlugins && typeof claudeSettings.enabledPlugins === "object") {
|
|
19218
20051
|
for (const [name, enabled] of Object.entries(claudeSettings.enabledPlugins)) {
|
|
19219
20052
|
artifacts.push({
|
|
@@ -19227,10 +20060,10 @@ function collectOperationalInventory(options = {}) {
|
|
|
19227
20060
|
});
|
|
19228
20061
|
}
|
|
19229
20062
|
}
|
|
19230
|
-
artifacts.push(...skillArtifacts("claude_code",
|
|
19231
|
-
artifacts.push(...skillArtifacts("cursor",
|
|
19232
|
-
artifacts.push(...skillArtifacts("codex",
|
|
19233
|
-
artifacts.push(...cursorExtensionArtifacts(
|
|
20063
|
+
artifacts.push(...skillArtifacts("claude_code", join43(home, ".claude", "skills")));
|
|
20064
|
+
artifacts.push(...skillArtifacts("cursor", join43(home, ".cursor", "skills")));
|
|
20065
|
+
artifacts.push(...skillArtifacts("codex", join43(codexHome, "skills")));
|
|
20066
|
+
artifacts.push(...cursorExtensionArtifacts(join43(home, ".cursor", "extensions")));
|
|
19234
20067
|
const uniqueArtifacts = /* @__PURE__ */ new Map();
|
|
19235
20068
|
for (const artifact of artifacts) {
|
|
19236
20069
|
const key = `${artifact.harness || "global"}:${artifact.type}:${artifact.canonical_id}`;
|
|
@@ -19283,18 +20116,18 @@ __export(sync_exports2, {
|
|
|
19283
20116
|
syncOperationalInventoryDetached: () => syncOperationalInventoryDetached
|
|
19284
20117
|
});
|
|
19285
20118
|
import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
|
|
19286
|
-
import { spawn as
|
|
20119
|
+
import { spawn as spawn11 } from "child_process";
|
|
19287
20120
|
import {
|
|
19288
|
-
existsSync as
|
|
20121
|
+
existsSync as existsSync44,
|
|
19289
20122
|
mkdirSync as mkdirSync25,
|
|
19290
20123
|
readFileSync as readFileSync40,
|
|
19291
20124
|
renameSync as renameSync10,
|
|
19292
|
-
writeFileSync as
|
|
20125
|
+
writeFileSync as writeFileSync31
|
|
19293
20126
|
} from "fs";
|
|
19294
|
-
import { homedir as
|
|
19295
|
-
import { dirname as
|
|
20127
|
+
import { homedir as homedir46 } from "os";
|
|
20128
|
+
import { dirname as dirname14, join as join44 } from "path";
|
|
19296
20129
|
function syncStatePath() {
|
|
19297
|
-
return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH ||
|
|
20130
|
+
return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH || join44(homedir46(), ".synkro", "inventory-sync.json");
|
|
19298
20131
|
}
|
|
19299
20132
|
function readState(path = syncStatePath()) {
|
|
19300
20133
|
try {
|
|
@@ -19306,9 +20139,9 @@ function readState(path = syncStatePath()) {
|
|
|
19306
20139
|
}
|
|
19307
20140
|
function writeState(state, path = syncStatePath()) {
|
|
19308
20141
|
try {
|
|
19309
|
-
mkdirSync25(
|
|
20142
|
+
mkdirSync25(dirname14(path), { recursive: true, mode: 448 });
|
|
19310
20143
|
const temp = `${path}.${process.pid}.tmp`;
|
|
19311
|
-
|
|
20144
|
+
writeFileSync31(temp, JSON.stringify(state, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
19312
20145
|
renameSync10(temp, path);
|
|
19313
20146
|
} catch {
|
|
19314
20147
|
}
|
|
@@ -19321,7 +20154,7 @@ function shouldSyncInventory(state, now = Date.now(), target) {
|
|
|
19321
20154
|
return !Number.isFinite(lastAttempt) || lastAttempt <= 0 || now - lastAttempt >= FAILURE_RETRY_MS;
|
|
19322
20155
|
}
|
|
19323
20156
|
function readConfig() {
|
|
19324
|
-
const path =
|
|
20157
|
+
const path = join44(homedir46(), ".synkro", "config.env");
|
|
19325
20158
|
const out = {};
|
|
19326
20159
|
try {
|
|
19327
20160
|
for (const rawLine of readFileSync40(path, "utf8").split("\n")) {
|
|
@@ -19363,7 +20196,7 @@ function resolveInventoryGateway(raw) {
|
|
|
19363
20196
|
}
|
|
19364
20197
|
async function loadToken() {
|
|
19365
20198
|
try {
|
|
19366
|
-
const durable = readFileSync40(
|
|
20199
|
+
const durable = readFileSync40(join44(homedir46(), ".synkro", ".mcp-jwt"), "utf8").trim();
|
|
19367
20200
|
if (durable) return durable;
|
|
19368
20201
|
} catch {
|
|
19369
20202
|
}
|
|
@@ -19481,8 +20314,8 @@ function syncOperationalInventoryDetached() {
|
|
|
19481
20314
|
writeState({ ...state, last_attempt_at: (/* @__PURE__ */ new Date()).toISOString(), last_target: target }, path);
|
|
19482
20315
|
try {
|
|
19483
20316
|
const script = process.argv[1];
|
|
19484
|
-
if (!script || !
|
|
19485
|
-
const child =
|
|
20317
|
+
if (!script || !existsSync44(script)) return;
|
|
20318
|
+
const child = spawn11(process.execPath, [script, "inventory-sync", "--detached"], {
|
|
19486
20319
|
detached: true,
|
|
19487
20320
|
stdio: "ignore",
|
|
19488
20321
|
env: { ...process.env, SYNKRO_INVENTORY_DETACHED: "1" }
|
|
@@ -19505,14 +20338,14 @@ var init_sync2 = __esm({
|
|
|
19505
20338
|
});
|
|
19506
20339
|
|
|
19507
20340
|
// cli/bootstrap.js
|
|
19508
|
-
import { readFileSync as readFileSync41, existsSync as
|
|
19509
|
-
import { resolve as
|
|
20341
|
+
import { readFileSync as readFileSync41, existsSync as existsSync45 } from "fs";
|
|
20342
|
+
import { resolve as resolve7 } from "path";
|
|
19510
20343
|
process.title = "synkro";
|
|
19511
20344
|
var envCandidates = [
|
|
19512
|
-
|
|
20345
|
+
resolve7(process.env.HOME ?? "", ".synkro", "config.env")
|
|
19513
20346
|
];
|
|
19514
20347
|
for (const envPath of envCandidates) {
|
|
19515
|
-
if (!
|
|
20348
|
+
if (!existsSync45(envPath)) continue;
|
|
19516
20349
|
const envContent = readFileSync41(envPath, "utf-8");
|
|
19517
20350
|
for (const line of envContent.split("\n")) {
|
|
19518
20351
|
const trimmed = line.trim();
|
|
@@ -19530,7 +20363,7 @@ var subArgs = args.slice(1);
|
|
|
19530
20363
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
19531
20364
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "inventory-sync", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
19532
20365
|
function printVersion() {
|
|
19533
|
-
console.log("1.10.
|
|
20366
|
+
console.log("1.10.8");
|
|
19534
20367
|
}
|
|
19535
20368
|
function printHelp2() {
|
|
19536
20369
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|