@synkro-sh/cli 1.10.9 → 1.10.11
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 +467 -135
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -147,7 +147,7 @@ function getIdentity() {
|
|
|
147
147
|
if (cached2) return cached2;
|
|
148
148
|
let cliVersion2 = "0.0.0";
|
|
149
149
|
try {
|
|
150
|
-
cliVersion2 = "1.10.
|
|
150
|
+
cliVersion2 = "1.10.11";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -1645,10 +1645,9 @@ function installCCHooks(settingsPath, config) {
|
|
|
1645
1645
|
{
|
|
1646
1646
|
type: "command",
|
|
1647
1647
|
command: config.cvePrecheckScriptPath,
|
|
1648
|
-
//
|
|
1649
|
-
//
|
|
1650
|
-
|
|
1651
|
-
timeout: 15
|
|
1648
|
+
// Only a confirmed vulnerable runtime dependency reaches VEX triage. Codex can
|
|
1649
|
+
// take 13-17s for a valid answer, so match the bounded 25s gate plus its margin.
|
|
1650
|
+
timeout: 30
|
|
1652
1651
|
},
|
|
1653
1652
|
// skill-judge — its OWN system-message block, parallel with edit-precheck.
|
|
1654
1653
|
...config.skillJudgeScriptPath ? [{
|
|
@@ -1972,7 +1971,7 @@ function installCursorHooks(hooksJsonPath, config) {
|
|
|
1972
1971
|
matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
|
|
1973
1972
|
});
|
|
1974
1973
|
pushCcHook(h, "preToolUse", config.cvePrecheckScriptPath, {
|
|
1975
|
-
timeout:
|
|
1974
|
+
timeout: 30,
|
|
1976
1975
|
matcher: "Write|Edit|StrReplace|MultiEdit|NotebookEdit|edit_file|reapply|edit_notebook|ApplyPatch|apply_patch"
|
|
1977
1976
|
});
|
|
1978
1977
|
pushCcHook(h, "preToolUse", config.agentJudgeScriptPath, {
|
|
@@ -2204,7 +2203,7 @@ function installCodexHooks(hooksJsonPath, config) {
|
|
|
2204
2203
|
push(h, "PreToolUse", [
|
|
2205
2204
|
cmd(config.editPrecheckScriptPath, 50, "Reviewing edit"),
|
|
2206
2205
|
cmd(config.cwePrecheckScriptPath, 50),
|
|
2207
|
-
cmd(config.cvePrecheckScriptPath,
|
|
2206
|
+
cmd(config.cvePrecheckScriptPath, 30),
|
|
2208
2207
|
...config.skillJudgeScriptPath ? [cmd(config.skillJudgeScriptPath, 50)] : []
|
|
2209
2208
|
], M_EDIT);
|
|
2210
2209
|
push(h, "PostToolUse", [
|
|
@@ -7934,7 +7933,7 @@ var init_dockerInstall = __esm({
|
|
|
7934
7933
|
HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
|
|
7935
7934
|
CONTAINER_NAME = resolveContainerName();
|
|
7936
7935
|
defaultImageVersion = () => {
|
|
7937
|
-
if (true) return "1.10.
|
|
7936
|
+
if (true) return "1.10.11";
|
|
7938
7937
|
try {
|
|
7939
7938
|
const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
|
|
7940
7939
|
if (pkg.version) return pkg.version;
|
|
@@ -8994,7 +8993,7 @@ function writeConfigEnv(opts) {
|
|
|
8994
8993
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
8995
8994
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
8996
8995
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
8997
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.10.
|
|
8996
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.10.11")}`
|
|
8998
8997
|
];
|
|
8999
8998
|
if (safeSynkroBin) lines2.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
9000
8999
|
if (safeUserId) lines2.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -9741,7 +9740,7 @@ async function installCommand(opts = {}) {
|
|
|
9741
9740
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
9742
9741
|
emit("install", {
|
|
9743
9742
|
phase: "started",
|
|
9744
|
-
cli_version_to: "1.10.
|
|
9743
|
+
cli_version_to: "1.10.11",
|
|
9745
9744
|
agents_detected: agents.map((a) => a.kind),
|
|
9746
9745
|
with_github: false,
|
|
9747
9746
|
with_local_cc: false,
|
|
@@ -15166,7 +15165,6 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
|
|
|
15166
15165
|
const tabPopup = "display-popup -E -w 60 -h 18 -S fg=colour111 " + shellQuote3(tabLauncher);
|
|
15167
15166
|
const dispatch = 'if-shell -F "#{==:#{mouse_status_range},tabmenu}" "' + tabPopup + '" "select-window -t="';
|
|
15168
15167
|
const style = [
|
|
15169
|
-
["set-option", "-t", UI_SESSION, "-q", "@synkro_build", buildStamp(bootPath)],
|
|
15170
15168
|
// Mouse: drag the pane border to resize the sidebar, click a pane to
|
|
15171
15169
|
// focus it, click rows/chips. Without this the fixed split reads as
|
|
15172
15170
|
// "blocked in".
|
|
@@ -15197,21 +15195,86 @@ async function styleOuterSession(bootPath, repoCwd, sidebarWidth) {
|
|
|
15197
15195
|
// Alt+s always returns focus to the sidebar (the leftmost pane).
|
|
15198
15196
|
["bind-key", "-n", "M-s", "select-pane", "-L"]
|
|
15199
15197
|
];
|
|
15200
|
-
for (const argv of style)
|
|
15201
|
-
|
|
15198
|
+
for (const argv of style) {
|
|
15199
|
+
if (!(await run(HOST, ["tmux", ...argv])).ok) return false;
|
|
15200
|
+
}
|
|
15201
|
+
for (const argv of buildClipboardBindings(UI_SESSION)) {
|
|
15202
|
+
if (!(await run(HOST, argv)).ok) return false;
|
|
15203
|
+
}
|
|
15204
|
+
return true;
|
|
15202
15205
|
}
|
|
15203
15206
|
function parseUiPanes(output) {
|
|
15204
|
-
return String(output || "").split("\n").map((line) => line.split("|")).filter((cols) => cols.length >=
|
|
15207
|
+
return String(output || "").split("\n").map((line) => line.split("|")).filter((cols) => cols.length >= 7).map(([window, pane, width, dead, remain, cwd, ...command]) => ({
|
|
15205
15208
|
window,
|
|
15206
15209
|
pane,
|
|
15207
15210
|
width: Number(width) || 0,
|
|
15211
|
+
dead: dead === "1",
|
|
15212
|
+
remain: remain === "on",
|
|
15213
|
+
cwd,
|
|
15208
15214
|
command: command.join("|")
|
|
15209
15215
|
})).filter((row2) => Boolean(row2.window && row2.pane));
|
|
15210
15216
|
}
|
|
15211
|
-
async function
|
|
15212
|
-
const
|
|
15213
|
-
|
|
15214
|
-
|
|
15217
|
+
async function ensureUiCenterPane(options) {
|
|
15218
|
+
const listed = await run(HOST, [
|
|
15219
|
+
"tmux",
|
|
15220
|
+
"list-panes",
|
|
15221
|
+
"-t",
|
|
15222
|
+
options.sidebarPane,
|
|
15223
|
+
"-F",
|
|
15224
|
+
"#{pane_id}|#{pane_dead}|#{remain-on-exit}|#{pane_start_command}"
|
|
15225
|
+
]);
|
|
15226
|
+
if (!listed.ok) return { ok: false, pane: "", created: false, recovered: false };
|
|
15227
|
+
const rows = listed.stdout.split("\n").map((line) => line.split("|")).filter((cols) => cols.length >= 4).map(([pane, dead, remain, ...command]) => ({
|
|
15228
|
+
pane,
|
|
15229
|
+
dead: dead === "1",
|
|
15230
|
+
remain: remain === "on",
|
|
15231
|
+
command: command.join("|")
|
|
15232
|
+
}));
|
|
15233
|
+
let center = rows.find((row2) => row2.pane !== options.sidebarPane);
|
|
15234
|
+
let created = false;
|
|
15235
|
+
if (!center) {
|
|
15236
|
+
const split = await run(HOST, [
|
|
15237
|
+
"tmux",
|
|
15238
|
+
"split-window",
|
|
15239
|
+
"-h",
|
|
15240
|
+
"-t",
|
|
15241
|
+
options.sidebarPane,
|
|
15242
|
+
"-c",
|
|
15243
|
+
options.cwd,
|
|
15244
|
+
"-P",
|
|
15245
|
+
"-F",
|
|
15246
|
+
"#{pane_id}",
|
|
15247
|
+
"tail -f /dev/null"
|
|
15248
|
+
]);
|
|
15249
|
+
const pane = split.stdout.trim();
|
|
15250
|
+
if (!split.ok || !pane) return { ok: false, pane: "", created: false, recovered: false };
|
|
15251
|
+
center = { pane, dead: false, remain: false, command: "tail -f /dev/null" };
|
|
15252
|
+
created = true;
|
|
15253
|
+
}
|
|
15254
|
+
const kept = await run(HOST, buildKeepPaneAfterExit(center.pane));
|
|
15255
|
+
if (!kept.ok) return { ok: false, pane: center.pane, created, recovered: false };
|
|
15256
|
+
const recovered = created || center.dead;
|
|
15257
|
+
if (recovered) {
|
|
15258
|
+
const command = options.terminalCommand === void 0 ? makeTerminalCommand(options.bootPath) : options.terminalCommand;
|
|
15259
|
+
const respawn = ["tmux", "respawn-pane", "-k", "-t", center.pane, "-c", options.cwd];
|
|
15260
|
+
if (command) respawn.push(command);
|
|
15261
|
+
const restored = await run(HOST, respawn);
|
|
15262
|
+
if (!restored.ok) return { ok: false, pane: center.pane, created, recovered: false };
|
|
15263
|
+
const spaceName = options.cwd.split("/").filter(Boolean).pop() || "space";
|
|
15264
|
+
const metadata = [
|
|
15265
|
+
["tmux", "set-option", "-t", center.pane, "-w", "-q", "@synkro_agent", ""],
|
|
15266
|
+
["tmux", "set-option", "-t", center.pane, "-w", "-q", "@synkro_cwd", options.cwd],
|
|
15267
|
+
["tmux", "set-option", "-t", center.pane, "-w", "-q", "@synkro_kind", "terminal"],
|
|
15268
|
+
["tmux", "set-option", "-t", center.pane, "-w", "automatic-rename", "off"],
|
|
15269
|
+
["tmux", "rename-window", "-t", center.pane, tabTitle("terminal", spaceName)]
|
|
15270
|
+
];
|
|
15271
|
+
for (const argv of metadata) {
|
|
15272
|
+
if (!(await run(HOST, argv)).ok) return { ok: false, pane: center.pane, created, recovered: false };
|
|
15273
|
+
}
|
|
15274
|
+
}
|
|
15275
|
+
return { ok: true, pane: center.pane, created, recovered };
|
|
15276
|
+
}
|
|
15277
|
+
async function reconcileUiShell(bootPath, repoCwd, options = {}) {
|
|
15215
15278
|
const listed = await run(HOST, [
|
|
15216
15279
|
"tmux",
|
|
15217
15280
|
"list-panes",
|
|
@@ -15219,18 +15282,64 @@ async function refreshUiShell(bootPath, repoCwd) {
|
|
|
15219
15282
|
"-t",
|
|
15220
15283
|
UI_SESSION,
|
|
15221
15284
|
"-F",
|
|
15222
|
-
"#{window_id}|#{pane_id}|#{pane_width}|#{pane_start_command}"
|
|
15285
|
+
"#{window_id}|#{pane_id}|#{pane_width}|#{pane_dead}|#{remain-on-exit}|#{@synkro_cwd}|#{pane_start_command}"
|
|
15223
15286
|
]);
|
|
15224
|
-
if (!listed.ok) return;
|
|
15287
|
+
if (!listed.ok) return { ok: false, changed: false };
|
|
15225
15288
|
const panes = parseUiPanes(listed.stdout);
|
|
15226
15289
|
const sidebars = panes.filter((row2) => row2.command.includes("--sidebar"));
|
|
15227
|
-
if (sidebars.length === 0) return;
|
|
15228
|
-
|
|
15290
|
+
if (sidebars.length === 0) return { ok: false, changed: false };
|
|
15291
|
+
let changed = false;
|
|
15229
15292
|
for (const sidebar of sidebars) {
|
|
15230
|
-
const
|
|
15231
|
-
|
|
15232
|
-
|
|
15293
|
+
const previous = panes.find((row2) => row2.window === sidebar.window && row2.pane !== sidebar.pane);
|
|
15294
|
+
const cwd = sidebar.cwd ? sidebar.cwd.startsWith("/") ? sidebar.cwd : join34(repoCwd, sidebar.cwd) : repoCwd;
|
|
15295
|
+
const center = await ensureUiCenterPane({
|
|
15296
|
+
sidebarPane: sidebar.pane,
|
|
15297
|
+
bootPath,
|
|
15298
|
+
cwd,
|
|
15299
|
+
terminalCommand: options.terminalCommand
|
|
15300
|
+
});
|
|
15301
|
+
if (!center.ok) return { ok: false, changed };
|
|
15302
|
+
if (sidebar.cwd !== cwd) {
|
|
15303
|
+
const migrated2 = await run(HOST, ["tmux", "set-option", "-t", sidebar.window, "-w", "-q", "@synkro_cwd", cwd]);
|
|
15304
|
+
if (!migrated2.ok) return { ok: false, changed };
|
|
15305
|
+
changed = true;
|
|
15306
|
+
}
|
|
15307
|
+
const staleBinding = !sidebar.command.includes("SYNKRO_UI_CENTER=" + center.pane + " ");
|
|
15308
|
+
if (options.refreshSidebars || center.recovered || staleBinding) {
|
|
15309
|
+
const rebound = await run(HOST, [
|
|
15310
|
+
"tmux",
|
|
15311
|
+
"respawn-pane",
|
|
15312
|
+
"-k",
|
|
15313
|
+
"-t",
|
|
15314
|
+
sidebar.pane,
|
|
15315
|
+
sidebarCommand(bootPath, center.pane, repoCwd)
|
|
15316
|
+
]);
|
|
15317
|
+
if (!rebound.ok) return { ok: false, changed };
|
|
15318
|
+
changed = true;
|
|
15319
|
+
}
|
|
15320
|
+
if (!previous?.remain || center.created || center.recovered) changed = true;
|
|
15233
15321
|
}
|
|
15322
|
+
return { ok: true, changed };
|
|
15323
|
+
}
|
|
15324
|
+
async function refreshUiShell(bootPath, repoCwd, options = {}) {
|
|
15325
|
+
const expected = buildStamp(bootPath);
|
|
15326
|
+
const current = await run(HOST, ["tmux", "show-options", "-t", UI_SESSION, "-v", "@synkro_build"]);
|
|
15327
|
+
const buildChanged = !current.ok || current.stdout.trim() !== expected;
|
|
15328
|
+
if (buildChanged) {
|
|
15329
|
+
const width = await currentWindowColumns(UI_SESSION);
|
|
15330
|
+
if (!await styleOuterSession(bootPath, repoCwd, Number(sidebarColumns(width)))) return false;
|
|
15331
|
+
}
|
|
15332
|
+
const reconciled = await reconcileUiShell(bootPath, repoCwd, {
|
|
15333
|
+
terminalCommand: options.terminalCommand,
|
|
15334
|
+
refreshSidebars: buildChanged
|
|
15335
|
+
});
|
|
15336
|
+
if (!reconciled.ok) return false;
|
|
15337
|
+
if (reconciled.changed && options.persistLayout !== false) await snapshotTabs();
|
|
15338
|
+
if (buildChanged) {
|
|
15339
|
+
const stamped = await run(HOST, ["tmux", "set-option", "-t", UI_SESSION, "-q", "@synkro_build", expected]);
|
|
15340
|
+
if (!stamped.ok) return false;
|
|
15341
|
+
}
|
|
15342
|
+
return true;
|
|
15234
15343
|
}
|
|
15235
15344
|
async function buildTab(bootPath, repoCwd, spec) {
|
|
15236
15345
|
await run(HOST, ["tmux", "set-option", "-g", "history-limit", "50000"]);
|
|
@@ -15238,8 +15347,20 @@ async function buildTab(bootPath, repoCwd, spec) {
|
|
|
15238
15347
|
if (!await uiSessionExists()) {
|
|
15239
15348
|
const cols = String(Number(process.stdout.columns || 0) || 220);
|
|
15240
15349
|
const rows = String(Number(process.stdout.rows || 0) || 55);
|
|
15241
|
-
const create = [
|
|
15242
|
-
|
|
15350
|
+
const create = [
|
|
15351
|
+
"tmux",
|
|
15352
|
+
"new-session",
|
|
15353
|
+
"-d",
|
|
15354
|
+
"-s",
|
|
15355
|
+
UI_SESSION,
|
|
15356
|
+
"-x",
|
|
15357
|
+
cols,
|
|
15358
|
+
"-y",
|
|
15359
|
+
rows,
|
|
15360
|
+
"-c",
|
|
15361
|
+
spec.cwd,
|
|
15362
|
+
"tail -f /dev/null"
|
|
15363
|
+
];
|
|
15243
15364
|
const made = await run(HOST, create);
|
|
15244
15365
|
if (!made.ok) {
|
|
15245
15366
|
throw new Error("tmux new-session failed: " + made.stderr.trim());
|
|
@@ -15249,14 +15370,26 @@ async function buildTab(bootPath, repoCwd, spec) {
|
|
|
15249
15370
|
const first = await run(HOST, ["tmux", "list-windows", "-t", UI_SESSION, "-F", "#{window_id}"]);
|
|
15250
15371
|
windowTarget = first.stdout.split("\n")[0]?.trim() || UI_SESSION + ":1";
|
|
15251
15372
|
} else {
|
|
15252
|
-
const create = [
|
|
15253
|
-
|
|
15373
|
+
const create = [
|
|
15374
|
+
"tmux",
|
|
15375
|
+
"new-window",
|
|
15376
|
+
"-t",
|
|
15377
|
+
UI_SESSION,
|
|
15378
|
+
"-P",
|
|
15379
|
+
"-F",
|
|
15380
|
+
"#{window_id}",
|
|
15381
|
+
"-c",
|
|
15382
|
+
spec.cwd,
|
|
15383
|
+
"tail -f /dev/null"
|
|
15384
|
+
];
|
|
15254
15385
|
const created = await run(HOST, create);
|
|
15255
15386
|
windowTarget = created.stdout.trim();
|
|
15256
15387
|
if (!created.ok || !windowTarget) return;
|
|
15257
15388
|
}
|
|
15258
15389
|
const sidebarCols = sidebarColumns(await currentWindowColumns(windowTarget));
|
|
15259
|
-
await styleOuterSession(bootPath, repoCwd, Number(sidebarCols))
|
|
15390
|
+
if (!await styleOuterSession(bootPath, repoCwd, Number(sidebarCols))) {
|
|
15391
|
+
throw new Error("tmux outer session styling failed");
|
|
15392
|
+
}
|
|
15260
15393
|
const split = await run(HOST, [
|
|
15261
15394
|
"tmux",
|
|
15262
15395
|
"split-window",
|
|
@@ -15271,18 +15404,35 @@ async function buildTab(bootPath, repoCwd, spec) {
|
|
|
15271
15404
|
"tail -f /dev/null"
|
|
15272
15405
|
]);
|
|
15273
15406
|
const sidebarPane = split.stdout.trim();
|
|
15407
|
+
if (!split.ok || !sidebarPane) throw new Error("tmux sidebar split failed: " + split.stderr.trim());
|
|
15274
15408
|
const panes = await run(HOST, ["tmux", "list-panes", "-t", windowTarget, "-F", "#{pane_id}"]);
|
|
15275
15409
|
const centerPane = panes.stdout.split("\n").map((line) => line.trim()).filter(Boolean).find((id) => id !== sidebarPane) || "";
|
|
15276
|
-
|
|
15410
|
+
if (!centerPane) throw new Error("tmux center pane was not created");
|
|
15411
|
+
const kept = await run(HOST, buildKeepPaneAfterExit(centerPane));
|
|
15412
|
+
if (!kept.ok) throw new Error("tmux center durability failed: " + kept.stderr.trim());
|
|
15277
15413
|
if (spec.agentSession) {
|
|
15278
15414
|
await run(HOST, ["tmux", "set-option", "-t", windowTarget, "-w", "-q", "@synkro_agent", spec.agentSession]);
|
|
15279
15415
|
}
|
|
15280
15416
|
await run(HOST, ["tmux", "set-option", "-t", windowTarget, "-w", "-q", "@synkro_cwd", spec.cwd]);
|
|
15281
15417
|
await run(HOST, ["tmux", "set-option", "-t", windowTarget, "-w", "-q", "@synkro_kind", spec.kind || "terminal"]);
|
|
15282
|
-
await run(HOST, [
|
|
15418
|
+
const sidebarStarted = await run(HOST, [
|
|
15419
|
+
"tmux",
|
|
15420
|
+
"respawn-pane",
|
|
15421
|
+
"-k",
|
|
15422
|
+
"-t",
|
|
15423
|
+
sidebarPane,
|
|
15424
|
+
sidebarCommand(bootPath, centerPane, repoCwd)
|
|
15425
|
+
]);
|
|
15426
|
+
if (!sidebarStarted.ok) throw new Error("tmux sidebar start failed: " + sidebarStarted.stderr.trim());
|
|
15427
|
+
const centerStart = ["tmux", "respawn-pane", "-k", "-t", centerPane, "-c", spec.cwd];
|
|
15428
|
+
if (spec.center) centerStart.push(spec.center);
|
|
15429
|
+
const centerStarted = await run(HOST, centerStart);
|
|
15430
|
+
if (!centerStarted.ok) throw new Error("tmux center start failed: " + centerStarted.stderr.trim());
|
|
15283
15431
|
const title = spec.title || tabTitle("terminal", spec.cwd.split("/").filter(Boolean).pop() || "space");
|
|
15284
15432
|
await run(HOST, ["tmux", "set-option", "-t", windowTarget, "-w", "automatic-rename", "off"]);
|
|
15285
15433
|
await run(HOST, ["tmux", "rename-window", "-t", windowTarget, title]);
|
|
15434
|
+
const stamped = await run(HOST, ["tmux", "set-option", "-t", UI_SESSION, "-q", "@synkro_build", buildStamp(bootPath)]);
|
|
15435
|
+
if (!stamped.ok) throw new Error("tmux build stamp failed: " + stamped.stderr.trim());
|
|
15286
15436
|
await snapshotTabs();
|
|
15287
15437
|
await run(HOST, ["tmux", "select-pane", "-t", spec.focus === "sidebar" ? sidebarPane : centerPane]);
|
|
15288
15438
|
}
|
|
@@ -15325,7 +15475,9 @@ async function launchUi(bootPath, repoCwd) {
|
|
|
15325
15475
|
if (!await uiSessionExists()) {
|
|
15326
15476
|
await buildTab(bootPath, repoCwd, { cwd: repoCwd, center: makeTerminalCommand(bootPath), focus: "sidebar" });
|
|
15327
15477
|
} else {
|
|
15328
|
-
await refreshUiShell(bootPath, repoCwd)
|
|
15478
|
+
if (!await refreshUiShell(bootPath, repoCwd)) {
|
|
15479
|
+
throw new Error("Synkro UI could not repair its tmux layout");
|
|
15480
|
+
}
|
|
15329
15481
|
}
|
|
15330
15482
|
await pruneCollapsedClients();
|
|
15331
15483
|
return runInherit(process.env.TMUX ? ["tmux", "switch-client", "-t", UI_SESSION] : ["tmux", "attach-session", "-t", UI_SESSION]);
|
|
@@ -15798,8 +15950,11 @@ var init_awake = __esm({
|
|
|
15798
15950
|
function runnerFor(backend) {
|
|
15799
15951
|
return backend === "container" ? { kind: "container", container: CONTAINER_NAME2 } : { kind: "host" };
|
|
15800
15952
|
}
|
|
15953
|
+
function hasLiveAgentCenter(paneOutput, session) {
|
|
15954
|
+
return String(paneOutput || "").split("\n").map((line) => line.split("|")).some(([dead, ...command]) => dead === "0" && !command.join("|").includes("--sidebar") && command.join("|").includes(session));
|
|
15955
|
+
}
|
|
15801
15956
|
async function runSidebar() {
|
|
15802
|
-
|
|
15957
|
+
let centerPane = process.env.SYNKRO_UI_CENTER || "";
|
|
15803
15958
|
const outerSession = process.env.SYNKRO_UI_OUTER || "synkro-ui";
|
|
15804
15959
|
const bootPath = process.env.SYNKRO_UI_BOOT || process.argv[1];
|
|
15805
15960
|
const repoCwd = process.env.SYNKRO_UI_REPO || process.cwd();
|
|
@@ -15967,53 +16122,114 @@ async function runSidebar() {
|
|
|
15967
16122
|
const agent = state.agents[state.agentIndex];
|
|
15968
16123
|
if (agent) await showAgent(agent);
|
|
15969
16124
|
}
|
|
16125
|
+
async function resolveCenterPane(cwd) {
|
|
16126
|
+
if (!ownPane || !cwd) return "";
|
|
16127
|
+
const resolved = await ensureUiCenterPane({ sidebarPane: ownPane, bootPath, cwd });
|
|
16128
|
+
if (!resolved.ok) {
|
|
16129
|
+
state.message = "could not restore this tab";
|
|
16130
|
+
return "";
|
|
16131
|
+
}
|
|
16132
|
+
centerPane = resolved.pane;
|
|
16133
|
+
if (resolved.recovered) {
|
|
16134
|
+
state.viewing = "";
|
|
16135
|
+
await snapshotTabs();
|
|
16136
|
+
stripWidth = -1;
|
|
16137
|
+
await syncChipStrip();
|
|
16138
|
+
}
|
|
16139
|
+
return centerPane;
|
|
16140
|
+
}
|
|
15970
16141
|
async function showWorkspaceTerminal(cwd) {
|
|
15971
|
-
|
|
16142
|
+
const pane = await resolveCenterPane(cwd);
|
|
16143
|
+
if (!pane) return false;
|
|
15972
16144
|
const command = makeTerminalCommand(bootPath);
|
|
15973
|
-
const argv = ["tmux", "respawn-pane", "-k", "-t",
|
|
16145
|
+
const argv = ["tmux", "respawn-pane", "-k", "-t", pane, "-c", cwd];
|
|
15974
16146
|
if (command) argv.push(command);
|
|
15975
16147
|
const respawned = await run(host, argv);
|
|
15976
16148
|
if (!respawned.ok) {
|
|
15977
16149
|
state.message = "could not restore workspace terminal";
|
|
15978
|
-
return;
|
|
16150
|
+
return false;
|
|
15979
16151
|
}
|
|
15980
16152
|
const spaceName = cwd.split("/").filter(Boolean).pop() || "space";
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
16153
|
+
const metadata = [
|
|
16154
|
+
["tmux", "set-option", "-t", pane, "-w", "-q", "@synkro_agent", ""],
|
|
16155
|
+
["tmux", "set-option", "-t", pane, "-w", "-q", "@synkro_cwd", cwd],
|
|
16156
|
+
["tmux", "set-option", "-t", pane, "-w", "-q", "@synkro_kind", "terminal"],
|
|
16157
|
+
["tmux", "rename-window", "-t", pane, tabTitle("terminal", spaceName)]
|
|
16158
|
+
];
|
|
16159
|
+
for (const commandArgv of metadata) {
|
|
16160
|
+
if (!(await run(host, commandArgv)).ok) {
|
|
16161
|
+
state.message = "could not update this tab";
|
|
16162
|
+
return false;
|
|
16163
|
+
}
|
|
16164
|
+
}
|
|
15985
16165
|
state.viewing = "";
|
|
15986
16166
|
await snapshotTabs();
|
|
16167
|
+
return true;
|
|
15987
16168
|
}
|
|
15988
|
-
async function
|
|
15989
|
-
if (!centerPane) return;
|
|
15990
|
-
if (agent.status === "offline") {
|
|
15991
|
-
await restoreSelected();
|
|
15992
|
-
return;
|
|
15993
|
-
}
|
|
16169
|
+
async function markAgentViewed(agent) {
|
|
15994
16170
|
const current = memory.get(agent.session);
|
|
15995
16171
|
memory.set(agent.session, { hash: current?.hash || "", wasWorking: false, seen: true });
|
|
15996
16172
|
if (agent.space) rememberLastAgent(await repoOf(agent.space), agent.session);
|
|
15997
16173
|
state.viewing = agent.session;
|
|
16174
|
+
}
|
|
16175
|
+
async function showAgent(agent) {
|
|
16176
|
+
if (agent.status === "offline") {
|
|
16177
|
+
await restoreSelected();
|
|
16178
|
+
return false;
|
|
16179
|
+
}
|
|
15998
16180
|
const windows = await run(host, ["tmux", "list-windows", "-t", outerSession, "-F", "#{window_id}|#{@synkro_agent}"]);
|
|
15999
16181
|
const holder = windows.stdout.split("\n").map((line) => line.split("|")).find((cols) => cols[1] === agent.session);
|
|
16000
16182
|
if (holder) {
|
|
16001
|
-
await run(host, [
|
|
16002
|
-
|
|
16003
|
-
|
|
16183
|
+
const holderPanes = await run(host, [
|
|
16184
|
+
"tmux",
|
|
16185
|
+
"list-panes",
|
|
16186
|
+
"-t",
|
|
16187
|
+
holder[0],
|
|
16188
|
+
"-F",
|
|
16189
|
+
"#{pane_dead}|#{pane_start_command}"
|
|
16190
|
+
]);
|
|
16191
|
+
const liveCenter = holderPanes.ok && hasLiveAgentCenter(holderPanes.stdout, agent.session);
|
|
16192
|
+
if (liveCenter) {
|
|
16193
|
+
const selected = await run(host, ["tmux", "select-window", "-t", holder[0]]);
|
|
16194
|
+
if (selected.ok) {
|
|
16195
|
+
await markAgentViewed(agent);
|
|
16196
|
+
state.message = agent.name;
|
|
16197
|
+
return true;
|
|
16198
|
+
}
|
|
16199
|
+
}
|
|
16200
|
+
await run(host, ["tmux", "set-option", "-t", holder[0], "-w", "-q", "@synkro_agent", ""]);
|
|
16004
16201
|
}
|
|
16202
|
+
const pane = await resolveCenterPane(agent.space || selectedSpace()?.path || repoCwd);
|
|
16203
|
+
if (!pane) return false;
|
|
16005
16204
|
await run(runnerFor(agent.backend), buildEnableMouse(agent.session));
|
|
16006
16205
|
for (const argv of buildClipboardBindings(agent.session)) {
|
|
16007
16206
|
await run(runnerFor(agent.backend), argv);
|
|
16008
16207
|
}
|
|
16009
16208
|
const command = buildCenterAttachCommand(runnerFor(agent.backend), agent.session);
|
|
16010
|
-
await run(host, ["tmux", "respawn-pane", "-k", "-t",
|
|
16209
|
+
const attached = await run(host, ["tmux", "respawn-pane", "-k", "-t", pane, command]);
|
|
16210
|
+
if (!attached.ok) {
|
|
16211
|
+
state.message = "could not open " + agent.name;
|
|
16212
|
+
await showWorkspaceTerminal(agent.space || selectedSpace()?.path || repoCwd);
|
|
16213
|
+
return false;
|
|
16214
|
+
}
|
|
16011
16215
|
const where = agent.space ? agent.space.split("/").filter(Boolean).pop() || "" : "";
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16216
|
+
const metadata = [
|
|
16217
|
+
["tmux", "set-option", "-t", pane, "-w", "-q", "@synkro_agent", agent.session],
|
|
16218
|
+
["tmux", "set-option", "-t", pane, "-w", "-q", "@synkro_cwd", agent.space || repoCwd],
|
|
16219
|
+
["tmux", "set-option", "-t", pane, "-w", "-q", "@synkro_kind", agent.harness],
|
|
16220
|
+
["tmux", "rename-window", "-t", pane, tabTitle(agent.harness, where)]
|
|
16221
|
+
];
|
|
16222
|
+
for (const commandArgv of metadata) {
|
|
16223
|
+
if (!(await run(host, commandArgv)).ok) {
|
|
16224
|
+
state.message = "could not update " + agent.name + " tab";
|
|
16225
|
+
await showWorkspaceTerminal(agent.space || selectedSpace()?.path || repoCwd);
|
|
16226
|
+
return false;
|
|
16227
|
+
}
|
|
16228
|
+
}
|
|
16229
|
+
await markAgentViewed(agent);
|
|
16015
16230
|
await snapshotTabs();
|
|
16016
16231
|
state.message = "attached " + agent.name;
|
|
16232
|
+
return true;
|
|
16017
16233
|
}
|
|
16018
16234
|
async function confirmKillAgent() {
|
|
16019
16235
|
const agent = state.agents[state.agentIndex];
|
|
@@ -16024,7 +16240,8 @@ async function runSidebar() {
|
|
|
16024
16240
|
if (allAgents.some((candidate) => candidate.session === agent.session && candidate.status !== "offline")) return;
|
|
16025
16241
|
state.message = "closed " + agent.name;
|
|
16026
16242
|
if (!wasViewing) return;
|
|
16027
|
-
await showWorkspaceTerminal(agent.space || selectedSpace()?.path || repoCwd);
|
|
16243
|
+
const restored = await showWorkspaceTerminal(agent.space || selectedSpace()?.path || repoCwd);
|
|
16244
|
+
if (!restored) return;
|
|
16028
16245
|
const successor = nextLiveAgent(allAgents, agent.repo || "");
|
|
16029
16246
|
if (successor) await showAgent(successor);
|
|
16030
16247
|
}
|
|
@@ -16940,6 +17157,42 @@ var init_dialog = __esm({
|
|
|
16940
17157
|
}
|
|
16941
17158
|
});
|
|
16942
17159
|
|
|
17160
|
+
// cli/harness/events.ts
|
|
17161
|
+
function fixPollIds(raw) {
|
|
17162
|
+
const text = String(raw || "");
|
|
17163
|
+
const ids = [
|
|
17164
|
+
...Array.from(text.matchAll(FIX_POLL_MARKERS), (match) => match[1]),
|
|
17165
|
+
...Array.from(text.matchAll(LEGACY_FIX_POLL_MARKERS), (match) => match[1])
|
|
17166
|
+
];
|
|
17167
|
+
return Array.from(new Set(ids.filter(Boolean)));
|
|
17168
|
+
}
|
|
17169
|
+
function fixPollId(raw) {
|
|
17170
|
+
return fixPollIds(raw)[0] || "";
|
|
17171
|
+
}
|
|
17172
|
+
function cleanGuardText(raw) {
|
|
17173
|
+
return String(raw || "").replace(FIX_POLL_MARKERS, "").replace(/\n*\s*SYNKRO FIX POLL[\s\S]*$/i, "").replace(/\n?\d{4}-\d\d-\d\dT[^\n]*\sERROR\s+codex_core::tools::router:[^\n]*/gi, "").replace(/\s*Checking command\s*$/i, "").trim();
|
|
17174
|
+
}
|
|
17175
|
+
function blockReason(raw) {
|
|
17176
|
+
const text = cleanGuardText(raw);
|
|
17177
|
+
if (!text) return "blocked by policy";
|
|
17178
|
+
const guardAt = text.lastIndexOf("Guard:");
|
|
17179
|
+
if (guardAt >= 0) return text.slice(guardAt + "Guard:".length).trim();
|
|
17180
|
+
const afterTag = text.match(/\[synkro:[^\]]*\]\s*(.+)/is);
|
|
17181
|
+
if (afterTag) return afterTag[1].trim();
|
|
17182
|
+
const afterHook = text.match(/blocked by a hook:\s*(.+)/is);
|
|
17183
|
+
if (afterHook) return afterHook[1].trim();
|
|
17184
|
+
return text;
|
|
17185
|
+
}
|
|
17186
|
+
var BLOCK_MARKER, FIX_POLL_MARKERS, LEGACY_FIX_POLL_MARKERS;
|
|
17187
|
+
var init_events = __esm({
|
|
17188
|
+
"cli/harness/events.ts"() {
|
|
17189
|
+
"use strict";
|
|
17190
|
+
BLOCK_MARKER = /blocked by a hook|\[synkro:/i;
|
|
17191
|
+
FIX_POLL_MARKERS = /\[synkro:fix-poll\s+item_id=\\*["']?([A-Za-z0-9_-]+)\\*["']?\s*\]/gi;
|
|
17192
|
+
LEGACY_FIX_POLL_MARKERS = /SYNKRO FIX POLL\s*\(item_id=([A-Za-z0-9_-]+)\)/gi;
|
|
17193
|
+
}
|
|
17194
|
+
});
|
|
17195
|
+
|
|
16943
17196
|
// cli/harness/render.ts
|
|
16944
17197
|
function spinnerFrame(tick) {
|
|
16945
17198
|
return SPINNER[Math.abs(tick) % SPINNER.length];
|
|
@@ -17001,10 +17254,11 @@ function renderEvent(event, width = 100) {
|
|
|
17001
17254
|
}
|
|
17002
17255
|
case "tool-end": {
|
|
17003
17256
|
if (event.blocked) {
|
|
17257
|
+
const reason = cleanGuardText(event.reason);
|
|
17004
17258
|
return [
|
|
17005
17259
|
S2.panel + S2.blocked + " \u29C9 Blocked" + S2.reset + S2.panel + S2.secondary + " " + clip3(terminalText(event.target), body - 21) + S2.reset,
|
|
17006
|
-
...wrap(terminalText(
|
|
17007
|
-
...wrap(blockNextStep(
|
|
17260
|
+
...wrap(terminalText(reason), body - 6).map((line, index) => index === 0 ? S2.blocked + " " + ELBOW + " " + S2.reset + S2.rule + line + S2.reset : S2.rule + " " + line + S2.reset),
|
|
17261
|
+
...wrap(blockNextStep(reason), body - 6).map((line) => S2.secondary + " " + line + S2.reset)
|
|
17008
17262
|
];
|
|
17009
17263
|
}
|
|
17010
17264
|
const timing = event.durationMs == null ? "" : " \xB7 " + seconds(event.durationMs);
|
|
@@ -17053,6 +17307,7 @@ var ESC2, color, S2, CLEAR_LINE, BULLET, ELBOW, SPINNER, TOOL_LABEL;
|
|
|
17053
17307
|
var init_render2 = __esm({
|
|
17054
17308
|
"cli/harness/render.ts"() {
|
|
17055
17309
|
"use strict";
|
|
17310
|
+
init_events();
|
|
17056
17311
|
ESC2 = "\x1B[";
|
|
17057
17312
|
color = (hex, plane = 38) => {
|
|
17058
17313
|
const [r, g, b] = hex.match(/../g).map((part) => Number.parseInt(part, 16));
|
|
@@ -17107,35 +17362,6 @@ var init_composer = __esm({
|
|
|
17107
17362
|
}
|
|
17108
17363
|
});
|
|
17109
17364
|
|
|
17110
|
-
// cli/harness/events.ts
|
|
17111
|
-
function fixPollId(raw) {
|
|
17112
|
-
const text = String(raw || "");
|
|
17113
|
-
return FIX_POLL_MARKER.exec(text)?.[1] || LEGACY_FIX_POLL_MARKER.exec(text)?.[1] || "";
|
|
17114
|
-
}
|
|
17115
|
-
function cleanGuardText(raw) {
|
|
17116
|
-
return String(raw || "").replace(FIX_POLL_MARKER, "").replace(/\n*\s*SYNKRO FIX POLL[\s\S]*$/i, "").replace(/\n?\d{4}-\d\d-\d\dT[^\n]*\sERROR\s+codex_core::tools::router:[^\n]*/gi, "").replace(/\s*Checking command\s*$/i, "").trim();
|
|
17117
|
-
}
|
|
17118
|
-
function blockReason(raw) {
|
|
17119
|
-
const text = cleanGuardText(raw);
|
|
17120
|
-
if (!text) return "blocked by policy";
|
|
17121
|
-
const guardAt = text.lastIndexOf("Guard:");
|
|
17122
|
-
if (guardAt >= 0) return text.slice(guardAt + "Guard:".length).trim();
|
|
17123
|
-
const afterTag = text.match(/\[synkro:[^\]]*\]\s*(.+)/is);
|
|
17124
|
-
if (afterTag) return afterTag[1].trim();
|
|
17125
|
-
const afterHook = text.match(/blocked by a hook:\s*(.+)/is);
|
|
17126
|
-
if (afterHook) return afterHook[1].trim();
|
|
17127
|
-
return text;
|
|
17128
|
-
}
|
|
17129
|
-
var BLOCK_MARKER, FIX_POLL_MARKER, LEGACY_FIX_POLL_MARKER;
|
|
17130
|
-
var init_events = __esm({
|
|
17131
|
-
"cli/harness/events.ts"() {
|
|
17132
|
-
"use strict";
|
|
17133
|
-
BLOCK_MARKER = /blocked by a hook|\[synkro:/i;
|
|
17134
|
-
FIX_POLL_MARKER = /\[synkro:fix-poll\s+item_id=\\?["']?([A-Za-z0-9_-]+)\\?["']?\]/i;
|
|
17135
|
-
LEGACY_FIX_POLL_MARKER = /SYNKRO FIX POLL\s*\(item_id=([A-Za-z0-9_-]+)\)/i;
|
|
17136
|
-
}
|
|
17137
|
-
});
|
|
17138
|
-
|
|
17139
17365
|
// cli/harness/changes.ts
|
|
17140
17366
|
import { lstatSync as lstatSync2, readFileSync as readFileSync33, readlinkSync } from "fs";
|
|
17141
17367
|
import { resolve as resolve6, relative } from "path";
|
|
@@ -17419,6 +17645,7 @@ function parseCursorLine(line, streamPartial = false) {
|
|
|
17419
17645
|
blocked,
|
|
17420
17646
|
reason: rejected ? blocked ? blockReason(rawReason) : rawReason || "rejected" : "",
|
|
17421
17647
|
pollId: blocked ? fixPollId(rawReason) : "",
|
|
17648
|
+
pollIds: blocked ? fixPollIds(rawReason) : [],
|
|
17422
17649
|
exitCode: success ? Number(success.exitCode ?? 0) : null,
|
|
17423
17650
|
output: String(success?.stdout || success?.stderr || ""),
|
|
17424
17651
|
fileChanges: completedFileChanges(body)
|
|
@@ -17761,6 +17988,11 @@ var init_run = __esm({
|
|
|
17761
17988
|
// cli/harness/codex.ts
|
|
17762
17989
|
import { spawn as spawn10 } from "child_process";
|
|
17763
17990
|
import { createInterface as createInterface6 } from "readline";
|
|
17991
|
+
function codexStderrNotice(raw, initialized) {
|
|
17992
|
+
const text = String(raw || "").trim();
|
|
17993
|
+
if (!text || initialized) return "";
|
|
17994
|
+
return /fatal|panic|authentication failed|not logged in/i.test(text) ? text.split("\n")[0].slice(0, 300) : "";
|
|
17995
|
+
}
|
|
17764
17996
|
function stringify(value) {
|
|
17765
17997
|
if (typeof value === "string") return value;
|
|
17766
17998
|
try {
|
|
@@ -17843,6 +18075,7 @@ function codexEvents(message) {
|
|
|
17843
18075
|
blocked,
|
|
17844
18076
|
reason: blocked ? blockReason(raw || status) : "",
|
|
17845
18077
|
pollId: blocked ? fixPollId(raw) : "",
|
|
18078
|
+
pollIds: blocked ? fixPollIds(raw) : [],
|
|
17846
18079
|
exitCode: typeof item.exitCode === "number" ? item.exitCode : null,
|
|
17847
18080
|
output: raw,
|
|
17848
18081
|
durationMs: typeof item.durationMs === "number" ? item.durationMs : null,
|
|
@@ -17864,6 +18097,7 @@ function codexEvents(message) {
|
|
|
17864
18097
|
exitCode: null,
|
|
17865
18098
|
output: reason,
|
|
17866
18099
|
pollId: fixPollId(reason),
|
|
18100
|
+
pollIds: fixPollIds(reason),
|
|
17867
18101
|
durationMs: typeof run2.durationMs === "number" ? run2.durationMs : null
|
|
17868
18102
|
}];
|
|
17869
18103
|
}
|
|
@@ -17898,6 +18132,7 @@ var init_codex = __esm({
|
|
|
17898
18132
|
cwd;
|
|
17899
18133
|
resumeId;
|
|
17900
18134
|
child = null;
|
|
18135
|
+
initialized = false;
|
|
17901
18136
|
nextId = 1;
|
|
17902
18137
|
pending = /* @__PURE__ */ new Map();
|
|
17903
18138
|
onEvent = null;
|
|
@@ -17974,7 +18209,7 @@ var init_codex = __esm({
|
|
|
17974
18209
|
}
|
|
17975
18210
|
});
|
|
17976
18211
|
this.child.stderr.on("data", (chunk) => {
|
|
17977
|
-
const message = String(chunk || "").
|
|
18212
|
+
const message = codexStderrNotice(String(chunk || ""), this.initialized);
|
|
17978
18213
|
if (message) this.onEvent?.({ type: "notice", text: message });
|
|
17979
18214
|
});
|
|
17980
18215
|
this.child.once("exit", (code) => {
|
|
@@ -17985,6 +18220,7 @@ var init_codex = __esm({
|
|
|
17985
18220
|
});
|
|
17986
18221
|
await this.request("initialize", { clientInfo: { name: "synkro-terminal", title: "Synkro", version: "1" }, capabilities: { experimentalApi: true } });
|
|
17987
18222
|
this.notify("initialized");
|
|
18223
|
+
this.initialized = true;
|
|
17988
18224
|
try {
|
|
17989
18225
|
const event = codexAccountUsageEvent(await this.request("account/rateLimits/read", null, 4e3));
|
|
17990
18226
|
if (event) this.accountUsage = event;
|
|
@@ -18081,6 +18317,7 @@ var init_screen = __esm({
|
|
|
18081
18317
|
cwd;
|
|
18082
18318
|
write;
|
|
18083
18319
|
transcript = [];
|
|
18320
|
+
inlinePanel = [];
|
|
18084
18321
|
draft = "";
|
|
18085
18322
|
input = "";
|
|
18086
18323
|
selectionAnchor = null;
|
|
@@ -18122,6 +18359,17 @@ var init_screen = __esm({
|
|
|
18122
18359
|
this.selectionFocus = null;
|
|
18123
18360
|
this.draw();
|
|
18124
18361
|
}
|
|
18362
|
+
setInlinePanel(lines2) {
|
|
18363
|
+
this.inlinePanel = [...lines2];
|
|
18364
|
+
this.scrollOffset = 0;
|
|
18365
|
+
this.draw();
|
|
18366
|
+
}
|
|
18367
|
+
commitInlinePanel(lines2) {
|
|
18368
|
+
this.inlinePanel = [];
|
|
18369
|
+
this.transcript.push(...lines2);
|
|
18370
|
+
this.scrollOffset = 0;
|
|
18371
|
+
this.draw();
|
|
18372
|
+
}
|
|
18125
18373
|
selectionRange() {
|
|
18126
18374
|
if (this.selectionAnchor === null || this.selectionFocus === null || this.selectionAnchor === this.selectionFocus) return null;
|
|
18127
18375
|
return [Math.min(this.selectionAnchor, this.selectionFocus), Math.max(this.selectionAnchor, this.selectionFocus)];
|
|
@@ -18230,7 +18478,7 @@ var init_screen = __esm({
|
|
|
18230
18478
|
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
18479
|
const rule = S2.border + " " + "\u2500".repeat(inner) + S2.reset;
|
|
18232
18480
|
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];
|
|
18481
|
+
const composer = this.inlinePanel.length ? [rule] : [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
18482
|
const usedPercent = this.context ? Math.min(100, Math.round(this.used / this.context * 100)) : null;
|
|
18235
18483
|
const contextUsage = usedPercent === null ? "\u2014 context" : usedPercent + "% context";
|
|
18236
18484
|
const resetDate = this.weeklyResetsAt == null ? "" : " \xB7 " + resetDateFormat.format(new Date(this.weeklyResetsAt * 1e3));
|
|
@@ -18241,7 +18489,7 @@ var init_screen = __esm({
|
|
|
18241
18489
|
const right = badge + S2.secondary + " \xB7 " + provider + (this.model ? " " + cleanOutput(this.model) : "") + " \xB7 \u29C9 powered by synkro " + S2.reset + S2.canvas;
|
|
18242
18490
|
const footer = left + " ".repeat(Math.max(1, width - visible(left) - visible(right))) + right;
|
|
18243
18491
|
const viewport = Math.max(3, height - composer.length - 2);
|
|
18244
|
-
const fullContent = [...this.transcript, ...draft, status];
|
|
18492
|
+
const fullContent = [...this.transcript, ...draft, ...this.inlinePanel, status];
|
|
18245
18493
|
const maxScroll = Math.max(0, fullContent.length - viewport);
|
|
18246
18494
|
const effectiveOffset = Math.min(this.scrollOffset, maxScroll);
|
|
18247
18495
|
const end = fullContent.length - effectiveOffset;
|
|
@@ -18321,24 +18569,31 @@ async function loadFixPoll(itemId, fetchImpl = fetch) {
|
|
|
18321
18569
|
const body = await response.json();
|
|
18322
18570
|
const candidates = Array.isArray(body.candidates) ? body.candidates.filter((candidate) => typeof candidate === "string").slice(0, 8) : [];
|
|
18323
18571
|
if (!candidates.length || body.status !== "pending") return null;
|
|
18572
|
+
const recommendedIdx = Number.isInteger(body.recommended_idx) && body.recommended_idx >= 0 && body.recommended_idx < candidates.length ? body.recommended_idx : -1;
|
|
18324
18573
|
return {
|
|
18325
18574
|
itemId,
|
|
18326
18575
|
filePath: String(body.file_path || ""),
|
|
18327
18576
|
ruleId: String(body.rule_id || ""),
|
|
18328
|
-
candidates
|
|
18577
|
+
candidates,
|
|
18578
|
+
recommendedIdx
|
|
18329
18579
|
};
|
|
18330
18580
|
}
|
|
18331
|
-
async function recordFixPoll(itemId,
|
|
18581
|
+
async function recordFixPoll(itemId, selection, fetchImpl = fetch) {
|
|
18332
18582
|
const response = await fetchImpl(pollBaseUrl() + "/record", {
|
|
18333
18583
|
method: "POST",
|
|
18334
18584
|
headers: { "content-type": "application/json" },
|
|
18335
|
-
body: JSON.stringify({
|
|
18585
|
+
body: JSON.stringify({
|
|
18586
|
+
item_id: itemId,
|
|
18587
|
+
chosen_idx: selection.chosenIdx,
|
|
18588
|
+
custom_response: selection.customResponse || void 0
|
|
18589
|
+
})
|
|
18336
18590
|
});
|
|
18337
18591
|
return response.ok;
|
|
18338
18592
|
}
|
|
18339
|
-
function fixPollLines(poll, selected, width = 100) {
|
|
18593
|
+
function fixPollLines(poll, selected, width = 100, customResponse = "", editingCustom = false) {
|
|
18340
18594
|
const max = Math.max(32, width - 10);
|
|
18341
|
-
const choices = [...poll.candidates,
|
|
18595
|
+
const choices = [...poll.candidates, CUSTOM_CHOICE, NONE_CHOICE];
|
|
18596
|
+
const customIndex = poll.candidates.length;
|
|
18342
18597
|
const scope = [poll.ruleId, poll.filePath].filter(Boolean).join(" \xB7 ");
|
|
18343
18598
|
const lines2 = [
|
|
18344
18599
|
"",
|
|
@@ -18348,43 +18603,105 @@ function fixPollLines(poll, selected, width = 100) {
|
|
|
18348
18603
|
];
|
|
18349
18604
|
choices.forEach((choice, index) => {
|
|
18350
18605
|
const prefix = index === selected ? S2.user + " \u276F " : S2.dim + " ";
|
|
18351
|
-
const
|
|
18606
|
+
const recommendation = index === poll.recommendedIdx ? "Recommended: " : "";
|
|
18607
|
+
const text = wrap(index + 1 + ". " + recommendation + neutralizeTerminalControls(choice), max);
|
|
18352
18608
|
lines2.push(prefix + (text[0] || "") + S2.reset);
|
|
18353
18609
|
for (const continuation of text.slice(1)) lines2.push(" " + continuation);
|
|
18610
|
+
if (index === customIndex && editingCustom) {
|
|
18611
|
+
const answer = neutralizeTerminalControls(customResponse);
|
|
18612
|
+
const answerLines = wrap("Your answer: " + answer + "\u258C", max - 2);
|
|
18613
|
+
for (const answerLine of answerLines) lines2.push(S2.user + " " + answerLine + S2.reset);
|
|
18614
|
+
}
|
|
18354
18615
|
});
|
|
18355
|
-
|
|
18616
|
+
const footer = editingCustom ? " Type your answer \xB7 Enter submit \xB7 Esc back" : " \u2191/\u2193 select \xB7 Enter confirm \xB7 " + (choices.length === 10 ? "1-9/0" : "1-" + choices.length) + " choose";
|
|
18617
|
+
lines2.push("", S2.dim + footer + S2.reset);
|
|
18618
|
+
return lines2;
|
|
18619
|
+
}
|
|
18620
|
+
function fixPollResultLines(poll, selection, recorded, width = 100) {
|
|
18621
|
+
const max = Math.max(32, width - 14);
|
|
18622
|
+
const scope = [poll.ruleId, poll.filePath].filter(Boolean).join(" \xB7 ");
|
|
18623
|
+
const selected = selection.customResponse ? "Custom answer: " + selection.customResponse : selection.chosenIdx >= 0 && selection.chosenIdx < poll.candidates.length ? "Selected: " + poll.candidates[selection.chosenIdx] : "Selected: " + NONE_CHOICE;
|
|
18624
|
+
const lines2 = [
|
|
18625
|
+
"",
|
|
18626
|
+
(recorded ? S2.ok : S2.blocked) + S2.bold + " " + (recorded ? "Synkro decision recorded" : "Synkro decision was not recorded") + S2.reset,
|
|
18627
|
+
...scope ? [S2.dim + " " + neutralizeTerminalControls(scope) + S2.reset] : []
|
|
18628
|
+
];
|
|
18629
|
+
const answer = wrap(neutralizeTerminalControls(selected), max);
|
|
18630
|
+
answer.forEach((line, index) => lines2.push((index ? " " : S2.user + " \u23BF ") + line + S2.reset));
|
|
18356
18631
|
return lines2;
|
|
18357
18632
|
}
|
|
18358
18633
|
async function pickFixPoll(poll, io = {}) {
|
|
18359
18634
|
const input = io.input || process.stdin;
|
|
18360
18635
|
const output = io.output || process.stdout;
|
|
18361
|
-
if (!input.isTTY || !output.isTTY) return -1;
|
|
18362
|
-
|
|
18636
|
+
if (!input.isTTY || !output.isTTY) return { chosenIdx: -1, customResponse: "" };
|
|
18637
|
+
const customIndex = poll.candidates.length;
|
|
18638
|
+
const noneIndex = customIndex + 1;
|
|
18639
|
+
const choices = poll.candidates.length + 2;
|
|
18640
|
+
let selected = poll.recommendedIdx >= 0 && poll.recommendedIdx < poll.candidates.length ? poll.recommendedIdx : 0;
|
|
18641
|
+
let customResponse = "";
|
|
18642
|
+
let editingCustom = false;
|
|
18363
18643
|
let rendered = 0;
|
|
18364
18644
|
const draw = () => {
|
|
18365
|
-
|
|
18366
|
-
|
|
18367
|
-
|
|
18368
|
-
|
|
18645
|
+
const lines2 = fixPollLines(
|
|
18646
|
+
poll,
|
|
18647
|
+
selected,
|
|
18648
|
+
Number(output.columns || 100),
|
|
18649
|
+
customResponse,
|
|
18650
|
+
editingCustom
|
|
18651
|
+
);
|
|
18652
|
+
if (io.render) io.render(lines2);
|
|
18653
|
+
else {
|
|
18654
|
+
if (rendered) output.write("\x1B[" + rendered + "A\x1B[J");
|
|
18655
|
+
output.write(lines2.join("\n") + "\n");
|
|
18656
|
+
rendered = lines2.length;
|
|
18657
|
+
}
|
|
18369
18658
|
};
|
|
18370
|
-
|
|
18371
|
-
const choices = poll.candidates.length + 1;
|
|
18659
|
+
return new Promise((resolve9) => {
|
|
18372
18660
|
const wasRaw = Boolean(input.isRaw);
|
|
18373
|
-
const done = (
|
|
18661
|
+
const done = (selection) => {
|
|
18374
18662
|
input.off("data", onData);
|
|
18375
18663
|
if (input.setRawMode) input.setRawMode(wasRaw);
|
|
18376
|
-
resolve9(
|
|
18664
|
+
resolve9(selection);
|
|
18665
|
+
};
|
|
18666
|
+
const confirmSelection = () => {
|
|
18667
|
+
if (selected < customIndex) return done({ chosenIdx: selected, customResponse: "" });
|
|
18668
|
+
if (selected === noneIndex) return done({ chosenIdx: -1, customResponse: "" });
|
|
18669
|
+
editingCustom = true;
|
|
18670
|
+
draw();
|
|
18377
18671
|
};
|
|
18378
18672
|
const onData = (chunk) => {
|
|
18379
18673
|
const key2 = chunk.toString("utf8");
|
|
18380
|
-
if (
|
|
18381
|
-
|
|
18382
|
-
|
|
18674
|
+
if (editingCustom) {
|
|
18675
|
+
if (key2 === "") return done({ chosenIdx: -1, customResponse: "" });
|
|
18676
|
+
if (key2 === "\x1B") {
|
|
18677
|
+
editingCustom = false;
|
|
18678
|
+
draw();
|
|
18679
|
+
return;
|
|
18680
|
+
}
|
|
18681
|
+
const typed = key2.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
18682
|
+
for (const char of typed) {
|
|
18683
|
+
if (char === "\r" || char === "\n") {
|
|
18684
|
+
const answer = customResponse.trim();
|
|
18685
|
+
if (answer) return done({ chosenIdx: -2, customResponse: answer });
|
|
18686
|
+
} else if (char === "\x7F" || char === "\b") customResponse = customResponse.slice(0, -1);
|
|
18687
|
+
else if (char === "") customResponse = "";
|
|
18688
|
+
else if (char === "") customResponse = customResponse.replace(/\s*\S+\s*$/, "");
|
|
18689
|
+
else if (char >= " " && customResponse.length < MAX_CUSTOM_RESPONSE) customResponse += char;
|
|
18690
|
+
}
|
|
18691
|
+
draw();
|
|
18692
|
+
return;
|
|
18693
|
+
}
|
|
18694
|
+
if (key2 === "") return done({ chosenIdx: -1, customResponse: "" });
|
|
18695
|
+
if (key2 === "\x1B" || key2.toLowerCase() === "n") return done({ chosenIdx: -1, customResponse: "" });
|
|
18696
|
+
if (key2 === "\r" || key2 === "\n") return confirmSelection();
|
|
18383
18697
|
if (key2 === "\x1B[A" || key2 === "k") selected = (selected - 1 + choices) % choices;
|
|
18384
18698
|
else if (key2 === "\x1B[B" || key2 === "j") selected = (selected + 1) % choices;
|
|
18385
|
-
else if (/^[
|
|
18386
|
-
const index = Number(key2) - 1;
|
|
18387
|
-
if (index < choices)
|
|
18699
|
+
else if (/^[0-9]$/.test(key2)) {
|
|
18700
|
+
const index = key2 === "0" ? 9 : Number(key2) - 1;
|
|
18701
|
+
if (index < choices) {
|
|
18702
|
+
selected = index;
|
|
18703
|
+
return confirmSelection();
|
|
18704
|
+
}
|
|
18388
18705
|
} else return;
|
|
18389
18706
|
draw();
|
|
18390
18707
|
};
|
|
@@ -18393,25 +18710,30 @@ async function pickFixPoll(poll, io = {}) {
|
|
|
18393
18710
|
input.on("data", onData);
|
|
18394
18711
|
draw();
|
|
18395
18712
|
});
|
|
18396
|
-
return choice;
|
|
18397
18713
|
}
|
|
18398
18714
|
async function resolveFixPolls(events, io = {}) {
|
|
18399
|
-
const ids = Array.from(new Set(events.filter((event) => event.type === "tool-end").
|
|
18715
|
+
const ids = Array.from(new Set(events.filter((event) => event.type === "tool-end").flatMap((event) => event.pollIds?.length ? event.pollIds : [event.pollId || ""]).filter(Boolean)));
|
|
18400
18716
|
const recorded = [];
|
|
18401
18717
|
for (const itemId of ids) {
|
|
18402
18718
|
const poll = await loadFixPoll(itemId, io.fetchImpl).catch(() => null);
|
|
18403
18719
|
if (!poll) continue;
|
|
18404
|
-
const
|
|
18405
|
-
const ok = await recordFixPoll(itemId,
|
|
18406
|
-
|
|
18407
|
-
|
|
18720
|
+
const selection = await pickFixPoll(poll, io);
|
|
18721
|
+
const ok = await recordFixPoll(itemId, selection, io.fetchImpl).catch(() => false);
|
|
18722
|
+
const result = fixPollResultLines(poll, selection, ok, Number((io.output || process.stdout).columns || 100));
|
|
18723
|
+
if (io.commit) io.commit(result);
|
|
18724
|
+
else (io.output || process.stdout).write(result.join("\n") + "\n");
|
|
18725
|
+
if (ok) recorded.push(selection.chosenIdx);
|
|
18408
18726
|
}
|
|
18409
18727
|
return recorded;
|
|
18410
18728
|
}
|
|
18729
|
+
var CUSTOM_CHOICE, NONE_CHOICE, MAX_CUSTOM_RESPONSE;
|
|
18411
18730
|
var init_fixPoll = __esm({
|
|
18412
18731
|
"cli/harness/fixPoll.ts"() {
|
|
18413
18732
|
"use strict";
|
|
18414
18733
|
init_render2();
|
|
18734
|
+
CUSTOM_CHOICE = "Write my own answer";
|
|
18735
|
+
NONE_CHOICE = "None of the above";
|
|
18736
|
+
MAX_CUSTOM_RESPONSE = 4e3;
|
|
18415
18737
|
}
|
|
18416
18738
|
});
|
|
18417
18739
|
|
|
@@ -18420,6 +18742,12 @@ function deleteSelectedText(value, range) {
|
|
|
18420
18742
|
if (!range) return value.slice(0, -1);
|
|
18421
18743
|
return value.slice(0, range[0]) + value.slice(range[1]);
|
|
18422
18744
|
}
|
|
18745
|
+
function idlePromptControl(data, hasSelection) {
|
|
18746
|
+
if (data === "\x1B") return hasSelection ? "delete-selection" : "ignore";
|
|
18747
|
+
if (data.includes("")) return "clear";
|
|
18748
|
+
if (data.includes("")) return "exit";
|
|
18749
|
+
return null;
|
|
18750
|
+
}
|
|
18423
18751
|
function handleScrollInput(screen, data) {
|
|
18424
18752
|
let handled = false;
|
|
18425
18753
|
const mousePattern = /\x1b\[<(\d+);\d+;\d+[Mm]/g;
|
|
@@ -18500,19 +18828,24 @@ function readPrompt(screen) {
|
|
|
18500
18828
|
screen.setInput(value);
|
|
18501
18829
|
return;
|
|
18502
18830
|
}
|
|
18503
|
-
|
|
18504
|
-
|
|
18505
|
-
|
|
18506
|
-
|
|
18507
|
-
} else finish(null);
|
|
18831
|
+
const control = idlePromptControl(data, Boolean(selection));
|
|
18832
|
+
if (control === "delete-selection") {
|
|
18833
|
+
value = deleteSelectedText(value, selection);
|
|
18834
|
+
screen.setInput(value);
|
|
18508
18835
|
return;
|
|
18509
18836
|
}
|
|
18837
|
+
if (control === "clear") {
|
|
18838
|
+
value = "";
|
|
18839
|
+
screen.setInput(value);
|
|
18840
|
+
return;
|
|
18841
|
+
}
|
|
18842
|
+
if (control === "exit") {
|
|
18843
|
+
finish(null);
|
|
18844
|
+
return;
|
|
18845
|
+
}
|
|
18846
|
+
if (control === "ignore") return;
|
|
18510
18847
|
if (data.startsWith("\x1B")) return;
|
|
18511
18848
|
for (const char of data) {
|
|
18512
|
-
if (char === "" || char === "") {
|
|
18513
|
-
finish(null);
|
|
18514
|
-
return;
|
|
18515
|
-
}
|
|
18516
18849
|
if (char === "\r" || char === "\n") {
|
|
18517
18850
|
finish(value);
|
|
18518
18851
|
return;
|
|
@@ -18550,12 +18883,11 @@ async function cancellable(screen, run2) {
|
|
|
18550
18883
|
}
|
|
18551
18884
|
async function resolveScreenFixPolls(screen, events) {
|
|
18552
18885
|
if (!events.some((event) => event.type === "tool-end" && Boolean(event.pollId))) return;
|
|
18553
|
-
screen.
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
}
|
|
18886
|
+
screen.setStatus("Decision required");
|
|
18887
|
+
await resolveFixPolls(events, {
|
|
18888
|
+
render: (lines2) => screen.setInlinePanel(lines2),
|
|
18889
|
+
commit: (lines2) => screen.commitInlinePanel(lines2)
|
|
18890
|
+
});
|
|
18559
18891
|
}
|
|
18560
18892
|
async function runSynkroSession(harness, cwd, prompt) {
|
|
18561
18893
|
if (prompt) return runOnce(harness, cwd, prompt);
|
|
@@ -19930,7 +20262,7 @@ function pseudonymousHostnameHash(installationId, host) {
|
|
|
19930
20262
|
}
|
|
19931
20263
|
function cliVersion() {
|
|
19932
20264
|
try {
|
|
19933
|
-
return "1.10.
|
|
20265
|
+
return "1.10.11";
|
|
19934
20266
|
} catch {
|
|
19935
20267
|
return "0.0.0";
|
|
19936
20268
|
}
|
|
@@ -20700,7 +21032,7 @@ var subArgs = args.slice(1);
|
|
|
20700
21032
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
20701
21033
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "inventory-sync", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
20702
21034
|
function printVersion() {
|
|
20703
|
-
console.log("1.10.
|
|
21035
|
+
console.log("1.10.11");
|
|
20704
21036
|
}
|
|
20705
21037
|
function printHelp2() {
|
|
20706
21038
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|