@synkro-sh/cli 1.10.8 → 1.10.9
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 +1484 -1147
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -81,10 +81,10 @@ import { createHash } from "crypto";
|
|
|
81
81
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
82
82
|
import { homedir as homedir2, hostname, platform } from "os";
|
|
83
83
|
import { join as join2 } from "path";
|
|
84
|
-
function readConfigValue(
|
|
85
|
-
if (process.env[
|
|
84
|
+
function readConfigValue(key2) {
|
|
85
|
+
if (process.env[key2]) return String(process.env[key2]).toLowerCase();
|
|
86
86
|
try {
|
|
87
|
-
const re =
|
|
87
|
+
const re = key2 === "SYNKRO_GRADING_MODE" ? /^SYNKRO_GRADING_MODE=['"]?([^'"\n]*)/m : /^SYNKRO_STORAGE_MODE=['"]?([^'"\n]*)/m;
|
|
88
88
|
const m = readFileSync2(CONFIG_PATH, "utf-8").match(re);
|
|
89
89
|
return m ? m[1].toLowerCase() : "";
|
|
90
90
|
} catch {
|
|
@@ -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.9";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -789,9 +789,9 @@ function sanitize(raw, maxLen = 256) {
|
|
|
789
789
|
function shellQuoteSingle(value) {
|
|
790
790
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
791
791
|
}
|
|
792
|
-
function writeConfigEnvFlag(
|
|
792
|
+
function writeConfigEnvFlag(key2, value) {
|
|
793
793
|
const safe = sanitize(value, 8);
|
|
794
|
-
const line = `${
|
|
794
|
+
const line = `${key2}=${shellQuoteSingle(safe)}`;
|
|
795
795
|
let content = "";
|
|
796
796
|
if (existsSync5(CONFIG_PATH2)) {
|
|
797
797
|
try {
|
|
@@ -800,7 +800,7 @@ function writeConfigEnvFlag(key, value) {
|
|
|
800
800
|
content = "";
|
|
801
801
|
}
|
|
802
802
|
}
|
|
803
|
-
const re = new RegExp(`^${
|
|
803
|
+
const re = new RegExp(`^${key2}=.*$`, "m");
|
|
804
804
|
if (re.test(content)) {
|
|
805
805
|
content = content.replace(re, line);
|
|
806
806
|
} else {
|
|
@@ -1231,15 +1231,15 @@ async function exportEvents(path) {
|
|
|
1231
1231
|
writeFileSync4(path, "", "utf-8");
|
|
1232
1232
|
return;
|
|
1233
1233
|
}
|
|
1234
|
-
const
|
|
1234
|
+
const lines2 = [];
|
|
1235
1235
|
try {
|
|
1236
1236
|
const rows = await sql`SELECT * FROM telemetry_events ORDER BY occurred_at ASC`;
|
|
1237
1237
|
for (const row2 of rows) {
|
|
1238
|
-
|
|
1238
|
+
lines2.push(JSON.stringify(row2));
|
|
1239
1239
|
}
|
|
1240
1240
|
} catch {
|
|
1241
1241
|
}
|
|
1242
|
-
writeFileSync4(path,
|
|
1242
|
+
writeFileSync4(path, lines2.join("\n") + (lines2.length > 0 ? "\n" : ""), "utf-8");
|
|
1243
1243
|
}
|
|
1244
1244
|
function pendingFileSize() {
|
|
1245
1245
|
try {
|
|
@@ -1856,21 +1856,21 @@ function validateHooksPath(path) {
|
|
|
1856
1856
|
return resolved;
|
|
1857
1857
|
}
|
|
1858
1858
|
function readHooksFile(rawPath) {
|
|
1859
|
-
const
|
|
1859
|
+
const safePath2 = validateHooksPath(rawPath);
|
|
1860
1860
|
try {
|
|
1861
|
-
const raw = readFileSync10(
|
|
1861
|
+
const raw = readFileSync10(safePath2, "utf-8");
|
|
1862
1862
|
return JSON.parse(raw);
|
|
1863
1863
|
} catch (err) {
|
|
1864
1864
|
if (err?.code === "ENOENT") return { version: 1, hooks: {} };
|
|
1865
|
-
throw new Error(`Failed to parse ${
|
|
1865
|
+
throw new Error(`Failed to parse ${safePath2}: ${err.message}`);
|
|
1866
1866
|
}
|
|
1867
1867
|
}
|
|
1868
1868
|
function writeHooksFileAtomic(rawPath, data) {
|
|
1869
|
-
const
|
|
1870
|
-
mkdirSync5(dirname2(
|
|
1871
|
-
const tmpPath = `${
|
|
1869
|
+
const safePath2 = validateHooksPath(rawPath);
|
|
1870
|
+
mkdirSync5(dirname2(safePath2), { recursive: true });
|
|
1871
|
+
const tmpPath = `${safePath2}.synkro.tmp`;
|
|
1872
1872
|
writeFileSync7(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
1873
|
-
renameSync4(tmpPath,
|
|
1873
|
+
renameSync4(tmpPath, safePath2);
|
|
1874
1874
|
}
|
|
1875
1875
|
function isSynkroEntry2(entry) {
|
|
1876
1876
|
if (entry?.[SYNKRO_MARKER2]) return true;
|
|
@@ -2158,20 +2158,20 @@ function validateHooksPath2(path) {
|
|
|
2158
2158
|
return resolved;
|
|
2159
2159
|
}
|
|
2160
2160
|
function readHooksFile2(rawPath) {
|
|
2161
|
-
const
|
|
2162
|
-
if (!existsSync12(
|
|
2161
|
+
const safePath2 = validateHooksPath2(rawPath);
|
|
2162
|
+
if (!existsSync12(safePath2)) return { hooks: {} };
|
|
2163
2163
|
try {
|
|
2164
|
-
return JSON.parse(readFileSync11(
|
|
2164
|
+
return JSON.parse(readFileSync11(safePath2, "utf-8"));
|
|
2165
2165
|
} catch (err) {
|
|
2166
|
-
throw new Error(`Failed to parse ${
|
|
2166
|
+
throw new Error(`Failed to parse ${safePath2}: ${err.message}`);
|
|
2167
2167
|
}
|
|
2168
2168
|
}
|
|
2169
2169
|
function writeHooksFileAtomic2(rawPath, data) {
|
|
2170
|
-
const
|
|
2171
|
-
mkdirSync6(dirname3(
|
|
2172
|
-
const tmpPath = `${
|
|
2170
|
+
const safePath2 = validateHooksPath2(rawPath);
|
|
2171
|
+
mkdirSync6(dirname3(safePath2), { recursive: true });
|
|
2172
|
+
const tmpPath = `${safePath2}.synkro.tmp`;
|
|
2173
2173
|
writeFileSync8(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
2174
|
-
renameSync5(tmpPath,
|
|
2174
|
+
renameSync5(tmpPath, safePath2);
|
|
2175
2175
|
}
|
|
2176
2176
|
function isSynkroEntry3(entry) {
|
|
2177
2177
|
if (entry?.[SYNKRO_MARKER3]) return true;
|
|
@@ -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((resolve9) => {
|
|
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
|
+
resolve9(summary);
|
|
2373
2373
|
};
|
|
2374
2374
|
const timer = setTimeout(() => finish(null), 1e4);
|
|
2375
2375
|
try {
|
|
@@ -2384,9 +2384,9 @@ function queryCodexHookTrust(codexBinary = "codex", cwd = process.cwd(), autoTru
|
|
|
2384
2384
|
child.stdout.on("data", (chunk) => {
|
|
2385
2385
|
stdout += chunk;
|
|
2386
2386
|
pending += chunk;
|
|
2387
|
-
const
|
|
2388
|
-
pending =
|
|
2389
|
-
for (const line of
|
|
2387
|
+
const lines2 = pending.split("\n");
|
|
2388
|
+
pending = lines2.pop() || "";
|
|
2389
|
+
for (const line of lines2) {
|
|
2390
2390
|
let message;
|
|
2391
2391
|
try {
|
|
2392
2392
|
message = JSON.parse(line);
|
|
@@ -2620,11 +2620,11 @@ function writeCodexTomlAtomic(path, content) {
|
|
|
2620
2620
|
renameSync6(tmpPath, path);
|
|
2621
2621
|
}
|
|
2622
2622
|
function removeCodexManagedBlock(content, path) {
|
|
2623
|
-
const
|
|
2623
|
+
const lines2 = content.split("\n");
|
|
2624
2624
|
const out = [];
|
|
2625
2625
|
let removed = false;
|
|
2626
2626
|
let inside = false;
|
|
2627
|
-
for (const line of
|
|
2627
|
+
for (const line of lines2) {
|
|
2628
2628
|
if (!inside && line.trim() === CODEX_MCP_BEGIN) {
|
|
2629
2629
|
inside = true;
|
|
2630
2630
|
removed = true;
|
|
@@ -2652,8 +2652,8 @@ function findCodexMcpSection(content) {
|
|
|
2652
2652
|
body: content.slice(match.index, next?.index ?? content.length)
|
|
2653
2653
|
};
|
|
2654
2654
|
}
|
|
2655
|
-
function readTomlString(block,
|
|
2656
|
-
const match = block.match(new RegExp(`^\\s*${
|
|
2655
|
+
function readTomlString(block, key2) {
|
|
2656
|
+
const match = block.match(new RegExp(`^\\s*${key2}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")\\s*$`, "m"));
|
|
2657
2657
|
if (!match) return null;
|
|
2658
2658
|
try {
|
|
2659
2659
|
return JSON.parse(match[1]);
|
|
@@ -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((resolve9, 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
|
+
resolve9(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((resolve9) => rl.question(question, resolve9));
|
|
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((resolve9) => {
|
|
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
|
+
resolve9();
|
|
5865
5865
|
});
|
|
5866
5866
|
});
|
|
5867
5867
|
}
|
|
@@ -7034,8 +7034,8 @@ function cursorApiKeyConfigured() {
|
|
|
7034
7034
|
return false;
|
|
7035
7035
|
}
|
|
7036
7036
|
}
|
|
7037
|
-
function writeCursorApiKey(
|
|
7038
|
-
const trimmed =
|
|
7037
|
+
function writeCursorApiKey(key2) {
|
|
7038
|
+
const trimmed = key2.trim();
|
|
7039
7039
|
if (!trimmed) return;
|
|
7040
7040
|
mkdirSync11(CURSOR_CREDS_DIR, { recursive: true });
|
|
7041
7041
|
chmodSync2(CURSOR_CREDS_DIR, 448);
|
|
@@ -7043,15 +7043,15 @@ function writeCursorApiKey(key) {
|
|
|
7043
7043
|
chmodSync2(CURSOR_API_KEY_FILE, 384);
|
|
7044
7044
|
}
|
|
7045
7045
|
async function validateCursorApiKey() {
|
|
7046
|
-
let
|
|
7046
|
+
let key2;
|
|
7047
7047
|
try {
|
|
7048
|
-
|
|
7048
|
+
key2 = readFileSync16(CURSOR_API_KEY_FILE, "utf-8").trim();
|
|
7049
7049
|
} catch {
|
|
7050
7050
|
return null;
|
|
7051
7051
|
}
|
|
7052
|
-
if (!
|
|
7052
|
+
if (!key2) return null;
|
|
7053
7053
|
try {
|
|
7054
|
-
const auth = Buffer.from(`${
|
|
7054
|
+
const auth = Buffer.from(`${key2}:`).toString("base64");
|
|
7055
7055
|
const r = await fetch("https://api.cursor.com/v1/me", {
|
|
7056
7056
|
headers: { Authorization: `Basic ${auth}` },
|
|
7057
7057
|
signal: AbortSignal.timeout(8e3)
|
|
@@ -7391,15 +7391,15 @@ function parseSynkroToml(raw) {
|
|
|
7391
7391
|
}
|
|
7392
7392
|
const eq = line.indexOf("=");
|
|
7393
7393
|
if (eq === -1) continue;
|
|
7394
|
-
const
|
|
7394
|
+
const key2 = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
|
|
7395
7395
|
let valRaw = line.slice(eq + 1).trim();
|
|
7396
7396
|
if (!valRaw.startsWith('"') && !valRaw.startsWith("'") && !valRaw.startsWith("[")) {
|
|
7397
7397
|
const h = valRaw.indexOf("#");
|
|
7398
7398
|
if (h !== -1) valRaw = valRaw.slice(0, h).trim();
|
|
7399
7399
|
}
|
|
7400
7400
|
const value = parseTomlValue(valRaw);
|
|
7401
|
-
if (section) result[section][
|
|
7402
|
-
else result[
|
|
7401
|
+
if (section) result[section][key2] = value;
|
|
7402
|
+
else result[key2] = value;
|
|
7403
7403
|
}
|
|
7404
7404
|
return result;
|
|
7405
7405
|
}
|
|
@@ -7676,7 +7676,7 @@ async function dockerInstall(opts = {}) {
|
|
|
7676
7676
|
"SYNKRO_CODEX_MODEL",
|
|
7677
7677
|
"SYNKRO_CONDUCTOR_MODEL",
|
|
7678
7678
|
"SYNKRO_ROUTE_MODEL"
|
|
7679
|
-
].flatMap((
|
|
7679
|
+
].flatMap((key2) => process.env[key2] ? ["-e", `${key2}=${process.env[key2]}`] : []),
|
|
7680
7680
|
// Fix-poll kill switch. Default ON in the image; a benchmark/headless run
|
|
7681
7681
|
// (e.g. sec-code-bench) sets SYNKRO_FIX_POLL=0 so ask-mode violations skip the
|
|
7682
7682
|
// interactive AskUserQuestion poll and fall through to generate-the-fix. Only
|
|
@@ -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.9";
|
|
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((resolve9, 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
|
+
resolve9(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((resolve9, 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 ? resolve9() : reject(new Error(`codex login exited with code ${code}`)));
|
|
8069
8069
|
});
|
|
8070
8070
|
}
|
|
8071
8071
|
async function setupCodexCloud(gatewayUrl, bearerToken, onStatus) {
|
|
@@ -8477,8 +8477,8 @@ function addUsage(a, b) {
|
|
|
8477
8477
|
}
|
|
8478
8478
|
function parseCodexTranscriptUsage(transcript, options = {}) {
|
|
8479
8479
|
if (!transcript) return null;
|
|
8480
|
-
const
|
|
8481
|
-
const hasCanonicalAssistant =
|
|
8480
|
+
const lines2 = transcript.split("\n");
|
|
8481
|
+
const hasCanonicalAssistant = lines2.some((line) => {
|
|
8482
8482
|
try {
|
|
8483
8483
|
const entry = JSON.parse(line);
|
|
8484
8484
|
return entry?.type === "event_msg" && entry?.payload?.type === "agent_message";
|
|
@@ -8492,8 +8492,8 @@ function parseCodexTranscriptUsage(transcript, options = {}) {
|
|
|
8492
8492
|
let previous = { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 };
|
|
8493
8493
|
let latest = null;
|
|
8494
8494
|
let sawSnapshot = false;
|
|
8495
|
-
for (let i = 0; i <
|
|
8496
|
-
const line =
|
|
8495
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
8496
|
+
const line = lines2[i].trim();
|
|
8497
8497
|
if (!line) continue;
|
|
8498
8498
|
let entry;
|
|
8499
8499
|
try {
|
|
@@ -8675,8 +8675,8 @@ function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ n
|
|
|
8675
8675
|
const entryModel = typeof message.model === "string" && message.model ? message.model : "unknown";
|
|
8676
8676
|
if (entryModel !== "<synthetic>") model = entryModel;
|
|
8677
8677
|
const day = isoDay(entry.timestamp, fallbackDay);
|
|
8678
|
-
const
|
|
8679
|
-
const row2 = rollups.get(
|
|
8678
|
+
const key2 = `${day}\0${entryModel}`;
|
|
8679
|
+
const row2 = rollups.get(key2) ?? {
|
|
8680
8680
|
day,
|
|
8681
8681
|
model: entryModel,
|
|
8682
8682
|
turns: 0,
|
|
@@ -8690,7 +8690,7 @@ function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ n
|
|
|
8690
8690
|
row2.output_tokens += counts.output_tokens;
|
|
8691
8691
|
row2.cache_creation_input_tokens += counts.cache_creation_input_tokens;
|
|
8692
8692
|
row2.cache_read_input_tokens += counts.cache_read_input_tokens;
|
|
8693
|
-
rollups.set(
|
|
8693
|
+
rollups.set(key2, row2);
|
|
8694
8694
|
turns += 1;
|
|
8695
8695
|
usage2.input_tokens += counts.input_tokens;
|
|
8696
8696
|
usage2.output_tokens += counts.output_tokens;
|
|
@@ -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((resolve9) => {
|
|
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 resolve9(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 resolve9([detected[n - 1]]);
|
|
8781
8781
|
}
|
|
8782
8782
|
console.log("Invalid choice. Try again.");
|
|
8783
|
-
|
|
8783
|
+
resolve9(ask3());
|
|
8784
8784
|
});
|
|
8785
8785
|
});
|
|
8786
8786
|
return ask3();
|
|
@@ -8803,17 +8803,17 @@ async function promptCursorApiKey(opts) {
|
|
|
8803
8803
|
return;
|
|
8804
8804
|
}
|
|
8805
8805
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
8806
|
-
const
|
|
8806
|
+
const key2 = await new Promise((resolve9) => {
|
|
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
|
+
resolve9(answer.trim());
|
|
8812
8812
|
}
|
|
8813
8813
|
);
|
|
8814
8814
|
});
|
|
8815
|
-
if (
|
|
8816
|
-
writeCursorApiKey2(
|
|
8815
|
+
if (key2) {
|
|
8816
|
+
writeCursorApiKey2(key2);
|
|
8817
8817
|
console.log(" \u2713 Cursor API key saved.");
|
|
8818
8818
|
} else {
|
|
8819
8819
|
console.log(" \u26A0 Skipped \u2014 Cursor workers will be idle. Re-run install or pass --cursor-api-key=\u2026 later.");
|
|
@@ -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((resolve9) => {
|
|
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
|
+
resolve9(a === "cloud" ? "cloud" : a === "local" ? "local" : current);
|
|
8836
8836
|
}
|
|
8837
8837
|
);
|
|
8838
8838
|
});
|
|
@@ -8986,7 +8986,7 @@ function writeConfigEnv(opts) {
|
|
|
8986
8986
|
const safeTier = sanitizeConfigValue(opts.tier ?? "pro", 32);
|
|
8987
8987
|
const safeInference = sanitizeConfigValue(opts.inference ?? "fast", 16);
|
|
8988
8988
|
const safeSynkroBin = sanitizeConfigValue(opts.synkroBin ?? "", 1024);
|
|
8989
|
-
const
|
|
8989
|
+
const lines2 = [
|
|
8990
8990
|
"# Synkro CLI config (managed by synkro install)",
|
|
8991
8991
|
"# JWT auth \u2014 the hook scripts read SYNKRO_CREDENTIALS_PATH at runtime",
|
|
8992
8992
|
"# and send Authorization: Bearer <access_token> on every gateway call.",
|
|
@@ -8994,27 +8994,27 @@ 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.9")}`
|
|
8998
8998
|
];
|
|
8999
|
-
if (safeSynkroBin)
|
|
9000
|
-
if (safeUserId)
|
|
9001
|
-
if (safeOrgId)
|
|
9002
|
-
if (safeEmail)
|
|
8999
|
+
if (safeSynkroBin) lines2.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
9000
|
+
if (safeUserId) lines2.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
9001
|
+
if (safeOrgId) lines2.push(`SYNKRO_ORG_ID=${shellQuoteSingle2(safeOrgId)}`);
|
|
9002
|
+
if (safeEmail) lines2.push(`SYNKRO_EMAIL=${shellQuoteSingle2(safeEmail)}`);
|
|
9003
9003
|
if (opts.transcriptConsent !== void 0) {
|
|
9004
|
-
|
|
9004
|
+
lines2.push(`SYNKRO_TRANSCRIPT_CONSENT=${shellQuoteSingle2(opts.transcriptConsent ? "yes" : "no")}`);
|
|
9005
9005
|
}
|
|
9006
|
-
if (opts.transcriptConsentCC !== void 0)
|
|
9007
|
-
if (opts.transcriptConsentCursor !== void 0)
|
|
9008
|
-
if (opts.transcriptConsentCodex !== void 0)
|
|
9009
|
-
|
|
9006
|
+
if (opts.transcriptConsentCC !== void 0) lines2.push(`SYNKRO_TRANSCRIPT_CONSENT_CC=${shellQuoteSingle2(opts.transcriptConsentCC ? "yes" : "no")}`);
|
|
9007
|
+
if (opts.transcriptConsentCursor !== void 0) lines2.push(`SYNKRO_TRANSCRIPT_CONSENT_CURSOR=${shellQuoteSingle2(opts.transcriptConsentCursor ? "yes" : "no")}`);
|
|
9008
|
+
if (opts.transcriptConsentCodex !== void 0) lines2.push(`SYNKRO_TRANSCRIPT_CONSENT_CODEX=${shellQuoteSingle2(opts.transcriptConsentCodex ? "yes" : "no")}`);
|
|
9009
|
+
lines2.push(`SYNKRO_LOCAL_INFERENCE=${shellQuoteSingle2(opts.localInference ? "yes" : "no")}`);
|
|
9010
9010
|
const safeMode = sanitizeConfigValue(opts.deploymentMode ?? "docker", 16);
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
writeFileSync17(CONFIG_PATH4,
|
|
9011
|
+
lines2.push(`SYNKRO_DEPLOYMENT_MODE=${shellQuoteSingle2(safeMode)}`);
|
|
9012
|
+
lines2.push(`SYNKRO_GRADING_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.gradingMode ?? "local", 16))}`);
|
|
9013
|
+
lines2.push(`SYNKRO_STORAGE_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.storageMode ?? "local", 16))}`);
|
|
9014
|
+
lines2.push(`SYNKRO_DEPLOY_LOCATION=${shellQuoteSingle2(sanitizeConfigValue(opts.deployLocation ?? "local", 16))}`);
|
|
9015
|
+
lines2.push(`SYNKRO_HOOK_MODE=${shellQuoteSingle2(sanitizeConfigValue(opts.hookMode ?? "stub", 8))}`);
|
|
9016
|
+
lines2.push("");
|
|
9017
|
+
writeFileSync17(CONFIG_PATH4, lines2.join("\n"), "utf-8");
|
|
9018
9018
|
chmodSync5(CONFIG_PATH4, 384);
|
|
9019
9019
|
}
|
|
9020
9020
|
function persistedTranscriptConsent(source) {
|
|
@@ -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.9",
|
|
9745
9745
|
agents_detected: agents.map((a) => a.kind),
|
|
9746
9746
|
with_github: false,
|
|
9747
9747
|
with_local_cc: false,
|
|
@@ -10303,15 +10303,15 @@ function parseSynkroToml2(raw) {
|
|
|
10303
10303
|
}
|
|
10304
10304
|
const eq = line.indexOf("=");
|
|
10305
10305
|
if (eq === -1) continue;
|
|
10306
|
-
const
|
|
10306
|
+
const key2 = line.slice(0, eq).trim().replace(/^["']|["']$/g, "");
|
|
10307
10307
|
let valRaw = line.slice(eq + 1).trim();
|
|
10308
10308
|
if (!valRaw.startsWith('"') && !valRaw.startsWith("'") && !valRaw.startsWith("[")) {
|
|
10309
10309
|
const h = valRaw.indexOf("#");
|
|
10310
10310
|
if (h !== -1) valRaw = valRaw.slice(0, h).trim();
|
|
10311
10311
|
}
|
|
10312
10312
|
const value = parseTomlValue2(valRaw);
|
|
10313
|
-
if (section) result[section][
|
|
10314
|
-
else result[
|
|
10313
|
+
if (section) result[section][key2] = value;
|
|
10314
|
+
else result[key2] = value;
|
|
10315
10315
|
}
|
|
10316
10316
|
return result;
|
|
10317
10317
|
}
|
|
@@ -10592,8 +10592,8 @@ async function syncSkillFiles() {
|
|
|
10592
10592
|
console.log(` \u2298 skill ${source}: empty file, skipped`);
|
|
10593
10593
|
return null;
|
|
10594
10594
|
}
|
|
10595
|
-
const
|
|
10596
|
-
console.log(` \u2192 read ${source} (${
|
|
10595
|
+
const lines2 = content.split("\n").length;
|
|
10596
|
+
console.log(` \u2192 read ${source} (${lines2} lines, ${(content.length / 1024).toFixed(1)} KB)`);
|
|
10597
10597
|
return { source, content };
|
|
10598
10598
|
}).filter(Boolean);
|
|
10599
10599
|
if (tasks.length === 0) return;
|
|
@@ -10872,10 +10872,10 @@ function extractSessionInsights(projectsDir) {
|
|
|
10872
10872
|
const filePath = join19(projectsDir, file);
|
|
10873
10873
|
try {
|
|
10874
10874
|
const content = readFileSync21(filePath, "utf-8");
|
|
10875
|
-
const
|
|
10876
|
-
for (let i = 0; i <
|
|
10875
|
+
const lines2 = content.split("\n").filter(Boolean);
|
|
10876
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
10877
10877
|
try {
|
|
10878
|
-
const entry = JSON.parse(
|
|
10878
|
+
const entry = JSON.parse(lines2[i]);
|
|
10879
10879
|
if (entry.type === "user" && typeof entry.message?.content === "string" && entry.message.content.startsWith("This session is being continued")) {
|
|
10880
10880
|
insights.push({
|
|
10881
10881
|
session_id: sessionId,
|
|
@@ -10888,9 +10888,9 @@ function extractSessionInsights(projectsDir) {
|
|
|
10888
10888
|
}
|
|
10889
10889
|
}
|
|
10890
10890
|
const userMessages = [];
|
|
10891
|
-
for (let i =
|
|
10891
|
+
for (let i = lines2.length - 1; i >= 0 && userMessages.length < 20; i--) {
|
|
10892
10892
|
try {
|
|
10893
|
-
const entry = JSON.parse(
|
|
10893
|
+
const entry = JSON.parse(lines2[i]);
|
|
10894
10894
|
if (entry.type === "user") {
|
|
10895
10895
|
const text = typeof entry.message?.content === "string" ? entry.message.content : Array.isArray(entry.message?.content) ? entry.message.content.map((b) => b.text ?? b).filter((t) => typeof t === "string").join(" ") : null;
|
|
10896
10896
|
if (text && text.length > 10 && text.length < 2e3 && !text.startsWith("This session is being continued")) {
|
|
@@ -10949,13 +10949,13 @@ function extractTextContent(content) {
|
|
|
10949
10949
|
function getCodexTranscriptFiles(repo) {
|
|
10950
10950
|
const sessionsDir = join19(process.env.CODEX_HOME || join19(homedir21(), ".codex"), "sessions");
|
|
10951
10951
|
if (!existsSync22(sessionsDir)) return [];
|
|
10952
|
-
let
|
|
10952
|
+
let relative3 = [];
|
|
10953
10953
|
try {
|
|
10954
|
-
|
|
10954
|
+
relative3 = readdirSync4(sessionsDir, { recursive: true, encoding: "utf-8" });
|
|
10955
10955
|
} catch {
|
|
10956
10956
|
return [];
|
|
10957
10957
|
}
|
|
10958
|
-
return
|
|
10958
|
+
return relative3.filter((p) => p.endsWith(".jsonl")).map((p) => join19(sessionsDir, p)).filter((filePath) => {
|
|
10959
10959
|
try {
|
|
10960
10960
|
const first = readFileSync21(filePath, "utf-8").split("\n", 1)[0];
|
|
10961
10961
|
const meta = JSON.parse(first);
|
|
@@ -10972,11 +10972,11 @@ function isJsonSyntaxError(error) {
|
|
|
10972
10972
|
}
|
|
10973
10973
|
function parseCodexTranscriptFile(filePath) {
|
|
10974
10974
|
const transcript = readFileSync21(filePath, "utf-8");
|
|
10975
|
-
const
|
|
10975
|
+
const lines2 = transcript.split("\n");
|
|
10976
10976
|
const transcriptUsage = parseCodexTranscriptUsage(transcript);
|
|
10977
10977
|
let sessionId = "";
|
|
10978
10978
|
let model = "";
|
|
10979
|
-
for (const line of
|
|
10979
|
+
for (const line of lines2) {
|
|
10980
10980
|
try {
|
|
10981
10981
|
const entry = JSON.parse(line);
|
|
10982
10982
|
if (entry.type === "session_meta") {
|
|
@@ -11060,11 +11060,11 @@ function isSafeConvId(id) {
|
|
|
11060
11060
|
}
|
|
11061
11061
|
function parseCursorTranscriptFile(filePath) {
|
|
11062
11062
|
const content = readFileSync21(filePath, "utf-8");
|
|
11063
|
-
const
|
|
11063
|
+
const lines2 = content.split("\n").filter(Boolean);
|
|
11064
11064
|
const messages = [];
|
|
11065
|
-
for (let i = 0; i <
|
|
11065
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
11066
11066
|
try {
|
|
11067
|
-
const entry = JSON.parse(
|
|
11067
|
+
const entry = JSON.parse(lines2[i]);
|
|
11068
11068
|
const role = entry.role || entry.message?.role;
|
|
11069
11069
|
if (role !== "user" && role !== "assistant") continue;
|
|
11070
11070
|
const text = extractTextContent(entry.message?.content ?? entry.content);
|
|
@@ -11124,11 +11124,11 @@ async function syncCursorTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
11124
11124
|
}
|
|
11125
11125
|
function parseTranscriptFile(filePath) {
|
|
11126
11126
|
const content = readFileSync21(filePath, "utf-8");
|
|
11127
|
-
const
|
|
11127
|
+
const lines2 = content.split("\n").filter(Boolean);
|
|
11128
11128
|
const messages = [];
|
|
11129
|
-
for (let i = 0; i <
|
|
11129
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
11130
11130
|
try {
|
|
11131
|
-
const entry = JSON.parse(
|
|
11131
|
+
const entry = JSON.parse(lines2[i]);
|
|
11132
11132
|
if (entry.type !== "user" && entry.type !== "assistant") continue;
|
|
11133
11133
|
const msg = {
|
|
11134
11134
|
message_index: i,
|
|
@@ -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((resolve9) => {
|
|
12034
12034
|
rl.question(" Type 'yes' to wipe everything (anything else cancels): ", (answer) => {
|
|
12035
12035
|
rl.close();
|
|
12036
|
-
|
|
12036
|
+
resolve9(answer.trim().toLowerCase() === "yes");
|
|
12037
12037
|
});
|
|
12038
12038
|
});
|
|
12039
12039
|
}
|
|
@@ -12194,8 +12194,8 @@ function readRecentTurns(n = 20) {
|
|
|
12194
12194
|
const size = statSync3(TURN_LOG_PATH).size;
|
|
12195
12195
|
if (size === 0) return [];
|
|
12196
12196
|
const text = readFileSync23(TURN_LOG_PATH, "utf-8");
|
|
12197
|
-
const
|
|
12198
|
-
const lastN =
|
|
12197
|
+
const lines2 = text.split("\n").filter(Boolean);
|
|
12198
|
+
const lastN = lines2.slice(-n).reverse();
|
|
12199
12199
|
return lastN.map((line) => {
|
|
12200
12200
|
try {
|
|
12201
12201
|
return JSON.parse(line);
|
|
@@ -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((resolve9, 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
|
+
resolve9(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((resolve9) => {
|
|
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
|
+
resolve9(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((resolve9, 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", () => resolve9(Buffer.concat(chunks).toString("utf-8")));
|
|
12384
12384
|
process.stdin.on("error", reject);
|
|
12385
12385
|
});
|
|
12386
12386
|
}
|
|
@@ -12467,9 +12467,9 @@ async function fetchOrgRules(gatewayUrl, apiKey) {
|
|
|
12467
12467
|
function applyLiteralMatchNegative(rules, file) {
|
|
12468
12468
|
if (!file.patch) return [];
|
|
12469
12469
|
const findings = [];
|
|
12470
|
-
const
|
|
12470
|
+
const lines2 = file.patch.split("\n");
|
|
12471
12471
|
let currentNewLine = 0;
|
|
12472
|
-
for (const line of
|
|
12472
|
+
for (const line of lines2) {
|
|
12473
12473
|
if (line.startsWith("@@")) {
|
|
12474
12474
|
const m = line.match(/\+(\d+)(?:,\d+)?/);
|
|
12475
12475
|
if (m) currentNewLine = parseInt(m[1], 10);
|
|
@@ -12597,12 +12597,12 @@ async function fetchScanContext(gatewayUrl, apiKey, repo, prNumber, sha) {
|
|
|
12597
12597
|
}
|
|
12598
12598
|
function getFileDiffWithLines(file) {
|
|
12599
12599
|
if (!file.patch) return { hunks: "", newFileLineMap: /* @__PURE__ */ new Map() };
|
|
12600
|
-
const
|
|
12600
|
+
const lines2 = file.patch.split("\n");
|
|
12601
12601
|
const annotated = [];
|
|
12602
12602
|
const lineMap = /* @__PURE__ */ new Map();
|
|
12603
12603
|
let currentNewLine = 0;
|
|
12604
12604
|
let patchIndex = 0;
|
|
12605
|
-
for (const line of
|
|
12605
|
+
for (const line of lines2) {
|
|
12606
12606
|
patchIndex++;
|
|
12607
12607
|
if (line.startsWith("@@")) {
|
|
12608
12608
|
const match = line.match(/\+(\d+)(?:,\d+)?/);
|
|
@@ -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((resolve9) => {
|
|
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
|
+
resolve9({ 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
|
+
resolve9({ findings, latencyMs });
|
|
12685
12685
|
} catch (parseErr) {
|
|
12686
12686
|
console.warn(` failed to parse claude response: ${stdout.slice(0, 300)}`);
|
|
12687
|
-
|
|
12687
|
+
resolve9({ 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((resolve9) => {
|
|
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
|
+
resolve9(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
|
+
resolve9({ summary: review.summary || "", comments, severity: maxSeverity });
|
|
12785
12785
|
} catch {
|
|
12786
12786
|
console.warn(` failed to parse opus response, using fallback`);
|
|
12787
|
-
|
|
12787
|
+
resolve9(fallbackReview(findings));
|
|
12788
12788
|
}
|
|
12789
12789
|
});
|
|
12790
12790
|
});
|
|
@@ -12792,15 +12792,15 @@ function spawnOpusConsolidator(findings, claudeToken) {
|
|
|
12792
12792
|
function fallbackReview(findings) {
|
|
12793
12793
|
const grouped = /* @__PURE__ */ new Map();
|
|
12794
12794
|
for (const f of findings) {
|
|
12795
|
-
const
|
|
12796
|
-
if (!grouped.has(
|
|
12797
|
-
grouped.get(
|
|
12795
|
+
const key2 = `${f.file}::${f.category}`;
|
|
12796
|
+
if (!grouped.has(key2)) grouped.set(key2, []);
|
|
12797
|
+
grouped.get(key2).push(f);
|
|
12798
12798
|
}
|
|
12799
12799
|
const comments = [];
|
|
12800
12800
|
for (const [, group] of grouped) {
|
|
12801
12801
|
const first = group[0];
|
|
12802
|
-
const
|
|
12803
|
-
const linesStr =
|
|
12802
|
+
const lines2 = group.map((f) => f.line);
|
|
12803
|
+
const linesStr = lines2.length > 1 ? `Lines ${lines2.join(", ")}` : `Line ${lines2[0]}`;
|
|
12804
12804
|
const severityEmoji = first.severity === "critical" ? "\u{1F534}" : first.severity === "high" ? "\u{1F7E0}" : first.severity === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
12805
12805
|
comments.push({
|
|
12806
12806
|
path: first.file,
|
|
@@ -13254,10 +13254,10 @@ function stopTask(channel = CHANNEL_PRIMARY) {
|
|
|
13254
13254
|
t = findTask(channel);
|
|
13255
13255
|
}
|
|
13256
13256
|
}
|
|
13257
|
-
function tailLogs(
|
|
13257
|
+
function tailLogs(lines2 = 80, channel = CHANNEL_PRIMARY) {
|
|
13258
13258
|
const t = findTask(channel);
|
|
13259
13259
|
if (!t) return `(no ${channel.taskLabel} task)`;
|
|
13260
|
-
const r = spawnSync9("pueue", ["log", "--lines", String(
|
|
13260
|
+
const r = spawnSync9("pueue", ["log", "--lines", String(lines2), String(t.id)], { encoding: "utf-8" });
|
|
13261
13261
|
return r.stdout || r.stderr || "(no output)";
|
|
13262
13262
|
}
|
|
13263
13263
|
function ensureRunning(opts = {}) {
|
|
@@ -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((resolve9) => {
|
|
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
|
+
resolve9(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((resolve9) => {
|
|
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
|
+
resolve9();
|
|
13836
13836
|
};
|
|
13837
13837
|
process.on("SIGINT", onSigint);
|
|
13838
13838
|
});
|
|
@@ -14050,7 +14050,7 @@ function extractToolResultText(content, e) {
|
|
|
14050
14050
|
function parseSession(file, seenStableIds) {
|
|
14051
14051
|
const { filePath, sessionId, parentSessionId } = file;
|
|
14052
14052
|
const transcript = readFileSync27(filePath, "utf-8");
|
|
14053
|
-
const
|
|
14053
|
+
const lines2 = transcript.split("\n").filter(Boolean);
|
|
14054
14054
|
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
14055
14055
|
transcript,
|
|
14056
14056
|
statSync4(filePath).mtime.toISOString().slice(0, 10),
|
|
@@ -14062,10 +14062,10 @@ function parseSession(file, seenStableIds) {
|
|
|
14062
14062
|
const messages = [];
|
|
14063
14063
|
const actions = [];
|
|
14064
14064
|
let step = 0;
|
|
14065
|
-
for (let i = 0; i <
|
|
14065
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
14066
14066
|
let e;
|
|
14067
14067
|
try {
|
|
14068
|
-
e = JSON.parse(
|
|
14068
|
+
e = JSON.parse(lines2[i]);
|
|
14069
14069
|
} catch {
|
|
14070
14070
|
continue;
|
|
14071
14071
|
}
|
|
@@ -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((resolve9) => rl.question(q, (a) => {
|
|
14125
14125
|
rl.close();
|
|
14126
|
-
|
|
14126
|
+
resolve9(/^y(es)?$/i.test(a.trim()));
|
|
14127
14127
|
}));
|
|
14128
14128
|
}
|
|
14129
14129
|
async function importCommand() {
|
|
@@ -14261,8 +14261,8 @@ function computeDigest(canonical2) {
|
|
|
14261
14261
|
}
|
|
14262
14262
|
function verifySignature(digest, signatureB64, publicKeyPem) {
|
|
14263
14263
|
try {
|
|
14264
|
-
const
|
|
14265
|
-
return crypto.verify(null, Buffer.from(digest, "utf8"),
|
|
14264
|
+
const key2 = crypto.createPublicKey(publicKeyPem);
|
|
14265
|
+
return crypto.verify(null, Buffer.from(digest, "utf8"), key2, Buffer.from(signatureB64, "base64"));
|
|
14266
14266
|
} catch {
|
|
14267
14267
|
return false;
|
|
14268
14268
|
}
|
|
@@ -14654,6 +14654,7 @@ function buildSpawnAgent(opts) {
|
|
|
14654
14654
|
["tmux", "set-option", "-t", session, "-q", "@synkro_harness", opts.harness],
|
|
14655
14655
|
["tmux", "set-option", "-t", session, "-q", "@synkro_space", opts.space],
|
|
14656
14656
|
["tmux", "set-option", "-t", session, "-q", "@synkro_backend", opts.backend],
|
|
14657
|
+
["tmux", "set-option", "-t", session, "-q", "@synkro_mode", opts.mode || "native"],
|
|
14657
14658
|
// Keep the pane visible after exit so the sidebar can render 'done'
|
|
14658
14659
|
// instead of the agent silently vanishing.
|
|
14659
14660
|
["tmux", "set-option", "-t", session, "remain-on-exit", "on"]
|
|
@@ -14675,7 +14676,7 @@ function buildAgentSnapshot() {
|
|
|
14675
14676
|
return ["sh", "-c", script];
|
|
14676
14677
|
}
|
|
14677
14678
|
function parseAgentSnapshot(output) {
|
|
14678
|
-
const
|
|
14679
|
+
const lines2 = String(output || "").split("\n");
|
|
14679
14680
|
const captures = /* @__PURE__ */ new Map();
|
|
14680
14681
|
const listLines = [];
|
|
14681
14682
|
let current = null;
|
|
@@ -14683,7 +14684,7 @@ function parseAgentSnapshot(output) {
|
|
|
14683
14684
|
const flush2 = () => {
|
|
14684
14685
|
if (current) captures.set(current, chunk.join("\n"));
|
|
14685
14686
|
};
|
|
14686
|
-
for (const line of
|
|
14687
|
+
for (const line of lines2) {
|
|
14687
14688
|
if (line.startsWith("===")) {
|
|
14688
14689
|
flush2();
|
|
14689
14690
|
current = line.slice(3).trim();
|
|
@@ -14962,12 +14963,12 @@ async function discoverAgents(runner, backend, memory) {
|
|
|
14962
14963
|
}
|
|
14963
14964
|
function offlineAgents(live, records) {
|
|
14964
14965
|
const alive = new Set(live.map((agent) => agent.session));
|
|
14965
|
-
return records.filter((
|
|
14966
|
-
name:
|
|
14967
|
-
session:
|
|
14968
|
-
harness:
|
|
14969
|
-
space:
|
|
14970
|
-
backend:
|
|
14966
|
+
return records.filter((record2) => !alive.has(record2.session)).map((record2) => ({
|
|
14967
|
+
name: record2.name,
|
|
14968
|
+
session: record2.session,
|
|
14969
|
+
harness: record2.harness,
|
|
14970
|
+
space: record2.space,
|
|
14971
|
+
backend: record2.backend,
|
|
14971
14972
|
status: "offline"
|
|
14972
14973
|
}));
|
|
14973
14974
|
}
|
|
@@ -15004,8 +15005,8 @@ function saveRecords(records) {
|
|
|
15004
15005
|
} catch {
|
|
15005
15006
|
}
|
|
15006
15007
|
}
|
|
15007
|
-
function recordSession(
|
|
15008
|
-
saveRecords([...loadRecords().filter((row2) => row2.session !==
|
|
15008
|
+
function recordSession(record2) {
|
|
15009
|
+
saveRecords([...loadRecords().filter((row2) => row2.session !== record2.session), record2]);
|
|
15009
15010
|
}
|
|
15010
15011
|
function forgetSession(session) {
|
|
15011
15012
|
saveRecords(loadRecords().filter((row2) => row2.session !== session));
|
|
@@ -15040,11 +15041,11 @@ function lastAgentFor(space) {
|
|
|
15040
15041
|
return loadLastAgents()[String(space || "").replace(/\/+$/, "")] || "";
|
|
15041
15042
|
}
|
|
15042
15043
|
function rememberLastAgent(space, session) {
|
|
15043
|
-
const
|
|
15044
|
-
if (!
|
|
15044
|
+
const key2 = String(space || "").replace(/\/+$/, "");
|
|
15045
|
+
if (!key2 || !session) return;
|
|
15045
15046
|
try {
|
|
15046
15047
|
mkdirSync21(dirname10(LAST_AGENT_FILE), { recursive: true });
|
|
15047
|
-
writeFileSync24(LAST_AGENT_FILE, JSON.stringify({ ...loadLastAgents(), [
|
|
15048
|
+
writeFileSync24(LAST_AGENT_FILE, JSON.stringify({ ...loadLastAgents(), [key2]: session }, null, 2));
|
|
15048
15049
|
} catch {
|
|
15049
15050
|
}
|
|
15050
15051
|
}
|
|
@@ -15083,9 +15084,17 @@ var init_manifest = __esm({
|
|
|
15083
15084
|
});
|
|
15084
15085
|
|
|
15085
15086
|
// cli/ui/launch.ts
|
|
15086
|
-
import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync25 } from "fs";
|
|
15087
|
+
import { mkdirSync as mkdirSync22, statSync as statSync5, writeFileSync as writeFileSync25 } from "fs";
|
|
15087
15088
|
import { homedir as homedir34 } from "os";
|
|
15088
15089
|
import { join as join34 } from "path";
|
|
15090
|
+
function buildStamp(bootPath) {
|
|
15091
|
+
try {
|
|
15092
|
+
const stat = statSync5(bootPath);
|
|
15093
|
+
return String(stat.size) + ":" + String(Math.floor(stat.mtimeMs));
|
|
15094
|
+
} catch {
|
|
15095
|
+
return bootPath;
|
|
15096
|
+
}
|
|
15097
|
+
}
|
|
15089
15098
|
function sidebarColumns(totalColumns) {
|
|
15090
15099
|
return String(Math.max(20, Math.min(34, Math.round(totalColumns * 0.24))));
|
|
15091
15100
|
}
|
|
@@ -15157,6 +15166,7 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
|
|
|
15157
15166
|
const tabPopup = "display-popup -E -w 60 -h 18 -S fg=colour111 " + shellQuote3(tabLauncher);
|
|
15158
15167
|
const dispatch = 'if-shell -F "#{==:#{mouse_status_range},tabmenu}" "' + tabPopup + '" "select-window -t="';
|
|
15159
15168
|
const style = [
|
|
15169
|
+
["set-option", "-t", UI_SESSION, "-q", "@synkro_build", buildStamp(bootPath)],
|
|
15160
15170
|
// Mouse: drag the pane border to resize the sidebar, click a pane to
|
|
15161
15171
|
// focus it, click rows/chips. Without this the fixed split reads as
|
|
15162
15172
|
// "blocked in".
|
|
@@ -15190,6 +15200,38 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
|
|
|
15190
15200
|
for (const argv of style) await run(HOST, ["tmux", ...argv]);
|
|
15191
15201
|
for (const argv of buildClipboardBindings(UI_SESSION)) await run(HOST, argv);
|
|
15192
15202
|
}
|
|
15203
|
+
function parseUiPanes(output) {
|
|
15204
|
+
return String(output || "").split("\n").map((line) => line.split("|")).filter((cols) => cols.length >= 4).map(([window, pane, width, ...command]) => ({
|
|
15205
|
+
window,
|
|
15206
|
+
pane,
|
|
15207
|
+
width: Number(width) || 0,
|
|
15208
|
+
command: command.join("|")
|
|
15209
|
+
})).filter((row2) => Boolean(row2.window && row2.pane));
|
|
15210
|
+
}
|
|
15211
|
+
async function refreshUiShell(bootPath, repoCwd) {
|
|
15212
|
+
const expected = buildStamp(bootPath);
|
|
15213
|
+
const current = await run(HOST, ["tmux", "show-options", "-t", UI_SESSION, "-v", "@synkro_build"]);
|
|
15214
|
+
if (current.ok && current.stdout.trim() === expected) return;
|
|
15215
|
+
const listed = await run(HOST, [
|
|
15216
|
+
"tmux",
|
|
15217
|
+
"list-panes",
|
|
15218
|
+
"-s",
|
|
15219
|
+
"-t",
|
|
15220
|
+
UI_SESSION,
|
|
15221
|
+
"-F",
|
|
15222
|
+
"#{window_id}|#{pane_id}|#{pane_width}|#{pane_start_command}"
|
|
15223
|
+
]);
|
|
15224
|
+
if (!listed.ok) return;
|
|
15225
|
+
const panes = parseUiPanes(listed.stdout);
|
|
15226
|
+
const sidebars = panes.filter((row2) => row2.command.includes("--sidebar"));
|
|
15227
|
+
if (sidebars.length === 0) return;
|
|
15228
|
+
await styleOuterSession(bootPath, repoCwd, sidebars[0].width || 30);
|
|
15229
|
+
for (const sidebar of sidebars) {
|
|
15230
|
+
const center = panes.find((row2) => row2.window === sidebar.window && row2.pane !== sidebar.pane);
|
|
15231
|
+
if (!center) continue;
|
|
15232
|
+
await run(HOST, ["tmux", "respawn-pane", "-k", "-t", sidebar.pane, sidebarCommand(bootPath, center.pane, repoCwd)]);
|
|
15233
|
+
}
|
|
15234
|
+
}
|
|
15193
15235
|
async function buildTab(bootPath, repoCwd, spec) {
|
|
15194
15236
|
await run(HOST, ["tmux", "set-option", "-g", "history-limit", "50000"]);
|
|
15195
15237
|
let windowTarget;
|
|
@@ -15282,6 +15324,8 @@ async function launchUi(bootPath, repoCwd) {
|
|
|
15282
15324
|
rememberSpace(repoCwd);
|
|
15283
15325
|
if (!await uiSessionExists()) {
|
|
15284
15326
|
await buildTab(bootPath, repoCwd, { cwd: repoCwd, center: makeTerminalCommand(bootPath), focus: "sidebar" });
|
|
15327
|
+
} else {
|
|
15328
|
+
await refreshUiShell(bootPath, repoCwd);
|
|
15285
15329
|
}
|
|
15286
15330
|
await pruneCollapsedClients();
|
|
15287
15331
|
return runInherit(process.env.TMUX ? ["tmux", "switch-client", "-t", UI_SESSION] : ["tmux", "attach-session", "-t", UI_SESSION]);
|
|
@@ -15338,10 +15382,10 @@ function row(selected, width, content) {
|
|
|
15338
15382
|
return STYLE.select + body.split(STYLE.reset).join(STYLE.reset + STYLE.select) + STYLE.reset;
|
|
15339
15383
|
}
|
|
15340
15384
|
function stripForPad(text, width) {
|
|
15341
|
-
let
|
|
15385
|
+
let visible2 = 0;
|
|
15342
15386
|
let out = "";
|
|
15343
15387
|
let index = 0;
|
|
15344
|
-
while (index < text.length &&
|
|
15388
|
+
while (index < text.length && visible2 < width) {
|
|
15345
15389
|
if (text.startsWith(ESC, index)) {
|
|
15346
15390
|
const end = text.indexOf("m", index);
|
|
15347
15391
|
if (end === -1) break;
|
|
@@ -15350,13 +15394,13 @@ function stripForPad(text, width) {
|
|
|
15350
15394
|
} else {
|
|
15351
15395
|
out += text[index];
|
|
15352
15396
|
index += 1;
|
|
15353
|
-
|
|
15397
|
+
visible2 += 1;
|
|
15354
15398
|
}
|
|
15355
15399
|
}
|
|
15356
|
-
return out + " ".repeat(Math.max(0, width -
|
|
15400
|
+
return out + " ".repeat(Math.max(0, width - visible2));
|
|
15357
15401
|
}
|
|
15358
15402
|
function visibleLength(text) {
|
|
15359
|
-
let
|
|
15403
|
+
let visible2 = 0;
|
|
15360
15404
|
let index = 0;
|
|
15361
15405
|
while (index < text.length) {
|
|
15362
15406
|
if (text.startsWith(ESC, index)) {
|
|
@@ -15364,11 +15408,11 @@ function visibleLength(text) {
|
|
|
15364
15408
|
if (end === -1) break;
|
|
15365
15409
|
index = end + 1;
|
|
15366
15410
|
} else {
|
|
15367
|
-
|
|
15411
|
+
visible2 += 1;
|
|
15368
15412
|
index += 1;
|
|
15369
15413
|
}
|
|
15370
15414
|
}
|
|
15371
|
-
return
|
|
15415
|
+
return visible2;
|
|
15372
15416
|
}
|
|
15373
15417
|
function splitRow(width, left, right) {
|
|
15374
15418
|
const gap = Math.max(1, width - 2 - visibleLength(left) - visibleLength(right));
|
|
@@ -15380,10 +15424,10 @@ function windowAround(count, selected, capacity) {
|
|
|
15380
15424
|
return { start, end: start + capacity };
|
|
15381
15425
|
}
|
|
15382
15426
|
function renderCollapsed(state, width, height) {
|
|
15383
|
-
const
|
|
15427
|
+
const lines2 = [];
|
|
15384
15428
|
const targets = [];
|
|
15385
15429
|
const push2 = (text, target = null) => {
|
|
15386
|
-
|
|
15430
|
+
lines2.push(stripForPad(" " + text, width));
|
|
15387
15431
|
targets.push(target);
|
|
15388
15432
|
};
|
|
15389
15433
|
push2("");
|
|
@@ -15400,16 +15444,16 @@ function renderCollapsed(state, width, height) {
|
|
|
15400
15444
|
});
|
|
15401
15445
|
push2("");
|
|
15402
15446
|
push2(STYLE.dim + "+" + STYLE.reset, { kind: "new" });
|
|
15403
|
-
while (
|
|
15447
|
+
while (lines2.length < height - 1) push2("");
|
|
15404
15448
|
push2(STYLE.dim + "\u203A\u203A" + STYLE.reset, { kind: "collapse" });
|
|
15405
|
-
return { lines:
|
|
15449
|
+
return { lines: lines2.slice(0, height), targets: targets.slice(0, height) };
|
|
15406
15450
|
}
|
|
15407
15451
|
function renderLayout(state, width = 30, height = 40) {
|
|
15408
15452
|
if (state.collapsed) return renderCollapsed(state, width, height);
|
|
15409
|
-
const
|
|
15453
|
+
const lines2 = [];
|
|
15410
15454
|
const targets = [];
|
|
15411
15455
|
const push2 = (line, target = null) => {
|
|
15412
|
-
|
|
15456
|
+
lines2.push(line);
|
|
15413
15457
|
targets.push(target);
|
|
15414
15458
|
};
|
|
15415
15459
|
const chrome = 8;
|
|
@@ -15431,7 +15475,7 @@ function renderLayout(state, width = 30, height = 40) {
|
|
|
15431
15475
|
push2(row(selected, width, " " + STYLE.branch + clip(space.branch, width - 6 - (space.track ? space.track.length + 1 : 0)) + STYLE.reset + drift), target);
|
|
15432
15476
|
});
|
|
15433
15477
|
if (state.spaces.length === 0) push2(row(false, width, STYLE.dim + "no spaces found" + STYLE.reset));
|
|
15434
|
-
while (
|
|
15478
|
+
while (lines2.length < 3 + spacesCapacity * 2) push2(pad("", width));
|
|
15435
15479
|
push2("");
|
|
15436
15480
|
push2(splitRow(width, "new", "menu"), { kind: "new" });
|
|
15437
15481
|
push2(STYLE.dim + "\u2500".repeat(Math.max(0, width)) + STYLE.reset);
|
|
@@ -15476,13 +15520,13 @@ function renderLayout(state, width = 30, height = 40) {
|
|
|
15476
15520
|
push2("");
|
|
15477
15521
|
push2(row(false, width, STYLE.blocked + "\u26D4 needs consent" + STYLE.reset));
|
|
15478
15522
|
for (const action of actionsForAsk(selectedAgent.ask)) {
|
|
15479
|
-
const
|
|
15480
|
-
push2(row(false, width, STYLE.dim + " " +
|
|
15523
|
+
const key2 = action === "track" ? "g" : action === "skip" ? "s" : "y";
|
|
15524
|
+
push2(row(false, width, STYLE.dim + " " + key2 + " \u2014 " + action + STYLE.reset));
|
|
15481
15525
|
}
|
|
15482
15526
|
}
|
|
15483
|
-
while (
|
|
15527
|
+
while (lines2.length < height - 1) push2(pad("", width));
|
|
15484
15528
|
push2(stripForPad(pad("", width - 3) + STYLE.dim + "\u2039\u2039 " + STYLE.reset, width), { kind: "collapse" });
|
|
15485
|
-
return { lines:
|
|
15529
|
+
return { lines: lines2.slice(0, height), targets: targets.slice(0, height) };
|
|
15486
15530
|
}
|
|
15487
15531
|
function nextSelection(state, spaces, agents, delta) {
|
|
15488
15532
|
const flat = state.section === "spaces" ? state.spaceIndex : spaces + state.agentIndex;
|
|
@@ -15631,8 +15675,8 @@ async function spawnAgent(info, request) {
|
|
|
15631
15675
|
if (request.backend === "container") {
|
|
15632
15676
|
cwd = request.cwd.startsWith(CONTAINER_WORK) ? request.cwd : await provisionContainerWorkspace(runner, slug);
|
|
15633
15677
|
}
|
|
15634
|
-
const command = harnessCommand(request);
|
|
15635
|
-
for (const argv of buildSpawnAgent({ name: slug, cwd, command, harness: request.harness, space: cwd, backend: request.backend })) {
|
|
15678
|
+
const command = request.command || harnessCommand(request);
|
|
15679
|
+
for (const argv of buildSpawnAgent({ name: slug, cwd, command, harness: request.harness, space: cwd, backend: request.backend, mode: request.mode })) {
|
|
15636
15680
|
const result = await run(runner, argv);
|
|
15637
15681
|
if (!result.ok && argv[1] === "new-session") {
|
|
15638
15682
|
return { ok: false, session, error: result.stderr.trim() || "tmux new-session failed" };
|
|
@@ -15646,7 +15690,8 @@ async function spawnAgent(info, request) {
|
|
|
15646
15690
|
harness: request.harness,
|
|
15647
15691
|
space: cwd,
|
|
15648
15692
|
spaceName: request.spaceName,
|
|
15649
|
-
backend: request.backend
|
|
15693
|
+
backend: request.backend,
|
|
15694
|
+
mode: request.mode || "native"
|
|
15650
15695
|
});
|
|
15651
15696
|
return { ok: true, session };
|
|
15652
15697
|
}
|
|
@@ -16046,45 +16091,45 @@ async function runSidebar() {
|
|
|
16046
16091
|
}
|
|
16047
16092
|
await activate(target);
|
|
16048
16093
|
}
|
|
16049
|
-
async function handleKey(
|
|
16050
|
-
if (
|
|
16051
|
-
else if (
|
|
16052
|
-
else if (
|
|
16053
|
-
else if (
|
|
16094
|
+
async function handleKey(key2) {
|
|
16095
|
+
if (key2 === " ") state.section = state.section === "spaces" ? "agents" : "spaces";
|
|
16096
|
+
else if (key2 === "j" || key2 === CSI + "B") moveSelection(1);
|
|
16097
|
+
else if (key2 === "k" || key2 === CSI + "A") moveSelection(-1);
|
|
16098
|
+
else if (key2 === "\r") {
|
|
16054
16099
|
if (state.section === "agents") await attachSelected();
|
|
16055
16100
|
else await openDialog("new-tab");
|
|
16056
|
-
} else if (
|
|
16101
|
+
} else if (key2 === "n" || key2 === "T") {
|
|
16057
16102
|
worldChanged = true;
|
|
16058
16103
|
await openDialog("new-tab");
|
|
16059
|
-
} else if (
|
|
16060
|
-
else if (
|
|
16061
|
-
else if (
|
|
16104
|
+
} else if (key2 === "m") await mainMenu();
|
|
16105
|
+
else if (key2 === "O") void openDialog("new-workspace");
|
|
16106
|
+
else if (key2 === "C") {
|
|
16062
16107
|
worldChanged = true;
|
|
16063
16108
|
closeSelectedWorkspace();
|
|
16064
|
-
} else if (
|
|
16109
|
+
} else if (key2 === "r") {
|
|
16065
16110
|
worldChanged = true;
|
|
16066
16111
|
await restoreSelected();
|
|
16067
|
-
} else if (
|
|
16112
|
+
} else if (key2 === "d") {
|
|
16068
16113
|
const client = await attachedClient();
|
|
16069
16114
|
await run(host, client ? ["tmux", "detach-client", "-t", client] : ["tmux", "detach-client"]);
|
|
16070
|
-
} else if (
|
|
16071
|
-
else if (
|
|
16115
|
+
} else if (key2 === "K") await keybindsMenu();
|
|
16116
|
+
else if (key2 === "G") {
|
|
16072
16117
|
state.grouped = !state.grouped;
|
|
16073
16118
|
applyFilter();
|
|
16074
|
-
} else if (
|
|
16119
|
+
} else if (key2 === "a") {
|
|
16075
16120
|
state.filter = state.filter === "space" ? "all" : "space";
|
|
16076
16121
|
applyFilter();
|
|
16077
|
-
} else if (
|
|
16078
|
-
else if (
|
|
16122
|
+
} else if (key2 === "<" || key2 === ">" || key2 === "," || key2 === ".") await toggleCollapsed();
|
|
16123
|
+
else if (key2 === "x") {
|
|
16079
16124
|
worldChanged = true;
|
|
16080
16125
|
await confirmKillAgent();
|
|
16081
|
-
} else if (
|
|
16126
|
+
} else if (key2 === "i") {
|
|
16082
16127
|
const agent = state.agents[state.agentIndex];
|
|
16083
16128
|
if (agent) await run(runnerFor(agent.backend), buildInterrupt(agent.session));
|
|
16084
|
-
} else if (
|
|
16085
|
-
else if (
|
|
16086
|
-
else if (
|
|
16087
|
-
else if (
|
|
16129
|
+
} else if (key2 === "g") await consent("track");
|
|
16130
|
+
else if (key2 === "s") await consent("skip");
|
|
16131
|
+
else if (key2 === "y") await consent("stay");
|
|
16132
|
+
else if (key2 === "q" || key2 === KEY_CTRL_C) {
|
|
16088
16133
|
await snapshotTabs();
|
|
16089
16134
|
releaseAwake();
|
|
16090
16135
|
await run(host, ["tmux", "kill-session", "-t", outerSession]);
|
|
@@ -16206,8 +16251,8 @@ var init_repos = __esm({
|
|
|
16206
16251
|
|
|
16207
16252
|
// cli/ui/tabs.ts
|
|
16208
16253
|
import { execSync as execSync7 } from "child_process";
|
|
16209
|
-
function
|
|
16210
|
-
return
|
|
16254
|
+
function embeddedSessionCommand(bootPath, harness, cwd) {
|
|
16255
|
+
return ["node", bootPath, "ui", "--run", harness, cwd].map(shellQuote3).join(" ");
|
|
16211
16256
|
}
|
|
16212
16257
|
function repoRoot() {
|
|
16213
16258
|
try {
|
|
@@ -16229,6 +16274,29 @@ async function createTab(bootPath, kind, spacePath) {
|
|
|
16229
16274
|
});
|
|
16230
16275
|
return;
|
|
16231
16276
|
}
|
|
16277
|
+
if (kind === "cursor-synkro" || kind === "codex-synkro") {
|
|
16278
|
+
const harness2 = kind === "codex-synkro" ? "codex" : "cursor";
|
|
16279
|
+
const command = embeddedSessionCommand(bootPath, harness2, spacePath);
|
|
16280
|
+
const spawned2 = await spawnAgent({ runner: HOST2, backend: "host", note: "runtime: host" }, {
|
|
16281
|
+
name: space + "-" + harness2 + "-synkro-" + String(process.pid % 1e4),
|
|
16282
|
+
harness: harness2,
|
|
16283
|
+
spaceName: space,
|
|
16284
|
+
cwd: spacePath,
|
|
16285
|
+
backend: "host",
|
|
16286
|
+
command,
|
|
16287
|
+
mode: "embedded"
|
|
16288
|
+
});
|
|
16289
|
+
if (!spawned2.ok) return;
|
|
16290
|
+
await buildTab(bootPath, repo, {
|
|
16291
|
+
cwd: spacePath,
|
|
16292
|
+
center: buildCenterAttachCommand(HOST2, spawned2.session),
|
|
16293
|
+
title: tabTitle(harness2, space),
|
|
16294
|
+
kind,
|
|
16295
|
+
agentSession: spawned2.session,
|
|
16296
|
+
focus: "center"
|
|
16297
|
+
});
|
|
16298
|
+
return;
|
|
16299
|
+
}
|
|
16232
16300
|
if (kind === "settings") {
|
|
16233
16301
|
await buildTab(bootPath, repo, {
|
|
16234
16302
|
cwd: spacePath,
|
|
@@ -16272,43 +16340,26 @@ async function openAgentTab(bootPath, session) {
|
|
|
16272
16340
|
"-p",
|
|
16273
16341
|
"-t",
|
|
16274
16342
|
session,
|
|
16275
|
-
["#{@synkro_harness}", "#{@synkro_space}", "#{@synkro_backend}"].join("|")
|
|
16343
|
+
["#{@synkro_harness}", "#{@synkro_space}", "#{@synkro_backend}", "#{@synkro_mode}"].join("|")
|
|
16276
16344
|
]);
|
|
16277
|
-
const [harness, space, backend] = (meta.stdout.trim() || "
|
|
16345
|
+
const [harness, space, backend, mode] = (meta.stdout.trim() || "|||").split("|");
|
|
16278
16346
|
const containerHosted = backend === "container";
|
|
16279
|
-
|
|
16280
|
-
let activeSession = session;
|
|
16347
|
+
const runner = mode === "embedded" ? HOST2 : containerHosted ? info.runner : HOST2;
|
|
16281
16348
|
const spaceName = (space || "").split("/").filter(Boolean).pop() || "space";
|
|
16282
|
-
|
|
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;
|
|
16349
|
+
const alive = (await run(runner, ["tmux", "has-session", "-t", session])).ok;
|
|
16300
16350
|
if (!alive) return;
|
|
16301
16351
|
await buildTab(bootPath, repoRoot(), {
|
|
16302
16352
|
cwd: space || repoRoot(),
|
|
16303
|
-
center: buildCenterAttachCommand(runner,
|
|
16353
|
+
center: buildCenterAttachCommand(runner, session),
|
|
16304
16354
|
title: tabTitle(harness || "claude", spaceName),
|
|
16305
|
-
kind: harness || "claude",
|
|
16306
|
-
agentSession:
|
|
16355
|
+
kind: mode === "embedded" ? (harness || "cursor") + "-synkro" : harness || "claude",
|
|
16356
|
+
agentSession: session,
|
|
16307
16357
|
focus: "center"
|
|
16308
16358
|
});
|
|
16309
16359
|
}
|
|
16310
16360
|
async function restoreTabs(bootPath, repoCwd) {
|
|
16311
16361
|
const tabs = loadTabs();
|
|
16362
|
+
const records = loadRecords();
|
|
16312
16363
|
if (tabs.length === 0) return false;
|
|
16313
16364
|
const info = await detectContainerBackend();
|
|
16314
16365
|
const hostSessions = (await run(HOST2, ["tmux", "list-sessions", "-F", "#{session_name}"])).stdout;
|
|
@@ -16318,16 +16369,11 @@ async function restoreTabs(bootPath, repoCwd) {
|
|
|
16318
16369
|
for (const tab of tabs) {
|
|
16319
16370
|
const space = tab.cwd.split("/").filter(Boolean).pop() || "space";
|
|
16320
16371
|
const containerHosted = info.backend === "container" && (await run(info.runner, ["test", "-d", tab.cwd])).ok;
|
|
16321
|
-
const
|
|
16322
|
-
const
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
if (command.ok && isLegacyCodexRenderer(command.stdout)) {
|
|
16327
|
-
await run(HOST2, buildKillSession(tab.agentSession));
|
|
16328
|
-
sessionAlive = false;
|
|
16329
|
-
}
|
|
16330
|
-
}
|
|
16372
|
+
const embedded = tab.kind === "cursor-synkro" || tab.kind === "codex-synkro";
|
|
16373
|
+
const harness = embedded ? tab.kind.replace("-synkro", "") : ["claude", "codex", "cursor"].includes(tab.kind) ? tab.kind : "";
|
|
16374
|
+
const sessionRunner = embedded || harness === "codex" ? HOST2 : containerHosted ? info.runner : HOST2;
|
|
16375
|
+
const sessionAlive = Boolean(tab.agentSession && alive.has(tab.agentSession));
|
|
16376
|
+
const record2 = records.find((row2) => row2.session === tab.agentSession);
|
|
16331
16377
|
if (tab.agentSession && sessionAlive) {
|
|
16332
16378
|
await run(sessionRunner, buildEnableMouse(tab.agentSession));
|
|
16333
16379
|
for (const argv of buildClipboardBindings(tab.agentSession)) {
|
|
@@ -16342,20 +16388,23 @@ async function restoreTabs(bootPath, repoCwd) {
|
|
|
16342
16388
|
focus: "center"
|
|
16343
16389
|
});
|
|
16344
16390
|
} else if (harness) {
|
|
16391
|
+
const command = embedded ? embeddedSessionCommand(bootPath, harness, tab.cwd) : void 0;
|
|
16345
16392
|
const spawned = await spawnAgent(info, {
|
|
16346
|
-
name: space + "-" + harness + "-" + String(process.pid % 1e4),
|
|
16393
|
+
name: record2?.name || space + "-" + harness + "-" + String(process.pid % 1e4),
|
|
16347
16394
|
harness,
|
|
16348
16395
|
spaceName: space,
|
|
16349
16396
|
cwd: tab.cwd,
|
|
16350
|
-
backend: harness === "codex" ? "host" : containerHosted ? "container" : "host",
|
|
16351
|
-
resume:
|
|
16397
|
+
backend: embedded || harness === "codex" ? "host" : containerHosted ? "container" : "host",
|
|
16398
|
+
resume: !embedded,
|
|
16399
|
+
command,
|
|
16400
|
+
mode: embedded ? "embedded" : "native"
|
|
16352
16401
|
});
|
|
16353
|
-
const restoredInContainer = harness !== "codex" && containerHosted;
|
|
16402
|
+
const restoredInContainer = !embedded && harness !== "codex" && containerHosted;
|
|
16354
16403
|
await buildTab(bootPath, repoCwd, spawned.ok ? {
|
|
16355
16404
|
cwd: tab.cwd,
|
|
16356
16405
|
center: buildCenterAttachCommand(restoredInContainer ? info.runner : HOST2, spawned.session),
|
|
16357
16406
|
title: tabTitle(harness, space),
|
|
16358
|
-
kind: harness,
|
|
16407
|
+
kind: embedded ? tab.kind : harness,
|
|
16359
16408
|
agentSession: spawned.session,
|
|
16360
16409
|
focus: "center"
|
|
16361
16410
|
} : { cwd: tab.cwd, center: makeTerminalCommand(bootPath), title: tabTitle("terminal", space), kind: "terminal", focus: "center" });
|
|
@@ -16387,11 +16436,20 @@ var init_tabs = __esm({
|
|
|
16387
16436
|
// cli/ui/dialog.ts
|
|
16388
16437
|
import { existsSync as existsSync37 } from "fs";
|
|
16389
16438
|
import { homedir as homedir37 } from "os";
|
|
16439
|
+
function providerChoices(harnesses) {
|
|
16440
|
+
return [
|
|
16441
|
+
{ value: "terminal", label: TAB_GLYPHS.terminal + " Terminal" },
|
|
16442
|
+
...harnesses.map((harness) => ({
|
|
16443
|
+
value: harness === "cursor" || harness === "codex" ? harness + "-synkro" : harness,
|
|
16444
|
+
label: (TAB_GLYPHS[harness] || "") + " " + (HARNESS_LABELS[harness] || harness)
|
|
16445
|
+
}))
|
|
16446
|
+
];
|
|
16447
|
+
}
|
|
16390
16448
|
function write(text) {
|
|
16391
16449
|
process.stdout.write(text);
|
|
16392
16450
|
}
|
|
16393
16451
|
function visibleLength2(text) {
|
|
16394
|
-
let
|
|
16452
|
+
let visible2 = 0;
|
|
16395
16453
|
let index = 0;
|
|
16396
16454
|
while (index < text.length) {
|
|
16397
16455
|
if (text.startsWith(CSI2, index)) {
|
|
@@ -16399,11 +16457,11 @@ function visibleLength2(text) {
|
|
|
16399
16457
|
if (end === -1) break;
|
|
16400
16458
|
index = end + 1;
|
|
16401
16459
|
} else {
|
|
16402
|
-
|
|
16460
|
+
visible2 += 1;
|
|
16403
16461
|
index += 1;
|
|
16404
16462
|
}
|
|
16405
16463
|
}
|
|
16406
|
-
return
|
|
16464
|
+
return visible2;
|
|
16407
16465
|
}
|
|
16408
16466
|
function clipPath(text, max) {
|
|
16409
16467
|
const value = String(text || "");
|
|
@@ -16446,34 +16504,34 @@ async function pick(opts) {
|
|
|
16446
16504
|
const perItem = opts.choices.some((choice) => choice.detail) ? 2 : 1;
|
|
16447
16505
|
const visibleItems = Math.max(1, Math.floor(Math.max(2, height - 5) / perItem));
|
|
16448
16506
|
const firstRow = 4;
|
|
16449
|
-
return new Promise((
|
|
16507
|
+
return new Promise((resolve9) => {
|
|
16450
16508
|
const view = () => opts.choices.filter((choice) => matches(choice, filter));
|
|
16451
16509
|
const draw = () => {
|
|
16452
16510
|
const shown = view();
|
|
16453
16511
|
if (selected >= shown.length) selected = Math.max(0, shown.length - 1);
|
|
16454
16512
|
if (selected < top) top = selected;
|
|
16455
16513
|
if (selected >= top + visibleItems) top = selected - visibleItems + 1;
|
|
16456
|
-
const
|
|
16457
|
-
|
|
16514
|
+
const lines2 = [];
|
|
16515
|
+
lines2.push(" " + S.title + opts.title + S.reset);
|
|
16458
16516
|
const left = filter && !opts.menu ? " " + S.accent + "/ " + S.reset + filter + "\u258C" : " " + S.dim + opts.hint + S.reset;
|
|
16459
16517
|
const right = opts.menu ? "" : S.dim + String(shown.length) + (shown.length === 1 ? " match" : " matches") + S.reset;
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
if (shown.length === 0)
|
|
16518
|
+
lines2.push(padRow(left, Math.max(0, width - visibleLength2(right) - 1)) + right);
|
|
16519
|
+
lines2.push("");
|
|
16520
|
+
if (shown.length === 0) lines2.push(" " + S.dim + (opts.emptyNote || "nothing matches") + S.reset);
|
|
16463
16521
|
shown.slice(top, top + visibleItems).forEach((choice, offset) => {
|
|
16464
16522
|
const index = top + offset;
|
|
16465
16523
|
const isSelected = index === selected;
|
|
16466
16524
|
const marker = isSelected ? S.accent + "\u203A" + S.reset + " " : " ";
|
|
16467
16525
|
const note = choice.note ? S.dim + choice.note + S.reset : "";
|
|
16468
16526
|
const head = " " + marker + S.bold + clip2(choice.label, width - 6 - visibleLength2(note)) + S.reset;
|
|
16469
|
-
|
|
16527
|
+
lines2.push(selectable(padRow(head, Math.max(0, width - visibleLength2(note) - 1)) + note, width, isSelected));
|
|
16470
16528
|
if (perItem === 2) {
|
|
16471
|
-
|
|
16529
|
+
lines2.push(selectable(" " + S.muted + clipPath(choice.detail || "", width - 6) + S.reset, width, isSelected));
|
|
16472
16530
|
}
|
|
16473
16531
|
});
|
|
16474
|
-
while (
|
|
16475
|
-
|
|
16476
|
-
write(CSI2 + "H" +
|
|
16532
|
+
while (lines2.length < height - 1) lines2.push("");
|
|
16533
|
+
lines2.push(" " + S.dim + opts.footer + S.reset);
|
|
16534
|
+
write(CSI2 + "H" + lines2.slice(0, height).map((line) => padRow(line, width)).join("\n"));
|
|
16477
16535
|
};
|
|
16478
16536
|
const rowOfItem = (index) => firstRow + (index - top) * perItem;
|
|
16479
16537
|
const MOUSE = /\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
@@ -16494,7 +16552,7 @@ async function pick(opts) {
|
|
|
16494
16552
|
const hit = shown.findIndex((_, index) => y >= rowOfItem(index) && y < rowOfItem(index) + perItem);
|
|
16495
16553
|
if (hit >= 0) {
|
|
16496
16554
|
process.stdin.off("data", onData);
|
|
16497
|
-
|
|
16555
|
+
resolve9(shown[hit].value);
|
|
16498
16556
|
return;
|
|
16499
16557
|
}
|
|
16500
16558
|
}
|
|
@@ -16502,13 +16560,13 @@ async function pick(opts) {
|
|
|
16502
16560
|
if (!sawMouse) {
|
|
16503
16561
|
if (input === KEY_ESC && !input.includes("[<") || input === KEY_CTRL_C2) {
|
|
16504
16562
|
process.stdin.off("data", onData);
|
|
16505
|
-
|
|
16563
|
+
resolve9(null);
|
|
16506
16564
|
return;
|
|
16507
16565
|
}
|
|
16508
16566
|
if (input === "\r") {
|
|
16509
16567
|
if (shown.length === 0) return;
|
|
16510
16568
|
process.stdin.off("data", onData);
|
|
16511
|
-
|
|
16569
|
+
resolve9(shown[selected].value);
|
|
16512
16570
|
return;
|
|
16513
16571
|
}
|
|
16514
16572
|
const down = input === CSI2 + "B" || opts.menu && (input === "j" || input === CSI2 + "C");
|
|
@@ -16532,9 +16590,9 @@ async function readLine(opts) {
|
|
|
16532
16590
|
let value = "";
|
|
16533
16591
|
let error = "";
|
|
16534
16592
|
const width = Math.max(20, Number(process.stdout.columns || 80));
|
|
16535
|
-
return new Promise((
|
|
16593
|
+
return new Promise((resolve9) => {
|
|
16536
16594
|
const draw = () => {
|
|
16537
|
-
const
|
|
16595
|
+
const lines2 = [
|
|
16538
16596
|
" " + S.title + opts.title + S.reset,
|
|
16539
16597
|
"",
|
|
16540
16598
|
" " + S.dim + opts.label + S.reset,
|
|
@@ -16545,13 +16603,13 @@ async function readLine(opts) {
|
|
|
16545
16603
|
"",
|
|
16546
16604
|
" " + S.dim + opts.footer + S.reset
|
|
16547
16605
|
];
|
|
16548
|
-
write(CSI2 + "2J" + CSI2 + "H" +
|
|
16606
|
+
write(CSI2 + "2J" + CSI2 + "H" + lines2.map((line) => padRow(line, width)).join("\n"));
|
|
16549
16607
|
};
|
|
16550
16608
|
const onData = (chunk) => {
|
|
16551
16609
|
const input = chunk.toString("utf8");
|
|
16552
16610
|
if (input === KEY_ESC || input === KEY_CTRL_C2) {
|
|
16553
16611
|
process.stdin.off("data", onData);
|
|
16554
|
-
|
|
16612
|
+
resolve9(null);
|
|
16555
16613
|
return;
|
|
16556
16614
|
}
|
|
16557
16615
|
if (input === "\r") {
|
|
@@ -16563,7 +16621,7 @@ async function readLine(opts) {
|
|
|
16563
16621
|
return;
|
|
16564
16622
|
}
|
|
16565
16623
|
process.stdin.off("data", onData);
|
|
16566
|
-
|
|
16624
|
+
resolve9(value.trim());
|
|
16567
16625
|
})();
|
|
16568
16626
|
return;
|
|
16569
16627
|
}
|
|
@@ -16823,13 +16881,7 @@ async function runDialog(kind, repoCwd, argA = "", argB = "") {
|
|
|
16823
16881
|
process.exit(0);
|
|
16824
16882
|
}
|
|
16825
16883
|
const harnesses = await detectHarnesses();
|
|
16826
|
-
const sessions =
|
|
16827
|
-
{ value: "terminal", label: TAB_GLYPHS.terminal + " Terminal" },
|
|
16828
|
-
...harnesses.map((harness) => ({
|
|
16829
|
-
value: harness,
|
|
16830
|
-
label: (TAB_GLYPHS[harness] || "") + " " + (HARNESS_LABELS[harness] || harness)
|
|
16831
|
-
}))
|
|
16832
|
-
];
|
|
16884
|
+
const sessions = providerChoices(harnesses);
|
|
16833
16885
|
for (; ; ) {
|
|
16834
16886
|
const session = await pick({
|
|
16835
16887
|
menu: true,
|
|
@@ -16896,15 +16948,27 @@ function terminalText(text) {
|
|
|
16896
16948
|
return String(text).replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
|
|
16897
16949
|
}
|
|
16898
16950
|
function clip3(text, max) {
|
|
16899
|
-
const value =
|
|
16951
|
+
const value = cleanOutput(text).replace(/\s+/g, " ").trim();
|
|
16900
16952
|
if (max <= 1) return value;
|
|
16901
16953
|
return value.length <= max ? value : value.slice(0, max - 1) + "\u2026";
|
|
16902
16954
|
}
|
|
16955
|
+
function cleanOutput(text) {
|
|
16956
|
+
return String(text || "").replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -\/]*[@-~]/g, "").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
|
|
16957
|
+
}
|
|
16903
16958
|
function seconds(ms) {
|
|
16904
16959
|
const total = Math.max(0, Math.round(ms / 1e3));
|
|
16905
16960
|
if (total < 60) return total + "s";
|
|
16906
16961
|
return Math.floor(total / 60) + "m" + String(total % 60).padStart(2, "0") + "s";
|
|
16907
16962
|
}
|
|
16963
|
+
function blockNextStep(reason) {
|
|
16964
|
+
if (/tracking (?:decision|is required)|task tracking|skip tracking|\bconductor\b/i.test(reason)) {
|
|
16965
|
+
return "Next: reply \u201Ctrack it\u201D to create a task, or \u201Cskip tracking\u201D to continue untracked.";
|
|
16966
|
+
}
|
|
16967
|
+
if (/\bCWE-\d+\b|\bCVE-\d{4}-\d+\b/i.test(reason)) {
|
|
16968
|
+
return "Next: revise the flagged code, then retry. Do not bypass the rule.";
|
|
16969
|
+
}
|
|
16970
|
+
return "Next: resolve the rule above, then retry.";
|
|
16971
|
+
}
|
|
16908
16972
|
function statusLine(opts) {
|
|
16909
16973
|
const hint = opts.hint ? " \xB7 " + opts.hint : "";
|
|
16910
16974
|
const body = opts.text + " (" + seconds(opts.elapsedMs) + hint + ")";
|
|
@@ -16914,86 +16978,103 @@ function statusLine(opts) {
|
|
|
16914
16978
|
function renderEvent(event, width = 100) {
|
|
16915
16979
|
const body = Math.max(30, width - 4);
|
|
16916
16980
|
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
16981
|
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
|
-
];
|
|
16982
|
+
return [];
|
|
16926
16983
|
case "user-message":
|
|
16927
|
-
return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset
|
|
16984
|
+
return ["", S2.user + " \u276F " + S2.reset + S2.bold + clip3(event.text, body) + S2.reset];
|
|
16928
16985
|
// Live state, not transcript. The runner promotes these to the status line.
|
|
16929
16986
|
case "thinking":
|
|
16930
16987
|
return [];
|
|
16931
16988
|
case "assistant-message": {
|
|
16932
|
-
const
|
|
16989
|
+
const lines2 = wrap(event.text, body - 2);
|
|
16933
16990
|
return [
|
|
16934
16991
|
"",
|
|
16935
|
-
...
|
|
16936
|
-
""
|
|
16992
|
+
...lines2.map((line, index) => index === 0 ? S2.agent + " " + BULLET + " " + line + S2.reset : S2.agent + " " + line + S2.reset)
|
|
16937
16993
|
];
|
|
16938
16994
|
}
|
|
16939
16995
|
case "tool-start": {
|
|
16940
16996
|
const label = TOOL_LABEL[event.kind] || TOOL_LABEL.other;
|
|
16941
16997
|
return [
|
|
16942
|
-
|
|
16998
|
+
"",
|
|
16999
|
+
S2.panel + S2.tool + " " + BULLET + " " + label + S2.reset + S2.panel + S2.secondary + "(" + clip3(event.target, body - label.length - 8) + ")" + S2.reset
|
|
16943
17000
|
];
|
|
16944
17001
|
}
|
|
16945
17002
|
case "tool-end": {
|
|
16946
17003
|
if (event.blocked) {
|
|
16947
17004
|
return [
|
|
16948
|
-
S2.
|
|
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)
|
|
17005
|
+
S2.panel + S2.blocked + " \u29C9 Blocked" + S2.reset + S2.panel + S2.secondary + " " + clip3(terminalText(event.target), body - 21) + S2.reset,
|
|
17006
|
+
...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),
|
|
17007
|
+
...wrap(blockNextStep(event.reason), body - 6).map((line) => S2.secondary + " " + line + S2.reset)
|
|
16950
17008
|
];
|
|
16951
17009
|
}
|
|
17010
|
+
const timing = event.durationMs == null ? "" : " \xB7 " + seconds(event.durationMs);
|
|
16952
17011
|
const detail = event.ok ? event.output.trim() ? clip3(terminalText(event.output), body - 10) : "done" : "failed" + (event.exitCode === null ? "" : " (exit " + event.exitCode + ")");
|
|
16953
17012
|
const tint = event.ok ? S2.ok : S2.blocked;
|
|
16954
|
-
|
|
17013
|
+
const changes = (event.fileChanges || []).flatMap((change) => wrap(change.path, body - 20).map((line, index) => S2.panel + S2.secondary + (index === 0 ? " " : " ") + line + (index === 0 ? " " + S2.ok + "+" + change.additions + S2.secondary + " " + S2.blocked + "-" + change.deletions : "") + S2.reset));
|
|
17014
|
+
return [
|
|
17015
|
+
S2.panel + tint + " " + ELBOW + " " + S2.reset + S2.panel + S2.secondary + detail + timing + S2.reset,
|
|
17016
|
+
...changes
|
|
17017
|
+
];
|
|
16955
17018
|
}
|
|
16956
17019
|
// A clean finish needs no announcement: the prompt returning IS the signal.
|
|
16957
17020
|
case "turn-end":
|
|
16958
17021
|
return event.ok ? [] : ["", S2.blocked + " " + BULLET + " turn ended with an error" + S2.reset, ""];
|
|
16959
17022
|
case "notice":
|
|
16960
|
-
return [S2.
|
|
17023
|
+
return [S2.secondary + " " + clip3(event.text, body) + S2.reset];
|
|
17024
|
+
case "plan":
|
|
17025
|
+
return [
|
|
17026
|
+
S2.panel + S2.tool + " Plan" + S2.reset,
|
|
17027
|
+
...event.steps.map((row2) => S2.panel + (row2.status === "completed" ? S2.ok + " \u2713 " : S2.secondary + " \xB7 ") + clip3(row2.step, body - 4) + S2.reset)
|
|
17028
|
+
];
|
|
17029
|
+
case "assistant-delta":
|
|
17030
|
+
case "usage":
|
|
17031
|
+
return [];
|
|
16961
17032
|
default:
|
|
16962
17033
|
return [];
|
|
16963
17034
|
}
|
|
16964
17035
|
}
|
|
16965
17036
|
function wrap(text, width) {
|
|
16966
|
-
const words =
|
|
17037
|
+
const words = cleanOutput(text).replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
|
|
16967
17038
|
if (words.length === 0) return [];
|
|
16968
|
-
const
|
|
17039
|
+
const lines2 = [];
|
|
16969
17040
|
let line = "";
|
|
16970
17041
|
for (const word of words) {
|
|
16971
17042
|
if (!line) line = word;
|
|
16972
17043
|
else if ((line + " " + word).length <= width) line += " " + word;
|
|
16973
17044
|
else {
|
|
16974
|
-
|
|
17045
|
+
lines2.push(line);
|
|
16975
17046
|
line = word;
|
|
16976
17047
|
}
|
|
16977
17048
|
}
|
|
16978
|
-
if (line)
|
|
16979
|
-
return
|
|
17049
|
+
if (line) lines2.push(line);
|
|
17050
|
+
return lines2;
|
|
16980
17051
|
}
|
|
16981
|
-
var ESC2, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
|
|
17052
|
+
var ESC2, color, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
|
|
16982
17053
|
var init_render2 = __esm({
|
|
16983
17054
|
"cli/harness/render.ts"() {
|
|
16984
17055
|
"use strict";
|
|
16985
17056
|
ESC2 = "\x1B[";
|
|
17057
|
+
color = (hex, plane = 38) => {
|
|
17058
|
+
const [r, g, b] = hex.match(/../g).map((part) => Number.parseInt(part, 16));
|
|
17059
|
+
return ESC2 + plane + ";2;" + r + ";" + g + ";" + b + "m";
|
|
17060
|
+
};
|
|
16986
17061
|
S2 = {
|
|
16987
17062
|
reset: ESC2 + "0m",
|
|
16988
17063
|
dim: ESC2 + "2m",
|
|
16989
17064
|
bold: ESC2 + "1m",
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
|
|
17065
|
+
canvas: color("101416", 48),
|
|
17066
|
+
panel: color("171D1F", 48),
|
|
17067
|
+
composer: color("13191B", 48),
|
|
17068
|
+
text: color("C4C8C4"),
|
|
17069
|
+
secondary: color("7F8A86"),
|
|
17070
|
+
border: color("2A3334"),
|
|
17071
|
+
user: color("87A68E"),
|
|
17072
|
+
agent: color("C4C8C4"),
|
|
17073
|
+
think: color("B29A6A"),
|
|
17074
|
+
tool: color("7F9CA5"),
|
|
17075
|
+
ok: color("789B82"),
|
|
17076
|
+
blocked: color("B87474"),
|
|
17077
|
+
rule: color("B87474")
|
|
16997
17078
|
};
|
|
16998
17079
|
CLEAR_LINE = "\r" + ESC2 + "2K";
|
|
16999
17080
|
BULLET = "\u23FA";
|
|
@@ -17015,262 +17096,14 @@ var init_render2 = __esm({
|
|
|
17015
17096
|
|
|
17016
17097
|
// cli/harness/composer.ts
|
|
17017
17098
|
import { execFileSync as execFileSync5, spawnSync as spawnSync12 } from "child_process";
|
|
17018
|
-
import { existsSync as existsSync38, mkdtempSync as mkdtempSync2, rmSync as rmSync7, statSync as
|
|
17099
|
+
import { existsSync as existsSync38, mkdtempSync as mkdtempSync2, rmSync as rmSync7, statSync as statSync6, writeFileSync as writeFileSync27 } from "fs";
|
|
17019
17100
|
import { homedir as homedir38, tmpdir } from "os";
|
|
17020
17101
|
import { basename as basename3, dirname as dirname12, extname, isAbsolute as isAbsolute2, join as join37, resolve as resolve5, sep as sep3 } from "path";
|
|
17021
17102
|
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
17103
|
var init_composer = __esm({
|
|
17268
17104
|
"cli/harness/composer.ts"() {
|
|
17269
17105
|
"use strict";
|
|
17270
17106
|
init_render2();
|
|
17271
|
-
PASTE_START = "\x1B[200~";
|
|
17272
|
-
PASTE_END = "\x1B[201~";
|
|
17273
|
-
IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
|
|
17274
17107
|
}
|
|
17275
17108
|
});
|
|
17276
17109
|
|
|
@@ -17303,6 +17136,193 @@ var init_events = __esm({
|
|
|
17303
17136
|
}
|
|
17304
17137
|
});
|
|
17305
17138
|
|
|
17139
|
+
// cli/harness/changes.ts
|
|
17140
|
+
import { lstatSync as lstatSync2, readFileSync as readFileSync33, readlinkSync } from "fs";
|
|
17141
|
+
import { resolve as resolve6, relative } from "path";
|
|
17142
|
+
function lines(text) {
|
|
17143
|
+
if (!text) return [];
|
|
17144
|
+
const rows = text.replace(/\r\n/g, "\n").split("\n");
|
|
17145
|
+
if (rows[rows.length - 1] === "") rows.pop();
|
|
17146
|
+
return rows;
|
|
17147
|
+
}
|
|
17148
|
+
function lineChangeCounts(before, after) {
|
|
17149
|
+
const a = lines(before);
|
|
17150
|
+
const b = lines(after);
|
|
17151
|
+
if (a.length === 0) return { additions: b.length, deletions: 0 };
|
|
17152
|
+
if (b.length === 0) return { additions: 0, deletions: a.length };
|
|
17153
|
+
const max = a.length + b.length;
|
|
17154
|
+
let frontier = /* @__PURE__ */ new Map([[1, 0]]);
|
|
17155
|
+
for (let distance = 0; distance <= Math.min(max, MAX_DIFF_DISTANCE); distance++) {
|
|
17156
|
+
const next = /* @__PURE__ */ new Map();
|
|
17157
|
+
for (let diagonal = -distance; diagonal <= distance; diagonal += 2) {
|
|
17158
|
+
const down = frontier.get(diagonal + 1) ?? -1;
|
|
17159
|
+
const right = (frontier.get(diagonal - 1) ?? -1) + 1;
|
|
17160
|
+
let x = diagonal === -distance || diagonal !== distance && right < down ? down : right;
|
|
17161
|
+
if (x < 0) x = 0;
|
|
17162
|
+
let y = x - diagonal;
|
|
17163
|
+
while (x < a.length && y < b.length && a[x] === b[y]) {
|
|
17164
|
+
x++;
|
|
17165
|
+
y++;
|
|
17166
|
+
}
|
|
17167
|
+
next.set(diagonal, x);
|
|
17168
|
+
if (x >= a.length && y >= b.length) {
|
|
17169
|
+
return {
|
|
17170
|
+
additions: (distance + b.length - a.length) / 2,
|
|
17171
|
+
deletions: (distance + a.length - b.length) / 2
|
|
17172
|
+
};
|
|
17173
|
+
}
|
|
17174
|
+
}
|
|
17175
|
+
frontier = next;
|
|
17176
|
+
}
|
|
17177
|
+
let start = 0;
|
|
17178
|
+
while (start < a.length && start < b.length && a[start] === b[start]) start++;
|
|
17179
|
+
let aEnd = a.length;
|
|
17180
|
+
let bEnd = b.length;
|
|
17181
|
+
while (aEnd > start && bEnd > start && a[aEnd - 1] === b[bEnd - 1]) {
|
|
17182
|
+
aEnd--;
|
|
17183
|
+
bEnd--;
|
|
17184
|
+
}
|
|
17185
|
+
return { additions: bEnd - start, deletions: aEnd - start };
|
|
17186
|
+
}
|
|
17187
|
+
function unifiedDiffCounts(diff) {
|
|
17188
|
+
let additions = 0;
|
|
17189
|
+
let deletions = 0;
|
|
17190
|
+
for (const line of String(diff || "").split("\n")) {
|
|
17191
|
+
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
17192
|
+
if (line.startsWith("+")) additions++;
|
|
17193
|
+
else if (line.startsWith("-")) deletions++;
|
|
17194
|
+
}
|
|
17195
|
+
return { additions, deletions };
|
|
17196
|
+
}
|
|
17197
|
+
function safePath(cwd, file) {
|
|
17198
|
+
const absolute = resolve6(cwd, file);
|
|
17199
|
+
const rel = relative(cwd, absolute);
|
|
17200
|
+
return rel && rel !== ".." && !rel.startsWith("../") ? rel : null;
|
|
17201
|
+
}
|
|
17202
|
+
function worktreeContent(cwd, file) {
|
|
17203
|
+
const rel = safePath(cwd, file);
|
|
17204
|
+
if (!rel) return null;
|
|
17205
|
+
try {
|
|
17206
|
+
const absolute = resolve6(cwd, rel);
|
|
17207
|
+
const stat = lstatSync2(absolute);
|
|
17208
|
+
if (stat.isSymbolicLink()) return "symlink:" + readlinkSync(absolute);
|
|
17209
|
+
if (!stat.isFile() || stat.size > MAX_TEXT_BYTES) return null;
|
|
17210
|
+
const content = readFileSync33(absolute);
|
|
17211
|
+
return content.includes(0) ? null : content.toString("utf8");
|
|
17212
|
+
} catch {
|
|
17213
|
+
return null;
|
|
17214
|
+
}
|
|
17215
|
+
}
|
|
17216
|
+
function fileOperation(event) {
|
|
17217
|
+
return (event.type === "tool-start" || event.type === "tool-end") && /^(edit|write|delete)$/.test(event.kind);
|
|
17218
|
+
}
|
|
17219
|
+
function eventPaths(cwd, event) {
|
|
17220
|
+
const provided = event.type === "tool-end" ? (event.fileChanges || []).map((change) => change.path) : [];
|
|
17221
|
+
const targets = String(event.target || "").split(/,\s*/);
|
|
17222
|
+
return [...new Set([...provided, ...targets].map((file) => safePath(cwd, file)).filter((file) => Boolean(file)))];
|
|
17223
|
+
}
|
|
17224
|
+
function snapshotOperation(cwd, event) {
|
|
17225
|
+
return new Map(eventPaths(cwd, event).map((file) => [file, worktreeContent(cwd, file)]));
|
|
17226
|
+
}
|
|
17227
|
+
var MAX_TEXT_BYTES, MAX_DIFF_DISTANCE, OperationChangeTracker;
|
|
17228
|
+
var init_changes = __esm({
|
|
17229
|
+
"cli/harness/changes.ts"() {
|
|
17230
|
+
"use strict";
|
|
17231
|
+
MAX_TEXT_BYTES = 2 * 1024 * 1024;
|
|
17232
|
+
MAX_DIFF_DISTANCE = 4e3;
|
|
17233
|
+
OperationChangeTracker = class {
|
|
17234
|
+
constructor(cwd, capture) {
|
|
17235
|
+
this.cwd = cwd;
|
|
17236
|
+
this.capture = capture;
|
|
17237
|
+
}
|
|
17238
|
+
cwd;
|
|
17239
|
+
capture;
|
|
17240
|
+
starts = /* @__PURE__ */ new Map();
|
|
17241
|
+
snapshot(event) {
|
|
17242
|
+
return this.capture ? this.capture() : snapshotOperation(this.cwd, event);
|
|
17243
|
+
}
|
|
17244
|
+
observe(event) {
|
|
17245
|
+
if (event.type === "tool-start") {
|
|
17246
|
+
if (fileOperation(event)) this.starts.set(event.id, this.snapshot(event));
|
|
17247
|
+
return event;
|
|
17248
|
+
}
|
|
17249
|
+
if (event.type !== "tool-end") return event;
|
|
17250
|
+
const before = this.starts.get(event.id);
|
|
17251
|
+
this.starts.delete(event.id);
|
|
17252
|
+
if (!before) return event;
|
|
17253
|
+
const after = this.snapshot(event);
|
|
17254
|
+
const files = [.../* @__PURE__ */ new Set([...before.keys(), ...after.keys()])].sort();
|
|
17255
|
+
const fileChanges2 = [];
|
|
17256
|
+
for (const file of files) {
|
|
17257
|
+
const oldText = before.get(file);
|
|
17258
|
+
const newText = after.has(file) ? after.get(file) : worktreeContent(this.cwd, file);
|
|
17259
|
+
if (oldText === newText || oldText === null && newText === null) continue;
|
|
17260
|
+
const counts = lineChangeCounts(oldText || "", newText || "");
|
|
17261
|
+
fileChanges2.push({ path: file, ...counts });
|
|
17262
|
+
}
|
|
17263
|
+
const provided = event.fileChanges || [];
|
|
17264
|
+
const measured = new Map(fileChanges2.map((change) => [change.path, change]));
|
|
17265
|
+
const merged = provided.map((change) => {
|
|
17266
|
+
const actual = measured.get(change.path) || measured.get(safePath(this.cwd, change.path) || "");
|
|
17267
|
+
if (actual && change.additions === 0 && change.deletions === 0) return actual;
|
|
17268
|
+
return change;
|
|
17269
|
+
});
|
|
17270
|
+
const providedPaths = new Set(provided.flatMap((change) => [change.path, safePath(this.cwd, change.path) || ""]));
|
|
17271
|
+
for (const change of fileChanges2) if (!providedPaths.has(change.path)) merged.push(change);
|
|
17272
|
+
return { ...event, fileChanges: merged.length ? merged : fileChanges2 };
|
|
17273
|
+
}
|
|
17274
|
+
};
|
|
17275
|
+
}
|
|
17276
|
+
});
|
|
17277
|
+
|
|
17278
|
+
// cli/codexUsage.ts
|
|
17279
|
+
function record(value) {
|
|
17280
|
+
return value != null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17281
|
+
}
|
|
17282
|
+
function finiteNumber(value) {
|
|
17283
|
+
const n = Number(value);
|
|
17284
|
+
return Number.isFinite(n) ? n : null;
|
|
17285
|
+
}
|
|
17286
|
+
function rateWindow(value) {
|
|
17287
|
+
const row2 = record(value);
|
|
17288
|
+
if (!row2) return null;
|
|
17289
|
+
const usedPercent = finiteNumber(row2.usedPercent);
|
|
17290
|
+
if (usedPercent == null) return null;
|
|
17291
|
+
return {
|
|
17292
|
+
durationMins: finiteNumber(row2.windowDurationMins),
|
|
17293
|
+
usedPercent,
|
|
17294
|
+
resetsAt: finiteNumber(row2.resetsAt)
|
|
17295
|
+
};
|
|
17296
|
+
}
|
|
17297
|
+
function codexUsageFromRateLimitsResponse(value) {
|
|
17298
|
+
const response = record(value);
|
|
17299
|
+
if (!response) return null;
|
|
17300
|
+
const byLimitId = record(response.rateLimitsByLimitId);
|
|
17301
|
+
const snapshot = record(byLimitId?.codex) || record(response.rateLimits);
|
|
17302
|
+
if (!snapshot) return null;
|
|
17303
|
+
const primary = rateWindow(snapshot.primary);
|
|
17304
|
+
const secondary = rateWindow(snapshot.secondary);
|
|
17305
|
+
const windows = [primary, secondary].filter((w) => w != null);
|
|
17306
|
+
if (windows.length === 0) return null;
|
|
17307
|
+
const fiveHour = windows.find((w) => w.durationMins === 300) || windows.find((w) => w.durationMins != null && w.durationMins <= 360) || (primary?.durationMins == null ? primary : null);
|
|
17308
|
+
const sevenDay = windows.find((w) => w.durationMins === 10080) || windows.find((w) => w.durationMins != null && w.durationMins >= 1440) || (secondary !== fiveHour && secondary?.durationMins == null ? secondary : null);
|
|
17309
|
+
const status = typeof snapshot.rateLimitReachedType === "string" ? snapshot.rateLimitReachedType : snapshot.spendControlReached === true ? "spend_control_reached" : null;
|
|
17310
|
+
return {
|
|
17311
|
+
util5h: fiveHour ? fiveHour.usedPercent / 100 : null,
|
|
17312
|
+
util7d: sevenDay ? sevenDay.usedPercent / 100 : null,
|
|
17313
|
+
reset5h: fiveHour?.resetsAt ?? null,
|
|
17314
|
+
reset7d: sevenDay?.resetsAt ?? null,
|
|
17315
|
+
status,
|
|
17316
|
+
planType: typeof snapshot.planType === "string" ? snapshot.planType : null,
|
|
17317
|
+
limitId: typeof snapshot.limitId === "string" ? snapshot.limitId : null
|
|
17318
|
+
};
|
|
17319
|
+
}
|
|
17320
|
+
var init_codexUsage = __esm({
|
|
17321
|
+
"cli/codexUsage.ts"() {
|
|
17322
|
+
"use strict";
|
|
17323
|
+
}
|
|
17324
|
+
});
|
|
17325
|
+
|
|
17306
17326
|
// cli/harness/cursor.ts
|
|
17307
17327
|
function unwrapReplay(text) {
|
|
17308
17328
|
return text.replace(/<\/?user_query>/gi, "").trim();
|
|
@@ -17315,10 +17335,10 @@ function textOf(message) {
|
|
|
17315
17335
|
}
|
|
17316
17336
|
function toolPayload(toolCall) {
|
|
17317
17337
|
if (!toolCall) return { kind: "other", body: {} };
|
|
17318
|
-
for (const [
|
|
17319
|
-
if (toolCall[
|
|
17338
|
+
for (const [key2, kind] of Object.entries(TOOL_KINDS)) {
|
|
17339
|
+
if (toolCall[key2]) return { kind, body: toolCall[key2] };
|
|
17320
17340
|
}
|
|
17321
|
-
const fallback = Object.keys(toolCall).find((
|
|
17341
|
+
const fallback = Object.keys(toolCall).find((key2) => key2.endsWith("ToolCall"));
|
|
17322
17342
|
return fallback ? { kind: "other", body: toolCall[fallback] } : { kind: "other", body: {} };
|
|
17323
17343
|
}
|
|
17324
17344
|
function targetOf(kind, body) {
|
|
@@ -17327,7 +17347,25 @@ function targetOf(kind, body) {
|
|
|
17327
17347
|
const text = typeof candidate === "string" ? candidate : JSON.stringify(candidate ?? "");
|
|
17328
17348
|
return text || body?.description || kind;
|
|
17329
17349
|
}
|
|
17330
|
-
function
|
|
17350
|
+
function completedFileChanges(body) {
|
|
17351
|
+
const success = body?.result?.success || {};
|
|
17352
|
+
const rows = Array.isArray(success.changes) ? success.changes : Array.isArray(success.files) ? success.files : [];
|
|
17353
|
+
return rows.flatMap((change) => {
|
|
17354
|
+
const path = String(change?.path || change?.filePath || change?.file_path || "");
|
|
17355
|
+
if (!path) return [];
|
|
17356
|
+
const diff = String(change?.diff || change?.patch || "");
|
|
17357
|
+
const hasAdditions = change?.additions != null || change?.linesAdded != null;
|
|
17358
|
+
const hasDeletions = change?.deletions != null || change?.linesRemoved != null;
|
|
17359
|
+
if (!diff && !hasAdditions && !hasDeletions) return [];
|
|
17360
|
+
const counted = unifiedDiffCounts(diff);
|
|
17361
|
+
return [{
|
|
17362
|
+
path,
|
|
17363
|
+
additions: hasAdditions ? Number(change?.additions ?? change?.linesAdded) : counted.additions,
|
|
17364
|
+
deletions: hasDeletions ? Number(change?.deletions ?? change?.linesRemoved) : counted.deletions
|
|
17365
|
+
}];
|
|
17366
|
+
});
|
|
17367
|
+
}
|
|
17368
|
+
function parseCursorLine(line, streamPartial = false) {
|
|
17331
17369
|
const trimmed = String(line || "").trim();
|
|
17332
17370
|
if (!trimmed) return [];
|
|
17333
17371
|
let frame;
|
|
@@ -17353,7 +17391,7 @@ function parseCursorLine(line) {
|
|
|
17353
17391
|
}
|
|
17354
17392
|
if (type === "assistant") {
|
|
17355
17393
|
const text = textOf(frame.message);
|
|
17356
|
-
return text ? [{ type: "assistant-message", text }] : [];
|
|
17394
|
+
return text ? [streamPartial ? { type: "assistant-delta", id: String(frame.model_call_id || ""), text } : { type: "assistant-message", text }] : [];
|
|
17357
17395
|
}
|
|
17358
17396
|
if (type === "thinking" && subtype === "delta" && frame.text) {
|
|
17359
17397
|
return [{ type: "thinking", text: String(frame.text) }];
|
|
@@ -17382,7 +17420,8 @@ function parseCursorLine(line) {
|
|
|
17382
17420
|
reason: rejected ? blocked ? blockReason(rawReason) : rawReason || "rejected" : "",
|
|
17383
17421
|
pollId: blocked ? fixPollId(rawReason) : "",
|
|
17384
17422
|
exitCode: success ? Number(success.exitCode ?? 0) : null,
|
|
17385
|
-
output: String(success?.stdout || success?.stderr || "")
|
|
17423
|
+
output: String(success?.stdout || success?.stderr || ""),
|
|
17424
|
+
fileChanges: completedFileChanges(body)
|
|
17386
17425
|
}];
|
|
17387
17426
|
}
|
|
17388
17427
|
return [];
|
|
@@ -17397,11 +17436,15 @@ function parseCursorLine(line) {
|
|
|
17397
17436
|
}];
|
|
17398
17437
|
}
|
|
17399
17438
|
if (type === "result") {
|
|
17400
|
-
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
17439
|
+
const result = String(frame.result || "");
|
|
17440
|
+
return [
|
|
17441
|
+
...streamPartial && result ? [{ type: "assistant-message", text: result }] : [],
|
|
17442
|
+
{
|
|
17443
|
+
type: "turn-end",
|
|
17444
|
+
ok: !frame.is_error,
|
|
17445
|
+
text: result
|
|
17446
|
+
}
|
|
17447
|
+
];
|
|
17405
17448
|
}
|
|
17406
17449
|
return [];
|
|
17407
17450
|
}
|
|
@@ -17420,12 +17463,14 @@ function feed(buffer, chunk) {
|
|
|
17420
17463
|
const rest = parts.pop() ?? "";
|
|
17421
17464
|
return { lines: parts, rest };
|
|
17422
17465
|
}
|
|
17423
|
-
function cursorArgs(prompt) {
|
|
17466
|
+
function cursorArgs(prompt, resumeId = "") {
|
|
17424
17467
|
return [
|
|
17425
17468
|
"-p",
|
|
17426
17469
|
prompt,
|
|
17427
17470
|
"--output-format",
|
|
17428
17471
|
"stream-json",
|
|
17472
|
+
"--stream-partial-output",
|
|
17473
|
+
...resumeId ? ["--resume", resumeId] : [],
|
|
17429
17474
|
// --force auto-runs tools but does NOT bypass hooks (verified live), so
|
|
17430
17475
|
// Synkro's guards still gate every call; --trust loads workspace hooks.
|
|
17431
17476
|
"--force",
|
|
@@ -17437,6 +17482,7 @@ var init_cursor = __esm({
|
|
|
17437
17482
|
"cli/harness/cursor.ts"() {
|
|
17438
17483
|
"use strict";
|
|
17439
17484
|
init_events();
|
|
17485
|
+
init_changes();
|
|
17440
17486
|
TOOL_KINDS = {
|
|
17441
17487
|
shellToolCall: "shell",
|
|
17442
17488
|
readToolCall: "read",
|
|
@@ -17473,6 +17519,7 @@ function createTurnSink(opts) {
|
|
|
17473
17519
|
const now = opts.now || (() => Date.now());
|
|
17474
17520
|
const showPrompt = opts.showPrompt !== false;
|
|
17475
17521
|
let blocked = 0;
|
|
17522
|
+
let sessionId = "";
|
|
17476
17523
|
let replaying = false;
|
|
17477
17524
|
const seen = /* @__PURE__ */ new Set();
|
|
17478
17525
|
let sawTurnEnd = false;
|
|
@@ -17553,6 +17600,7 @@ function createTurnSink(opts) {
|
|
|
17553
17600
|
};
|
|
17554
17601
|
const emit2 = (event) => {
|
|
17555
17602
|
events.push(event);
|
|
17603
|
+
if (event.type === "session-start" && event.sessionId) sessionId = event.sessionId;
|
|
17556
17604
|
if (event.type === "tool-end" && event.blocked) blocked += 1;
|
|
17557
17605
|
opts.onEvent?.(event);
|
|
17558
17606
|
if (event.type === "notice" && event.kind === "retry") {
|
|
@@ -17603,25 +17651,25 @@ function createTurnSink(opts) {
|
|
|
17603
17651
|
}
|
|
17604
17652
|
replaying = true;
|
|
17605
17653
|
}
|
|
17606
|
-
const
|
|
17607
|
-
if (
|
|
17608
|
-
if (replaying && seen.has(
|
|
17654
|
+
const key2 = replayKey(event);
|
|
17655
|
+
if (key2) {
|
|
17656
|
+
if (replaying && seen.has(key2)) {
|
|
17609
17657
|
lastWasAnswer = event.type === "assistant-message";
|
|
17610
17658
|
settle();
|
|
17611
17659
|
return;
|
|
17612
17660
|
}
|
|
17613
|
-
seen.add(
|
|
17661
|
+
seen.add(key2);
|
|
17614
17662
|
}
|
|
17615
|
-
if (
|
|
17663
|
+
if (key2 || event.type === "thinking") lastWasAnswer = event.type === "assistant-message";
|
|
17616
17664
|
if (event.type === "turn-end") sawTurnEnd = true;
|
|
17617
17665
|
if (event.type === "assistant-message") sawAnswer = true;
|
|
17618
17666
|
emit2(event);
|
|
17619
17667
|
};
|
|
17620
17668
|
return {
|
|
17621
17669
|
chunk(text) {
|
|
17622
|
-
const { lines, rest } = feed(buffer, text);
|
|
17670
|
+
const { lines: lines2, rest } = feed(buffer, text);
|
|
17623
17671
|
buffer = rest;
|
|
17624
|
-
for (const line of
|
|
17672
|
+
for (const line of lines2) for (const event of parseCursorLine(line, opts.streamPartial)) take(event);
|
|
17625
17673
|
},
|
|
17626
17674
|
notice(text) {
|
|
17627
17675
|
emit2({ type: "notice", text });
|
|
@@ -17644,7 +17692,7 @@ function createTurnSink(opts) {
|
|
|
17644
17692
|
}
|
|
17645
17693
|
}
|
|
17646
17694
|
stopStatus();
|
|
17647
|
-
return { events, blocked, exitCode: interrupted ? 130 : sawAnswer ? 0 : exit };
|
|
17695
|
+
return { events, blocked, exitCode: interrupted ? 130 : sawAnswer ? 0 : exit, sessionId };
|
|
17648
17696
|
}
|
|
17649
17697
|
};
|
|
17650
17698
|
}
|
|
@@ -17660,10 +17708,11 @@ async function runCursorTurn(opts) {
|
|
|
17660
17708
|
animate: Boolean(process.stdout.isTTY),
|
|
17661
17709
|
showPrompt: opts.showPrompt,
|
|
17662
17710
|
showHeader: opts.showHeader,
|
|
17663
|
-
onGiveUp: () => stopHarness()
|
|
17711
|
+
onGiveUp: () => stopHarness(),
|
|
17712
|
+
streamPartial: true
|
|
17664
17713
|
});
|
|
17665
|
-
return new Promise((
|
|
17666
|
-
const child = spawn9("cursor-agent", cursorArgs(opts.prompt), {
|
|
17714
|
+
return new Promise((resolve9) => {
|
|
17715
|
+
const child = spawn9("cursor-agent", cursorArgs(opts.prompt, opts.resumeId), {
|
|
17667
17716
|
cwd: opts.cwd,
|
|
17668
17717
|
stdio: ["ignore", "pipe", "pipe"]
|
|
17669
17718
|
});
|
|
@@ -17691,11 +17740,11 @@ async function runCursorTurn(opts) {
|
|
|
17691
17740
|
});
|
|
17692
17741
|
child.on("close", (code) => {
|
|
17693
17742
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
17694
|
-
|
|
17743
|
+
resolve9(sink.finish(code ?? 0, interrupted));
|
|
17695
17744
|
});
|
|
17696
17745
|
child.on("error", (error) => {
|
|
17697
17746
|
sink.notice("failed to start cursor-agent: " + String(error));
|
|
17698
|
-
|
|
17747
|
+
resolve9(sink.finish(1, interrupted));
|
|
17699
17748
|
});
|
|
17700
17749
|
});
|
|
17701
17750
|
}
|
|
@@ -17711,360 +17760,553 @@ var init_run = __esm({
|
|
|
17711
17760
|
|
|
17712
17761
|
// cli/harness/codex.ts
|
|
17713
17762
|
import { spawn as spawn10 } from "child_process";
|
|
17714
|
-
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
return
|
|
17721
|
-
}
|
|
17722
|
-
|
|
17723
|
-
|
|
17724
|
-
|
|
17725
|
-
if (item
|
|
17726
|
-
|
|
17727
|
-
|
|
17728
|
-
|
|
17763
|
+
import { createInterface as createInterface6 } from "readline";
|
|
17764
|
+
function stringify(value) {
|
|
17765
|
+
if (typeof value === "string") return value;
|
|
17766
|
+
try {
|
|
17767
|
+
return JSON.stringify(value);
|
|
17768
|
+
} catch {
|
|
17769
|
+
return String(value ?? "");
|
|
17770
|
+
}
|
|
17771
|
+
}
|
|
17772
|
+
function toolKind(item) {
|
|
17773
|
+
if (item.type === "commandExecution") return "shell";
|
|
17774
|
+
if (item.type === "fileChange") return "edit";
|
|
17775
|
+
if (item.type === "webSearch") return "search";
|
|
17776
|
+
return "other";
|
|
17777
|
+
}
|
|
17778
|
+
function toolTarget(item) {
|
|
17779
|
+
if (item.type === "commandExecution") return item.command || "command";
|
|
17780
|
+
if (item.type === "fileChange") return (item.changes || []).map((change) => change.path || change.filePath || "").filter(Boolean).join(", ") || "workspace files";
|
|
17781
|
+
if (item.type === "mcpToolCall") return [item.server, item.tool].filter(Boolean).join(".") || "MCP tool";
|
|
17782
|
+
if (item.type === "dynamicToolCall") return [item.namespace, item.tool].filter(Boolean).join(".") || "tool";
|
|
17783
|
+
return item.query || item.type || "tool";
|
|
17784
|
+
}
|
|
17785
|
+
function fileChanges(item) {
|
|
17786
|
+
if (item.type !== "fileChange" || !Array.isArray(item.changes)) return [];
|
|
17787
|
+
return item.changes.flatMap((change) => {
|
|
17788
|
+
const path = String(change.path || change.filePath || "");
|
|
17789
|
+
if (!path) return [];
|
|
17790
|
+
const diff = String(change.diff || change.patch || "");
|
|
17791
|
+
const hasAdditions = change.additions != null || change.linesAdded != null;
|
|
17792
|
+
const hasDeletions = change.deletions != null || change.linesRemoved != null;
|
|
17793
|
+
if (!diff && !hasAdditions && !hasDeletions) return [];
|
|
17794
|
+
const counted = unifiedDiffCounts(diff);
|
|
17795
|
+
const additions = hasAdditions ? Number(change.additions ?? change.linesAdded) : counted.additions;
|
|
17796
|
+
const deletions = hasDeletions ? Number(change.deletions ?? change.linesRemoved) : counted.deletions;
|
|
17797
|
+
return [{ path, additions, deletions }];
|
|
17798
|
+
});
|
|
17729
17799
|
}
|
|
17730
|
-
function
|
|
17731
|
-
|
|
17732
|
-
if (
|
|
17733
|
-
|
|
17734
|
-
|
|
17800
|
+
function codexAccountUsageEvent(value) {
|
|
17801
|
+
const usage2 = codexUsageFromRateLimitsResponse(value);
|
|
17802
|
+
if (usage2?.util7d == null) return null;
|
|
17803
|
+
return {
|
|
17804
|
+
type: "account-usage",
|
|
17805
|
+
period: "weekly",
|
|
17806
|
+
remainingPercent: Math.max(0, Math.min(100, Math.round((1 - usage2.util7d) * 100))),
|
|
17807
|
+
resetsAt: usage2.reset7d
|
|
17808
|
+
};
|
|
17735
17809
|
}
|
|
17736
|
-
function
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17740
|
-
|
|
17741
|
-
|
|
17742
|
-
|
|
17743
|
-
|
|
17744
|
-
|
|
17745
|
-
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
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");
|
|
17810
|
+
function codexMessageTurnId(message) {
|
|
17811
|
+
return String(message.params?.turnId || message.params?.turn?.id || "");
|
|
17812
|
+
}
|
|
17813
|
+
function codexMessageIsTurnScoped(message) {
|
|
17814
|
+
const method = String(message.method || "");
|
|
17815
|
+
return method.startsWith("item/") || method === "hook/completed" || method === "turn/plan/updated";
|
|
17816
|
+
}
|
|
17817
|
+
function codexEvents(message) {
|
|
17818
|
+
const method = String(message.method || "");
|
|
17819
|
+
const params = message.params || {};
|
|
17820
|
+
if (method === "account/rateLimits/updated") {
|
|
17821
|
+
const event = codexAccountUsageEvent(params);
|
|
17822
|
+
return event ? [event] : [];
|
|
17823
|
+
}
|
|
17824
|
+
const item = params.item || {};
|
|
17825
|
+
if (method === "item/agentMessage/delta") return [{ type: "assistant-delta", id: params.itemId || "", text: params.delta || "" }];
|
|
17826
|
+
if (method === "item/reasoning/summaryTextDelta") return [{ type: "thinking", text: params.delta || "Thinking" }];
|
|
17827
|
+
const tools = ["commandExecution", "fileChange", "mcpToolCall", "dynamicToolCall", "webSearch"];
|
|
17828
|
+
if (method === "item/started" && tools.includes(item.type)) {
|
|
17829
|
+
return [{ type: "tool-start", id: item.id || "", kind: toolKind(item), target: toolTarget(item), description: "" }];
|
|
17830
|
+
}
|
|
17831
|
+
if (method === "item/completed" && item.type === "agentMessage") return [{ type: "assistant-message", text: item.text || "" }];
|
|
17832
|
+
if (method === "item/completed" && tools.includes(item.type)) {
|
|
17833
|
+
const raw = stringify(item.aggregatedOutput || item.error?.message || item.result?.content || item.contentItems || "");
|
|
17797
17834
|
const status = String(item.status || "");
|
|
17798
|
-
const blocked = BLOCK_MARKER.test(
|
|
17799
|
-
const
|
|
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__");
|
|
17835
|
+
const blocked = BLOCK_MARKER.test(raw) || /declined|blocked|denied/.test(status);
|
|
17836
|
+
const ok = !blocked && !/failed|error/.test(status) && (item.exitCode === null || item.exitCode === void 0 || item.exitCode === 0);
|
|
17811
17837
|
return [{
|
|
17812
17838
|
type: "tool-end",
|
|
17813
|
-
id:
|
|
17814
|
-
|
|
17815
|
-
|
|
17839
|
+
id: item.id || "",
|
|
17840
|
+
kind: toolKind(item),
|
|
17841
|
+
target: toolTarget(item),
|
|
17842
|
+
ok,
|
|
17816
17843
|
blocked,
|
|
17817
|
-
reason: blocked ? blockReason(
|
|
17818
|
-
pollId: blocked ? fixPollId(
|
|
17819
|
-
exitCode: item.exitCode ===
|
|
17820
|
-
output:
|
|
17821
|
-
durationMs
|
|
17844
|
+
reason: blocked ? blockReason(raw || status) : "",
|
|
17845
|
+
pollId: blocked ? fixPollId(raw) : "",
|
|
17846
|
+
exitCode: typeof item.exitCode === "number" ? item.exitCode : null,
|
|
17847
|
+
output: raw,
|
|
17848
|
+
durationMs: typeof item.durationMs === "number" ? item.durationMs : null,
|
|
17849
|
+
fileChanges: fileChanges(item)
|
|
17822
17850
|
}];
|
|
17823
17851
|
}
|
|
17824
|
-
if (method === "
|
|
17825
|
-
const
|
|
17826
|
-
|
|
17827
|
-
const
|
|
17828
|
-
|
|
17829
|
-
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
|
|
17833
|
-
|
|
17834
|
-
|
|
17835
|
-
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
|
|
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;
|
|
17852
|
+
if (method === "hook/completed") {
|
|
17853
|
+
const run2 = params.run || {};
|
|
17854
|
+
if (run2.status !== "blocked") return [];
|
|
17855
|
+
const reason = (run2.entries || []).map((entry) => entry.text || "").filter(Boolean).join("\n") || run2.statusMessage || "blocked by policy";
|
|
17856
|
+
return [{
|
|
17857
|
+
type: "tool-end",
|
|
17858
|
+
id: "hook:" + (run2.id || ""),
|
|
17859
|
+
kind: "other",
|
|
17860
|
+
target: run2.eventName || "Guard",
|
|
17861
|
+
ok: false,
|
|
17862
|
+
blocked: true,
|
|
17863
|
+
reason: blockReason(reason),
|
|
17864
|
+
exitCode: null,
|
|
17865
|
+
output: reason,
|
|
17866
|
+
pollId: fixPollId(reason),
|
|
17867
|
+
durationMs: typeof run2.durationMs === "number" ? run2.durationMs : null
|
|
17868
|
+
}];
|
|
17850
17869
|
}
|
|
17851
|
-
|
|
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();
|
|
17870
|
+
if (method === "turn/plan/updated") {
|
|
17871
|
+
return [{ type: "plan", steps: (params.plan || []).map((row2) => ({ step: row2.step || "", status: row2.status || "pending" })) }];
|
|
17882
17872
|
}
|
|
17873
|
+
if (method === "thread/tokenUsage/updated") {
|
|
17874
|
+
const usage2 = params.tokenUsage || {};
|
|
17875
|
+
const total = usage2.total || usage2.last || {};
|
|
17876
|
+
const used = Number(total.totalTokens ?? total.total_tokens ?? usage2.totalTokens ?? 0);
|
|
17877
|
+
const context = Number(usage2.modelContextWindow ?? usage2.model_context_window ?? 0);
|
|
17878
|
+
return [{ type: "usage", used, contextWindow: context || null }];
|
|
17879
|
+
}
|
|
17880
|
+
if (method === "warning" || method === "error") return [{ type: "notice", text: params.message || params.error?.message || "Codex transport warning" }];
|
|
17881
|
+
return [];
|
|
17883
17882
|
}
|
|
17884
|
-
var
|
|
17883
|
+
var CodexSession;
|
|
17885
17884
|
var init_codex = __esm({
|
|
17886
17885
|
"cli/harness/codex.ts"() {
|
|
17887
17886
|
"use strict";
|
|
17888
17887
|
init_composer();
|
|
17889
17888
|
init_events();
|
|
17889
|
+
init_changes();
|
|
17890
|
+
init_codexUsage();
|
|
17890
17891
|
init_run();
|
|
17891
17892
|
init_render2();
|
|
17892
|
-
|
|
17893
|
+
CodexSession = class {
|
|
17894
|
+
constructor(cwd, resumeId = "") {
|
|
17895
|
+
this.cwd = cwd;
|
|
17896
|
+
this.resumeId = resumeId;
|
|
17897
|
+
}
|
|
17898
|
+
cwd;
|
|
17899
|
+
resumeId;
|
|
17893
17900
|
child = null;
|
|
17894
|
-
buffer = "";
|
|
17895
17901
|
nextId = 1;
|
|
17896
17902
|
pending = /* @__PURE__ */ new Map();
|
|
17897
|
-
|
|
17903
|
+
onEvent = null;
|
|
17904
|
+
activeTurn = "";
|
|
17905
|
+
turnDone = null;
|
|
17906
|
+
completedTurns = /* @__PURE__ */ new Map();
|
|
17907
|
+
accountUsage = null;
|
|
17898
17908
|
threadId = "";
|
|
17899
|
-
model = "
|
|
17900
|
-
|
|
17901
|
-
|
|
17902
|
-
send(message) {
|
|
17903
|
-
this.child?.stdin.write(JSON.stringify({ jsonrpc: "2.0", ...message }) + "\n");
|
|
17904
|
-
}
|
|
17905
|
-
request(method, params) {
|
|
17909
|
+
model = "";
|
|
17910
|
+
request(method, params, timeoutMs = 0) {
|
|
17911
|
+
if (!this.child) return Promise.reject(new Error("Codex app-server is not running"));
|
|
17906
17912
|
const id = this.nextId++;
|
|
17907
|
-
|
|
17908
|
-
|
|
17909
|
-
|
|
17913
|
+
this.child.stdin.write(JSON.stringify({ method, id, params }) + "\n");
|
|
17914
|
+
return new Promise((resolve9, reject) => {
|
|
17915
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
17916
|
+
this.pending.delete(id);
|
|
17917
|
+
reject(new Error(method + " timed out"));
|
|
17918
|
+
}, timeoutMs) : null;
|
|
17919
|
+
this.pending.set(id, {
|
|
17920
|
+
resolve: (value) => {
|
|
17921
|
+
if (timer) clearTimeout(timer);
|
|
17922
|
+
resolve9(value);
|
|
17923
|
+
},
|
|
17924
|
+
reject: (error) => {
|
|
17925
|
+
if (timer) clearTimeout(timer);
|
|
17926
|
+
reject(error);
|
|
17927
|
+
}
|
|
17928
|
+
});
|
|
17910
17929
|
});
|
|
17911
17930
|
}
|
|
17912
|
-
|
|
17913
|
-
this.
|
|
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, {});
|
|
17931
|
+
notify(method, params = {}) {
|
|
17932
|
+
this.child?.stdin.write(JSON.stringify({ method, params }) + "\n");
|
|
17936
17933
|
}
|
|
17937
|
-
|
|
17938
|
-
|
|
17939
|
-
|
|
17940
|
-
|
|
17941
|
-
|
|
17934
|
+
receive(message) {
|
|
17935
|
+
if (message.id !== void 0 && !message.method) {
|
|
17936
|
+
const pending = this.pending.get(message.id);
|
|
17937
|
+
if (!pending) return;
|
|
17938
|
+
this.pending.delete(message.id);
|
|
17939
|
+
if (message.error) pending.reject(new Error(message.error.message || stringify(message.error)));
|
|
17940
|
+
else pending.resolve(message.result);
|
|
17942
17941
|
return;
|
|
17943
17942
|
}
|
|
17944
|
-
if (message.
|
|
17945
|
-
|
|
17943
|
+
if (message.id !== void 0 && message.method) {
|
|
17944
|
+
this.child?.stdin.write(JSON.stringify({ id: message.id, result: { decision: "decline" } }) + "\n");
|
|
17945
|
+
this.onEvent?.({ type: "notice", text: "Codex requested an approval the embedded policy did not permit." });
|
|
17946
17946
|
return;
|
|
17947
17947
|
}
|
|
17948
|
-
|
|
17949
|
-
|
|
17950
|
-
|
|
17951
|
-
|
|
17952
|
-
|
|
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;
|
|
17948
|
+
const messageTurnId = codexMessageTurnId(message);
|
|
17949
|
+
const staleTurnEvent = codexMessageIsTurnScoped(message) && (!this.activeTurn || messageTurnId && messageTurnId !== this.activeTurn);
|
|
17950
|
+
for (const event of codexEvents(message)) {
|
|
17951
|
+
if (event.type === "account-usage") this.accountUsage = event;
|
|
17952
|
+
if (!staleTurnEvent) this.onEvent?.(event);
|
|
17960
17953
|
}
|
|
17961
|
-
if (message.id
|
|
17962
|
-
const
|
|
17963
|
-
|
|
17964
|
-
this.
|
|
17965
|
-
|
|
17966
|
-
|
|
17954
|
+
if (message.method === "turn/completed" && message.params?.turn?.id === this.activeTurn) {
|
|
17955
|
+
const status = String(message.params.turn.status || "");
|
|
17956
|
+
this.completedTurns.set(message.params.turn.id, status);
|
|
17957
|
+
this.turnDone?.(status);
|
|
17958
|
+
} else if (message.method === "turn/completed" && message.params?.turn?.id) {
|
|
17959
|
+
this.completedTurns.set(message.params.turn.id, String(message.params.turn.status || ""));
|
|
17967
17960
|
}
|
|
17968
17961
|
}
|
|
17969
|
-
async start(
|
|
17970
|
-
|
|
17971
|
-
this.
|
|
17972
|
-
|
|
17973
|
-
|
|
17974
|
-
this.
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17962
|
+
async start() {
|
|
17963
|
+
this.child = spawn10("codex", ["app-server", "--stdio"], { cwd: this.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
17964
|
+
this.child.on("error", (cause) => {
|
|
17965
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
17966
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
17967
|
+
this.pending.clear();
|
|
17968
|
+
this.turnDone?.("failed");
|
|
17969
|
+
});
|
|
17970
|
+
createInterface6({ input: this.child.stdout }).on("line", (line) => {
|
|
17971
|
+
try {
|
|
17972
|
+
this.receive(JSON.parse(line));
|
|
17973
|
+
} catch {
|
|
17981
17974
|
}
|
|
17982
17975
|
});
|
|
17983
17976
|
this.child.stderr.on("data", (chunk) => {
|
|
17984
|
-
const
|
|
17985
|
-
if (
|
|
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;
|
|
17977
|
+
const message = String(chunk || "").trim();
|
|
17978
|
+
if (message) this.onEvent?.({ type: "notice", text: message });
|
|
17996
17979
|
});
|
|
17997
|
-
this.child.
|
|
17998
|
-
const
|
|
17999
|
-
const
|
|
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);
|
|
17980
|
+
this.child.once("exit", (code) => {
|
|
17981
|
+
const error = new Error("Codex app-server exited" + (code === null ? "" : " (" + code + ")"));
|
|
17982
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
18006
17983
|
this.pending.clear();
|
|
17984
|
+
this.turnDone?.("failed");
|
|
18007
17985
|
});
|
|
18008
|
-
|
|
18009
|
-
this.initialized
|
|
18010
|
-
|
|
18011
|
-
|
|
18012
|
-
|
|
18013
|
-
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18017
|
-
|
|
18018
|
-
|
|
18019
|
-
|
|
18020
|
-
|
|
17986
|
+
await this.request("initialize", { clientInfo: { name: "synkro-terminal", title: "Synkro", version: "1" }, capabilities: { experimentalApi: true } });
|
|
17987
|
+
this.notify("initialized");
|
|
17988
|
+
try {
|
|
17989
|
+
const event = codexAccountUsageEvent(await this.request("account/rateLimits/read", null, 4e3));
|
|
17990
|
+
if (event) this.accountUsage = event;
|
|
17991
|
+
} catch {
|
|
17992
|
+
}
|
|
17993
|
+
let result;
|
|
17994
|
+
try {
|
|
17995
|
+
result = this.resumeId ? await this.request("thread/resume", { threadId: this.resumeId, cwd: this.cwd, approvalPolicy: "never", sandbox: "workspace-write" }) : await this.request("thread/start", { cwd: this.cwd, approvalPolicy: "never", sandbox: "workspace-write", serviceName: "Synkro" });
|
|
17996
|
+
} catch (error) {
|
|
17997
|
+
if (!this.resumeId) throw error;
|
|
17998
|
+
result = await this.request("thread/start", { cwd: this.cwd, approvalPolicy: "never", sandbox: "workspace-write", serviceName: "Synkro" });
|
|
17999
|
+
}
|
|
18000
|
+
this.threadId = result.thread?.id || "";
|
|
18001
|
+
this.model = result.model || "";
|
|
18021
18002
|
}
|
|
18022
|
-
async
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
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
|
-
});
|
|
18003
|
+
async runTurn(prompt, onEvent, signal) {
|
|
18004
|
+
let blocked = 0;
|
|
18005
|
+
let turnId = "";
|
|
18006
|
+
let interrupt = null;
|
|
18007
|
+
this.onEvent = (event) => {
|
|
18008
|
+
if (event.type === "tool-end" && event.blocked) blocked++;
|
|
18009
|
+
onEvent(event);
|
|
18046
18010
|
};
|
|
18047
|
-
if (opts.signal?.aborted) onAbort();
|
|
18048
|
-
else opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
18049
18011
|
try {
|
|
18050
|
-
|
|
18051
|
-
|
|
18052
|
-
|
|
18053
|
-
|
|
18012
|
+
onEvent({ type: "session-start", sessionId: this.threadId, model: this.model || "Codex", cwd: this.cwd, authSource: "login" });
|
|
18013
|
+
if (this.accountUsage) onEvent(this.accountUsage);
|
|
18014
|
+
onEvent({ type: "user-message", text: prompt });
|
|
18015
|
+
const response = await this.request("turn/start", { threadId: this.threadId, input: [{ type: "text", text: prompt }] });
|
|
18016
|
+
turnId = response.turn?.id || "";
|
|
18017
|
+
this.activeTurn = turnId;
|
|
18018
|
+
let aborted = false;
|
|
18019
|
+
let interruptSent = false;
|
|
18020
|
+
interrupt = () => {
|
|
18021
|
+
aborted = true;
|
|
18022
|
+
if (interruptSent || !this.activeTurn) return;
|
|
18023
|
+
interruptSent = true;
|
|
18024
|
+
void this.request("turn/interrupt", { threadId: this.threadId, turnId: this.activeTurn }).catch(() => {
|
|
18025
|
+
});
|
|
18026
|
+
};
|
|
18027
|
+
if (signal?.aborted) interrupt();
|
|
18028
|
+
else signal?.addEventListener("abort", interrupt, { once: true });
|
|
18029
|
+
const alreadyDone = this.completedTurns.get(turnId);
|
|
18030
|
+
const status = alreadyDone ?? await new Promise((resolve9) => {
|
|
18031
|
+
this.turnDone = resolve9;
|
|
18054
18032
|
});
|
|
18055
|
-
|
|
18033
|
+
this.completedTurns.delete(turnId);
|
|
18034
|
+
signal?.removeEventListener("abort", interrupt);
|
|
18035
|
+
this.activeTurn = "";
|
|
18036
|
+
this.turnDone = null;
|
|
18037
|
+
this.onEvent = null;
|
|
18038
|
+
onEvent({ type: "turn-end", ok: aborted || /completed/.test(status), text: aborted ? "interrupted" : status });
|
|
18039
|
+
return { exitCode: aborted ? 130 : /completed/.test(status) ? 0 : 1, blocked, sessionId: this.threadId };
|
|
18056
18040
|
} finally {
|
|
18057
|
-
|
|
18041
|
+
if (interrupt) signal?.removeEventListener("abort", interrupt);
|
|
18042
|
+
if (turnId) this.completedTurns.delete(turnId);
|
|
18043
|
+
this.activeTurn = "";
|
|
18044
|
+
this.turnDone = null;
|
|
18045
|
+
this.onEvent = null;
|
|
18058
18046
|
}
|
|
18059
18047
|
}
|
|
18060
18048
|
close() {
|
|
18061
|
-
this.child?.kill();
|
|
18049
|
+
this.child?.kill("SIGTERM");
|
|
18062
18050
|
this.child = null;
|
|
18063
18051
|
}
|
|
18064
18052
|
};
|
|
18065
18053
|
}
|
|
18066
18054
|
});
|
|
18067
18055
|
|
|
18056
|
+
// cli/harness/screen.ts
|
|
18057
|
+
var ANSI, REVERSE, STREAM_DRAW_MS, STATUS_TICK_MS, resetDateFormat, visible, pathLabel, SynkroScreen;
|
|
18058
|
+
var init_screen = __esm({
|
|
18059
|
+
"cli/harness/screen.ts"() {
|
|
18060
|
+
"use strict";
|
|
18061
|
+
init_render2();
|
|
18062
|
+
init_changes();
|
|
18063
|
+
ANSI = /\x1b\[[0-?]*[ -\/]*[@-~]/g;
|
|
18064
|
+
REVERSE = "\x1B[7m";
|
|
18065
|
+
STREAM_DRAW_MS = 80;
|
|
18066
|
+
STATUS_TICK_MS = 240;
|
|
18067
|
+
resetDateFormat = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric" });
|
|
18068
|
+
visible = (value) => value.replace(ANSI, "").length;
|
|
18069
|
+
pathLabel = (cwd) => {
|
|
18070
|
+
const parts = cwd.split("/").filter(Boolean);
|
|
18071
|
+
return parts.length > 2 ? "\u2026/" + parts.slice(-2).join("/") : cwd;
|
|
18072
|
+
};
|
|
18073
|
+
SynkroScreen = class {
|
|
18074
|
+
constructor(harness, cwd, write2 = (text) => process.stdout.write(text)) {
|
|
18075
|
+
this.harness = harness;
|
|
18076
|
+
this.cwd = cwd;
|
|
18077
|
+
this.write = write2;
|
|
18078
|
+
this.changes = new OperationChangeTracker(cwd);
|
|
18079
|
+
}
|
|
18080
|
+
harness;
|
|
18081
|
+
cwd;
|
|
18082
|
+
write;
|
|
18083
|
+
transcript = [];
|
|
18084
|
+
draft = "";
|
|
18085
|
+
input = "";
|
|
18086
|
+
selectionAnchor = null;
|
|
18087
|
+
selectionFocus = null;
|
|
18088
|
+
status = "Ready";
|
|
18089
|
+
statusSince = Date.now();
|
|
18090
|
+
tick = 0;
|
|
18091
|
+
used = 0;
|
|
18092
|
+
context = null;
|
|
18093
|
+
weeklyRemaining = null;
|
|
18094
|
+
weeklyResetsAt = null;
|
|
18095
|
+
model = "";
|
|
18096
|
+
timer = null;
|
|
18097
|
+
drawTimer = null;
|
|
18098
|
+
lastDrawAt = 0;
|
|
18099
|
+
scrollOffset = 0;
|
|
18100
|
+
changes;
|
|
18101
|
+
resize = () => this.draw();
|
|
18102
|
+
enter() {
|
|
18103
|
+
this.write("\x1B[?1049h\x1B[?25l\x1B[?1000h\x1B[?1002h\x1B[?1006h");
|
|
18104
|
+
process.stdout.on("resize", this.resize);
|
|
18105
|
+
this.timer = setInterval(() => {
|
|
18106
|
+
if (this.status === "Ready") return;
|
|
18107
|
+
this.tick++;
|
|
18108
|
+
this.queueDraw();
|
|
18109
|
+
}, STATUS_TICK_MS);
|
|
18110
|
+
this.timer.unref();
|
|
18111
|
+
this.draw();
|
|
18112
|
+
}
|
|
18113
|
+
leave() {
|
|
18114
|
+
if (this.timer) clearInterval(this.timer);
|
|
18115
|
+
if (this.drawTimer) clearTimeout(this.drawTimer);
|
|
18116
|
+
process.stdout.removeListener("resize", this.resize);
|
|
18117
|
+
this.write(S2.reset + "\x1B[?1006l\x1B[?1002l\x1B[?1000l\x1B[?25h\x1B[?1049l");
|
|
18118
|
+
}
|
|
18119
|
+
setInput(value) {
|
|
18120
|
+
this.input = cleanOutput(value);
|
|
18121
|
+
this.selectionAnchor = null;
|
|
18122
|
+
this.selectionFocus = null;
|
|
18123
|
+
this.draw();
|
|
18124
|
+
}
|
|
18125
|
+
selectionRange() {
|
|
18126
|
+
if (this.selectionAnchor === null || this.selectionFocus === null || this.selectionAnchor === this.selectionFocus) return null;
|
|
18127
|
+
return [Math.min(this.selectionAnchor, this.selectionFocus), Math.max(this.selectionAnchor, this.selectionFocus)];
|
|
18128
|
+
}
|
|
18129
|
+
clearSelection() {
|
|
18130
|
+
this.selectionAnchor = null;
|
|
18131
|
+
this.selectionFocus = null;
|
|
18132
|
+
this.draw();
|
|
18133
|
+
}
|
|
18134
|
+
scrollBy(lines2) {
|
|
18135
|
+
this.scrollOffset = Math.max(0, this.scrollOffset + lines2);
|
|
18136
|
+
this.draw();
|
|
18137
|
+
}
|
|
18138
|
+
scrollToBottom() {
|
|
18139
|
+
this.scrollOffset = 0;
|
|
18140
|
+
this.draw();
|
|
18141
|
+
}
|
|
18142
|
+
/** Select text using one-based SGR mouse coordinates from the terminal. */
|
|
18143
|
+
selectInputAt(x, y, extend, width = this.width(), height = this.height()) {
|
|
18144
|
+
const rows = this.inputRows(width);
|
|
18145
|
+
const composerLength = rows.length + 2;
|
|
18146
|
+
const viewport = Math.max(3, height - composerLength - 2);
|
|
18147
|
+
const lineIndex = y - (viewport + 2);
|
|
18148
|
+
const row2 = rows[lineIndex];
|
|
18149
|
+
if (!row2) return;
|
|
18150
|
+
const index = Math.min(row2.end, row2.start + Math.max(0, x - 5));
|
|
18151
|
+
if (!extend || this.selectionAnchor === null) this.selectionAnchor = index;
|
|
18152
|
+
this.selectionFocus = index;
|
|
18153
|
+
this.draw();
|
|
18154
|
+
}
|
|
18155
|
+
updateStatus(value) {
|
|
18156
|
+
if (this.status === value) return false;
|
|
18157
|
+
this.status = value;
|
|
18158
|
+
this.statusSince = Date.now();
|
|
18159
|
+
return true;
|
|
18160
|
+
}
|
|
18161
|
+
setStatus(value) {
|
|
18162
|
+
if (this.updateStatus(value)) this.draw();
|
|
18163
|
+
}
|
|
18164
|
+
add(event) {
|
|
18165
|
+
event = this.changes.observe(event);
|
|
18166
|
+
if (event.type === "session-start") {
|
|
18167
|
+
this.model = event.model;
|
|
18168
|
+
this.draw();
|
|
18169
|
+
return;
|
|
18170
|
+
}
|
|
18171
|
+
if (event.type === "assistant-delta") {
|
|
18172
|
+
this.draft += event.text;
|
|
18173
|
+
this.updateStatus("Writing\u2026");
|
|
18174
|
+
this.queueDraw();
|
|
18175
|
+
return;
|
|
18176
|
+
}
|
|
18177
|
+
if (event.type === "assistant-message") this.draft = "";
|
|
18178
|
+
if (event.type === "usage") {
|
|
18179
|
+
this.used = event.used;
|
|
18180
|
+
this.context = event.contextWindow;
|
|
18181
|
+
if (event.model) this.model = event.model;
|
|
18182
|
+
this.draw();
|
|
18183
|
+
return;
|
|
18184
|
+
}
|
|
18185
|
+
if (event.type === "account-usage") {
|
|
18186
|
+
this.weeklyRemaining = event.remainingPercent;
|
|
18187
|
+
this.weeklyResetsAt = event.resetsAt;
|
|
18188
|
+
this.draw();
|
|
18189
|
+
return;
|
|
18190
|
+
}
|
|
18191
|
+
if (event.type === "thinking") {
|
|
18192
|
+
if (this.updateStatus(event.text || "Thinking\u2026")) this.draw();
|
|
18193
|
+
return;
|
|
18194
|
+
}
|
|
18195
|
+
if (event.type === "tool-start") this.updateStatus("Working\u2026");
|
|
18196
|
+
if (event.type === "tool-end") this.updateStatus(event.blocked ? "Guard enforced" : "Working\u2026");
|
|
18197
|
+
if (event.type === "turn-end") this.updateStatus(event.ok ? "Ready" : "Turn ended");
|
|
18198
|
+
const rendered = renderEvent(event, this.width());
|
|
18199
|
+
if (this.scrollOffset > 0) this.scrollOffset += rendered.length;
|
|
18200
|
+
this.transcript.push(...rendered);
|
|
18201
|
+
this.draw();
|
|
18202
|
+
}
|
|
18203
|
+
width() {
|
|
18204
|
+
return Math.max(50, Number(process.stdout.columns || 100));
|
|
18205
|
+
}
|
|
18206
|
+
height() {
|
|
18207
|
+
return Math.max(16, Number(process.stdout.rows || 30));
|
|
18208
|
+
}
|
|
18209
|
+
inputRows(width) {
|
|
18210
|
+
const lineWidth = Math.max(1, Math.max(30, width - 4) - 4);
|
|
18211
|
+
if (!this.input) return [{ text: "", start: 0, end: 0 }];
|
|
18212
|
+
const rows = [];
|
|
18213
|
+
for (let start = 0; start < this.input.length; start += lineWidth) {
|
|
18214
|
+
const text = this.input.slice(start, start + lineWidth);
|
|
18215
|
+
rows.push({ text, start, end: start + text.length });
|
|
18216
|
+
}
|
|
18217
|
+
return rows.slice(-3);
|
|
18218
|
+
}
|
|
18219
|
+
selectedText(row2) {
|
|
18220
|
+
const range = this.selectionRange();
|
|
18221
|
+
if (!range || range[1] <= row2.start || range[0] >= row2.end) return S2.text + row2.text;
|
|
18222
|
+
const from = Math.max(range[0], row2.start) - row2.start;
|
|
18223
|
+
const to = Math.min(range[1], row2.end) - row2.start;
|
|
18224
|
+
return S2.text + row2.text.slice(0, from) + REVERSE + row2.text.slice(from, to) + S2.reset + S2.composer + S2.text + row2.text.slice(to);
|
|
18225
|
+
}
|
|
18226
|
+
frame(width = this.width(), height = this.height()) {
|
|
18227
|
+
const inner = Math.max(30, width - 4);
|
|
18228
|
+
const provider = this.harness === "codex" ? "Codex" : "Cursor";
|
|
18229
|
+
const draft = this.draft ? ["", ...wrap(this.draft, inner - 2).map((line, i) => S2.agent + " " + (i ? " " : "\u23FA ") + line + S2.reset)] : [];
|
|
18230
|
+
const status = this.status === "Ready" ? S2.secondary + " \xB7 Ready" + S2.reset : statusLine({ tick: this.tick, text: this.status, elapsedMs: Date.now() - this.statusSince, width });
|
|
18231
|
+
const rule = S2.border + " " + "\u2500".repeat(inner) + S2.reset;
|
|
18232
|
+
const inputRows = this.inputRows(width);
|
|
18233
|
+
const composer = [rule, ...inputRows.map((row2, i) => S2.composer + (i ? " " : S2.user + " \u276F ") + this.selectedText(row2) + (i === inputRows.length - 1 ? S2.user + " \u258C" : "") + S2.reset), rule];
|
|
18234
|
+
const usedPercent = this.context ? Math.min(100, Math.round(this.used / this.context * 100)) : null;
|
|
18235
|
+
const contextUsage = usedPercent === null ? "\u2014 context" : usedPercent + "% context";
|
|
18236
|
+
const resetDate = this.weeklyResetsAt == null ? "" : " \xB7 " + resetDateFormat.format(new Date(this.weeklyResetsAt * 1e3));
|
|
18237
|
+
const accountUsage = this.weeklyRemaining === null ? "Weekly \u2014" : "Weekly " + this.weeklyRemaining + "% left" + resetDate;
|
|
18238
|
+
const usage2 = contextUsage + " \xB7 " + accountUsage;
|
|
18239
|
+
const left = S2.secondary + " " + pathLabel(this.cwd) + S2.reset + S2.canvas;
|
|
18240
|
+
const badge = S2.panel + S2.secondary + " " + usage2 + " " + S2.reset + S2.canvas;
|
|
18241
|
+
const right = badge + S2.secondary + " \xB7 " + provider + (this.model ? " " + cleanOutput(this.model) : "") + " \xB7 \u29C9 powered by synkro " + S2.reset + S2.canvas;
|
|
18242
|
+
const footer = left + " ".repeat(Math.max(1, width - visible(left) - visible(right))) + right;
|
|
18243
|
+
const viewport = Math.max(3, height - composer.length - 2);
|
|
18244
|
+
const fullContent = [...this.transcript, ...draft, status];
|
|
18245
|
+
const maxScroll = Math.max(0, fullContent.length - viewport);
|
|
18246
|
+
const effectiveOffset = Math.min(this.scrollOffset, maxScroll);
|
|
18247
|
+
const end = fullContent.length - effectiveOffset;
|
|
18248
|
+
const content = fullContent.slice(Math.max(0, end - viewport), end);
|
|
18249
|
+
while (content.length < viewport) content.unshift("");
|
|
18250
|
+
const rows = [...content, ...composer, footer].slice(0, height);
|
|
18251
|
+
while (rows.length < height) rows.push("");
|
|
18252
|
+
return rows.map((line) => {
|
|
18253
|
+
const fill = line.startsWith(S2.panel) ? S2.panel : line.includes(S2.composer) ? S2.composer : S2.canvas;
|
|
18254
|
+
return S2.canvas + line + fill + " ".repeat(Math.max(0, width - visible(line))) + S2.reset;
|
|
18255
|
+
}).join("\n");
|
|
18256
|
+
}
|
|
18257
|
+
queueDraw() {
|
|
18258
|
+
if (this.drawTimer) return;
|
|
18259
|
+
const delay = Math.max(0, STREAM_DRAW_MS - (Date.now() - this.lastDrawAt));
|
|
18260
|
+
this.drawTimer = setTimeout(() => {
|
|
18261
|
+
this.drawTimer = null;
|
|
18262
|
+
this.draw();
|
|
18263
|
+
}, delay);
|
|
18264
|
+
this.drawTimer.unref?.();
|
|
18265
|
+
}
|
|
18266
|
+
draw() {
|
|
18267
|
+
if (this.drawTimer) {
|
|
18268
|
+
clearTimeout(this.drawTimer);
|
|
18269
|
+
this.drawTimer = null;
|
|
18270
|
+
}
|
|
18271
|
+
this.lastDrawAt = Date.now();
|
|
18272
|
+
this.write("\x1B[H" + this.frame());
|
|
18273
|
+
}
|
|
18274
|
+
};
|
|
18275
|
+
}
|
|
18276
|
+
});
|
|
18277
|
+
|
|
18278
|
+
// cli/harness/state.ts
|
|
18279
|
+
import { mkdirSync as mkdirSync24, readFileSync as readFileSync34, writeFileSync as writeFileSync28 } from "fs";
|
|
18280
|
+
import { homedir as homedir39 } from "os";
|
|
18281
|
+
import { dirname as dirname13, join as join38 } from "path";
|
|
18282
|
+
function load(file = STATE_FILE) {
|
|
18283
|
+
try {
|
|
18284
|
+
const value = JSON.parse(readFileSync34(file, "utf8"));
|
|
18285
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
18286
|
+
} catch {
|
|
18287
|
+
return {};
|
|
18288
|
+
}
|
|
18289
|
+
}
|
|
18290
|
+
function loadSessionId(harness, cwd, file = STATE_FILE) {
|
|
18291
|
+
return load(file)[key(harness, cwd)] || "";
|
|
18292
|
+
}
|
|
18293
|
+
function saveSessionId(harness, cwd, sessionId, file = STATE_FILE) {
|
|
18294
|
+
if (!sessionId) return;
|
|
18295
|
+
try {
|
|
18296
|
+
mkdirSync24(dirname13(file), { recursive: true });
|
|
18297
|
+
writeFileSync28(file, JSON.stringify({ ...load(file), [key(harness, cwd)]: sessionId }, null, 2));
|
|
18298
|
+
} catch {
|
|
18299
|
+
}
|
|
18300
|
+
}
|
|
18301
|
+
var STATE_FILE, key;
|
|
18302
|
+
var init_state = __esm({
|
|
18303
|
+
"cli/harness/state.ts"() {
|
|
18304
|
+
"use strict";
|
|
18305
|
+
STATE_FILE = join38(homedir39(), ".synkro", "terminal-sessions.json");
|
|
18306
|
+
key = (harness, cwd) => harness + ":" + cwd.replace(/\/+$/, "");
|
|
18307
|
+
}
|
|
18308
|
+
});
|
|
18309
|
+
|
|
18068
18310
|
// cli/harness/fixPoll.ts
|
|
18069
18311
|
function pollBaseUrl() {
|
|
18070
18312
|
const port = String(process.env.SYNKRO_MCP_PORT || "18931");
|
|
@@ -18098,7 +18340,7 @@ function fixPollLines(poll, selected, width = 100) {
|
|
|
18098
18340
|
const max = Math.max(32, width - 10);
|
|
18099
18341
|
const choices = [...poll.candidates, "None of the above"];
|
|
18100
18342
|
const scope = [poll.ruleId, poll.filePath].filter(Boolean).join(" \xB7 ");
|
|
18101
|
-
const
|
|
18343
|
+
const lines2 = [
|
|
18102
18344
|
"",
|
|
18103
18345
|
S2.bold + S2.rule + " Synkro needs your decision" + S2.reset,
|
|
18104
18346
|
...scope ? [S2.dim + " " + neutralizeTerminalControls(scope) + S2.reset] : [],
|
|
@@ -18107,11 +18349,11 @@ function fixPollLines(poll, selected, width = 100) {
|
|
|
18107
18349
|
choices.forEach((choice, index) => {
|
|
18108
18350
|
const prefix = index === selected ? S2.user + " \u276F " : S2.dim + " ";
|
|
18109
18351
|
const text = wrap(index + 1 + ". " + neutralizeTerminalControls(choice), max);
|
|
18110
|
-
|
|
18111
|
-
for (const continuation of text.slice(1))
|
|
18352
|
+
lines2.push(prefix + (text[0] || "") + S2.reset);
|
|
18353
|
+
for (const continuation of text.slice(1)) lines2.push(" " + continuation);
|
|
18112
18354
|
});
|
|
18113
|
-
|
|
18114
|
-
return
|
|
18355
|
+
lines2.push("", S2.dim + " \u2191/\u2193 select \xB7 Enter confirm \xB7 1-" + choices.length + " choose" + S2.reset);
|
|
18356
|
+
return lines2;
|
|
18115
18357
|
}
|
|
18116
18358
|
async function pickFixPoll(poll, io = {}) {
|
|
18117
18359
|
const input = io.input || process.stdin;
|
|
@@ -18121,27 +18363,27 @@ async function pickFixPoll(poll, io = {}) {
|
|
|
18121
18363
|
let rendered = 0;
|
|
18122
18364
|
const draw = () => {
|
|
18123
18365
|
if (rendered) output.write("\x1B[" + rendered + "A\x1B[J");
|
|
18124
|
-
const
|
|
18125
|
-
output.write(
|
|
18126
|
-
rendered =
|
|
18366
|
+
const lines2 = fixPollLines(poll, selected, Number(output.columns || 100));
|
|
18367
|
+
output.write(lines2.join("\n") + "\n");
|
|
18368
|
+
rendered = lines2.length;
|
|
18127
18369
|
};
|
|
18128
|
-
const choice = await new Promise((
|
|
18370
|
+
const choice = await new Promise((resolve9) => {
|
|
18129
18371
|
const choices = poll.candidates.length + 1;
|
|
18130
18372
|
const wasRaw = Boolean(input.isRaw);
|
|
18131
18373
|
const done = (index) => {
|
|
18132
18374
|
input.off("data", onData);
|
|
18133
18375
|
if (input.setRawMode) input.setRawMode(wasRaw);
|
|
18134
|
-
|
|
18376
|
+
resolve9(index === poll.candidates.length ? -1 : index);
|
|
18135
18377
|
};
|
|
18136
18378
|
const onData = (chunk) => {
|
|
18137
|
-
const
|
|
18138
|
-
if (
|
|
18139
|
-
if (
|
|
18140
|
-
if (
|
|
18141
|
-
if (
|
|
18142
|
-
else if (
|
|
18143
|
-
else if (/^[1-9]$/.test(
|
|
18144
|
-
const index = Number(
|
|
18379
|
+
const key2 = chunk.toString("utf8");
|
|
18380
|
+
if (key2 === "") return done(poll.candidates.length);
|
|
18381
|
+
if (key2 === "\x1B" || key2.toLowerCase() === "n") return done(poll.candidates.length);
|
|
18382
|
+
if (key2 === "\r" || key2 === "\n") return done(selected);
|
|
18383
|
+
if (key2 === "\x1B[A" || key2 === "k") selected = (selected - 1 + choices) % choices;
|
|
18384
|
+
else if (key2 === "\x1B[B" || key2 === "j") selected = (selected + 1) % choices;
|
|
18385
|
+
else if (/^[1-9]$/.test(key2)) {
|
|
18386
|
+
const index = Number(key2) - 1;
|
|
18145
18387
|
if (index < choices) return done(index);
|
|
18146
18388
|
} else return;
|
|
18147
18389
|
draw();
|
|
@@ -18173,134 +18415,227 @@ var init_fixPoll = __esm({
|
|
|
18173
18415
|
}
|
|
18174
18416
|
});
|
|
18175
18417
|
|
|
18176
|
-
// cli/harness/
|
|
18177
|
-
|
|
18178
|
-
|
|
18179
|
-
|
|
18180
|
-
const value = String(path || "");
|
|
18181
|
-
if (home && value === home) return "~";
|
|
18182
|
-
if (home && value.startsWith(home + "/")) return "~" + value.slice(home.length);
|
|
18183
|
-
return value;
|
|
18184
|
-
}
|
|
18185
|
-
function parseCursorAccount(output) {
|
|
18186
|
-
const match = String(output || "").match(/logged in as\s+(\S+)/i);
|
|
18187
|
-
return match ? match[1].trim() : "";
|
|
18418
|
+
// cli/harness/session.ts
|
|
18419
|
+
function deleteSelectedText(value, range) {
|
|
18420
|
+
if (!range) return value.slice(0, -1);
|
|
18421
|
+
return value.slice(0, range[0]) + value.slice(range[1]);
|
|
18188
18422
|
}
|
|
18189
|
-
function
|
|
18190
|
-
|
|
18191
|
-
|
|
18192
|
-
|
|
18193
|
-
|
|
18194
|
-
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
|
|
18423
|
+
function handleScrollInput(screen, data) {
|
|
18424
|
+
let handled = false;
|
|
18425
|
+
const mousePattern = /\x1b\[<(\d+);\d+;\d+[Mm]/g;
|
|
18426
|
+
let mouse;
|
|
18427
|
+
while ((mouse = mousePattern.exec(data)) !== null) {
|
|
18428
|
+
const button = Number(mouse[1]);
|
|
18429
|
+
if (button === 64 || button === 65) {
|
|
18430
|
+
screen.scrollBy(button === 64 ? 3 : -3);
|
|
18431
|
+
handled = true;
|
|
18432
|
+
}
|
|
18199
18433
|
}
|
|
18200
|
-
|
|
18201
|
-
|
|
18202
|
-
|
|
18203
|
-
try {
|
|
18204
|
-
email = String(getUserInfo().email || "");
|
|
18205
|
-
} catch {
|
|
18206
|
-
return { email: "", needsLogin: false };
|
|
18434
|
+
if (data.includes("\x1B[5~")) {
|
|
18435
|
+
screen.scrollBy(10);
|
|
18436
|
+
handled = true;
|
|
18207
18437
|
}
|
|
18208
|
-
|
|
18209
|
-
|
|
18210
|
-
|
|
18211
|
-
} catch {
|
|
18438
|
+
if (data.includes("\x1B[6~")) {
|
|
18439
|
+
screen.scrollBy(-10);
|
|
18440
|
+
handled = true;
|
|
18212
18441
|
}
|
|
18213
|
-
return
|
|
18214
|
-
}
|
|
18215
|
-
function readIdentity(harness) {
|
|
18216
|
-
const synkro = synkroAccount();
|
|
18217
|
-
return {
|
|
18218
|
-
harness: harness === "cursor" ? cursorAccount() : "",
|
|
18219
|
-
synkro: synkro.email,
|
|
18220
|
-
needsLogin: synkro.needsLogin
|
|
18221
|
-
};
|
|
18442
|
+
return handled;
|
|
18222
18443
|
}
|
|
18223
|
-
function
|
|
18224
|
-
const
|
|
18225
|
-
|
|
18226
|
-
if (opts.identity.harness) lines.push(label(opts.harness, opts.identity.harness));
|
|
18227
|
-
if (opts.identity.synkro) {
|
|
18228
|
-
lines.push(label("synkro", opts.identity.synkro, opts.identity.needsLogin ? S2.blocked + " \xB7 session ended, run synkro login" + S2.reset : ""));
|
|
18229
|
-
}
|
|
18230
|
-
lines.push("");
|
|
18231
|
-
return lines;
|
|
18444
|
+
function printEvent(event) {
|
|
18445
|
+
const lines2 = renderEvent(event, Number(process.stdout.columns || 100));
|
|
18446
|
+
if (lines2.length) process.stdout.write(lines2.join("\n") + "\n");
|
|
18232
18447
|
}
|
|
18233
|
-
var init_identity2 = __esm({
|
|
18234
|
-
"cli/harness/identity.ts"() {
|
|
18235
|
-
"use strict";
|
|
18236
|
-
init_auth();
|
|
18237
|
-
init_render2();
|
|
18238
|
-
}
|
|
18239
|
-
});
|
|
18240
|
-
|
|
18241
|
-
// cli/harness/session.ts
|
|
18242
18448
|
async function runOnce(harness, cwd, prompt, echoPrompt = true, showHeader = true, signal) {
|
|
18243
|
-
if (harness
|
|
18449
|
+
if (!supported(harness)) {
|
|
18244
18450
|
process.stdout.write(S2.dim + " " + harness + " sessions are not embedded yet\n" + S2.reset);
|
|
18245
18451
|
return 1;
|
|
18246
18452
|
}
|
|
18247
|
-
const
|
|
18248
|
-
|
|
18249
|
-
|
|
18250
|
-
|
|
18251
|
-
|
|
18252
|
-
|
|
18453
|
+
const resumeId = loadSessionId(harness, cwd);
|
|
18454
|
+
if (harness === "cursor") {
|
|
18455
|
+
const result = await runCursorTurn({ prompt, cwd, showPrompt: echoPrompt, showHeader, signal, resumeId });
|
|
18456
|
+
saveSessionId(harness, cwd, result.sessionId || "");
|
|
18457
|
+
await resolveFixPolls(result.events);
|
|
18458
|
+
return result.exitCode;
|
|
18459
|
+
}
|
|
18460
|
+
const session = new CodexSession(cwd, resumeId);
|
|
18461
|
+
const events = [];
|
|
18462
|
+
try {
|
|
18463
|
+
await session.start();
|
|
18464
|
+
const result = await session.runTurn(prompt, (event) => {
|
|
18465
|
+
events.push(event);
|
|
18466
|
+
printEvent(event);
|
|
18467
|
+
}, signal);
|
|
18468
|
+
saveSessionId(harness, cwd, result.sessionId);
|
|
18469
|
+
await resolveFixPolls(events);
|
|
18470
|
+
return result.exitCode;
|
|
18471
|
+
} finally {
|
|
18472
|
+
session.close();
|
|
18253
18473
|
}
|
|
18254
|
-
return result.exitCode;
|
|
18255
18474
|
}
|
|
18256
|
-
|
|
18257
|
-
|
|
18258
|
-
|
|
18259
|
-
|
|
18260
|
-
|
|
18261
|
-
|
|
18262
|
-
|
|
18475
|
+
function readPrompt(screen) {
|
|
18476
|
+
return new Promise((resolve9) => {
|
|
18477
|
+
let value = "";
|
|
18478
|
+
const finish = (answer) => {
|
|
18479
|
+
process.stdin.removeListener("data", onData);
|
|
18480
|
+
screen.setInput("");
|
|
18481
|
+
resolve9(answer);
|
|
18482
|
+
};
|
|
18483
|
+
const onData = (chunk) => {
|
|
18484
|
+
const data = chunk.toString("utf8");
|
|
18485
|
+
const scrolled = handleScrollInput(screen, data);
|
|
18486
|
+
const mousePattern = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
18487
|
+
let mouse;
|
|
18488
|
+
let sawMouse = false;
|
|
18489
|
+
while ((mouse = mousePattern.exec(data)) !== null) {
|
|
18490
|
+
sawMouse = true;
|
|
18491
|
+
const button = Number(mouse[1]);
|
|
18492
|
+
if (button === 0 || button === 32) {
|
|
18493
|
+
screen.selectInputAt(Number(mouse[2]), Number(mouse[3]), button === 32 || mouse[4] === "m");
|
|
18494
|
+
}
|
|
18495
|
+
}
|
|
18496
|
+
if (sawMouse || scrolled) return;
|
|
18497
|
+
const selection = screen.selectionRange();
|
|
18498
|
+
if (data === "\x1B[3~") {
|
|
18499
|
+
value = deleteSelectedText(value, selection);
|
|
18500
|
+
screen.setInput(value);
|
|
18501
|
+
return;
|
|
18502
|
+
}
|
|
18503
|
+
if (data === "\x1B") {
|
|
18504
|
+
if (selection) {
|
|
18505
|
+
value = deleteSelectedText(value, selection);
|
|
18506
|
+
screen.setInput(value);
|
|
18507
|
+
} else finish(null);
|
|
18508
|
+
return;
|
|
18509
|
+
}
|
|
18510
|
+
if (data.startsWith("\x1B")) return;
|
|
18511
|
+
for (const char of data) {
|
|
18512
|
+
if (char === "" || char === "") {
|
|
18513
|
+
finish(null);
|
|
18514
|
+
return;
|
|
18515
|
+
}
|
|
18516
|
+
if (char === "\r" || char === "\n") {
|
|
18517
|
+
finish(value);
|
|
18518
|
+
return;
|
|
18519
|
+
}
|
|
18520
|
+
if (char === "\x7F" || char === "\b") value = deleteSelectedText(value, screen.selectionRange());
|
|
18521
|
+
else if (char === "") value = "";
|
|
18522
|
+
else if (char === "") value = value.replace(/\s*\S+\s*$/, "");
|
|
18523
|
+
else if (char >= " ") {
|
|
18524
|
+
const range = screen.selectionRange();
|
|
18525
|
+
value = range ? value.slice(0, range[0]) + char + value.slice(range[1]) : value + char;
|
|
18526
|
+
}
|
|
18527
|
+
screen.setInput(value);
|
|
18528
|
+
}
|
|
18529
|
+
};
|
|
18530
|
+
process.stdin.on("data", onData);
|
|
18531
|
+
screen.setInput(value);
|
|
18532
|
+
});
|
|
18533
|
+
}
|
|
18534
|
+
async function cancellable(screen, run2) {
|
|
18535
|
+
const controller = new AbortController();
|
|
18536
|
+
const onData = (chunk) => {
|
|
18537
|
+
const text = chunk.toString("utf8");
|
|
18538
|
+
if (handleScrollInput(screen, text)) return;
|
|
18539
|
+
if (text.includes("") || text === "\x1B") {
|
|
18540
|
+
screen.setStatus("Interrupting\u2026");
|
|
18541
|
+
controller.abort();
|
|
18542
|
+
}
|
|
18263
18543
|
};
|
|
18264
|
-
process.on("
|
|
18265
|
-
let first = true;
|
|
18544
|
+
process.stdin.on("data", onData);
|
|
18266
18545
|
try {
|
|
18546
|
+
return await run2(controller.signal);
|
|
18547
|
+
} finally {
|
|
18548
|
+
process.stdin.removeListener("data", onData);
|
|
18549
|
+
}
|
|
18550
|
+
}
|
|
18551
|
+
async function resolveScreenFixPolls(screen, events) {
|
|
18552
|
+
if (!events.some((event) => event.type === "tool-end" && Boolean(event.pollId))) return;
|
|
18553
|
+
screen.leave();
|
|
18554
|
+
try {
|
|
18555
|
+
await resolveFixPolls(events);
|
|
18556
|
+
} finally {
|
|
18557
|
+
screen.enter();
|
|
18558
|
+
}
|
|
18559
|
+
}
|
|
18560
|
+
async function runSynkroSession(harness, cwd, prompt) {
|
|
18561
|
+
if (prompt) return runOnce(harness, cwd, prompt);
|
|
18562
|
+
if (!supported(harness)) return runOnce(harness, cwd, "");
|
|
18563
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
18564
|
+
process.stderr.write("synkro terminal requires a TTY\n");
|
|
18565
|
+
return 1;
|
|
18566
|
+
}
|
|
18567
|
+
const screen = new SynkroScreen(harness, cwd);
|
|
18568
|
+
const resumeId = loadSessionId(harness, cwd);
|
|
18569
|
+
let codex = null;
|
|
18570
|
+
process.stdin.setRawMode(true);
|
|
18571
|
+
process.stdin.resume();
|
|
18572
|
+
screen.enter();
|
|
18573
|
+
try {
|
|
18574
|
+
if (harness === "codex") {
|
|
18575
|
+
screen.setStatus("Connecting to Codex\u2026");
|
|
18576
|
+
codex = new CodexSession(cwd, resumeId);
|
|
18577
|
+
await codex.start();
|
|
18578
|
+
saveSessionId(harness, cwd, codex.threadId);
|
|
18579
|
+
screen.add({ type: "session-start", sessionId: codex.threadId, model: codex.model || "Codex", cwd, authSource: "login" });
|
|
18580
|
+
screen.setStatus("Ready");
|
|
18581
|
+
}
|
|
18267
18582
|
for (; ; ) {
|
|
18268
|
-
const
|
|
18269
|
-
if (
|
|
18270
|
-
const line =
|
|
18271
|
-
if (!line
|
|
18272
|
-
if (
|
|
18583
|
+
const answer = await readPrompt(screen);
|
|
18584
|
+
if (answer === null) break;
|
|
18585
|
+
const line = answer.trim();
|
|
18586
|
+
if (!line) continue;
|
|
18587
|
+
if (line === "/exit" || line === "/quit" || line === "exit" || line === "quit") break;
|
|
18588
|
+
screen.setStatus("Working\u2026");
|
|
18589
|
+
const events = [];
|
|
18273
18590
|
try {
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
|
|
18280
|
-
|
|
18281
|
-
|
|
18591
|
+
if (harness === "cursor") {
|
|
18592
|
+
const result = await cancellable(screen, (signal) => runCursorTurn({
|
|
18593
|
+
prompt: line,
|
|
18594
|
+
cwd,
|
|
18595
|
+
signal,
|
|
18596
|
+
resumeId: loadSessionId(harness, cwd),
|
|
18597
|
+
write: () => {
|
|
18598
|
+
},
|
|
18599
|
+
onEvent: (event) => {
|
|
18600
|
+
events.push(event);
|
|
18601
|
+
screen.add(event);
|
|
18602
|
+
},
|
|
18603
|
+
showHeader: false
|
|
18604
|
+
}));
|
|
18605
|
+
saveSessionId(harness, cwd, result.sessionId || "");
|
|
18606
|
+
} else {
|
|
18607
|
+
const result = await cancellable(screen, (signal) => codex.runTurn(line, (event) => {
|
|
18608
|
+
events.push(event);
|
|
18609
|
+
screen.add(event);
|
|
18610
|
+
}, signal));
|
|
18611
|
+
saveSessionId(harness, cwd, result.sessionId);
|
|
18282
18612
|
}
|
|
18283
|
-
|
|
18284
|
-
}
|
|
18285
|
-
|
|
18286
|
-
turn
|
|
18613
|
+
await resolveScreenFixPolls(screen, events);
|
|
18614
|
+
} catch (error) {
|
|
18615
|
+
screen.add({ type: "notice", text: error instanceof Error ? error.message : String(error) });
|
|
18616
|
+
screen.add({ type: "turn-end", ok: false, text: "" });
|
|
18287
18617
|
}
|
|
18618
|
+
screen.setStatus("Ready");
|
|
18288
18619
|
}
|
|
18620
|
+
return 0;
|
|
18289
18621
|
} finally {
|
|
18290
|
-
process.off("SIGINT", interruptTurn);
|
|
18291
18622
|
codex?.close();
|
|
18623
|
+
screen.leave();
|
|
18624
|
+
process.stdin.setRawMode(false);
|
|
18625
|
+
process.stdin.pause();
|
|
18292
18626
|
}
|
|
18293
|
-
return 0;
|
|
18294
18627
|
}
|
|
18628
|
+
var supported;
|
|
18295
18629
|
var init_session = __esm({
|
|
18296
18630
|
"cli/harness/session.ts"() {
|
|
18297
18631
|
"use strict";
|
|
18298
|
-
init_composer();
|
|
18299
|
-
init_run();
|
|
18300
18632
|
init_codex();
|
|
18301
|
-
|
|
18302
|
-
init_identity2();
|
|
18633
|
+
init_run();
|
|
18303
18634
|
init_render2();
|
|
18635
|
+
init_screen();
|
|
18636
|
+
init_state();
|
|
18637
|
+
init_fixPoll();
|
|
18638
|
+
supported = (harness) => harness === "cursor" || harness === "codex";
|
|
18304
18639
|
}
|
|
18305
18640
|
});
|
|
18306
18641
|
|
|
@@ -18341,17 +18676,19 @@ async function takeover(kind, cwd) {
|
|
|
18341
18676
|
const runner = backend === "container" ? info.runner : HOST3;
|
|
18342
18677
|
return runInherit(["env", "TMUX=", ...runnerInteractiveArgs(runner, ["tmux", "attach-session", "-t", spawned.session])]);
|
|
18343
18678
|
}
|
|
18344
|
-
async function restoreSession(session) {
|
|
18345
|
-
const
|
|
18346
|
-
if (!
|
|
18679
|
+
async function restoreSession(session, bootPath) {
|
|
18680
|
+
const record2 = loadRecords().find((row2) => row2.session === session);
|
|
18681
|
+
if (!record2) return;
|
|
18347
18682
|
const info = await detectContainerBackend();
|
|
18348
18683
|
await spawnAgent(info, {
|
|
18349
|
-
name:
|
|
18350
|
-
harness:
|
|
18351
|
-
spaceName:
|
|
18352
|
-
cwd:
|
|
18353
|
-
backend:
|
|
18354
|
-
resume:
|
|
18684
|
+
name: record2.name,
|
|
18685
|
+
harness: record2.harness,
|
|
18686
|
+
spaceName: record2.spaceName,
|
|
18687
|
+
cwd: record2.space,
|
|
18688
|
+
backend: record2.mode === "embedded" ? "host" : record2.backend,
|
|
18689
|
+
resume: record2.mode !== "embedded",
|
|
18690
|
+
mode: record2.mode,
|
|
18691
|
+
command: record2.mode === "embedded" ? embeddedSessionCommand(bootPath, record2.harness, record2.space) : void 0
|
|
18355
18692
|
});
|
|
18356
18693
|
}
|
|
18357
18694
|
async function printStatus() {
|
|
@@ -18404,7 +18741,7 @@ async function uiCommand(args2) {
|
|
|
18404
18741
|
const harness = args2[runAt + 1] || "cursor";
|
|
18405
18742
|
const cwd = args2[runAt + 2] || repoRoot();
|
|
18406
18743
|
const prompt = args2.slice(runAt + 3).join(" ").trim();
|
|
18407
|
-
process.exitCode = await
|
|
18744
|
+
process.exitCode = await runSynkroSession(harness, cwd, prompt);
|
|
18408
18745
|
return;
|
|
18409
18746
|
}
|
|
18410
18747
|
const takeoverAt = args2.indexOf("--takeover");
|
|
@@ -18414,7 +18751,7 @@ async function uiCommand(args2) {
|
|
|
18414
18751
|
}
|
|
18415
18752
|
const restoreAt = args2.indexOf("--restore");
|
|
18416
18753
|
if (restoreAt !== -1) {
|
|
18417
|
-
await restoreSession(args2[restoreAt + 1] || "");
|
|
18754
|
+
await restoreSession(args2[restoreAt + 1] || "", bootPath);
|
|
18418
18755
|
return;
|
|
18419
18756
|
}
|
|
18420
18757
|
if (args2.includes("--status")) {
|
|
@@ -18492,12 +18829,12 @@ __export(linear_exports, {
|
|
|
18492
18829
|
formatLinks: () => formatLinks,
|
|
18493
18830
|
linearCommand: () => linearCommand
|
|
18494
18831
|
});
|
|
18495
|
-
import { readFileSync as
|
|
18832
|
+
import { readFileSync as readFileSync35 } from "fs";
|
|
18496
18833
|
import { homedir as homedir40 } from "os";
|
|
18497
|
-
import { join as
|
|
18834
|
+
import { join as join39 } from "path";
|
|
18498
18835
|
function mcpJwt() {
|
|
18499
18836
|
try {
|
|
18500
|
-
return
|
|
18837
|
+
return readFileSync35(join39(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
18501
18838
|
} catch {
|
|
18502
18839
|
return "";
|
|
18503
18840
|
}
|
|
@@ -18536,7 +18873,7 @@ var SYNKRO_DIR14, PORT2, BASE;
|
|
|
18536
18873
|
var init_linear = __esm({
|
|
18537
18874
|
"cli/commands/linear.ts"() {
|
|
18538
18875
|
"use strict";
|
|
18539
|
-
SYNKRO_DIR14 =
|
|
18876
|
+
SYNKRO_DIR14 = join39(homedir40(), ".synkro");
|
|
18540
18877
|
PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
|
|
18541
18878
|
BASE = `http://127.0.0.1:${PORT2}`;
|
|
18542
18879
|
}
|
|
@@ -18544,13 +18881,13 @@ var init_linear = __esm({
|
|
|
18544
18881
|
|
|
18545
18882
|
// cli/scanning/cveReachability.ts
|
|
18546
18883
|
import { parse } from "@babel/parser";
|
|
18547
|
-
import { readFileSync as
|
|
18884
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
18548
18885
|
function walk(node, visit) {
|
|
18549
18886
|
if (!node || typeof node.type !== "string") return;
|
|
18550
18887
|
visit(node);
|
|
18551
|
-
for (const
|
|
18552
|
-
if (
|
|
18553
|
-
const child = node[
|
|
18888
|
+
for (const key2 of Object.keys(node)) {
|
|
18889
|
+
if (key2 === "loc" || key2 === "start" || key2 === "end" || key2 === "range" || key2 === "leadingComments" || key2 === "trailingComments") continue;
|
|
18890
|
+
const child = node[key2];
|
|
18554
18891
|
if (Array.isArray(child)) {
|
|
18555
18892
|
for (const c of child) if (c && typeof c.type === "string") walk(c, visit);
|
|
18556
18893
|
} else if (child && typeof child.type === "string") walk(child, visit);
|
|
@@ -18685,9 +19022,9 @@ var init_cveReachability = __esm({
|
|
|
18685
19022
|
});
|
|
18686
19023
|
|
|
18687
19024
|
// cli/reachability/reachabilityScan.ts
|
|
18688
|
-
import { spawnSync as spawnSync13, execFileSync as
|
|
18689
|
-
import { readFileSync as
|
|
18690
|
-
import { join as
|
|
19025
|
+
import { spawnSync as spawnSync13, execFileSync as execFileSync6 } from "child_process";
|
|
19026
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync29, existsSync as existsSync39, readdirSync as readdirSync10 } from "fs";
|
|
19027
|
+
import { join as join40 } from "path";
|
|
18691
19028
|
import { homedir as homedir41 } from "os";
|
|
18692
19029
|
import { createRequire } from "module";
|
|
18693
19030
|
function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
|
|
@@ -18705,7 +19042,7 @@ function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
18705
19042
|
}
|
|
18706
19043
|
for (const e of ents) {
|
|
18707
19044
|
if (files.length >= maxFiles) break;
|
|
18708
|
-
const full =
|
|
19045
|
+
const full = join40(dir, e.name);
|
|
18709
19046
|
if (e.isDirectory()) {
|
|
18710
19047
|
if (!SKIP2.has(e.name) && !e.name.startsWith(".")) stack.push(full);
|
|
18711
19048
|
continue;
|
|
@@ -18713,7 +19050,7 @@ function walkSourceFiles(repoRoot3, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
18713
19050
|
if (!EXT.test(e.name) || e.name.endsWith(".d.ts")) continue;
|
|
18714
19051
|
const rel = full.startsWith(repoRoot3 + "/") ? full.slice(repoRoot3.length + 1) : full;
|
|
18715
19052
|
try {
|
|
18716
|
-
const content =
|
|
19053
|
+
const content = readFileSync37(full, "utf8");
|
|
18717
19054
|
if (content.length <= maxBytes) files.push({ path: rel, content });
|
|
18718
19055
|
} catch {
|
|
18719
19056
|
}
|
|
@@ -18732,12 +19069,12 @@ function cleanVersion(spec) {
|
|
|
18732
19069
|
function gatherManifestVersions(repoRoot3) {
|
|
18733
19070
|
const out = {};
|
|
18734
19071
|
const dirs = [repoRoot3];
|
|
18735
|
-
const pkgsDir =
|
|
19072
|
+
const pkgsDir = join40(repoRoot3, "packages");
|
|
18736
19073
|
if (existsSync39(pkgsDir)) {
|
|
18737
19074
|
try {
|
|
18738
19075
|
for (const d of readdirSync10(pkgsDir)) {
|
|
18739
|
-
const pd =
|
|
18740
|
-
if (existsSync39(
|
|
19076
|
+
const pd = join40(pkgsDir, d);
|
|
19077
|
+
if (existsSync39(join40(pd, "package.json"))) dirs.push(pd);
|
|
18741
19078
|
}
|
|
18742
19079
|
} catch {
|
|
18743
19080
|
}
|
|
@@ -18746,7 +19083,7 @@ function gatherManifestVersions(repoRoot3) {
|
|
|
18746
19083
|
for (const dir of dirs) {
|
|
18747
19084
|
let pkg;
|
|
18748
19085
|
try {
|
|
18749
|
-
pkg = JSON.parse(
|
|
19086
|
+
pkg = JSON.parse(readFileSync37(join40(dir, "package.json"), "utf8"));
|
|
18750
19087
|
} catch {
|
|
18751
19088
|
continue;
|
|
18752
19089
|
}
|
|
@@ -18766,28 +19103,28 @@ function findJelly(repoRoot3) {
|
|
|
18766
19103
|
try {
|
|
18767
19104
|
const pkgJson = require2.resolve("@cs-au-dk/jelly/package.json");
|
|
18768
19105
|
const dir = pkgJson.slice(0, pkgJson.length - "package.json".length);
|
|
18769
|
-
const pkg = JSON.parse(
|
|
19106
|
+
const pkg = JSON.parse(readFileSync37(pkgJson, "utf8"));
|
|
18770
19107
|
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
|
|
18771
19108
|
if (bin) {
|
|
18772
|
-
const p =
|
|
19109
|
+
const p = join40(dir, bin);
|
|
18773
19110
|
if (existsSync39(p)) return p;
|
|
18774
19111
|
}
|
|
18775
19112
|
} catch {
|
|
18776
19113
|
}
|
|
18777
19114
|
for (const base of [repoRoot3, process.cwd()]) {
|
|
18778
|
-
const b =
|
|
19115
|
+
const b = join40(base, "node_modules", ".bin", "jelly");
|
|
18779
19116
|
if (existsSync39(b)) return b;
|
|
18780
19117
|
}
|
|
18781
19118
|
return null;
|
|
18782
19119
|
}
|
|
18783
19120
|
function findEntries(repoRoot3) {
|
|
18784
19121
|
const dirs = [repoRoot3];
|
|
18785
|
-
const pkgsDir =
|
|
19122
|
+
const pkgsDir = join40(repoRoot3, "packages");
|
|
18786
19123
|
if (existsSync39(pkgsDir)) {
|
|
18787
19124
|
try {
|
|
18788
19125
|
for (const d of readdirSync10(pkgsDir)) {
|
|
18789
|
-
const pd =
|
|
18790
|
-
if (existsSync39(
|
|
19126
|
+
const pd = join40(pkgsDir, d);
|
|
19127
|
+
if (existsSync39(join40(pd, "package.json"))) dirs.push(pd);
|
|
18791
19128
|
}
|
|
18792
19129
|
} catch {
|
|
18793
19130
|
}
|
|
@@ -18795,11 +19132,11 @@ function findEntries(repoRoot3) {
|
|
|
18795
19132
|
const entries = [];
|
|
18796
19133
|
for (const dir of dirs) {
|
|
18797
19134
|
try {
|
|
18798
|
-
const pkg = JSON.parse(
|
|
19135
|
+
const pkg = JSON.parse(readFileSync37(join40(dir, "package.json"), "utf8"));
|
|
18799
19136
|
const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
|
|
18800
19137
|
for (const c of cands) {
|
|
18801
19138
|
if (typeof c !== "string") continue;
|
|
18802
|
-
const f =
|
|
19139
|
+
const f = join40(dir, c);
|
|
18803
19140
|
if (existsSync39(f)) {
|
|
18804
19141
|
entries.push(f);
|
|
18805
19142
|
break;
|
|
@@ -18812,7 +19149,7 @@ function findEntries(repoRoot3) {
|
|
|
18812
19149
|
}
|
|
18813
19150
|
function currentCommit(repoRoot3) {
|
|
18814
19151
|
try {
|
|
18815
|
-
return
|
|
19152
|
+
return execFileSync6("git", ["rev-parse", "HEAD"], { cwd: repoRoot3, encoding: "utf8" }).trim();
|
|
18816
19153
|
} catch {
|
|
18817
19154
|
return "";
|
|
18818
19155
|
}
|
|
@@ -18835,7 +19172,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
|
|
|
18835
19172
|
const commit = currentCommit(repoRoot3);
|
|
18836
19173
|
if (!opts.force && commit && existsSync39(REACHABILITY_PATH)) {
|
|
18837
19174
|
try {
|
|
18838
|
-
const prev = JSON.parse(
|
|
19175
|
+
const prev = JSON.parse(readFileSync37(REACHABILITY_PATH, "utf8"));
|
|
18839
19176
|
if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
|
|
18840
19177
|
} catch {
|
|
18841
19178
|
}
|
|
@@ -18924,7 +19261,7 @@ function runReachabilityScan(repoRoot3, opts = {}) {
|
|
|
18924
19261
|
if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
|
|
18925
19262
|
const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot3) };
|
|
18926
19263
|
try {
|
|
18927
|
-
|
|
19264
|
+
writeFileSync29(REACHABILITY_PATH, JSON.stringify(file, null, 2));
|
|
18928
19265
|
} catch (e) {
|
|
18929
19266
|
return { ok: false, reason: "write failed: " + String(e.message || e) };
|
|
18930
19267
|
}
|
|
@@ -18936,7 +19273,7 @@ var init_reachabilityScan = __esm({
|
|
|
18936
19273
|
"use strict";
|
|
18937
19274
|
init_cveReachability();
|
|
18938
19275
|
require2 = createRequire(import.meta.url);
|
|
18939
|
-
REACHABILITY_PATH =
|
|
19276
|
+
REACHABILITY_PATH = join40(homedir41(), ".synkro", "reachability.json");
|
|
18940
19277
|
}
|
|
18941
19278
|
});
|
|
18942
19279
|
|
|
@@ -18945,15 +19282,15 @@ var reachabilityScan_exports = {};
|
|
|
18945
19282
|
__export(reachabilityScan_exports, {
|
|
18946
19283
|
reachabilityScanCommand: () => reachabilityScanCommand
|
|
18947
19284
|
});
|
|
18948
|
-
import { readFileSync as
|
|
18949
|
-
import { join as
|
|
19285
|
+
import { readFileSync as readFileSync38, existsSync as existsSync40 } from "fs";
|
|
19286
|
+
import { join as join41 } from "path";
|
|
18950
19287
|
import { homedir as homedir42 } from "os";
|
|
18951
|
-
import { execFileSync as
|
|
19288
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
18952
19289
|
function readConfigEnv4() {
|
|
18953
|
-
const p =
|
|
19290
|
+
const p = join41(SYNKRO_DIR15, "config.env");
|
|
18954
19291
|
if (!existsSync40(p)) return {};
|
|
18955
19292
|
const out = {};
|
|
18956
|
-
for (const line of
|
|
19293
|
+
for (const line of readFileSync38(p, "utf-8").split("\n")) {
|
|
18957
19294
|
const t = line.trim();
|
|
18958
19295
|
if (!t || t.startsWith("#")) continue;
|
|
18959
19296
|
const eq = t.indexOf("=");
|
|
@@ -18963,7 +19300,7 @@ function readConfigEnv4() {
|
|
|
18963
19300
|
}
|
|
18964
19301
|
function repoRoot2() {
|
|
18965
19302
|
try {
|
|
18966
|
-
return
|
|
19303
|
+
return execFileSync7("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" }).trim();
|
|
18967
19304
|
} catch {
|
|
18968
19305
|
return process.cwd();
|
|
18969
19306
|
}
|
|
@@ -18971,7 +19308,7 @@ function repoRoot2() {
|
|
|
18971
19308
|
function repoSlug(root) {
|
|
18972
19309
|
const run2 = (a) => {
|
|
18973
19310
|
try {
|
|
18974
|
-
return
|
|
19311
|
+
return execFileSync7("git", a, { encoding: "utf-8" }).trim();
|
|
18975
19312
|
} catch {
|
|
18976
19313
|
return "";
|
|
18977
19314
|
}
|
|
@@ -18985,11 +19322,11 @@ async function pushToCloud(cfg, repo) {
|
|
|
18985
19322
|
while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
|
|
18986
19323
|
let jwt2 = "";
|
|
18987
19324
|
try {
|
|
18988
|
-
jwt2 =
|
|
19325
|
+
jwt2 = readFileSync38(join41(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
|
|
18989
19326
|
} catch {
|
|
18990
19327
|
}
|
|
18991
19328
|
if (!jwt2 || !existsSync40(REACHABILITY_PATH)) return;
|
|
18992
|
-
const body =
|
|
19329
|
+
const body = readFileSync38(REACHABILITY_PATH, "utf-8");
|
|
18993
19330
|
try {
|
|
18994
19331
|
const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
|
|
18995
19332
|
method: "POST",
|
|
@@ -19021,7 +19358,7 @@ var init_reachabilityScan2 = __esm({
|
|
|
19021
19358
|
"cli/commands/reachabilityScan.ts"() {
|
|
19022
19359
|
"use strict";
|
|
19023
19360
|
init_reachabilityScan();
|
|
19024
|
-
SYNKRO_DIR15 =
|
|
19361
|
+
SYNKRO_DIR15 = join41(homedir42(), ".synkro");
|
|
19025
19362
|
}
|
|
19026
19363
|
});
|
|
19027
19364
|
|
|
@@ -19151,13 +19488,13 @@ var config_exports = {};
|
|
|
19151
19488
|
__export(config_exports, {
|
|
19152
19489
|
configCommand: () => configCommand
|
|
19153
19490
|
});
|
|
19154
|
-
import { readFileSync as
|
|
19155
|
-
import { join as
|
|
19491
|
+
import { readFileSync as readFileSync39, writeFileSync as writeFileSync30, existsSync as existsSync41 } from "fs";
|
|
19492
|
+
import { join as join42 } from "path";
|
|
19156
19493
|
import { homedir as homedir43 } from "os";
|
|
19157
19494
|
function readConfigEnv5() {
|
|
19158
19495
|
if (!existsSync41(CONFIG_PATH9)) return {};
|
|
19159
19496
|
const out = {};
|
|
19160
|
-
for (const line of
|
|
19497
|
+
for (const line of readFileSync39(CONFIG_PATH9, "utf-8").split("\n")) {
|
|
19161
19498
|
const t = line.trim();
|
|
19162
19499
|
if (!t || t.startsWith("#")) continue;
|
|
19163
19500
|
const eq = t.indexOf("=");
|
|
@@ -19165,23 +19502,23 @@ function readConfigEnv5() {
|
|
|
19165
19502
|
}
|
|
19166
19503
|
return out;
|
|
19167
19504
|
}
|
|
19168
|
-
function updateConfigValue(
|
|
19505
|
+
function updateConfigValue(key2, value) {
|
|
19169
19506
|
if (!existsSync41(CONFIG_PATH9)) {
|
|
19170
19507
|
console.error("No config found. Run `synkro install` first.");
|
|
19171
19508
|
process.exit(1);
|
|
19172
19509
|
}
|
|
19173
|
-
const
|
|
19174
|
-
const pattern = new RegExp(`^${
|
|
19510
|
+
const lines2 = readFileSync39(CONFIG_PATH9, "utf-8").split("\n");
|
|
19511
|
+
const pattern = new RegExp(`^${key2}=`);
|
|
19175
19512
|
let found = false;
|
|
19176
|
-
const updated =
|
|
19513
|
+
const updated = lines2.map((line) => {
|
|
19177
19514
|
if (pattern.test(line.trim())) {
|
|
19178
19515
|
found = true;
|
|
19179
|
-
return `${
|
|
19516
|
+
return `${key2}='${value}'`;
|
|
19180
19517
|
}
|
|
19181
19518
|
return line;
|
|
19182
19519
|
});
|
|
19183
|
-
if (!found) updated.splice(updated.length - 1, 0, `${
|
|
19184
|
-
|
|
19520
|
+
if (!found) updated.splice(updated.length - 1, 0, `${key2}='${value}'`);
|
|
19521
|
+
writeFileSync30(CONFIG_PATH9, updated.join("\n"), "utf-8");
|
|
19185
19522
|
}
|
|
19186
19523
|
function resolveInferenceMode(cfg) {
|
|
19187
19524
|
if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
|
|
@@ -19339,8 +19676,8 @@ var init_config = __esm({
|
|
|
19339
19676
|
"use strict";
|
|
19340
19677
|
init_stub();
|
|
19341
19678
|
init_optout();
|
|
19342
|
-
SYNKRO_DIR16 =
|
|
19343
|
-
CONFIG_PATH9 =
|
|
19679
|
+
SYNKRO_DIR16 = join42(homedir43(), ".synkro");
|
|
19680
|
+
CONFIG_PATH9 = join42(SYNKRO_DIR16, "config.env");
|
|
19344
19681
|
}
|
|
19345
19682
|
});
|
|
19346
19683
|
|
|
@@ -19349,7 +19686,7 @@ var telemetry_exports2 = {};
|
|
|
19349
19686
|
__export(telemetry_exports2, {
|
|
19350
19687
|
telemetryCommand: () => telemetryCommand
|
|
19351
19688
|
});
|
|
19352
|
-
import { createInterface as
|
|
19689
|
+
import { createInterface as createInterface7 } from "readline";
|
|
19353
19690
|
function parseFlag(args2, name) {
|
|
19354
19691
|
const prefix = `--${name}=`;
|
|
19355
19692
|
for (const a of args2) if (a.startsWith(prefix)) return a.slice(prefix.length);
|
|
@@ -19430,12 +19767,12 @@ async function runExport(args2) {
|
|
|
19430
19767
|
}
|
|
19431
19768
|
function confirmYesNo(question) {
|
|
19432
19769
|
if (!process.stdin.isTTY) return Promise.resolve(false);
|
|
19433
|
-
return new Promise((
|
|
19434
|
-
const rl =
|
|
19770
|
+
return new Promise((resolve9) => {
|
|
19771
|
+
const rl = createInterface7({ input: process.stdin, output: process.stdout });
|
|
19435
19772
|
rl.question(`${question} (y/N): `, (answer) => {
|
|
19436
19773
|
rl.close();
|
|
19437
19774
|
const t = answer.trim().toLowerCase();
|
|
19438
|
-
|
|
19775
|
+
resolve9(t === "y" || t === "yes");
|
|
19439
19776
|
});
|
|
19440
19777
|
});
|
|
19441
19778
|
}
|
|
@@ -19530,11 +19867,11 @@ Usage:
|
|
|
19530
19867
|
|
|
19531
19868
|
// cli/inventory/identity.ts
|
|
19532
19869
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
19533
|
-
import { existsSync as existsSync42, mkdirSync as
|
|
19870
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync25, readFileSync as readFileSync40, renameSync as renameSync9, writeFileSync as writeFileSync31 } from "fs";
|
|
19534
19871
|
import { homedir as homedir44 } from "os";
|
|
19535
|
-
import { dirname as
|
|
19872
|
+
import { dirname as dirname14, join as join43 } from "path";
|
|
19536
19873
|
function operationalIdentityPath() {
|
|
19537
|
-
return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH ||
|
|
19874
|
+
return process.env.SYNKRO_OPERATIONAL_IDENTITY_PATH || join43(homedir44(), ".synkro", "installation.json");
|
|
19538
19875
|
}
|
|
19539
19876
|
function validIdentity(value) {
|
|
19540
19877
|
if (!value || typeof value !== "object") return false;
|
|
@@ -19542,9 +19879,9 @@ function validIdentity(value) {
|
|
|
19542
19879
|
return typeof row2.installation_id === "string" && UUID_RE.test(row2.installation_id) && typeof row2.created_at === "string" && Number.isFinite(Date.parse(row2.created_at));
|
|
19543
19880
|
}
|
|
19544
19881
|
function writeIdentity(path, identity) {
|
|
19545
|
-
|
|
19882
|
+
mkdirSync25(dirname14(path), { recursive: true, mode: 448 });
|
|
19546
19883
|
const temp = `${path}.${process.pid}.${randomUUID5()}.tmp`;
|
|
19547
|
-
|
|
19884
|
+
writeFileSync31(temp, JSON.stringify(identity, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
19548
19885
|
renameSync9(temp, path);
|
|
19549
19886
|
}
|
|
19550
19887
|
function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
|
|
@@ -19552,7 +19889,7 @@ function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
|
|
|
19552
19889
|
if (prior) return prior;
|
|
19553
19890
|
if (existsSync42(path)) {
|
|
19554
19891
|
try {
|
|
19555
|
-
const parsed = JSON.parse(
|
|
19892
|
+
const parsed = JSON.parse(readFileSync40(path, "utf8"));
|
|
19556
19893
|
if (validIdentity(parsed)) {
|
|
19557
19894
|
cached4.set(path, parsed);
|
|
19558
19895
|
return parsed;
|
|
@@ -19566,7 +19903,7 @@ function getOperationalInstallationIdentity(path = operationalIdentityPath()) {
|
|
|
19566
19903
|
return identity;
|
|
19567
19904
|
}
|
|
19568
19905
|
var UUID_RE, cached4;
|
|
19569
|
-
var
|
|
19906
|
+
var init_identity2 = __esm({
|
|
19570
19907
|
"cli/inventory/identity.ts"() {
|
|
19571
19908
|
"use strict";
|
|
19572
19909
|
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -19578,12 +19915,12 @@ var init_identity3 = __esm({
|
|
|
19578
19915
|
import { createHash as createHash5 } from "crypto";
|
|
19579
19916
|
import {
|
|
19580
19917
|
existsSync as existsSync43,
|
|
19581
|
-
readFileSync as
|
|
19918
|
+
readFileSync as readFileSync41,
|
|
19582
19919
|
readdirSync as readdirSync11,
|
|
19583
|
-
statSync as
|
|
19920
|
+
statSync as statSync7
|
|
19584
19921
|
} from "fs";
|
|
19585
19922
|
import { arch, homedir as homedir45, hostname as hostname2, platform as platform6, release as release2 } from "os";
|
|
19586
|
-
import { basename as basename4, join as
|
|
19923
|
+
import { basename as basename4, join as join44, relative as relative2, resolve as resolve7 } from "path";
|
|
19587
19924
|
import { fileURLToPath } from "url";
|
|
19588
19925
|
function sha256(value) {
|
|
19589
19926
|
return createHash5("sha256").update(value).digest("hex");
|
|
@@ -19593,7 +19930,7 @@ function pseudonymousHostnameHash(installationId, host) {
|
|
|
19593
19930
|
}
|
|
19594
19931
|
function cliVersion() {
|
|
19595
19932
|
try {
|
|
19596
|
-
return "1.10.
|
|
19933
|
+
return "1.10.9";
|
|
19597
19934
|
} catch {
|
|
19598
19935
|
return "0.0.0";
|
|
19599
19936
|
}
|
|
@@ -19601,7 +19938,7 @@ function cliVersion() {
|
|
|
19601
19938
|
function readJson(path) {
|
|
19602
19939
|
try {
|
|
19603
19940
|
if (!existsSync43(path)) return null;
|
|
19604
|
-
const parsed = JSON.parse(
|
|
19941
|
+
const parsed = JSON.parse(readFileSync41(path, "utf8"));
|
|
19605
19942
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
19606
19943
|
} catch {
|
|
19607
19944
|
return null;
|
|
@@ -19610,7 +19947,7 @@ function readJson(path) {
|
|
|
19610
19947
|
function readText(path) {
|
|
19611
19948
|
try {
|
|
19612
19949
|
if (!existsSync43(path)) return "";
|
|
19613
|
-
return
|
|
19950
|
+
return readFileSync41(path, "utf8");
|
|
19614
19951
|
} catch {
|
|
19615
19952
|
return "";
|
|
19616
19953
|
}
|
|
@@ -19696,16 +20033,16 @@ function mcpArtifactsFromJson(harness, config, configScope = "user") {
|
|
|
19696
20033
|
}
|
|
19697
20034
|
function claudeDesktopConfigCandidates(home, targetPlatform) {
|
|
19698
20035
|
if (targetPlatform === "darwin") {
|
|
19699
|
-
return [
|
|
20036
|
+
return [join44(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
|
|
19700
20037
|
}
|
|
19701
20038
|
if (targetPlatform === "linux") {
|
|
19702
20039
|
return [
|
|
19703
|
-
|
|
19704
|
-
|
|
20040
|
+
join44(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
20041
|
+
join44(home, ".config", "claude", "claude_desktop_config.json")
|
|
19705
20042
|
];
|
|
19706
20043
|
}
|
|
19707
20044
|
if (targetPlatform === "win32" && process.env.APPDATA) {
|
|
19708
|
-
return [
|
|
20045
|
+
return [join44(process.env.APPDATA, "Claude", "claude_desktop_config.json")];
|
|
19709
20046
|
}
|
|
19710
20047
|
return [];
|
|
19711
20048
|
}
|
|
@@ -19713,7 +20050,7 @@ function claudeManagedMcpConfigCandidates(targetPlatform) {
|
|
|
19713
20050
|
if (targetPlatform === "darwin") return ["/Library/Application Support/ClaudeCode/managed-mcp.json"];
|
|
19714
20051
|
if (targetPlatform === "linux") return ["/etc/claude-code/managed-mcp.json"];
|
|
19715
20052
|
if (targetPlatform === "win32" && process.env.ProgramFiles) {
|
|
19716
|
-
return [
|
|
20053
|
+
return [join44(process.env.ProgramFiles, "ClaudeCode", "managed-mcp.json")];
|
|
19717
20054
|
}
|
|
19718
20055
|
return [];
|
|
19719
20056
|
}
|
|
@@ -19721,7 +20058,7 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
|
|
|
19721
20058
|
const roots = /* @__PURE__ */ new Set();
|
|
19722
20059
|
const add = (value) => {
|
|
19723
20060
|
if (typeof value !== "string" || !value.trim()) return;
|
|
19724
|
-
const path =
|
|
20061
|
+
const path = resolve7(value);
|
|
19725
20062
|
if (existsSync43(path)) roots.add(path);
|
|
19726
20063
|
};
|
|
19727
20064
|
add(currentDirectory);
|
|
@@ -19733,10 +20070,10 @@ function discoveredProjectRoots(claudeState, currentDirectory, explicit = [], cu
|
|
|
19733
20070
|
return [...roots];
|
|
19734
20071
|
}
|
|
19735
20072
|
function cursorWorkspaceStorageCandidates(home, targetPlatform) {
|
|
19736
|
-
if (targetPlatform === "darwin") return [
|
|
19737
|
-
if (targetPlatform === "linux") return [
|
|
20073
|
+
if (targetPlatform === "darwin") return [join44(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage")];
|
|
20074
|
+
if (targetPlatform === "linux") return [join44(home, ".config", "Cursor", "User", "workspaceStorage")];
|
|
19738
20075
|
if (targetPlatform === "win32" && process.env.APPDATA) {
|
|
19739
|
-
return [
|
|
20076
|
+
return [join44(process.env.APPDATA, "Cursor", "User", "workspaceStorage")];
|
|
19740
20077
|
}
|
|
19741
20078
|
return [];
|
|
19742
20079
|
}
|
|
@@ -19752,12 +20089,12 @@ function cursorWorkspaceRoots(home, targetPlatform) {
|
|
|
19752
20089
|
}
|
|
19753
20090
|
for (const entry of entries) {
|
|
19754
20091
|
if (!entry.isDirectory() || entry.isSymbolicLink?.()) continue;
|
|
19755
|
-
const state = readJson(
|
|
20092
|
+
const state = readJson(join44(storage, entry.name, "workspace.json"));
|
|
19756
20093
|
const raw = state?.folder;
|
|
19757
20094
|
if (typeof raw !== "string" || !raw.trim()) continue;
|
|
19758
20095
|
try {
|
|
19759
20096
|
const path = raw.startsWith("file:") ? fileURLToPath(raw) : raw;
|
|
19760
|
-
if (existsSync43(path)) roots.add(
|
|
20097
|
+
if (existsSync43(path)) roots.add(resolve7(path));
|
|
19761
20098
|
} catch {
|
|
19762
20099
|
}
|
|
19763
20100
|
}
|
|
@@ -19769,8 +20106,8 @@ function codexMcpArtifacts(content) {
|
|
|
19769
20106
|
const sections = [...content.matchAll(/^\s*\[\s*([^\]]+)\s*\]\s*$/gm)];
|
|
19770
20107
|
for (let index = 0; index < sections.length && artifacts.length < 1e3; index++) {
|
|
19771
20108
|
const section = sections[index];
|
|
19772
|
-
const
|
|
19773
|
-
const root =
|
|
20109
|
+
const key2 = section[1].trim();
|
|
20110
|
+
const root = key2.match(/^mcp_servers\s*\.\s*(?:"((?:[^"\\]|\\.)+)"|'([^']+)'|([A-Za-z0-9_-]+))$/);
|
|
19774
20111
|
if (!root) continue;
|
|
19775
20112
|
let name = root[1] || root[2] || root[3];
|
|
19776
20113
|
if (root[1]) {
|
|
@@ -19783,8 +20120,8 @@ function codexMcpArtifacts(content) {
|
|
|
19783
20120
|
const start = (section.index ?? 0) + section[0].length;
|
|
19784
20121
|
const end = sections[index + 1]?.index ?? content.length;
|
|
19785
20122
|
const block = content.slice(start, end);
|
|
19786
|
-
const stringValue = (
|
|
19787
|
-
const found = block.match(new RegExp(`^\\s*${
|
|
20123
|
+
const stringValue = (key3) => {
|
|
20124
|
+
const found = block.match(new RegExp(`^\\s*${key3}\\s*=\\s*("(?:[^"\\\\]|\\\\.)*")`, "m"));
|
|
19788
20125
|
if (!found) return void 0;
|
|
19789
20126
|
try {
|
|
19790
20127
|
return JSON.parse(found[1]);
|
|
@@ -19816,9 +20153,9 @@ function flattenHookEntries(value) {
|
|
|
19816
20153
|
const out = [];
|
|
19817
20154
|
for (const entry of value) {
|
|
19818
20155
|
if (!entry || typeof entry !== "object") continue;
|
|
19819
|
-
const
|
|
19820
|
-
if (typeof
|
|
19821
|
-
if (Array.isArray(
|
|
20156
|
+
const record2 = entry;
|
|
20157
|
+
if (typeof record2.command === "string") out.push(record2);
|
|
20158
|
+
if (Array.isArray(record2.hooks)) out.push(...flattenHookEntries(record2.hooks));
|
|
19822
20159
|
}
|
|
19823
20160
|
return out;
|
|
19824
20161
|
}
|
|
@@ -19847,7 +20184,7 @@ function hookArtifacts(harness, config) {
|
|
|
19847
20184
|
function parseFrontmatter(content) {
|
|
19848
20185
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
19849
20186
|
if (!match) return {};
|
|
19850
|
-
const value = (
|
|
20187
|
+
const value = (key2) => match[1].match(new RegExp(`^${key2}:\\s*["']?([^"'\\n]+)`, "m"))?.[1]?.trim();
|
|
19851
20188
|
return { name: value("name"), version: value("version") };
|
|
19852
20189
|
}
|
|
19853
20190
|
function skillArtifacts(harness, root) {
|
|
@@ -19862,7 +20199,7 @@ function skillArtifacts(harness, root) {
|
|
|
19862
20199
|
}
|
|
19863
20200
|
for (const entry of entries) {
|
|
19864
20201
|
if (entry.isSymbolicLink?.()) continue;
|
|
19865
|
-
const path =
|
|
20202
|
+
const path = join44(dir, entry.name);
|
|
19866
20203
|
if (entry.isFile() && entry.name === "SKILL.md") manifests.push(path);
|
|
19867
20204
|
else if (entry.isDirectory()) visit(path);
|
|
19868
20205
|
}
|
|
@@ -19871,8 +20208,8 @@ function skillArtifacts(harness, root) {
|
|
|
19871
20208
|
return manifests.map((path) => {
|
|
19872
20209
|
const content = readText(path);
|
|
19873
20210
|
const frontmatter = parseFrontmatter(content);
|
|
19874
|
-
const rel =
|
|
19875
|
-
const name = frontmatter.name || basename4(
|
|
20211
|
+
const rel = relative2(root, path).replaceAll("\\", "/");
|
|
20212
|
+
const name = frontmatter.name || basename4(join44(path, "..")) || "skill";
|
|
19876
20213
|
return {
|
|
19877
20214
|
harness,
|
|
19878
20215
|
type: "skill",
|
|
@@ -19896,7 +20233,7 @@ function cursorExtensionArtifacts(root) {
|
|
|
19896
20233
|
}
|
|
19897
20234
|
const artifacts = [];
|
|
19898
20235
|
for (const dir of dirs) {
|
|
19899
|
-
const pkg = readJson(
|
|
20236
|
+
const pkg = readJson(join44(root, dir.name, "package.json"));
|
|
19900
20237
|
if (!pkg) continue;
|
|
19901
20238
|
const publisher = typeof pkg.publisher === "string" ? pkg.publisher : void 0;
|
|
19902
20239
|
const name = typeof pkg.name === "string" ? pkg.name : dir.name;
|
|
@@ -19916,21 +20253,21 @@ function cursorExtensionArtifacts(root) {
|
|
|
19916
20253
|
return artifacts;
|
|
19917
20254
|
}
|
|
19918
20255
|
function deploymentMode2(home) {
|
|
19919
|
-
const raw = readText(
|
|
19920
|
-
const value = (
|
|
20256
|
+
const raw = readText(join44(home, ".synkro", "config.env"));
|
|
20257
|
+
const value = (key2) => raw.match(new RegExp(`^${key2}=['"]?([^'"\\n]*)`, "m"))?.[1]?.toLowerCase();
|
|
19921
20258
|
if (value("SYNKRO_GRADING_MODE") === "byok") return "byok";
|
|
19922
20259
|
if (value("SYNKRO_STORAGE_MODE") === "cloud") return "cloud";
|
|
19923
20260
|
return "local";
|
|
19924
20261
|
}
|
|
19925
20262
|
function telemetryHealth(home) {
|
|
19926
|
-
const meta = readJson(
|
|
20263
|
+
const meta = readJson(join44(home, ".synkro", "telemetry-meta.json"));
|
|
19927
20264
|
const health = {};
|
|
19928
20265
|
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;
|
|
19929
20266
|
if (meta?.last_flush_error) health.telemetry_last_error = "flush_failed";
|
|
19930
|
-
const queue =
|
|
20267
|
+
const queue = join44(home, ".synkro", "telemetry-pending.jsonl");
|
|
19931
20268
|
try {
|
|
19932
|
-
const size =
|
|
19933
|
-
health.telemetry_backlog = size <= 5 * 1024 * 1024 ?
|
|
20269
|
+
const size = statSync7(queue).size;
|
|
20270
|
+
health.telemetry_backlog = size <= 5 * 1024 * 1024 ? readFileSync41(queue, "utf8").split("\n").filter(Boolean).length : Math.ceil(size / 1024);
|
|
19934
20271
|
} catch {
|
|
19935
20272
|
}
|
|
19936
20273
|
return health;
|
|
@@ -19971,7 +20308,7 @@ function harnessSnapshot(agent) {
|
|
|
19971
20308
|
}
|
|
19972
20309
|
const config = readJson(agent.settingsPath);
|
|
19973
20310
|
const coverage = inspectCodexHooks(agent.settingsPath);
|
|
19974
|
-
const toml = readText(
|
|
20311
|
+
const toml = readText(join44(agent.configDir, "config.toml"));
|
|
19975
20312
|
const permission = toml.match(/^\s*approval_policy\s*=\s*["']([^"']+)/m)?.[1];
|
|
19976
20313
|
return {
|
|
19977
20314
|
row: {
|
|
@@ -19992,7 +20329,7 @@ function collectOperationalInventory(options = {}) {
|
|
|
19992
20329
|
const detected = options.detectedAgents ?? detectAgents();
|
|
19993
20330
|
const identity = getOperationalInstallationIdentity(options.identityPath);
|
|
19994
20331
|
const targetPlatform = options.platformName ?? platform6();
|
|
19995
|
-
const codexHome = options.homeDir ?
|
|
20332
|
+
const codexHome = options.homeDir ? join44(home, ".codex") : process.env.CODEX_HOME || join44(home, ".codex");
|
|
19996
20333
|
const harnesses = [];
|
|
19997
20334
|
const artifacts = [];
|
|
19998
20335
|
for (const agent of detected) {
|
|
@@ -20000,7 +20337,7 @@ function collectOperationalInventory(options = {}) {
|
|
|
20000
20337
|
harnesses.push(row2);
|
|
20001
20338
|
artifacts.push(...hookArtifacts(row2.harness, config));
|
|
20002
20339
|
}
|
|
20003
|
-
const claudeJson = readJson(
|
|
20340
|
+
const claudeJson = readJson(join44(home, ".claude.json"));
|
|
20004
20341
|
artifacts.push(...mcpArtifactsFromJson("claude_code", claudeJson));
|
|
20005
20342
|
if (claudeJson?.projects && typeof claudeJson.projects === "object") {
|
|
20006
20343
|
for (const [projectPath, project] of Object.entries(claudeJson.projects)) {
|
|
@@ -20008,8 +20345,8 @@ function collectOperationalInventory(options = {}) {
|
|
|
20008
20345
|
artifacts.push(...mcpArtifactsFromJson("claude_code", project, `local:${sha256(projectPath).slice(0, 16)}`));
|
|
20009
20346
|
}
|
|
20010
20347
|
}
|
|
20011
|
-
artifacts.push(...mcpArtifactsFromJson("cursor", readJson(
|
|
20012
|
-
artifacts.push(...codexMcpArtifacts(readText(
|
|
20348
|
+
artifacts.push(...mcpArtifactsFromJson("cursor", readJson(join44(home, ".cursor", "mcp.json"))));
|
|
20349
|
+
artifacts.push(...codexMcpArtifacts(readText(join44(codexHome, "config.toml"))));
|
|
20013
20350
|
const projectRoots = discoveredProjectRoots(
|
|
20014
20351
|
claudeJson,
|
|
20015
20352
|
options.currentDirectory ?? process.cwd(),
|
|
@@ -20020,11 +20357,11 @@ function collectOperationalInventory(options = {}) {
|
|
|
20020
20357
|
const scopeHash = sha256(projectRoot).slice(0, 16);
|
|
20021
20358
|
artifacts.push(...mcpArtifactsFromJson(
|
|
20022
20359
|
"claude_code",
|
|
20023
|
-
readJson(
|
|
20360
|
+
readJson(join44(projectRoot, ".mcp.json")),
|
|
20024
20361
|
`project:${scopeHash}`
|
|
20025
20362
|
));
|
|
20026
|
-
const cursorProjectConfig =
|
|
20027
|
-
if (
|
|
20363
|
+
const cursorProjectConfig = join44(projectRoot, ".cursor", "mcp.json");
|
|
20364
|
+
if (resolve7(cursorProjectConfig) !== resolve7(join44(home, ".cursor", "mcp.json"))) {
|
|
20028
20365
|
artifacts.push(...mcpArtifactsFromJson(
|
|
20029
20366
|
"cursor",
|
|
20030
20367
|
readJson(cursorProjectConfig),
|
|
@@ -20046,7 +20383,7 @@ function collectOperationalInventory(options = {}) {
|
|
|
20046
20383
|
});
|
|
20047
20384
|
artifacts.push(...mcpArtifactsFromJson("claude_desktop", desktopConfig));
|
|
20048
20385
|
}
|
|
20049
|
-
const claudeSettings = readJson(
|
|
20386
|
+
const claudeSettings = readJson(join44(home, ".claude", "settings.json"));
|
|
20050
20387
|
if (claudeSettings?.enabledPlugins && typeof claudeSettings.enabledPlugins === "object") {
|
|
20051
20388
|
for (const [name, enabled] of Object.entries(claudeSettings.enabledPlugins)) {
|
|
20052
20389
|
artifacts.push({
|
|
@@ -20060,14 +20397,14 @@ function collectOperationalInventory(options = {}) {
|
|
|
20060
20397
|
});
|
|
20061
20398
|
}
|
|
20062
20399
|
}
|
|
20063
|
-
artifacts.push(...skillArtifacts("claude_code",
|
|
20064
|
-
artifacts.push(...skillArtifacts("cursor",
|
|
20065
|
-
artifacts.push(...skillArtifacts("codex",
|
|
20066
|
-
artifacts.push(...cursorExtensionArtifacts(
|
|
20400
|
+
artifacts.push(...skillArtifacts("claude_code", join44(home, ".claude", "skills")));
|
|
20401
|
+
artifacts.push(...skillArtifacts("cursor", join44(home, ".cursor", "skills")));
|
|
20402
|
+
artifacts.push(...skillArtifacts("codex", join44(codexHome, "skills")));
|
|
20403
|
+
artifacts.push(...cursorExtensionArtifacts(join44(home, ".cursor", "extensions")));
|
|
20067
20404
|
const uniqueArtifacts = /* @__PURE__ */ new Map();
|
|
20068
20405
|
for (const artifact of artifacts) {
|
|
20069
|
-
const
|
|
20070
|
-
uniqueArtifacts.set(
|
|
20406
|
+
const key2 = `${artifact.harness || "global"}:${artifact.type}:${artifact.canonical_id}`;
|
|
20407
|
+
uniqueArtifacts.set(key2, artifact);
|
|
20071
20408
|
}
|
|
20072
20409
|
const codingHarnesses = harnesses.filter((row2) => row2.harness === "claude_code" || row2.harness === "cursor" || row2.harness === "codex");
|
|
20073
20410
|
const health = telemetryHealth(home) ?? {};
|
|
@@ -20100,7 +20437,7 @@ var init_collector = __esm({
|
|
|
20100
20437
|
init_ccHookConfig();
|
|
20101
20438
|
init_cursorHookConfig();
|
|
20102
20439
|
init_codexHookConfig();
|
|
20103
|
-
|
|
20440
|
+
init_identity2();
|
|
20104
20441
|
}
|
|
20105
20442
|
});
|
|
20106
20443
|
|
|
@@ -20119,19 +20456,19 @@ import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
|
|
|
20119
20456
|
import { spawn as spawn11 } from "child_process";
|
|
20120
20457
|
import {
|
|
20121
20458
|
existsSync as existsSync44,
|
|
20122
|
-
mkdirSync as
|
|
20123
|
-
readFileSync as
|
|
20459
|
+
mkdirSync as mkdirSync26,
|
|
20460
|
+
readFileSync as readFileSync42,
|
|
20124
20461
|
renameSync as renameSync10,
|
|
20125
|
-
writeFileSync as
|
|
20462
|
+
writeFileSync as writeFileSync32
|
|
20126
20463
|
} from "fs";
|
|
20127
20464
|
import { homedir as homedir46 } from "os";
|
|
20128
|
-
import { dirname as
|
|
20465
|
+
import { dirname as dirname15, join as join45 } from "path";
|
|
20129
20466
|
function syncStatePath() {
|
|
20130
|
-
return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH ||
|
|
20467
|
+
return process.env.SYNKRO_INVENTORY_SYNC_STATE_PATH || join45(homedir46(), ".synkro", "inventory-sync.json");
|
|
20131
20468
|
}
|
|
20132
20469
|
function readState(path = syncStatePath()) {
|
|
20133
20470
|
try {
|
|
20134
|
-
const parsed = JSON.parse(
|
|
20471
|
+
const parsed = JSON.parse(readFileSync42(path, "utf8"));
|
|
20135
20472
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
20136
20473
|
} catch {
|
|
20137
20474
|
return {};
|
|
@@ -20139,9 +20476,9 @@ function readState(path = syncStatePath()) {
|
|
|
20139
20476
|
}
|
|
20140
20477
|
function writeState(state, path = syncStatePath()) {
|
|
20141
20478
|
try {
|
|
20142
|
-
|
|
20479
|
+
mkdirSync26(dirname15(path), { recursive: true, mode: 448 });
|
|
20143
20480
|
const temp = `${path}.${process.pid}.tmp`;
|
|
20144
|
-
|
|
20481
|
+
writeFileSync32(temp, JSON.stringify(state, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
20145
20482
|
renameSync10(temp, path);
|
|
20146
20483
|
} catch {
|
|
20147
20484
|
}
|
|
@@ -20154,18 +20491,18 @@ function shouldSyncInventory(state, now = Date.now(), target) {
|
|
|
20154
20491
|
return !Number.isFinite(lastAttempt) || lastAttempt <= 0 || now - lastAttempt >= FAILURE_RETRY_MS;
|
|
20155
20492
|
}
|
|
20156
20493
|
function readConfig() {
|
|
20157
|
-
const path =
|
|
20494
|
+
const path = join45(homedir46(), ".synkro", "config.env");
|
|
20158
20495
|
const out = {};
|
|
20159
20496
|
try {
|
|
20160
|
-
for (const rawLine of
|
|
20497
|
+
for (const rawLine of readFileSync42(path, "utf8").split("\n")) {
|
|
20161
20498
|
const line = rawLine.trim();
|
|
20162
20499
|
if (!line || line.startsWith("#")) continue;
|
|
20163
20500
|
const index = line.indexOf("=");
|
|
20164
20501
|
if (index <= 0) continue;
|
|
20165
|
-
const
|
|
20502
|
+
const key2 = line.slice(0, index).trim();
|
|
20166
20503
|
let value = line.slice(index + 1).trim();
|
|
20167
20504
|
if (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
|
|
20168
|
-
out[
|
|
20505
|
+
out[key2] = value;
|
|
20169
20506
|
}
|
|
20170
20507
|
} catch {
|
|
20171
20508
|
}
|
|
@@ -20196,7 +20533,7 @@ function resolveInventoryGateway(raw) {
|
|
|
20196
20533
|
}
|
|
20197
20534
|
async function loadToken() {
|
|
20198
20535
|
try {
|
|
20199
|
-
const durable =
|
|
20536
|
+
const durable = readFileSync42(join45(homedir46(), ".synkro", ".mcp-jwt"), "utf8").trim();
|
|
20200
20537
|
if (durable) return durable;
|
|
20201
20538
|
} catch {
|
|
20202
20539
|
}
|
|
@@ -20212,7 +20549,7 @@ async function loadToken() {
|
|
|
20212
20549
|
function stable(value) {
|
|
20213
20550
|
if (Array.isArray(value)) return value.map(stable);
|
|
20214
20551
|
if (!value || typeof value !== "object") return value;
|
|
20215
|
-
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([
|
|
20552
|
+
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key2, child]) => [key2, stable(child)]));
|
|
20216
20553
|
}
|
|
20217
20554
|
function inventorySnapshotChunks(snapshot, maxBytes = INVENTORY_CHUNK_BYTES) {
|
|
20218
20555
|
const { collected_at: _heartbeat, ...material } = snapshot;
|
|
@@ -20338,23 +20675,23 @@ var init_sync2 = __esm({
|
|
|
20338
20675
|
});
|
|
20339
20676
|
|
|
20340
20677
|
// cli/bootstrap.js
|
|
20341
|
-
import { readFileSync as
|
|
20342
|
-
import { resolve as
|
|
20678
|
+
import { readFileSync as readFileSync43, existsSync as existsSync45 } from "fs";
|
|
20679
|
+
import { resolve as resolve8 } from "path";
|
|
20343
20680
|
process.title = "synkro";
|
|
20344
20681
|
var envCandidates = [
|
|
20345
|
-
|
|
20682
|
+
resolve8(process.env.HOME ?? "", ".synkro", "config.env")
|
|
20346
20683
|
];
|
|
20347
20684
|
for (const envPath of envCandidates) {
|
|
20348
20685
|
if (!existsSync45(envPath)) continue;
|
|
20349
|
-
const envContent =
|
|
20686
|
+
const envContent = readFileSync43(envPath, "utf-8");
|
|
20350
20687
|
for (const line of envContent.split("\n")) {
|
|
20351
20688
|
const trimmed = line.trim();
|
|
20352
20689
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
20353
20690
|
const eqIndex = trimmed.indexOf("=");
|
|
20354
20691
|
if (eqIndex <= 0) continue;
|
|
20355
|
-
const
|
|
20692
|
+
const key2 = trimmed.slice(0, eqIndex).trim();
|
|
20356
20693
|
const value = trimmed.slice(eqIndex + 1).trim().replace(/^['"]|['"]$/g, "");
|
|
20357
|
-
if (!process.env[
|
|
20694
|
+
if (!process.env[key2] && !value.startsWith("op://")) process.env[key2] = value;
|
|
20358
20695
|
}
|
|
20359
20696
|
}
|
|
20360
20697
|
var args = process.argv.slice(2);
|
|
@@ -20363,7 +20700,7 @@ var subArgs = args.slice(1);
|
|
|
20363
20700
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
20364
20701
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "inventory-sync", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
20365
20702
|
function printVersion() {
|
|
20366
|
-
console.log("1.10.
|
|
20703
|
+
console.log("1.10.9");
|
|
20367
20704
|
}
|
|
20368
20705
|
function printHelp2() {
|
|
20369
20706
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|