@synkro-sh/cli 1.7.94 → 1.7.95
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 +243 -32
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -147,7 +147,7 @@ function getIdentity() {
|
|
|
147
147
|
if (cached2) return cached2;
|
|
148
148
|
let cliVersion = "0.0.0";
|
|
149
149
|
try {
|
|
150
|
-
cliVersion = "1.7.
|
|
150
|
+
cliVersion = "1.7.95";
|
|
151
151
|
} catch {
|
|
152
152
|
}
|
|
153
153
|
const creds = loadCredentialsIdentity();
|
|
@@ -6612,8 +6612,16 @@ async function dockerInstall(opts = {}) {
|
|
|
6612
6612
|
...process.env.SYNKRO_MAX_BATCH_SIZE ? ["-e", `SYNKRO_MAX_BATCH_SIZE=${process.env.SYNKRO_MAX_BATCH_SIZE}`] : [],
|
|
6613
6613
|
// Full verifier prompt/response tracing is explicit opt-in because it contains source code.
|
|
6614
6614
|
...process.env.SYNKRO_VERIFY_TRACE === "1" ? ["-e", "SYNKRO_VERIFY_TRACE=1"] : [],
|
|
6615
|
-
//
|
|
6616
|
-
|
|
6615
|
+
// Explicit model overrides are preserved across local install/update for
|
|
6616
|
+
// every provider and isolated lane. The server reports the resolved values
|
|
6617
|
+
// in /healthz so operators can audit exactly what is spending tokens.
|
|
6618
|
+
...[
|
|
6619
|
+
"SYNKRO_CLAUDE_MODEL",
|
|
6620
|
+
"SYNKRO_CURSOR_MODEL",
|
|
6621
|
+
"SYNKRO_CODEX_MODEL",
|
|
6622
|
+
"SYNKRO_CONDUCTOR_MODEL",
|
|
6623
|
+
"SYNKRO_ROUTE_MODEL"
|
|
6624
|
+
].flatMap((key) => process.env[key] ? ["-e", `${key}=${process.env[key]}`] : []),
|
|
6617
6625
|
// Fix-poll kill switch. Default ON in the image; a benchmark/headless run
|
|
6618
6626
|
// (e.g. sec-code-bench) sets SYNKRO_FIX_POLL=0 so ask-mode violations skip the
|
|
6619
6627
|
// interactive AskUserQuestion poll and fall through to generate-the-fix. Only
|
|
@@ -6871,7 +6879,7 @@ var init_dockerInstall = __esm({
|
|
|
6871
6879
|
HOST_PGLITE_PORT = parseInt(process.env.SYNKRO_HOST_PGLITE_PORT || "15433", 10);
|
|
6872
6880
|
CONTAINER_NAME = resolveContainerName();
|
|
6873
6881
|
defaultImageVersion = () => {
|
|
6874
|
-
if (true) return "1.7.
|
|
6882
|
+
if (true) return "1.7.95";
|
|
6875
6883
|
try {
|
|
6876
6884
|
const pkg = JSON.parse(readFileSync17(new URL("../../package.json", import.meta.url), "utf8"));
|
|
6877
6885
|
if (pkg.version) return pkg.version;
|
|
@@ -7564,6 +7572,94 @@ var init_codexTranscriptMessages = __esm({
|
|
|
7564
7572
|
}
|
|
7565
7573
|
});
|
|
7566
7574
|
|
|
7575
|
+
// cli/scanning/claudeTranscriptUsage.ts
|
|
7576
|
+
function tokenCount(value) {
|
|
7577
|
+
const parsed = Number(value);
|
|
7578
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
7579
|
+
}
|
|
7580
|
+
function isoDay(value, fallbackDay) {
|
|
7581
|
+
if (typeof value === "string") {
|
|
7582
|
+
const parsed = new Date(value);
|
|
7583
|
+
if (Number.isFinite(parsed.getTime())) return parsed.toISOString().slice(0, 10);
|
|
7584
|
+
}
|
|
7585
|
+
return fallbackDay;
|
|
7586
|
+
}
|
|
7587
|
+
function parseClaudeTranscriptUsage(transcript, fallbackDay = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), options = {}) {
|
|
7588
|
+
const seen = options.seenStableIds ?? /* @__PURE__ */ new Set();
|
|
7589
|
+
const rollups = /* @__PURE__ */ new Map();
|
|
7590
|
+
const usage = {
|
|
7591
|
+
input_tokens: 0,
|
|
7592
|
+
output_tokens: 0,
|
|
7593
|
+
cache_creation_input_tokens: 0,
|
|
7594
|
+
cache_read_input_tokens: 0
|
|
7595
|
+
};
|
|
7596
|
+
let model = "";
|
|
7597
|
+
let turns = 0;
|
|
7598
|
+
let lineIndex = 0;
|
|
7599
|
+
for (const line of transcript.split("\n")) {
|
|
7600
|
+
lineIndex += 1;
|
|
7601
|
+
const text = line.trim();
|
|
7602
|
+
if (!text) continue;
|
|
7603
|
+
try {
|
|
7604
|
+
const entry = JSON.parse(text);
|
|
7605
|
+
const message = entry?.message;
|
|
7606
|
+
if (message?.role !== "assistant" || !message.usage || typeof message.usage !== "object") {
|
|
7607
|
+
continue;
|
|
7608
|
+
}
|
|
7609
|
+
const stableId = typeof entry.uuid === "string" && entry.uuid ? `uuid:${entry.uuid}` : typeof message.id === "string" && message.id ? `message:${message.id}:${String(entry.timestamp || "")}` : `${options.sourceId || "transcript"}:line:${lineIndex}`;
|
|
7610
|
+
if (seen.has(stableId)) continue;
|
|
7611
|
+
seen.add(stableId);
|
|
7612
|
+
const counts = {
|
|
7613
|
+
input_tokens: tokenCount(message.usage.input_tokens),
|
|
7614
|
+
output_tokens: tokenCount(message.usage.output_tokens),
|
|
7615
|
+
cache_creation_input_tokens: tokenCount(message.usage.cache_creation_input_tokens),
|
|
7616
|
+
cache_read_input_tokens: tokenCount(message.usage.cache_read_input_tokens)
|
|
7617
|
+
};
|
|
7618
|
+
const countTotal = counts.input_tokens + counts.output_tokens + counts.cache_creation_input_tokens + counts.cache_read_input_tokens;
|
|
7619
|
+
if (countTotal === 0) continue;
|
|
7620
|
+
const entryModel = typeof message.model === "string" && message.model ? message.model : "unknown";
|
|
7621
|
+
if (entryModel !== "<synthetic>") model = entryModel;
|
|
7622
|
+
const day = isoDay(entry.timestamp, fallbackDay);
|
|
7623
|
+
const key = `${day}\0${entryModel}`;
|
|
7624
|
+
const row = rollups.get(key) ?? {
|
|
7625
|
+
day,
|
|
7626
|
+
model: entryModel,
|
|
7627
|
+
turns: 0,
|
|
7628
|
+
input_tokens: 0,
|
|
7629
|
+
output_tokens: 0,
|
|
7630
|
+
cache_creation_input_tokens: 0,
|
|
7631
|
+
cache_read_input_tokens: 0
|
|
7632
|
+
};
|
|
7633
|
+
row.turns += 1;
|
|
7634
|
+
row.input_tokens += counts.input_tokens;
|
|
7635
|
+
row.output_tokens += counts.output_tokens;
|
|
7636
|
+
row.cache_creation_input_tokens += counts.cache_creation_input_tokens;
|
|
7637
|
+
row.cache_read_input_tokens += counts.cache_read_input_tokens;
|
|
7638
|
+
rollups.set(key, row);
|
|
7639
|
+
turns += 1;
|
|
7640
|
+
usage.input_tokens += counts.input_tokens;
|
|
7641
|
+
usage.output_tokens += counts.output_tokens;
|
|
7642
|
+
usage.cache_creation_input_tokens += counts.cache_creation_input_tokens;
|
|
7643
|
+
usage.cache_read_input_tokens += counts.cache_read_input_tokens;
|
|
7644
|
+
} catch {
|
|
7645
|
+
}
|
|
7646
|
+
}
|
|
7647
|
+
if (turns === 0) return null;
|
|
7648
|
+
return {
|
|
7649
|
+
usage,
|
|
7650
|
+
model: model || "unknown",
|
|
7651
|
+
rollups: [...rollups.values()].sort(
|
|
7652
|
+
(a, b) => a.day.localeCompare(b.day) || a.model.localeCompare(b.model)
|
|
7653
|
+
),
|
|
7654
|
+
turns
|
|
7655
|
+
};
|
|
7656
|
+
}
|
|
7657
|
+
var init_claudeTranscriptUsage = __esm({
|
|
7658
|
+
"cli/scanning/claudeTranscriptUsage.ts"() {
|
|
7659
|
+
"use strict";
|
|
7660
|
+
}
|
|
7661
|
+
});
|
|
7662
|
+
|
|
7567
7663
|
// cli/commands/install.ts
|
|
7568
7664
|
var install_exports = {};
|
|
7569
7665
|
__export(install_exports, {
|
|
@@ -7581,7 +7677,7 @@ __export(install_exports, {
|
|
|
7581
7677
|
});
|
|
7582
7678
|
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";
|
|
7583
7679
|
import { homedir as homedir21 } from "os";
|
|
7584
|
-
import { join as join19, isAbsolute, resolve as resolve4 } from "path";
|
|
7680
|
+
import { basename, join as join19, isAbsolute, resolve as resolve4, sep } from "path";
|
|
7585
7681
|
import { execSync as execSync4, spawn as spawn5 } from "child_process";
|
|
7586
7682
|
import { createInterface as createInterface2 } from "readline";
|
|
7587
7683
|
import { createHash as createHash4 } from "crypto";
|
|
@@ -7840,7 +7936,7 @@ function writeConfigEnv(opts) {
|
|
|
7840
7936
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle2(credsPath)}`,
|
|
7841
7937
|
`SYNKRO_TIER=${shellQuoteSingle2(safeTier)}`,
|
|
7842
7938
|
`SYNKRO_INFERENCE=${shellQuoteSingle2(safeInference)}`,
|
|
7843
|
-
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.
|
|
7939
|
+
`SYNKRO_VERSION=${shellQuoteSingle2("1.7.95")}`
|
|
7844
7940
|
];
|
|
7845
7941
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle2(safeSynkroBin)}`);
|
|
7846
7942
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle2(safeUserId)}`);
|
|
@@ -8018,6 +8114,11 @@ async function provisionCloudContainer(opts) {
|
|
|
8018
8114
|
cursor_workers: cursorWorkers,
|
|
8019
8115
|
codex_workers: codexWorkers,
|
|
8020
8116
|
conductor_provider: selectedKind,
|
|
8117
|
+
claude_model: process.env.SYNKRO_CLAUDE_MODEL || "",
|
|
8118
|
+
cursor_model: process.env.SYNKRO_CURSOR_MODEL || "",
|
|
8119
|
+
codex_model: process.env.SYNKRO_CODEX_MODEL || "",
|
|
8120
|
+
conductor_model: process.env.SYNKRO_CONDUCTOR_MODEL || "",
|
|
8121
|
+
route_model: process.env.SYNKRO_ROUTE_MODEL || "",
|
|
8021
8122
|
cursor_api_key: cursorApiKey,
|
|
8022
8123
|
// never logged; gateway stores it as the org secret
|
|
8023
8124
|
connected_repo: repo,
|
|
@@ -8582,7 +8683,7 @@ async function installCommand(opts = {}) {
|
|
|
8582
8683
|
await setTelemetryState({ enabled: true, remoteFlushEnabled: telemetryConsent });
|
|
8583
8684
|
emit("install", {
|
|
8584
8685
|
phase: "started",
|
|
8585
|
-
cli_version_to: "1.7.
|
|
8686
|
+
cli_version_to: "1.7.95",
|
|
8586
8687
|
agents_detected: agents.map((a) => a.kind),
|
|
8587
8688
|
with_github: false,
|
|
8588
8689
|
with_local_cc: false,
|
|
@@ -9649,8 +9750,8 @@ function ensureReachabilityGitHook() {
|
|
|
9649
9750
|
}
|
|
9650
9751
|
return "updated";
|
|
9651
9752
|
}
|
|
9652
|
-
const
|
|
9653
|
-
writeFileSync17(hookPath, cur +
|
|
9753
|
+
const sep3 = cur.endsWith("\n") ? "" : "\n";
|
|
9754
|
+
writeFileSync17(hookPath, cur + sep3 + "\n" + block + "\n");
|
|
9654
9755
|
try {
|
|
9655
9756
|
chmodSync5(hookPath, 493);
|
|
9656
9757
|
} catch {
|
|
@@ -9677,10 +9778,33 @@ function detectGitRepo2() {
|
|
|
9677
9778
|
}
|
|
9678
9779
|
function getClaudeProjectsFolder() {
|
|
9679
9780
|
const cwd = process.cwd();
|
|
9680
|
-
const sanitized =
|
|
9781
|
+
const sanitized = cwd.replace(/\//g, "-");
|
|
9681
9782
|
const projectsDir = join19(homedir21(), ".claude", "projects", sanitized);
|
|
9682
9783
|
return existsSync22(projectsDir) ? projectsDir : null;
|
|
9683
9784
|
}
|
|
9785
|
+
function getClaudeTranscriptFileEntries(projectsDir) {
|
|
9786
|
+
let relativeFiles = [];
|
|
9787
|
+
try {
|
|
9788
|
+
relativeFiles = readdirSync4(projectsDir, { recursive: true, encoding: "utf-8" });
|
|
9789
|
+
} catch {
|
|
9790
|
+
return [];
|
|
9791
|
+
}
|
|
9792
|
+
return relativeFiles.filter((file) => file.endsWith(".jsonl")).map((file) => {
|
|
9793
|
+
const parts = file.split(sep);
|
|
9794
|
+
const subagentsIndex = parts.lastIndexOf("subagents");
|
|
9795
|
+
if (subagentsIndex > 0) {
|
|
9796
|
+
return {
|
|
9797
|
+
filePath: join19(projectsDir, file),
|
|
9798
|
+
sessionId: basename(file, ".jsonl"),
|
|
9799
|
+
parentSessionId: parts[subagentsIndex - 1]
|
|
9800
|
+
};
|
|
9801
|
+
}
|
|
9802
|
+
return {
|
|
9803
|
+
filePath: join19(projectsDir, file),
|
|
9804
|
+
sessionId: basename(file, ".jsonl")
|
|
9805
|
+
};
|
|
9806
|
+
});
|
|
9807
|
+
}
|
|
9684
9808
|
function extractSessionInsights(projectsDir) {
|
|
9685
9809
|
const insights = [];
|
|
9686
9810
|
const files = readdirSync4(projectsDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -9980,23 +10104,40 @@ function parseTranscriptFile(filePath) {
|
|
|
9980
10104
|
async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
9981
10105
|
const projectsDir = getClaudeProjectsFolder();
|
|
9982
10106
|
if (!projectsDir) return { sessions: 0, messages: 0 };
|
|
9983
|
-
const files =
|
|
10107
|
+
const files = getClaudeTranscriptFileEntries(projectsDir);
|
|
9984
10108
|
if (files.length === 0) return { sessions: 0, messages: 0 };
|
|
9985
10109
|
console.log(` Found ${files.length} CC session transcripts, importing + embedding...`);
|
|
9986
10110
|
let totalSessions = 0;
|
|
9987
10111
|
let totalMessages = 0;
|
|
10112
|
+
const seenStableIds = /* @__PURE__ */ new Set();
|
|
9988
10113
|
for (let i = 0; i < files.length; i++) {
|
|
9989
10114
|
const file = files[i];
|
|
9990
|
-
const sessionId = file.
|
|
9991
|
-
const filePath =
|
|
10115
|
+
const sessionId = file.sessionId;
|
|
10116
|
+
const filePath = file.filePath;
|
|
9992
10117
|
try {
|
|
10118
|
+
const transcript = readFileSync21(filePath, "utf-8");
|
|
10119
|
+
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
10120
|
+
transcript,
|
|
10121
|
+
statSync2(filePath).mtime.toISOString().slice(0, 10),
|
|
10122
|
+
{ seenStableIds, sourceId: file.parentSessionId ? `${file.parentSessionId}:subagent:${sessionId}` : sessionId }
|
|
10123
|
+
);
|
|
9993
10124
|
const allMessages = parseTranscriptFile(filePath);
|
|
9994
10125
|
const messages = allMessages.length > 500 ? allMessages.slice(-500) : allMessages;
|
|
9995
10126
|
if (messages.length === 0) continue;
|
|
9996
10127
|
const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/conversation-sync`, {
|
|
9997
10128
|
method: "POST",
|
|
9998
10129
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpToken}` },
|
|
9999
|
-
body: JSON.stringify({
|
|
10130
|
+
body: JSON.stringify({
|
|
10131
|
+
session_id: sessionId,
|
|
10132
|
+
parent_session_id: file.parentSessionId,
|
|
10133
|
+
repo,
|
|
10134
|
+
messages,
|
|
10135
|
+
session_usage: transcriptUsage?.usage,
|
|
10136
|
+
usage_rollups: transcriptUsage?.rollups ?? [],
|
|
10137
|
+
model: transcriptUsage?.model,
|
|
10138
|
+
harness: "claude-code",
|
|
10139
|
+
usage_cumulative: true
|
|
10140
|
+
}),
|
|
10000
10141
|
signal: AbortSignal.timeout(15e3)
|
|
10001
10142
|
});
|
|
10002
10143
|
if (resp.ok) {
|
|
@@ -10010,9 +10151,10 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
10010
10151
|
process.stdout.write(`\r Progress: ${i + 1}/${files.length} sessions (${totalMessages} messages embedded) `);
|
|
10011
10152
|
}
|
|
10012
10153
|
try {
|
|
10013
|
-
const content = readFileSync21(
|
|
10154
|
+
const content = readFileSync21(filePath, "utf-8");
|
|
10014
10155
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
10015
|
-
|
|
10156
|
+
const offsetId = file.parentSessionId ? `${file.parentSessionId}_${sessionId}` : sessionId;
|
|
10157
|
+
writeFileSync17(join19(OFFSETS_DIR, offsetId), String(lineCount), "utf-8");
|
|
10016
10158
|
} catch {
|
|
10017
10159
|
}
|
|
10018
10160
|
}
|
|
@@ -10022,23 +10164,39 @@ async function syncTranscriptsLocal(mcpPort, mcpToken, repo) {
|
|
|
10022
10164
|
async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
10023
10165
|
const projectsDir = getClaudeProjectsFolder();
|
|
10024
10166
|
if (!projectsDir) return { sessions: 0, messages: 0 };
|
|
10025
|
-
const files =
|
|
10167
|
+
const files = getClaudeTranscriptFileEntries(projectsDir);
|
|
10026
10168
|
if (files.length === 0) return { sessions: 0, messages: 0 };
|
|
10027
10169
|
console.log(`Found ${files.length} CC session transcripts, syncing...`);
|
|
10028
10170
|
const maxMessagesPerSession = 500;
|
|
10029
10171
|
let totalSessions = 0;
|
|
10030
10172
|
let totalMessages = 0;
|
|
10173
|
+
const seenStableIds = /* @__PURE__ */ new Set();
|
|
10031
10174
|
for (let i = 0; i < files.length; i += 5) {
|
|
10032
10175
|
const batch = files.slice(i, i + 5);
|
|
10033
10176
|
const sessions = [];
|
|
10034
10177
|
for (const file of batch) {
|
|
10035
|
-
const sessionId = file.
|
|
10036
|
-
const filePath =
|
|
10178
|
+
const sessionId = file.sessionId;
|
|
10179
|
+
const filePath = file.filePath;
|
|
10037
10180
|
try {
|
|
10181
|
+
const transcript = readFileSync21(filePath, "utf-8");
|
|
10182
|
+
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
10183
|
+
transcript,
|
|
10184
|
+
statSync2(filePath).mtime.toISOString().slice(0, 10),
|
|
10185
|
+
{ seenStableIds, sourceId: file.parentSessionId ? `${file.parentSessionId}:subagent:${sessionId}` : sessionId }
|
|
10186
|
+
);
|
|
10038
10187
|
const allMessages = parseTranscriptFile(filePath);
|
|
10039
10188
|
const messages = allMessages.length > maxMessagesPerSession ? allMessages.slice(-maxMessagesPerSession) : allMessages;
|
|
10040
10189
|
if (messages.length > 0) {
|
|
10041
|
-
sessions.push({
|
|
10190
|
+
sessions.push({
|
|
10191
|
+
cc_session_id: sessionId,
|
|
10192
|
+
parent_session_id: file.parentSessionId,
|
|
10193
|
+
messages,
|
|
10194
|
+
model: transcriptUsage?.model,
|
|
10195
|
+
session_usage: transcriptUsage?.usage,
|
|
10196
|
+
usage_rollups: transcriptUsage?.rollups ?? [],
|
|
10197
|
+
harness: "claude-code",
|
|
10198
|
+
usage_cumulative: true
|
|
10199
|
+
});
|
|
10042
10200
|
}
|
|
10043
10201
|
} catch {
|
|
10044
10202
|
}
|
|
@@ -10061,12 +10219,13 @@ async function syncTranscriptsBulk(gatewayUrl, token, repo) {
|
|
|
10061
10219
|
} catch {
|
|
10062
10220
|
}
|
|
10063
10221
|
for (const file of batch) {
|
|
10064
|
-
const sessionId = file.
|
|
10065
|
-
const filePath =
|
|
10222
|
+
const sessionId = file.sessionId;
|
|
10223
|
+
const filePath = file.filePath;
|
|
10066
10224
|
try {
|
|
10067
10225
|
const content = readFileSync21(filePath, "utf-8");
|
|
10068
10226
|
const lineCount = content.split("\n").filter(Boolean).length;
|
|
10069
|
-
|
|
10227
|
+
const offsetId = file.parentSessionId ? `${file.parentSessionId}_${sessionId}` : sessionId;
|
|
10228
|
+
writeFileSync17(join19(OFFSETS_DIR, offsetId), String(lineCount), "utf-8");
|
|
10070
10229
|
} catch {
|
|
10071
10230
|
}
|
|
10072
10231
|
}
|
|
@@ -10149,6 +10308,7 @@ var init_install = __esm({
|
|
|
10149
10308
|
init_graderSmoke();
|
|
10150
10309
|
init_codexTranscriptUsage();
|
|
10151
10310
|
init_codexTranscriptMessages();
|
|
10311
|
+
init_claudeTranscriptUsage();
|
|
10152
10312
|
SYNKRO_DIR11 = join19(homedir21(), ".synkro");
|
|
10153
10313
|
HOOKS_DIR = join19(SYNKRO_DIR11, "hooks");
|
|
10154
10314
|
CONFIG_PATH4 = join19(SYNKRO_DIR11, "config.env");
|
|
@@ -12745,9 +12905,9 @@ var import_exports = {};
|
|
|
12745
12905
|
__export(import_exports, {
|
|
12746
12906
|
importCommand: () => importCommand
|
|
12747
12907
|
});
|
|
12748
|
-
import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6 } from "fs";
|
|
12908
|
+
import { existsSync as existsSync29, readFileSync as readFileSync27, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
12749
12909
|
import { homedir as homedir28 } from "os";
|
|
12750
|
-
import { join as join27 } from "path";
|
|
12910
|
+
import { basename as basename2, join as join27, sep as sep2 } from "path";
|
|
12751
12911
|
import { execSync as execSync6 } from "child_process";
|
|
12752
12912
|
import { createInterface as createInterface4 } from "readline";
|
|
12753
12913
|
function readMcpJwt() {
|
|
@@ -12775,6 +12935,23 @@ function projectsFolder() {
|
|
|
12775
12935
|
const dir = join27(homedir28(), ".claude", "projects", sanitized);
|
|
12776
12936
|
return existsSync29(dir) ? dir : null;
|
|
12777
12937
|
}
|
|
12938
|
+
function transcriptFiles(projectsDir) {
|
|
12939
|
+
let relativeFiles = [];
|
|
12940
|
+
try {
|
|
12941
|
+
relativeFiles = readdirSync6(projectsDir, { recursive: true, encoding: "utf-8" });
|
|
12942
|
+
} catch {
|
|
12943
|
+
return [];
|
|
12944
|
+
}
|
|
12945
|
+
return relativeFiles.filter((file) => file.endsWith(".jsonl")).map((file) => {
|
|
12946
|
+
const parts = file.split(sep2);
|
|
12947
|
+
const subagentsIndex = parts.lastIndexOf("subagents");
|
|
12948
|
+
return {
|
|
12949
|
+
filePath: join27(projectsDir, file),
|
|
12950
|
+
sessionId: basename2(file, ".jsonl"),
|
|
12951
|
+
parentSessionId: subagentsIndex > 0 ? parts[subagentsIndex - 1] : void 0
|
|
12952
|
+
};
|
|
12953
|
+
});
|
|
12954
|
+
}
|
|
12778
12955
|
function repoName() {
|
|
12779
12956
|
try {
|
|
12780
12957
|
const url = execSync6("git config --get remote.origin.url", { encoding: "utf-8" }).trim();
|
|
@@ -12811,8 +12988,18 @@ function extractToolResultText(content, e) {
|
|
|
12811
12988
|
}
|
|
12812
12989
|
return t;
|
|
12813
12990
|
}
|
|
12814
|
-
function parseSession(
|
|
12815
|
-
const
|
|
12991
|
+
function parseSession(file, seenStableIds) {
|
|
12992
|
+
const { filePath, sessionId, parentSessionId } = file;
|
|
12993
|
+
const transcript = readFileSync27(filePath, "utf-8");
|
|
12994
|
+
const lines = transcript.split("\n").filter(Boolean);
|
|
12995
|
+
const transcriptUsage = parseClaudeTranscriptUsage(
|
|
12996
|
+
transcript,
|
|
12997
|
+
statSync4(filePath).mtime.toISOString().slice(0, 10),
|
|
12998
|
+
{
|
|
12999
|
+
seenStableIds,
|
|
13000
|
+
sourceId: parentSessionId ? `${parentSessionId}:subagent:${sessionId}` : sessionId
|
|
13001
|
+
}
|
|
13002
|
+
);
|
|
12816
13003
|
const messages = [];
|
|
12817
13004
|
const actions = [];
|
|
12818
13005
|
let step = 0;
|
|
@@ -12861,7 +13048,17 @@ function parseSession(filePath, sessionId) {
|
|
|
12861
13048
|
}
|
|
12862
13049
|
messages.push(msg);
|
|
12863
13050
|
}
|
|
12864
|
-
return {
|
|
13051
|
+
return {
|
|
13052
|
+
cc_session_id: sessionId,
|
|
13053
|
+
parent_session_id: parentSessionId,
|
|
13054
|
+
messages,
|
|
13055
|
+
actions,
|
|
13056
|
+
model: transcriptUsage?.model,
|
|
13057
|
+
session_usage: transcriptUsage?.usage,
|
|
13058
|
+
usage_rollups: transcriptUsage?.rollups ?? [],
|
|
13059
|
+
harness: "claude-code",
|
|
13060
|
+
usage_cumulative: true
|
|
13061
|
+
};
|
|
12865
13062
|
}
|
|
12866
13063
|
function ask2(q) {
|
|
12867
13064
|
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
@@ -12879,7 +13076,7 @@ async function importCommand() {
|
|
|
12879
13076
|
console.log("No Claude Code transcripts found for this repo (~/.claude/projects).");
|
|
12880
13077
|
return;
|
|
12881
13078
|
}
|
|
12882
|
-
const files =
|
|
13079
|
+
const files = transcriptFiles(dir);
|
|
12883
13080
|
if (!files.length) {
|
|
12884
13081
|
console.log("No sessions to import.");
|
|
12885
13082
|
return;
|
|
@@ -12892,7 +13089,8 @@ async function importCommand() {
|
|
|
12892
13089
|
return;
|
|
12893
13090
|
}
|
|
12894
13091
|
}
|
|
12895
|
-
const
|
|
13092
|
+
const seenStableIds = /* @__PURE__ */ new Set();
|
|
13093
|
+
const sessions = files.map((file) => parseSession(file, seenStableIds)).filter((s) => s.messages.length > 0);
|
|
12896
13094
|
const totalMsgs = sessions.reduce((n, s) => n + s.messages.length, 0);
|
|
12897
13095
|
let ok = 0, fail = 0;
|
|
12898
13096
|
if (isCloud) {
|
|
@@ -12938,7 +13136,19 @@ async function importCommand() {
|
|
|
12938
13136
|
const r = await fetch(`http://127.0.0.1:${port}/api/ingest`, {
|
|
12939
13137
|
method: "POST",
|
|
12940
13138
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mcpJwt2}` },
|
|
12941
|
-
body: JSON.stringify({
|
|
13139
|
+
body: JSON.stringify({
|
|
13140
|
+
capture_type: "transcript_sync",
|
|
13141
|
+
session_id: s.cc_session_id,
|
|
13142
|
+
parent_session_id: s.parent_session_id,
|
|
13143
|
+
repo,
|
|
13144
|
+
messages: s.messages,
|
|
13145
|
+
actions: s.actions,
|
|
13146
|
+
model: s.model,
|
|
13147
|
+
session_usage: s.session_usage,
|
|
13148
|
+
usage_rollups: s.usage_rollups,
|
|
13149
|
+
harness: s.harness,
|
|
13150
|
+
usage_cumulative: true
|
|
13151
|
+
}),
|
|
12942
13152
|
// A full session can carry thousands of turns — 15s timed out mid-import.
|
|
12943
13153
|
signal: AbortSignal.timeout(12e4)
|
|
12944
13154
|
});
|
|
@@ -12967,6 +13177,7 @@ var init_import = __esm({
|
|
|
12967
13177
|
"cli/commands/import.ts"() {
|
|
12968
13178
|
"use strict";
|
|
12969
13179
|
init_stub();
|
|
13180
|
+
init_claudeTranscriptUsage();
|
|
12970
13181
|
CONFIG_PATH7 = join27(homedir28(), ".synkro", "config.env");
|
|
12971
13182
|
}
|
|
12972
13183
|
});
|
|
@@ -14339,7 +14550,7 @@ var subArgs = args.slice(1);
|
|
|
14339
14550
|
var isDetachedChild = process.env.SYNKRO_TELEMETRY_DETACHED === "1";
|
|
14340
14551
|
var FLUSH_SKIP = /* @__PURE__ */ new Set(["grade", "version", "--version", "-v", "help", "--help", "-h", ""]);
|
|
14341
14552
|
function printVersion() {
|
|
14342
|
-
console.log("1.7.
|
|
14553
|
+
console.log("1.7.95");
|
|
14343
14554
|
}
|
|
14344
14555
|
function printHelp2() {
|
|
14345
14556
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|