@evident-ai/cli 3.6.1-dev.43c1fd7 → 3.6.1-dev.4db2683
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 +4 -0
- package/dist/index.js +787 -132
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -723,6 +723,9 @@ async function reportResourceUsage(agentId, authHeader, usage) {
|
|
|
723
723
|
opencode_db_bytes: usage.opencodeDbBytes
|
|
724
724
|
});
|
|
725
725
|
}
|
|
726
|
+
async function reportSessionDeletions(agentId, authHeader, deletions) {
|
|
727
|
+
return postBestEffort(`/runners/${agentId}/session-deletions`, authHeader, { deletions });
|
|
728
|
+
}
|
|
726
729
|
async function getRunnerCredentialVault(agentId, authHeader) {
|
|
727
730
|
const empty = { prefix: null, vaultId: null, source: null };
|
|
728
731
|
try {
|
|
@@ -1176,9 +1179,9 @@ async function claudeUsage() {
|
|
|
1176
1179
|
}
|
|
1177
1180
|
|
|
1178
1181
|
// src/commands/run.ts
|
|
1179
|
-
import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as
|
|
1182
|
+
import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1180
1183
|
import { homedir as homedir8 } from "node:os";
|
|
1181
|
-
import { isAbsolute as isAbsolute4, join as
|
|
1184
|
+
import { isAbsolute as isAbsolute4, join as join14, parse, resolve as resolvePath2 } from "node:path";
|
|
1182
1185
|
import chalk7 from "chalk";
|
|
1183
1186
|
|
|
1184
1187
|
// ../../packages/types/src/agents/index.ts
|
|
@@ -1442,7 +1445,7 @@ import ora4 from "ora";
|
|
|
1442
1445
|
import { select as select4 } from "@inquirer/prompts";
|
|
1443
1446
|
|
|
1444
1447
|
// src/lib/telemetry.ts
|
|
1445
|
-
var CLI_VERSION = (true ? "3.6.1-dev.
|
|
1448
|
+
var CLI_VERSION = (true ? "3.6.1-dev.4db2683" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1446
1449
|
function getCliVersion() {
|
|
1447
1450
|
return CLI_VERSION;
|
|
1448
1451
|
}
|
|
@@ -2638,6 +2641,122 @@ function isSessionDbRecoveryRecord(value) {
|
|
|
2638
2641
|
);
|
|
2639
2642
|
}
|
|
2640
2643
|
|
|
2644
|
+
// src/lib/opencode/session-deletion-report.ts
|
|
2645
|
+
import { appendFileSync, mkdirSync, readFileSync as readFileSync3, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
2646
|
+
import { dirname as dirname4, join as join4 } from "node:path";
|
|
2647
|
+
var SESSION_DELETION_SPOOL_MAX_RECORDS = 1e3;
|
|
2648
|
+
var MAX_DIAGNOSTIC_LENGTH = 200;
|
|
2649
|
+
function sessionDeletionReportPath(homeDir, env) {
|
|
2650
|
+
const override = env.EVIDENT_SESSION_DELETION_REPORT?.trim();
|
|
2651
|
+
return override || join4(homeDir, ".local", "state", "evident", "session-deletions.jsonl");
|
|
2652
|
+
}
|
|
2653
|
+
function appendSessionDeletionRecords(path, records) {
|
|
2654
|
+
if (records.length === 0) return 0;
|
|
2655
|
+
mkdirSync(dirname4(path), { recursive: true });
|
|
2656
|
+
const current = readSessionDeletionRecords(path);
|
|
2657
|
+
if (current.readError !== null) {
|
|
2658
|
+
throw new Error(`cannot enforce the spool limit after reading ${path}: ${current.readError}`);
|
|
2659
|
+
}
|
|
2660
|
+
const combined = [...current.records, ...records];
|
|
2661
|
+
const kept = combined.slice(-SESSION_DELETION_SPOOL_MAX_RECORDS);
|
|
2662
|
+
const dropped = combined.length - kept.length;
|
|
2663
|
+
if (dropped > 0) {
|
|
2664
|
+
writeFileSync(path, `${kept.map((record) => JSON.stringify(record)).join("\n")}
|
|
2665
|
+
`, "utf8");
|
|
2666
|
+
} else {
|
|
2667
|
+
appendFileSync(path, `${records.map((record) => JSON.stringify(record)).join("\n")}
|
|
2668
|
+
`, "utf8");
|
|
2669
|
+
}
|
|
2670
|
+
return dropped;
|
|
2671
|
+
}
|
|
2672
|
+
function readSessionDeletionRecords(path) {
|
|
2673
|
+
let content;
|
|
2674
|
+
try {
|
|
2675
|
+
content = readFileSync3(path, "utf8");
|
|
2676
|
+
} catch (error2) {
|
|
2677
|
+
if (isMissingFile(error2)) return { records: [], skippedLines: 0, readError: null };
|
|
2678
|
+
const readError = boundedError(error2);
|
|
2679
|
+
console.error(`[session-deletion-report] could not read ${path}: ${readError}`);
|
|
2680
|
+
return { records: [], skippedLines: 0, readError };
|
|
2681
|
+
}
|
|
2682
|
+
let skippedLines = 0;
|
|
2683
|
+
const records = content.split("\n").flatMap((line) => {
|
|
2684
|
+
if (!line.trim()) return [];
|
|
2685
|
+
const record = parseRecord(line, path);
|
|
2686
|
+
if (record) return [record];
|
|
2687
|
+
skippedLines++;
|
|
2688
|
+
return [];
|
|
2689
|
+
});
|
|
2690
|
+
return { records, skippedLines, readError: null };
|
|
2691
|
+
}
|
|
2692
|
+
function removeDeliveredSessionDeletionRecords(path, delivered) {
|
|
2693
|
+
let content;
|
|
2694
|
+
try {
|
|
2695
|
+
content = readFileSync3(path, "utf8");
|
|
2696
|
+
} catch (error2) {
|
|
2697
|
+
if (isMissingFile(error2)) return;
|
|
2698
|
+
console.error(
|
|
2699
|
+
`[session-deletion-report] could not read ${path} before removing delivered records: ${boundedError(error2)}`
|
|
2700
|
+
);
|
|
2701
|
+
throw error2;
|
|
2702
|
+
}
|
|
2703
|
+
const toRemove = /* @__PURE__ */ new Map();
|
|
2704
|
+
for (const record of delivered) {
|
|
2705
|
+
toRemove.set(record.opencode_session_id, (toRemove.get(record.opencode_session_id) ?? 0) + 1);
|
|
2706
|
+
}
|
|
2707
|
+
const remaining = [];
|
|
2708
|
+
for (const line of content.split("\n")) {
|
|
2709
|
+
if (!line.trim()) continue;
|
|
2710
|
+
const record = parseRecord(line, path);
|
|
2711
|
+
const count = record ? toRemove.get(record.opencode_session_id) ?? 0 : 0;
|
|
2712
|
+
if (record && count > 0) {
|
|
2713
|
+
if (count === 1) toRemove.delete(record.opencode_session_id);
|
|
2714
|
+
else toRemove.set(record.opencode_session_id, count - 1);
|
|
2715
|
+
} else {
|
|
2716
|
+
remaining.push(line);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
try {
|
|
2720
|
+
if (remaining.length === 0) unlinkSync2(path);
|
|
2721
|
+
else writeFileSync(path, `${remaining.join("\n")}
|
|
2722
|
+
`, "utf8");
|
|
2723
|
+
} catch (error2) {
|
|
2724
|
+
if (isMissingFile(error2)) return;
|
|
2725
|
+
console.error(
|
|
2726
|
+
`[session-deletion-report] could not write ${path} after removing delivered records: ${boundedError(error2)}`
|
|
2727
|
+
);
|
|
2728
|
+
throw error2;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
function parseRecord(line, path) {
|
|
2732
|
+
try {
|
|
2733
|
+
const value = JSON.parse(line);
|
|
2734
|
+
if (isSessionDeletionRecord(value)) return value;
|
|
2735
|
+
console.error(`[session-deletion-report] skipped malformed record in ${path}: invalid shape`);
|
|
2736
|
+
return null;
|
|
2737
|
+
} catch (error2) {
|
|
2738
|
+
console.error(
|
|
2739
|
+
`[session-deletion-report] skipped malformed record in ${path}: ${boundedError(error2)}`
|
|
2740
|
+
);
|
|
2741
|
+
return null;
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
function isSessionDeletionRecord(value) {
|
|
2745
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2746
|
+
const record = value;
|
|
2747
|
+
const policy = record.policy;
|
|
2748
|
+
if (!policy || typeof policy !== "object" || Array.isArray(policy)) return false;
|
|
2749
|
+
const fields = policy;
|
|
2750
|
+
return typeof record.opencode_session_id === "string" && record.opencode_session_id.length > 0 && typeof record.deleted_at === "string" && record.reason === "auto_cleanup" && (fields.max_age_ms === null || typeof fields.max_age_ms === "number") && (fields.max_count === null || typeof fields.max_count === "number") && (fields.last_activity_at === null || typeof fields.last_activity_at === "string");
|
|
2751
|
+
}
|
|
2752
|
+
function isMissingFile(error2) {
|
|
2753
|
+
return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
|
|
2754
|
+
}
|
|
2755
|
+
function boundedError(error2) {
|
|
2756
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
2757
|
+
return message.slice(0, MAX_DIAGNOSTIC_LENGTH);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2641
2760
|
// src/lib/opencode/auth.ts
|
|
2642
2761
|
function buildOpenCodeBasicAuthHeader(password) {
|
|
2643
2762
|
return `Basic ${Buffer.from(["opencode", password].join(":")).toString("base64")}`;
|
|
@@ -3209,6 +3328,52 @@ async function getModelAttachmentCapability(port, model, client) {
|
|
|
3209
3328
|
return null;
|
|
3210
3329
|
}
|
|
3211
3330
|
}
|
|
3331
|
+
async function isPinnedProviderLoaded(port, model, client) {
|
|
3332
|
+
if (client?.version === "v2") return null;
|
|
3333
|
+
const { model: baseModel } = splitModelVariant(model);
|
|
3334
|
+
if (!baseModel) return null;
|
|
3335
|
+
const slash = baseModel.indexOf("/");
|
|
3336
|
+
if (slash <= 0) return null;
|
|
3337
|
+
const providerId = baseModel.slice(0, slash);
|
|
3338
|
+
try {
|
|
3339
|
+
const res = await requestWithClient(port, client, "/config/providers");
|
|
3340
|
+
if (!res.ok) {
|
|
3341
|
+
console.error(
|
|
3342
|
+
`[isPinnedProviderLoaded] GET /config/providers returned HTTP ${res.status} (port ${port})`
|
|
3343
|
+
);
|
|
3344
|
+
return null;
|
|
3345
|
+
}
|
|
3346
|
+
const body = await res.json();
|
|
3347
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
3348
|
+
console.error(
|
|
3349
|
+
`[isPinnedProviderLoaded] GET /config/providers body was not a plain object (port ${port})`
|
|
3350
|
+
);
|
|
3351
|
+
return null;
|
|
3352
|
+
}
|
|
3353
|
+
const providers = body.providers;
|
|
3354
|
+
if (!Array.isArray(providers)) {
|
|
3355
|
+
console.error(
|
|
3356
|
+
`[isPinnedProviderLoaded] GET /config/providers body had no providers array (port ${port})`
|
|
3357
|
+
);
|
|
3358
|
+
return null;
|
|
3359
|
+
}
|
|
3360
|
+
const providerIds = providers.filter(
|
|
3361
|
+
(provider) => provider !== null && typeof provider === "object" && typeof provider.id === "string"
|
|
3362
|
+
).map((provider) => provider.id);
|
|
3363
|
+
if (providerIds.length === 0) {
|
|
3364
|
+
console.error(
|
|
3365
|
+
`[isPinnedProviderLoaded] GET /config/providers had no provider entries with string ids (port ${port})`
|
|
3366
|
+
);
|
|
3367
|
+
return null;
|
|
3368
|
+
}
|
|
3369
|
+
return providerIds.includes(providerId);
|
|
3370
|
+
} catch (err) {
|
|
3371
|
+
console.error(
|
|
3372
|
+
`[isPinnedProviderLoaded] GET /config/providers failed (port ${port}): ${err instanceof Error ? err.message : String(err)}`
|
|
3373
|
+
);
|
|
3374
|
+
return null;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3212
3377
|
async function buildFileParts(attachments, capable) {
|
|
3213
3378
|
const outcomes = [];
|
|
3214
3379
|
const parts = [];
|
|
@@ -3623,9 +3788,17 @@ function isPreamblePinnedRunning(messages, userMessageId) {
|
|
|
3623
3788
|
const reply = findLastAssistantReplyFor(messages, userMessageId);
|
|
3624
3789
|
return completedOf(reply) != null && finishOf(reply) === "tool-calls";
|
|
3625
3790
|
}
|
|
3791
|
+
function isMidFlightPinnedRunning(messages, userMessageId) {
|
|
3792
|
+
if (messageRunState(messages, userMessageId) !== "running") return false;
|
|
3793
|
+
const reply = findLastAssistantReplyFor(messages, userMessageId);
|
|
3794
|
+
return reply !== null && isAssistantInFlight(reply) && completedOf(reply) == null;
|
|
3795
|
+
}
|
|
3626
3796
|
function isB2AbandonmentConfirmed(params) {
|
|
3627
3797
|
return params.pinnedForMs >= params.minPinnedMs && params.descendantOngoing === false && params.rootOngoing === false;
|
|
3628
3798
|
}
|
|
3799
|
+
function isMidFlightOrphanConfirmed(params) {
|
|
3800
|
+
return params.pinnedForMs >= params.minPinnedMs && params.notOngoingStreak >= params.minNotOngoingStreak;
|
|
3801
|
+
}
|
|
3629
3802
|
function isAmbiguousTerminalFinish(m) {
|
|
3630
3803
|
if (completedOf(m) == null) return false;
|
|
3631
3804
|
if (errorOf(m) != null) return false;
|
|
@@ -4491,13 +4664,13 @@ async function readV2SessionErrorStream(client, options) {
|
|
|
4491
4664
|
}
|
|
4492
4665
|
|
|
4493
4666
|
// src/lib/opencode/credential-import-v2.ts
|
|
4494
|
-
import { readFileSync as
|
|
4495
|
-
import { join as
|
|
4667
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
4668
|
+
import { join as join6 } from "node:path";
|
|
4496
4669
|
|
|
4497
4670
|
// src/lib/openai-usage.ts
|
|
4498
|
-
import { readFileSync as
|
|
4671
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
4499
4672
|
import { homedir as homedir3 } from "node:os";
|
|
4500
|
-
import { join as
|
|
4673
|
+
import { join as join5 } from "node:path";
|
|
4501
4674
|
var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
4502
4675
|
var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
|
|
4503
4676
|
var OpenAiUsageError = class extends Error {
|
|
@@ -4511,7 +4684,7 @@ function isLocalCredentialProblem2(err) {
|
|
|
4511
4684
|
}
|
|
4512
4685
|
function readOpenCodeChatGptCredentials() {
|
|
4513
4686
|
try {
|
|
4514
|
-
const raw =
|
|
4687
|
+
const raw = readFileSync4(join5(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
|
|
4515
4688
|
let parsed;
|
|
4516
4689
|
try {
|
|
4517
4690
|
parsed = JSON.parse(raw);
|
|
@@ -4743,7 +4916,7 @@ function emptyResult(unsupported = [], outcome = "completed") {
|
|
|
4743
4916
|
}
|
|
4744
4917
|
function readOpenCodeCredentials(homeDir, log3) {
|
|
4745
4918
|
try {
|
|
4746
|
-
const raw =
|
|
4919
|
+
const raw = readFileSync5(join6(homeDir, ...OPENCODE_AUTH_SEGMENTS), "utf8");
|
|
4747
4920
|
let parsed;
|
|
4748
4921
|
try {
|
|
4749
4922
|
parsed = JSON.parse(raw);
|
|
@@ -4943,9 +5116,9 @@ async function importOpenCodeV2Credentials({
|
|
|
4943
5116
|
|
|
4944
5117
|
// src/lib/opencode/session-db-boot.ts
|
|
4945
5118
|
import { spawn as spawn2 } from "node:child_process";
|
|
4946
|
-
import { mkdirSync, statSync as statSync3, unlinkSync as
|
|
5119
|
+
import { mkdirSync as mkdirSync2, statSync as statSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4947
5120
|
import { homedir as homedir4 } from "node:os";
|
|
4948
|
-
import { dirname as
|
|
5121
|
+
import { dirname as dirname5, resolve as resolvePath } from "node:path";
|
|
4949
5122
|
|
|
4950
5123
|
// src/lib/runner-synchroniser.ts
|
|
4951
5124
|
import { spawn } from "node:child_process";
|
|
@@ -5061,7 +5234,7 @@ function reportRecord(stage, outcome, reason, litestreamExitCode, options) {
|
|
|
5061
5234
|
function clearMarker(options) {
|
|
5062
5235
|
if (!options.noReplicateMarker) return;
|
|
5063
5236
|
try {
|
|
5064
|
-
|
|
5237
|
+
unlinkSync3(options.noReplicateMarker);
|
|
5065
5238
|
} catch (error2) {
|
|
5066
5239
|
if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") return;
|
|
5067
5240
|
options.log(
|
|
@@ -5073,8 +5246,8 @@ function clearMarker(options) {
|
|
|
5073
5246
|
function markNoReplicate(options, message) {
|
|
5074
5247
|
if (options.noReplicateMarker) {
|
|
5075
5248
|
try {
|
|
5076
|
-
|
|
5077
|
-
|
|
5249
|
+
mkdirSync2(dirname5(options.noReplicateMarker), { recursive: true });
|
|
5250
|
+
writeFileSync2(options.noReplicateMarker, "");
|
|
5078
5251
|
} catch (error2) {
|
|
5079
5252
|
options.log(
|
|
5080
5253
|
`Could not write session-DB no-replicate marker ${options.noReplicateMarker}: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
@@ -5087,7 +5260,7 @@ function markNoReplicate(options, message) {
|
|
|
5087
5260
|
function discardSessionDbDebris(options) {
|
|
5088
5261
|
for (const path of [options.dbPath, `${options.dbPath}-wal`, `${options.dbPath}-shm`]) {
|
|
5089
5262
|
try {
|
|
5090
|
-
|
|
5263
|
+
unlinkSync3(path);
|
|
5091
5264
|
} catch (error2) {
|
|
5092
5265
|
if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") continue;
|
|
5093
5266
|
options.log(
|
|
@@ -5228,8 +5401,8 @@ async function ensureLitestreamConfig(options, env) {
|
|
|
5228
5401
|
return null;
|
|
5229
5402
|
}
|
|
5230
5403
|
try {
|
|
5231
|
-
|
|
5232
|
-
|
|
5404
|
+
mkdirSync2(dirname5(configPath), { recursive: true });
|
|
5405
|
+
writeFileSync2(configPath, rendered.stdout);
|
|
5233
5406
|
} catch (error2) {
|
|
5234
5407
|
options.log(
|
|
5235
5408
|
`Could not write ${configPath}: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
@@ -5494,8 +5667,8 @@ async function restoreAndVerifySessionDb(options) {
|
|
|
5494
5667
|
|
|
5495
5668
|
// src/lib/opencode/session-db-provenance.ts
|
|
5496
5669
|
import { createRequire } from "node:module";
|
|
5497
|
-
import { mkdirSync as
|
|
5498
|
-
import { dirname as
|
|
5670
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5671
|
+
import { dirname as dirname6, join as join7 } from "node:path";
|
|
5499
5672
|
var require2 = createRequire(import.meta.url);
|
|
5500
5673
|
function readSessionDbMigrationIds(dbPath) {
|
|
5501
5674
|
let db;
|
|
@@ -5532,12 +5705,12 @@ function readSessionDbMigrationIds(dbPath) {
|
|
|
5532
5705
|
}
|
|
5533
5706
|
function sessionDbProvenanceStatePath(homeDir, env) {
|
|
5534
5707
|
const override = env.EVIDENT_SESSION_DB_PROVENANCE_STATE?.trim();
|
|
5535
|
-
return override ||
|
|
5708
|
+
return override || join7(homeDir, ".local", "state", "evident", "session-db-provenance.json");
|
|
5536
5709
|
}
|
|
5537
5710
|
function loadSessionDbProvenanceState(path) {
|
|
5538
5711
|
let value;
|
|
5539
5712
|
try {
|
|
5540
|
-
value = JSON.parse(
|
|
5713
|
+
value = JSON.parse(readFileSync6(path, "utf8"));
|
|
5541
5714
|
} catch (error2) {
|
|
5542
5715
|
if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") return {};
|
|
5543
5716
|
console.error(
|
|
@@ -5561,8 +5734,8 @@ function loadSessionDbProvenanceState(path) {
|
|
|
5561
5734
|
}
|
|
5562
5735
|
function saveSessionDbProvenanceState(path, state) {
|
|
5563
5736
|
try {
|
|
5564
|
-
|
|
5565
|
-
|
|
5737
|
+
mkdirSync3(dirname6(path), { recursive: true });
|
|
5738
|
+
writeFileSync3(path, `${JSON.stringify(state, null, 2)}
|
|
5566
5739
|
`, "utf8");
|
|
5567
5740
|
} catch (error2) {
|
|
5568
5741
|
console.error(
|
|
@@ -5621,7 +5794,11 @@ function isSessionDbProvenanceRecord(value) {
|
|
|
5621
5794
|
var OPENCODE2_PACKAGE_SPEC = "@opencode-ai/cli@0.0.0-beta-17823";
|
|
5622
5795
|
|
|
5623
5796
|
// src/lib/opencode/opencode-version-gate.ts
|
|
5624
|
-
var QUEUE_VALIDATED_OPENCODE_VERSIONS = [
|
|
5797
|
+
var QUEUE_VALIDATED_OPENCODE_VERSIONS = [
|
|
5798
|
+
"1.17.11",
|
|
5799
|
+
"1.18.3",
|
|
5800
|
+
"1.18.35"
|
|
5801
|
+
];
|
|
5625
5802
|
function isQueueValidatedVersion(version2) {
|
|
5626
5803
|
if (!version2) return false;
|
|
5627
5804
|
return QUEUE_VALIDATED_OPENCODE_VERSIONS.includes(version2);
|
|
@@ -6206,15 +6383,15 @@ function resolveSessionCleanupConfig(flags, env = process.env) {
|
|
|
6206
6383
|
|
|
6207
6384
|
// src/lib/opencode/session-db-size.ts
|
|
6208
6385
|
import { statSync as statSync4 } from "node:fs";
|
|
6209
|
-
import { join as
|
|
6386
|
+
import { join as join8 } from "node:path";
|
|
6210
6387
|
var LARGE_DB_THRESHOLD_BYTES = 268435456;
|
|
6211
6388
|
function statSessionDbBytes(homeDir) {
|
|
6212
|
-
const dbPath =
|
|
6389
|
+
const dbPath = join8(homeDir, ".local", "share", "opencode", "opencode.db");
|
|
6213
6390
|
try {
|
|
6214
6391
|
return statSync4(dbPath).size;
|
|
6215
6392
|
} catch (err) {
|
|
6216
|
-
const
|
|
6217
|
-
if (!
|
|
6393
|
+
const isMissingFile2 = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
6394
|
+
if (!isMissingFile2) {
|
|
6218
6395
|
console.error(
|
|
6219
6396
|
`[statSessionDbBytes] could not stat ${dbPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
6220
6397
|
);
|
|
@@ -6239,12 +6416,12 @@ function buildSessionStoreSizeWarning(input) {
|
|
|
6239
6416
|
// src/lib/opencode/log-tail.ts
|
|
6240
6417
|
import { statSync as statSync5 } from "node:fs";
|
|
6241
6418
|
import { homedir as homedir5 } from "node:os";
|
|
6242
|
-
import { join as
|
|
6419
|
+
import { join as join9 } from "node:path";
|
|
6243
6420
|
import { open as open2, stat } from "node:fs/promises";
|
|
6244
6421
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
6245
6422
|
function resolveOpenCodeLogPath(homeDir = homedir5(), env = process.env) {
|
|
6246
|
-
const dataDir = env.XDG_DATA_HOME ||
|
|
6247
|
-
return
|
|
6423
|
+
const dataDir = env.XDG_DATA_HOME || join9(homeDir, ".local", "share");
|
|
6424
|
+
return join9(dataDir, "opencode", "log", "opencode.log");
|
|
6248
6425
|
}
|
|
6249
6426
|
function isEnoent(error2) {
|
|
6250
6427
|
return error2?.code === "ENOENT";
|
|
@@ -6911,10 +7088,10 @@ var RunnerConnection = class _RunnerConnection {
|
|
|
6911
7088
|
};
|
|
6912
7089
|
|
|
6913
7090
|
// src/lib/tunnel/ready-marker.ts
|
|
6914
|
-
import { writeFileSync as
|
|
7091
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
6915
7092
|
function writeTunnelReadyMarker(path, agentId) {
|
|
6916
7093
|
try {
|
|
6917
|
-
|
|
7094
|
+
writeFileSync4(path, `${agentId}
|
|
6918
7095
|
`);
|
|
6919
7096
|
return { ok: true };
|
|
6920
7097
|
} catch (error2) {
|
|
@@ -6939,7 +7116,7 @@ async function stopSessionDbReplication(child, timeoutMs) {
|
|
|
6939
7116
|
}
|
|
6940
7117
|
|
|
6941
7118
|
// src/lib/process-liveness.ts
|
|
6942
|
-
import { readFileSync as
|
|
7119
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
6943
7120
|
function isProcessAlive(pid) {
|
|
6944
7121
|
try {
|
|
6945
7122
|
process.kill(pid, 0);
|
|
@@ -6954,7 +7131,7 @@ function isProcessAlive(pid) {
|
|
|
6954
7131
|
}
|
|
6955
7132
|
if (process.platform !== "linux") return true;
|
|
6956
7133
|
try {
|
|
6957
|
-
const status2 =
|
|
7134
|
+
const status2 = readFileSync7(`/proc/${pid}/status`, "utf8");
|
|
6958
7135
|
return !/^State:\s+Z(?:\s|$)/m.test(status2);
|
|
6959
7136
|
} catch (error2) {
|
|
6960
7137
|
console.error(
|
|
@@ -7313,12 +7490,12 @@ function createSessionStoreHealth(options = {}) {
|
|
|
7313
7490
|
import { homedir as homedir6 } from "node:os";
|
|
7314
7491
|
|
|
7315
7492
|
// src/lib/runner-file-sync.ts
|
|
7316
|
-
import { join as
|
|
7493
|
+
import { join as join11 } from "node:path";
|
|
7317
7494
|
|
|
7318
7495
|
// src/lib/file-push.ts
|
|
7319
7496
|
import { randomUUID } from "node:crypto";
|
|
7320
7497
|
import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/promises";
|
|
7321
|
-
import { basename as basename2, dirname as
|
|
7498
|
+
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join10, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
7322
7499
|
var FILE_MODE = 384;
|
|
7323
7500
|
var DIRECTORY_MODE = 448;
|
|
7324
7501
|
async function writePushedFile(request3) {
|
|
@@ -7349,9 +7526,9 @@ async function writePushedFile(request3) {
|
|
|
7349
7526
|
}
|
|
7350
7527
|
try {
|
|
7351
7528
|
const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
|
|
7352
|
-
|
|
7529
|
+
dirname7(candidate)
|
|
7353
7530
|
);
|
|
7354
|
-
const realTarget =
|
|
7531
|
+
const realTarget = join10(existingAncestor, ...missingSegments, basename2(candidate));
|
|
7355
7532
|
const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
|
|
7356
7533
|
if (allowedDirectory === null) {
|
|
7357
7534
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
@@ -7361,8 +7538,8 @@ async function writePushedFile(request3) {
|
|
|
7361
7538
|
}
|
|
7362
7539
|
if (missingSegments.length > 0) {
|
|
7363
7540
|
await createMissingDirectories(existingAncestor, missingSegments);
|
|
7364
|
-
const realParent = await realpath(
|
|
7365
|
-
if (realParent !==
|
|
7541
|
+
const realParent = await realpath(dirname7(realTarget));
|
|
7542
|
+
if (realParent !== dirname7(realTarget) || !contains(allowedDirectory, realTarget)) {
|
|
7366
7543
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
7367
7544
|
path: realTarget,
|
|
7368
7545
|
bytes,
|
|
@@ -7387,7 +7564,7 @@ function expandAndValidate(requestedPath, homeDir) {
|
|
|
7387
7564
|
if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
|
|
7388
7565
|
return null;
|
|
7389
7566
|
}
|
|
7390
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
7567
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join10(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
7391
7568
|
if (expanded.split(/[/\\]/).includes("..")) {
|
|
7392
7569
|
return null;
|
|
7393
7570
|
}
|
|
@@ -7405,7 +7582,7 @@ async function resolveNearestExistingAncestor(directory) {
|
|
|
7405
7582
|
try {
|
|
7406
7583
|
return { existingAncestor: await realpath(current), missingSegments };
|
|
7407
7584
|
} catch (err) {
|
|
7408
|
-
const parent =
|
|
7585
|
+
const parent = dirname7(current);
|
|
7409
7586
|
if (err.code !== "ENOENT" || parent === current) {
|
|
7410
7587
|
throw err;
|
|
7411
7588
|
}
|
|
@@ -7460,13 +7637,13 @@ function contains(realDirectory, realTarget) {
|
|
|
7460
7637
|
async function createMissingDirectories(existingAncestor, missingSegments) {
|
|
7461
7638
|
let current = existingAncestor;
|
|
7462
7639
|
for (const segment of missingSegments) {
|
|
7463
|
-
current =
|
|
7640
|
+
current = join10(current, segment);
|
|
7464
7641
|
await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
|
|
7465
7642
|
await chmod(current, DIRECTORY_MODE);
|
|
7466
7643
|
}
|
|
7467
7644
|
}
|
|
7468
7645
|
async function writeAtomically(realTarget, content) {
|
|
7469
|
-
const temporaryPath =
|
|
7646
|
+
const temporaryPath = join10(dirname7(realTarget), `.evident-push-${randomUUID()}.tmp`);
|
|
7470
7647
|
let handle;
|
|
7471
7648
|
try {
|
|
7472
7649
|
handle = await open3(temporaryPath, "wx", FILE_MODE);
|
|
@@ -7596,12 +7773,12 @@ var NOT_APPLIED = {
|
|
|
7596
7773
|
opencodeAuthApplied: false
|
|
7597
7774
|
};
|
|
7598
7775
|
function isClaudeCredentialPath(requestedPath, homeDir) {
|
|
7599
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
7600
|
-
return expanded ===
|
|
7776
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join11(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
7777
|
+
return expanded === join11(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
|
|
7601
7778
|
}
|
|
7602
7779
|
function isOpenCodeAuthPath(requestedPath, homeDir) {
|
|
7603
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
7604
|
-
return expanded ===
|
|
7780
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join11(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
7781
|
+
return expanded === join11(homeDir, ...OPENCODE_AUTH_SEGMENTS);
|
|
7605
7782
|
}
|
|
7606
7783
|
async function applyOne(options, file) {
|
|
7607
7784
|
const label = `${file.id.slice(0, 8)} (${file.path})`;
|
|
@@ -7738,6 +7915,211 @@ function describe(err) {
|
|
|
7738
7915
|
return err instanceof Error ? err.message : String(err);
|
|
7739
7916
|
}
|
|
7740
7917
|
|
|
7918
|
+
// src/lib/vault-file-sync.ts
|
|
7919
|
+
import { createHash } from "node:crypto";
|
|
7920
|
+
async function syncVaultFiles(options) {
|
|
7921
|
+
const files = await listVaultFiles(options);
|
|
7922
|
+
if (files === null) return { applied: 0 };
|
|
7923
|
+
const listedIds = new Set(files.map((file) => file.id));
|
|
7924
|
+
for (const fileId of options.outcomes.keys()) {
|
|
7925
|
+
if (!listedIds.has(fileId)) options.outcomes.delete(fileId);
|
|
7926
|
+
}
|
|
7927
|
+
let applied = 0;
|
|
7928
|
+
for (const file of files) {
|
|
7929
|
+
try {
|
|
7930
|
+
if (await reconcileVaultFile(options, file)) applied += 1;
|
|
7931
|
+
} catch (err) {
|
|
7932
|
+
options.log({
|
|
7933
|
+
level: "error",
|
|
7934
|
+
message: `Vault file ${file.id} reconciliation failed unexpectedly (message processing is unaffected): ${describe2(err)}`
|
|
7935
|
+
});
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
return { applied };
|
|
7939
|
+
}
|
|
7940
|
+
async function listVaultFiles(options) {
|
|
7941
|
+
let response;
|
|
7942
|
+
try {
|
|
7943
|
+
response = await options.fetchImpl(`${options.apiUrl}/runners/${options.agentId}/vault-files`, {
|
|
7944
|
+
headers: { Authorization: options.getAuthHeader() }
|
|
7945
|
+
});
|
|
7946
|
+
} catch (err) {
|
|
7947
|
+
options.log({
|
|
7948
|
+
level: "warn",
|
|
7949
|
+
message: `Could not list vault files \u2014 retrying on the next drain: ${describe2(err)}`
|
|
7950
|
+
});
|
|
7951
|
+
return null;
|
|
7952
|
+
}
|
|
7953
|
+
if (!response.ok) {
|
|
7954
|
+
options.log({
|
|
7955
|
+
level: response.status === 404 ? "debug" : "warn",
|
|
7956
|
+
message: `Listing vault files returned HTTP ${response.status} \u2014 retrying on the next drain`
|
|
7957
|
+
});
|
|
7958
|
+
return null;
|
|
7959
|
+
}
|
|
7960
|
+
let body;
|
|
7961
|
+
try {
|
|
7962
|
+
body = await response.json();
|
|
7963
|
+
} catch (err) {
|
|
7964
|
+
options.log({
|
|
7965
|
+
level: "warn",
|
|
7966
|
+
message: `Vault file list was not readable JSON \u2014 retrying on the next drain: ${describe2(err)}`
|
|
7967
|
+
});
|
|
7968
|
+
return null;
|
|
7969
|
+
}
|
|
7970
|
+
if (body === null || typeof body !== "object" || !Array.isArray(body.files)) {
|
|
7971
|
+
options.log({
|
|
7972
|
+
level: "warn",
|
|
7973
|
+
message: "Vault file list was not an object with a files array \u2014 ignoring it for this drain"
|
|
7974
|
+
});
|
|
7975
|
+
return null;
|
|
7976
|
+
}
|
|
7977
|
+
const files = [];
|
|
7978
|
+
for (const entry of body.files) {
|
|
7979
|
+
const file = asVaultFile(entry);
|
|
7980
|
+
if (file === null) {
|
|
7981
|
+
options.log({
|
|
7982
|
+
level: "warn",
|
|
7983
|
+
message: "Ignoring a malformed vault file entry"
|
|
7984
|
+
});
|
|
7985
|
+
continue;
|
|
7986
|
+
}
|
|
7987
|
+
files.push(file);
|
|
7988
|
+
}
|
|
7989
|
+
return files;
|
|
7990
|
+
}
|
|
7991
|
+
function asVaultFile(entry) {
|
|
7992
|
+
if (entry === null || typeof entry !== "object") return null;
|
|
7993
|
+
const value = entry;
|
|
7994
|
+
if (typeof value.id !== "string" || value.id === "" || typeof value.destination_path !== "string" || value.destination_path === "" || typeof value.size_bytes !== "number" || !Number.isFinite(value.size_bytes) || value.size_bytes < 0 || typeof value.content_sha256 !== "string" || value.content_sha256 === "" || typeof value.created_at !== "string" || typeof value.updated_at !== "string") {
|
|
7995
|
+
return null;
|
|
7996
|
+
}
|
|
7997
|
+
return {
|
|
7998
|
+
id: value.id,
|
|
7999
|
+
destination_path: value.destination_path,
|
|
8000
|
+
size_bytes: value.size_bytes,
|
|
8001
|
+
content_sha256: value.content_sha256,
|
|
8002
|
+
created_at: value.created_at,
|
|
8003
|
+
updated_at: value.updated_at
|
|
8004
|
+
};
|
|
8005
|
+
}
|
|
8006
|
+
async function reconcileVaultFile(options, file) {
|
|
8007
|
+
const existing = options.outcomes.get(file.id);
|
|
8008
|
+
if (existing?.sha256 === file.content_sha256) {
|
|
8009
|
+
if (existing.reported || existing.reportAttempts >= MAX_ACK_ATTEMPTS) return false;
|
|
8010
|
+
await reportOutcome(options, file, existing);
|
|
8011
|
+
return false;
|
|
8012
|
+
}
|
|
8013
|
+
const content = await downloadVaultFile(options, file);
|
|
8014
|
+
if (content === null) return false;
|
|
8015
|
+
const actualSha256 = createHash("sha256").update(content).digest("hex");
|
|
8016
|
+
if (actualSha256 !== file.content_sha256) {
|
|
8017
|
+
options.log({
|
|
8018
|
+
level: "warn",
|
|
8019
|
+
message: `Vault file ${file.id} content hash mismatch: expected ${file.content_sha256}, received ${actualSha256} \u2014 retrying on the next drain`
|
|
8020
|
+
});
|
|
8021
|
+
return false;
|
|
8022
|
+
}
|
|
8023
|
+
const outcome = await applyVaultFile(options, file, content);
|
|
8024
|
+
const entry = {
|
|
8025
|
+
sha256: file.content_sha256,
|
|
8026
|
+
outcome: outcome.ok ? "applied" : "refused",
|
|
8027
|
+
reason: outcome.ok ? null : outcome.code,
|
|
8028
|
+
reported: false,
|
|
8029
|
+
reportAttempts: 0
|
|
8030
|
+
};
|
|
8031
|
+
options.outcomes.set(file.id, entry);
|
|
8032
|
+
await reportOutcome(options, file, entry);
|
|
8033
|
+
return outcome.ok;
|
|
8034
|
+
}
|
|
8035
|
+
async function downloadVaultFile(options, file) {
|
|
8036
|
+
try {
|
|
8037
|
+
const response = await options.fetchImpl(
|
|
8038
|
+
`${options.apiUrl}/runners/${options.agentId}/vault-files/${file.id}/content`,
|
|
8039
|
+
{ headers: { Authorization: options.getAuthHeader() } }
|
|
8040
|
+
);
|
|
8041
|
+
if (!response.ok) {
|
|
8042
|
+
options.log({
|
|
8043
|
+
level: "warn",
|
|
8044
|
+
message: `Downloading vault file ${file.id} returned HTTP ${response.status} \u2014 retrying on the next drain`
|
|
8045
|
+
});
|
|
8046
|
+
return null;
|
|
8047
|
+
}
|
|
8048
|
+
return Buffer.from(await response.arrayBuffer());
|
|
8049
|
+
} catch (err) {
|
|
8050
|
+
options.log({
|
|
8051
|
+
level: "warn",
|
|
8052
|
+
message: `Downloading vault file ${file.id} failed \u2014 retrying on the next drain: ${describe2(err)}`
|
|
8053
|
+
});
|
|
8054
|
+
return null;
|
|
8055
|
+
}
|
|
8056
|
+
}
|
|
8057
|
+
async function applyVaultFile(options, file, content) {
|
|
8058
|
+
try {
|
|
8059
|
+
const outcome = await writePushedFile({
|
|
8060
|
+
requestedPath: file.destination_path,
|
|
8061
|
+
content,
|
|
8062
|
+
allowedDirectories: options.allowedDirectories,
|
|
8063
|
+
homeDir: options.homeDir
|
|
8064
|
+
});
|
|
8065
|
+
if (!outcome.ok) {
|
|
8066
|
+
options.log({
|
|
8067
|
+
level: "warn",
|
|
8068
|
+
message: `Vault file ${file.id} refused (${outcome.code}): ${outcome.message}`
|
|
8069
|
+
});
|
|
8070
|
+
} else {
|
|
8071
|
+
options.log({
|
|
8072
|
+
level: "info",
|
|
8073
|
+
message: `Vault file ${file.id} applied (${content.byteLength} bytes)`
|
|
8074
|
+
});
|
|
8075
|
+
}
|
|
8076
|
+
return outcome;
|
|
8077
|
+
} catch (err) {
|
|
8078
|
+
options.log({
|
|
8079
|
+
level: "error",
|
|
8080
|
+
message: `Vault file ${file.id} could not be written: ${describe2(err)}`
|
|
8081
|
+
});
|
|
8082
|
+
return { ok: false, code: "write_failed", message: "The runner could not write the file." };
|
|
8083
|
+
}
|
|
8084
|
+
}
|
|
8085
|
+
async function reportOutcome(options, file, entry) {
|
|
8086
|
+
const body = {
|
|
8087
|
+
status: entry.outcome === "applied" ? "applied" : "rejected",
|
|
8088
|
+
reported_sha256: entry.sha256
|
|
8089
|
+
};
|
|
8090
|
+
if (entry.outcome === "refused" && entry.reason !== null) body.reason = entry.reason;
|
|
8091
|
+
try {
|
|
8092
|
+
const response = await options.fetchImpl(
|
|
8093
|
+
`${options.apiUrl}/runners/${options.agentId}/vault-files/${file.id}/status`,
|
|
8094
|
+
{
|
|
8095
|
+
method: "POST",
|
|
8096
|
+
headers: {
|
|
8097
|
+
Authorization: options.getAuthHeader(),
|
|
8098
|
+
"Content-Type": "application/json"
|
|
8099
|
+
},
|
|
8100
|
+
body: JSON.stringify(body)
|
|
8101
|
+
}
|
|
8102
|
+
);
|
|
8103
|
+
if (response.ok) {
|
|
8104
|
+
entry.reported = true;
|
|
8105
|
+
return;
|
|
8106
|
+
}
|
|
8107
|
+
recordReportFailure(options, file, entry, `HTTP ${response.status}`);
|
|
8108
|
+
} catch (err) {
|
|
8109
|
+
recordReportFailure(options, file, entry, describe2(err));
|
|
8110
|
+
}
|
|
8111
|
+
}
|
|
8112
|
+
function recordReportFailure(options, file, entry, reason) {
|
|
8113
|
+
entry.reportAttempts += 1;
|
|
8114
|
+
options.log({
|
|
8115
|
+
level: "error",
|
|
8116
|
+
message: entry.reportAttempts >= MAX_ACK_ATTEMPTS ? `Reporting vault file ${file.id} as ${entry.outcome} failed (${reason}) \u2014 giving up after ${entry.reportAttempts} attempts` : `Reporting vault file ${file.id} as ${entry.outcome} failed (${reason}) \u2014 retrying on the next drain (attempt ${entry.reportAttempts} of ${MAX_ACK_ATTEMPTS})`
|
|
8117
|
+
});
|
|
8118
|
+
}
|
|
8119
|
+
function describe2(err) {
|
|
8120
|
+
return err instanceof Error ? err.message : String(err);
|
|
8121
|
+
}
|
|
8122
|
+
|
|
7741
8123
|
// src/lib/channels/driver.ts
|
|
7742
8124
|
function messageIdOf(m) {
|
|
7743
8125
|
if (!m || typeof m !== "object") return void 0;
|
|
@@ -7746,7 +8128,7 @@ function messageIdOf(m) {
|
|
|
7746
8128
|
return typeof infoId === "string" ? infoId : void 0;
|
|
7747
8129
|
}
|
|
7748
8130
|
function composeAbandonmentNote(finish, replyShape, outcome) {
|
|
7749
|
-
const base = `OpenCode ended this turn after a tool call (finish="${finish ?? "(unknown)"}") ` + (outcome === "without-answer" ? "without writing an answer, and confirmed it is no longer working on this conversation. Evident is reporting the run as failed so the failure and its reason are visible in failed runs." : "after writing only part of an answer, and confirmed it is no longer working on this conversation. Evident delivered what was written and is reporting the run as finished.");
|
|
8131
|
+
const base = outcome === "midflight-without-answer" || outcome === "midflight-with-answer" ? "OpenCode stopped this turn without ever finishing, and confirmed the session is no longer running. " + (outcome === "midflight-without-answer" ? "Evident is reporting the run as failed so the failure and its reason are visible in failed runs." : "Evident delivered what was written and is reporting the run as finished.") : `OpenCode ended this turn after a tool call (finish="${finish ?? "(unknown)"}") ` + (outcome === "without-answer" ? "without writing an answer, and confirmed it is no longer working on this conversation. Evident is reporting the run as failed so the failure and its reason are visible in failed runs." : "after writing only part of an answer, and confirmed it is no longer working on this conversation. Evident delivered what was written and is reporting the run as finished.");
|
|
7750
8132
|
if (replyShape === null) return base;
|
|
7751
8133
|
return `${base} ${replyShape.lastToolName === null ? "No tool call was recorded" : `The last tool call was "${replyShape.lastToolName}" with status "${replyShape.lastToolStatus ?? "(unknown)"}"`}; the reply's final part types were [${replyShape.partTypes.join(", ")}]${replyShape.droppedPartCount > 0 ? ` (${replyShape.droppedPartCount} earlier part types omitted)` : ""}.`;
|
|
7752
8134
|
}
|
|
@@ -7783,6 +8165,9 @@ var ALIVE_WITHHELD_THRESHOLD_MS = 3 * HEARTBEAT_MS;
|
|
|
7783
8165
|
var ABSOLUTE_MAX_PROCESSING_MS = 6 * 60 * 60 * 1e3;
|
|
7784
8166
|
var B2_ABANDONMENT_MIN_PINNED_MS = 3 * 6e4;
|
|
7785
8167
|
var AMBIGUOUS_FINISH_MAX_PINNED_MS = 3 * 6e4;
|
|
8168
|
+
var MIDFLIGHT_ORPHAN_MIN_PINNED_MS = 3 * 6e4;
|
|
8169
|
+
var MIDFLIGHT_ORPHAN_RECHECK_MS = 6e4;
|
|
8170
|
+
var MIDFLIGHT_ORPHAN_MIN_NOT_ONGOING_STREAK = 2;
|
|
7786
8171
|
var B2_ABANDONMENT_RECHECK_MS = HEARTBEAT_MS;
|
|
7787
8172
|
var POLL_MISS_GRACE_MS = HEARTBEAT_MS;
|
|
7788
8173
|
var WATCHER_STALL_MS = 3 * POLL_MISS_GRACE_MS;
|
|
@@ -8146,6 +8531,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8146
8531
|
* re-downloaded and re-written every ~2s until the server expires it.
|
|
8147
8532
|
*/
|
|
8148
8533
|
fileAckFailures = /* @__PURE__ */ new Map();
|
|
8534
|
+
vaultFileOutcomes = /* @__PURE__ */ new Map();
|
|
8149
8535
|
/**
|
|
8150
8536
|
* Monotonic count of files this runner has pulled and written (#559). Only
|
|
8151
8537
|
* ever increases, so `run.ts` detects work by comparing it against the value
|
|
@@ -8303,10 +8689,21 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8303
8689
|
ackFailures: this.fileAckFailures,
|
|
8304
8690
|
log: this.log
|
|
8305
8691
|
});
|
|
8306
|
-
|
|
8692
|
+
const vaultResult = await syncVaultFiles({
|
|
8693
|
+
agentId: this.agentId,
|
|
8694
|
+
apiUrl: this.apiUrl,
|
|
8695
|
+
getAuthHeader: this.getAuthHeader,
|
|
8696
|
+
fetchImpl: this.fetchImpl,
|
|
8697
|
+
allowedDirectories: this.fileSyncDirectories,
|
|
8698
|
+
homeDir: this.homeDir,
|
|
8699
|
+
outcomes: this.vaultFileOutcomes,
|
|
8700
|
+
log: this.log
|
|
8701
|
+
});
|
|
8702
|
+
const applied = result.applied + vaultResult.applied;
|
|
8703
|
+
this.appliedFileCount += applied;
|
|
8307
8704
|
if (result.claudeCredentialApplied) this.claudeCredentialApplyCount += 1;
|
|
8308
8705
|
if (result.opencodeAuthApplied) this.opencodeAuthApplyCount += 1;
|
|
8309
|
-
return
|
|
8706
|
+
return applied;
|
|
8310
8707
|
} catch (err) {
|
|
8311
8708
|
this.log({
|
|
8312
8709
|
level: "error",
|
|
@@ -8718,6 +9115,47 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8718
9115
|
agent: message.opencode_agent ?? void 0,
|
|
8719
9116
|
model: message.opencode_model ?? void 0
|
|
8720
9117
|
};
|
|
9118
|
+
const pinnedProviderLoaded = await isPinnedProviderLoaded(
|
|
9119
|
+
this.port,
|
|
9120
|
+
options.model,
|
|
9121
|
+
this.openCodeClient
|
|
9122
|
+
);
|
|
9123
|
+
if (pinnedProviderLoaded === false) {
|
|
9124
|
+
const dispatchedModel = message.opencode_model;
|
|
9125
|
+
const separator = dispatchedModel?.indexOf("/") ?? -1;
|
|
9126
|
+
const hasProviderAndModel = dispatchedModel !== null && separator > 0 && separator < dispatchedModel.length - 1;
|
|
9127
|
+
const providerId = hasProviderAndModel ? dispatchedModel.slice(0, separator) : null;
|
|
9128
|
+
const modelId = hasProviderAndModel ? dispatchedModel.slice(separator + 1) : null;
|
|
9129
|
+
const errorMessage3 = `Provider namespace not loaded on this runner: ${message.opencode_model}. Refused before dispatch; no turn was started.`;
|
|
9130
|
+
this.log({
|
|
9131
|
+
level: "error",
|
|
9132
|
+
message: `Refusing message ${message.id.slice(0, 8)} in conversation ${conv.id.slice(0, 8)}: provider namespace ${providerId ?? "(unknown)"} is not loaded; no turn was started`,
|
|
9133
|
+
conversation_id: conv.id,
|
|
9134
|
+
message_id: message.id
|
|
9135
|
+
});
|
|
9136
|
+
try {
|
|
9137
|
+
await this.markFailed(conv.id, message.id, sessionId, errorMessage3, void 0, {
|
|
9138
|
+
classification: {
|
|
9139
|
+
kind: "model_auth",
|
|
9140
|
+
providerId,
|
|
9141
|
+
modelId,
|
|
9142
|
+
reason: "missing"
|
|
9143
|
+
},
|
|
9144
|
+
errorShape: { name: null, statusCode: null }
|
|
9145
|
+
});
|
|
9146
|
+
this.signalDispatchNotStarted(conv, message, "model_provider_absent");
|
|
9147
|
+
} catch (markErr) {
|
|
9148
|
+
this.log({
|
|
9149
|
+
level: "warn",
|
|
9150
|
+
message: `markFailed PATCH for refused message ${message.id.slice(0, 8)} (conversation ${conv.id.slice(0, 8)}) failed: ${markErr instanceof Error ? markErr.message : String(markErr)}`,
|
|
9151
|
+
conversation_id: conv.id,
|
|
9152
|
+
message_id: message.id
|
|
9153
|
+
});
|
|
9154
|
+
this.signalDispatchNotStarted(conv, message, "failure_unreported");
|
|
9155
|
+
}
|
|
9156
|
+
await this.repairProviderRegistry(conv.id, message.id);
|
|
9157
|
+
break;
|
|
9158
|
+
}
|
|
8721
9159
|
let opencodeMessageId;
|
|
8722
9160
|
let preDispatchUserIds = null;
|
|
8723
9161
|
try {
|
|
@@ -9842,6 +10280,12 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9842
10280
|
b2AbandonedWithoutAnswer: false,
|
|
9843
10281
|
ambiguousPinnedSinceMs: 0,
|
|
9844
10282
|
ambiguousResolved: false,
|
|
10283
|
+
midFlightPinnedSinceMs: 0,
|
|
10284
|
+
midFlightNotOngoingStreak: 0,
|
|
10285
|
+
midFlightLastStatusCheckMs: 0,
|
|
10286
|
+
midFlightOrphanSignalled: false,
|
|
10287
|
+
midFlightOrphanWithoutAnswer: false,
|
|
10288
|
+
midFlightHeldLogged: false,
|
|
9845
10289
|
sessionStoreFailurePending: false,
|
|
9846
10290
|
neverStartedResolved: false
|
|
9847
10291
|
});
|
|
@@ -9981,6 +10425,12 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9981
10425
|
b2AbandonedWithoutAnswer: false,
|
|
9982
10426
|
ambiguousPinnedSinceMs: 0,
|
|
9983
10427
|
ambiguousResolved: false,
|
|
10428
|
+
midFlightPinnedSinceMs: 0,
|
|
10429
|
+
midFlightNotOngoingStreak: 0,
|
|
10430
|
+
midFlightLastStatusCheckMs: 0,
|
|
10431
|
+
midFlightOrphanSignalled: false,
|
|
10432
|
+
midFlightOrphanWithoutAnswer: false,
|
|
10433
|
+
midFlightHeldLogged: false,
|
|
9984
10434
|
sessionStoreFailurePending: false,
|
|
9985
10435
|
neverStartedResolved: false
|
|
9986
10436
|
});
|
|
@@ -10752,7 +11202,11 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10752
11202
|
watcher,
|
|
10753
11203
|
inFlight,
|
|
10754
11204
|
messages,
|
|
10755
|
-
|
|
11205
|
+
composeAbandonmentNote(
|
|
11206
|
+
finish,
|
|
11207
|
+
describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
|
|
11208
|
+
"without-answer"
|
|
11209
|
+
)
|
|
10756
11210
|
);
|
|
10757
11211
|
} else {
|
|
10758
11212
|
await this.settleMessageDone(
|
|
@@ -10811,7 +11265,11 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10811
11265
|
watcher,
|
|
10812
11266
|
inFlight,
|
|
10813
11267
|
messages,
|
|
10814
|
-
|
|
11268
|
+
composeAbandonmentNote(
|
|
11269
|
+
finish,
|
|
11270
|
+
describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
|
|
11271
|
+
"without-answer"
|
|
11272
|
+
)
|
|
10815
11273
|
);
|
|
10816
11274
|
return;
|
|
10817
11275
|
}
|
|
@@ -10905,6 +11363,106 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10905
11363
|
return;
|
|
10906
11364
|
}
|
|
10907
11365
|
}
|
|
11366
|
+
const midFlightPinnedNow = activelyRunning && isMidFlightPinnedRunning(messages, inFlight.opencodeMessageId);
|
|
11367
|
+
if (!midFlightPinnedNow) {
|
|
11368
|
+
if (snapshotReadable) {
|
|
11369
|
+
inFlight.midFlightPinnedSinceMs = 0;
|
|
11370
|
+
inFlight.midFlightNotOngoingStreak = 0;
|
|
11371
|
+
inFlight.midFlightLastStatusCheckMs = 0;
|
|
11372
|
+
inFlight.midFlightOrphanSignalled = false;
|
|
11373
|
+
inFlight.midFlightOrphanWithoutAnswer = false;
|
|
11374
|
+
inFlight.midFlightHeldLogged = false;
|
|
11375
|
+
}
|
|
11376
|
+
} else {
|
|
11377
|
+
if (inFlight.midFlightOrphanSignalled) {
|
|
11378
|
+
const note = composeAbandonmentNote(
|
|
11379
|
+
void 0,
|
|
11380
|
+
describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
|
|
11381
|
+
inFlight.midFlightOrphanWithoutAnswer ? "midflight-without-answer" : "midflight-with-answer"
|
|
11382
|
+
);
|
|
11383
|
+
if (inFlight.midFlightOrphanWithoutAnswer) {
|
|
11384
|
+
await this.settleMessageAbandonedWithoutAnswer(
|
|
11385
|
+
sessionId,
|
|
11386
|
+
watcher,
|
|
11387
|
+
inFlight,
|
|
11388
|
+
messages,
|
|
11389
|
+
note
|
|
11390
|
+
);
|
|
11391
|
+
} else {
|
|
11392
|
+
await this.settleMessageDone(sessionId, watcher, inFlight, messages, note);
|
|
11393
|
+
}
|
|
11394
|
+
return;
|
|
11395
|
+
}
|
|
11396
|
+
if (inFlight.midFlightPinnedSinceMs === 0) {
|
|
11397
|
+
inFlight.midFlightPinnedSinceMs = this.now();
|
|
11398
|
+
}
|
|
11399
|
+
const pinnedForMs = this.now() - inFlight.midFlightPinnedSinceMs;
|
|
11400
|
+
if (pinnedForMs >= MIDFLIGHT_ORPHAN_MIN_PINNED_MS && this.now() - inFlight.midFlightLastStatusCheckMs >= MIDFLIGHT_ORPHAN_RECHECK_MS) {
|
|
11401
|
+
inFlight.midFlightLastStatusCheckMs = this.now();
|
|
11402
|
+
const rootOngoing = await this.isSessionOngoing(sessionId);
|
|
11403
|
+
inFlight.midFlightNotOngoingStreak = rootOngoing === false ? inFlight.midFlightNotOngoingStreak + 1 : 0;
|
|
11404
|
+
if (isMidFlightOrphanConfirmed({
|
|
11405
|
+
pinnedForMs,
|
|
11406
|
+
minPinnedMs: MIDFLIGHT_ORPHAN_MIN_PINNED_MS,
|
|
11407
|
+
notOngoingStreak: inFlight.midFlightNotOngoingStreak,
|
|
11408
|
+
minNotOngoingStreak: MIDFLIGHT_ORPHAN_MIN_NOT_ONGOING_STREAK
|
|
11409
|
+
})) {
|
|
11410
|
+
if (this.sessionStoreHealth?.isUnwritable()) {
|
|
11411
|
+
inFlight.sessionStoreFailurePending = true;
|
|
11412
|
+
void this.postSignal(conv.id, id, "midflight_orphan_resolved", {
|
|
11413
|
+
watched_for_ms: pinnedForMs,
|
|
11414
|
+
...rootOngoing != null ? { root_ongoing: rootOngoing } : {},
|
|
11415
|
+
opencode_message_id: inFlight.opencodeMessageId,
|
|
11416
|
+
run_state: "failed"
|
|
11417
|
+
});
|
|
11418
|
+
await this.settleMessageSessionStoreUnwritable(sessionId, watcher, inFlight);
|
|
11419
|
+
return;
|
|
11420
|
+
}
|
|
11421
|
+
inFlight.midFlightOrphanSignalled = true;
|
|
11422
|
+
const noteOutcome = extractReplyTextForMessage(messages, inFlight.opencodeMessageId) === null ? "midflight-without-answer" : "midflight-with-answer";
|
|
11423
|
+
inFlight.midFlightOrphanWithoutAnswer = noteOutcome === "midflight-without-answer";
|
|
11424
|
+
void this.postSignal(conv.id, id, "midflight_orphan_resolved", {
|
|
11425
|
+
watched_for_ms: pinnedForMs,
|
|
11426
|
+
...rootOngoing != null ? { root_ongoing: rootOngoing } : {},
|
|
11427
|
+
opencode_message_id: inFlight.opencodeMessageId,
|
|
11428
|
+
...inFlight.midFlightOrphanWithoutAnswer ? { run_state: "failed" } : {}
|
|
11429
|
+
});
|
|
11430
|
+
const note = composeAbandonmentNote(
|
|
11431
|
+
void 0,
|
|
11432
|
+
describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
|
|
11433
|
+
noteOutcome
|
|
11434
|
+
);
|
|
11435
|
+
this.log({
|
|
11436
|
+
level: "warn",
|
|
11437
|
+
message: `Message ${id.slice(0, 8)} was pinned mid-flight for ${Math.round(pinnedForMs / 1e3)}s with root_ongoing=${rootOngoing} \u2014 resolving the orphaned turn`,
|
|
11438
|
+
conversation_id: conv.id,
|
|
11439
|
+
message_id: id
|
|
11440
|
+
});
|
|
11441
|
+
if (inFlight.midFlightOrphanWithoutAnswer) {
|
|
11442
|
+
await this.settleMessageAbandonedWithoutAnswer(
|
|
11443
|
+
sessionId,
|
|
11444
|
+
watcher,
|
|
11445
|
+
inFlight,
|
|
11446
|
+
messages,
|
|
11447
|
+
note
|
|
11448
|
+
);
|
|
11449
|
+
} else {
|
|
11450
|
+
await this.settleMessageDone(sessionId, watcher, inFlight, messages, note);
|
|
11451
|
+
}
|
|
11452
|
+
return;
|
|
11453
|
+
}
|
|
11454
|
+
if (rootOngoing === true && !inFlight.midFlightHeldLogged) {
|
|
11455
|
+
inFlight.midFlightHeldLogged = true;
|
|
11456
|
+
const replyShape = describeAbandonedReplyShape(messages, inFlight.opencodeMessageId);
|
|
11457
|
+
this.log({
|
|
11458
|
+
level: "warn",
|
|
11459
|
+
message: `Message ${id.slice(0, 8)} was pinned mid-flight for ${Math.round(pinnedForMs / 1e3)}s with root_ongoing=true \u2014 holding; last tool ${replyShape?.lastToolName ?? "(unknown)"} status ${replyShape?.lastToolStatus ?? "(unknown)"}`,
|
|
11460
|
+
conversation_id: conv.id,
|
|
11461
|
+
message_id: id
|
|
11462
|
+
});
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11465
|
+
}
|
|
10908
11466
|
if (watcher.inFlight.has(inFlight.evidentMessageId) && activelyRunning && this.now() - inFlight.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS) {
|
|
10909
11467
|
await this.settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick);
|
|
10910
11468
|
return;
|
|
@@ -11104,13 +11662,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
11104
11662
|
this.removeInFlight(watcher, inFlight.evidentMessageId);
|
|
11105
11663
|
}
|
|
11106
11664
|
/** Report a confirmed b2 abandonment that produced no answer text. */
|
|
11107
|
-
async settleMessageAbandonedWithoutAnswer(sessionId, watcher, inFlight, messages,
|
|
11665
|
+
async settleMessageAbandonedWithoutAnswer(sessionId, watcher, inFlight, messages, error2) {
|
|
11108
11666
|
const conv = watcher.conv;
|
|
11109
|
-
const error2 = composeAbandonmentNote(
|
|
11110
|
-
finish,
|
|
11111
|
-
describeAbandonedReplyShape(messages, inFlight.opencodeMessageId),
|
|
11112
|
-
"without-answer"
|
|
11113
|
-
);
|
|
11114
11667
|
this.anchorDeliveryDeadline(inFlight);
|
|
11115
11668
|
this.log({
|
|
11116
11669
|
level: "warn",
|
|
@@ -13622,7 +14175,7 @@ Port ${port} is already in use.`));
|
|
|
13622
14175
|
}
|
|
13623
14176
|
|
|
13624
14177
|
// src/lib/runner-credentials.ts
|
|
13625
|
-
import { chmodSync as chmodSync2, writeFileSync as
|
|
14178
|
+
import { chmodSync as chmodSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13626
14179
|
import { spawn as spawn5 } from "node:child_process";
|
|
13627
14180
|
var RUNNER_SECRET_FETCH_TIMEOUT_MS = 6e4;
|
|
13628
14181
|
var CREDENTIAL_RESTORE_TIMEOUT_MS = 6e4;
|
|
@@ -13865,8 +14418,8 @@ async function configureGitHubAccess({ env, log: log3 }) {
|
|
|
13865
14418
|
}
|
|
13866
14419
|
try {
|
|
13867
14420
|
env.GIT_CONFIG_GLOBAL = GIT_CONFIG_GLOBAL;
|
|
13868
|
-
|
|
13869
|
-
|
|
14421
|
+
writeFileSync5(GIT_CONFIG_GLOBAL, "");
|
|
14422
|
+
writeFileSync5(GIT_CREDENTIAL_HELPER, GIT_CREDENTIAL_HELPER_CONTENT, { mode: 448 });
|
|
13870
14423
|
chmodSync2(GIT_CREDENTIAL_HELPER, 448);
|
|
13871
14424
|
const config = [
|
|
13872
14425
|
["user.name", env.GIT_USER_NAME ?? "evident-bot"],
|
|
@@ -13899,7 +14452,7 @@ async function configureGitHubAccess({ env, log: log3 }) {
|
|
|
13899
14452
|
// src/lib/opencode/config-overlay.ts
|
|
13900
14453
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
13901
14454
|
import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
|
|
13902
|
-
import { isAbsolute as isAbsolute3, join as
|
|
14455
|
+
import { isAbsolute as isAbsolute3, join as join12, resolve as resolve4 } from "node:path";
|
|
13903
14456
|
function parseConfigContent(content) {
|
|
13904
14457
|
if (!content?.trim()) return {};
|
|
13905
14458
|
const parsed = JSON.parse(content);
|
|
@@ -13916,22 +14469,6 @@ function mergeManagedOpenCodeConfigContent(content, managedProviders) {
|
|
|
13916
14469
|
}
|
|
13917
14470
|
return JSON.stringify({ ...config, provider: { ...provider, ...managedProviders } });
|
|
13918
14471
|
}
|
|
13919
|
-
function removeManagedOpenCodeConfigContent(content) {
|
|
13920
|
-
if (!content?.trim()) return content;
|
|
13921
|
-
const config = parseConfigContent(content);
|
|
13922
|
-
if (typeof config.provider !== "object" || config.provider === null || Array.isArray(config.provider)) {
|
|
13923
|
-
return content;
|
|
13924
|
-
}
|
|
13925
|
-
const provider = { ...config.provider };
|
|
13926
|
-
let changed = false;
|
|
13927
|
-
for (const namespace of CREDENTIAL_PROXY_NATIVE_NAMESPACES) {
|
|
13928
|
-
if (namespace in provider) {
|
|
13929
|
-
delete provider[namespace];
|
|
13930
|
-
changed = true;
|
|
13931
|
-
}
|
|
13932
|
-
}
|
|
13933
|
-
return changed ? JSON.stringify({ ...config, provider }) : content;
|
|
13934
|
-
}
|
|
13935
14472
|
function isFile(filePath) {
|
|
13936
14473
|
return existsSync2(filePath) && statSync6(filePath).isFile();
|
|
13937
14474
|
}
|
|
@@ -13945,7 +14482,7 @@ function applyRunnerOpenCodeConfig({
|
|
|
13945
14482
|
return;
|
|
13946
14483
|
}
|
|
13947
14484
|
const source = isAbsolute3(overlayPath) ? overlayPath : resolve4(cwd, overlayPath);
|
|
13948
|
-
const target = isFile(
|
|
14485
|
+
const target = isFile(join12(cwd, "opencode.jsonc")) ? "opencode.jsonc" : "opencode.json";
|
|
13949
14486
|
if (!isFile(source)) {
|
|
13950
14487
|
log3(
|
|
13951
14488
|
`RUNNER-OPENCODE-CONFIG-MISSING: ${source} is not a file; headless turns will wedge on the first external-directory permission prompt (#563)`,
|
|
@@ -13953,7 +14490,7 @@ function applyRunnerOpenCodeConfig({
|
|
|
13953
14490
|
);
|
|
13954
14491
|
return;
|
|
13955
14492
|
}
|
|
13956
|
-
copyFileSync(source,
|
|
14493
|
+
copyFileSync(source, join12(cwd, target));
|
|
13957
14494
|
try {
|
|
13958
14495
|
execFileSync2("git", ["-C", cwd, "update-index", "--skip-worktree", target], {
|
|
13959
14496
|
stdio: "ignore"
|
|
@@ -13962,13 +14499,13 @@ function applyRunnerOpenCodeConfig({
|
|
|
13962
14499
|
const detail = error2 instanceof Error ? error2.message : String(error2);
|
|
13963
14500
|
log3(`could not mark ${target} skip-worktree: ${detail}; it may show as a local change`, "warn");
|
|
13964
14501
|
}
|
|
13965
|
-
log3(`Applied runner OpenCode config ${source} to ${
|
|
14502
|
+
log3(`Applied runner OpenCode config ${source} to ${join12(cwd, target)}`);
|
|
13966
14503
|
}
|
|
13967
14504
|
|
|
13968
14505
|
// src/lib/opencode/runtime-provenance.ts
|
|
13969
|
-
import { readFileSync as
|
|
14506
|
+
import { readFileSync as readFileSync8, readdirSync } from "node:fs";
|
|
13970
14507
|
import { homedir as homedir7 } from "node:os";
|
|
13971
|
-
import { join as
|
|
14508
|
+
import { join as join13 } from "node:path";
|
|
13972
14509
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
|
|
13973
14510
|
var REGISTRY_SPEC = /^[a-z0-9.*+^~<>=|-]+$/i;
|
|
13974
14511
|
function errorKind(error2) {
|
|
@@ -14036,7 +14573,7 @@ function parseJsonc(value) {
|
|
|
14036
14573
|
return JSON.parse(output);
|
|
14037
14574
|
}
|
|
14038
14575
|
function openCodeCacheRoot(env = process.env, homeDir = homedir7()) {
|
|
14039
|
-
return
|
|
14576
|
+
return join13(env.XDG_CACHE_HOME ?? join13(homeDir, ".cache"), "opencode");
|
|
14040
14577
|
}
|
|
14041
14578
|
function readConfiguredPluginSpecs({
|
|
14042
14579
|
cwd = process.cwd(),
|
|
@@ -14045,10 +14582,10 @@ function readConfiguredPluginSpecs({
|
|
|
14045
14582
|
let config;
|
|
14046
14583
|
try {
|
|
14047
14584
|
try {
|
|
14048
|
-
config = parseJsonc(
|
|
14585
|
+
config = parseJsonc(readFileSync8(join13(cwd, "opencode.jsonc"), "utf8"));
|
|
14049
14586
|
} catch (error2) {
|
|
14050
14587
|
if (!("code" in Object(error2)) || Object(error2).code !== "ENOENT") throw error2;
|
|
14051
|
-
config = parseJsonc(
|
|
14588
|
+
config = parseJsonc(readFileSync8(join13(cwd, "opencode.json"), "utf8"));
|
|
14052
14589
|
}
|
|
14053
14590
|
} catch (error2) {
|
|
14054
14591
|
log3(`Could not read OpenCode plugin configuration: ${errorKind(error2)}`);
|
|
@@ -14076,7 +14613,7 @@ function resolveInstalledPluginVersions({
|
|
|
14076
14613
|
homeDir = homedir7(),
|
|
14077
14614
|
log: log3
|
|
14078
14615
|
}) {
|
|
14079
|
-
const packagesRoot =
|
|
14616
|
+
const packagesRoot = join13(cacheRoot ?? openCodeCacheRoot(env, homeDir), "packages");
|
|
14080
14617
|
let directories;
|
|
14081
14618
|
try {
|
|
14082
14619
|
directories = readdirSync(packagesRoot, { withFileTypes: true });
|
|
@@ -14097,8 +14634,8 @@ function resolveInstalledPluginVersions({
|
|
|
14097
14634
|
let resolvedVersion = null;
|
|
14098
14635
|
try {
|
|
14099
14636
|
const packageJson = JSON.parse(
|
|
14100
|
-
|
|
14101
|
-
|
|
14637
|
+
readFileSync8(
|
|
14638
|
+
join13(packagesRoot, directory.name, "node_modules", parsed.name, "package.json"),
|
|
14102
14639
|
"utf8"
|
|
14103
14640
|
)
|
|
14104
14641
|
);
|
|
@@ -14119,7 +14656,7 @@ function resolveInstalledPluginVersions({
|
|
|
14119
14656
|
}
|
|
14120
14657
|
|
|
14121
14658
|
// src/lib/credential-sync.ts
|
|
14122
|
-
import { renameSync, writeFileSync as
|
|
14659
|
+
import { renameSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
14123
14660
|
var CREDENTIAL_SYNC_TICK_TIMEOUT_MS = 3e4;
|
|
14124
14661
|
var CREDENTIAL_FLUSH_DEADLINE_MS = 8e3;
|
|
14125
14662
|
var CREDENTIAL_FLUSH_ABORT_GRACE_MS = 500;
|
|
@@ -14153,7 +14690,7 @@ function writeMarker(markerPath, outcomes, log3) {
|
|
|
14153
14690
|
`;
|
|
14154
14691
|
const temporaryPath = `${markerPath}.tmp`;
|
|
14155
14692
|
try {
|
|
14156
|
-
|
|
14693
|
+
writeFileSync6(temporaryPath, body, { mode: 384 });
|
|
14157
14694
|
renameSync(temporaryPath, markerPath);
|
|
14158
14695
|
} catch (error2) {
|
|
14159
14696
|
log3(`CREDENTIAL-FLUSH-MARKER-UNWRITABLE: ${markerPath}: ${errorMessage2(error2)}`, "warn");
|
|
@@ -14393,23 +14930,23 @@ async function responseJson(response) {
|
|
|
14393
14930
|
function readSetup(value) {
|
|
14394
14931
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
14395
14932
|
const setup = value;
|
|
14396
|
-
if (typeof setup.runner_id !== "string" || typeof setup.generation !== "string" || typeof setup.status !== "string" || setup.token_id !== null && typeof setup.token_id !== "string") {
|
|
14933
|
+
if (typeof setup.runner_id !== "string" || typeof setup.generation !== "string" || typeof setup.status !== "string" || setup.token_id !== null && typeof setup.token_id !== "string" || typeof setup.acknowledged_at !== "string" && setup.acknowledged_at !== null) {
|
|
14397
14934
|
return null;
|
|
14398
14935
|
}
|
|
14399
14936
|
return setup;
|
|
14400
14937
|
}
|
|
14401
14938
|
function clearLocalSetup(options) {
|
|
14402
14939
|
const env = options.env ?? process.env;
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
|
|
14406
|
-
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
}
|
|
14940
|
+
const store = options.store ?? defaultStore;
|
|
14941
|
+
const stored = store.get(options.agentId);
|
|
14942
|
+
store.clear(options.agentId);
|
|
14943
|
+
const token = env.EVIDENT_PROXY_TOKEN;
|
|
14944
|
+
if (token !== void 0 && token === stored?.token) {
|
|
14945
|
+
delete env.EVIDENT_PROXY_TOKEN;
|
|
14946
|
+
} else if (token !== void 0) {
|
|
14410
14947
|
options.log(
|
|
14411
|
-
|
|
14412
|
-
"
|
|
14948
|
+
`MANAGED-INFERENCE-CLEANUP-PRESERVED-EXTERNAL-TOKEN: runner=${options.agentId}`,
|
|
14949
|
+
"debug"
|
|
14413
14950
|
);
|
|
14414
14951
|
}
|
|
14415
14952
|
}
|
|
@@ -14435,6 +14972,25 @@ async function discoverManagedInferenceSetup(options) {
|
|
|
14435
14972
|
const setup = readSetup(body?.setup);
|
|
14436
14973
|
return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed setup discovery returned an invalid response" };
|
|
14437
14974
|
}
|
|
14975
|
+
async function enrollManagedInferenceSetup(options) {
|
|
14976
|
+
const response = await post(options, "enroll", {});
|
|
14977
|
+
if (!response) {
|
|
14978
|
+
return { kind: "failed", reason: safeFailure("Managed credential enrollment") };
|
|
14979
|
+
}
|
|
14980
|
+
if (response.status === 404) {
|
|
14981
|
+
return { kind: "none" };
|
|
14982
|
+
}
|
|
14983
|
+
const body = await responseJson(response);
|
|
14984
|
+
if (!response.ok) {
|
|
14985
|
+
const serverError = body?.error;
|
|
14986
|
+
return {
|
|
14987
|
+
kind: "failed",
|
|
14988
|
+
reason: typeof serverError === "string" && serverError.trim() ? redactDiagnosticText(serverError).slice(0, 500) : safeFailure("Managed credential enrollment", response.status)
|
|
14989
|
+
};
|
|
14990
|
+
}
|
|
14991
|
+
const setup = readSetup(body?.setup);
|
|
14992
|
+
return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed credential enrollment returned an invalid response" };
|
|
14993
|
+
}
|
|
14438
14994
|
async function post(options, operation, body) {
|
|
14439
14995
|
const headers = requestHeaders(options);
|
|
14440
14996
|
try {
|
|
@@ -14449,31 +15005,37 @@ async function post(options, operation, body) {
|
|
|
14449
15005
|
}
|
|
14450
15006
|
}
|
|
14451
15007
|
function applyCredential(options, credential) {
|
|
14452
|
-
const { config, unsupportedProviders } = buildCredentialProxyNativeConfig({
|
|
14453
|
-
inferenceBaseUrl: credential.inferenceBaseUrl,
|
|
14454
|
-
modelDescriptors: credential.modelDescriptors
|
|
14455
|
-
});
|
|
14456
|
-
if (unsupportedProviders.length > 0 || Object.keys(config.provider).length === 0) {
|
|
14457
|
-
return "Managed setup contains unsupported provider descriptors";
|
|
14458
|
-
}
|
|
14459
15008
|
const env = options.env ?? process.env;
|
|
14460
15009
|
let merged;
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
|
|
15010
|
+
if (options.autoConfigureCredentialsProxy) {
|
|
15011
|
+
const { config, unsupportedProviders } = buildCredentialProxyNativeConfig({
|
|
15012
|
+
inferenceBaseUrl: credential.inferenceBaseUrl,
|
|
15013
|
+
modelDescriptors: credential.modelDescriptors
|
|
15014
|
+
});
|
|
15015
|
+
if (unsupportedProviders.length > 0 || Object.keys(config.provider).length === 0) {
|
|
15016
|
+
return "Managed setup contains unsupported provider descriptors";
|
|
15017
|
+
}
|
|
15018
|
+
try {
|
|
15019
|
+
merged = mergeManagedOpenCodeConfigContent(env.OPENCODE_CONFIG_CONTENT, config.provider);
|
|
15020
|
+
} catch (error2) {
|
|
15021
|
+
return "OPENCODE_CONFIG_CONTENT is malformed; managed providers were not installed";
|
|
15022
|
+
}
|
|
14465
15023
|
}
|
|
14466
15024
|
try {
|
|
14467
15025
|
(options.store ?? defaultStore).set(options.agentId, credential);
|
|
14468
15026
|
} catch (error2) {
|
|
15027
|
+
const detail = (error2 instanceof Error ? error2.message : String(error2)).replaceAll(
|
|
15028
|
+
credential.token,
|
|
15029
|
+
"[redacted]"
|
|
15030
|
+
);
|
|
14469
15031
|
options.log(
|
|
14470
|
-
`MANAGED-INFERENCE-STORE-FAILED: runner=${options.agentId} generation=${credential.generation}`,
|
|
15032
|
+
`MANAGED-INFERENCE-STORE-FAILED: runner=${options.agentId} generation=${credential.generation} detail=${redactDiagnosticText(detail)}`,
|
|
14471
15033
|
"warn"
|
|
14472
15034
|
);
|
|
14473
15035
|
return "Managed credentials could not be written to the protected store";
|
|
14474
15036
|
}
|
|
14475
15037
|
env.EVIDENT_PROXY_TOKEN = credential.token;
|
|
14476
|
-
env.OPENCODE_CONFIG_CONTENT = merged;
|
|
15038
|
+
if (merged !== void 0) env.OPENCODE_CONFIG_CONTENT = merged;
|
|
14477
15039
|
return null;
|
|
14478
15040
|
}
|
|
14479
15041
|
async function reportManagedInferenceApplyFailure(options, failure) {
|
|
@@ -14524,7 +15086,7 @@ async function prepareManagedInferenceSetup(options, discovery, health) {
|
|
|
14524
15086
|
}
|
|
14525
15087
|
return reaffirmStored(options, stored);
|
|
14526
15088
|
}
|
|
14527
|
-
if (discovery.setup.
|
|
15089
|
+
if (discovery.setup.acknowledged_at !== null && stored?.generation === discovery.setup.generation && stored.tokenId === discovery.setup.token_id) {
|
|
14528
15090
|
return reaffirmStored(options, stored);
|
|
14529
15091
|
}
|
|
14530
15092
|
const response = await post(options, "bootstrap", { generation: discovery.setup.generation });
|
|
@@ -14562,7 +15124,7 @@ async function acknowledgeManagedInferenceSetup(options, preparation) {
|
|
|
14562
15124
|
const response = await post(options, "acknowledge", {
|
|
14563
15125
|
generation: preparation.generation,
|
|
14564
15126
|
token_id: preparation.tokenId,
|
|
14565
|
-
config_validated: true
|
|
15127
|
+
config_validated: options.autoConfigureCredentialsProxy === true
|
|
14566
15128
|
});
|
|
14567
15129
|
if (!response?.ok) {
|
|
14568
15130
|
throw new Error(
|
|
@@ -14617,7 +15179,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
14617
15179
|
if (trimmed === "") {
|
|
14618
15180
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
14619
15181
|
}
|
|
14620
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
15182
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join14(homeDir, trimmed.slice(2)) : trimmed;
|
|
14621
15183
|
if (!isAbsolute4(expanded)) {
|
|
14622
15184
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
14623
15185
|
}
|
|
@@ -15273,7 +15835,7 @@ function shouldWarnForReclaimSkip(reason) {
|
|
|
15273
15835
|
return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
|
|
15274
15836
|
}
|
|
15275
15837
|
function sessionDbPath() {
|
|
15276
|
-
return
|
|
15838
|
+
return join14(homedir8(), ".local", "share", "opencode", "opencode.db");
|
|
15277
15839
|
}
|
|
15278
15840
|
function logSessionCleanupActivity(state, phase, entry) {
|
|
15279
15841
|
logActivity(state, {
|
|
@@ -15282,6 +15844,47 @@ function logSessionCleanupActivity(state, phase, entry) {
|
|
|
15282
15844
|
metadata: { ...entry.metadata, phase }
|
|
15283
15845
|
});
|
|
15284
15846
|
}
|
|
15847
|
+
async function drainSessionDeletionReports(state) {
|
|
15848
|
+
const path = sessionDeletionReportPath(homedir8(), process.env);
|
|
15849
|
+
let delivered = 0;
|
|
15850
|
+
let failure = null;
|
|
15851
|
+
try {
|
|
15852
|
+
const spool = readSessionDeletionRecords(path);
|
|
15853
|
+
if (spool.readError !== null) {
|
|
15854
|
+
failure = `could not read spool: ${spool.readError}`;
|
|
15855
|
+
} else if (spool.records.length === 0) {
|
|
15856
|
+
return;
|
|
15857
|
+
} else if (!state.agentId || !state.authHeader) {
|
|
15858
|
+
failure = "runner authentication is not available";
|
|
15859
|
+
} else {
|
|
15860
|
+
for (let offset = 0; offset < spool.records.length; offset += 200) {
|
|
15861
|
+
const batch = spool.records.slice(offset, offset + 200);
|
|
15862
|
+
const result = await reportSessionDeletions(state.agentId, state.authHeader, batch);
|
|
15863
|
+
if (!result.ok) {
|
|
15864
|
+
failure = result.error ?? "unknown API error";
|
|
15865
|
+
break;
|
|
15866
|
+
}
|
|
15867
|
+
removeDeliveredSessionDeletionRecords(path, batch);
|
|
15868
|
+
delivered += batch.length;
|
|
15869
|
+
}
|
|
15870
|
+
}
|
|
15871
|
+
} catch (error2) {
|
|
15872
|
+
failure = error2 instanceof Error ? error2.message : String(error2);
|
|
15873
|
+
}
|
|
15874
|
+
const pending = readSessionDeletionRecords(path);
|
|
15875
|
+
const pendingCount = pending.readError === null ? pending.records.length : null;
|
|
15876
|
+
const message = `Session deletion reports: delivered ${delivered}, ${pendingCount === null ? "pending count unavailable" : `${pendingCount} pending`}` + (failure === null ? "." : `; delivery failed: ${failure}.`);
|
|
15877
|
+
logActivity(state, {
|
|
15878
|
+
type: "info",
|
|
15879
|
+
level: failure === null ? "info" : "warn",
|
|
15880
|
+
message,
|
|
15881
|
+
metadata: {
|
|
15882
|
+
outcome: failure === null ? "session-deletion-reports-drained" : "session-deletion-reports-pending",
|
|
15883
|
+
delivered,
|
|
15884
|
+
pending: pendingCount
|
|
15885
|
+
}
|
|
15886
|
+
});
|
|
15887
|
+
}
|
|
15285
15888
|
function logSessionDbProvenanceMismatch(state, provenance, currentVersion, preBootMigrationCount) {
|
|
15286
15889
|
const record = {
|
|
15287
15890
|
v: 1,
|
|
@@ -15339,17 +15942,22 @@ async function runSweep(state, driver, config) {
|
|
|
15339
15942
|
let failedThrew = 0;
|
|
15340
15943
|
let skippedNewlyActive = 0;
|
|
15341
15944
|
let remaining = 0;
|
|
15945
|
+
const deletionRecords = [];
|
|
15342
15946
|
let protectedNow = driver.protectedSessionIds();
|
|
15343
15947
|
if (sessions !== null) {
|
|
15344
|
-
const
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
protectedIds: protectedNow
|
|
15351
|
-
}
|
|
15948
|
+
const snapshots = sessions.map((session) => ({
|
|
15949
|
+
id: session.id,
|
|
15950
|
+
lastActivityMs: sessionLastActivityMs(session)
|
|
15951
|
+
}));
|
|
15952
|
+
const lastActivityById = new Map(
|
|
15953
|
+
snapshots.map(({ id, lastActivityMs }) => [id, lastActivityMs])
|
|
15352
15954
|
);
|
|
15955
|
+
const toDelete = selectSessionsToDelete(snapshots, {
|
|
15956
|
+
maxAgeMs: config.maxAgeMs,
|
|
15957
|
+
maxCount: config.maxCount,
|
|
15958
|
+
nowMs: Date.now(),
|
|
15959
|
+
protectedIds: protectedNow
|
|
15960
|
+
});
|
|
15353
15961
|
const eligibleToDelete = toDelete.filter(
|
|
15354
15962
|
(id) => (state.sessionDeleteFailures.get(id)?.isolatedFailures ?? 0) < SESSION_CLEANUP_GIVE_UP_AFTER_FAILURES
|
|
15355
15963
|
);
|
|
@@ -15369,6 +15977,17 @@ async function runSweep(state, driver, config) {
|
|
|
15369
15977
|
const outcome = await deleteSession(state.port, id, state.opencodeClient ?? void 0);
|
|
15370
15978
|
if (outcome.ok) {
|
|
15371
15979
|
deleted++;
|
|
15980
|
+
const lastActivityMs = lastActivityById.get(id) ?? null;
|
|
15981
|
+
deletionRecords.push({
|
|
15982
|
+
opencode_session_id: id,
|
|
15983
|
+
deleted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15984
|
+
reason: "auto_cleanup",
|
|
15985
|
+
policy: {
|
|
15986
|
+
max_age_ms: config.maxAgeMs ?? null,
|
|
15987
|
+
max_count: config.maxCount ?? null,
|
|
15988
|
+
last_activity_at: lastActivityMs === null ? null : new Date(lastActivityMs).toISOString()
|
|
15989
|
+
}
|
|
15990
|
+
});
|
|
15372
15991
|
} else if (outcome.status === "threw") {
|
|
15373
15992
|
failed++;
|
|
15374
15993
|
failedThrew++;
|
|
@@ -15424,6 +16043,32 @@ async function runSweep(state, driver, config) {
|
|
|
15424
16043
|
message: `Session cleanup: inspected ${sessions.length}, deleted ${deleted}${failedNote}${skippedNote}${remainingNote} (${mode})`
|
|
15425
16044
|
});
|
|
15426
16045
|
}
|
|
16046
|
+
if (deletionRecords.length > 0) {
|
|
16047
|
+
try {
|
|
16048
|
+
const dropped = appendSessionDeletionRecords(
|
|
16049
|
+
sessionDeletionReportPath(homedir8(), process.env),
|
|
16050
|
+
deletionRecords
|
|
16051
|
+
);
|
|
16052
|
+
if (dropped > 0) {
|
|
16053
|
+
logSessionCleanupActivity(state, "sweep", {
|
|
16054
|
+
type: "info",
|
|
16055
|
+
level: "warn",
|
|
16056
|
+
message: `Session cleanup: dropped ${dropped} oldest pending deletion report(s) after reaching the spool limit`,
|
|
16057
|
+
metadata: { outcome: "deletion-reports-dropped", dropped_count: dropped }
|
|
16058
|
+
});
|
|
16059
|
+
}
|
|
16060
|
+
} catch (error2) {
|
|
16061
|
+
logSessionCleanupActivity(state, "sweep", {
|
|
16062
|
+
type: "info",
|
|
16063
|
+
level: "warn",
|
|
16064
|
+
message: `Session cleanup: could not persist ${deletionRecords.length} confirmed deletion report(s): ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
16065
|
+
metadata: {
|
|
16066
|
+
outcome: "deletion-report-spool-write-failed",
|
|
16067
|
+
records: deletionRecords.length
|
|
16068
|
+
}
|
|
16069
|
+
});
|
|
16070
|
+
}
|
|
16071
|
+
}
|
|
15427
16072
|
const sessionMetadata = {
|
|
15428
16073
|
sessions_inspected: sessions?.length ?? null,
|
|
15429
16074
|
sessions_deleted: deleted,
|
|
@@ -15493,6 +16138,7 @@ async function runSweep(state, driver, config) {
|
|
|
15493
16138
|
}
|
|
15494
16139
|
});
|
|
15495
16140
|
}
|
|
16141
|
+
await drainSessionDeletionReports(state);
|
|
15496
16142
|
} catch (error2) {
|
|
15497
16143
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
15498
16144
|
logSessionCleanupActivity(state, "sweep", {
|
|
@@ -16335,6 +16981,7 @@ async function run(options) {
|
|
|
16335
16981
|
}
|
|
16336
16982
|
}
|
|
16337
16983
|
reportSessionDbRecovery(state);
|
|
16984
|
+
await drainSessionDeletionReports(state);
|
|
16338
16985
|
if (sessionDbVerifyFatal) {
|
|
16339
16986
|
throw new Error(
|
|
16340
16987
|
sessionDbVerifyFatalReason === void 0 ? "SESSION-DB-INTEGRITY-EXHAUSTED: the corrupt session DB could not be proven separated from the active backup prefix or removed from disk" : `SESSION-DB-VERIFY-TIMEOUT: session DB verification timed out (${sessionDbVerifyFatalReason}); refusing to start`
|
|
@@ -16365,13 +17012,17 @@ async function run(options) {
|
|
|
16365
17012
|
agentId: state.agentId,
|
|
16366
17013
|
apiUrl: getApiUrlConfig(),
|
|
16367
17014
|
getAuthHeader: () => state.authHeader,
|
|
17015
|
+
autoConfigureCredentialsProxy: options.autoConfigureCredentialsProxy,
|
|
16368
17016
|
log: (message, level = "info") => logActivity(state, {
|
|
16369
17017
|
type: level === "error" ? "error" : "info",
|
|
16370
17018
|
level,
|
|
16371
17019
|
...level === "error" ? { error: message } : { message }
|
|
16372
17020
|
})
|
|
16373
17021
|
};
|
|
16374
|
-
|
|
17022
|
+
let managedDiscovery = await discoverManagedInferenceSetup(managedSetupOptions);
|
|
17023
|
+
if (managedDiscovery.kind === "none") {
|
|
17024
|
+
managedDiscovery = await enrollManagedInferenceSetup(managedSetupOptions);
|
|
17025
|
+
}
|
|
16375
17026
|
let managedPreparation = { kind: "none" };
|
|
16376
17027
|
if (managedDiscovery.kind === "failed") {
|
|
16377
17028
|
managedPreparation = managedDiscovery;
|
|
@@ -16461,7 +17112,7 @@ async function run(options) {
|
|
|
16461
17112
|
state.opencodeClient = openCodeClient;
|
|
16462
17113
|
if (options.opencodePidFile && oc.process?.pid !== void 0) {
|
|
16463
17114
|
try {
|
|
16464
|
-
|
|
17115
|
+
writeFileSync7(options.opencodePidFile, `${oc.process.pid}
|
|
16465
17116
|
`, { mode: 384 });
|
|
16466
17117
|
chmodSync3(options.opencodePidFile, 384);
|
|
16467
17118
|
} catch (error2) {
|
|
@@ -16571,7 +17222,7 @@ async function run(options) {
|
|
|
16571
17222
|
let existingPid;
|
|
16572
17223
|
if (existsSync3(options.litestreamPidFile)) {
|
|
16573
17224
|
try {
|
|
16574
|
-
const rawPid =
|
|
17225
|
+
const rawPid = readFileSync9(options.litestreamPidFile, "utf8").trim();
|
|
16575
17226
|
const parsedPid = Number(rawPid);
|
|
16576
17227
|
if (/^\d+$/.test(rawPid) && Number.isSafeInteger(parsedPid) && parsedPid > 0) {
|
|
16577
17228
|
existingPid = parsedPid;
|
|
@@ -16608,7 +17259,7 @@ async function run(options) {
|
|
|
16608
17259
|
});
|
|
16609
17260
|
try {
|
|
16610
17261
|
if (litestreamProcess.pid !== void 0) {
|
|
16611
|
-
|
|
17262
|
+
writeFileSync7(options.litestreamPidFile, `${litestreamProcess.pid}
|
|
16612
17263
|
`, {
|
|
16613
17264
|
mode: 384
|
|
16614
17265
|
});
|
|
@@ -16992,6 +17643,9 @@ program.command("run").description("Connect to Evident and process messages").op
|
|
|
16992
17643
|
).option(
|
|
16993
17644
|
"--opencode-config-overlay <path>",
|
|
16994
17645
|
"Apply this runner-provided OpenCode config before starting OpenCode."
|
|
17646
|
+
).option(
|
|
17647
|
+
"--auto-configure-credentials-proxy",
|
|
17648
|
+
"Install Evident's generated OpenCode providers for the runner's credential pool (off by default)."
|
|
16995
17649
|
).option(
|
|
16996
17650
|
"--credential-sync-marker <path>",
|
|
16997
17651
|
"Own the interval credential sync and write this marker once the shutdown flush has finished, so the runner image's lifecycle hooks can wait on it."
|
|
@@ -17035,6 +17689,7 @@ program.command("run").description("Connect to Evident and process messages").op
|
|
|
17035
17689
|
restoreSessionDb: options.restoreSessionDb,
|
|
17036
17690
|
restoreRunnerCredentials: options.restoreRunnerCredentials,
|
|
17037
17691
|
opencodeConfigOverlay: options.opencodeConfigOverlay,
|
|
17692
|
+
autoConfigureCredentialsProxy: options.autoConfigureCredentialsProxy,
|
|
17038
17693
|
credentialSyncMarker: options.credentialSyncMarker
|
|
17039
17694
|
});
|
|
17040
17695
|
}
|