@evident-ai/runner-synchroniser 3.5.2-dev.06654ec → 3.5.2-dev.31c0c2e
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/README.md +41 -7
- package/dist/cli.js +119 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -278,8 +278,13 @@ nothing had ever run a real `PRAGMA integrity_check`).
|
|
|
278
278
|
then the prefix is re-listed as empty before the corrupt local DB and sidecars are
|
|
279
279
|
discarded. Only both proofs permit a fresh boot. Otherwise exit `34` stops the boot before
|
|
280
280
|
OpenCode or Litestream starts; the backup history remains readable at its quarantine
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
destination or original key, and surviving local files remain in place without a process
|
|
282
|
+
opening or writing them.
|
|
283
|
+
|
|
284
|
+
Each restore-point listing and each individual candidate restore has its own hard timeout.
|
|
285
|
+
Replica separation also has a wall-clock budget. When that budget expires, the synchroniser
|
|
286
|
+
reports the number moved and remaining, leaves the active prefix unverified, and exits `34`;
|
|
287
|
+
the next refused start can continue moving the remaining prefix.
|
|
283
288
|
|
|
284
289
|
A walked-back boot keeps replicating into the **same** replica — no S3 mutation, no new
|
|
285
290
|
prefix. litestream re-bases to the replica's high-water mark and continues the txid
|
|
@@ -311,6 +316,7 @@ before looking at everything" would report the wrong conclusion to the operator.
|
|
|
311
316
|
| `WARNING: SESSION-DB-INTEGRITY-FAILED` | `verifySessionDb` (`session-db-verify.ts`) | The local DB fails its check, before the walkback starts | — (prose carries the PRAGMA's own detail) |
|
|
312
317
|
| `WARNING: SESSION-DB-WALKBACK-ENUMERATION-FAILED` | the walkback loop (`session-db-verify.ts`) | `litestream ltx` exited non-zero (prose carries its stderr — missing binary, bad `-config`, S3 error) or answered with a listing that could not be parsed | — (prose carries litestream's own stderr) |
|
|
313
318
|
| `WARNING: SESSION-DB-WALKBACK-CANDIDATE-FAILED` | the walkback loop (`session-db-verify.ts`) | A candidate restore point fails to restore, or restores but also fails its own check | `txid` |
|
|
319
|
+
| `WARNING: SESSION-DB-REPLICA-QUARANTINED` | `quarantineReplica` (`replica-recovery.ts`) | A corrupt replica is moved aside, whether the move finishes or its disposal budget stops it early | `moved`, `remaining`, quarantine destination |
|
|
314
320
|
| `INFO: SESSION-DB-WALKBACK-ADOPTED` | `adopt` (`session-db-verify.ts`) | A candidate passes and is adopted over `opencode.db` | `txid`, `candidates`, `bytes` |
|
|
315
321
|
| `SESSION-DB-INTEGRITY` | `describeSessionDbVerification` (`diagnostics.ts`), logged once per invocation as the final outcome | The local DB passed its check as-is | `bytes` |
|
|
316
322
|
| `SESSION-DB-INTEGRITY-WALKBACK` | `describeSessionDbVerification` (`diagnostics.ts`), logged once per invocation as the final outcome | Same event as `SESSION-DB-WALKBACK-ADOPTED` above, restated as the command's outcome | `txid`, `candidates`, `bytes` |
|
|
@@ -318,11 +324,36 @@ before looking at everything" would report the wrong conclusion to the operator.
|
|
|
318
324
|
| `SESSION-DB-REPLICA-SEPARATED` | `separateCorruptReplica` (`replica-recovery.ts`) | The active prefix was re-listed empty after separation | replica root and quarantine destination |
|
|
319
325
|
| `SESSION-DB-REPLICA-SEPARATION-UNVERIFIED` | `separateCorruptReplica` (`replica-recovery.ts`) | A separation could not be proven | remaining objects or list error |
|
|
320
326
|
| `SESSION-DB-LOCAL-DISCARD-FAILED` | `verifySessionDb` (`session-db-verify.ts`) | The local DB or a sidecar could not be proven gone | removed and surviving paths |
|
|
321
|
-
|
|
322
|
-
The
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
327
|
+
| `SESSION-DB-VERIFY-TIMEOUT` | the boot wrapper around `session-db-verify` | Verification did not finish before its outer deadline; the message says whether corruption was already proven and whether separation had started | timeout and refusal reason |
|
|
328
|
+
| `SESSION-DB-VERIFY-SKIPPED` | `evident run`'s session-DB step | The start serves a session database without verifying it: the restore flag was absent, OpenCode was already serving it, or the health check returned an authentication challenge. `flag-absent` is expected on a `/resume` start by design. | `reason` (`flag-absent`\|`opencode-serving`\|`opencode-auth-challenge`) |
|
|
329
|
+
|
|
330
|
+
The walkback markers describe each attempt and its progress. The final outcome markers
|
|
331
|
+
describe whether the active prefix and local files were proven safe; `entrypoint.sh` reuses
|
|
332
|
+
`SESSION-DB-INTEGRITY-EXHAUSTED` verbatim when it logs its own `33` handling, the same way it
|
|
333
|
+
already reuses `SESSION-DB-REPLICA-UNUSABLE` for `session-db-classify`'s `31`. The CLI wrapper
|
|
334
|
+
emits `SESSION-DB-VERIFY-TIMEOUT` when the outer verification deadline is reached.
|
|
335
|
+
|
|
336
|
+
#### Refused verification outcomes
|
|
337
|
+
|
|
338
|
+
The recovery report records `replica_separation_unproven` when separation did not prove the
|
|
339
|
+
active prefix empty, `local_discard_failed` when local session-database files could not be
|
|
340
|
+
proven removed, `verification_timeout_before_separation` when verification timed out after
|
|
341
|
+
integrity failure but before replica separation began, and
|
|
342
|
+
`verification_timeout_during_separation` when the timeout occurred after separation had
|
|
343
|
+
started. The timeout reasons point to `SESSION-DB-VERIFY-TIMEOUT` and do not claim that
|
|
344
|
+
objects were removed.
|
|
345
|
+
|
|
346
|
+
On a refused boot, wait while the reported `remaining` count falls: each start moves another
|
|
347
|
+
bounded slice and the runner comes online after the active prefix drains. If it stops falling,
|
|
348
|
+
move or delete the objects under `<prefix>/opencode.db/` manually; the next start then uses a
|
|
349
|
+
fresh session database. Set-aside history remains at
|
|
350
|
+
`<prefix>/quarantine/opencode.db/<stamp>/`, with one stamp for each start that moved anything.
|
|
351
|
+
Do not use `EVIDENT_ON_UNUSABLE_REPLICA` for this case; that variable controls the earlier
|
|
352
|
+
restore-failure classification path, not post-restore verification.
|
|
353
|
+
|
|
354
|
+
A store too large for one candidate restore to finish inside
|
|
355
|
+
`EVIDENT_SESSION_DB_RESTORE_CANDIDATE_TIMEOUT_SECONDS` is refused and drained rather than
|
|
356
|
+
adopting an older restore point.
|
|
326
357
|
|
|
327
358
|
### Why the contract is asymmetric
|
|
328
359
|
|
|
@@ -386,6 +417,9 @@ key, so they never appear in `env`'s output or `litestream.yml`.
|
|
|
386
417
|
| `CREDS_SYNC_INTERVAL` | Seconds between sync ticks, reported by `env` for the caller's loop. | Falls back to `60`; also on unparseable or non-positive values. |
|
|
387
418
|
| `EVIDENT_SESSION_DB_WALKBACK_MAX_POINTS` | Distinct restore points `session-db-verify`'s walkback will try before giving up. | Falls back to `10`; also on unparseable or non-positive values. |
|
|
388
419
|
| `EVIDENT_SESSION_DB_WALKBACK_BUDGET_SECONDS` | Wall-clock budget, in seconds, for the whole walkback loop. | Falls back to `180`; also on unparseable or non-positive values. |
|
|
420
|
+
| `EVIDENT_SESSION_DB_RESTORE_POINTS_TIMEOUT_SECONDS` | Hard timeout, in seconds, for one `litestream ltx` restore-point listing. | Falls back to `120`; also on unparseable or non-positive values. |
|
|
421
|
+
| `EVIDENT_SESSION_DB_RESTORE_CANDIDATE_TIMEOUT_SECONDS` | Hard timeout, in seconds, for each individual `litestream restore` walkback candidate. | Falls back to `300`; also on unparseable or non-positive values. |
|
|
422
|
+
| `EVIDENT_SESSION_DB_DISPOSAL_BUDGET_SECONDS` | Wall-clock budget, in seconds, for moving a corrupt replica aside. | Falls back to `900`; also on unparseable or non-positive values. |
|
|
389
423
|
| `EVIDENT_SESSION_DB_RECOVERY_REPORT` | JSONL report drained once into runner activity after authentication. | `$HOME/.local/state/evident/session-db-recovery.jsonl`; blank values use the default. |
|
|
390
424
|
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` | Presence alone counts as configured model auth (see `model-auth-ready`). | No API-key fallback; `model-auth-ready` then depends solely on the credential files. |
|
|
391
425
|
| `CLUSTER` † / `SERVICE` † | ECS cluster/service `self-stop` scales to `desiredCount=0`. | Warns "cannot self-stop" and exits `20` (keep the task). Ignored by every other command. |
|
package/dist/cli.js
CHANGED
|
@@ -50887,6 +50887,9 @@ init_esm_shims();
|
|
|
50887
50887
|
var DEFAULT_SYNC_INTERVAL_SECONDS = 60;
|
|
50888
50888
|
var DEFAULT_WALKBACK_MAX_POINTS = 10;
|
|
50889
50889
|
var DEFAULT_WALKBACK_BUDGET_SECONDS = 180;
|
|
50890
|
+
var DEFAULT_RESTORE_POINTS_TIMEOUT_SECONDS = 120;
|
|
50891
|
+
var DEFAULT_RESTORE_CANDIDATE_TIMEOUT_SECONDS = 300;
|
|
50892
|
+
var DEFAULT_DISPOSAL_BUDGET_SECONDS = 900;
|
|
50890
50893
|
function positiveIntOr(value, defaultValue) {
|
|
50891
50894
|
const parsed = Number.parseInt(value ?? "", 10);
|
|
50892
50895
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultValue;
|
|
@@ -50931,6 +50934,18 @@ function resolveConfig(env4) {
|
|
|
50931
50934
|
env4.EVIDENT_SESSION_DB_WALKBACK_BUDGET_SECONDS,
|
|
50932
50935
|
DEFAULT_WALKBACK_BUDGET_SECONDS
|
|
50933
50936
|
),
|
|
50937
|
+
restorePointsTimeoutSeconds: positiveIntOr(
|
|
50938
|
+
env4.EVIDENT_SESSION_DB_RESTORE_POINTS_TIMEOUT_SECONDS,
|
|
50939
|
+
DEFAULT_RESTORE_POINTS_TIMEOUT_SECONDS
|
|
50940
|
+
),
|
|
50941
|
+
restoreCandidateTimeoutSeconds: positiveIntOr(
|
|
50942
|
+
env4.EVIDENT_SESSION_DB_RESTORE_CANDIDATE_TIMEOUT_SECONDS,
|
|
50943
|
+
DEFAULT_RESTORE_CANDIDATE_TIMEOUT_SECONDS
|
|
50944
|
+
),
|
|
50945
|
+
disposalBudgetSeconds: positiveIntOr(
|
|
50946
|
+
env4.EVIDENT_SESSION_DB_DISPOSAL_BUDGET_SECONDS,
|
|
50947
|
+
DEFAULT_DISPOSAL_BUDGET_SECONDS
|
|
50948
|
+
),
|
|
50934
50949
|
hasModelApiKey: nonEmpty(env4.ANTHROPIC_API_KEY) !== null || nonEmpty(env4.OPENAI_API_KEY) !== null,
|
|
50935
50950
|
runnerKey: nonEmpty(env4.EVIDENT_RUNNER_KEY) ?? nonEmpty(env4.EVIDENT_AGENT_KEY),
|
|
50936
50951
|
apiUrl: nonEmpty(env4.EVIDENT_API_URL)
|
|
@@ -51241,49 +51256,88 @@ function stderrOf(error) {
|
|
|
51241
51256
|
}
|
|
51242
51257
|
return describeError(error);
|
|
51243
51258
|
}
|
|
51259
|
+
function commandDetail(error, operation4, timeoutSeconds) {
|
|
51260
|
+
if (typeof error === "object" && error !== null) {
|
|
51261
|
+
const processError = error;
|
|
51262
|
+
const signal = typeof processError.signal === "string" ? processError.signal : null;
|
|
51263
|
+
if (processError.killed === true || signal === "SIGKILL") {
|
|
51264
|
+
return `${operation4} timed out after ${timeoutSeconds}s (process killed with ${signal ?? "SIGKILL"})`;
|
|
51265
|
+
}
|
|
51266
|
+
}
|
|
51267
|
+
return stderrOf(error);
|
|
51268
|
+
}
|
|
51244
51269
|
var LitestreamCli = class {
|
|
51245
|
-
constructor(configPath, dbPath) {
|
|
51270
|
+
constructor(configPath, dbPath, timeouts) {
|
|
51246
51271
|
this.configPath = configPath;
|
|
51247
51272
|
this.dbPath = dbPath;
|
|
51273
|
+
this.timeouts = timeouts;
|
|
51248
51274
|
}
|
|
51249
51275
|
async listRestorePoints() {
|
|
51250
51276
|
try {
|
|
51251
|
-
const { stdout, stderr } = await execFileAsync(
|
|
51252
|
-
"
|
|
51253
|
-
|
|
51254
|
-
|
|
51255
|
-
|
|
51256
|
-
|
|
51257
|
-
|
|
51258
|
-
|
|
51259
|
-
|
|
51260
|
-
|
|
51261
|
-
|
|
51277
|
+
const { stdout, stderr } = await execFileAsync(
|
|
51278
|
+
"litestream",
|
|
51279
|
+
[
|
|
51280
|
+
"ltx",
|
|
51281
|
+
"-config",
|
|
51282
|
+
this.configPath,
|
|
51283
|
+
// MEASURED: `-level` defaults to 0, which only shows L0 — `all` is
|
|
51284
|
+
// required to see the L1/L2/L9 compaction levels too (plan §2.3).
|
|
51285
|
+
"-level",
|
|
51286
|
+
"all",
|
|
51287
|
+
"-json",
|
|
51288
|
+
this.dbPath
|
|
51289
|
+
],
|
|
51290
|
+
{
|
|
51291
|
+
timeout: this.timeouts.restorePointsTimeoutSeconds * 1e3,
|
|
51292
|
+
killSignal: "SIGKILL"
|
|
51293
|
+
}
|
|
51294
|
+
);
|
|
51262
51295
|
return { stdout, detail: stderr };
|
|
51263
51296
|
} catch (error) {
|
|
51264
|
-
return {
|
|
51297
|
+
return {
|
|
51298
|
+
stdout: null,
|
|
51299
|
+
detail: commandDetail(
|
|
51300
|
+
error,
|
|
51301
|
+
"litestream restore-point enumeration",
|
|
51302
|
+
this.timeouts.restorePointsTimeoutSeconds
|
|
51303
|
+
)
|
|
51304
|
+
};
|
|
51265
51305
|
}
|
|
51266
51306
|
}
|
|
51267
51307
|
async restoreAt(txid, outputPath) {
|
|
51268
51308
|
try {
|
|
51269
|
-
const { stderr } = await execFileAsync(
|
|
51270
|
-
"
|
|
51271
|
-
|
|
51272
|
-
|
|
51273
|
-
|
|
51274
|
-
|
|
51275
|
-
|
|
51276
|
-
|
|
51277
|
-
|
|
51278
|
-
|
|
51279
|
-
|
|
51280
|
-
|
|
51281
|
-
|
|
51282
|
-
|
|
51283
|
-
|
|
51309
|
+
const { stderr } = await execFileAsync(
|
|
51310
|
+
"litestream",
|
|
51311
|
+
[
|
|
51312
|
+
"restore",
|
|
51313
|
+
"-config",
|
|
51314
|
+
this.configPath,
|
|
51315
|
+
"-txid",
|
|
51316
|
+
txid,
|
|
51317
|
+
// MEASURED: without `-force`, restoring into a path that already
|
|
51318
|
+
// exists exits 1 with "output path already exists and is not
|
|
51319
|
+
// empty" (plan §2.4) — the caller always restores into a scratch
|
|
51320
|
+
// path it just cleared, so overwriting it is always intended.
|
|
51321
|
+
"-force",
|
|
51322
|
+
"-o",
|
|
51323
|
+
outputPath,
|
|
51324
|
+
this.dbPath
|
|
51325
|
+
],
|
|
51326
|
+
{
|
|
51327
|
+
timeout: this.timeouts.restoreCandidateTimeoutSeconds * 1e3,
|
|
51328
|
+
killSignal: "SIGKILL"
|
|
51329
|
+
}
|
|
51330
|
+
);
|
|
51284
51331
|
return { ok: true, detail: stderr };
|
|
51285
51332
|
} catch (error) {
|
|
51286
|
-
return {
|
|
51333
|
+
return {
|
|
51334
|
+
ok: false,
|
|
51335
|
+
detail: commandDetail(
|
|
51336
|
+
error,
|
|
51337
|
+
`litestream restore of txid=${txid}`,
|
|
51338
|
+
this.timeouts.restoreCandidateTimeoutSeconds
|
|
51339
|
+
)
|
|
51340
|
+
};
|
|
51287
51341
|
}
|
|
51288
51342
|
}
|
|
51289
51343
|
};
|
|
@@ -61444,7 +61498,9 @@ async function probeReplica(store, prefix, log) {
|
|
|
61444
61498
|
return { ok: true, objects };
|
|
61445
61499
|
} catch (error) {
|
|
61446
61500
|
const detail = describeError(error);
|
|
61447
|
-
log(
|
|
61501
|
+
log(
|
|
61502
|
+
`WARNING: SESSION-DB-REPLICA-SEPARATION-UNVERIFIED: could not list the replica prefix ${root12}: ${detail}`
|
|
61503
|
+
);
|
|
61448
61504
|
return { ok: false, detail };
|
|
61449
61505
|
}
|
|
61450
61506
|
}
|
|
@@ -61499,13 +61555,15 @@ async function pruneNewestL0(store, prefix, keys, log) {
|
|
|
61499
61555
|
function quarantineRoot(prefix, stamp) {
|
|
61500
61556
|
return `${prefix}/quarantine/opencode.db/${stamp}/`;
|
|
61501
61557
|
}
|
|
61502
|
-
async function quarantineReplica(store, prefix, objects, log, stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")) {
|
|
61558
|
+
async function quarantineReplica(store, prefix, objects, log, stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"), budgetSeconds = DEFAULT_DISPOSAL_BUDGET_SECONDS, now = Date.now) {
|
|
61503
61559
|
const destination = quarantineRoot(prefix, stamp);
|
|
61504
61560
|
const root12 = replicaDbPrefix(prefix);
|
|
61505
61561
|
const moved = [];
|
|
61506
61562
|
const failed = [];
|
|
61507
61563
|
let movedBytes = 0;
|
|
61564
|
+
const deadline = now() + budgetSeconds * 1e3;
|
|
61508
61565
|
for (const object of objects) {
|
|
61566
|
+
if (now() >= deadline) break;
|
|
61509
61567
|
const { key } = object;
|
|
61510
61568
|
if (!isDeletableReplicaKey(prefix, key)) {
|
|
61511
61569
|
log(`WARNING: refusing to quarantine ${key}: outside the replica prefix.`);
|
|
@@ -61533,15 +61591,15 @@ async function quarantineReplica(store, prefix, objects, log, stamp = (/* @__PUR
|
|
|
61533
61591
|
moved.push(key);
|
|
61534
61592
|
movedBytes += object.size;
|
|
61535
61593
|
}
|
|
61536
|
-
logQuarantined(destination, moved, failed, log);
|
|
61594
|
+
logQuarantined(destination, moved, failed, objects.length - moved.length, log);
|
|
61537
61595
|
return { destination, moved, failed, movedBytes };
|
|
61538
61596
|
}
|
|
61539
|
-
function logQuarantined(destination, moved, failed, log) {
|
|
61597
|
+
function logQuarantined(destination, moved, failed, remaining, log) {
|
|
61540
61598
|
log(
|
|
61541
|
-
`WARNING: SESSION-DB-REPLICA-QUARANTINED: moved ${moved.length} unusable replica object(s) aside to ${destination} (${failed.length} could not be moved). Moved history remains readable at that destination; failed moves remain at their original keys.` + (
|
|
61599
|
+
`WARNING: SESSION-DB-REPLICA-QUARANTINED: moved ${moved.length} unusable replica object(s) aside to ${destination} (${failed.length} could not be moved). Moved history remains readable at that destination; failed moves remain at their original keys.` + (remaining > 0 ? ` The active replica prefix still has ${remaining} object(s).` : "") + ` Progress: moved=${moved.length} remaining=${remaining}. See SESSION-DB-REPLICA-SIZE above for how much was set aside.`
|
|
61542
61600
|
);
|
|
61543
61601
|
}
|
|
61544
|
-
async function separateCorruptReplica(store, prefix, log, stamp) {
|
|
61602
|
+
async function separateCorruptReplica(store, prefix, log, stamp, disposalBudgetSeconds = DEFAULT_DISPOSAL_BUDGET_SECONDS, now = Date.now) {
|
|
61545
61603
|
const probe = await probeReplica(store, prefix, log);
|
|
61546
61604
|
if (!probe.ok) return { kind: "unreachable", detail: probe.detail };
|
|
61547
61605
|
const root12 = replicaDbPrefix(prefix);
|
|
@@ -61551,7 +61609,15 @@ async function separateCorruptReplica(store, prefix, log, stamp) {
|
|
|
61551
61609
|
);
|
|
61552
61610
|
return { kind: "alreadyEmpty" };
|
|
61553
61611
|
}
|
|
61554
|
-
const result = await quarantineReplica(
|
|
61612
|
+
const result = await quarantineReplica(
|
|
61613
|
+
store,
|
|
61614
|
+
prefix,
|
|
61615
|
+
probe.objects,
|
|
61616
|
+
log,
|
|
61617
|
+
stamp,
|
|
61618
|
+
disposalBudgetSeconds,
|
|
61619
|
+
now
|
|
61620
|
+
);
|
|
61555
61621
|
try {
|
|
61556
61622
|
const remaining = await store.list(root12);
|
|
61557
61623
|
if (remaining.length > 0) {
|
|
@@ -61882,7 +61948,14 @@ async function verifySessionDb(params) {
|
|
|
61882
61948
|
);
|
|
61883
61949
|
const decision = await walkBack(params);
|
|
61884
61950
|
if (decision.kind !== "exhausted") return decision;
|
|
61885
|
-
const separation = params.store === null || config.prefix === null ? { kind: "notConfigured" } : await separateCorruptReplica(
|
|
61951
|
+
const separation = params.store === null || config.prefix === null ? { kind: "notConfigured" } : await separateCorruptReplica(
|
|
61952
|
+
params.store,
|
|
61953
|
+
config.prefix,
|
|
61954
|
+
log,
|
|
61955
|
+
void 0,
|
|
61956
|
+
params.disposalBudgetSeconds,
|
|
61957
|
+
params.now
|
|
61958
|
+
);
|
|
61886
61959
|
const localDb = separation.kind === "notConfigured" || separation.kind === "alreadyEmpty" || separation.kind === "quarantined" ? await discardCorruptDb(fileOps, dbPath, log) : { kind: "retained", reason: "separationUnproven" };
|
|
61887
61960
|
return { ...decision, separation, localDb };
|
|
61888
61961
|
}
|
|
@@ -62527,8 +62600,11 @@ function awsSelfStopAccessFor(config) {
|
|
|
62527
62600
|
function nodeSqliteIntegrityFor() {
|
|
62528
62601
|
return nodeSqliteIntegrity;
|
|
62529
62602
|
}
|
|
62530
|
-
function litestreamCliFor(configPath, dbPath) {
|
|
62531
|
-
return new LitestreamCli(configPath, dbPath
|
|
62603
|
+
function litestreamCliFor(configPath, dbPath, config) {
|
|
62604
|
+
return new LitestreamCli(configPath, dbPath, {
|
|
62605
|
+
restorePointsTimeoutSeconds: config.restorePointsTimeoutSeconds,
|
|
62606
|
+
restoreCandidateTimeoutSeconds: config.restoreCandidateTimeoutSeconds
|
|
62607
|
+
});
|
|
62532
62608
|
}
|
|
62533
62609
|
function parseStoreName(value) {
|
|
62534
62610
|
return value === "claude" || value === "opencode" ? value : null;
|
|
@@ -62688,7 +62764,8 @@ async function commandSessionDbVerify(litestreamConfigPath, config, deps) {
|
|
|
62688
62764
|
const { fileOps, log } = deps;
|
|
62689
62765
|
const litestream = (deps.litestreamFor ?? litestreamCliFor)(
|
|
62690
62766
|
litestreamConfigPath,
|
|
62691
|
-
config.opencodeDbPath
|
|
62767
|
+
config.opencodeDbPath,
|
|
62768
|
+
config
|
|
62692
62769
|
);
|
|
62693
62770
|
const outcome = await verifySessionDb({
|
|
62694
62771
|
config: { opencodeDbPath: config.opencodeDbPath, prefix: config.prefix },
|
|
@@ -62698,7 +62775,8 @@ async function commandSessionDbVerify(litestreamConfigPath, config, deps) {
|
|
|
62698
62775
|
litestream,
|
|
62699
62776
|
log,
|
|
62700
62777
|
maxPoints: config.walkbackMaxPoints,
|
|
62701
|
-
budgetSeconds: config.walkbackBudgetSeconds
|
|
62778
|
+
budgetSeconds: config.walkbackBudgetSeconds,
|
|
62779
|
+
disposalBudgetSeconds: config.disposalBudgetSeconds
|
|
62702
62780
|
});
|
|
62703
62781
|
log(describeSessionDbVerification(outcome));
|
|
62704
62782
|
if (outcome.kind === "exhausted" && sessionDbVerifyExitCode(outcome) === 34) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evident-ai/runner-synchroniser",
|
|
3
|
-
"version": "3.5.2-dev.
|
|
3
|
+
"version": "3.5.2-dev.31c0c2e",
|
|
4
4
|
"description": "Restores and syncs the Evident runner's OpenCode credential stores (and litestream config) to an object store, so a runner survives task replacement with almost no state loss.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli.js",
|