@synkro-sh/cli 1.7.92 → 1.7.93
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 +377 -817
- package/dist/bootstrap.js.map +1 -1
- package/package.json +16 -15
package/dist/bootstrap.js
CHANGED
|
@@ -147,7 +147,7 @@ function getIdentity() {
|
|
|
147
147
|
if (cached2) return cached2;
|
|
148
148
|
let cliVersion = "0.0.0";
|
|
149
149
|
try {
|
|
150
|
-
cliVersion = "1.7.
|
|
150
|
+
cliVersion = "1.7.93";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -1735,18 +1735,6 @@ function installCCHooks(settingsPath, config) {
|
|
|
1735
1735
|
],
|
|
1736
1736
|
[SYNKRO_MARKER]: true
|
|
1737
1737
|
});
|
|
1738
|
-
if (config.promptRouteScriptPath) {
|
|
1739
|
-
settings.hooks.UserPromptSubmit.push({
|
|
1740
|
-
hooks: [
|
|
1741
|
-
{
|
|
1742
|
-
type: "command",
|
|
1743
|
-
command: config.promptRouteScriptPath,
|
|
1744
|
-
timeout: 5
|
|
1745
|
-
}
|
|
1746
|
-
],
|
|
1747
|
-
[SYNKRO_MARKER]: true
|
|
1748
|
-
});
|
|
1749
|
-
}
|
|
1750
1738
|
settings.hooks.Stop = settings.hooks.Stop ?? [];
|
|
1751
1739
|
removeSynkroEntries(settings.hooks, "Stop");
|
|
1752
1740
|
settings.hooks.Stop.push({
|
|
@@ -2112,7 +2100,6 @@ function installCodexHooks(hooksJsonPath, config) {
|
|
|
2112
2100
|
if (config.taskActivateIntentScriptPath) push(h, "PermissionRequest", [cmd(config.taskActivateIntentScriptPath, 5)], M_ACTIVATE);
|
|
2113
2101
|
push(h, "PostToolUse", [cmd(config.bashFollowupScriptPath, 10)], M_BASH);
|
|
2114
2102
|
push(h, "UserPromptSubmit", [cmd(config.userPromptSubmitScriptPath, 5)]);
|
|
2115
|
-
if (config.promptRouteScriptPath) push(h, "UserPromptSubmit", [cmd(config.promptRouteScriptPath, 5)]);
|
|
2116
2103
|
push(h, "SessionStart", [cmd(config.sessionStartScriptPath, 5)]);
|
|
2117
2104
|
push(h, "SubagentStart", [cmd(config.subagentStartScriptPath, 5)]);
|
|
2118
2105
|
if (!config.skipTranscriptSync) push(h, "SubagentStop", [cmd(config.subagentStopScriptPath, 5)]);
|
|
@@ -2141,13 +2128,14 @@ function uninstallCodexHooks(hooksJsonPath) {
|
|
|
2141
2128
|
writeHooksFileAtomic2(hooksJsonPath, file);
|
|
2142
2129
|
return true;
|
|
2143
2130
|
}
|
|
2144
|
-
var SYNKRO_MARKER3, M_BASH, CODEX_EDIT_MATCHER, M_EDIT, M_AGENT, M_MCP, M_ACTIVATE, ALL_EVENTS2;
|
|
2131
|
+
var SYNKRO_MARKER3, CODEX_BASH_MATCHER, M_BASH, CODEX_EDIT_MATCHER, M_EDIT, M_AGENT, M_MCP, M_ACTIVATE, ALL_EVENTS2;
|
|
2145
2132
|
var init_codexHookConfig = __esm({
|
|
2146
2133
|
"cli/installer/codexHookConfig.ts"() {
|
|
2147
2134
|
"use strict";
|
|
2148
2135
|
init_platform();
|
|
2149
2136
|
SYNKRO_MARKER3 = "__synkro_managed__";
|
|
2150
|
-
|
|
2137
|
+
CODEX_BASH_MATCHER = "^(?:Bash|exec_command|functions[._]exec_command)$";
|
|
2138
|
+
M_BASH = CODEX_BASH_MATCHER;
|
|
2151
2139
|
CODEX_EDIT_MATCHER = "^(?:apply_patch|ApplyPatch|Edit|Write|functions[._]apply_patch)$";
|
|
2152
2140
|
M_EDIT = CODEX_EDIT_MATCHER;
|
|
2153
2141
|
M_AGENT = "Agent";
|
|
@@ -2157,6 +2145,126 @@ var init_codexHookConfig = __esm({
|
|
|
2157
2145
|
}
|
|
2158
2146
|
});
|
|
2159
2147
|
|
|
2148
|
+
// cli/installer/codexHookTrust.ts
|
|
2149
|
+
import { spawn as spawn2 } from "child_process";
|
|
2150
|
+
function parseCodexHookTrustOutput(stdout) {
|
|
2151
|
+
for (const line of stdout.split("\n")) {
|
|
2152
|
+
let message;
|
|
2153
|
+
try {
|
|
2154
|
+
message = JSON.parse(line);
|
|
2155
|
+
} catch {
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2158
|
+
const data = message?.result?.data;
|
|
2159
|
+
if (!Array.isArray(data)) continue;
|
|
2160
|
+
const hooks = data.flatMap((entry) => Array.isArray(entry?.hooks) ? entry.hooks : []).filter((hook) => typeof hook.command === "string" && isSynkroHookCommand(hook.command));
|
|
2161
|
+
if (!hooks.length) return null;
|
|
2162
|
+
return {
|
|
2163
|
+
total: hooks.length,
|
|
2164
|
+
trusted: hooks.filter((hook) => hook.trustStatus === "trusted" || hook.trustStatus === "managed").length,
|
|
2165
|
+
needsReview: hooks.filter((hook) => hook.trustStatus === "modified" || hook.trustStatus === "untrusted").length,
|
|
2166
|
+
disabled: hooks.filter((hook) => hook.enabled === false).length
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
return null;
|
|
2170
|
+
}
|
|
2171
|
+
function inspectCodexHookTrust(codexBinary = "codex", cwd = process.cwd()) {
|
|
2172
|
+
return new Promise((resolve6) => {
|
|
2173
|
+
let settled = false;
|
|
2174
|
+
let stdout = "";
|
|
2175
|
+
let pending = "";
|
|
2176
|
+
let child;
|
|
2177
|
+
const finish = (summary) => {
|
|
2178
|
+
if (settled) return;
|
|
2179
|
+
settled = true;
|
|
2180
|
+
clearTimeout(timer);
|
|
2181
|
+
try {
|
|
2182
|
+
child.stdin.end();
|
|
2183
|
+
} catch {
|
|
2184
|
+
}
|
|
2185
|
+
try {
|
|
2186
|
+
child.kill();
|
|
2187
|
+
} catch {
|
|
2188
|
+
}
|
|
2189
|
+
resolve6(summary);
|
|
2190
|
+
};
|
|
2191
|
+
const timer = setTimeout(() => finish(null), 1e4);
|
|
2192
|
+
try {
|
|
2193
|
+
child = spawn2(codexBinary, ["app-server", "--stdio"], {
|
|
2194
|
+
cwd,
|
|
2195
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
2196
|
+
windowsHide: true
|
|
2197
|
+
});
|
|
2198
|
+
child.once("error", () => finish(null));
|
|
2199
|
+
child.once("exit", () => finish(parseCodexHookTrustOutput(stdout)));
|
|
2200
|
+
child.stdout.setEncoding("utf8");
|
|
2201
|
+
child.stdout.on("data", (chunk) => {
|
|
2202
|
+
stdout += chunk;
|
|
2203
|
+
pending += chunk;
|
|
2204
|
+
const lines = pending.split("\n");
|
|
2205
|
+
pending = lines.pop() || "";
|
|
2206
|
+
for (const line of lines) {
|
|
2207
|
+
let message;
|
|
2208
|
+
try {
|
|
2209
|
+
message = JSON.parse(line);
|
|
2210
|
+
} catch {
|
|
2211
|
+
continue;
|
|
2212
|
+
}
|
|
2213
|
+
if (message?.id === 1 && message?.result) {
|
|
2214
|
+
child.stdin.write(JSON.stringify({
|
|
2215
|
+
id: 2,
|
|
2216
|
+
method: "hooks/list",
|
|
2217
|
+
params: { cwds: [cwd] }
|
|
2218
|
+
}) + "\n");
|
|
2219
|
+
} else if (message?.id === 2) {
|
|
2220
|
+
finish(parseCodexHookTrustOutput(line));
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
child.stdin.write(JSON.stringify({
|
|
2225
|
+
id: 1,
|
|
2226
|
+
method: "initialize",
|
|
2227
|
+
params: {
|
|
2228
|
+
clientInfo: { name: "synkro-cli", version: "1" },
|
|
2229
|
+
capabilities: { experimentalApi: true }
|
|
2230
|
+
}
|
|
2231
|
+
}) + "\n");
|
|
2232
|
+
} catch {
|
|
2233
|
+
finish(null);
|
|
2234
|
+
}
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
function codexHookTrustLines(summary) {
|
|
2238
|
+
if (!summary) {
|
|
2239
|
+
return [
|
|
2240
|
+
" \u26A0 Codex hook trust could not be verified.",
|
|
2241
|
+
" Restart Codex, run /hooks, and review the Synkro hooks before relying on enforcement."
|
|
2242
|
+
];
|
|
2243
|
+
}
|
|
2244
|
+
if (summary.needsReview === 0 && summary.disabled === 0 && summary.trusted === summary.total) {
|
|
2245
|
+
return [` \u2713 Codex hook trust confirmed (${summary.trusted}/${summary.total} active)`];
|
|
2246
|
+
}
|
|
2247
|
+
const issues = [
|
|
2248
|
+
summary.needsReview > 0 ? `${summary.needsReview} need review` : "",
|
|
2249
|
+
summary.disabled > 0 ? `${summary.disabled} disabled` : ""
|
|
2250
|
+
].filter(Boolean).join(", ");
|
|
2251
|
+
return [
|
|
2252
|
+
` \u26A0 Codex security enforcement is not active (${issues}).`,
|
|
2253
|
+
" Restart Codex, run /hooks, and trust only the Synkro hooks before relying on enforcement."
|
|
2254
|
+
];
|
|
2255
|
+
}
|
|
2256
|
+
async function reportCodexHookTrust(codexBinary, cwd) {
|
|
2257
|
+
const summary = await inspectCodexHookTrust(codexBinary || "codex", cwd);
|
|
2258
|
+
for (const line of codexHookTrustLines(summary)) console.log(line);
|
|
2259
|
+
return summary;
|
|
2260
|
+
}
|
|
2261
|
+
var init_codexHookTrust = __esm({
|
|
2262
|
+
"cli/installer/codexHookTrust.ts"() {
|
|
2263
|
+
"use strict";
|
|
2264
|
+
init_platform();
|
|
2265
|
+
}
|
|
2266
|
+
});
|
|
2267
|
+
|
|
2160
2268
|
// cli/installer/mcpConfig.ts
|
|
2161
2269
|
import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync7 } from "fs";
|
|
2162
2270
|
import { homedir as homedir12 } from "os";
|
|
@@ -2982,6 +3090,26 @@ export function normalizeCodexApplyPatch(payload: any): void {
|
|
|
2982
3090
|
} catch { /* leave payload untouched on any parse error */ }
|
|
2983
3091
|
}
|
|
2984
3092
|
|
|
3093
|
+
/**
|
|
3094
|
+
* Normalize Codex app/local-function command aliases into the Bash hook shape
|
|
3095
|
+
* consumed by scanRouter. Codex CLI already emits Bash + command; bridges may
|
|
3096
|
+
* retain exec_command (optionally namespaced) + cmd.
|
|
3097
|
+
*/
|
|
3098
|
+
export function normalizeCodexExecCommand(payload: any): void {
|
|
3099
|
+
const toolName = String(payload?.tool_name || '');
|
|
3100
|
+
if (!/^(?:exec_command|functions[._]exec_command)$/i.test(toolName)) return;
|
|
3101
|
+
const toolInput = payload?.tool_input && typeof payload.tool_input === 'object'
|
|
3102
|
+
? payload.tool_input
|
|
3103
|
+
: {};
|
|
3104
|
+
payload.tool_name = 'Bash';
|
|
3105
|
+
payload.tool_input = {
|
|
3106
|
+
...toolInput,
|
|
3107
|
+
command: typeof toolInput.command === 'string'
|
|
3108
|
+
? toolInput.command
|
|
3109
|
+
: String(toolInput.cmd || ''),
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
3112
|
+
|
|
2985
3113
|
type RecoveredCodexEdit = { payload: Record<string, any>; baseContent?: string };
|
|
2986
3114
|
|
|
2987
3115
|
function completedCodexToolRecord(entry: any): any {
|
|
@@ -3295,9 +3423,12 @@ export async function runStub(surface: string, opts: StubOpts = {}): Promise<voi
|
|
|
3295
3423
|
const input = await readStdin();
|
|
3296
3424
|
if (!input.trim()) { out(failOpen(harness)); return; }
|
|
3297
3425
|
const payload = JSON.parse(input);
|
|
3298
|
-
// Codex: rewrite
|
|
3299
|
-
// surface reads
|
|
3300
|
-
if (harness === 'codex')
|
|
3426
|
+
// Codex: rewrite bridged tool aliases into the shared CC shapes before any
|
|
3427
|
+
// surface reads them. No-op for CC/Cursor and already-canonical tools.
|
|
3428
|
+
if (harness === 'codex') {
|
|
3429
|
+
normalizeCodexApplyPatch(payload);
|
|
3430
|
+
normalizeCodexExecCommand(payload);
|
|
3431
|
+
}
|
|
3301
3432
|
if (harness === 'codex' && opts.subagent) {
|
|
3302
3433
|
const parentSessionId = String(payload?.session_id || '');
|
|
3303
3434
|
const childSessionId = String(payload?.agent_id || '');
|
|
@@ -3763,165 +3894,8 @@ function emitStubTelemetry(
|
|
|
3763
3894
|
STUB_USER_PROMPT_SUBMIT_TS = stubHook("prompt-submit", "{ telemetry: true }");
|
|
3764
3895
|
STUB_BASH_FOLLOWUP_TS = stubHook("bash-followup", "{ telemetry: true }");
|
|
3765
3896
|
STUB_PROMPT_ROUTE_TS = `#!/usr/bin/env bun
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
import { join } from 'node:path';
|
|
3769
|
-
import { spawnSync, spawn } from 'node:child_process';
|
|
3770
|
-
|
|
3771
|
-
const HOME = homedir();
|
|
3772
|
-
const MODEL_RE = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
3773
|
-
|
|
3774
|
-
function cliBin(): string {
|
|
3775
|
-
try {
|
|
3776
|
-
const txt = readFileSync(join(HOME, '.synkro', 'config.env'), 'utf-8');
|
|
3777
|
-
for (const line of txt.split('\\n')) {
|
|
3778
|
-
const t = line.trim();
|
|
3779
|
-
if (t.startsWith('SYNKRO_CLI_BIN=')) {
|
|
3780
|
-
let v = t.slice('SYNKRO_CLI_BIN='.length).trim();
|
|
3781
|
-
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
3782
|
-
return v;
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
} catch {}
|
|
3786
|
-
return process.env.SYNKRO_CLI_BIN || '';
|
|
3787
|
-
}
|
|
3788
|
-
|
|
3789
|
-
function mcpJwt(): string {
|
|
3790
|
-
try { return readFileSync(join(HOME, '.synkro', '.mcp-jwt'), 'utf-8').trim(); } catch { return ''; }
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
// The caller's own tmux session, from the inherited $TMUX \u2014 so routing targets
|
|
3794
|
-
// THIS session, not whichever shim last wrote the shared active-session file.
|
|
3795
|
-
function currentTmuxSession(): string {
|
|
3796
|
-
if (!process.env.TMUX) return '';
|
|
3797
|
-
try {
|
|
3798
|
-
const pane = process.env.TMUX_PANE || '';
|
|
3799
|
-
const args = pane
|
|
3800
|
-
? ['display-message', '-p', '-t', pane, '#{session_name}']
|
|
3801
|
-
: ['display-message', '-p', '#{session_name}'];
|
|
3802
|
-
const r = spawnSync('tmux', args, { encoding: 'utf-8' });
|
|
3803
|
-
if (r.status === 0) return (r.stdout || '').trim();
|
|
3804
|
-
} catch {}
|
|
3805
|
-
return '';
|
|
3806
|
-
}
|
|
3807
|
-
|
|
3808
|
-
const chunks: Buffer[] = [];
|
|
3809
|
-
for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3810
|
-
|
|
3811
|
-
// Verdict: allow ({}) unless we decide to BLOCK this prompt for routing. We must
|
|
3812
|
-
// emit exactly one JSON line at the end (not up-front) so the block path can win.
|
|
3813
|
-
let blockForRouting = false;
|
|
3814
|
-
try {
|
|
3815
|
-
const payload = JSON.parse(Buffer.concat(chunks).toString('utf-8') || '{}');
|
|
3816
|
-
const sid = String(payload.session_id || payload.conversation_id || '');
|
|
3817
|
-
if (sid) {
|
|
3818
|
-
const safeSid = sid.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
3819
|
-
const tmuxSession = currentTmuxSession();
|
|
3820
|
-
const prompt = String(payload.prompt || '');
|
|
3821
|
-
|
|
3822
|
-
// Session registry: record THIS live session (heartbeat \u2014 ts refreshed every
|
|
3823
|
-
// prompt) so a router or a human can enumerate live sessions via
|
|
3824
|
-
// 'synkro sessions' and target exactly one by session_id. Wrapped + best-effort
|
|
3825
|
-
// so it never delays or breaks the prompt.
|
|
3826
|
-
try {
|
|
3827
|
-
const sessDir = join(HOME, '.synkro', 'pty', 'sessions');
|
|
3828
|
-
mkdirSync(sessDir, { recursive: true });
|
|
3829
|
-
const rec = {
|
|
3830
|
-
session_id: sid,
|
|
3831
|
-
tmux_session: tmuxSession,
|
|
3832
|
-
cwd: String(payload.cwd || ''),
|
|
3833
|
-
last_prompt: prompt.slice(0, 200),
|
|
3834
|
-
transcript_path: String(payload.transcript_path || ''),
|
|
3835
|
-
ts: Date.now(),
|
|
3836
|
-
};
|
|
3837
|
-
writeFileSync(join(sessDir, safeSid + '.json'), JSON.stringify(rec));
|
|
3838
|
-
// TTL-prune stale records (24h) \u2014 cheap, no per-record process spawn. Live
|
|
3839
|
-
// sessions refresh their ts every prompt, so only ended ones age out.
|
|
3840
|
-
const TTL = 24 * 60 * 60 * 1000;
|
|
3841
|
-
const now = Date.now();
|
|
3842
|
-
for (const f of readdirSync(sessDir)) {
|
|
3843
|
-
if (!f.endsWith('.json')) continue;
|
|
3844
|
-
try {
|
|
3845
|
-
const r = JSON.parse(readFileSync(join(sessDir, f), 'utf-8'));
|
|
3846
|
-
if (!r || typeof r.ts !== 'number' || now - r.ts > TTL) unlinkSync(join(sessDir, f));
|
|
3847
|
-
} catch { try { unlinkSync(join(sessDir, f)); } catch {} }
|
|
3848
|
-
}
|
|
3849
|
-
} catch {}
|
|
3850
|
-
|
|
3851
|
-
// \u2500\u2500 Routing: block + re-inject \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3852
|
-
// Loop-guard: a re-injected prompt carries route-guard-<sid>; clear it and
|
|
3853
|
-
// pass straight through (never route the re-injected prompt \u2014 that would loop).
|
|
3854
|
-
const guardFile = join(HOME, '.synkro', 'pty', 'route-guard-' + safeSid);
|
|
3855
|
-
let isReinjected = false;
|
|
3856
|
-
try {
|
|
3857
|
-
if (existsSync(guardFile)) { isReinjected = true; try { unlinkSync(guardFile); } catch {} }
|
|
3858
|
-
} catch {}
|
|
3859
|
-
|
|
3860
|
-
// Only route real task prompts. Skip empty input, slash-commands (/\u2026), bash
|
|
3861
|
-
// lines (!\u2026), and common conversational turns \u2014 those are never something to
|
|
3862
|
-
// pick a coding model for, and blocking them would add a needless pause. The
|
|
3863
|
-
// haiku 'keep' verdict is the backstop for anything this quick list misses.
|
|
3864
|
-
const t = prompt.trim();
|
|
3865
|
-
const norm = t.toLowerCase().replace(/[.!?,;:\\s]+$/, '').trim();
|
|
3866
|
-
const CONVERSATIONAL = new Set([
|
|
3867
|
-
'ok', 'okay', 'k', 'kk', 'yes', 'yep', 'yeah', 'ya', 'y', 'no', 'nope', 'n',
|
|
3868
|
-
'sure', 'thanks', 'thank you', 'thx', 'ty', 'cool', 'nice', 'great', 'awesome',
|
|
3869
|
-
'perfect', 'got it', 'gotcha', 'right', 'makes sense', 'sounds good', 'lgtm',
|
|
3870
|
-
'continue', 'keep going', 'go on', 'go ahead', 'proceed', 'next', 'more',
|
|
3871
|
-
'again', 'retry', 'stop', 'wait', 'hmm', 'hm', 'oh', 'i see', 'done', 'good',
|
|
3872
|
-
]);
|
|
3873
|
-
const routable = t.length > 0 && !t.startsWith('/') && !t.startsWith('!') && !CONVERSATIONAL.has(norm);
|
|
3874
|
-
|
|
3875
|
-
// Routing is opt-in via a marker file \u2014 either global ('routing-on', every
|
|
3876
|
-
// session) or per-session ('routing-on-<sid>', just this one). Per-session lets
|
|
3877
|
-
// us enable routing for one session (e.g. a test) without touching the others.
|
|
3878
|
-
let routingEnabled = false;
|
|
3879
|
-
try {
|
|
3880
|
-
routingEnabled = existsSync(join(HOME, '.synkro', 'pty', 'routing-on'))
|
|
3881
|
-
|| existsSync(join(HOME, '.synkro', 'pty', 'routing-on-' + safeSid));
|
|
3882
|
-
} catch {}
|
|
3883
|
-
|
|
3884
|
-
if (!isReinjected && routable && routingEnabled) {
|
|
3885
|
-
// ESCALATION-ONLY: classify in-place (fast local head, 1s cap). Block +
|
|
3886
|
-
// escalate to Opus ONLY for a confident opus verdict; never downgrade. Any
|
|
3887
|
-
// timeout / non-opus / error \u2192 run on the current model (no block). This
|
|
3888
|
-
// makes the classifier's weakness (under-firing opus) harmless: worst case
|
|
3889
|
-
// is a MISSED escalation = status quo, never a broken output on a weak model.
|
|
3890
|
-
let escalate = false;
|
|
3891
|
-
try {
|
|
3892
|
-
const resp = await fetch('http://127.0.0.1:' + (process.env.SYNKRO_GRADER_HOST_PORT || '18929') + '/submit', {
|
|
3893
|
-
method: 'POST',
|
|
3894
|
-
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + mcpJwt() },
|
|
3895
|
-
body: JSON.stringify({ role: 'route-classify', payload: t.slice(0, 1000), content: 'x', hedge: false }),
|
|
3896
|
-
signal: AbortSignal.timeout(1000),
|
|
3897
|
-
});
|
|
3898
|
-
if (resp.ok) {
|
|
3899
|
-
const data = await resp.json();
|
|
3900
|
-
if (/<model>\\s*claude-opus-4-8\\s*<\\/model>/i.test(String(data.result || ''))) escalate = true;
|
|
3901
|
-
}
|
|
3902
|
-
} catch {}
|
|
3903
|
-
if (escalate) {
|
|
3904
|
-
try {
|
|
3905
|
-
const bin = cliBin();
|
|
3906
|
-
if (bin) {
|
|
3907
|
-
// Orchestrator applies the pre-decided escalation (skip re-classify).
|
|
3908
|
-
const child = spawn('node', [bin, 'route-and-resubmit', sid, t.slice(0, 1000), tmuxSession || '', 'claude-opus-4-8'], { detached: true, stdio: 'ignore' });
|
|
3909
|
-
child.unref();
|
|
3910
|
-
blockForRouting = true; // hold this prompt; the orchestrator re-submits it on Opus
|
|
3911
|
-
}
|
|
3912
|
-
} catch {}
|
|
3913
|
-
}
|
|
3914
|
-
}
|
|
3915
|
-
}
|
|
3916
|
-
} catch {}
|
|
3917
|
-
|
|
3918
|
-
// Emit the single verdict line. Block (with a visible reason) when routing is
|
|
3919
|
-
// taking over the prompt; otherwise allow. Fail-open: any throw above \u2192 allow.
|
|
3920
|
-
if (blockForRouting) {
|
|
3921
|
-
process.stdout.write(JSON.stringify({ decision: 'block', reason: 'Synkro is routing this to the best model and will resubmit it automatically\u2026' }) + '\\n');
|
|
3922
|
-
} else {
|
|
3923
|
-
process.stdout.write('{}\\n');
|
|
3924
|
-
}
|
|
3897
|
+
for await (const _chunk of process.stdin) {}
|
|
3898
|
+
process.stdout.write('{}\\n');
|
|
3925
3899
|
`;
|
|
3926
3900
|
STUB_TASK_ACTIVATE_INTENT_TS = `#!/usr/bin/env bun
|
|
3927
3901
|
import { readFileSync } from 'node:fs';
|
|
@@ -4560,7 +4534,7 @@ __export(claudeDesktopTap_exports, {
|
|
|
4560
4534
|
claudeDesktopInstalled: () => claudeDesktopInstalled,
|
|
4561
4535
|
runClaudeDesktopTap: () => runClaudeDesktopTap
|
|
4562
4536
|
});
|
|
4563
|
-
import { spawn as
|
|
4537
|
+
import { spawn as spawn3 } from "child_process";
|
|
4564
4538
|
import { writeFileSync as writeFileSync12, mkdtempSync, mkdirSync as mkdirSync10, readFileSync as readFileSync15, existsSync as existsSync17 } from "fs";
|
|
4565
4539
|
import { join as join13 } from "path";
|
|
4566
4540
|
import { homedir as homedir15 } from "os";
|
|
@@ -4709,7 +4683,7 @@ async function runClaudeDesktopTap(opts = {}) {
|
|
|
4709
4683
|
const runnerPath = join13(sessionDir, "run.sh");
|
|
4710
4684
|
writeFileSync12(runnerPath, buildRunner(sessionDir), { mode: 493 });
|
|
4711
4685
|
await new Promise((resolve6) => {
|
|
4712
|
-
const child =
|
|
4686
|
+
const child = spawn3("bash", [runnerPath], {
|
|
4713
4687
|
stdio: "inherit",
|
|
4714
4688
|
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" : "" }
|
|
4715
4689
|
});
|
|
@@ -6093,6 +6067,7 @@ __export(dockerInstall_exports, {
|
|
|
6093
6067
|
dockerUpdate: () => dockerUpdate,
|
|
6094
6068
|
ensurePgliteProxyCredentials: () => ensurePgliteProxyCredentials,
|
|
6095
6069
|
imageTag: () => imageTag,
|
|
6070
|
+
isContainerActiveState: () => isContainerActiveState,
|
|
6096
6071
|
normalizeProvider: () => normalizeProvider,
|
|
6097
6072
|
poolLabel: () => poolLabel,
|
|
6098
6073
|
readContainerConfig: () => readContainerConfig,
|
|
@@ -6126,6 +6101,9 @@ function resolveContainerName(raw = process.env.SYNKRO_CONTAINER_NAME) {
|
|
|
6126
6101
|
}
|
|
6127
6102
|
return value;
|
|
6128
6103
|
}
|
|
6104
|
+
function isContainerActiveState(status) {
|
|
6105
|
+
return status === "running" || status === "restarting" || status === "paused";
|
|
6106
|
+
}
|
|
6129
6107
|
function createPgliteScramVerifier(password, salt = randomBytes2(16)) {
|
|
6130
6108
|
const iterations = 4096;
|
|
6131
6109
|
const saltedPassword = pbkdf2Sync(password, salt, iterations, 32, "sha256");
|
|
@@ -6134,27 +6112,29 @@ function createPgliteScramVerifier(password, salt = randomBytes2(16)) {
|
|
|
6134
6112
|
const serverKey = createHmac("sha256", saltedPassword).update("Server Key").digest();
|
|
6135
6113
|
return `SCRAM-SHA-256$${iterations}:${salt.toString("base64")}$${storedKey.toString("base64")}:${serverKey.toString("base64")}`;
|
|
6136
6114
|
}
|
|
6137
|
-
function ensurePgliteProxyCredentials() {
|
|
6138
|
-
const
|
|
6139
|
-
const
|
|
6115
|
+
function ensurePgliteProxyCredentials(paths = {}) {
|
|
6116
|
+
const passwordPath = paths.passwordPath ?? PGLITE_PASSWORD_PATH;
|
|
6117
|
+
const userlistPath = paths.userlistPath ?? PGLITE_USERLIST_PATH;
|
|
6118
|
+
const hasPassword = existsSync19(passwordPath) && readFileSync17(passwordPath, "utf-8").trim().length > 0;
|
|
6119
|
+
const hasUserlist = existsSync19(userlistPath) && readFileSync17(userlistPath, "utf-8").trim().length > 0;
|
|
6140
6120
|
if (hasPassword && hasUserlist) {
|
|
6141
|
-
chmodSync3(
|
|
6142
|
-
chmodSync3(
|
|
6121
|
+
chmodSync3(passwordPath, 384);
|
|
6122
|
+
chmodSync3(userlistPath, 384);
|
|
6143
6123
|
return;
|
|
6144
6124
|
}
|
|
6145
6125
|
const password = randomBytes2(24).toString("base64url");
|
|
6146
6126
|
const verifier = createPgliteScramVerifier(password);
|
|
6147
6127
|
const suffix = `${process.pid}.${Date.now()}.tmp`;
|
|
6148
|
-
const passwordTmp = `${
|
|
6149
|
-
const userlistTmp = `${
|
|
6128
|
+
const passwordTmp = `${passwordPath}.${suffix}`;
|
|
6129
|
+
const userlistTmp = `${userlistPath}.${suffix}`;
|
|
6150
6130
|
writeFileSync14(passwordTmp, `${password}
|
|
6151
6131
|
`, { mode: 384 });
|
|
6152
6132
|
writeFileSync14(userlistTmp, `"synkro" "${verifier}"
|
|
6153
6133
|
`, { mode: 384 });
|
|
6154
|
-
renameSync7(passwordTmp,
|
|
6155
|
-
renameSync7(userlistTmp,
|
|
6156
|
-
chmodSync3(
|
|
6157
|
-
chmodSync3(
|
|
6134
|
+
renameSync7(passwordTmp, passwordPath);
|
|
6135
|
+
renameSync7(userlistTmp, userlistPath);
|
|
6136
|
+
chmodSync3(passwordPath, 384);
|
|
6137
|
+
chmodSync3(userlistPath, 384);
|
|
6158
6138
|
}
|
|
6159
6139
|
function resolveConductorProvider(pool, counts) {
|
|
6160
6140
|
if (pool !== "auto") return pool;
|
|
@@ -6615,7 +6595,7 @@ function dockerStatus() {
|
|
|
6615
6595
|
timeout: 5e3
|
|
6616
6596
|
});
|
|
6617
6597
|
const status = (r.stdout || "").trim();
|
|
6618
|
-
if (status
|
|
6598
|
+
if (!isContainerActiveState(status)) return { running: false };
|
|
6619
6599
|
return {
|
|
6620
6600
|
running: true,
|
|
6621
6601
|
image: imageTag(),
|
|
@@ -6785,7 +6765,7 @@ var init_dockerInstall = __esm({
|
|
|
6785
6765
|
HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
|
|
6786
6766
|
CONTAINER_NAME = resolveContainerName();
|
|
6787
6767
|
defaultImageVersion = () => {
|
|
6788
|
-
if (true) return "1.7.
|
|
6768
|
+
if (true) return "1.7.93";
|
|
6789
6769
|
try {
|
|
6790
6770
|
const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
|
|
6791
6771
|
if (pkg.version) return pkg.version;
|
|
@@ -7022,6 +7002,7 @@ __export(ptyShim_exports, {
|
|
|
7022
7002
|
injectModel: () => injectModel,
|
|
7023
7003
|
installPtyShim: () => installPtyShim,
|
|
7024
7004
|
listSessions: () => listSessions,
|
|
7005
|
+
restoreLegacyClaudeShim: () => restoreLegacyClaudeShim,
|
|
7025
7006
|
uninstallPtyShim: () => uninstallPtyShim
|
|
7026
7007
|
});
|
|
7027
7008
|
import {
|
|
@@ -7038,48 +7019,11 @@ import {
|
|
|
7038
7019
|
} from "fs";
|
|
7039
7020
|
import { homedir as homedir20 } from "os";
|
|
7040
7021
|
import { join as join18 } from "path";
|
|
7041
|
-
import { spawnSync as spawnSync5, spawn as
|
|
7022
|
+
import { spawnSync as spawnSync5, spawn as spawn4 } from "child_process";
|
|
7042
7023
|
function rcFiles() {
|
|
7043
7024
|
const h = homedir20();
|
|
7044
7025
|
return [join18(h, ".zshrc"), join18(h, ".bashrc"), join18(h, ".bash_profile")];
|
|
7045
7026
|
}
|
|
7046
|
-
function resolveRealClaude() {
|
|
7047
|
-
const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
|
|
7048
|
-
const p = (r.stdout || "").trim();
|
|
7049
|
-
if (p) {
|
|
7050
|
-
try {
|
|
7051
|
-
return realpathSync(p);
|
|
7052
|
-
} catch {
|
|
7053
|
-
return p;
|
|
7054
|
-
}
|
|
7055
|
-
}
|
|
7056
|
-
for (const c of [join18(homedir20(), ".local", "bin", "claude"), "/usr/local/bin/claude", "/opt/homebrew/bin/claude"]) {
|
|
7057
|
-
if (existsSync21(c)) {
|
|
7058
|
-
try {
|
|
7059
|
-
return realpathSync(c);
|
|
7060
|
-
} catch {
|
|
7061
|
-
return c;
|
|
7062
|
-
}
|
|
7063
|
-
}
|
|
7064
|
-
}
|
|
7065
|
-
return "claude";
|
|
7066
|
-
}
|
|
7067
|
-
function findClaudeLink() {
|
|
7068
|
-
const r = spawnSync5("bash", ["-lc", `PATH="$(printf '%s' "$PATH" | sed "s#$HOME/.synkro/bin:##g")" command -v claude`], { encoding: "utf-8" });
|
|
7069
|
-
let linkPath = (r.stdout || "").trim();
|
|
7070
|
-
if (!linkPath) {
|
|
7071
|
-
const c = join18(homedir20(), ".local", "bin", "claude");
|
|
7072
|
-
if (existsSync21(c)) linkPath = c;
|
|
7073
|
-
else return null;
|
|
7074
|
-
}
|
|
7075
|
-
if (linkPath === SHIM_PATH) return null;
|
|
7076
|
-
let realTarget = linkPath;
|
|
7077
|
-
try {
|
|
7078
|
-
realTarget = realpathSync(linkPath);
|
|
7079
|
-
} catch {
|
|
7080
|
-
}
|
|
7081
|
-
return { linkPath, realTarget };
|
|
7082
|
-
}
|
|
7083
7027
|
function isOurShim(path) {
|
|
7084
7028
|
try {
|
|
7085
7029
|
return readFileSync20(path, "utf-8").slice(0, 300).includes("Synkro pty shim");
|
|
@@ -7087,44 +7031,39 @@ function isOurShim(path) {
|
|
|
7087
7031
|
return false;
|
|
7088
7032
|
}
|
|
7089
7033
|
}
|
|
7090
|
-
function
|
|
7091
|
-
|
|
7092
|
-
if (!found) {
|
|
7093
|
-
console.log(" \xB7 no claude binary to shadow \u2014 PATH shim only");
|
|
7094
|
-
return;
|
|
7095
|
-
}
|
|
7096
|
-
const { linkPath, realTarget } = found;
|
|
7097
|
-
if (isOurShim(linkPath)) {
|
|
7098
|
-
console.log(` \xB7 ${linkPath.replace(homedir20(), "~")} already shadowed`);
|
|
7099
|
-
return;
|
|
7100
|
-
}
|
|
7101
|
-
let wasSymlink = false;
|
|
7034
|
+
function latestVersionedClaude(versionsDir) {
|
|
7035
|
+
let versions = [];
|
|
7102
7036
|
try {
|
|
7103
|
-
|
|
7037
|
+
versions = readdirSync3(versionsDir).map((entry) => join18(versionsDir, entry)).filter((entry) => existsSync21(entry)).sort((a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" }));
|
|
7104
7038
|
} catch {
|
|
7105
7039
|
}
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
writeFileSync16(linkPath, SHIM_SOURCE.replace("__BAKED_CLAUDE__", realTarget), "utf-8");
|
|
7114
|
-
chmodSync4(linkPath, 493);
|
|
7115
|
-
console.log(` \u2713 shadowed ${linkPath.replace(homedir20(), "~")} \u2192 shim (real: ${realTarget.replace(homedir20(), "~")})`);
|
|
7116
|
-
} catch (e) {
|
|
7117
|
-
console.warn(` \u26A0 could not shadow ${linkPath}: ${e.message}`);
|
|
7040
|
+
return versions.at(-1) || "";
|
|
7041
|
+
}
|
|
7042
|
+
function restoreLegacyClaudeShim(linkPath, versionsDir) {
|
|
7043
|
+
if (!isOurShim(linkPath)) return false;
|
|
7044
|
+
const realTarget = latestVersionedClaude(versionsDir);
|
|
7045
|
+
if (!realTarget) {
|
|
7046
|
+
throw new Error(`no Claude version found under ${versionsDir}`);
|
|
7118
7047
|
}
|
|
7048
|
+
rmSync2(linkPath, { force: true });
|
|
7049
|
+
symlinkSync(realTarget, linkPath);
|
|
7050
|
+
return true;
|
|
7119
7051
|
}
|
|
7120
7052
|
function unshadowClaude() {
|
|
7121
7053
|
let state;
|
|
7122
7054
|
try {
|
|
7123
7055
|
state = JSON.parse(readFileSync20(SHADOW_STATE_FILE, "utf-8"));
|
|
7124
7056
|
} catch {
|
|
7057
|
+
}
|
|
7058
|
+
if (!state?.linkPath) {
|
|
7059
|
+
const legacyLink = join18(homedir20(), ".local", "bin", "claude");
|
|
7060
|
+
const versionsDir = join18(homedir20(), ".local", "share", "claude", "versions");
|
|
7061
|
+
if (restoreLegacyClaudeShim(legacyLink, versionsDir)) {
|
|
7062
|
+
const target = realpathSync(legacyLink);
|
|
7063
|
+
console.log(`\u2713 restored ${legacyLink.replace(homedir20(), "~")} \u2192 ${target.replace(homedir20(), "~")}`);
|
|
7064
|
+
}
|
|
7125
7065
|
return;
|
|
7126
7066
|
}
|
|
7127
|
-
if (!state?.linkPath) return;
|
|
7128
7067
|
try {
|
|
7129
7068
|
if (existsSync21(state.linkPath) && !isOurShim(state.linkPath)) return;
|
|
7130
7069
|
rmSync2(state.linkPath, { force: true });
|
|
@@ -7134,25 +7073,6 @@ function unshadowClaude() {
|
|
|
7134
7073
|
console.warn(` \u26A0 could not restore claude: ${e.message} \u2014 run: ln -sf ${state.realTarget} ${state.linkPath}`);
|
|
7135
7074
|
}
|
|
7136
7075
|
}
|
|
7137
|
-
function addPathBlock() {
|
|
7138
|
-
const touched = [];
|
|
7139
|
-
for (const rc of rcFiles()) {
|
|
7140
|
-
if (!existsSync21(rc) && rc.endsWith(".bash_profile")) continue;
|
|
7141
|
-
let body = "";
|
|
7142
|
-
try {
|
|
7143
|
-
body = readFileSync20(rc, "utf-8");
|
|
7144
|
-
} catch {
|
|
7145
|
-
}
|
|
7146
|
-
const cleaned = stripBlock(body);
|
|
7147
|
-
const next = cleaned.replace(/\n*$/, "") + (cleaned ? "\n\n" : "") + RC_BLOCK + "\n";
|
|
7148
|
-
try {
|
|
7149
|
-
writeFileSync16(rc, next, "utf-8");
|
|
7150
|
-
touched.push(rc);
|
|
7151
|
-
} catch {
|
|
7152
|
-
}
|
|
7153
|
-
}
|
|
7154
|
-
return touched;
|
|
7155
|
-
}
|
|
7156
7076
|
function stripBlock(body) {
|
|
7157
7077
|
if (!body.includes(RC_BEGIN)) return body;
|
|
7158
7078
|
const re = new RegExp(`\\n?${escapeRe(RC_BEGIN)}[\\s\\S]*?${escapeRe(RC_END)}\\n?`, "g");
|
|
@@ -7162,28 +7082,14 @@ function escapeRe(s) {
|
|
|
7162
7082
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7163
7083
|
}
|
|
7164
7084
|
function installPtyShim() {
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
const real = resolveRealClaude();
|
|
7168
|
-
writeFileSync16(SHIM_PATH, SHIM_SOURCE.replace("__BAKED_CLAUDE__", real), "utf-8");
|
|
7169
|
-
chmodSync4(SHIM_PATH, 493);
|
|
7170
|
-
const touched = addPathBlock();
|
|
7171
|
-
console.log(` \u2713 pty routing shim installed (real claude: ${real})`);
|
|
7172
|
-
shadowClaude();
|
|
7173
|
-
if (touched.length) console.log(` added ~/.synkro/bin to PATH in: ${touched.map((t) => t.replace(homedir20(), "~")).join(", ")}`);
|
|
7085
|
+
console.warn("Synkro host model routing has been removed; cleaning up any legacy pty shim.");
|
|
7086
|
+
uninstallPtyShim();
|
|
7174
7087
|
}
|
|
7175
7088
|
function uninstallPtyShim() {
|
|
7176
7089
|
try {
|
|
7177
7090
|
unshadowClaude();
|
|
7178
7091
|
} catch {
|
|
7179
7092
|
}
|
|
7180
|
-
try {
|
|
7181
|
-
const ls = spawnSync5("tmux", ["ls", "-F", "#{session_name}"], { encoding: "utf-8" });
|
|
7182
|
-
for (const s of (ls.stdout || "").split("\n")) {
|
|
7183
|
-
if (s.startsWith(SHIM_SESSION_PREFIX)) spawnSync5("tmux", ["kill-session", "-t", `=${s}`], { encoding: "utf-8" });
|
|
7184
|
-
}
|
|
7185
|
-
} catch {
|
|
7186
|
-
}
|
|
7187
7093
|
const cleaned = [];
|
|
7188
7094
|
for (const rc of rcFiles()) {
|
|
7189
7095
|
if (!existsSync21(rc)) continue;
|
|
@@ -7284,7 +7190,7 @@ function injectModel(model, sessionOverride) {
|
|
|
7284
7190
|
} catch {
|
|
7285
7191
|
}
|
|
7286
7192
|
const poll = `old=$(cat '${pidFile}' 2>/dev/null); if [ -n "$old" ]; then kill "$old" 2>/dev/null; fi; rm -f '${pidFile}'; set -o noclobber; echo $$ > '${pidFile}' 2>/dev/null || exit 1; set +o noclobber; trap 'rm -f "${pidFile}"' EXIT; for i in $(seq 1 40); do if tmux capture-pane -t ${session} -p -S -25 2>/dev/null | grep -qiE 'switch model|yes, switch'; then tmux send-keys -t ${session} -l '1'; sleep 0.1; tmux send-keys -t ${session} Enter; break; fi; sleep 0.1; done`;
|
|
7287
|
-
const child =
|
|
7193
|
+
const child = spawn4("bash", ["-c", poll], { detached: true, stdio: "ignore" });
|
|
7288
7194
|
child.unref();
|
|
7289
7195
|
return true;
|
|
7290
7196
|
}
|
|
@@ -7570,7 +7476,7 @@ __export(install_exports, {
|
|
|
7570
7476
|
import { existsSync as existsSync22, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17, chmodSync as chmodSync5, readFileSync as readFileSync21, readdirSync as readdirSync4, unlinkSync as unlinkSync7, statSync as statSync2 } from "fs";
|
|
7571
7477
|
import { homedir as homedir21 } from "os";
|
|
7572
7478
|
import { join as join19, isAbsolute, resolve as resolve4 } from "path";
|
|
7573
|
-
import { execSync as execSync4, spawn as
|
|
7479
|
+
import { execSync as execSync4, spawn as spawn5 } from "child_process";
|
|
7574
7480
|
import { createInterface as createInterface2 } from "readline";
|
|
7575
7481
|
import { createHash as createHash4 } from "crypto";
|
|
7576
7482
|
function resolvePersistedHookMode() {
|
|
@@ -7698,7 +7604,6 @@ async function promptTranscriptSources(wantCC, wantCursor, wantCodex) {
|
|
|
7698
7604
|
function ensureSynkroDir() {
|
|
7699
7605
|
mkdirSync15(SYNKRO_DIR11, { recursive: true });
|
|
7700
7606
|
mkdirSync15(HOOKS_DIR, { recursive: true });
|
|
7701
|
-
mkdirSync15(BIN_DIR, { recursive: true });
|
|
7702
7607
|
mkdirSync15(OFFSETS_DIR, { recursive: true });
|
|
7703
7608
|
mkdirSync15(join19(SYNKRO_DIR11, "sessions"), { recursive: true });
|
|
7704
7609
|
}
|
|
@@ -7722,7 +7627,7 @@ function writeHookScripts() {
|
|
|
7722
7627
|
const subagentStartScriptPath = join19(HOOKS_DIR, "codex-subagent-start.ts");
|
|
7723
7628
|
const subagentStopScriptPath = join19(HOOKS_DIR, "codex-subagent-stop.ts");
|
|
7724
7629
|
const userPromptSubmitScriptPath = join19(HOOKS_DIR, "cc-user-prompt-submit.ts");
|
|
7725
|
-
const
|
|
7630
|
+
const legacyPromptRouteScriptPath = join19(HOOKS_DIR, "cc-prompt-route.ts");
|
|
7726
7631
|
const commonScriptPath = join19(HOOKS_DIR, "_synkro-common.ts");
|
|
7727
7632
|
const commonBashScriptPath = join19(HOOKS_DIR, "_synkro-common.sh");
|
|
7728
7633
|
const installScanScriptPath = join19(HOOKS_DIR, "cc-install-scan.ts");
|
|
@@ -7753,7 +7658,7 @@ function writeHookScripts() {
|
|
|
7753
7658
|
[subagentStartScriptPath, STUB_SUBAGENT_START_TS],
|
|
7754
7659
|
[subagentStopScriptPath, STUB_SUBAGENT_STOP_TS],
|
|
7755
7660
|
[userPromptSubmitScriptPath, STUB_USER_PROMPT_SUBMIT_TS],
|
|
7756
|
-
[
|
|
7661
|
+
[legacyPromptRouteScriptPath, STUB_PROMPT_ROUTE_TS],
|
|
7757
7662
|
[installScanScriptPath, STUB_INSTALL_SCAN_TS],
|
|
7758
7663
|
[taskActivateIntentScriptPath, STUB_TASK_ACTIVATE_INTENT_TS],
|
|
7759
7664
|
[mcpGateScriptPath, STUB_MCP_GATE_TS],
|
|
@@ -7792,7 +7697,6 @@ function writeHookScripts() {
|
|
|
7792
7697
|
subagentStartScript: subagentStartScriptPath,
|
|
7793
7698
|
subagentStopScript: subagentStopScriptPath,
|
|
7794
7699
|
userPromptSubmitScript: userPromptSubmitScriptPath,
|
|
7795
|
-
promptRouteScript: promptRouteScriptPath,
|
|
7796
7700
|
installScanScript: installScanScriptPath,
|
|
7797
7701
|
cursorBashJudgeScript: cursorBashJudgePath,
|
|
7798
7702
|
cursorEditCaptureScript: cursorEditCapturePath,
|
|
@@ -7830,7 +7734,7 @@ function writeConfigEnv(opts) {
|
|
|
7830
7734
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
7831
7735
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
7832
7736
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
7833
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.
|
|
7737
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.93")}`
|
|
7834
7738
|
];
|
|
7835
7739
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
7836
7740
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -8572,7 +8476,7 @@ async function installCommand(opts = {}) {
|
|
|
8572
8476
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
8573
8477
|
emit("install", {
|
|
8574
8478
|
phase: "started",
|
|
8575
|
-
cli_version_to: "1.7.
|
|
8479
|
+
cli_version_to: "1.7.93",
|
|
8576
8480
|
agents_detected: agents.map((a) => a.kind),
|
|
8577
8481
|
with_github: false,
|
|
8578
8482
|
with_local_cc: false,
|
|
@@ -8613,7 +8517,6 @@ async function installCommand(opts = {}) {
|
|
|
8613
8517
|
sessionStartScriptPath: scripts.sessionStartScript,
|
|
8614
8518
|
transcriptSyncScriptPath: scripts.transcriptSyncScript,
|
|
8615
8519
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
8616
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
8617
8520
|
installScanScriptPath: scripts.installScanScript,
|
|
8618
8521
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
8619
8522
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
@@ -8661,25 +8564,22 @@ async function installCommand(opts = {}) {
|
|
|
8661
8564
|
subagentStartScriptPath: scripts.subagentStartScript,
|
|
8662
8565
|
subagentStopScriptPath: scripts.subagentStopScript,
|
|
8663
8566
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
8664
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
8665
8567
|
installScanScriptPath: scripts.installScanScript,
|
|
8666
8568
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
8667
8569
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
8668
8570
|
skipTranscriptSync: !transcriptCodex
|
|
8669
8571
|
});
|
|
8670
8572
|
console.log(`Configured ${agent.name} hooks at ${agent.settingsPath}`);
|
|
8671
|
-
|
|
8573
|
+
await reportCodexHookTrust(agent.binaryPath, process.cwd());
|
|
8672
8574
|
}
|
|
8673
8575
|
}
|
|
8674
8576
|
console.log();
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
console.warn(` \u26A0 pty shim install skipped: ${e.message}`);
|
|
8680
|
-
}
|
|
8681
|
-
console.log();
|
|
8577
|
+
try {
|
|
8578
|
+
uninstallPtyShim();
|
|
8579
|
+
} catch (e) {
|
|
8580
|
+
console.warn(` \u26A0 legacy pty shim cleanup skipped: ${e.message}`);
|
|
8682
8581
|
}
|
|
8582
|
+
console.log();
|
|
8683
8583
|
let userId;
|
|
8684
8584
|
let orgId;
|
|
8685
8585
|
let email;
|
|
@@ -9093,7 +8993,7 @@ async function installCommand(opts = {}) {
|
|
|
9093
8993
|
}
|
|
9094
8994
|
}
|
|
9095
8995
|
try {
|
|
9096
|
-
const child =
|
|
8996
|
+
const child = spawn5(process.execPath, [process.argv[1], "reachability-scan", "--quiet"], {
|
|
9097
8997
|
detached: true,
|
|
9098
8998
|
stdio: "ignore",
|
|
9099
8999
|
cwd: process.cwd()
|
|
@@ -9274,7 +9174,6 @@ function reconcileHarness() {
|
|
|
9274
9174
|
sessionStartScriptPath: scripts.sessionStartScript,
|
|
9275
9175
|
transcriptSyncScriptPath: scripts.transcriptSyncScript,
|
|
9276
9176
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
9277
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
9278
9177
|
installScanScriptPath: scripts.installScanScript,
|
|
9279
9178
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
9280
9179
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
@@ -9344,14 +9243,13 @@ function reconcileHarness() {
|
|
|
9344
9243
|
subagentStartScriptPath: scripts.subagentStartScript,
|
|
9345
9244
|
subagentStopScriptPath: scripts.subagentStopScript,
|
|
9346
9245
|
userPromptSubmitScriptPath: scripts.userPromptSubmitScript,
|
|
9347
|
-
promptRouteScriptPath: scripts.promptRouteScript,
|
|
9348
9246
|
installScanScriptPath: scripts.installScanScript,
|
|
9349
9247
|
taskActivateIntentScriptPath: scripts.taskActivateIntentScript,
|
|
9350
9248
|
mcpGateScriptPath: scripts.mcpGateScript,
|
|
9351
9249
|
skipTranscriptSync: !persistedTranscriptConsent("CODEX")
|
|
9352
9250
|
});
|
|
9353
9251
|
console.log(" \u2713 Codex hooks registered");
|
|
9354
|
-
console.log("
|
|
9252
|
+
console.log(" Restart Codex and review /hooks whenever Codex marks a Synkro hook modified.");
|
|
9355
9253
|
try {
|
|
9356
9254
|
installCodexMcpConfig({ gatewayUrl: "", bearerToken: "", local: true });
|
|
9357
9255
|
console.log(" \u2713 Codex MCP registered");
|
|
@@ -10119,7 +10017,7 @@ async function syncCodexTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
10119
10017
|
}
|
|
10120
10018
|
return { sessions: totalSessions, messages: totalMessages };
|
|
10121
10019
|
}
|
|
10122
|
-
var SYNKRO_DIR11, HOOKS_DIR,
|
|
10020
|
+
var SYNKRO_DIR11, HOOKS_DIR, CONFIG_PATH4, MCP_STDIO_PROXY_SRC, OFFSETS_DIR, CLOUD_JWT_PATH, SKILLS_DISCOVERED_PATH;
|
|
10123
10021
|
var init_install = __esm({
|
|
10124
10022
|
"cli/commands/install.ts"() {
|
|
10125
10023
|
"use strict";
|
|
@@ -10127,6 +10025,7 @@ var init_install = __esm({
|
|
|
10127
10025
|
init_ccHookConfig();
|
|
10128
10026
|
init_cursorHookConfig();
|
|
10129
10027
|
init_codexHookConfig();
|
|
10028
|
+
init_codexHookTrust();
|
|
10130
10029
|
init_mcpConfig();
|
|
10131
10030
|
init_synkroCommand();
|
|
10132
10031
|
init_skillParser();
|
|
@@ -10146,7 +10045,6 @@ var init_install = __esm({
|
|
|
10146
10045
|
init_codexTranscriptMessages();
|
|
10147
10046
|
SYNKRO_DIR11 = join19(homedir21(), ".synkro");
|
|
10148
10047
|
HOOKS_DIR = join19(SYNKRO_DIR11, "hooks");
|
|
10149
|
-
BIN_DIR = join19(SYNKRO_DIR11, "bin");
|
|
10150
10048
|
CONFIG_PATH4 = join19(SYNKRO_DIR11, "config.env");
|
|
10151
10049
|
MCP_STDIO_PROXY_SRC = `#!/usr/bin/env bun
|
|
10152
10050
|
import { readFileSync } from 'node:fs';
|
|
@@ -11202,7 +11100,7 @@ var scanPr_exports = {};
|
|
|
11202
11100
|
__export(scanPr_exports, {
|
|
11203
11101
|
scanPrCommand: () => scanPrCommand
|
|
11204
11102
|
});
|
|
11205
|
-
import { execSync as execSync5, spawn as
|
|
11103
|
+
import { execSync as execSync5, spawn as spawn6 } from "child_process";
|
|
11206
11104
|
import { readFileSync as readFileSync24, existsSync as existsSync26 } from "fs";
|
|
11207
11105
|
import { join as join23 } from "path";
|
|
11208
11106
|
function parseMatchSpec(condition) {
|
|
@@ -11413,7 +11311,7 @@ ${hunks}`;
|
|
|
11413
11311
|
const fullPrompt = promptHeader + userMessage;
|
|
11414
11312
|
return new Promise((resolve6) => {
|
|
11415
11313
|
const t0 = Date.now();
|
|
11416
|
-
const proc =
|
|
11314
|
+
const proc = spawn6(
|
|
11417
11315
|
"claude",
|
|
11418
11316
|
["--print", "--model", "claude-sonnet-4-6", "--output-format", "json", "--no-session-persistence"],
|
|
11419
11317
|
{
|
|
@@ -11512,7 +11410,7 @@ ${JSON.stringify(findings, null, 2)}
|
|
|
11512
11410
|
function spawnOpusConsolidator(findings, claudeToken) {
|
|
11513
11411
|
return new Promise((resolve6) => {
|
|
11514
11412
|
const prompt = buildConsolidationPrompt(findings);
|
|
11515
|
-
const proc =
|
|
11413
|
+
const proc = spawn6(
|
|
11516
11414
|
"claude",
|
|
11517
11415
|
["--print", "--model", "claude-opus-4-7", "--output-format", "json", "--no-session-persistence"],
|
|
11518
11416
|
{
|
|
@@ -11925,296 +11823,20 @@ var init_scanPr = __esm({
|
|
|
11925
11823
|
}
|
|
11926
11824
|
});
|
|
11927
11825
|
|
|
11928
|
-
// cli/local-cc/
|
|
11929
|
-
|
|
11930
|
-
__export(routeDecide_exports, {
|
|
11931
|
-
routeDecide: () => routeDecide
|
|
11932
|
-
});
|
|
11933
|
-
import { readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "fs";
|
|
11826
|
+
// cli/local-cc/pueue.ts
|
|
11827
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync9, spawn as spawn7 } from "child_process";
|
|
11934
11828
|
import { homedir as homedir25 } from "os";
|
|
11935
11829
|
import { join as join24 } from "path";
|
|
11936
|
-
function safeSid(sid) {
|
|
11937
|
-
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
11938
|
-
}
|
|
11939
|
-
function loadMcpJwt() {
|
|
11940
|
-
try {
|
|
11941
|
-
return readFileSync25(join24(SYNKRO_DIR13, ".mcp-jwt"), "utf-8").trim();
|
|
11942
|
-
} catch {
|
|
11943
|
-
return "";
|
|
11944
|
-
}
|
|
11945
|
-
}
|
|
11946
|
-
async function routeDecide(sessionId) {
|
|
11947
|
-
if (!sessionId) return;
|
|
11948
|
-
const sid = safeSid(sessionId);
|
|
11949
|
-
let prompt = "";
|
|
11950
|
-
try {
|
|
11951
|
-
const rec = JSON.parse(readFileSync25(join24(SESSIONS_DIR2, sid + ".json"), "utf-8"));
|
|
11952
|
-
prompt = String(rec.last_prompt || "").trim();
|
|
11953
|
-
} catch {
|
|
11954
|
-
return;
|
|
11955
|
-
}
|
|
11956
|
-
if (!prompt) return;
|
|
11957
|
-
let model = "";
|
|
11958
|
-
try {
|
|
11959
|
-
const resp = await fetch(`http://127.0.0.1:${GRADER_HOST_PORT}/submit`, {
|
|
11960
|
-
method: "POST",
|
|
11961
|
-
headers: { "Content-Type": "application/json", Authorization: "Bearer " + loadMcpJwt() },
|
|
11962
|
-
body: JSON.stringify({ role: "route-classify", payload: prompt, content: prompt, hedge: false }),
|
|
11963
|
-
// First call may spin up the route lane (a haiku worker boot), so allow headroom.
|
|
11964
|
-
signal: AbortSignal.timeout(12e4)
|
|
11965
|
-
});
|
|
11966
|
-
if (!resp.ok) return;
|
|
11967
|
-
const data = await resp.json();
|
|
11968
|
-
const m = (data.result || "").match(/<model>\s*([A-Za-z0-9._:-]+)\s*<\/model>/i);
|
|
11969
|
-
model = m ? m[1].trim() : "";
|
|
11970
|
-
} catch {
|
|
11971
|
-
return;
|
|
11972
|
-
}
|
|
11973
|
-
if (!model || !VALID_MODELS.has(model)) return;
|
|
11974
|
-
const lastFile = join24(PTY_DIR, "route-last-" + sid);
|
|
11975
|
-
let last = "";
|
|
11976
|
-
try {
|
|
11977
|
-
last = readFileSync25(lastFile, "utf-8").trim();
|
|
11978
|
-
} catch {
|
|
11979
|
-
}
|
|
11980
|
-
if (model === last) return;
|
|
11981
|
-
try {
|
|
11982
|
-
writeFileSync19(lastFile, model);
|
|
11983
|
-
} catch {
|
|
11984
|
-
}
|
|
11985
|
-
try {
|
|
11986
|
-
writeFileSync19(join24(PTY_DIR, "route-" + sid), model);
|
|
11987
|
-
} catch {
|
|
11988
|
-
}
|
|
11989
|
-
}
|
|
11990
|
-
var SYNKRO_DIR13, PTY_DIR, SESSIONS_DIR2, VALID_MODELS, GRADER_HOST_PORT;
|
|
11991
|
-
var init_routeDecide = __esm({
|
|
11992
|
-
"cli/local-cc/routeDecide.ts"() {
|
|
11993
|
-
"use strict";
|
|
11994
|
-
SYNKRO_DIR13 = join24(homedir25(), ".synkro");
|
|
11995
|
-
PTY_DIR = join24(SYNKRO_DIR13, "pty");
|
|
11996
|
-
SESSIONS_DIR2 = join24(PTY_DIR, "sessions");
|
|
11997
|
-
VALID_MODELS = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
|
|
11998
|
-
GRADER_HOST_PORT = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
|
|
11999
|
-
}
|
|
12000
|
-
});
|
|
12001
|
-
|
|
12002
|
-
// cli/local-cc/routeOrchestrate.ts
|
|
12003
|
-
var routeOrchestrate_exports = {};
|
|
12004
|
-
__export(routeOrchestrate_exports, {
|
|
12005
|
-
routeAndResubmit: () => routeAndResubmit
|
|
12006
|
-
});
|
|
12007
|
-
import { readFileSync as readFileSync26, writeFileSync as writeFileSync20 } from "fs";
|
|
12008
|
-
import { homedir as homedir26 } from "os";
|
|
12009
|
-
import { join as join25 } from "path";
|
|
12010
|
-
import { spawnSync as spawnSync9 } from "child_process";
|
|
12011
|
-
function safeSid2(sid) {
|
|
12012
|
-
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
12013
|
-
}
|
|
12014
|
-
function safeSession(s) {
|
|
12015
|
-
return /^[A-Za-z0-9._:-]{1,64}$/.test(s);
|
|
12016
|
-
}
|
|
12017
|
-
function loadMcpJwt2() {
|
|
12018
|
-
try {
|
|
12019
|
-
return readFileSync26(join25(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
12020
|
-
} catch {
|
|
12021
|
-
return "";
|
|
12022
|
-
}
|
|
12023
|
-
}
|
|
12024
|
-
async function classifyTask(prompt) {
|
|
12025
|
-
try {
|
|
12026
|
-
const resp = await fetch(`http://127.0.0.1:${GRADER_HOST_PORT2}/submit`, {
|
|
12027
|
-
method: "POST",
|
|
12028
|
-
headers: { "Content-Type": "application/json", Authorization: "Bearer " + loadMcpJwt2() },
|
|
12029
|
-
body: JSON.stringify({ role: "route-classify", payload: prompt, content: prompt, hedge: false }),
|
|
12030
|
-
signal: AbortSignal.timeout(6e4)
|
|
12031
|
-
});
|
|
12032
|
-
if (!resp.ok) return null;
|
|
12033
|
-
const data = await resp.json();
|
|
12034
|
-
const m = (data.result || "").match(/<model>\s*([A-Za-z0-9._:-]+)\s*<\/model>/i);
|
|
12035
|
-
if (!m) return null;
|
|
12036
|
-
const v = m[1].trim();
|
|
12037
|
-
if (v.toLowerCase() === "keep") return "keep";
|
|
12038
|
-
return VALID_MODELS2.has(v) ? v : null;
|
|
12039
|
-
} catch {
|
|
12040
|
-
return null;
|
|
12041
|
-
}
|
|
12042
|
-
}
|
|
12043
|
-
function lastRoutedModel(sid) {
|
|
12044
|
-
try {
|
|
12045
|
-
const v = readFileSync26(join25(PTY_DIR2, "route-last-" + sid), "utf-8").trim();
|
|
12046
|
-
return VALID_MODELS2.has(v) ? v : "";
|
|
12047
|
-
} catch {
|
|
12048
|
-
return "";
|
|
12049
|
-
}
|
|
12050
|
-
}
|
|
12051
|
-
function resolveSession(sid, tmuxSession) {
|
|
12052
|
-
const candidates = [];
|
|
12053
|
-
if (tmuxSession) candidates.push(tmuxSession);
|
|
12054
|
-
try {
|
|
12055
|
-
const rec = JSON.parse(readFileSync26(join25(SESSIONS_DIR3, safeSid2(sid) + ".json"), "utf-8"));
|
|
12056
|
-
if (rec.tmux_session) candidates.push(rec.tmux_session);
|
|
12057
|
-
} catch {
|
|
12058
|
-
}
|
|
12059
|
-
try {
|
|
12060
|
-
candidates.push(readFileSync26(ACTIVE_SESSION_FILE2, "utf-8").trim());
|
|
12061
|
-
} catch {
|
|
12062
|
-
}
|
|
12063
|
-
for (const c of candidates) if (c && safeSession(c)) return c;
|
|
12064
|
-
return "";
|
|
12065
|
-
}
|
|
12066
|
-
async function routeAndResubmit(sessionId, task, tmuxSession, forceModel) {
|
|
12067
|
-
const trimmed = (task || "").trim();
|
|
12068
|
-
if (!sessionId || !trimmed) return;
|
|
12069
|
-
const sid = safeSid2(sessionId);
|
|
12070
|
-
const session = resolveSession(sid, tmuxSession);
|
|
12071
|
-
if (!session) return;
|
|
12072
|
-
if (spawnSync9("tmux", ["has-session", "-t", `=${session}`], { encoding: "utf-8" }).status !== 0) return;
|
|
12073
|
-
const current = lastRoutedModel(sid);
|
|
12074
|
-
const picked = forceModel && VALID_MODELS2.has(forceModel) ? forceModel : await classifyTask(trimmed);
|
|
12075
|
-
const doSwap = !!picked && picked !== "keep" && picked !== current;
|
|
12076
|
-
const sk = (...a) => spawnSync9("tmux", ["send-keys", "-t", session, ...a], { encoding: "utf-8" });
|
|
12077
|
-
const capture = () => spawnSync9("tmux", ["capture-pane", "-t", session, "-p", "-S", "-25"], { encoding: "utf-8" }).stdout || "";
|
|
12078
|
-
await wait(400);
|
|
12079
|
-
if (doSwap) {
|
|
12080
|
-
sk("C-u");
|
|
12081
|
-
await wait(80);
|
|
12082
|
-
sk("-l", `/model ${picked}`);
|
|
12083
|
-
sk("Enter");
|
|
12084
|
-
for (let i = 0; i < 50; i++) {
|
|
12085
|
-
await wait(100);
|
|
12086
|
-
if (/switch model|yes, switch/i.test(capture())) {
|
|
12087
|
-
sk("-l", "1");
|
|
12088
|
-
await wait(120);
|
|
12089
|
-
sk("Enter");
|
|
12090
|
-
break;
|
|
12091
|
-
}
|
|
12092
|
-
}
|
|
12093
|
-
await wait(500);
|
|
12094
|
-
try {
|
|
12095
|
-
writeFileSync20(join25(PTY_DIR2, "route-last-" + sid), picked);
|
|
12096
|
-
} catch {
|
|
12097
|
-
}
|
|
12098
|
-
}
|
|
12099
|
-
try {
|
|
12100
|
-
writeFileSync20(join25(PTY_DIR2, "route-guard-" + sid), "1");
|
|
12101
|
-
} catch {
|
|
12102
|
-
}
|
|
12103
|
-
sk("C-u");
|
|
12104
|
-
await wait(80);
|
|
12105
|
-
spawnSync9("tmux", ["set-buffer", "-b", "synkro-route", trimmed], { encoding: "utf-8" });
|
|
12106
|
-
spawnSync9("tmux", ["paste-buffer", "-t", session, "-b", "synkro-route", "-d"], { encoding: "utf-8" });
|
|
12107
|
-
await wait(120);
|
|
12108
|
-
sk("Enter");
|
|
12109
|
-
}
|
|
12110
|
-
var SYNKRO_DIR14, PTY_DIR2, SESSIONS_DIR3, ACTIVE_SESSION_FILE2, VALID_MODELS2, GRADER_HOST_PORT2, wait;
|
|
12111
|
-
var init_routeOrchestrate = __esm({
|
|
12112
|
-
"cli/local-cc/routeOrchestrate.ts"() {
|
|
12113
|
-
"use strict";
|
|
12114
|
-
SYNKRO_DIR14 = join25(homedir26(), ".synkro");
|
|
12115
|
-
PTY_DIR2 = join25(SYNKRO_DIR14, "pty");
|
|
12116
|
-
SESSIONS_DIR3 = join25(PTY_DIR2, "sessions");
|
|
12117
|
-
ACTIVE_SESSION_FILE2 = join25(PTY_DIR2, "active");
|
|
12118
|
-
VALID_MODELS2 = /* @__PURE__ */ new Set(["claude-haiku-4-5", "claude-sonnet-5", "claude-opus-4-8"]);
|
|
12119
|
-
GRADER_HOST_PORT2 = process.env.SYNKRO_GRADER_HOST_PORT || "18929";
|
|
12120
|
-
wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12121
|
-
}
|
|
12122
|
-
});
|
|
12123
|
-
|
|
12124
|
-
// cli/local-cc/routingToggle.ts
|
|
12125
|
-
var routingToggle_exports = {};
|
|
12126
|
-
__export(routingToggle_exports, {
|
|
12127
|
-
routingCommand: () => routingCommand
|
|
12128
|
-
});
|
|
12129
|
-
import { writeFileSync as writeFileSync21, unlinkSync as unlinkSync9, existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
12130
|
-
import { homedir as homedir27 } from "os";
|
|
12131
|
-
import { join as join26 } from "path";
|
|
12132
|
-
function safeSid3(sid) {
|
|
12133
|
-
return sid.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
12134
|
-
}
|
|
12135
|
-
function markerFor(session) {
|
|
12136
|
-
return session ? join26(PTY_DIR3, "routing-on-" + safeSid3(session)) : join26(PTY_DIR3, "routing-on");
|
|
12137
|
-
}
|
|
12138
|
-
function routingCommand(args2) {
|
|
12139
|
-
const sub = (args2[0] || "status").trim();
|
|
12140
|
-
let session;
|
|
12141
|
-
const si = args2.indexOf("--session");
|
|
12142
|
-
if (si >= 0 && args2[si + 1]) session = args2[si + 1].trim();
|
|
12143
|
-
if (sub === "on") {
|
|
12144
|
-
try {
|
|
12145
|
-
writeFileSync21(markerFor(session), "1");
|
|
12146
|
-
} catch (e) {
|
|
12147
|
-
console.error("routing on failed:", String(e));
|
|
12148
|
-
return;
|
|
12149
|
-
}
|
|
12150
|
-
console.log(session ? `Routing ON for session ${session}.` : "Routing ON (all sessions).");
|
|
12151
|
-
console.log(" New task prompts will be classified and run on the best model automatically.");
|
|
12152
|
-
return;
|
|
12153
|
-
}
|
|
12154
|
-
if (sub === "off") {
|
|
12155
|
-
let removed = 0;
|
|
12156
|
-
if (session) {
|
|
12157
|
-
if (existsSync27(markerFor(session))) {
|
|
12158
|
-
try {
|
|
12159
|
-
unlinkSync9(markerFor(session));
|
|
12160
|
-
removed++;
|
|
12161
|
-
} catch {
|
|
12162
|
-
}
|
|
12163
|
-
}
|
|
12164
|
-
} else {
|
|
12165
|
-
for (const f of safeList()) {
|
|
12166
|
-
if (f === "routing-on" || f.startsWith("routing-on-")) {
|
|
12167
|
-
try {
|
|
12168
|
-
unlinkSync9(join26(PTY_DIR3, f));
|
|
12169
|
-
removed++;
|
|
12170
|
-
} catch {
|
|
12171
|
-
}
|
|
12172
|
-
}
|
|
12173
|
-
}
|
|
12174
|
-
}
|
|
12175
|
-
console.log(`Routing OFF${session ? ` for session ${session}` : ""} (${removed} marker${removed === 1 ? "" : "s"} cleared).`);
|
|
12176
|
-
return;
|
|
12177
|
-
}
|
|
12178
|
-
const markers = safeList().filter((f) => f === "routing-on" || f.startsWith("routing-on-"));
|
|
12179
|
-
if (markers.length === 0) {
|
|
12180
|
-
console.log("Routing is OFF (no markers).");
|
|
12181
|
-
return;
|
|
12182
|
-
}
|
|
12183
|
-
console.log("Routing is ON:");
|
|
12184
|
-
for (const m of markers) {
|
|
12185
|
-
if (m === "routing-on") console.log(" \u2022 global (all sessions)");
|
|
12186
|
-
else console.log(" \u2022 session " + m.slice("routing-on-".length));
|
|
12187
|
-
}
|
|
12188
|
-
}
|
|
12189
|
-
function safeList() {
|
|
12190
|
-
try {
|
|
12191
|
-
return readdirSync6(PTY_DIR3);
|
|
12192
|
-
} catch {
|
|
12193
|
-
return [];
|
|
12194
|
-
}
|
|
12195
|
-
}
|
|
12196
|
-
var PTY_DIR3;
|
|
12197
|
-
var init_routingToggle = __esm({
|
|
12198
|
-
"cli/local-cc/routingToggle.ts"() {
|
|
12199
|
-
"use strict";
|
|
12200
|
-
PTY_DIR3 = join26(homedir27(), ".synkro", "pty");
|
|
12201
|
-
}
|
|
12202
|
-
});
|
|
12203
|
-
|
|
12204
|
-
// cli/local-cc/pueue.ts
|
|
12205
|
-
import { execFileSync as execFileSync4, spawnSync as spawnSync10, spawn as spawn6 } from "child_process";
|
|
12206
|
-
import { homedir as homedir28 } from "os";
|
|
12207
|
-
import { join as join27 } from "path";
|
|
12208
11830
|
import { connect as connect2 } from "net";
|
|
12209
11831
|
function pueueAvailable() {
|
|
12210
|
-
const r =
|
|
11832
|
+
const r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
|
|
12211
11833
|
if (r.status !== 0) {
|
|
12212
11834
|
throw new PueueError("pueue CLI not found on PATH. Install pueue (https://github.com/Nukesor/pueue) and start `pueued`.");
|
|
12213
11835
|
}
|
|
12214
11836
|
}
|
|
12215
11837
|
function statusJson() {
|
|
12216
11838
|
pueueAvailable();
|
|
12217
|
-
const r =
|
|
11839
|
+
const r = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8" });
|
|
12218
11840
|
if (r.status !== 0) {
|
|
12219
11841
|
throw new PueueError(`pueue status failed: ${r.stderr || r.stdout || "unknown error"} \u2014 is pueued running?`);
|
|
12220
11842
|
}
|
|
@@ -12259,18 +11881,18 @@ function startTask(opts = {}) {
|
|
|
12259
11881
|
let existing = findTask(ch);
|
|
12260
11882
|
while (existing) {
|
|
12261
11883
|
if (existing.status === "Running" || existing.status === "Queued") {
|
|
12262
|
-
|
|
12263
|
-
|
|
11884
|
+
spawnSync9("tmux", ["kill-session", "-t", `=${ch.tmuxSession}`], { encoding: "utf-8" });
|
|
11885
|
+
spawnSync9("pueue", ["kill", String(existing.id)], { encoding: "utf-8" });
|
|
12264
11886
|
for (let i = 0; i < 10; i++) {
|
|
12265
11887
|
const check = findTask(ch);
|
|
12266
11888
|
if (!check || check.id !== existing.id || check.status !== "Running" && check.status !== "Queued") break;
|
|
12267
|
-
|
|
11889
|
+
spawnSync9("sleep", ["0.5"], { encoding: "utf-8" });
|
|
12268
11890
|
}
|
|
12269
11891
|
}
|
|
12270
|
-
|
|
11892
|
+
spawnSync9("pueue", ["remove", String(existing.id)], { encoding: "utf-8" });
|
|
12271
11893
|
existing = findTask(ch);
|
|
12272
11894
|
}
|
|
12273
|
-
const runScript =
|
|
11895
|
+
const runScript = join24(cwd, "run-claude.sh");
|
|
12274
11896
|
const args2 = [
|
|
12275
11897
|
"add",
|
|
12276
11898
|
"--label",
|
|
@@ -12281,7 +11903,7 @@ function startTask(opts = {}) {
|
|
|
12281
11903
|
"bash",
|
|
12282
11904
|
runScript
|
|
12283
11905
|
];
|
|
12284
|
-
const r =
|
|
11906
|
+
const r = spawnSync9("pueue", args2, { encoding: "utf-8" });
|
|
12285
11907
|
if (r.status !== 0) {
|
|
12286
11908
|
throw new PueueError(`pueue add failed: ${r.stderr || r.stdout}`);
|
|
12287
11909
|
}
|
|
@@ -12292,25 +11914,25 @@ function startTask(opts = {}) {
|
|
|
12292
11914
|
return created;
|
|
12293
11915
|
}
|
|
12294
11916
|
function stopTask(channel = CHANNEL_PRIMARY) {
|
|
12295
|
-
|
|
11917
|
+
spawnSync9("tmux", ["kill-session", "-t", `=${channel.tmuxSession}`], { encoding: "utf-8" });
|
|
12296
11918
|
let t = findTask(channel);
|
|
12297
11919
|
while (t) {
|
|
12298
11920
|
if (t.status === "Running" || t.status === "Queued") {
|
|
12299
|
-
|
|
11921
|
+
spawnSync9("pueue", ["kill", String(t.id)], { encoding: "utf-8" });
|
|
12300
11922
|
for (let i = 0; i < 10; i++) {
|
|
12301
11923
|
const check = findTask(channel);
|
|
12302
11924
|
if (!check || check.id !== t.id || check.status !== "Running" && check.status !== "Queued") break;
|
|
12303
|
-
|
|
11925
|
+
spawnSync9("sleep", ["0.5"], { encoding: "utf-8" });
|
|
12304
11926
|
}
|
|
12305
11927
|
}
|
|
12306
|
-
|
|
11928
|
+
spawnSync9("pueue", ["remove", String(t.id)], { encoding: "utf-8" });
|
|
12307
11929
|
t = findTask(channel);
|
|
12308
11930
|
}
|
|
12309
11931
|
}
|
|
12310
11932
|
function tailLogs(lines = 80, channel = CHANNEL_PRIMARY) {
|
|
12311
11933
|
const t = findTask(channel);
|
|
12312
11934
|
if (!t) return `(no ${channel.taskLabel} task)`;
|
|
12313
|
-
const r =
|
|
11935
|
+
const r = spawnSync9("pueue", ["log", "--lines", String(lines), String(t.id)], { encoding: "utf-8" });
|
|
12314
11936
|
return r.stdout || r.stderr || "(no output)";
|
|
12315
11937
|
}
|
|
12316
11938
|
function ensureRunning(opts = {}) {
|
|
@@ -12335,8 +11957,8 @@ function probePort(host, port, timeoutMs = 500) {
|
|
|
12335
11957
|
});
|
|
12336
11958
|
}
|
|
12337
11959
|
function tmuxDismissPrompts(tmuxSession = TMUX_SESSION) {
|
|
12338
|
-
|
|
12339
|
-
|
|
11960
|
+
spawnSync9("tmux", ["send-keys", "-t", tmuxSession, "1"], { encoding: "utf-8" });
|
|
11961
|
+
spawnSync9("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { encoding: "utf-8" });
|
|
12340
11962
|
}
|
|
12341
11963
|
async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tmuxSession = TMUX_SESSION) {
|
|
12342
11964
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -12348,46 +11970,46 @@ async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1", tm
|
|
|
12348
11970
|
return probePort(host, port);
|
|
12349
11971
|
}
|
|
12350
11972
|
function brewInstall(pkg) {
|
|
12351
|
-
const brew =
|
|
11973
|
+
const brew = spawnSync9("brew", ["--version"], { encoding: "utf-8" });
|
|
12352
11974
|
if (brew.status !== 0) return false;
|
|
12353
11975
|
console.log(` Installing ${pkg} via brew...`);
|
|
12354
|
-
const r =
|
|
11976
|
+
const r = spawnSync9("brew", ["install", pkg], { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
12355
11977
|
return r.status === 0;
|
|
12356
11978
|
}
|
|
12357
11979
|
function assertPueueInstalled() {
|
|
12358
|
-
let r =
|
|
11980
|
+
let r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
|
|
12359
11981
|
if (r.status !== 0) {
|
|
12360
11982
|
if (process.platform === "darwin" && brewInstall("pueue")) {
|
|
12361
|
-
r =
|
|
11983
|
+
r = spawnSync9("pueue", ["--version"], { encoding: "utf-8" });
|
|
12362
11984
|
if (r.status !== 0) throw new PueueError("pueue install succeeded but binary not found on PATH.");
|
|
12363
11985
|
} else {
|
|
12364
11986
|
throw new PueueError("pueue not found. Install it: brew install pueue (macOS) or https://github.com/Nukesor/pueue");
|
|
12365
11987
|
}
|
|
12366
11988
|
}
|
|
12367
|
-
const status =
|
|
11989
|
+
const status = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
|
|
12368
11990
|
if (status.status !== 0) {
|
|
12369
11991
|
console.log(" Starting pueued daemon...");
|
|
12370
|
-
const child =
|
|
11992
|
+
const child = spawn7("pueued", ["-d"], { stdio: "ignore", detached: true });
|
|
12371
11993
|
child.unref();
|
|
12372
|
-
|
|
12373
|
-
const retry =
|
|
11994
|
+
spawnSync9("sleep", ["1"]);
|
|
11995
|
+
const retry = spawnSync9("pueue", ["status", "--json"], { encoding: "utf-8", timeout: 5e3 });
|
|
12374
11996
|
if (retry.status !== 0) {
|
|
12375
11997
|
throw new PueueError("pueue daemon not reachable after starting pueued. Check `pueued` manually.");
|
|
12376
11998
|
}
|
|
12377
11999
|
}
|
|
12378
|
-
|
|
12000
|
+
spawnSync9("pueue", ["parallel", "2"], { encoding: "utf-8" });
|
|
12379
12001
|
}
|
|
12380
12002
|
function assertClaudeInstalled() {
|
|
12381
|
-
const r =
|
|
12003
|
+
const r = spawnSync9("claude", ["--version"], { encoding: "utf-8" });
|
|
12382
12004
|
if (r.status !== 0) {
|
|
12383
12005
|
throw new PueueError("claude CLI not found on PATH. Install Claude Code first: https://docs.claude.com/claude-code");
|
|
12384
12006
|
}
|
|
12385
12007
|
}
|
|
12386
12008
|
function assertTmuxInstalled() {
|
|
12387
|
-
let r =
|
|
12009
|
+
let r = spawnSync9("tmux", ["-V"], { encoding: "utf-8" });
|
|
12388
12010
|
if (r.status !== 0) {
|
|
12389
12011
|
if (process.platform === "darwin" && brewInstall("tmux")) {
|
|
12390
|
-
r =
|
|
12012
|
+
r = spawnSync9("tmux", ["-V"], { encoding: "utf-8" });
|
|
12391
12013
|
if (r.status !== 0) throw new PueueError("tmux install succeeded but binary not found on PATH.");
|
|
12392
12014
|
} else {
|
|
12393
12015
|
throw new PueueError("tmux not found. Install it: brew install tmux (macOS) or apt install tmux (Linux)");
|
|
@@ -12400,12 +12022,12 @@ var init_pueue = __esm({
|
|
|
12400
12022
|
"use strict";
|
|
12401
12023
|
TASK_LABEL = "synkro-local-cc";
|
|
12402
12024
|
TMUX_SESSION = "synkro-local-cc";
|
|
12403
|
-
SESSION_DIR2 =
|
|
12025
|
+
SESSION_DIR2 = join24(homedir25(), ".synkro", "cc_sessions");
|
|
12404
12026
|
TASK_LABEL_2 = "synkro-local-cc-2";
|
|
12405
12027
|
TMUX_SESSION_2 = "synkro-local-cc-2";
|
|
12406
|
-
SESSION_DIR_22 =
|
|
12407
|
-
SESSION_DIR_32 =
|
|
12408
|
-
SESSION_DIR_42 =
|
|
12028
|
+
SESSION_DIR_22 = join24(homedir25(), ".synkro", "cc_sessions_2");
|
|
12029
|
+
SESSION_DIR_32 = join24(homedir25(), ".synkro", "cc_sessions_3");
|
|
12030
|
+
SESSION_DIR_42 = join24(homedir25(), ".synkro", "cc_sessions_4");
|
|
12409
12031
|
PueueError = class extends Error {
|
|
12410
12032
|
constructor(message, cause) {
|
|
12411
12033
|
super(message);
|
|
@@ -12420,13 +12042,13 @@ var init_pueue = __esm({
|
|
|
12420
12042
|
});
|
|
12421
12043
|
|
|
12422
12044
|
// cli/local-cc/settings.ts
|
|
12423
|
-
import { existsSync as
|
|
12424
|
-
import { homedir as
|
|
12425
|
-
import { join as
|
|
12045
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
|
|
12046
|
+
import { homedir as homedir26 } from "os";
|
|
12047
|
+
import { join as join25 } from "path";
|
|
12426
12048
|
function isLocalCCEnabled() {
|
|
12427
|
-
if (!
|
|
12049
|
+
if (!existsSync27(CONFIG_PATH5)) return false;
|
|
12428
12050
|
try {
|
|
12429
|
-
const content =
|
|
12051
|
+
const content = readFileSync25(CONFIG_PATH5, "utf-8");
|
|
12430
12052
|
const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
|
|
12431
12053
|
return match?.[1] === "yes";
|
|
12432
12054
|
} catch {
|
|
@@ -12437,7 +12059,7 @@ var CONFIG_PATH5;
|
|
|
12437
12059
|
var init_settings = __esm({
|
|
12438
12060
|
"cli/local-cc/settings.ts"() {
|
|
12439
12061
|
"use strict";
|
|
12440
|
-
CONFIG_PATH5 =
|
|
12062
|
+
CONFIG_PATH5 = join25(homedir26(), ".synkro", "config.env");
|
|
12441
12063
|
}
|
|
12442
12064
|
});
|
|
12443
12065
|
|
|
@@ -12446,11 +12068,11 @@ var localCc_exports = {};
|
|
|
12446
12068
|
__export(localCc_exports, {
|
|
12447
12069
|
localCcCommand: () => localCcCommand
|
|
12448
12070
|
});
|
|
12449
|
-
import { spawnSync as
|
|
12450
|
-
import { homedir as
|
|
12451
|
-
import { join as
|
|
12071
|
+
import { spawnSync as spawnSync10 } from "child_process";
|
|
12072
|
+
import { homedir as homedir27 } from "os";
|
|
12073
|
+
import { join as join26 } from "path";
|
|
12452
12074
|
import { readFileSync as fsReadFileSync, existsSync as fsExistsSync } from "fs";
|
|
12453
|
-
import { existsSync as
|
|
12075
|
+
import { existsSync as existsSync28, readFileSync as readFileSync26, writeFileSync as writeFileSync19 } from "fs";
|
|
12454
12076
|
function deploymentMode() {
|
|
12455
12077
|
const env = (process.env.SYNKRO_DEPLOYMENT_MODE || "").toLowerCase();
|
|
12456
12078
|
if (env === "docker") return "docker";
|
|
@@ -12556,15 +12178,15 @@ TROUBLESHOOTING
|
|
|
12556
12178
|
`);
|
|
12557
12179
|
}
|
|
12558
12180
|
function readGatewayUrl() {
|
|
12559
|
-
if (
|
|
12560
|
-
const m =
|
|
12181
|
+
if (existsSync28(CONFIG_PATH6)) {
|
|
12182
|
+
const m = readFileSync26(CONFIG_PATH6, "utf-8").match(/^SYNKRO_GATEWAY_URL='([^']*)'/m);
|
|
12561
12183
|
if (m) return m[1];
|
|
12562
12184
|
}
|
|
12563
12185
|
return "https://api.synkro.sh";
|
|
12564
12186
|
}
|
|
12565
12187
|
function updateLocalInferenceFlag(enabled) {
|
|
12566
|
-
if (!
|
|
12567
|
-
let content =
|
|
12188
|
+
if (!existsSync28(CONFIG_PATH6)) return;
|
|
12189
|
+
let content = readFileSync26(CONFIG_PATH6, "utf-8");
|
|
12568
12190
|
const flag = enabled ? "yes" : "no";
|
|
12569
12191
|
if (content.includes("SYNKRO_LOCAL_INFERENCE=")) {
|
|
12570
12192
|
content = content.replace(/^SYNKRO_LOCAL_INFERENCE='[^']*'/m, `SYNKRO_LOCAL_INFERENCE='${flag}'`);
|
|
@@ -12573,7 +12195,7 @@ function updateLocalInferenceFlag(enabled) {
|
|
|
12573
12195
|
SYNKRO_LOCAL_INFERENCE='${flag}'
|
|
12574
12196
|
`;
|
|
12575
12197
|
}
|
|
12576
|
-
|
|
12198
|
+
writeFileSync19(CONFIG_PATH6, content, "utf-8");
|
|
12577
12199
|
}
|
|
12578
12200
|
async function setServerGradingProvider(provider) {
|
|
12579
12201
|
await ensureValidToken();
|
|
@@ -12627,7 +12249,7 @@ async function cmdStatus() {
|
|
|
12627
12249
|
}
|
|
12628
12250
|
const ch1Up = await isChannelAvailable();
|
|
12629
12251
|
console.log(`Channel 1 ${CHANNEL_HOST}:${CHANNEL_PORT}: ${ch1Up ? "reachable" : "unreachable"}`);
|
|
12630
|
-
const tmux1 =
|
|
12252
|
+
const tmux1 = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
|
|
12631
12253
|
console.log(`tmux '${TMUX_SESSION_NAME}': ${tmux1.status === 0 ? "live" : "absent"}`);
|
|
12632
12254
|
const t2 = findTask(CHANNEL_SECONDARY);
|
|
12633
12255
|
if (!t2) {
|
|
@@ -12637,7 +12259,7 @@ async function cmdStatus() {
|
|
|
12637
12259
|
}
|
|
12638
12260
|
const ch2Up = await isChannelAvailable(CHANNEL_2_PORT);
|
|
12639
12261
|
console.log(`Channel 2 ${CHANNEL_HOST}:${CHANNEL_2_PORT}: ${ch2Up ? "reachable" : "unreachable"}`);
|
|
12640
|
-
const tmux2 =
|
|
12262
|
+
const tmux2 = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME_2}`], { encoding: "utf-8" });
|
|
12641
12263
|
console.log(`tmux '${TMUX_SESSION_NAME_2}': ${tmux2.status === 0 ? "live" : "absent"}`);
|
|
12642
12264
|
}
|
|
12643
12265
|
async function cmdEnable() {
|
|
@@ -12845,7 +12467,7 @@ function cmdLogs(rest) {
|
|
|
12845
12467
|
}
|
|
12846
12468
|
return "200";
|
|
12847
12469
|
})();
|
|
12848
|
-
|
|
12470
|
+
spawnSync10("docker", ["logs", "--tail", tailArg, ...followFlag, "synkro-server"], { stdio: "inherit" });
|
|
12849
12471
|
return;
|
|
12850
12472
|
}
|
|
12851
12473
|
for (const arg of rest) {
|
|
@@ -12893,7 +12515,7 @@ function cmdLogs(rest) {
|
|
|
12893
12515
|
function cmdAttach(rest) {
|
|
12894
12516
|
assertTmuxInstalled();
|
|
12895
12517
|
const readonly = rest.some((a) => a === "--readonly" || a === "-r");
|
|
12896
|
-
const has =
|
|
12518
|
+
const has = spawnSync10("tmux", ["has-session", "-t", `=${TMUX_SESSION_NAME}`], { encoding: "utf-8" });
|
|
12897
12519
|
if (has.status !== 0) {
|
|
12898
12520
|
console.error(`No tmux session '${TMUX_SESSION_NAME}' running. Start it with: synkro local-cc start`);
|
|
12899
12521
|
process.exit(1);
|
|
@@ -12906,7 +12528,7 @@ function cmdAttach(rest) {
|
|
|
12906
12528
|
console.log("Detach with Ctrl-B then D. (Do not press Ctrl-C \u2014 that would interrupt claude.)");
|
|
12907
12529
|
console.log();
|
|
12908
12530
|
const args2 = readonly ? ["attach-session", "-r", "-t", TMUX_SESSION_NAME] : ["attach-session", "-t", TMUX_SESSION_NAME];
|
|
12909
|
-
const r =
|
|
12531
|
+
const r = spawnSync10("tmux", args2, { stdio: "inherit" });
|
|
12910
12532
|
process.exit(r.status ?? 0);
|
|
12911
12533
|
}
|
|
12912
12534
|
async function cmdTest() {
|
|
@@ -13007,8 +12629,8 @@ var init_localCc = __esm({
|
|
|
13007
12629
|
init_install();
|
|
13008
12630
|
init_client2();
|
|
13009
12631
|
init_stub();
|
|
13010
|
-
SYNKRO_CONFIG_PATH =
|
|
13011
|
-
CONFIG_PATH6 =
|
|
12632
|
+
SYNKRO_CONFIG_PATH = join26(homedir27(), ".synkro", "config.env");
|
|
12633
|
+
CONFIG_PATH6 = join26(homedir27(), ".synkro", "config.env");
|
|
13012
12634
|
}
|
|
13013
12635
|
});
|
|
13014
12636
|
|
|
@@ -13017,14 +12639,14 @@ var import_exports = {};
|
|
|
13017
12639
|
__export(import_exports, {
|
|
13018
12640
|
importCommand: () => importCommand
|
|
13019
12641
|
});
|
|
13020
|
-
import { existsSync as
|
|
13021
|
-
import { homedir as
|
|
13022
|
-
import { join as
|
|
12642
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6 } from "fs";
|
|
12643
|
+
import { homedir as homedir28 } from "os";
|
|
12644
|
+
import { join as join27 } from "path";
|
|
13023
12645
|
import { execSync as execSync6 } from "child_process";
|
|
13024
12646
|
import { createInterface as createInterface4 } from "readline";
|
|
13025
12647
|
function readMcpJwt() {
|
|
13026
12648
|
try {
|
|
13027
|
-
return
|
|
12649
|
+
return readFileSync27(join27(homedir28(), ".synkro", ".mcp-jwt"), "utf-8").trim();
|
|
13028
12650
|
} catch {
|
|
13029
12651
|
return "";
|
|
13030
12652
|
}
|
|
@@ -13032,7 +12654,7 @@ function readMcpJwt() {
|
|
|
13032
12654
|
function readConfigEnv2() {
|
|
13033
12655
|
const out = {};
|
|
13034
12656
|
try {
|
|
13035
|
-
for (const line of
|
|
12657
|
+
for (const line of readFileSync27(CONFIG_PATH7, "utf-8").split("\n")) {
|
|
13036
12658
|
const t = line.trim();
|
|
13037
12659
|
if (!t || t.startsWith("#")) continue;
|
|
13038
12660
|
const eq = t.indexOf("=");
|
|
@@ -13044,8 +12666,8 @@ function readConfigEnv2() {
|
|
|
13044
12666
|
}
|
|
13045
12667
|
function projectsFolder() {
|
|
13046
12668
|
const sanitized = process.cwd().replace(/\//g, "-");
|
|
13047
|
-
const dir =
|
|
13048
|
-
return
|
|
12669
|
+
const dir = join27(homedir28(), ".claude", "projects", sanitized);
|
|
12670
|
+
return existsSync29(dir) ? dir : null;
|
|
13049
12671
|
}
|
|
13050
12672
|
function repoName() {
|
|
13051
12673
|
try {
|
|
@@ -13084,7 +12706,7 @@ function extractToolResultText(content, e) {
|
|
|
13084
12706
|
return t;
|
|
13085
12707
|
}
|
|
13086
12708
|
function parseSession(filePath, sessionId) {
|
|
13087
|
-
const lines =
|
|
12709
|
+
const lines = readFileSync27(filePath, "utf-8").split("\n").filter(Boolean);
|
|
13088
12710
|
const messages = [];
|
|
13089
12711
|
const actions = [];
|
|
13090
12712
|
let step = 0;
|
|
@@ -13151,7 +12773,7 @@ async function importCommand() {
|
|
|
13151
12773
|
console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
|
|
13152
12774
|
return;
|
|
13153
12775
|
}
|
|
13154
|
-
const files =
|
|
12776
|
+
const files = readdirSync6(dir).filter((f) => f.endsWith(".jsonl"));
|
|
13155
12777
|
if (!files.length) {
|
|
13156
12778
|
console.log("No sessions to import.");
|
|
13157
12779
|
return;
|
|
@@ -13164,7 +12786,7 @@ async function importCommand() {
|
|
|
13164
12786
|
return;
|
|
13165
12787
|
}
|
|
13166
12788
|
}
|
|
13167
|
-
const sessions = files.map((f) => parseSession(
|
|
12789
|
+
const sessions = files.map((f) => parseSession(join27(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
|
|
13168
12790
|
const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
|
|
13169
12791
|
let ok = 0, fail = 0;
|
|
13170
12792
|
if (isCloud) {
|
|
@@ -13239,7 +12861,7 @@ var init_import = __esm({
|
|
|
13239
12861
|
"cli/commands/import.ts"() {
|
|
13240
12862
|
"use strict";
|
|
13241
12863
|
init_stub();
|
|
13242
|
-
CONFIG_PATH7 =
|
|
12864
|
+
CONFIG_PATH7 = join27(homedir28(), ".synkro", "config.env");
|
|
13243
12865
|
}
|
|
13244
12866
|
});
|
|
13245
12867
|
|
|
@@ -13281,10 +12903,10 @@ var init_packVerify = __esm({
|
|
|
13281
12903
|
});
|
|
13282
12904
|
|
|
13283
12905
|
// cli/installer/lockfile.ts
|
|
13284
|
-
import { existsSync as
|
|
13285
|
-
import { join as
|
|
12906
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync20 } from "fs";
|
|
12907
|
+
import { join as join28 } from "path";
|
|
13286
12908
|
function lockPath(repoRoot2) {
|
|
13287
|
-
return
|
|
12909
|
+
return join28(repoRoot2, LOCK_FILE);
|
|
13288
12910
|
}
|
|
13289
12911
|
function writeLockfile(repoRoot2, entries) {
|
|
13290
12912
|
const sorted = [...entries].sort((a, b) => a.ref.localeCompare(b.ref));
|
|
@@ -13302,7 +12924,7 @@ function writeLockfile(repoRoot2, entries) {
|
|
|
13302
12924
|
""
|
|
13303
12925
|
])
|
|
13304
12926
|
].join("\n");
|
|
13305
|
-
|
|
12927
|
+
writeFileSync20(lockPath(repoRoot2), body, "utf-8");
|
|
13306
12928
|
}
|
|
13307
12929
|
var LOCK_FILE;
|
|
13308
12930
|
var init_lockfile = __esm({
|
|
@@ -13317,9 +12939,9 @@ var sync_exports = {};
|
|
|
13317
12939
|
__export(sync_exports, {
|
|
13318
12940
|
syncCommand: () => syncCommand
|
|
13319
12941
|
});
|
|
13320
|
-
import { existsSync as
|
|
13321
|
-
import { homedir as
|
|
13322
|
-
import { join as
|
|
12942
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync18, readdirSync as readdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync21 } from "fs";
|
|
12943
|
+
import { homedir as homedir29 } from "os";
|
|
12944
|
+
import { join as join29 } from "path";
|
|
13323
12945
|
function cacheKey(ref, version) {
|
|
13324
12946
|
return ref.replace(/\//g, "__").replace(/[^\w.@-]/g, "_") + "@" + version + ".json";
|
|
13325
12947
|
}
|
|
@@ -13350,7 +12972,7 @@ async function syncCommand(_args = []) {
|
|
|
13350
12972
|
}
|
|
13351
12973
|
const gateway = (process.env.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
13352
12974
|
const cloud = process.env.SYNKRO_DEPLOY_LOCATION === "cloud";
|
|
13353
|
-
const cacheDir =
|
|
12975
|
+
const cacheDir = join29(homedir29(), ".synkro", "cache", "packs");
|
|
13354
12976
|
if (!cloud) mkdirSync18(cacheDir, { recursive: true });
|
|
13355
12977
|
console.log(`Syncing ${refs.length} standard(s) from the registry\u2026`);
|
|
13356
12978
|
const lock = [];
|
|
@@ -13379,7 +13001,7 @@ async function syncCommand(_args = []) {
|
|
|
13379
13001
|
if (!cloud) {
|
|
13380
13002
|
const fname = cacheKey(ref, data.version);
|
|
13381
13003
|
keptCacheFiles.add(fname);
|
|
13382
|
-
|
|
13004
|
+
writeFileSync21(join29(cacheDir, fname), JSON.stringify({
|
|
13383
13005
|
ref,
|
|
13384
13006
|
version: data.version,
|
|
13385
13007
|
digest: data.digest,
|
|
@@ -13391,11 +13013,11 @@ async function syncCommand(_args = []) {
|
|
|
13391
13013
|
const ruleCount = Array.isArray(pack.rules) ? pack.rules.length : 0;
|
|
13392
13014
|
console.log(` \u2713 ${ref}:${data.version} \u2014 verified (${ruleCount} rule${ruleCount === 1 ? "" : "s"})`);
|
|
13393
13015
|
}
|
|
13394
|
-
if (!cloud &&
|
|
13395
|
-
for (const f of
|
|
13016
|
+
if (!cloud && existsSync31(cacheDir)) {
|
|
13017
|
+
for (const f of readdirSync7(cacheDir)) {
|
|
13396
13018
|
if (f.endsWith(".json") && !keptCacheFiles.has(f)) {
|
|
13397
13019
|
try {
|
|
13398
|
-
rmSync4(
|
|
13020
|
+
rmSync4(join29(cacheDir, f));
|
|
13399
13021
|
} catch {
|
|
13400
13022
|
}
|
|
13401
13023
|
}
|
|
@@ -13424,13 +13046,13 @@ var whoami_exports = {};
|
|
|
13424
13046
|
__export(whoami_exports, {
|
|
13425
13047
|
whoamiCommand: () => whoamiCommand
|
|
13426
13048
|
});
|
|
13427
|
-
import { readFileSync as
|
|
13428
|
-
import { join as
|
|
13429
|
-
import { homedir as
|
|
13049
|
+
import { readFileSync as readFileSync29, existsSync as existsSync32 } from "fs";
|
|
13050
|
+
import { join as join30 } from "path";
|
|
13051
|
+
import { homedir as homedir30 } from "os";
|
|
13430
13052
|
function readConfigEnv3() {
|
|
13431
|
-
if (!
|
|
13053
|
+
if (!existsSync32(CONFIG_PATH8)) return {};
|
|
13432
13054
|
const out = {};
|
|
13433
|
-
for (const line of
|
|
13055
|
+
for (const line of readFileSync29(CONFIG_PATH8, "utf-8").split("\n")) {
|
|
13434
13056
|
const t = line.trim();
|
|
13435
13057
|
if (!t || t.startsWith("#")) continue;
|
|
13436
13058
|
const eq = t.indexOf("=");
|
|
@@ -13440,8 +13062,8 @@ function readConfigEnv3() {
|
|
|
13440
13062
|
}
|
|
13441
13063
|
function jwtStatus() {
|
|
13442
13064
|
try {
|
|
13443
|
-
if (!
|
|
13444
|
-
const jwt2 =
|
|
13065
|
+
if (!existsSync32(JWT_PATH2)) return { status: "none" };
|
|
13066
|
+
const jwt2 = readFileSync29(JWT_PATH2, "utf-8").trim();
|
|
13445
13067
|
if (!jwt2) return { status: "none" };
|
|
13446
13068
|
const payload = jwt2.split(".")[1];
|
|
13447
13069
|
if (!payload) return { status: "valid" };
|
|
@@ -13499,13 +13121,13 @@ async function whoamiCommand(args2 = []) {
|
|
|
13499
13121
|
console.log("synkro identity");
|
|
13500
13122
|
for (const [k, v] of rows) console.log(` ${k.padEnd(width)} ${v}`);
|
|
13501
13123
|
}
|
|
13502
|
-
var
|
|
13124
|
+
var SYNKRO_DIR13, CONFIG_PATH8, JWT_PATH2, GRADING_LABEL;
|
|
13503
13125
|
var init_whoami = __esm({
|
|
13504
13126
|
"cli/commands/whoami.ts"() {
|
|
13505
13127
|
"use strict";
|
|
13506
|
-
|
|
13507
|
-
CONFIG_PATH8 =
|
|
13508
|
-
JWT_PATH2 =
|
|
13128
|
+
SYNKRO_DIR13 = join30(homedir30(), ".synkro");
|
|
13129
|
+
CONFIG_PATH8 = join30(SYNKRO_DIR13, "config.env");
|
|
13130
|
+
JWT_PATH2 = join30(SYNKRO_DIR13, ".mcp-jwt");
|
|
13509
13131
|
GRADING_LABEL = {
|
|
13510
13132
|
local: "on-device worker pool",
|
|
13511
13133
|
cloud: "Synkro Cloud worker pool",
|
|
@@ -13550,12 +13172,12 @@ __export(linear_exports, {
|
|
|
13550
13172
|
formatLinks: () => formatLinks,
|
|
13551
13173
|
linearCommand: () => linearCommand
|
|
13552
13174
|
});
|
|
13553
|
-
import { readFileSync as
|
|
13554
|
-
import { homedir as
|
|
13555
|
-
import { join as
|
|
13175
|
+
import { readFileSync as readFileSync30 } from "fs";
|
|
13176
|
+
import { homedir as homedir31 } from "os";
|
|
13177
|
+
import { join as join31 } from "path";
|
|
13556
13178
|
function mcpJwt() {
|
|
13557
13179
|
try {
|
|
13558
|
-
return
|
|
13180
|
+
return readFileSync30(join31(SYNKRO_DIR14, ".mcp-jwt"), "utf-8").trim();
|
|
13559
13181
|
} catch {
|
|
13560
13182
|
return "";
|
|
13561
13183
|
}
|
|
@@ -13590,11 +13212,11 @@ async function linearCommand(_args = []) {
|
|
|
13590
13212
|
}
|
|
13591
13213
|
console.log(formatLinks(links));
|
|
13592
13214
|
}
|
|
13593
|
-
var
|
|
13215
|
+
var SYNKRO_DIR14, PORT2, BASE;
|
|
13594
13216
|
var init_linear = __esm({
|
|
13595
13217
|
"cli/commands/linear.ts"() {
|
|
13596
13218
|
"use strict";
|
|
13597
|
-
|
|
13219
|
+
SYNKRO_DIR14 = join31(homedir31(), ".synkro");
|
|
13598
13220
|
PORT2 = process.env.SYNKRO_MCP_PORT || "18931";
|
|
13599
13221
|
BASE = `http://127.0.0.1:${PORT2}`;
|
|
13600
13222
|
}
|
|
@@ -13602,7 +13224,7 @@ var init_linear = __esm({
|
|
|
13602
13224
|
|
|
13603
13225
|
// cli/scanning/cveReachability.ts
|
|
13604
13226
|
import { parse } from "@babel/parser";
|
|
13605
|
-
import { readFileSync as
|
|
13227
|
+
import { readFileSync as readFileSync31 } from "fs";
|
|
13606
13228
|
function walk(node, visit) {
|
|
13607
13229
|
if (!node || typeof node.type !== "string") return;
|
|
13608
13230
|
visit(node);
|
|
@@ -13743,10 +13365,10 @@ var init_cveReachability = __esm({
|
|
|
13743
13365
|
});
|
|
13744
13366
|
|
|
13745
13367
|
// cli/reachability/reachabilityScan.ts
|
|
13746
|
-
import { spawnSync as
|
|
13747
|
-
import { readFileSync as
|
|
13748
|
-
import { join as
|
|
13749
|
-
import { homedir as
|
|
13368
|
+
import { spawnSync as spawnSync11, execFileSync as execFileSync5 } from "child_process";
|
|
13369
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync22, existsSync as existsSync33, readdirSync as readdirSync8 } from "fs";
|
|
13370
|
+
import { join as join32 } from "path";
|
|
13371
|
+
import { homedir as homedir32 } from "os";
|
|
13750
13372
|
import { createRequire } from "module";
|
|
13751
13373
|
function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
13752
13374
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".turbo", "out", ".cache", ".synkro", ".claude", "vendor", "__tests__", "test-results"]);
|
|
@@ -13757,13 +13379,13 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
13757
13379
|
const dir = stack.pop();
|
|
13758
13380
|
let ents;
|
|
13759
13381
|
try {
|
|
13760
|
-
ents =
|
|
13382
|
+
ents = readdirSync8(dir, { withFileTypes: true });
|
|
13761
13383
|
} catch {
|
|
13762
13384
|
continue;
|
|
13763
13385
|
}
|
|
13764
13386
|
for (const e of ents) {
|
|
13765
13387
|
if (files.length >= maxFiles) break;
|
|
13766
|
-
const full =
|
|
13388
|
+
const full = join32(dir, e.name);
|
|
13767
13389
|
if (e.isDirectory()) {
|
|
13768
13390
|
if (!SKIP.has(e.name) && !e.name.startsWith(".")) stack.push(full);
|
|
13769
13391
|
continue;
|
|
@@ -13771,7 +13393,7 @@ function walkSourceFiles(repoRoot2, maxFiles = 4e3, maxBytes = 5e5) {
|
|
|
13771
13393
|
if (!EXT.test(e.name) || e.name.endsWith(".d.ts")) continue;
|
|
13772
13394
|
const rel = full.startsWith(repoRoot2 + "/") ? full.slice(repoRoot2.length + 1) : full;
|
|
13773
13395
|
try {
|
|
13774
|
-
const content =
|
|
13396
|
+
const content = readFileSync32(full, "utf8");
|
|
13775
13397
|
if (content.length <= maxBytes) files.push({ path: rel, content });
|
|
13776
13398
|
} catch {
|
|
13777
13399
|
}
|
|
@@ -13790,12 +13412,12 @@ function cleanVersion(spec) {
|
|
|
13790
13412
|
function gatherManifestVersions(repoRoot2) {
|
|
13791
13413
|
const out = {};
|
|
13792
13414
|
const dirs = [repoRoot2];
|
|
13793
|
-
const pkgsDir =
|
|
13794
|
-
if (
|
|
13415
|
+
const pkgsDir = join32(repoRoot2, "packages");
|
|
13416
|
+
if (existsSync33(pkgsDir)) {
|
|
13795
13417
|
try {
|
|
13796
|
-
for (const d of
|
|
13797
|
-
const pd =
|
|
13798
|
-
if (
|
|
13418
|
+
for (const d of readdirSync8(pkgsDir)) {
|
|
13419
|
+
const pd = join32(pkgsDir, d);
|
|
13420
|
+
if (existsSync33(join32(pd, "package.json"))) dirs.push(pd);
|
|
13799
13421
|
}
|
|
13800
13422
|
} catch {
|
|
13801
13423
|
}
|
|
@@ -13804,7 +13426,7 @@ function gatherManifestVersions(repoRoot2) {
|
|
|
13804
13426
|
for (const dir of dirs) {
|
|
13805
13427
|
let pkg;
|
|
13806
13428
|
try {
|
|
13807
|
-
pkg = JSON.parse(
|
|
13429
|
+
pkg = JSON.parse(readFileSync32(join32(dir, "package.json"), "utf8"));
|
|
13808
13430
|
} catch {
|
|
13809
13431
|
continue;
|
|
13810
13432
|
}
|
|
@@ -13824,28 +13446,28 @@ function findJelly(repoRoot2) {
|
|
|
13824
13446
|
try {
|
|
13825
13447
|
const pkgJson = require2.resolve("@cs-au-dk/jelly/package.json");
|
|
13826
13448
|
const dir = pkgJson.slice(0, pkgJson.length - "package.json".length);
|
|
13827
|
-
const pkg = JSON.parse(
|
|
13449
|
+
const pkg = JSON.parse(readFileSync32(pkgJson, "utf8"));
|
|
13828
13450
|
const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin && (pkg.bin.jelly || pkg.bin[Object.keys(pkg.bin)[0]]);
|
|
13829
13451
|
if (bin) {
|
|
13830
|
-
const p =
|
|
13831
|
-
if (
|
|
13452
|
+
const p = join32(dir, bin);
|
|
13453
|
+
if (existsSync33(p)) return p;
|
|
13832
13454
|
}
|
|
13833
13455
|
} catch {
|
|
13834
13456
|
}
|
|
13835
13457
|
for (const base of [repoRoot2, process.cwd()]) {
|
|
13836
|
-
const b =
|
|
13837
|
-
if (
|
|
13458
|
+
const b = join32(base, "node_modules", ".bin", "jelly");
|
|
13459
|
+
if (existsSync33(b)) return b;
|
|
13838
13460
|
}
|
|
13839
13461
|
return null;
|
|
13840
13462
|
}
|
|
13841
13463
|
function findEntries(repoRoot2) {
|
|
13842
13464
|
const dirs = [repoRoot2];
|
|
13843
|
-
const pkgsDir =
|
|
13844
|
-
if (
|
|
13465
|
+
const pkgsDir = join32(repoRoot2, "packages");
|
|
13466
|
+
if (existsSync33(pkgsDir)) {
|
|
13845
13467
|
try {
|
|
13846
|
-
for (const d of
|
|
13847
|
-
const pd =
|
|
13848
|
-
if (
|
|
13468
|
+
for (const d of readdirSync8(pkgsDir)) {
|
|
13469
|
+
const pd = join32(pkgsDir, d);
|
|
13470
|
+
if (existsSync33(join32(pd, "package.json"))) dirs.push(pd);
|
|
13849
13471
|
}
|
|
13850
13472
|
} catch {
|
|
13851
13473
|
}
|
|
@@ -13853,12 +13475,12 @@ function findEntries(repoRoot2) {
|
|
|
13853
13475
|
const entries = [];
|
|
13854
13476
|
for (const dir of dirs) {
|
|
13855
13477
|
try {
|
|
13856
|
-
const pkg = JSON.parse(
|
|
13478
|
+
const pkg = JSON.parse(readFileSync32(join32(dir, "package.json"), "utf8"));
|
|
13857
13479
|
const cands = [pkg.source, pkg.module, pkg.main, "src/index.ts", "src/index.js", "src/main.ts", "src/server.ts", "index.ts", "index.js"];
|
|
13858
13480
|
for (const c of cands) {
|
|
13859
13481
|
if (typeof c !== "string") continue;
|
|
13860
|
-
const f =
|
|
13861
|
-
if (
|
|
13482
|
+
const f = join32(dir, c);
|
|
13483
|
+
if (existsSync33(f)) {
|
|
13862
13484
|
entries.push(f);
|
|
13863
13485
|
break;
|
|
13864
13486
|
}
|
|
@@ -13891,9 +13513,9 @@ function parseApiUsage(log) {
|
|
|
13891
13513
|
}
|
|
13892
13514
|
function runReachabilityScan(repoRoot2, opts = {}) {
|
|
13893
13515
|
const commit = currentCommit(repoRoot2);
|
|
13894
|
-
if (!opts.force && commit &&
|
|
13516
|
+
if (!opts.force && commit && existsSync33(REACHABILITY_PATH)) {
|
|
13895
13517
|
try {
|
|
13896
|
-
const prev = JSON.parse(
|
|
13518
|
+
const prev = JSON.parse(readFileSync32(REACHABILITY_PATH, "utf8"));
|
|
13897
13519
|
if (prev.commit === commit) return { ok: true, cached: true, packages: Object.keys(prev.packages || {}).length };
|
|
13898
13520
|
} catch {
|
|
13899
13521
|
}
|
|
@@ -13944,7 +13566,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
|
|
|
13944
13566
|
if (jelly) {
|
|
13945
13567
|
const entries = findEntries(repoRoot2);
|
|
13946
13568
|
if (entries.length > 0) {
|
|
13947
|
-
const r =
|
|
13569
|
+
const r = spawnSync11(
|
|
13948
13570
|
process.execPath,
|
|
13949
13571
|
[jelly, "-b", repoRoot2, "--api-usage", ...entries],
|
|
13950
13572
|
{ encoding: "utf8", timeout: opts.timeoutMs ?? 18e4, maxBuffer: 2e8 }
|
|
@@ -13982,7 +13604,7 @@ function runReachabilityScan(repoRoot2, opts = {}) {
|
|
|
13982
13604
|
if (Object.keys(packages).length === 0) return { ok: false, reason: "no package usage found (no jelly output, no AST imports)" };
|
|
13983
13605
|
const file = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), commit, tool, packages, versions: gatherManifestVersions(repoRoot2) };
|
|
13984
13606
|
try {
|
|
13985
|
-
|
|
13607
|
+
writeFileSync22(REACHABILITY_PATH, JSON.stringify(file, null, 2));
|
|
13986
13608
|
} catch (e) {
|
|
13987
13609
|
return { ok: false, reason: "write failed: " + String(e.message || e) };
|
|
13988
13610
|
}
|
|
@@ -13994,7 +13616,7 @@ var init_reachabilityScan = __esm({
|
|
|
13994
13616
|
"use strict";
|
|
13995
13617
|
init_cveReachability();
|
|
13996
13618
|
require2 = createRequire(import.meta.url);
|
|
13997
|
-
REACHABILITY_PATH =
|
|
13619
|
+
REACHABILITY_PATH = join32(homedir32(), ".synkro", "reachability.json");
|
|
13998
13620
|
}
|
|
13999
13621
|
});
|
|
14000
13622
|
|
|
@@ -14003,15 +13625,15 @@ var reachabilityScan_exports = {};
|
|
|
14003
13625
|
__export(reachabilityScan_exports, {
|
|
14004
13626
|
reachabilityScanCommand: () => reachabilityScanCommand
|
|
14005
13627
|
});
|
|
14006
|
-
import { readFileSync as
|
|
14007
|
-
import { join as
|
|
14008
|
-
import { homedir as
|
|
13628
|
+
import { readFileSync as readFileSync33, existsSync as existsSync34 } from "fs";
|
|
13629
|
+
import { join as join33 } from "path";
|
|
13630
|
+
import { homedir as homedir33 } from "os";
|
|
14009
13631
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
14010
13632
|
function readConfigEnv4() {
|
|
14011
|
-
const p =
|
|
14012
|
-
if (!
|
|
13633
|
+
const p = join33(SYNKRO_DIR15, "config.env");
|
|
13634
|
+
if (!existsSync34(p)) return {};
|
|
14013
13635
|
const out = {};
|
|
14014
|
-
for (const line of
|
|
13636
|
+
for (const line of readFileSync33(p, "utf-8").split("\n")) {
|
|
14015
13637
|
const t = line.trim();
|
|
14016
13638
|
if (!t || t.startsWith("#")) continue;
|
|
14017
13639
|
const eq = t.indexOf("=");
|
|
@@ -14043,11 +13665,11 @@ async function pushToCloud(cfg, repo) {
|
|
|
14043
13665
|
while (gwBase.endsWith("/")) gwBase = gwBase.slice(0, -1);
|
|
14044
13666
|
let jwt2 = "";
|
|
14045
13667
|
try {
|
|
14046
|
-
jwt2 =
|
|
13668
|
+
jwt2 = readFileSync33(join33(SYNKRO_DIR15, ".mcp-jwt"), "utf-8").trim();
|
|
14047
13669
|
} catch {
|
|
14048
13670
|
}
|
|
14049
|
-
if (!jwt2 || !
|
|
14050
|
-
const body =
|
|
13671
|
+
if (!jwt2 || !existsSync34(REACHABILITY_PATH)) return;
|
|
13672
|
+
const body = readFileSync33(REACHABILITY_PATH, "utf-8");
|
|
14051
13673
|
try {
|
|
14052
13674
|
const resp = await fetch(gwBase + "/api/v1/reachability?repo=" + encodeURIComponent(repo), {
|
|
14053
13675
|
method: "POST",
|
|
@@ -14074,12 +13696,12 @@ async function reachabilityScanCommand(args2 = []) {
|
|
|
14074
13696
|
const isCloud = cfg.SYNKRO_DEPLOY_LOCATION === "cloud" || cfg.SYNKRO_STORAGE_MODE === "cloud";
|
|
14075
13697
|
if (isCloud) await pushToCloud(cfg, cfg.SYNKRO_CONNECTED_REPO || repoSlug(root));
|
|
14076
13698
|
}
|
|
14077
|
-
var
|
|
13699
|
+
var SYNKRO_DIR15;
|
|
14078
13700
|
var init_reachabilityScan2 = __esm({
|
|
14079
13701
|
"cli/commands/reachabilityScan.ts"() {
|
|
14080
13702
|
"use strict";
|
|
14081
13703
|
init_reachabilityScan();
|
|
14082
|
-
|
|
13704
|
+
SYNKRO_DIR15 = join33(homedir33(), ".synkro");
|
|
14083
13705
|
}
|
|
14084
13706
|
});
|
|
14085
13707
|
|
|
@@ -14209,13 +13831,13 @@ var config_exports = {};
|
|
|
14209
13831
|
__export(config_exports, {
|
|
14210
13832
|
configCommand: () => configCommand
|
|
14211
13833
|
});
|
|
14212
|
-
import { readFileSync as
|
|
14213
|
-
import { join as
|
|
14214
|
-
import { homedir as
|
|
13834
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync23, existsSync as existsSync35 } from "fs";
|
|
13835
|
+
import { join as join34 } from "path";
|
|
13836
|
+
import { homedir as homedir34 } from "os";
|
|
14215
13837
|
function readConfigEnv5() {
|
|
14216
|
-
if (!
|
|
13838
|
+
if (!existsSync35(CONFIG_PATH9)) return {};
|
|
14217
13839
|
const out = {};
|
|
14218
|
-
for (const line of
|
|
13840
|
+
for (const line of readFileSync34(CONFIG_PATH9, "utf-8").split("\n")) {
|
|
14219
13841
|
const t = line.trim();
|
|
14220
13842
|
if (!t || t.startsWith("#")) continue;
|
|
14221
13843
|
const eq = t.indexOf("=");
|
|
@@ -14224,11 +13846,11 @@ function readConfigEnv5() {
|
|
|
14224
13846
|
return out;
|
|
14225
13847
|
}
|
|
14226
13848
|
function updateConfigValue(key, value) {
|
|
14227
|
-
if (!
|
|
13849
|
+
if (!existsSync35(CONFIG_PATH9)) {
|
|
14228
13850
|
console.error("No config found. Run `synkro install` first.");
|
|
14229
13851
|
process.exit(1);
|
|
14230
13852
|
}
|
|
14231
|
-
const lines =
|
|
13853
|
+
const lines = readFileSync34(CONFIG_PATH9, "utf-8").split("\n");
|
|
14232
13854
|
const pattern = new RegExp(`^${key}=`);
|
|
14233
13855
|
let found = false;
|
|
14234
13856
|
const updated = lines.map((line) => {
|
|
@@ -14239,7 +13861,7 @@ function updateConfigValue(key, value) {
|
|
|
14239
13861
|
return line;
|
|
14240
13862
|
});
|
|
14241
13863
|
if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
|
|
14242
|
-
|
|
13864
|
+
writeFileSync23(CONFIG_PATH9, updated.join("\n"), "utf-8");
|
|
14243
13865
|
}
|
|
14244
13866
|
function resolveInferenceMode(cfg) {
|
|
14245
13867
|
if ((cfg.SYNKRO_GRADING_MODE || "local") === "byok") return "byok";
|
|
@@ -14391,14 +14013,14 @@ To change:`);
|
|
|
14391
14013
|
}
|
|
14392
14014
|
if (inferenceValue !== "cloud") await reconcileContainer();
|
|
14393
14015
|
}
|
|
14394
|
-
var
|
|
14016
|
+
var SYNKRO_DIR16, CONFIG_PATH9;
|
|
14395
14017
|
var init_config = __esm({
|
|
14396
14018
|
"cli/commands/config.ts"() {
|
|
14397
14019
|
"use strict";
|
|
14398
14020
|
init_stub();
|
|
14399
14021
|
init_optout();
|
|
14400
|
-
|
|
14401
|
-
CONFIG_PATH9 =
|
|
14022
|
+
SYNKRO_DIR16 = join34(homedir34(), ".synkro");
|
|
14023
|
+
CONFIG_PATH9 = join34(SYNKRO_DIR16, "config.env");
|
|
14402
14024
|
}
|
|
14403
14025
|
});
|
|
14404
14026
|
|
|
@@ -14587,14 +14209,14 @@ Usage:
|
|
|
14587
14209
|
});
|
|
14588
14210
|
|
|
14589
14211
|
// cli/bootstrap.js
|
|
14590
|
-
import { readFileSync as
|
|
14212
|
+
import { readFileSync as readFileSync35, existsSync as existsSync36 } from "fs";
|
|
14591
14213
|
import { resolve as resolve5 } from "path";
|
|
14592
14214
|
var envCandidates = [
|
|
14593
14215
|
resolve5(process.env.HOME ?? "", ".synkro", "config.env")
|
|
14594
14216
|
];
|
|
14595
14217
|
for (const envPath of envCandidates) {
|
|
14596
|
-
if (!
|
|
14597
|
-
const envContent =
|
|
14218
|
+
if (!existsSync36(envPath)) continue;
|
|
14219
|
+
const envContent = readFileSync35(envPath, "utf-8");
|
|
14598
14220
|
for (const line of envContent.split("\n")) {
|
|
14599
14221
|
const trimmed = line.trim();
|
|
14600
14222
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -14611,7 +14233,7 @@ var subArgs = args.slice(1);
|
|
|
14611
14233
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
14612
14234
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
14613
14235
|
function printVersion() {
|
|
14614
|
-
console.log("1.7.
|
|
14236
|
+
console.log("1.7.93");
|
|
14615
14237
|
}
|
|
14616
14238
|
function printHelp2() {
|
|
14617
14239
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|
|
@@ -14703,79 +14325,17 @@ async function main() {
|
|
|
14703
14325
|
await scanPrCommand2();
|
|
14704
14326
|
break;
|
|
14705
14327
|
}
|
|
14706
|
-
case "route":
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
if (model) {
|
|
14711
|
-
try {
|
|
14712
|
-
injectModel2(model, sessionOverride);
|
|
14713
|
-
} catch {
|
|
14714
|
-
}
|
|
14715
|
-
}
|
|
14716
|
-
break;
|
|
14717
|
-
}
|
|
14718
|
-
case "route-decide": {
|
|
14719
|
-
const { routeDecide: routeDecide2 } = await Promise.resolve().then(() => (init_routeDecide(), routeDecide_exports));
|
|
14720
|
-
const sid = (subArgs[0] || "").trim();
|
|
14721
|
-
if (sid) {
|
|
14722
|
-
try {
|
|
14723
|
-
await routeDecide2(sid);
|
|
14724
|
-
} catch {
|
|
14725
|
-
}
|
|
14726
|
-
}
|
|
14727
|
-
break;
|
|
14728
|
-
}
|
|
14729
|
-
case "route-and-resubmit": {
|
|
14730
|
-
const { routeAndResubmit: routeAndResubmit2 } = await Promise.resolve().then(() => (init_routeOrchestrate(), routeOrchestrate_exports));
|
|
14731
|
-
const sid = (subArgs[0] || "").trim();
|
|
14732
|
-
const task = (subArgs[1] || "").trim();
|
|
14733
|
-
const tmuxSession = (subArgs[2] || "").trim() || void 0;
|
|
14734
|
-
const forceModel = (subArgs[3] || "").trim() || void 0;
|
|
14735
|
-
if (sid && task) {
|
|
14736
|
-
try {
|
|
14737
|
-
await routeAndResubmit2(sid, task, tmuxSession, forceModel);
|
|
14738
|
-
} catch {
|
|
14739
|
-
}
|
|
14740
|
-
}
|
|
14741
|
-
break;
|
|
14742
|
-
}
|
|
14743
|
-
case "routing": {
|
|
14744
|
-
const { routingCommand: routingCommand2 } = await Promise.resolve().then(() => (init_routingToggle(), routingToggle_exports));
|
|
14745
|
-
routingCommand2(subArgs);
|
|
14746
|
-
break;
|
|
14747
|
-
}
|
|
14328
|
+
case "route":
|
|
14329
|
+
case "route-decide":
|
|
14330
|
+
case "route-and-resubmit":
|
|
14331
|
+
case "routing":
|
|
14748
14332
|
case "sessions": {
|
|
14749
|
-
|
|
14750
|
-
const rows = listSessions2({ liveOnly: !subArgs.includes("--all") });
|
|
14751
|
-
if (rows.length === 0) {
|
|
14752
|
-
console.log("No active Synkro-wrapped Claude Code sessions.");
|
|
14753
|
-
console.log("(Start one with `claude`; it registers on your first prompt.)");
|
|
14754
|
-
break;
|
|
14755
|
-
}
|
|
14756
|
-
const now = Date.now();
|
|
14757
|
-
const age = (ts) => {
|
|
14758
|
-
const s = Math.max(0, Math.round((now - (ts || 0)) / 1e3));
|
|
14759
|
-
if (s < 60) return `${s}s`;
|
|
14760
|
-
if (s < 3600) return `${Math.round(s / 60)}m`;
|
|
14761
|
-
return `${Math.round(s / 3600)}h`;
|
|
14762
|
-
};
|
|
14763
|
-
console.log(`${rows.length} session${rows.length === 1 ? "" : "s"}:
|
|
14764
|
-
`);
|
|
14765
|
-
for (const r of rows) {
|
|
14766
|
-
console.log(` ${r.session_id} (${age(r.ts)} ago)`);
|
|
14767
|
-
if (r.cwd) console.log(` cwd: ${r.cwd}`);
|
|
14768
|
-
if (r.last_prompt) console.log(` prompt: ${r.last_prompt.replace(/\s+/g, " ").slice(0, 80)}`);
|
|
14769
|
-
console.log(` route: echo <model> > ~/.synkro/pty/route-${r.session_id}`);
|
|
14770
|
-
console.log();
|
|
14771
|
-
}
|
|
14333
|
+
console.error("Synkro host model routing has been removed.");
|
|
14772
14334
|
break;
|
|
14773
14335
|
}
|
|
14774
14336
|
case "pty-shim": {
|
|
14775
|
-
const {
|
|
14776
|
-
|
|
14777
|
-
if (sub === "uninstall" || sub === "remove") uninstallPtyShim2();
|
|
14778
|
-
else installPtyShim2();
|
|
14337
|
+
const { uninstallPtyShim: uninstallPtyShim2 } = await Promise.resolve().then(() => (init_ptyShim(), ptyShim_exports));
|
|
14338
|
+
uninstallPtyShim2();
|
|
14779
14339
|
break;
|
|
14780
14340
|
}
|
|
14781
14341
|
case "local-cc": {
|