@synkro-sh/cli 1.6.96 → 1.6.97
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 +53 -9
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -1750,7 +1750,7 @@ async function cloudGrade(surface: string, prompt: string, jwt: string, timeoutM
|
|
|
1750
1750
|
// to the org's dispatcher /submit), but to the hosted ingress with the gateway
|
|
1751
1751
|
// JWT (the Worker derives the org from the token and routes to that org's
|
|
1752
1752
|
// container). Any non-2xx throws so the caller's catch fails open.
|
|
1753
|
-
const CONTAINERS_URL = process.env.SYNKRO_CONTAINERS_URL || 'https://
|
|
1753
|
+
const CONTAINERS_URL = process.env.SYNKRO_CONTAINERS_URL || 'https://api.synkro.sh';
|
|
1754
1754
|
// Cloud grades cross the network and can hit a cold/queued worker, so they get a
|
|
1755
1755
|
// longer leash than on-device channel grades. The hook chain is sized around it:
|
|
1756
1756
|
// grade abort (45s) < watchdog (48s) < CC hook budget (50s) \u2014 the grade aborts
|
|
@@ -1832,6 +1832,39 @@ export async function cloudTaskDrift(envelope: Record<string, unknown>, jwt: str
|
|
|
1832
1832
|
}
|
|
1833
1833
|
}
|
|
1834
1834
|
|
|
1835
|
+
// Cloud task COMPLETION sweep \u2014 the end-of-turn FINAL CHECK (fired from the Stop hook).
|
|
1836
|
+
// Hits the org container's 'task-complete' surface: verify EVERY requirement against the
|
|
1837
|
+
// actual written code (file snapshots + transcript), mark met/open, and retire if all
|
|
1838
|
+
// met. Returns the result systemMessage ("complete \u2713 N/N retired" or "N/M \u2014 missing: \u2026").
|
|
1839
|
+
// payload.stop_hook_active MUST be falsey or handleTaskComplete bails (stop-loop guard).
|
|
1840
|
+
// Fail-silent: any hiccup returns '' so a flaky sweep never blocks the agent stopping.
|
|
1841
|
+
export async function cloudTaskComplete(envelope: Record<string, unknown>, jwt: string, timeoutMs = CLOUD_GRADE_TIMEOUT_MS): Promise<string> {
|
|
1842
|
+
const sid = typeof envelope.sessionId === 'string' ? envelope.sessionId : undefined;
|
|
1843
|
+
const repo = typeof envelope.cwd === 'string' ? envelope.cwd : undefined;
|
|
1844
|
+
try {
|
|
1845
|
+
const body = JSON.stringify({ role: 'task-complete', payload: scrubSecrets(JSON.stringify(envelope)), content: '' });
|
|
1846
|
+
const resp = await fetch(CONTAINERS_URL + '/grade/submit', {
|
|
1847
|
+
method: 'POST',
|
|
1848
|
+
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + jwt },
|
|
1849
|
+
body,
|
|
1850
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1851
|
+
});
|
|
1852
|
+
if (!resp.ok) {
|
|
1853
|
+
void logGraderUnavailable('taskComplete', 'task-complete', 'http ' + resp.status, undefined, { sessionId: sid, repo }).catch(() => {});
|
|
1854
|
+
return '';
|
|
1855
|
+
}
|
|
1856
|
+
const j = await resp.json() as { systemMessage?: unknown };
|
|
1857
|
+
return typeof j?.systemMessage === 'string' ? j.systemMessage : '';
|
|
1858
|
+
} catch (e) {
|
|
1859
|
+
const err = e as { name?: string; message?: string };
|
|
1860
|
+
const reason = err?.name === 'TimeoutError'
|
|
1861
|
+
? 'task-complete timed out after ' + timeoutMs + 'ms'
|
|
1862
|
+
: (err?.message || 'task-complete failed');
|
|
1863
|
+
void logGraderUnavailable('taskComplete', 'task-complete', reason, undefined, { sessionId: sid, repo }).catch(() => {});
|
|
1864
|
+
return '';
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1835
1868
|
// Option B: when a cloud grade fails (esp. a timeout), the REAL reason lives
|
|
1836
1869
|
// inside the container, not on the wire \u2014 the client only ever sees "timed out".
|
|
1837
1870
|
// Pull the org's per-worker claude/cursor sick reasons + log tails from the
|
|
@@ -6533,6 +6566,7 @@ import {
|
|
|
6533
6566
|
loadJwt, detectRepo, loadConfig, tag, readStdin, aggregateUsage, cleanupSessionLog,
|
|
6534
6567
|
outputJson, outputEmpty, shipCloud, setupCursorHookSignals, hookSessionId, GATEWAY_URL,
|
|
6535
6568
|
resolveTranscriptPath, emitUsageTick, cursorModelFromPayload, log, synkroFilePresent,
|
|
6569
|
+
cloudTaskComplete,
|
|
6536
6570
|
} from './_synkro-common.ts';
|
|
6537
6571
|
|
|
6538
6572
|
async function main() {
|
|
@@ -6584,10 +6618,20 @@ async function main() {
|
|
|
6584
6618
|
|
|
6585
6619
|
cleanupSessionLog(sessionId);
|
|
6586
6620
|
|
|
6621
|
+
// Cloud FINAL CHECK: the agent stopped after editing \u2014 sweep the active task,
|
|
6622
|
+
// verify EVERY requirement against the actual written code, and retire if all met.
|
|
6623
|
+
// Returns '' when there's no active task (cheap getActiveTask short-circuit) or in
|
|
6624
|
+
// local mode. stop_hook_active passes through so handleTaskComplete's stop-loop guard
|
|
6625
|
+
// works (it bails when already inside a stop loop).
|
|
6626
|
+
const taskMsg = process.env.SYNKRO_DEPLOY_LOCATION === 'cloud'
|
|
6627
|
+
? await cloudTaskComplete({ payload: { stop_hook_active: payload.stop_hook_active === true }, harness: 'claude_code', cwd: gitRepo, sessionId }, jwt)
|
|
6628
|
+
: '';
|
|
6629
|
+
const taskSuffix = taskMsg ? '\\n' + taskMsg : '';
|
|
6630
|
+
|
|
6587
6631
|
if (!findings) {
|
|
6588
|
-
outputJson({ systemMessage: tagStr + ' stop \u2192 0 issues across ' + edits + ' edit(s), session complete' });
|
|
6632
|
+
outputJson({ systemMessage: tagStr + ' stop \u2192 0 issues across ' + edits + ' edit(s), session complete' + taskSuffix });
|
|
6589
6633
|
} else {
|
|
6590
|
-
outputJson({ systemMessage: tagStr + ' stop \u2192 ' + findings + ' finding(s): ' + autoFixed + ' auto-fixed, ' + open + ' open' });
|
|
6634
|
+
outputJson({ systemMessage: tagStr + ' stop \u2192 ' + findings + ' finding(s): ' + autoFixed + ' auto-fixed, ' + open + ' open' + taskSuffix });
|
|
6591
6635
|
}
|
|
6592
6636
|
} catch (err) {
|
|
6593
6637
|
log('stopSummary error: ' + String(err));
|
|
@@ -6659,7 +6703,7 @@ async function main() {
|
|
|
6659
6703
|
|
|
6660
6704
|
const fakeConfig: HookConfig = { captureDepth: 'local_only', tier: 'standard', silent, policyName, rules: [], scanExemptions: [], gradingMode, storageMode: process.env.SYNKRO_STORAGE_MODE || 'local' };
|
|
6661
6705
|
const tagStr = tag(rt, fakeConfig);
|
|
6662
|
-
const routeLine = tagStr + ' inference: ' + (gradingMode === 'byok' ? 'cloud (BYOK)' : deployCloud ? 'cloud container
|
|
6706
|
+
const routeLine = tagStr + ' inference: ' + (gradingMode === 'byok' ? 'cloud (BYOK)' : deployCloud ? 'cloud container' : isChannelUp ? 'local-cc (channel reachable on 127.0.0.1:18929)' : 'cloud (local-cc channel not reachable)');
|
|
6663
6707
|
|
|
6664
6708
|
if (!jwt) {
|
|
6665
6709
|
outputJson({ systemMessage: routeLine });
|
|
@@ -11366,7 +11410,7 @@ function writeConfigEnv(opts) {
|
|
|
11366
11410
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
11367
11411
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
11368
11412
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
11369
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.6.
|
|
11413
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.6.97")}`
|
|
11370
11414
|
];
|
|
11371
11415
|
if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
|
|
11372
11416
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
@@ -11539,7 +11583,7 @@ async function printWorkerDebug(base, jwt2) {
|
|
|
11539
11583
|
}
|
|
11540
11584
|
}
|
|
11541
11585
|
async function verifyCloudGrader(jwt2) {
|
|
11542
|
-
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://
|
|
11586
|
+
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
11543
11587
|
console.log(" Warming the cloud grader (booting container + checking workers)...");
|
|
11544
11588
|
const deadline = Date.now() + 18e4;
|
|
11545
11589
|
let healthy = 0;
|
|
@@ -11743,7 +11787,7 @@ async function recycleCloudContainer() {
|
|
|
11743
11787
|
console.error("No access token \u2014 run `synkro login`.");
|
|
11744
11788
|
return;
|
|
11745
11789
|
}
|
|
11746
|
-
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://
|
|
11790
|
+
const base = (process.env.SYNKRO_CONTAINERS_URL || "https://api.synkro.sh").replace(/\/$/, "");
|
|
11747
11791
|
console.log("Synkro: recycling the cloud grader container (destroy \u2192 fresh boot)\u2026");
|
|
11748
11792
|
try {
|
|
11749
11793
|
const r = await fetch(`${base}/recycle`, { method: "POST", headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(3e4) });
|
|
@@ -15394,7 +15438,7 @@ To change:`);
|
|
|
15394
15438
|
console.log(" BYOK grading uses your own provider key \u2014 register one in the");
|
|
15395
15439
|
console.log(" dashboard under Settings \u2192 Provider Keys if you have not already.");
|
|
15396
15440
|
} else if (inferenceValue === "cloud") {
|
|
15397
|
-
console.log(" Grading runs on the Synkro cloud worker pool
|
|
15441
|
+
console.log(" Grading runs on the Synkro cloud worker pool.");
|
|
15398
15442
|
}
|
|
15399
15443
|
if (inferenceValue !== "cloud") await reconcileContainer();
|
|
15400
15444
|
}
|
|
@@ -15431,7 +15475,7 @@ var args = process.argv.slice(2);
|
|
|
15431
15475
|
var cmd = args[0] || "";
|
|
15432
15476
|
var subArgs = args.slice(1);
|
|
15433
15477
|
function printVersion() {
|
|
15434
|
-
console.log("1.6.
|
|
15478
|
+
console.log("1.6.97");
|
|
15435
15479
|
}
|
|
15436
15480
|
function printHelp2() {
|
|
15437
15481
|
console.log(`Synkro CLI \u2014 runtime safety for AI coding agents
|