@synkro-sh/cli 1.6.79 → 1.6.81
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 +289 -67
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -913,7 +913,7 @@ var init_hookScriptsTs = __esm({
|
|
|
913
913
|
"use strict";
|
|
914
914
|
SYNKRO_COMMON_TS = `
|
|
915
915
|
// Shared Synkro hook utilities \u2014 imported by all hook scripts.
|
|
916
|
-
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, renameSync, openSync, closeSync, unlinkSync, readdirSync, statSync, createReadStream } from 'node:fs';
|
|
916
|
+
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, renameSync, openSync, closeSync, readSync, unlinkSync, readdirSync, statSync, createReadStream } from 'node:fs';
|
|
917
917
|
import { createInterface } from 'node:readline';
|
|
918
918
|
import { join, dirname, basename, extname, resolve as resolvePath } from 'node:path';
|
|
919
919
|
import { homedir } from 'node:os';
|
|
@@ -3019,6 +3019,18 @@ export async function syncConversationTranscript(
|
|
|
3019
3019
|
};
|
|
3020
3020
|
});
|
|
3021
3021
|
if (toolCalls.length > 0) msg.tool_calls = toolCalls;
|
|
3022
|
+
// Per-turn usage + model so the session view can show input/output/cache
|
|
3023
|
+
// tokens and cost line-by-line (not just the session aggregate).
|
|
3024
|
+
if (msgObj && typeof msgObj.model === 'string') msg.model = msgObj.model;
|
|
3025
|
+
const u = msgObj && (msgObj.usage as Record<string, unknown> | undefined);
|
|
3026
|
+
if (u && typeof u === 'object') {
|
|
3027
|
+
msg.usage = {
|
|
3028
|
+
input_tokens: Number(u.input_tokens) || 0,
|
|
3029
|
+
output_tokens: Number(u.output_tokens) || 0,
|
|
3030
|
+
cache_creation_input_tokens: Number(u.cache_creation_input_tokens) || 0,
|
|
3031
|
+
cache_read_input_tokens: Number(u.cache_read_input_tokens) || 0,
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3022
3034
|
if (msgObj && typeof msgObj === 'object') {
|
|
3023
3035
|
msg.model = msgObj.model || null;
|
|
3024
3036
|
const u = msgObj.usage as Record<string, unknown> | undefined;
|
|
@@ -3374,47 +3386,76 @@ export function aggregateUsage(
|
|
|
3374
3386
|
): { model: string; totals: Record<string, number> } {
|
|
3375
3387
|
const result = { model: opts?.modelFallback || '', totals: { in: 0, out: 0, cw: 0, cr: 0 } };
|
|
3376
3388
|
if (!transcriptPath || !existsSync(transcriptPath)) return result;
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
}
|
|
3393
|
-
|
|
3394
|
-
for (const entry of entries) {
|
|
3395
|
-
const role = String(entry.role || entry.type || '');
|
|
3396
|
-
const modelInfo = entry.modelInfo as Record<string, unknown> | undefined;
|
|
3397
|
-
const model = (typeof modelInfo?.modelName === 'string' && modelInfo.modelName)
|
|
3398
|
-
|| (typeof modelInfo?.model === 'string' && modelInfo.model)
|
|
3399
|
-
|| (typeof entry.model === 'string' && entry.model)
|
|
3400
|
-
|| msgModel(entry);
|
|
3401
|
-
if (model) result.model = model;
|
|
3402
|
-
|
|
3403
|
-
const tokenCount = (entry.tokenCount ?? entry.token_count) as Record<string, unknown> | undefined;
|
|
3404
|
-
const msg = entry.message as Record<string, unknown> | undefined;
|
|
3405
|
-
const hadLine = addUsageBlock(result, tokenCount)
|
|
3406
|
-
|| addUsageBlock(result, msg?.usage as Record<string, unknown>)
|
|
3407
|
-
|| addUsageBlock(result, entry.usage as Record<string, unknown>);
|
|
3408
|
-
|
|
3409
|
-
if (hadLine || sawExplicit) continue;
|
|
3410
|
-
|
|
3411
|
-
const text = usageTextFromTranscriptEntry(entry);
|
|
3412
|
-
if (!text) continue;
|
|
3413
|
-
const est = Math.ceil(text.length / 4);
|
|
3414
|
-
if (role === 'user' || entry.type === 'user') result.totals.in += est;
|
|
3415
|
-
else if (role === 'assistant' || entry.type === 'assistant') result.totals.out += est;
|
|
3416
|
-
}
|
|
3389
|
+
// CWE-22: only ever read the agent's own transcript, which lives under HOME
|
|
3390
|
+
// (~/.claude, ~/.cursor). Never follow an arbitrary external path.
|
|
3391
|
+
if (!isPathUnder(transcriptPath, HOME)) return result;
|
|
3392
|
+
|
|
3393
|
+
// INCREMENTAL DELTA: returns ONLY the usage in transcript bytes appended since
|
|
3394
|
+
// the last call (a byte offset is tracked per transcript in a sidecar). All three
|
|
3395
|
+
// usage hooks (Claude UserPromptSubmit + Stop, Cursor transcript-sync) share that
|
|
3396
|
+
// offset, so their deltas never overlap; the DB then INCREMENTS by each delta, so
|
|
3397
|
+
// usage climbs per message in both harnesses. We never re-read the whole
|
|
3398
|
+
// (100s-of-MB) transcript \u2014 the old full read OOM'd bun and dropped the tick.
|
|
3399
|
+
const key = (transcriptPath.split('/').pop() || 'session').replace(/[^A-Za-z0-9._-]/g, '_');
|
|
3400
|
+
const stateFile = join(SESSIONS_DIR, key + '.usage.json');
|
|
3401
|
+
let offset = 0, sawExplicit = false, model = '';
|
|
3402
|
+
try {
|
|
3403
|
+
const s = JSON.parse(readFileSync(stateFile, 'utf-8'));
|
|
3404
|
+
if (s && typeof s.offset === 'number') { offset = s.offset; sawExplicit = !!s.sawExplicit; model = s.model || ''; }
|
|
3417
3405
|
} catch {}
|
|
3406
|
+
|
|
3407
|
+
let fd = -1;
|
|
3408
|
+
try {
|
|
3409
|
+
const size = statSync(transcriptPath).size;
|
|
3410
|
+
if (size < offset) { offset = 0; sawExplicit = false; } // rotated/truncated
|
|
3411
|
+
// Never read more than 16MB in one shot; first sight of a giant existing
|
|
3412
|
+
// transcript jumps to the tail rather than OOM on the whole history.
|
|
3413
|
+
const MAX_READ = 16 * 1024 * 1024;
|
|
3414
|
+
let start = offset, capped = false;
|
|
3415
|
+
if (size - start > MAX_READ) { start = size - MAX_READ; capped = true; }
|
|
3416
|
+
const len = size - start;
|
|
3417
|
+
if (len > 0) {
|
|
3418
|
+
fd = openSync(transcriptPath, 'r');
|
|
3419
|
+
const buf = Buffer.alloc(len);
|
|
3420
|
+
readSync(fd, buf, 0, len, start);
|
|
3421
|
+
const lines = buf.toString('utf-8').split('\\n');
|
|
3422
|
+
if (capped && lines.length) lines.shift(); // drop the partial first line when we jumped mid-file
|
|
3423
|
+
for (const line of lines) {
|
|
3424
|
+
if (!line.trim()) continue;
|
|
3425
|
+
let entry: Record<string, unknown>;
|
|
3426
|
+
try { entry = JSON.parse(line) as Record<string, unknown>; } catch { continue; }
|
|
3427
|
+
const msg = entry.message as Record<string, unknown> | undefined;
|
|
3428
|
+
const modelInfo = entry.modelInfo as Record<string, unknown> | undefined;
|
|
3429
|
+
const m = (typeof modelInfo?.modelName === 'string' && modelInfo.modelName)
|
|
3430
|
+
|| (typeof modelInfo?.model === 'string' && modelInfo.model)
|
|
3431
|
+
|| (typeof entry.model === 'string' && entry.model)
|
|
3432
|
+
|| msgModel(entry);
|
|
3433
|
+
if (m) { model = m; result.model = m; }
|
|
3434
|
+
|
|
3435
|
+
const tokenCount = (entry.tokenCount ?? entry.token_count) as Record<string, unknown> | undefined;
|
|
3436
|
+
// result.totals is the DELTA for THIS call only (it never carries prior totals).
|
|
3437
|
+
const had = addUsageBlock(result, tokenCount)
|
|
3438
|
+
|| addUsageBlock(result, msg?.usage as Record<string, unknown>)
|
|
3439
|
+
|| addUsageBlock(result, entry.usage as Record<string, unknown>);
|
|
3440
|
+
if (had) { sawExplicit = true; continue; }
|
|
3441
|
+
if (sawExplicit) continue;
|
|
3442
|
+
|
|
3443
|
+
// Fallback (Cursor / transcripts without usage blocks): estimate from text.
|
|
3444
|
+
const text = usageTextFromTranscriptEntry(entry);
|
|
3445
|
+
if (!text) continue;
|
|
3446
|
+
const est = Math.ceil(text.length / 4);
|
|
3447
|
+
const role = String(entry.role || entry.type || '');
|
|
3448
|
+
if (role === 'user') result.totals.in += est;
|
|
3449
|
+
else if (role === 'assistant') result.totals.out += est;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
// Advance the offset (delta consumed). Ship is best-effort like the other
|
|
3453
|
+
// hooks \u2014 a rare dropped tick loses only that one delta, not the running total.
|
|
3454
|
+
try { mkdirSync(SESSIONS_DIR, { recursive: true }); writeFileSync(stateFile, JSON.stringify({ offset: size, model, sawExplicit })); } catch {}
|
|
3455
|
+
} catch {} finally {
|
|
3456
|
+
if (fd >= 0) { try { closeSync(fd); } catch {} }
|
|
3457
|
+
}
|
|
3458
|
+
if (!result.model) result.model = model || opts?.modelFallback || '';
|
|
3418
3459
|
return result;
|
|
3419
3460
|
}
|
|
3420
3461
|
|
|
@@ -10437,7 +10478,7 @@ async function promptAgentSelection(detected) {
|
|
|
10437
10478
|
detected.forEach((a, i) => console.log(` ${i + 1}. ${a.name}`));
|
|
10438
10479
|
console.log(` ${detected.length + 1}. Both / all (default)`);
|
|
10439
10480
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
10440
|
-
const
|
|
10481
|
+
const ask3 = () => new Promise((resolve4) => {
|
|
10441
10482
|
rl.question(`Pick [1-${detected.length + 1}] (default: all): `, (answer) => {
|
|
10442
10483
|
const t = answer.trim().toLowerCase();
|
|
10443
10484
|
if (t === "" || t === String(detected.length + 1) || t === "both" || t === "all") {
|
|
@@ -10450,10 +10491,10 @@ async function promptAgentSelection(detected) {
|
|
|
10450
10491
|
return resolve4([detected[n - 1]]);
|
|
10451
10492
|
}
|
|
10452
10493
|
console.log("Invalid choice. Try again.");
|
|
10453
|
-
resolve4(
|
|
10494
|
+
resolve4(ask3());
|
|
10454
10495
|
});
|
|
10455
10496
|
});
|
|
10456
|
-
return
|
|
10497
|
+
return ask3();
|
|
10457
10498
|
}
|
|
10458
10499
|
async function promptCursorApiKey(opts) {
|
|
10459
10500
|
const { cursorApiKeyConfigured: cursorApiKeyConfigured2, writeCursorApiKey: writeCursorApiKey2, validateCursorApiKey: validateCursorApiKey2 } = await Promise.resolve().then(() => (init_macKeychain(), macKeychain_exports));
|
|
@@ -10505,7 +10546,7 @@ async function promptDeployLocation() {
|
|
|
10505
10546
|
async function promptTranscriptSources(wantCC, wantCursor) {
|
|
10506
10547
|
if (!wantCC && !wantCursor) return { cc: false, cursor: false };
|
|
10507
10548
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
10508
|
-
const
|
|
10549
|
+
const ask3 = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim().toLowerCase())));
|
|
10509
10550
|
try {
|
|
10510
10551
|
if (wantCC && wantCursor) {
|
|
10511
10552
|
console.log("Import and embed past session history? This indexes sessions so Ask Synkro");
|
|
@@ -10514,14 +10555,14 @@ async function promptTranscriptSources(wantCC, wantCursor) {
|
|
|
10514
10555
|
console.log(" 2. Claude Code only");
|
|
10515
10556
|
console.log(" 3. Both");
|
|
10516
10557
|
console.log(" n. Skip");
|
|
10517
|
-
const a2 = await
|
|
10558
|
+
const a2 = await ask3("Choose [1/2/3/n] (default: 3): ");
|
|
10518
10559
|
if (a2 === "n" || a2 === "no") return { cc: false, cursor: false };
|
|
10519
10560
|
if (a2 === "1") return { cc: false, cursor: true };
|
|
10520
10561
|
if (a2 === "2") return { cc: true, cursor: false };
|
|
10521
10562
|
return { cc: true, cursor: true };
|
|
10522
10563
|
}
|
|
10523
10564
|
const which2 = wantCursor ? "Cursor" : "Claude Code";
|
|
10524
|
-
const a = await
|
|
10565
|
+
const a = await ask3(
|
|
10525
10566
|
`Import and embed your ${which2} session history?
|
|
10526
10567
|
This indexes past sessions so Ask Synkro can answer questions about your
|
|
10527
10568
|
coding patterns and the dashboard shows full history. (Y/n) `
|
|
@@ -10709,7 +10750,7 @@ function writeConfigEnv(opts) {
|
|
|
10709
10750
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
10710
10751
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
10711
10752
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
10712
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
10753
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.81")}`
|
|
10713
10754
|
];
|
|
10714
10755
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
10715
10756
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -14104,6 +14145,182 @@ var init_localCc = __esm({
|
|
|
14104
14145
|
}
|
|
14105
14146
|
});
|
|
14106
14147
|
|
|
14148
|
+
// cli/commands/import.ts
|
|
14149
|
+
var import_exports = {};
|
|
14150
|
+
__export(import_exports, {
|
|
14151
|
+
importCommand: () => importCommand
|
|
14152
|
+
});
|
|
14153
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15, readdirSync as readdirSync5 } from "fs";
|
|
14154
|
+
import { homedir as homedir17 } from "os";
|
|
14155
|
+
import { join as join17 } from "path";
|
|
14156
|
+
import { execSync as execSync7 } from "child_process";
|
|
14157
|
+
import { createInterface as createInterface5 } from "readline";
|
|
14158
|
+
function readConfigEnv() {
|
|
14159
|
+
const out = {};
|
|
14160
|
+
try {
|
|
14161
|
+
for (const line of readFileSync15(CONFIG_PATH5, "utf-8").split("\n")) {
|
|
14162
|
+
const t = line.trim();
|
|
14163
|
+
if (!t || t.startsWith("#")) continue;
|
|
14164
|
+
const eq = t.indexOf("=");
|
|
14165
|
+
if (eq > 0) out[t.slice(0, eq).trim()] = t.slice(eq + 1).trim().replace(/^['"]|['"]$/g, "");
|
|
14166
|
+
}
|
|
14167
|
+
} catch {
|
|
14168
|
+
}
|
|
14169
|
+
return out;
|
|
14170
|
+
}
|
|
14171
|
+
function projectsFolder() {
|
|
14172
|
+
const sanitized = "-" + process.cwd().replace(/\//g, "-");
|
|
14173
|
+
const dir = join17(homedir17(), ".claude", "projects", sanitized);
|
|
14174
|
+
return existsSync17(dir) ? dir : null;
|
|
14175
|
+
}
|
|
14176
|
+
function repoName() {
|
|
14177
|
+
try {
|
|
14178
|
+
return execSync7("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim().split("/").pop() || "repo";
|
|
14179
|
+
} catch {
|
|
14180
|
+
return process.cwd().split("/").pop() || "repo";
|
|
14181
|
+
}
|
|
14182
|
+
}
|
|
14183
|
+
function extractText(content) {
|
|
14184
|
+
if (typeof content === "string") return content;
|
|
14185
|
+
if (Array.isArray(content)) {
|
|
14186
|
+
return content.map((b) => typeof b === "string" ? b : b?.type === "text" ? b.text || "" : "").filter(Boolean).join("\n");
|
|
14187
|
+
}
|
|
14188
|
+
return "";
|
|
14189
|
+
}
|
|
14190
|
+
function parseSession(filePath, sessionId) {
|
|
14191
|
+
const lines = readFileSync15(filePath, "utf-8").split("\n").filter(Boolean);
|
|
14192
|
+
const messages = [];
|
|
14193
|
+
const actions = [];
|
|
14194
|
+
let step = 0;
|
|
14195
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14196
|
+
let e;
|
|
14197
|
+
try {
|
|
14198
|
+
e = JSON.parse(lines[i]);
|
|
14199
|
+
} catch {
|
|
14200
|
+
continue;
|
|
14201
|
+
}
|
|
14202
|
+
const kind = e.type === "assistant" || e.message?.role === "assistant" ? "assistant" : e.type === "user" || e.message?.role === "user" ? "user" : "";
|
|
14203
|
+
if (!kind) continue;
|
|
14204
|
+
const msgObj = e.message;
|
|
14205
|
+
const text = extractText(msgObj?.content ?? e.content);
|
|
14206
|
+
if (!text.trim()) continue;
|
|
14207
|
+
const msg = {
|
|
14208
|
+
message_index: i,
|
|
14209
|
+
type: kind,
|
|
14210
|
+
content: text.slice(0, 8e3),
|
|
14211
|
+
timestamp: e.timestamp || null
|
|
14212
|
+
};
|
|
14213
|
+
if (kind === "assistant") {
|
|
14214
|
+
if (typeof msgObj?.model === "string") msg.model = msgObj.model;
|
|
14215
|
+
const u = msgObj?.usage;
|
|
14216
|
+
if (u && typeof u === "object") {
|
|
14217
|
+
msg.usage = {
|
|
14218
|
+
input_tokens: Number(u.input_tokens) || 0,
|
|
14219
|
+
output_tokens: Number(u.output_tokens) || 0,
|
|
14220
|
+
cache_creation_input_tokens: Number(u.cache_creation_input_tokens) || 0,
|
|
14221
|
+
cache_read_input_tokens: Number(u.cache_read_input_tokens) || 0
|
|
14222
|
+
};
|
|
14223
|
+
}
|
|
14224
|
+
const blocks = Array.isArray(msgObj?.content) ? msgObj.content : [];
|
|
14225
|
+
const toolCalls = blocks.filter((b) => b?.type === "tool_use").map((b) => ({ name: b.name, input: JSON.stringify(b.input || {}).slice(0, 500), id: b.id }));
|
|
14226
|
+
if (toolCalls.length) {
|
|
14227
|
+
msg.tool_calls = toolCalls;
|
|
14228
|
+
for (const t of toolCalls) actions.push({ step: ++step, tool: t.name, summary: "", file: null, outcome: null });
|
|
14229
|
+
}
|
|
14230
|
+
}
|
|
14231
|
+
messages.push(msg);
|
|
14232
|
+
}
|
|
14233
|
+
return { cc_session_id: sessionId, messages, actions };
|
|
14234
|
+
}
|
|
14235
|
+
function ask2(q) {
|
|
14236
|
+
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
14237
|
+
return new Promise((resolve4) => rl.question(q, (a) => {
|
|
14238
|
+
rl.close();
|
|
14239
|
+
resolve4(/^y(es)?$/i.test(a.trim()));
|
|
14240
|
+
}));
|
|
14241
|
+
}
|
|
14242
|
+
async function importCommand() {
|
|
14243
|
+
const config = readConfigEnv();
|
|
14244
|
+
const isCloud = config.SYNKRO_DEPLOY_LOCATION === "cloud" || config.SYNKRO_STORAGE_MODE === "cloud";
|
|
14245
|
+
const repo = repoName();
|
|
14246
|
+
const dir = projectsFolder();
|
|
14247
|
+
if (!dir) {
|
|
14248
|
+
console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
|
|
14249
|
+
return;
|
|
14250
|
+
}
|
|
14251
|
+
const files = readdirSync5(dir).filter((f) => f.endsWith(".jsonl"));
|
|
14252
|
+
if (!files.length) {
|
|
14253
|
+
console.log("No sessions to import.");
|
|
14254
|
+
return;
|
|
14255
|
+
}
|
|
14256
|
+
console.log(`Found ${files.length} session(s) for "${repo}" (${isCloud ? "cloud" : "local"} mode).`);
|
|
14257
|
+
if (!isCloud) {
|
|
14258
|
+
const ok2 = await ask2(`Import all ${files.length} historical session(s) into your on-device store? [y/N] `);
|
|
14259
|
+
if (!ok2) {
|
|
14260
|
+
console.log("Aborted \u2014 nothing imported.");
|
|
14261
|
+
return;
|
|
14262
|
+
}
|
|
14263
|
+
}
|
|
14264
|
+
const sessions = files.map((f) => parseSession(join17(dir, f), f.replace(".jsonl", ""))).filter((s) => s.messages.length > 0);
|
|
14265
|
+
const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
|
|
14266
|
+
let ok = 0, fail = 0;
|
|
14267
|
+
if (isCloud) {
|
|
14268
|
+
await ensureValidToken();
|
|
14269
|
+
const token = getAccessToken();
|
|
14270
|
+
if (!token) {
|
|
14271
|
+
console.log("Not authenticated \u2014 run `synkro login` first.");
|
|
14272
|
+
return;
|
|
14273
|
+
}
|
|
14274
|
+
const gateway = config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh";
|
|
14275
|
+
for (let i = 0; i < sessions.length; i += 20) {
|
|
14276
|
+
const batch = sessions.slice(i, i + 20);
|
|
14277
|
+
try {
|
|
14278
|
+
const r = await fetch(`${gateway}/api/v1/cli/sync-transcripts`, {
|
|
14279
|
+
method: "POST",
|
|
14280
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
14281
|
+
body: JSON.stringify({ repo, sessions: batch }),
|
|
14282
|
+
signal: AbortSignal.timeout(3e4)
|
|
14283
|
+
});
|
|
14284
|
+
if (r.ok) ok += batch.length;
|
|
14285
|
+
else {
|
|
14286
|
+
fail += batch.length;
|
|
14287
|
+
console.warn(` batch failed: HTTP ${r.status}`);
|
|
14288
|
+
}
|
|
14289
|
+
} catch (e) {
|
|
14290
|
+
fail += batch.length;
|
|
14291
|
+
console.warn(` batch error: ${e.message}`);
|
|
14292
|
+
}
|
|
14293
|
+
}
|
|
14294
|
+
} else {
|
|
14295
|
+
const port = config.SYNKRO_MCP_PORT || "18931";
|
|
14296
|
+
for (const s of sessions) {
|
|
14297
|
+
try {
|
|
14298
|
+
const r = await fetch(`http://127.0.0.1:${port}/api/ingest`, {
|
|
14299
|
+
method: "POST",
|
|
14300
|
+
headers: { "Content-Type": "application/json" },
|
|
14301
|
+
body: JSON.stringify({ capture_type: "transcript_sync", session_id: s.cc_session_id, repo, messages: s.messages, actions: s.actions }),
|
|
14302
|
+
signal: AbortSignal.timeout(15e3)
|
|
14303
|
+
});
|
|
14304
|
+
if (r.ok) ok++;
|
|
14305
|
+
else fail++;
|
|
14306
|
+
} catch {
|
|
14307
|
+
fail++;
|
|
14308
|
+
}
|
|
14309
|
+
}
|
|
14310
|
+
if (fail && ok === 0) console.log(" (is the local container running? `synkro status`)");
|
|
14311
|
+
}
|
|
14312
|
+
console.log(`
|
|
14313
|
+
\u2713 Imported ${ok} session(s) (${totalMsgs} messages)${fail ? `, ${fail} failed` : ""}.`);
|
|
14314
|
+
}
|
|
14315
|
+
var CONFIG_PATH5;
|
|
14316
|
+
var init_import = __esm({
|
|
14317
|
+
"cli/commands/import.ts"() {
|
|
14318
|
+
"use strict";
|
|
14319
|
+
init_stub();
|
|
14320
|
+
CONFIG_PATH5 = join17(homedir17(), ".synkro", "config.env");
|
|
14321
|
+
}
|
|
14322
|
+
});
|
|
14323
|
+
|
|
14107
14324
|
// cli/commands/lifecycle.ts
|
|
14108
14325
|
var lifecycle_exports = {};
|
|
14109
14326
|
__export(lifecycle_exports, {
|
|
@@ -14216,13 +14433,13 @@ var config_exports = {};
|
|
|
14216
14433
|
__export(config_exports, {
|
|
14217
14434
|
configCommand: () => configCommand
|
|
14218
14435
|
});
|
|
14219
|
-
import { readFileSync as
|
|
14220
|
-
import { join as
|
|
14221
|
-
import { homedir as
|
|
14222
|
-
function
|
|
14223
|
-
if (!
|
|
14436
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync11, existsSync as existsSync18 } from "fs";
|
|
14437
|
+
import { join as join18 } from "path";
|
|
14438
|
+
import { homedir as homedir18 } from "os";
|
|
14439
|
+
function readConfigEnv2() {
|
|
14440
|
+
if (!existsSync18(CONFIG_PATH6)) return {};
|
|
14224
14441
|
const out = {};
|
|
14225
|
-
for (const line of
|
|
14442
|
+
for (const line of readFileSync16(CONFIG_PATH6, "utf-8").split("\n")) {
|
|
14226
14443
|
const t = line.trim();
|
|
14227
14444
|
if (!t || t.startsWith("#")) continue;
|
|
14228
14445
|
const eq = t.indexOf("=");
|
|
@@ -14231,11 +14448,11 @@ function readConfigEnv() {
|
|
|
14231
14448
|
return out;
|
|
14232
14449
|
}
|
|
14233
14450
|
function updateConfigValue(key, value) {
|
|
14234
|
-
if (!
|
|
14451
|
+
if (!existsSync18(CONFIG_PATH6)) {
|
|
14235
14452
|
console.error("No config found. Run `synkro install` first.");
|
|
14236
14453
|
process.exit(1);
|
|
14237
14454
|
}
|
|
14238
|
-
const lines =
|
|
14455
|
+
const lines = readFileSync16(CONFIG_PATH6, "utf-8").split("\n");
|
|
14239
14456
|
const pattern = new RegExp(`^${key}=`);
|
|
14240
14457
|
let found = false;
|
|
14241
14458
|
const updated = lines.map((line) => {
|
|
@@ -14246,10 +14463,10 @@ function updateConfigValue(key, value) {
|
|
|
14246
14463
|
return line;
|
|
14247
14464
|
});
|
|
14248
14465
|
if (!found) updated.splice(updated.length - 1, 0, `${key}='${value}'`);
|
|
14249
|
-
writeFileSync11(
|
|
14466
|
+
writeFileSync11(CONFIG_PATH6, updated.join("\n"), "utf-8");
|
|
14250
14467
|
}
|
|
14251
14468
|
async function reconcileContainer() {
|
|
14252
|
-
const cfg =
|
|
14469
|
+
const cfg = readConfigEnv2();
|
|
14253
14470
|
const cloudOnly = (cfg.SYNKRO_GRADING_MODE || "local") === "byok" && (cfg.SYNKRO_STORAGE_MODE || "local") === "cloud";
|
|
14254
14471
|
try {
|
|
14255
14472
|
const { dockerInstall: dockerInstall2, dockerSafeStop: dockerSafeStop2, readContainerConfig: readContainerConfig2, assertDockerAvailable: assertDockerAvailable2, waitForContainerReady: waitForContainerReady2 } = await Promise.resolve().then(() => (init_dockerInstall(), dockerInstall_exports));
|
|
@@ -14273,7 +14490,7 @@ async function reconcileContainer() {
|
|
|
14273
14490
|
}
|
|
14274
14491
|
async function configCommand(args2) {
|
|
14275
14492
|
if (args2.length === 0) {
|
|
14276
|
-
const config2 =
|
|
14493
|
+
const config2 = readConfigEnv2();
|
|
14277
14494
|
console.log("Synkro config:\n");
|
|
14278
14495
|
console.log(` grading: ${config2.SYNKRO_GRADING_MODE || "local"}`);
|
|
14279
14496
|
console.log(` storage: ${config2.SYNKRO_STORAGE_MODE || "local"}`);
|
|
@@ -14328,7 +14545,7 @@ To change:`);
|
|
|
14328
14545
|
process.exit(1);
|
|
14329
14546
|
}
|
|
14330
14547
|
const token = getAccessToken();
|
|
14331
|
-
const config =
|
|
14548
|
+
const config = readConfigEnv2();
|
|
14332
14549
|
const gatewayUrl = (config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
14333
14550
|
try {
|
|
14334
14551
|
const resp = await fetch(`${gatewayUrl}/api/v1/cli/me`, {
|
|
@@ -14351,25 +14568,25 @@ To change:`);
|
|
|
14351
14568
|
updateConfigValue("SYNKRO_INFERENCE", inferenceValue);
|
|
14352
14569
|
console.log(`\u2713 Inference set to '${inferenceValue}'.`);
|
|
14353
14570
|
}
|
|
14354
|
-
var SYNKRO_DIR7,
|
|
14571
|
+
var SYNKRO_DIR7, CONFIG_PATH6;
|
|
14355
14572
|
var init_config = __esm({
|
|
14356
14573
|
"cli/commands/config.ts"() {
|
|
14357
14574
|
"use strict";
|
|
14358
14575
|
init_stub();
|
|
14359
|
-
SYNKRO_DIR7 =
|
|
14360
|
-
|
|
14576
|
+
SYNKRO_DIR7 = join18(homedir18(), ".synkro");
|
|
14577
|
+
CONFIG_PATH6 = join18(SYNKRO_DIR7, "config.env");
|
|
14361
14578
|
}
|
|
14362
14579
|
});
|
|
14363
14580
|
|
|
14364
14581
|
// cli/bootstrap.js
|
|
14365
|
-
import { readFileSync as
|
|
14582
|
+
import { readFileSync as readFileSync17, existsSync as existsSync19 } from "fs";
|
|
14366
14583
|
import { resolve as resolve3 } from "path";
|
|
14367
14584
|
var envCandidates = [
|
|
14368
14585
|
resolve3(process.env.HOME ?? "", ".synkro", "config.env")
|
|
14369
14586
|
];
|
|
14370
14587
|
for (const envPath of envCandidates) {
|
|
14371
|
-
if (!
|
|
14372
|
-
const envContent =
|
|
14588
|
+
if (!existsSync19(envPath)) continue;
|
|
14589
|
+
const envContent = readFileSync17(envPath, "utf-8");
|
|
14373
14590
|
for (const line of envContent.split("\n")) {
|
|
14374
14591
|
const trimmed = line.trim();
|
|
14375
14592
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -14384,7 +14601,7 @@ var args = process.argv.slice(2);
|
|
|
14384
14601
|
var cmd = args[0] || "";
|
|
14385
14602
|
var subArgs = args.slice(1);
|
|
14386
14603
|
function printVersion() {
|
|
14387
|
-
console.log("1.6.
|
|
14604
|
+
console.log("1.6.81");
|
|
14388
14605
|
}
|
|
14389
14606
|
function printHelp2() {
|
|
14390
14607
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|
|
@@ -14458,6 +14675,11 @@ async function main() {
|
|
|
14458
14675
|
await localCcCommand2(subArgs);
|
|
14459
14676
|
break;
|
|
14460
14677
|
}
|
|
14678
|
+
case "import": {
|
|
14679
|
+
const { importCommand: importCommand2 } = await Promise.resolve().then(() => (init_import(), import_exports));
|
|
14680
|
+
await importCommand2();
|
|
14681
|
+
break;
|
|
14682
|
+
}
|
|
14461
14683
|
case "version":
|
|
14462
14684
|
case "--version":
|
|
14463
14685
|
case "-v": {
|