@synkro-sh/cli 1.4.60 → 1.4.62
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 +111 -47
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -911,63 +911,93 @@ function decodeJwtExp(jwt: string): number {
|
|
|
911
911
|
}
|
|
912
912
|
|
|
913
913
|
export async function refreshJwt(jwt: string): Promise<string> {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
if (!rt) return jwt;
|
|
914
|
+
const creds = JSON.parse(readFileSync(CREDS_PATH, 'utf-8'));
|
|
915
|
+
const rt = creds.refresh_token;
|
|
916
|
+
if (!rt) return jwt;
|
|
918
917
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
const newRt = data.refresh_token || rt;
|
|
930
|
-
const existing = (() => {
|
|
931
|
-
try { return JSON.parse(readFileSync(CREDS_PATH, 'utf-8')); } catch { return {}; }
|
|
932
|
-
})();
|
|
933
|
-
const updated = { ...existing, access_token: newAt, refresh_token: newRt };
|
|
934
|
-
const tmp = CREDS_PATH + '.synkro.tmp';
|
|
935
|
-
writeFileSync(tmp, JSON.stringify(updated, null, 2));
|
|
936
|
-
renameSync(tmp, CREDS_PATH);
|
|
937
|
-
return newAt;
|
|
938
|
-
} catch {
|
|
918
|
+
const resp = await fetch(GATEWAY_URL + '/api/auth/refresh', {
|
|
919
|
+
method: 'POST',
|
|
920
|
+
headers: { 'Content-Type': 'application/json' },
|
|
921
|
+
body: JSON.stringify({ refresh_token: rt }),
|
|
922
|
+
signal: AbortSignal.timeout(4000),
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
if (!resp.ok) {
|
|
926
|
+
log('refresh failed: HTTP ' + resp.status);
|
|
939
927
|
return jwt;
|
|
940
928
|
}
|
|
929
|
+
|
|
930
|
+
const data = await resp.json() as any;
|
|
931
|
+
const newAt = data.access_token;
|
|
932
|
+
if (!newAt) return jwt;
|
|
933
|
+
|
|
934
|
+
const newRt = data.refresh_token || rt;
|
|
935
|
+
const existing = (() => {
|
|
936
|
+
try { return JSON.parse(readFileSync(CREDS_PATH, 'utf-8')); } catch { return {}; }
|
|
937
|
+
})();
|
|
938
|
+
const updated = { ...existing, access_token: newAt, refresh_token: newRt };
|
|
939
|
+
const tmp = CREDS_PATH + '.synkro.tmp';
|
|
940
|
+
writeFileSync(tmp, JSON.stringify(updated, null, 2));
|
|
941
|
+
renameSync(tmp, CREDS_PATH);
|
|
942
|
+
return newAt;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function jwtIsExpired(jwt: string): boolean {
|
|
946
|
+
const exp = decodeJwtExp(jwt);
|
|
947
|
+
return exp - Math.floor(Date.now() / 1000) < 60;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function lockIsStale(lockfile: string, maxAgeMs = 8000): boolean {
|
|
951
|
+
try {
|
|
952
|
+
const stat = require('node:fs').statSync(lockfile);
|
|
953
|
+
return Date.now() - stat.mtimeMs > maxAgeMs;
|
|
954
|
+
} catch {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
941
957
|
}
|
|
942
958
|
|
|
943
959
|
export async function ensureFreshJwt(jwt: string): Promise<string> {
|
|
944
960
|
if (!jwt) return jwt;
|
|
945
|
-
|
|
946
|
-
const now = Math.floor(Date.now() / 1000);
|
|
947
|
-
if (exp - now >= 60) return jwt;
|
|
961
|
+
if (!jwtIsExpired(jwt)) return jwt;
|
|
948
962
|
|
|
949
963
|
const lockfile = CREDS_PATH + '.lock';
|
|
964
|
+
|
|
965
|
+
// Clean stale lock left by a killed hook
|
|
966
|
+
if (existsSync(lockfile) && lockIsStale(lockfile)) {
|
|
967
|
+
try { unlinkSync(lockfile); } catch {}
|
|
968
|
+
}
|
|
969
|
+
|
|
950
970
|
let fd = -1;
|
|
951
971
|
try {
|
|
952
972
|
fd = openSync(lockfile, FS_CONSTANTS.O_WRONLY | FS_CONSTANTS.O_CREAT | FS_CONSTANTS.O_EXCL, 0o644);
|
|
953
973
|
} catch {
|
|
954
|
-
// Another process
|
|
955
|
-
for (let i = 0; i <
|
|
956
|
-
await new Promise(r => setTimeout(r,
|
|
974
|
+
// Another process is refreshing \u2014 wait for it
|
|
975
|
+
for (let i = 0; i < 8; i++) {
|
|
976
|
+
await new Promise(r => setTimeout(r, 500));
|
|
957
977
|
if (!existsSync(lockfile)) break;
|
|
958
978
|
}
|
|
979
|
+
// Stale lock after full wait \u2014 force-remove
|
|
980
|
+
if (existsSync(lockfile) && lockIsStale(lockfile)) {
|
|
981
|
+
try { unlinkSync(lockfile); } catch {}
|
|
982
|
+
}
|
|
983
|
+
// Re-read \u2014 the winner should have written a fresh JWT
|
|
959
984
|
const fresh = loadJwt();
|
|
960
|
-
|
|
985
|
+
if (fresh && !jwtIsExpired(fresh)) return fresh;
|
|
986
|
+
// Winner's refresh failed \u2014 try to acquire lock ourselves
|
|
987
|
+
try {
|
|
988
|
+
fd = openSync(lockfile, FS_CONSTANTS.O_WRONLY | FS_CONSTANTS.O_CREAT | FS_CONSTANTS.O_EXCL, 0o644);
|
|
989
|
+
} catch {
|
|
990
|
+
return fresh || jwt;
|
|
991
|
+
}
|
|
961
992
|
}
|
|
962
993
|
|
|
963
994
|
try {
|
|
964
|
-
// Re-check \u2014 another hook may have
|
|
995
|
+
// Re-check \u2014 another hook may have written fresh creds while we waited for lock
|
|
965
996
|
const freshJwt = loadJwt();
|
|
966
|
-
if (freshJwt)
|
|
967
|
-
const freshExp = decodeJwtExp(freshJwt);
|
|
968
|
-
if (freshExp - Math.floor(Date.now() / 1000) >= 60) return freshJwt;
|
|
969
|
-
}
|
|
997
|
+
if (freshJwt && !jwtIsExpired(freshJwt)) return freshJwt;
|
|
970
998
|
return await refreshJwt(jwt);
|
|
999
|
+
} catch {
|
|
1000
|
+
return jwt;
|
|
971
1001
|
} finally {
|
|
972
1002
|
try { closeSync(fd); } catch {}
|
|
973
1003
|
try { unlinkSync(lockfile); } catch {}
|
|
@@ -1255,6 +1285,20 @@ export function dispatchCapture(
|
|
|
1255
1285
|
if (repo) body.repo = repo;
|
|
1256
1286
|
if (sessionId) body.session_id = sessionId;
|
|
1257
1287
|
|
|
1288
|
+
// Local telemetry always gets full content \u2014 data never leaves the machine
|
|
1289
|
+
const localBody = { ...body };
|
|
1290
|
+
if (opts) {
|
|
1291
|
+
if (opts.command) localBody.command = opts.command;
|
|
1292
|
+
if (opts.reasoning) localBody.reasoning = opts.reasoning;
|
|
1293
|
+
if (opts.rulesChecked) localBody.rules_checked = opts.rulesChecked;
|
|
1294
|
+
if (opts.violatedRules) localBody.violated_rules = opts.violatedRules;
|
|
1295
|
+
if (opts.recentUserMessages) localBody.recent_user_messages = opts.recentUserMessages;
|
|
1296
|
+
}
|
|
1297
|
+
appendLocalTelemetry(localBody);
|
|
1298
|
+
|
|
1299
|
+
// local_only: no data leaves the machine
|
|
1300
|
+
if (captureDepth === 'local_only') return;
|
|
1301
|
+
|
|
1258
1302
|
if (sendFull && opts) {
|
|
1259
1303
|
body.capture_depth = captureDepth;
|
|
1260
1304
|
if (opts.command) body.command = opts.command;
|
|
@@ -1264,8 +1308,6 @@ export function dispatchCapture(
|
|
|
1264
1308
|
if (opts.recentUserMessages) body.recent_user_messages = opts.recentUserMessages;
|
|
1265
1309
|
}
|
|
1266
1310
|
|
|
1267
|
-
appendLocalTelemetry(body);
|
|
1268
|
-
|
|
1269
1311
|
fetch(GATEWAY_URL + '/api/v1/hook/capture', {
|
|
1270
1312
|
method: 'POST',
|
|
1271
1313
|
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
|
|
@@ -1848,6 +1890,25 @@ async function main() {
|
|
|
1848
1890
|
const proposed = reconstructContent(toolName, toolInput, filePath, cwd);
|
|
1849
1891
|
if (!proposed) { outputEmpty(); return; }
|
|
1850
1892
|
|
|
1893
|
+
// Change-anchored window: for Edit/MultiEdit send context around the diff,
|
|
1894
|
+
// for Write send first 4000 chars (new files have patterns at the top).
|
|
1895
|
+
let cweContent: string;
|
|
1896
|
+
if (toolName === 'Edit' || toolName === 'MultiEdit') {
|
|
1897
|
+
const newStr = toolName === 'Edit'
|
|
1898
|
+
? (toolInput.new_string || '')
|
|
1899
|
+
: (Array.isArray(toolInput.edits) ? toolInput.edits.map((e: any) => e?.new_string || '').join('\\n') : '');
|
|
1900
|
+
const changeIdx = proposed.indexOf(newStr);
|
|
1901
|
+
if (changeIdx >= 0 && proposed.length > 6000) {
|
|
1902
|
+
const start = Math.max(0, changeIdx - 2000);
|
|
1903
|
+
const end = Math.min(proposed.length, changeIdx + newStr.length + 2000);
|
|
1904
|
+
cweContent = proposed.slice(start, end);
|
|
1905
|
+
} else {
|
|
1906
|
+
cweContent = proposed.slice(0, 6000);
|
|
1907
|
+
}
|
|
1908
|
+
} else {
|
|
1909
|
+
cweContent = proposed.slice(0, 4000);
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1851
1912
|
const config = await loadConfig(jwt);
|
|
1852
1913
|
const rt = await cweRoute(config);
|
|
1853
1914
|
|
|
@@ -1885,7 +1946,7 @@ async function main() {
|
|
|
1885
1946
|
const graderPrompt = [
|
|
1886
1947
|
'File: ' + filePath,
|
|
1887
1948
|
'Content:',
|
|
1888
|
-
|
|
1949
|
+
cweContent,
|
|
1889
1950
|
'',
|
|
1890
1951
|
'CWE rules to check against:',
|
|
1891
1952
|
JSON.stringify(cweRules),
|
|
@@ -2590,7 +2651,7 @@ main();
|
|
|
2590
2651
|
`;
|
|
2591
2652
|
BASH_FOLLOWUP_TS = `#!/usr/bin/env bun
|
|
2592
2653
|
import {
|
|
2593
|
-
loadJwt, readStdin, hashCommand, consentGrant, consentHasActive, consentConsume,
|
|
2654
|
+
loadJwt, loadConfig, readStdin, hashCommand, consentGrant, consentHasActive, consentConsume,
|
|
2594
2655
|
outputEmpty, appendLocalTelemetry, GATEWAY_URL,
|
|
2595
2656
|
} from './_synkro-common.ts';
|
|
2596
2657
|
|
|
@@ -2634,12 +2695,15 @@ async function main() {
|
|
|
2634
2695
|
|
|
2635
2696
|
appendLocalTelemetry(body);
|
|
2636
2697
|
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2698
|
+
const config = await loadConfig(jwt);
|
|
2699
|
+
if (config.captureDepth !== 'local_only') {
|
|
2700
|
+
fetch(GATEWAY_URL + '/api/v1/hook/capture', {
|
|
2701
|
+
method: 'POST',
|
|
2702
|
+
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
|
|
2703
|
+
body: JSON.stringify(body),
|
|
2704
|
+
signal: AbortSignal.timeout(3000),
|
|
2705
|
+
}).catch(() => {});
|
|
2706
|
+
}
|
|
2643
2707
|
|
|
2644
2708
|
outputEmpty();
|
|
2645
2709
|
} catch {
|
|
@@ -5137,7 +5201,7 @@ function writeConfigEnv(opts) {
|
|
|
5137
5201
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
5138
5202
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
5139
5203
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
5140
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.4.
|
|
5204
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.4.62")}`
|
|
5141
5205
|
];
|
|
5142
5206
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
5143
5207
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|