@synkro-sh/cli 1.6.81 → 1.6.83
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 +164 -65
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -1744,6 +1744,26 @@ async function hostedGrade(role: GradeRole, prompt: string, jwt: string, timeout
|
|
|
1744
1744
|
return String(data.result || '');
|
|
1745
1745
|
}
|
|
1746
1746
|
|
|
1747
|
+
// Option B: when a cloud grade fails (esp. a timeout), the REAL reason lives
|
|
1748
|
+
// inside the container, not on the wire \u2014 the client only ever sees "timed out".
|
|
1749
|
+
// Pull the org's per-worker claude/cursor sick reasons + log tails from the
|
|
1750
|
+
// hosted /debug endpoint so the diagnostic records WHY the grade actually died
|
|
1751
|
+
// (e.g. a claude 401 inside the worker). Best-effort + tightly timed; null if
|
|
1752
|
+
// the container is itself unreachable.
|
|
1753
|
+
async function fetchContainerSickReason(jwt: string, timeoutMs = 2500): Promise<string | null> {
|
|
1754
|
+
try {
|
|
1755
|
+
const r = await fetch(CONTAINERS_URL + '/debug', {
|
|
1756
|
+
headers: { Authorization: 'Bearer ' + jwt },
|
|
1757
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1758
|
+
});
|
|
1759
|
+
if (!r.ok) return 'debug ' + r.status + ': ' + (await r.text().catch(() => '')).slice(0, 400);
|
|
1760
|
+
const data = await r.json().catch(() => null);
|
|
1761
|
+
return data ? JSON.stringify(data).slice(0, 8000) : null;
|
|
1762
|
+
} catch {
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1747
1767
|
// Cloud transcript capture \u2014 the cloud mirror of the local /api/conversation-sync
|
|
1748
1768
|
// + /api/session-action telemetry. Posts to the API /api/v1/cli/sync-transcripts
|
|
1749
1769
|
// (the org is derived from the login JWT, same one cloud grading uses). Best-effort:
|
|
@@ -2989,35 +3009,38 @@ export async function syncConversationTranscript(
|
|
|
2989
3009
|
const entry = JSON.parse(allLines[i]) as Record<string, unknown>;
|
|
2990
3010
|
const kind = transcriptEntryType(entry);
|
|
2991
3011
|
if (kind !== 'user' && kind !== 'assistant') continue;
|
|
2992
|
-
const text = extractTranscriptEntryText(entry, 8000, sessionId);
|
|
2993
|
-
if (!text) continue;
|
|
2994
|
-
|
|
2995
3012
|
const msgObj = entry.message as Record<string, unknown> | undefined;
|
|
2996
3013
|
const content = (msgObj && typeof msgObj === 'object' ? msgObj.content : undefined) ?? entry.content;
|
|
2997
3014
|
const singleBlock = transcriptContentBlock(entry);
|
|
3015
|
+
// Tool calls FIRST \u2014 a tool-only assistant turn (Edit/Bash/Read, no text) is
|
|
3016
|
+
// most of a coding session and must still be captured; skipping on empty text
|
|
3017
|
+
// dropped both the turn and its tool calls.
|
|
3018
|
+
const blocks = (kind === 'assistant')
|
|
3019
|
+
? (Array.isArray(content) ? content : (singleBlock ? [singleBlock] : []))
|
|
3020
|
+
: [];
|
|
3021
|
+
const toolCalls = blocks
|
|
3022
|
+
.filter((c: unknown) => {
|
|
3023
|
+
const b = c as Record<string, unknown>;
|
|
3024
|
+
return b?.type === 'tool_use' || b?.type === 'tool_call';
|
|
3025
|
+
})
|
|
3026
|
+
.map((c: unknown) => {
|
|
3027
|
+
const b = c as Record<string, unknown>;
|
|
3028
|
+
return {
|
|
3029
|
+
name: b.name,
|
|
3030
|
+
input: JSON.stringify(b.input || b.arguments || {}).slice(0, 500),
|
|
3031
|
+
id: b.id,
|
|
3032
|
+
};
|
|
3033
|
+
});
|
|
3034
|
+
const text = extractTranscriptEntryText(entry, 8000, sessionId);
|
|
3035
|
+
if (!text && toolCalls.length === 0) continue;
|
|
3036
|
+
|
|
2998
3037
|
const msg: Record<string, unknown> = {
|
|
2999
3038
|
message_index: i,
|
|
3000
3039
|
type: kind,
|
|
3001
|
-
content: text,
|
|
3040
|
+
content: text || ('\u21B3 ' + toolCalls.map((t: any) => t.name).join(', ')),
|
|
3002
3041
|
ts: entry.timestamp || null,
|
|
3003
3042
|
};
|
|
3004
3043
|
if (kind === 'assistant') {
|
|
3005
|
-
const blocks = Array.isArray(content)
|
|
3006
|
-
? content
|
|
3007
|
-
: (singleBlock ? [singleBlock] : []);
|
|
3008
|
-
const toolCalls = blocks
|
|
3009
|
-
.filter((c: unknown) => {
|
|
3010
|
-
const b = c as Record<string, unknown>;
|
|
3011
|
-
return b?.type === 'tool_use' || b?.type === 'tool_call';
|
|
3012
|
-
})
|
|
3013
|
-
.map((c: unknown) => {
|
|
3014
|
-
const b = c as Record<string, unknown>;
|
|
3015
|
-
return {
|
|
3016
|
-
name: b.name,
|
|
3017
|
-
input: JSON.stringify(b.input || b.arguments || {}).slice(0, 500),
|
|
3018
|
-
id: b.id,
|
|
3019
|
-
};
|
|
3020
|
-
});
|
|
3021
3044
|
if (toolCalls.length > 0) msg.tool_calls = toolCalls;
|
|
3022
3045
|
// Per-turn usage + model so the session view can show input/output/cache
|
|
3023
3046
|
// tokens and cost line-by-line (not just the session aggregate).
|
|
@@ -3746,35 +3769,99 @@ export function isGraderNotConfigured(errorMessage: string): boolean {
|
|
|
3746
3769
|
return lower.includes('no worker') || lower.includes('not configured') || lower.includes('agent_kind') || lower.includes('no pool');
|
|
3747
3770
|
}
|
|
3748
3771
|
|
|
3749
|
-
|
|
3772
|
+
// Categorize a grader failure into a stable machine category + a human "why"
|
|
3773
|
+
// (with a fix hint). Shared by the terminal message and the diagnostic log so
|
|
3774
|
+
// the two never disagree about the reason.
|
|
3775
|
+
export function classifyGraderError(errorMessage: string): { category: string; why: string } {
|
|
3776
|
+
const e = (errorMessage || '').toLowerCase();
|
|
3777
|
+
const snippet = (errorMessage || '').replace(/\\s+/g, ' ').trim().slice(0, 160);
|
|
3750
3778
|
if (errorMessage === 'SYNKRO_CHANNEL_DOWN') {
|
|
3751
|
-
return
|
|
3779
|
+
return { category: 'channel_down', why: 'grader container not running (run \`synkro status\`)' };
|
|
3752
3780
|
}
|
|
3753
3781
|
if (isGraderNotConfigured(errorMessage)) {
|
|
3754
|
-
|
|
3755
|
-
return hook + ' ' + target + ' \u2192 local grader not configured for ' + agent + '. Add this agent to your synkro.toml file and run \`synkro install\`.';
|
|
3782
|
+
return { category: 'not_configured', why: 'grader not configured for this agent \u2014 add it to synkro.toml and run \`synkro install\`' };
|
|
3756
3783
|
}
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3784
|
+
if (/tim(e|ed)\\s*out|aborted|operation was aborted/.test(e)) {
|
|
3785
|
+
return { category: 'timeout', why: 'grade timed out (45s) \u2014 grader cold/slow, or its Claude token is invalid (re-run \`synkro install\`)' };
|
|
3786
|
+
}
|
|
3787
|
+
if (/\\b401\\b|unauthorized|token expired|invalid token|\\b403\\b|forbidden/.test(e)) {
|
|
3788
|
+
return { category: 'auth', why: 'auth rejected \u2014 session token expired (re-run \`synkro login\`, or \`synkro install\` if the grader Claude token is stale)' };
|
|
3789
|
+
}
|
|
3790
|
+
if (/econnrefused|enotfound|eai_again|fetch failed|socket|econnreset|getaddrinfo|network/.test(e)) {
|
|
3791
|
+
return { category: 'unreachable', why: 'grader unreachable \u2014 network or container down' };
|
|
3792
|
+
}
|
|
3793
|
+
if (/\\b5\\d\\d\\b/.test(e)) {
|
|
3794
|
+
return { category: 'server_error', why: 'grader server error: ' + snippet };
|
|
3795
|
+
}
|
|
3796
|
+
return { category: 'unknown', why: snippet || 'unknown error' };
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
export function graderUnavailableMessage(hook: string, target: string, errorMessage: string, agentKind: AgentKind = 'claude_code'): string {
|
|
3800
|
+
const base = hook + ' ' + target + ' \u2192 ';
|
|
3801
|
+
if (isGraderNotConfigured(errorMessage)) {
|
|
3802
|
+
const agent = agentKind === 'cursor' ? 'Cursor' : 'Claude Code';
|
|
3803
|
+
return base + 'local grader not configured for ' + agent + '. Add this agent to your synkro.toml file and run \`synkro install\`.';
|
|
3804
|
+
}
|
|
3805
|
+
// Always surface WHY (not just "unavailable"), categorized, with a fix hint \u2014
|
|
3806
|
+
// the full raw error + the exact graded prompt are in grader-unavailable.log.
|
|
3807
|
+
const where = process.env.SYNKRO_DEPLOY_LOCATION === 'cloud' ? 'cloud grader' : 'local grader';
|
|
3808
|
+
const { why } = classifyGraderError(errorMessage);
|
|
3809
|
+
return base + where + ' unavailable \u2014 ' + why + '; skipped (full detail: ~/.synkro/grader-unavailable.log)';
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3812
|
+
// Diagnostic record for a skipped grade \u2014 the categorized reason AND the EXACT
|
|
3813
|
+
// prompt that was sent for inference (gradeInput), so you can see WHAT was being
|
|
3814
|
+
// graded when it failed. Routing differs by mode:
|
|
3815
|
+
// \u2022 CLOUD \u2192 ship to Timescale via /api/v1/cli/grader-unavailable (central, so
|
|
3816
|
+
// we can debug customer failures), enriched on timeout/outage with the
|
|
3817
|
+
// container's own claude error (option B). NOT written to the user's machine.
|
|
3818
|
+
// \u2022 LOCAL \u2192 the on-device ~/.synkro/grader-unavailable.log file.
|
|
3819
|
+
// Fire-and-forget in cloud mode: callers don't await \u2014 the pending fetch keeps
|
|
3820
|
+
// the hook process alive until it settles (or the watchdog ends the run).
|
|
3821
|
+
export async function logGraderUnavailable(
|
|
3822
|
+
hook: string, target: string, errorMessage: string,
|
|
3823
|
+
gradeInput?: string, ctx?: { sessionId?: string; repo?: string },
|
|
3824
|
+
): Promise<void> {
|
|
3825
|
+
const { category, why } = classifyGraderError(errorMessage);
|
|
3826
|
+
|
|
3760
3827
|
if (process.env.SYNKRO_DEPLOY_LOCATION === 'cloud') {
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3828
|
+
try {
|
|
3829
|
+
const jwt = loadJwt();
|
|
3830
|
+
if (!jwt) return;
|
|
3831
|
+
// Only chase the container's internal reason for failures where it adds
|
|
3832
|
+
// signal (a timeout/outage hides the real claude error); skip it for fast,
|
|
3833
|
+
// already-explanatory failures like auth.
|
|
3834
|
+
let sickReason: string | null = null;
|
|
3835
|
+
if (category === 'timeout' || category === 'unreachable' || category === 'server_error') {
|
|
3836
|
+
sickReason = await fetchContainerSickReason(jwt);
|
|
3837
|
+
}
|
|
3838
|
+
await fetch(GATEWAY_URL + '/api/v1/cli/grader-unavailable', {
|
|
3839
|
+
method: 'POST',
|
|
3840
|
+
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
|
|
3841
|
+
body: JSON.stringify({
|
|
3842
|
+
hook, target, category, why,
|
|
3843
|
+
error: (errorMessage || '').slice(0, 2000),
|
|
3844
|
+
grade_input: gradeInput ? String(gradeInput).slice(0, 20000) : null,
|
|
3845
|
+
sick_reason: sickReason,
|
|
3846
|
+
session_id: ctx?.sessionId || null,
|
|
3847
|
+
repo: ctx?.repo || null,
|
|
3848
|
+
}),
|
|
3849
|
+
signal: AbortSignal.timeout(3000),
|
|
3850
|
+
}).catch(() => {});
|
|
3851
|
+
} catch {
|
|
3852
|
+
// best-effort \u2014 never let diagnostics cascade into a hook failure
|
|
3764
3853
|
}
|
|
3765
|
-
return
|
|
3854
|
+
return;
|
|
3766
3855
|
}
|
|
3767
|
-
return hook + ' ' + target + ' \u2192 local grader unavailable, skipped';
|
|
3768
|
-
}
|
|
3769
3856
|
|
|
3770
|
-
|
|
3857
|
+
// Local mode: on-device diagnostic log.
|
|
3771
3858
|
try {
|
|
3772
|
-
const entry = {
|
|
3773
|
-
ts: new Date().toISOString(),
|
|
3774
|
-
hook,
|
|
3775
|
-
|
|
3776
|
-
error: errorMessage.slice(0, 500),
|
|
3859
|
+
const entry: Record<string, unknown> = {
|
|
3860
|
+
ts: new Date().toISOString(), mode: 'local',
|
|
3861
|
+
hook, target, category, why,
|
|
3862
|
+
error: (errorMessage || '').slice(0, 1000),
|
|
3777
3863
|
};
|
|
3864
|
+
if (gradeInput) entry.gradeInput = String(gradeInput).slice(0, 20000);
|
|
3778
3865
|
appendFileSync(UNAVAIL_LOG, JSON.stringify(entry) + '\\n', 'utf-8');
|
|
3779
3866
|
} catch {
|
|
3780
3867
|
// best-effort \u2014 never let logging failure cascade into a hook failure
|
|
@@ -4034,7 +4121,7 @@ async function main() {
|
|
|
4034
4121
|
}
|
|
4035
4122
|
} catch (err) {
|
|
4036
4123
|
const errMsg = (err as Error).message || String(err);
|
|
4037
|
-
logGraderUnavailable('editGuard', fileShort, errMsg);
|
|
4124
|
+
logGraderUnavailable('editGuard', fileShort, errMsg, buildCombined(proposed), { sessionId, repo: gitRepo });
|
|
4038
4125
|
outputJson({ systemMessage: tagStr + ' ' + graderUnavailableMessage('editGuard', fileShort, errMsg, graderPool) });
|
|
4039
4126
|
return;
|
|
4040
4127
|
}
|
|
@@ -4108,7 +4195,7 @@ async function main() {
|
|
|
4108
4195
|
gradeResp = await localGrade('edit', graderPrompt, undefined, graderPool);
|
|
4109
4196
|
} catch (err) {
|
|
4110
4197
|
const errMsg = (err as Error).message || String(err);
|
|
4111
|
-
logGraderUnavailable('editGuard', fileShort, errMsg);
|
|
4198
|
+
logGraderUnavailable('editGuard', fileShort, errMsg, graderPrompt, { sessionId, repo: gitRepo });
|
|
4112
4199
|
outputJson({ systemMessage: tagStr + ' ' + graderUnavailableMessage('editGuard', fileShort, errMsg, graderPool) });
|
|
4113
4200
|
return;
|
|
4114
4201
|
}
|
|
@@ -4675,7 +4762,7 @@ async function main() {
|
|
|
4675
4762
|
gradeResponses = [resp1, resp2];
|
|
4676
4763
|
} catch (gradeErr: any) {
|
|
4677
4764
|
const reason = gradeErr?.message || String(gradeErr);
|
|
4678
|
-
logGraderUnavailable('cweGuard', fileShort, reason);
|
|
4765
|
+
logGraderUnavailable('cweGuard', fileShort, reason, buildCwePrompt(cweContent), { sessionId, repo: gitRepo });
|
|
4679
4766
|
outputJson({ systemMessage: cweTag + ' ' + graderUnavailableMessage('cweGuard', fileShort, reason, graderPool) });
|
|
4680
4767
|
return;
|
|
4681
4768
|
}
|
|
@@ -4684,7 +4771,7 @@ async function main() {
|
|
|
4684
4771
|
gradeResponses = [await localGradeCwe(buildCwePrompt(cweContent), graderPool)];
|
|
4685
4772
|
} catch (gradeErr: any) {
|
|
4686
4773
|
const reason = gradeErr?.message || String(gradeErr);
|
|
4687
|
-
logGraderUnavailable('cweGuard', fileShort, reason);
|
|
4774
|
+
logGraderUnavailable('cweGuard', fileShort, reason, buildCwePrompt(cweContent), { sessionId, repo: gitRepo });
|
|
4688
4775
|
outputJson({ systemMessage: cweTag + ' ' + graderUnavailableMessage('cweGuard', fileShort, reason, graderPool) });
|
|
4689
4776
|
return;
|
|
4690
4777
|
}
|
|
@@ -5432,7 +5519,7 @@ async function main() {
|
|
|
5432
5519
|
gradeResp = await localGrade('bash', graderPrompt, undefined, graderPool);
|
|
5433
5520
|
} catch (err) {
|
|
5434
5521
|
const errMsg = (err as Error).message || String(err);
|
|
5435
|
-
logGraderUnavailable('bashGuard', toolInput.command?.slice(0, 200) || '', errMsg);
|
|
5522
|
+
logGraderUnavailable('bashGuard', toolInput.command?.slice(0, 200) || '', errMsg, graderPrompt, { sessionId, repo: gitRepo });
|
|
5436
5523
|
if (scanConcern) {
|
|
5437
5524
|
const ctx = scanBlockContext + ' Synkro flagged this install but the grader is unavailable to check consent — ask the user for explicit consent, then retry.';
|
|
5438
5525
|
outputJson({
|
|
@@ -5650,7 +5737,7 @@ async function main() {
|
|
|
5650
5737
|
gradeResp = await localGrade('bash', graderPrompt, undefined, graderPool);
|
|
5651
5738
|
} catch (err) {
|
|
5652
5739
|
const errMsg = (err as Error).message || String(err);
|
|
5653
|
-
logGraderUnavailable('agentGuard', subagentType || (description || '').slice(0, 100), errMsg);
|
|
5740
|
+
logGraderUnavailable('agentGuard', subagentType || (description || '').slice(0, 100), errMsg, graderPrompt, { sessionId, repo: gitRepo });
|
|
5654
5741
|
outputJson({ systemMessage: tagStr + ' ' + graderUnavailableMessage('agentGuard', subagentType || 'agent', errMsg, graderPool) });
|
|
5655
5742
|
return;
|
|
5656
5743
|
}
|
|
@@ -5741,7 +5828,7 @@ import {
|
|
|
5741
5828
|
loadJwt, ensureFreshJwt, detectRepo, loadConfig, route, tag, localGrade,
|
|
5742
5829
|
parseVerdict, dispatchCapture, appendSessionAction, readSessionLog, compressSessionLog, postWithRetry, readStdin, log,
|
|
5743
5830
|
outputJson, outputEmpty, setupCursorHookSignals, installHookWatchdog, isPlanTool, hookSessionId, GATEWAY_URL,
|
|
5744
|
-
filterRules, graderUnavailableMessage, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
5831
|
+
filterRules, graderUnavailableMessage, logGraderUnavailable, resolveTranscriptPath, isCursorInvokingCcHook,
|
|
5745
5832
|
loadSynkroFile, effectiveGraderPool, synkroFilePresent, noSynkroSkipMessage,
|
|
5746
5833
|
} from './_synkro-common.ts';
|
|
5747
5834
|
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
|
|
@@ -5861,7 +5948,9 @@ async function main() {
|
|
|
5861
5948
|
try {
|
|
5862
5949
|
gradeResp = await localGrade('plan', graderPrompt, undefined, graderPool);
|
|
5863
5950
|
} catch (err) {
|
|
5864
|
-
|
|
5951
|
+
const errMsg = (err as Error).message || String(err);
|
|
5952
|
+
logGraderUnavailable('planReview', 'plan', errMsg, graderPrompt, { sessionId, repo: gitRepo });
|
|
5953
|
+
outputJson({ systemMessage: tagStr + ' ' + graderUnavailableMessage('planReview', 'plan', errMsg, graderPool) });
|
|
5865
5954
|
return;
|
|
5866
5955
|
}
|
|
5867
5956
|
|
|
@@ -6475,7 +6564,7 @@ async function main() {
|
|
|
6475
6564
|
try {
|
|
6476
6565
|
gradeResp = await localGrade('bash', graderPrompt, CURSOR_GRADE_TIMEOUT_MS, graderPool);
|
|
6477
6566
|
} catch (e) {
|
|
6478
|
-
logGraderUnavailable('bashGuard', command.slice(0, 200), (e as Error).message || String(e));
|
|
6567
|
+
logGraderUnavailable('bashGuard', command.slice(0, 200), (e as Error).message || String(e), graderPrompt, { sessionId, repo });
|
|
6479
6568
|
if (scanConcern) {
|
|
6480
6569
|
// Grader unavailable to run the consent check \u2014 fail closed on a
|
|
6481
6570
|
// scanner-flagged install (ask-mode so the user can still consent).
|
|
@@ -10750,7 +10839,7 @@ function writeConfigEnv(opts) {
|
|
|
10750
10839
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
10751
10840
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
10752
10841
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
10753
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
10842
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.83")}`
|
|
10754
10843
|
];
|
|
10755
10844
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
10756
10845
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -14169,11 +14258,17 @@ function readConfigEnv() {
|
|
|
14169
14258
|
return out;
|
|
14170
14259
|
}
|
|
14171
14260
|
function projectsFolder() {
|
|
14172
|
-
const sanitized =
|
|
14261
|
+
const sanitized = process.cwd().replace(/\//g, "-");
|
|
14173
14262
|
const dir = join17(homedir17(), ".claude", "projects", sanitized);
|
|
14174
14263
|
return existsSync17(dir) ? dir : null;
|
|
14175
14264
|
}
|
|
14176
14265
|
function repoName() {
|
|
14266
|
+
try {
|
|
14267
|
+
const url = execSync7("git config --get remote.origin.url", { encoding: "utf-8" }).trim();
|
|
14268
|
+
const m = url.match(/[:/]([^/:]+\/[^/]+?)(?:\.git)?$/);
|
|
14269
|
+
if (m) return m[1];
|
|
14270
|
+
} catch {
|
|
14271
|
+
}
|
|
14177
14272
|
try {
|
|
14178
14273
|
return execSync7("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim().split("/").pop() || "repo";
|
|
14179
14274
|
} catch {
|
|
@@ -14203,11 +14298,13 @@ function parseSession(filePath, sessionId) {
|
|
|
14203
14298
|
if (!kind) continue;
|
|
14204
14299
|
const msgObj = e.message;
|
|
14205
14300
|
const text = extractText(msgObj?.content ?? e.content);
|
|
14206
|
-
|
|
14301
|
+
const blocks = kind === "assistant" && Array.isArray(msgObj?.content) ? msgObj.content : [];
|
|
14302
|
+
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 }));
|
|
14303
|
+
if (!text.trim() && toolCalls.length === 0) continue;
|
|
14207
14304
|
const msg = {
|
|
14208
14305
|
message_index: i,
|
|
14209
14306
|
type: kind,
|
|
14210
|
-
content: text.slice(0, 8e3),
|
|
14307
|
+
content: text.trim() ? text.slice(0, 8e3) : "\u21B3 " + toolCalls.map((t) => t.name).join(", "),
|
|
14211
14308
|
timestamp: e.timestamp || null
|
|
14212
14309
|
};
|
|
14213
14310
|
if (kind === "assistant") {
|
|
@@ -14221,8 +14318,6 @@ function parseSession(filePath, sessionId) {
|
|
|
14221
14318
|
cache_read_input_tokens: Number(u.cache_read_input_tokens) || 0
|
|
14222
14319
|
};
|
|
14223
14320
|
}
|
|
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
14321
|
if (toolCalls.length) {
|
|
14227
14322
|
msg.tool_calls = toolCalls;
|
|
14228
14323
|
for (const t of toolCalls) actions.push({ step: ++step, tool: t.name, summary: "", file: null, outcome: null });
|
|
@@ -14272,25 +14367,29 @@ async function importCommand() {
|
|
|
14272
14367
|
return;
|
|
14273
14368
|
}
|
|
14274
14369
|
const gateway = config.SYNKRO_GATEWAY_URL || "https://api.synkro.sh";
|
|
14275
|
-
for (
|
|
14276
|
-
const batch = sessions.slice(i, i + 20);
|
|
14370
|
+
for (const s of sessions) {
|
|
14277
14371
|
try {
|
|
14278
14372
|
const r = await fetch(`${gateway}/api/v1/cli/sync-transcripts`, {
|
|
14279
14373
|
method: "POST",
|
|
14280
14374
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
14281
|
-
body: JSON.stringify({ repo, sessions:
|
|
14282
|
-
signal: AbortSignal.timeout(
|
|
14375
|
+
body: JSON.stringify({ repo, sessions: [s] }),
|
|
14376
|
+
signal: AbortSignal.timeout(6e4)
|
|
14283
14377
|
});
|
|
14284
|
-
if (r.ok)
|
|
14285
|
-
|
|
14286
|
-
|
|
14287
|
-
|
|
14378
|
+
if (r.ok) {
|
|
14379
|
+
ok++;
|
|
14380
|
+
process.stdout.write(".");
|
|
14381
|
+
} else {
|
|
14382
|
+
fail++;
|
|
14383
|
+
console.warn(`
|
|
14384
|
+
${String(s.cc_session_id).slice(0, 8)} failed: HTTP ${r.status}`);
|
|
14288
14385
|
}
|
|
14289
14386
|
} catch (e) {
|
|
14290
|
-
fail
|
|
14291
|
-
console.warn(`
|
|
14387
|
+
fail++;
|
|
14388
|
+
console.warn(`
|
|
14389
|
+
${String(s.cc_session_id).slice(0, 8)} error: ${e.message}`);
|
|
14292
14390
|
}
|
|
14293
14391
|
}
|
|
14392
|
+
process.stdout.write("\n");
|
|
14294
14393
|
} else {
|
|
14295
14394
|
const port = config.SYNKRO_MCP_PORT || "18931";
|
|
14296
14395
|
for (const s of sessions) {
|
|
@@ -14601,7 +14700,7 @@ var args = process.argv.slice(2);
|
|
|
14601
14700
|
var cmd = args[0] || "";
|
|
14602
14701
|
var subArgs = args.slice(1);
|
|
14603
14702
|
function printVersion() {
|
|
14604
|
-
console.log("1.6.
|
|
14703
|
+
console.log("1.6.83");
|
|
14605
14704
|
}
|
|
14606
14705
|
function printHelp2() {
|
|
14607
14706
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|