@synkro-sh/cli 1.4.1 → 1.4.3
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 +366 -359
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -3906,15 +3906,278 @@ var init_pueue = __esm({
|
|
|
3906
3906
|
}
|
|
3907
3907
|
});
|
|
3908
3908
|
|
|
3909
|
+
// cli/local-cc/prompts.ts
|
|
3910
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
3911
|
+
import { homedir as homedir8 } from "os";
|
|
3912
|
+
import { join as join9 } from "path";
|
|
3913
|
+
function loadCachedPrompts() {
|
|
3914
|
+
if (_cached) return _cached;
|
|
3915
|
+
if (!existsSync9(CACHE_PATH2)) {
|
|
3916
|
+
throw new Error("Prompts cache not found. Run `synkro install` or `synkro update` first.");
|
|
3917
|
+
}
|
|
3918
|
+
try {
|
|
3919
|
+
_cached = JSON.parse(readFileSync7(CACHE_PATH2, "utf-8"));
|
|
3920
|
+
return _cached;
|
|
3921
|
+
} catch {
|
|
3922
|
+
throw new Error("Prompts cache is corrupted. Run `synkro update` to refresh.");
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
function getPrimer(role) {
|
|
3926
|
+
const cache = loadCachedPrompts();
|
|
3927
|
+
const primer = role === "grade-edit" ? cache.grader_primer_edit : cache.grader_primer_bash;
|
|
3928
|
+
if (!primer) {
|
|
3929
|
+
throw new Error(`No cached primer for role "${role}". Run \`synkro update\` to refresh prompts.`);
|
|
3930
|
+
}
|
|
3931
|
+
return primer;
|
|
3932
|
+
}
|
|
3933
|
+
function buildChannelContent(role, payload) {
|
|
3934
|
+
return `${getPrimer(role)}
|
|
3935
|
+
|
|
3936
|
+
---
|
|
3937
|
+
PAYLOAD (the input to evaluate):
|
|
3938
|
+
|
|
3939
|
+
${payload}`;
|
|
3940
|
+
}
|
|
3941
|
+
var CACHE_PATH2, _cached;
|
|
3942
|
+
var init_prompts = __esm({
|
|
3943
|
+
"cli/local-cc/prompts.ts"() {
|
|
3944
|
+
"use strict";
|
|
3945
|
+
CACHE_PATH2 = join9(homedir8(), ".synkro", "prompts", "judge-prompts.json");
|
|
3946
|
+
_cached = null;
|
|
3947
|
+
}
|
|
3948
|
+
});
|
|
3949
|
+
|
|
3950
|
+
// cli/local-cc/turnLog.ts
|
|
3951
|
+
import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync8, readSync, closeSync as closeSync2, statSync, watchFile, unwatchFile } from "fs";
|
|
3952
|
+
import { dirname as dirname4, join as join10 } from "path";
|
|
3953
|
+
import { homedir as homedir9 } from "os";
|
|
3954
|
+
function truncate(s, max = PREVIEW_MAX) {
|
|
3955
|
+
if (s.length <= max) return s;
|
|
3956
|
+
return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
|
|
3957
|
+
}
|
|
3958
|
+
function extractSeverity(result) {
|
|
3959
|
+
const m = result.match(/<synkro-(?:verdict|intent)>([\s\S]*?)<\/synkro-(?:verdict|intent)>/);
|
|
3960
|
+
if (!m) return void 0;
|
|
3961
|
+
try {
|
|
3962
|
+
const obj = JSON.parse(m[1]);
|
|
3963
|
+
if (obj.severity) return String(obj.severity);
|
|
3964
|
+
if (typeof obj.ok === "boolean") return obj.ok ? "ok" : "violations";
|
|
3965
|
+
if (obj.type) return String(obj.type);
|
|
3966
|
+
if (obj.verdict) return String(obj.verdict);
|
|
3967
|
+
} catch {
|
|
3968
|
+
}
|
|
3969
|
+
return void 0;
|
|
3970
|
+
}
|
|
3971
|
+
function appendTurn(args2) {
|
|
3972
|
+
try {
|
|
3973
|
+
mkdirSync7(dirname4(TURN_LOG_PATH), { recursive: true });
|
|
3974
|
+
const entry = {
|
|
3975
|
+
ts: new Date(args2.startedAt).toISOString(),
|
|
3976
|
+
role: args2.role,
|
|
3977
|
+
duration_ms: Date.now() - args2.startedAt,
|
|
3978
|
+
status: args2.status,
|
|
3979
|
+
request_preview: truncate(args2.request),
|
|
3980
|
+
response_preview: args2.result ? truncate(args2.result) : "",
|
|
3981
|
+
severity: args2.result ? extractSeverity(args2.result) : void 0,
|
|
3982
|
+
error: args2.error
|
|
3983
|
+
};
|
|
3984
|
+
appendFileSync(TURN_LOG_PATH, JSON.stringify(entry) + "\n", "utf-8");
|
|
3985
|
+
} catch {
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
function readRecentTurns(n = 20) {
|
|
3989
|
+
if (!existsSync10(TURN_LOG_PATH)) return [];
|
|
3990
|
+
try {
|
|
3991
|
+
const size = statSync(TURN_LOG_PATH).size;
|
|
3992
|
+
if (size === 0) return [];
|
|
3993
|
+
const text = readFileSync8(TURN_LOG_PATH, "utf-8");
|
|
3994
|
+
const lines = text.split("\n").filter(Boolean);
|
|
3995
|
+
const lastN = lines.slice(-n).reverse();
|
|
3996
|
+
return lastN.map((line) => {
|
|
3997
|
+
try {
|
|
3998
|
+
return JSON.parse(line);
|
|
3999
|
+
} catch {
|
|
4000
|
+
return null;
|
|
4001
|
+
}
|
|
4002
|
+
}).filter((x) => x !== null);
|
|
4003
|
+
} catch {
|
|
4004
|
+
return [];
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
function followTurns(onEntry) {
|
|
4008
|
+
try {
|
|
4009
|
+
mkdirSync7(dirname4(TURN_LOG_PATH), { recursive: true });
|
|
4010
|
+
if (!existsSync10(TURN_LOG_PATH)) {
|
|
4011
|
+
appendFileSync(TURN_LOG_PATH, "", "utf-8");
|
|
4012
|
+
}
|
|
4013
|
+
} catch {
|
|
4014
|
+
}
|
|
4015
|
+
let lastSize = (() => {
|
|
4016
|
+
try {
|
|
4017
|
+
return statSync(TURN_LOG_PATH).size;
|
|
4018
|
+
} catch {
|
|
4019
|
+
return 0;
|
|
4020
|
+
}
|
|
4021
|
+
})();
|
|
4022
|
+
let pendingPartial = "";
|
|
4023
|
+
const drainNewBytes = (from, to) => {
|
|
4024
|
+
if (to <= from) return;
|
|
4025
|
+
let fd = null;
|
|
4026
|
+
try {
|
|
4027
|
+
fd = openSync2(TURN_LOG_PATH, "r");
|
|
4028
|
+
const len = to - from;
|
|
4029
|
+
const buf = Buffer.alloc(len);
|
|
4030
|
+
readSync(fd, buf, 0, len, from);
|
|
4031
|
+
const text = pendingPartial + buf.toString("utf-8");
|
|
4032
|
+
const lastNewline = text.lastIndexOf("\n");
|
|
4033
|
+
if (lastNewline === -1) {
|
|
4034
|
+
pendingPartial = text;
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
const complete = text.slice(0, lastNewline);
|
|
4038
|
+
pendingPartial = text.slice(lastNewline + 1);
|
|
4039
|
+
for (const line of complete.split("\n")) {
|
|
4040
|
+
if (!line) continue;
|
|
4041
|
+
try {
|
|
4042
|
+
onEntry(JSON.parse(line));
|
|
4043
|
+
} catch {
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
} catch {
|
|
4047
|
+
} finally {
|
|
4048
|
+
if (fd !== null) {
|
|
4049
|
+
try {
|
|
4050
|
+
closeSync2(fd);
|
|
4051
|
+
} catch {
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
};
|
|
4056
|
+
watchFile(TURN_LOG_PATH, { interval: 250 }, (curr, prev) => {
|
|
4057
|
+
if (curr.size < lastSize) {
|
|
4058
|
+
lastSize = 0;
|
|
4059
|
+
pendingPartial = "";
|
|
4060
|
+
}
|
|
4061
|
+
if (curr.size > lastSize) {
|
|
4062
|
+
drainNewBytes(lastSize, curr.size);
|
|
4063
|
+
lastSize = curr.size;
|
|
4064
|
+
}
|
|
4065
|
+
});
|
|
4066
|
+
return () => unwatchFile(TURN_LOG_PATH);
|
|
4067
|
+
}
|
|
4068
|
+
var TURN_LOG_PATH, PREVIEW_MAX;
|
|
4069
|
+
var init_turnLog = __esm({
|
|
4070
|
+
"cli/local-cc/turnLog.ts"() {
|
|
4071
|
+
"use strict";
|
|
4072
|
+
TURN_LOG_PATH = join10(homedir9(), ".synkro", "cc_sessions", "turns.log");
|
|
4073
|
+
PREVIEW_MAX = 400;
|
|
4074
|
+
}
|
|
4075
|
+
});
|
|
4076
|
+
|
|
4077
|
+
// cli/local-cc/client.ts
|
|
4078
|
+
import { request as httpRequest } from "http";
|
|
4079
|
+
import { connect as connect2 } from "net";
|
|
4080
|
+
async function submitToChannel(role, payload, opts = {}) {
|
|
4081
|
+
const content = buildChannelContent(role, payload);
|
|
4082
|
+
const body = JSON.stringify({ role, content });
|
|
4083
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
4084
|
+
const startedAt = Date.now();
|
|
4085
|
+
try {
|
|
4086
|
+
const result = await new Promise((resolve2, reject) => {
|
|
4087
|
+
const req = httpRequest({
|
|
4088
|
+
host: CHANNEL_HOST,
|
|
4089
|
+
port: CHANNEL_PORT,
|
|
4090
|
+
method: "POST",
|
|
4091
|
+
path: "/submit",
|
|
4092
|
+
headers: {
|
|
4093
|
+
"Content-Type": "application/json",
|
|
4094
|
+
"Content-Length": Buffer.byteLength(body)
|
|
4095
|
+
},
|
|
4096
|
+
timeout: timeoutMs
|
|
4097
|
+
}, (res) => {
|
|
4098
|
+
const chunks = [];
|
|
4099
|
+
res.on("data", (c) => chunks.push(c));
|
|
4100
|
+
res.on("end", () => {
|
|
4101
|
+
const text = Buffer.concat(chunks).toString("utf-8");
|
|
4102
|
+
if (res.statusCode !== 200) {
|
|
4103
|
+
reject(new LocalCCError(`channel returned ${res.statusCode}: ${text.slice(0, 500)}`));
|
|
4104
|
+
return;
|
|
4105
|
+
}
|
|
4106
|
+
try {
|
|
4107
|
+
const parsed = JSON.parse(text);
|
|
4108
|
+
if (parsed.error) {
|
|
4109
|
+
reject(new LocalCCError(parsed.error));
|
|
4110
|
+
return;
|
|
4111
|
+
}
|
|
4112
|
+
resolve2(String(parsed.result ?? ""));
|
|
4113
|
+
} catch (err) {
|
|
4114
|
+
reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
|
|
4115
|
+
}
|
|
4116
|
+
});
|
|
4117
|
+
});
|
|
4118
|
+
req.on("timeout", () => {
|
|
4119
|
+
req.destroy(new LocalCCError(`channel request timed out after ${timeoutMs}ms`));
|
|
4120
|
+
});
|
|
4121
|
+
req.on("error", (err) => {
|
|
4122
|
+
const msg = err.code === "ECONNREFUSED" ? `channel connection refused at ${CHANNEL_HOST}:${CHANNEL_PORT} (is the pueue task running?)` : `channel request failed: ${err.message}`;
|
|
4123
|
+
reject(new LocalCCError(msg, err));
|
|
4124
|
+
});
|
|
4125
|
+
req.write(body);
|
|
4126
|
+
req.end();
|
|
4127
|
+
});
|
|
4128
|
+
appendTurn({ startedAt, role, request: payload, result, status: "ok" });
|
|
4129
|
+
return result;
|
|
4130
|
+
} catch (err) {
|
|
4131
|
+
const message = err.message ?? String(err);
|
|
4132
|
+
const status = /timed out/i.test(message) ? "timeout" : "error";
|
|
4133
|
+
appendTurn({ startedAt, role, request: payload, status, error: message });
|
|
4134
|
+
throw err;
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
function isChannelAvailable(timeoutMs = 500) {
|
|
4138
|
+
return new Promise((resolve2) => {
|
|
4139
|
+
const sock = connect2(CHANNEL_PORT, CHANNEL_HOST);
|
|
4140
|
+
const done = (ok) => {
|
|
4141
|
+
try {
|
|
4142
|
+
sock.destroy();
|
|
4143
|
+
} catch {
|
|
4144
|
+
}
|
|
4145
|
+
resolve2(ok);
|
|
4146
|
+
};
|
|
4147
|
+
sock.once("connect", () => done(true));
|
|
4148
|
+
sock.once("error", () => done(false));
|
|
4149
|
+
sock.setTimeout(timeoutMs, () => done(false));
|
|
4150
|
+
});
|
|
4151
|
+
}
|
|
4152
|
+
var CHANNEL_HOST, CHANNEL_PORT, DEFAULT_TIMEOUT_MS, LocalCCError;
|
|
4153
|
+
var init_client = __esm({
|
|
4154
|
+
"cli/local-cc/client.ts"() {
|
|
4155
|
+
"use strict";
|
|
4156
|
+
init_prompts();
|
|
4157
|
+
init_turnLog();
|
|
4158
|
+
CHANNEL_HOST = "127.0.0.1";
|
|
4159
|
+
CHANNEL_PORT = parseInt(process.env.SYNKRO_CHANNEL_PORT || "8929", 10);
|
|
4160
|
+
DEFAULT_TIMEOUT_MS = 9e4;
|
|
4161
|
+
LocalCCError = class extends Error {
|
|
4162
|
+
constructor(message, cause) {
|
|
4163
|
+
super(message);
|
|
4164
|
+
this.cause = cause;
|
|
4165
|
+
this.name = "LocalCCError";
|
|
4166
|
+
}
|
|
4167
|
+
cause;
|
|
4168
|
+
};
|
|
4169
|
+
}
|
|
4170
|
+
});
|
|
4171
|
+
|
|
3909
4172
|
// cli/commands/install.ts
|
|
3910
4173
|
var install_exports = {};
|
|
3911
4174
|
__export(install_exports, {
|
|
3912
4175
|
installCommand: () => installCommand,
|
|
3913
4176
|
parseArgs: () => parseArgs
|
|
3914
4177
|
});
|
|
3915
|
-
import { existsSync as
|
|
3916
|
-
import { homedir as
|
|
3917
|
-
import { join as
|
|
4178
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, chmodSync as chmodSync2, readFileSync as readFileSync9, readdirSync } from "fs";
|
|
4179
|
+
import { homedir as homedir10 } from "os";
|
|
4180
|
+
import { join as join11 } from "path";
|
|
3918
4181
|
import { execSync as execSync5 } from "child_process";
|
|
3919
4182
|
import { createInterface as createInterface3 } from "readline";
|
|
3920
4183
|
function sanitizeGatewayCandidate(raw) {
|
|
@@ -3951,19 +4214,19 @@ async function promptTranscriptConsent() {
|
|
|
3951
4214
|
});
|
|
3952
4215
|
}
|
|
3953
4216
|
function ensureSynkroDir() {
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
4217
|
+
mkdirSync8(SYNKRO_DIR2, { recursive: true });
|
|
4218
|
+
mkdirSync8(HOOKS_DIR, { recursive: true });
|
|
4219
|
+
mkdirSync8(BIN_DIR, { recursive: true });
|
|
4220
|
+
mkdirSync8(OFFSETS_DIR, { recursive: true });
|
|
3958
4221
|
}
|
|
3959
4222
|
function writeHookScripts() {
|
|
3960
|
-
const bashScriptPath =
|
|
3961
|
-
const bashFollowupScriptPath =
|
|
3962
|
-
const editCaptureScriptPath =
|
|
3963
|
-
const editPrecheckScriptPath =
|
|
3964
|
-
const stopSummaryScriptPath =
|
|
3965
|
-
const sessionStartScriptPath =
|
|
3966
|
-
const transcriptSyncScriptPath =
|
|
4223
|
+
const bashScriptPath = join11(HOOKS_DIR, "cc-bash-judge.sh");
|
|
4224
|
+
const bashFollowupScriptPath = join11(HOOKS_DIR, "cc-bash-followup.sh");
|
|
4225
|
+
const editCaptureScriptPath = join11(HOOKS_DIR, "cc-edit-capture.sh");
|
|
4226
|
+
const editPrecheckScriptPath = join11(HOOKS_DIR, "cc-edit-precheck.sh");
|
|
4227
|
+
const stopSummaryScriptPath = join11(HOOKS_DIR, "cc-stop-summary.sh");
|
|
4228
|
+
const sessionStartScriptPath = join11(HOOKS_DIR, "cc-session-start.sh");
|
|
4229
|
+
const transcriptSyncScriptPath = join11(HOOKS_DIR, "cc-transcript-sync.sh");
|
|
3967
4230
|
writeFileSync7(bashScriptPath, CC_BASH_JUDGE_SCRIPT, "utf-8");
|
|
3968
4231
|
writeFileSync7(bashFollowupScriptPath, CC_BASH_FOLLOWUP_SCRIPT, "utf-8");
|
|
3969
4232
|
writeFileSync7(editCaptureScriptPath, CC_EDIT_CAPTURE_SCRIPT, "utf-8");
|
|
@@ -3997,11 +4260,11 @@ function shellQuoteSingle(value) {
|
|
|
3997
4260
|
}
|
|
3998
4261
|
function resolveSynkroBundle() {
|
|
3999
4262
|
const scriptPath = process.argv[1];
|
|
4000
|
-
if (scriptPath &&
|
|
4263
|
+
if (scriptPath && existsSync11(scriptPath)) return scriptPath;
|
|
4001
4264
|
return null;
|
|
4002
4265
|
}
|
|
4003
4266
|
function writeConfigEnv(opts) {
|
|
4004
|
-
const credsPath =
|
|
4267
|
+
const credsPath = join11(SYNKRO_DIR2, "credentials.json");
|
|
4005
4268
|
const safeGateway = sanitizeConfigValue(opts.gatewayUrl);
|
|
4006
4269
|
const safeUserId = sanitizeConfigValue(opts.userId);
|
|
4007
4270
|
const safeOrgId = sanitizeConfigValue(opts.orgId);
|
|
@@ -4017,7 +4280,7 @@ function writeConfigEnv(opts) {
|
|
|
4017
4280
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
4018
4281
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
4019
4282
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
4020
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.4.
|
|
4283
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.4.3")}`
|
|
4021
4284
|
];
|
|
4022
4285
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
4023
4286
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -4049,16 +4312,16 @@ function collectLocalMetadata() {
|
|
|
4049
4312
|
meta.cc_version = execSync5("claude --version", { encoding: "utf-8", timeout: 5e3 }).trim().split("\n")[0];
|
|
4050
4313
|
} catch {
|
|
4051
4314
|
}
|
|
4052
|
-
const claudeDir =
|
|
4315
|
+
const claudeDir = join11(homedir10(), ".claude");
|
|
4053
4316
|
try {
|
|
4054
|
-
const settings = JSON.parse(
|
|
4317
|
+
const settings = JSON.parse(readFileSync9(join11(claudeDir, "settings.json"), "utf-8"));
|
|
4055
4318
|
const plugins = Object.keys(settings.enabledPlugins ?? {}).filter((k) => settings.enabledPlugins[k]);
|
|
4056
4319
|
if (plugins.length) meta.enabled_plugins = plugins;
|
|
4057
4320
|
if (settings.permissions?.defaultMode) meta.permissions_mode = settings.permissions.defaultMode;
|
|
4058
4321
|
} catch {
|
|
4059
4322
|
}
|
|
4060
4323
|
try {
|
|
4061
|
-
const mcpCache = JSON.parse(
|
|
4324
|
+
const mcpCache = JSON.parse(readFileSync9(join11(claudeDir, "mcp-needs-auth-cache.json"), "utf-8"));
|
|
4062
4325
|
const mcpNames = Object.keys(mcpCache);
|
|
4063
4326
|
if (mcpNames.length) meta.mcp_servers = mcpNames;
|
|
4064
4327
|
} catch {
|
|
@@ -4070,10 +4333,10 @@ function collectLocalMetadata() {
|
|
|
4070
4333
|
} catch {
|
|
4071
4334
|
}
|
|
4072
4335
|
try {
|
|
4073
|
-
const sessionsDir =
|
|
4336
|
+
const sessionsDir = join11(claudeDir, "sessions");
|
|
4074
4337
|
const files = readdirSync(sessionsDir).filter((f) => f.endsWith(".json")).slice(-5);
|
|
4075
4338
|
for (const f of files) {
|
|
4076
|
-
const s = JSON.parse(
|
|
4339
|
+
const s = JSON.parse(readFileSync9(join11(sessionsDir, f), "utf-8"));
|
|
4077
4340
|
if (s.version) {
|
|
4078
4341
|
meta.cc_version = meta.cc_version || s.version;
|
|
4079
4342
|
break;
|
|
@@ -4129,19 +4392,19 @@ function assertGatewayAllowed(gatewayUrl) {
|
|
|
4129
4392
|
}
|
|
4130
4393
|
function isAlreadyInstalled() {
|
|
4131
4394
|
const requiredScripts = [
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4395
|
+
join11(HOOKS_DIR, "cc-bash-judge.sh"),
|
|
4396
|
+
join11(HOOKS_DIR, "cc-bash-followup.sh"),
|
|
4397
|
+
join11(HOOKS_DIR, "cc-edit-precheck.sh"),
|
|
4398
|
+
join11(HOOKS_DIR, "cc-edit-capture.sh"),
|
|
4399
|
+
join11(HOOKS_DIR, "cc-stop-summary.sh"),
|
|
4400
|
+
join11(HOOKS_DIR, "cc-session-start.sh")
|
|
4138
4401
|
];
|
|
4139
|
-
if (!requiredScripts.every((p) =>
|
|
4140
|
-
if (!
|
|
4141
|
-
const settingsPath =
|
|
4142
|
-
if (!
|
|
4402
|
+
if (!requiredScripts.every((p) => existsSync11(p))) return false;
|
|
4403
|
+
if (!existsSync11(CONFIG_PATH2)) return false;
|
|
4404
|
+
const settingsPath = join11(homedir10(), ".claude", "settings.json");
|
|
4405
|
+
if (!existsSync11(settingsPath)) return false;
|
|
4143
4406
|
try {
|
|
4144
|
-
const settings = JSON.parse(
|
|
4407
|
+
const settings = JSON.parse(readFileSync9(settingsPath, "utf-8"));
|
|
4145
4408
|
const hooks = settings?.hooks;
|
|
4146
4409
|
if (!hooks || typeof hooks !== "object") return false;
|
|
4147
4410
|
const hasManaged = (kind) => Array.isArray(hooks[kind]) && hooks[kind].some((entry) => entry?.__synkro_managed__ === true);
|
|
@@ -4249,9 +4512,9 @@ async function installCommand(opts = {}) {
|
|
|
4249
4512
|
console.log(` ${scripts.transcriptSyncScript}
|
|
4250
4513
|
`);
|
|
4251
4514
|
for (const mode of ["edit", "bash"]) {
|
|
4252
|
-
const pidFile =
|
|
4515
|
+
const pidFile = join11(SYNKRO_DIR2, "daemon", mode, "daemon.pid");
|
|
4253
4516
|
try {
|
|
4254
|
-
const pid = parseInt(
|
|
4517
|
+
const pid = parseInt(readFileSync9(pidFile, "utf-8").trim(), 10);
|
|
4255
4518
|
if (pid > 0) {
|
|
4256
4519
|
process.kill(pid, "SIGTERM");
|
|
4257
4520
|
console.log(`Stopped stale ${mode} grader daemon (pid ${pid})`);
|
|
@@ -4342,14 +4605,20 @@ async function installCommand(opts = {}) {
|
|
|
4342
4605
|
try {
|
|
4343
4606
|
assertClaudeInstalled();
|
|
4344
4607
|
assertPueueInstalled();
|
|
4608
|
+
assertTmuxInstalled();
|
|
4345
4609
|
const r = installLocalCC();
|
|
4346
4610
|
console.log(`Installed local-CC channel plugin at ${r.pluginPath}`);
|
|
4347
4611
|
const t = ensureRunning();
|
|
4348
|
-
console.log(`Local-CC pueue task: id=${t.id} status=${t.status}
|
|
4612
|
+
console.log(`Local-CC pueue task: id=${t.id} status=${t.status}`);
|
|
4613
|
+
console.log("Waiting for channel...");
|
|
4614
|
+
const ready = await waitForChannelReady(CHANNEL_PORT, 6e4, CHANNEL_HOST);
|
|
4615
|
+
if (ready) console.log(` channel ready at ${CHANNEL_HOST}:${CHANNEL_PORT}
|
|
4616
|
+
`);
|
|
4617
|
+
else console.warn(` \u26A0 channel did not come up within 60s \u2014 check \`synkro local-cc logs\`
|
|
4349
4618
|
`);
|
|
4350
4619
|
} catch (err) {
|
|
4351
4620
|
console.warn(` \u26A0 Local-CC setup skipped: ${err.message}`);
|
|
4352
|
-
console.warn("
|
|
4621
|
+
console.warn(" Install pueue, tmux, and claude, then re-run install.\n");
|
|
4353
4622
|
}
|
|
4354
4623
|
}
|
|
4355
4624
|
if (transcriptConsent) {
|
|
@@ -4398,17 +4667,17 @@ function detectGitRepo2() {
|
|
|
4398
4667
|
function getClaudeProjectsFolder() {
|
|
4399
4668
|
const cwd = process.cwd();
|
|
4400
4669
|
const sanitized = "-" + cwd.replace(/\//g, "-");
|
|
4401
|
-
const projectsDir =
|
|
4402
|
-
return
|
|
4670
|
+
const projectsDir = join11(homedir10(), ".claude", "projects", sanitized);
|
|
4671
|
+
return existsSync11(projectsDir) ? projectsDir : null;
|
|
4403
4672
|
}
|
|
4404
4673
|
function extractSessionInsights(projectsDir) {
|
|
4405
4674
|
const insights = [];
|
|
4406
4675
|
const files = readdirSync(projectsDir).filter((f) => f.endsWith(".jsonl"));
|
|
4407
4676
|
for (const file of files) {
|
|
4408
4677
|
const sessionId = file.replace(".jsonl", "");
|
|
4409
|
-
const filePath =
|
|
4678
|
+
const filePath = join11(projectsDir, file);
|
|
4410
4679
|
try {
|
|
4411
|
-
const content =
|
|
4680
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
4412
4681
|
const lines = content.split("\n").filter(Boolean);
|
|
4413
4682
|
for (let i = 0; i < lines.length; i++) {
|
|
4414
4683
|
try {
|
|
@@ -4484,7 +4753,7 @@ function extractTextContent(content) {
|
|
|
4484
4753
|
return "";
|
|
4485
4754
|
}
|
|
4486
4755
|
function parseTranscriptFile(filePath) {
|
|
4487
|
-
const content =
|
|
4756
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
4488
4757
|
const lines = content.split("\n").filter(Boolean);
|
|
4489
4758
|
const messages = [];
|
|
4490
4759
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -4535,7 +4804,7 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
4535
4804
|
const sessions = [];
|
|
4536
4805
|
for (const file of batch) {
|
|
4537
4806
|
const sessionId = file.replace(".jsonl", "");
|
|
4538
|
-
const filePath =
|
|
4807
|
+
const filePath = join11(projectsDir, file);
|
|
4539
4808
|
try {
|
|
4540
4809
|
const allMessages = parseTranscriptFile(filePath);
|
|
4541
4810
|
const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
|
|
@@ -4564,11 +4833,11 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
4564
4833
|
}
|
|
4565
4834
|
for (const file of batch) {
|
|
4566
4835
|
const sessionId = file.replace(".jsonl", "");
|
|
4567
|
-
const filePath =
|
|
4836
|
+
const filePath = join11(projectsDir, file);
|
|
4568
4837
|
try {
|
|
4569
|
-
const content =
|
|
4838
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
4570
4839
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
4571
|
-
writeFileSync7(
|
|
4840
|
+
writeFileSync7(join11(OFFSETS_DIR, sessionId), String(lineCount), "utf-8");
|
|
4572
4841
|
} catch {
|
|
4573
4842
|
}
|
|
4574
4843
|
}
|
|
@@ -4590,11 +4859,12 @@ var init_install2 = __esm({
|
|
|
4590
4859
|
init_promptFetcher();
|
|
4591
4860
|
init_install();
|
|
4592
4861
|
init_pueue();
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4862
|
+
init_client();
|
|
4863
|
+
SYNKRO_DIR2 = join11(homedir10(), ".synkro");
|
|
4864
|
+
HOOKS_DIR = join11(SYNKRO_DIR2, "hooks");
|
|
4865
|
+
BIN_DIR = join11(SYNKRO_DIR2, "bin");
|
|
4866
|
+
CONFIG_PATH2 = join11(SYNKRO_DIR2, "config.env");
|
|
4867
|
+
OFFSETS_DIR = join11(SYNKRO_DIR2, ".transcript-offsets");
|
|
4598
4868
|
}
|
|
4599
4869
|
});
|
|
4600
4870
|
|
|
@@ -4670,13 +4940,13 @@ var status_exports = {};
|
|
|
4670
4940
|
__export(status_exports, {
|
|
4671
4941
|
statusCommand: () => statusCommand
|
|
4672
4942
|
});
|
|
4673
|
-
import { existsSync as
|
|
4674
|
-
import { homedir as
|
|
4675
|
-
import { join as
|
|
4943
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
4944
|
+
import { homedir as homedir11 } from "os";
|
|
4945
|
+
import { join as join12 } from "path";
|
|
4676
4946
|
function readConfigEnv() {
|
|
4677
|
-
if (!
|
|
4947
|
+
if (!existsSync12(CONFIG_PATH3)) return {};
|
|
4678
4948
|
const out = {};
|
|
4679
|
-
const raw =
|
|
4949
|
+
const raw = readFileSync10(CONFIG_PATH3, "utf-8");
|
|
4680
4950
|
for (const line of raw.split("\n")) {
|
|
4681
4951
|
const trimmed = line.trim();
|
|
4682
4952
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -4749,19 +5019,19 @@ async function statusCommand() {
|
|
|
4749
5019
|
}
|
|
4750
5020
|
}
|
|
4751
5021
|
console.log();
|
|
4752
|
-
const bashScript =
|
|
4753
|
-
const bashFollowupScript =
|
|
4754
|
-
const editPrecheckScript =
|
|
4755
|
-
const editCaptureScript =
|
|
4756
|
-
const stopSummaryScript =
|
|
4757
|
-
const sessionStartScript =
|
|
5022
|
+
const bashScript = join12(SYNKRO_DIR3, "hooks", "cc-bash-judge.sh");
|
|
5023
|
+
const bashFollowupScript = join12(SYNKRO_DIR3, "hooks", "cc-bash-followup.sh");
|
|
5024
|
+
const editPrecheckScript = join12(SYNKRO_DIR3, "hooks", "cc-edit-precheck.sh");
|
|
5025
|
+
const editCaptureScript = join12(SYNKRO_DIR3, "hooks", "cc-edit-capture.sh");
|
|
5026
|
+
const stopSummaryScript = join12(SYNKRO_DIR3, "hooks", "cc-stop-summary.sh");
|
|
5027
|
+
const sessionStartScript = join12(SYNKRO_DIR3, "hooks", "cc-session-start.sh");
|
|
4758
5028
|
console.log("Hook scripts:");
|
|
4759
|
-
console.log(` ${
|
|
4760
|
-
console.log(` ${
|
|
4761
|
-
console.log(` ${
|
|
4762
|
-
console.log(` ${
|
|
4763
|
-
console.log(` ${
|
|
4764
|
-
console.log(` ${
|
|
5029
|
+
console.log(` ${existsSync12(bashScript) ? "\u2713" : "\u2717"} ${bashScript}`);
|
|
5030
|
+
console.log(` ${existsSync12(bashFollowupScript) ? "\u2713" : "\u2717"} ${bashFollowupScript}`);
|
|
5031
|
+
console.log(` ${existsSync12(editPrecheckScript) ? "\u2713" : "\u2717"} ${editPrecheckScript}`);
|
|
5032
|
+
console.log(` ${existsSync12(editCaptureScript) ? "\u2713" : "\u2717"} ${editCaptureScript}`);
|
|
5033
|
+
console.log(` ${existsSync12(stopSummaryScript) ? "\u2713" : "\u2717"} ${stopSummaryScript}`);
|
|
5034
|
+
console.log(` ${existsSync12(sessionStartScript) ? "\u2713" : "\u2717"} ${sessionStartScript}`);
|
|
4765
5035
|
console.log();
|
|
4766
5036
|
const mcp = inspectMcpConfig();
|
|
4767
5037
|
console.log("Guardrails MCP server (Claude Code):");
|
|
@@ -4781,8 +5051,8 @@ var init_status = __esm({
|
|
|
4781
5051
|
init_agentDetect();
|
|
4782
5052
|
init_ccHookConfig();
|
|
4783
5053
|
init_mcpConfig();
|
|
4784
|
-
SYNKRO_DIR3 =
|
|
4785
|
-
CONFIG_PATH3 =
|
|
5054
|
+
SYNKRO_DIR3 = join12(homedir11(), ".synkro");
|
|
5055
|
+
CONFIG_PATH3 = join12(SYNKRO_DIR3, "config.env");
|
|
4786
5056
|
}
|
|
4787
5057
|
});
|
|
4788
5058
|
|
|
@@ -4871,13 +5141,13 @@ var config_exports = {};
|
|
|
4871
5141
|
__export(config_exports, {
|
|
4872
5142
|
configCommand: () => configCommand
|
|
4873
5143
|
});
|
|
4874
|
-
import { readFileSync as
|
|
4875
|
-
import { join as
|
|
4876
|
-
import { homedir as
|
|
5144
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
|
|
5145
|
+
import { join as join13 } from "path";
|
|
5146
|
+
import { homedir as homedir12 } from "os";
|
|
4877
5147
|
function readConfigEnv2() {
|
|
4878
|
-
if (!
|
|
5148
|
+
if (!existsSync13(CONFIG_PATH4)) return {};
|
|
4879
5149
|
const out = {};
|
|
4880
|
-
for (const line of
|
|
5150
|
+
for (const line of readFileSync11(CONFIG_PATH4, "utf-8").split("\n")) {
|
|
4881
5151
|
const t = line.trim();
|
|
4882
5152
|
if (!t || t.startsWith("#")) continue;
|
|
4883
5153
|
const eq = t.indexOf("=");
|
|
@@ -4886,11 +5156,11 @@ function readConfigEnv2() {
|
|
|
4886
5156
|
return out;
|
|
4887
5157
|
}
|
|
4888
5158
|
function updateConfigValue(key, value) {
|
|
4889
|
-
if (!
|
|
5159
|
+
if (!existsSync13(CONFIG_PATH4)) {
|
|
4890
5160
|
console.error("No config found. Run `synkro install` first.");
|
|
4891
5161
|
process.exit(1);
|
|
4892
5162
|
}
|
|
4893
|
-
const lines =
|
|
5163
|
+
const lines = readFileSync11(CONFIG_PATH4, "utf-8").split("\n");
|
|
4894
5164
|
const pattern = new RegExp(`^${key}=`);
|
|
4895
5165
|
let found = false;
|
|
4896
5166
|
const updated = lines.map((line) => {
|
|
@@ -4957,8 +5227,8 @@ var init_config = __esm({
|
|
|
4957
5227
|
"cli/commands/config.ts"() {
|
|
4958
5228
|
"use strict";
|
|
4959
5229
|
init_stub();
|
|
4960
|
-
SYNKRO_DIR4 =
|
|
4961
|
-
CONFIG_PATH4 =
|
|
5230
|
+
SYNKRO_DIR4 = join13(homedir12(), ".synkro");
|
|
5231
|
+
CONFIG_PATH4 = join13(SYNKRO_DIR4, "config.env");
|
|
4962
5232
|
}
|
|
4963
5233
|
});
|
|
4964
5234
|
|
|
@@ -4968,8 +5238,8 @@ __export(scanPr_exports, {
|
|
|
4968
5238
|
scanPrCommand: () => scanPrCommand
|
|
4969
5239
|
});
|
|
4970
5240
|
import { execSync as execSync6, spawn } from "child_process";
|
|
4971
|
-
import { readFileSync as
|
|
4972
|
-
import { join as
|
|
5241
|
+
import { readFileSync as readFileSync12, existsSync as existsSync14 } from "fs";
|
|
5242
|
+
import { join as join14 } from "path";
|
|
4973
5243
|
function parseMatchSpec(condition) {
|
|
4974
5244
|
if (!condition.startsWith("match_spec:")) return null;
|
|
4975
5245
|
try {
|
|
@@ -5448,10 +5718,10 @@ function shouldFail(findings, threshold) {
|
|
|
5448
5718
|
return findings.some((f) => order.indexOf(f.severity) >= thresholdIdx);
|
|
5449
5719
|
}
|
|
5450
5720
|
function readRepoDeps() {
|
|
5451
|
-
const pkgPath =
|
|
5452
|
-
if (!
|
|
5721
|
+
const pkgPath = join14(process.cwd(), "package.json");
|
|
5722
|
+
if (!existsSync14(pkgPath)) return {};
|
|
5453
5723
|
try {
|
|
5454
|
-
const pkg = JSON.parse(
|
|
5724
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
5455
5725
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
5456
5726
|
} catch {
|
|
5457
5727
|
return {};
|
|
@@ -5713,9 +5983,9 @@ var disconnect_exports = {};
|
|
|
5713
5983
|
__export(disconnect_exports, {
|
|
5714
5984
|
disconnectCommand: () => disconnectCommand
|
|
5715
5985
|
});
|
|
5716
|
-
import { existsSync as
|
|
5717
|
-
import { homedir as
|
|
5718
|
-
import { join as
|
|
5986
|
+
import { existsSync as existsSync15, rmSync } from "fs";
|
|
5987
|
+
import { homedir as homedir13 } from "os";
|
|
5988
|
+
import { join as join15 } from "path";
|
|
5719
5989
|
function tearDownLocalCC() {
|
|
5720
5990
|
let hadTask = false;
|
|
5721
5991
|
try {
|
|
@@ -5745,13 +6015,13 @@ function disconnectCommand(args2 = []) {
|
|
|
5745
6015
|
console.log(`${mcpRemoved ? "\u2713" : "\xB7"} MCP guardrails server: ${mcpRemoved ? "removed entry from ~/.claude.json" : "no Synkro MCP entry found"}`);
|
|
5746
6016
|
}
|
|
5747
6017
|
if (purge) {
|
|
5748
|
-
if (
|
|
6018
|
+
if (existsSync15(SYNKRO_DIR5)) {
|
|
5749
6019
|
rmSync(SYNKRO_DIR5, { recursive: true, force: true });
|
|
5750
6020
|
console.log(`\u2713 Removed ${SYNKRO_DIR5}`);
|
|
5751
6021
|
} else {
|
|
5752
6022
|
console.log(`\xB7 ${SYNKRO_DIR5} already gone, nothing to remove`);
|
|
5753
6023
|
}
|
|
5754
|
-
} else if (
|
|
6024
|
+
} else if (existsSync15(SYNKRO_DIR5)) {
|
|
5755
6025
|
console.log(`Config preserved at ${SYNKRO_DIR5}. Run with --purge to remove.`);
|
|
5756
6026
|
}
|
|
5757
6027
|
console.log("\nSynkro disconnected.");
|
|
@@ -5765,7 +6035,7 @@ var init_disconnect = __esm({
|
|
|
5765
6035
|
init_mcpConfig();
|
|
5766
6036
|
init_pueue();
|
|
5767
6037
|
init_install();
|
|
5768
|
-
SYNKRO_DIR5 =
|
|
6038
|
+
SYNKRO_DIR5 = join15(homedir13(), ".synkro");
|
|
5769
6039
|
}
|
|
5770
6040
|
});
|
|
5771
6041
|
|
|
@@ -5806,141 +6076,14 @@ var init_reinstall = __esm({
|
|
|
5806
6076
|
}
|
|
5807
6077
|
});
|
|
5808
6078
|
|
|
5809
|
-
// cli/local-cc/turnLog.ts
|
|
5810
|
-
import { appendFileSync, existsSync as existsSync14, mkdirSync as mkdirSync8, openSync as openSync2, readFileSync as readFileSync11, readSync, closeSync as closeSync2, statSync, watchFile, unwatchFile } from "fs";
|
|
5811
|
-
import { dirname as dirname5, join as join14 } from "path";
|
|
5812
|
-
import { homedir as homedir12 } from "os";
|
|
5813
|
-
function truncate(s, max = PREVIEW_MAX) {
|
|
5814
|
-
if (s.length <= max) return s;
|
|
5815
|
-
return s.slice(0, max) + "\u2026 [+" + (s.length - max) + " chars]";
|
|
5816
|
-
}
|
|
5817
|
-
function extractSeverity(result) {
|
|
5818
|
-
const m = result.match(/<synkro-(?:verdict|intent)>([\s\S]*?)<\/synkro-(?:verdict|intent)>/);
|
|
5819
|
-
if (!m) return void 0;
|
|
5820
|
-
try {
|
|
5821
|
-
const obj = JSON.parse(m[1]);
|
|
5822
|
-
if (obj.severity) return String(obj.severity);
|
|
5823
|
-
if (typeof obj.ok === "boolean") return obj.ok ? "ok" : "violations";
|
|
5824
|
-
if (obj.type) return String(obj.type);
|
|
5825
|
-
if (obj.verdict) return String(obj.verdict);
|
|
5826
|
-
} catch {
|
|
5827
|
-
}
|
|
5828
|
-
return void 0;
|
|
5829
|
-
}
|
|
5830
|
-
function appendTurn(args2) {
|
|
5831
|
-
try {
|
|
5832
|
-
mkdirSync8(dirname5(TURN_LOG_PATH), { recursive: true });
|
|
5833
|
-
const entry = {
|
|
5834
|
-
ts: new Date(args2.startedAt).toISOString(),
|
|
5835
|
-
role: args2.role,
|
|
5836
|
-
duration_ms: Date.now() - args2.startedAt,
|
|
5837
|
-
status: args2.status,
|
|
5838
|
-
request_preview: truncate(args2.request),
|
|
5839
|
-
response_preview: args2.result ? truncate(args2.result) : "",
|
|
5840
|
-
severity: args2.result ? extractSeverity(args2.result) : void 0,
|
|
5841
|
-
error: args2.error
|
|
5842
|
-
};
|
|
5843
|
-
appendFileSync(TURN_LOG_PATH, JSON.stringify(entry) + "\n", "utf-8");
|
|
5844
|
-
} catch {
|
|
5845
|
-
}
|
|
5846
|
-
}
|
|
5847
|
-
function readRecentTurns(n = 20) {
|
|
5848
|
-
if (!existsSync14(TURN_LOG_PATH)) return [];
|
|
5849
|
-
try {
|
|
5850
|
-
const size = statSync(TURN_LOG_PATH).size;
|
|
5851
|
-
if (size === 0) return [];
|
|
5852
|
-
const text = readFileSync11(TURN_LOG_PATH, "utf-8");
|
|
5853
|
-
const lines = text.split("\n").filter(Boolean);
|
|
5854
|
-
const lastN = lines.slice(-n).reverse();
|
|
5855
|
-
return lastN.map((line) => {
|
|
5856
|
-
try {
|
|
5857
|
-
return JSON.parse(line);
|
|
5858
|
-
} catch {
|
|
5859
|
-
return null;
|
|
5860
|
-
}
|
|
5861
|
-
}).filter((x) => x !== null);
|
|
5862
|
-
} catch {
|
|
5863
|
-
return [];
|
|
5864
|
-
}
|
|
5865
|
-
}
|
|
5866
|
-
function followTurns(onEntry) {
|
|
5867
|
-
try {
|
|
5868
|
-
mkdirSync8(dirname5(TURN_LOG_PATH), { recursive: true });
|
|
5869
|
-
if (!existsSync14(TURN_LOG_PATH)) {
|
|
5870
|
-
appendFileSync(TURN_LOG_PATH, "", "utf-8");
|
|
5871
|
-
}
|
|
5872
|
-
} catch {
|
|
5873
|
-
}
|
|
5874
|
-
let lastSize = (() => {
|
|
5875
|
-
try {
|
|
5876
|
-
return statSync(TURN_LOG_PATH).size;
|
|
5877
|
-
} catch {
|
|
5878
|
-
return 0;
|
|
5879
|
-
}
|
|
5880
|
-
})();
|
|
5881
|
-
let pendingPartial = "";
|
|
5882
|
-
const drainNewBytes = (from, to) => {
|
|
5883
|
-
if (to <= from) return;
|
|
5884
|
-
let fd = null;
|
|
5885
|
-
try {
|
|
5886
|
-
fd = openSync2(TURN_LOG_PATH, "r");
|
|
5887
|
-
const len = to - from;
|
|
5888
|
-
const buf = Buffer.alloc(len);
|
|
5889
|
-
readSync(fd, buf, 0, len, from);
|
|
5890
|
-
const text = pendingPartial + buf.toString("utf-8");
|
|
5891
|
-
const lastNewline = text.lastIndexOf("\n");
|
|
5892
|
-
if (lastNewline === -1) {
|
|
5893
|
-
pendingPartial = text;
|
|
5894
|
-
return;
|
|
5895
|
-
}
|
|
5896
|
-
const complete = text.slice(0, lastNewline);
|
|
5897
|
-
pendingPartial = text.slice(lastNewline + 1);
|
|
5898
|
-
for (const line of complete.split("\n")) {
|
|
5899
|
-
if (!line) continue;
|
|
5900
|
-
try {
|
|
5901
|
-
onEntry(JSON.parse(line));
|
|
5902
|
-
} catch {
|
|
5903
|
-
}
|
|
5904
|
-
}
|
|
5905
|
-
} catch {
|
|
5906
|
-
} finally {
|
|
5907
|
-
if (fd !== null) {
|
|
5908
|
-
try {
|
|
5909
|
-
closeSync2(fd);
|
|
5910
|
-
} catch {
|
|
5911
|
-
}
|
|
5912
|
-
}
|
|
5913
|
-
}
|
|
5914
|
-
};
|
|
5915
|
-
watchFile(TURN_LOG_PATH, { interval: 250 }, (curr, prev) => {
|
|
5916
|
-
if (curr.size < lastSize) {
|
|
5917
|
-
lastSize = 0;
|
|
5918
|
-
pendingPartial = "";
|
|
5919
|
-
}
|
|
5920
|
-
if (curr.size > lastSize) {
|
|
5921
|
-
drainNewBytes(lastSize, curr.size);
|
|
5922
|
-
lastSize = curr.size;
|
|
5923
|
-
}
|
|
5924
|
-
});
|
|
5925
|
-
return () => unwatchFile(TURN_LOG_PATH);
|
|
5926
|
-
}
|
|
5927
|
-
var TURN_LOG_PATH, PREVIEW_MAX;
|
|
5928
|
-
var init_turnLog = __esm({
|
|
5929
|
-
"cli/local-cc/turnLog.ts"() {
|
|
5930
|
-
"use strict";
|
|
5931
|
-
TURN_LOG_PATH = join14(homedir12(), ".synkro", "cc_sessions", "turns.log");
|
|
5932
|
-
PREVIEW_MAX = 400;
|
|
5933
|
-
}
|
|
5934
|
-
});
|
|
5935
|
-
|
|
5936
6079
|
// cli/local-cc/settings.ts
|
|
5937
|
-
import { existsSync as
|
|
5938
|
-
import { homedir as
|
|
5939
|
-
import { join as
|
|
6080
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
6081
|
+
import { homedir as homedir14 } from "os";
|
|
6082
|
+
import { join as join16 } from "path";
|
|
5940
6083
|
function isLocalCCEnabled() {
|
|
5941
|
-
if (!
|
|
6084
|
+
if (!existsSync16(CONFIG_PATH5)) return false;
|
|
5942
6085
|
try {
|
|
5943
|
-
const content =
|
|
6086
|
+
const content = readFileSync13(CONFIG_PATH5, "utf-8");
|
|
5944
6087
|
const match = content.match(/^SYNKRO_LOCAL_INFERENCE='([^']*)'/m);
|
|
5945
6088
|
return match?.[1] === "yes";
|
|
5946
6089
|
} catch {
|
|
@@ -5951,143 +6094,7 @@ var CONFIG_PATH5;
|
|
|
5951
6094
|
var init_settings = __esm({
|
|
5952
6095
|
"cli/local-cc/settings.ts"() {
|
|
5953
6096
|
"use strict";
|
|
5954
|
-
CONFIG_PATH5 =
|
|
5955
|
-
}
|
|
5956
|
-
});
|
|
5957
|
-
|
|
5958
|
-
// cli/local-cc/prompts.ts
|
|
5959
|
-
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
5960
|
-
import { homedir as homedir14 } from "os";
|
|
5961
|
-
import { join as join16 } from "path";
|
|
5962
|
-
function loadCachedPrompts() {
|
|
5963
|
-
if (_cached) return _cached;
|
|
5964
|
-
if (!existsSync16(CACHE_PATH2)) {
|
|
5965
|
-
throw new Error("Prompts cache not found. Run `synkro install` or `synkro update` first.");
|
|
5966
|
-
}
|
|
5967
|
-
try {
|
|
5968
|
-
_cached = JSON.parse(readFileSync13(CACHE_PATH2, "utf-8"));
|
|
5969
|
-
return _cached;
|
|
5970
|
-
} catch {
|
|
5971
|
-
throw new Error("Prompts cache is corrupted. Run `synkro update` to refresh.");
|
|
5972
|
-
}
|
|
5973
|
-
}
|
|
5974
|
-
function getPrimer(role) {
|
|
5975
|
-
const cache = loadCachedPrompts();
|
|
5976
|
-
const primer = role === "grade-edit" ? cache.grader_primer_edit : cache.grader_primer_bash;
|
|
5977
|
-
if (!primer) {
|
|
5978
|
-
throw new Error(`No cached primer for role "${role}". Run \`synkro update\` to refresh prompts.`);
|
|
5979
|
-
}
|
|
5980
|
-
return primer;
|
|
5981
|
-
}
|
|
5982
|
-
function buildChannelContent(role, payload) {
|
|
5983
|
-
return `${getPrimer(role)}
|
|
5984
|
-
|
|
5985
|
-
---
|
|
5986
|
-
PAYLOAD (the input to evaluate):
|
|
5987
|
-
|
|
5988
|
-
${payload}`;
|
|
5989
|
-
}
|
|
5990
|
-
var CACHE_PATH2, _cached;
|
|
5991
|
-
var init_prompts = __esm({
|
|
5992
|
-
"cli/local-cc/prompts.ts"() {
|
|
5993
|
-
"use strict";
|
|
5994
|
-
CACHE_PATH2 = join16(homedir14(), ".synkro", "prompts", "judge-prompts.json");
|
|
5995
|
-
_cached = null;
|
|
5996
|
-
}
|
|
5997
|
-
});
|
|
5998
|
-
|
|
5999
|
-
// cli/local-cc/client.ts
|
|
6000
|
-
import { request as httpRequest } from "http";
|
|
6001
|
-
import { connect as connect2 } from "net";
|
|
6002
|
-
async function submitToChannel(role, payload, opts = {}) {
|
|
6003
|
-
const content = buildChannelContent(role, payload);
|
|
6004
|
-
const body = JSON.stringify({ role, content });
|
|
6005
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
6006
|
-
const startedAt = Date.now();
|
|
6007
|
-
try {
|
|
6008
|
-
const result = await new Promise((resolve2, reject) => {
|
|
6009
|
-
const req = httpRequest({
|
|
6010
|
-
host: CHANNEL_HOST,
|
|
6011
|
-
port: CHANNEL_PORT,
|
|
6012
|
-
method: "POST",
|
|
6013
|
-
path: "/submit",
|
|
6014
|
-
headers: {
|
|
6015
|
-
"Content-Type": "application/json",
|
|
6016
|
-
"Content-Length": Buffer.byteLength(body)
|
|
6017
|
-
},
|
|
6018
|
-
timeout: timeoutMs
|
|
6019
|
-
}, (res) => {
|
|
6020
|
-
const chunks = [];
|
|
6021
|
-
res.on("data", (c) => chunks.push(c));
|
|
6022
|
-
res.on("end", () => {
|
|
6023
|
-
const text = Buffer.concat(chunks).toString("utf-8");
|
|
6024
|
-
if (res.statusCode !== 200) {
|
|
6025
|
-
reject(new LocalCCError(`channel returned ${res.statusCode}: ${text.slice(0, 500)}`));
|
|
6026
|
-
return;
|
|
6027
|
-
}
|
|
6028
|
-
try {
|
|
6029
|
-
const parsed = JSON.parse(text);
|
|
6030
|
-
if (parsed.error) {
|
|
6031
|
-
reject(new LocalCCError(parsed.error));
|
|
6032
|
-
return;
|
|
6033
|
-
}
|
|
6034
|
-
resolve2(String(parsed.result ?? ""));
|
|
6035
|
-
} catch (err) {
|
|
6036
|
-
reject(new LocalCCError(`malformed channel response: ${text.slice(0, 200)}`, err));
|
|
6037
|
-
}
|
|
6038
|
-
});
|
|
6039
|
-
});
|
|
6040
|
-
req.on("timeout", () => {
|
|
6041
|
-
req.destroy(new LocalCCError(`channel request timed out after ${timeoutMs}ms`));
|
|
6042
|
-
});
|
|
6043
|
-
req.on("error", (err) => {
|
|
6044
|
-
const msg = err.code === "ECONNREFUSED" ? `channel connection refused at ${CHANNEL_HOST}:${CHANNEL_PORT} (is the pueue task running?)` : `channel request failed: ${err.message}`;
|
|
6045
|
-
reject(new LocalCCError(msg, err));
|
|
6046
|
-
});
|
|
6047
|
-
req.write(body);
|
|
6048
|
-
req.end();
|
|
6049
|
-
});
|
|
6050
|
-
appendTurn({ startedAt, role, request: payload, result, status: "ok" });
|
|
6051
|
-
return result;
|
|
6052
|
-
} catch (err) {
|
|
6053
|
-
const message = err.message ?? String(err);
|
|
6054
|
-
const status = /timed out/i.test(message) ? "timeout" : "error";
|
|
6055
|
-
appendTurn({ startedAt, role, request: payload, status, error: message });
|
|
6056
|
-
throw err;
|
|
6057
|
-
}
|
|
6058
|
-
}
|
|
6059
|
-
function isChannelAvailable(timeoutMs = 500) {
|
|
6060
|
-
return new Promise((resolve2) => {
|
|
6061
|
-
const sock = connect2(CHANNEL_PORT, CHANNEL_HOST);
|
|
6062
|
-
const done = (ok) => {
|
|
6063
|
-
try {
|
|
6064
|
-
sock.destroy();
|
|
6065
|
-
} catch {
|
|
6066
|
-
}
|
|
6067
|
-
resolve2(ok);
|
|
6068
|
-
};
|
|
6069
|
-
sock.once("connect", () => done(true));
|
|
6070
|
-
sock.once("error", () => done(false));
|
|
6071
|
-
sock.setTimeout(timeoutMs, () => done(false));
|
|
6072
|
-
});
|
|
6073
|
-
}
|
|
6074
|
-
var CHANNEL_HOST, CHANNEL_PORT, DEFAULT_TIMEOUT_MS, LocalCCError;
|
|
6075
|
-
var init_client = __esm({
|
|
6076
|
-
"cli/local-cc/client.ts"() {
|
|
6077
|
-
"use strict";
|
|
6078
|
-
init_prompts();
|
|
6079
|
-
init_turnLog();
|
|
6080
|
-
CHANNEL_HOST = "127.0.0.1";
|
|
6081
|
-
CHANNEL_PORT = parseInt(process.env.SYNKRO_CHANNEL_PORT || "8929", 10);
|
|
6082
|
-
DEFAULT_TIMEOUT_MS = 9e4;
|
|
6083
|
-
LocalCCError = class extends Error {
|
|
6084
|
-
constructor(message, cause) {
|
|
6085
|
-
super(message);
|
|
6086
|
-
this.cause = cause;
|
|
6087
|
-
this.name = "LocalCCError";
|
|
6088
|
-
}
|
|
6089
|
-
cause;
|
|
6090
|
-
};
|
|
6097
|
+
CONFIG_PATH5 = join16(homedir14(), ".synkro", "config.env");
|
|
6091
6098
|
}
|
|
6092
6099
|
});
|
|
6093
6100
|
|