@evident-ai/cli 3.2.1-dev.978ce0d → 3.2.1-dev.cf740cc
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 -1
- package/dist/index.js +39 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,7 +111,10 @@ Options:
|
|
|
111
111
|
`7d`, `24h`). Setting this (or `--session-cleanup-max-count`) is what enables
|
|
112
112
|
cleanup — there is no separate on/off flag. An invalid value is warned about
|
|
113
113
|
and ignored, which can leave cleanup off if it was the only rule set. Env:
|
|
114
|
-
`EVIDENT_SESSION_CLEANUP_MAX_AGE`.
|
|
114
|
+
`EVIDENT_SESSION_CLEANUP_MAX_AGE`. With cleanup off, a runner whose local
|
|
115
|
+
session store has grown large warns once at startup, on the runner's page,
|
|
116
|
+
naming this flag — pruning stops the store growing, but it does not shrink
|
|
117
|
+
what has already grown.
|
|
115
118
|
- `--session-cleanup-max-count <n>` — Keep only the newest N OpenCode sessions
|
|
116
119
|
by last activity, deleting the rest. Also enables cleanup; combines with
|
|
117
120
|
`--session-cleanup-max-age` as OR. A session with a turn actively in progress
|
package/dist/index.js
CHANGED
|
@@ -1023,7 +1023,7 @@ async function claudeUsage() {
|
|
|
1023
1023
|
|
|
1024
1024
|
// src/commands/run.ts
|
|
1025
1025
|
import { homedir as homedir3 } from "os";
|
|
1026
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1026
|
+
import { isAbsolute as isAbsolute2, join as join4, parse, resolve as resolvePath } from "path";
|
|
1027
1027
|
import chalk6 from "chalk";
|
|
1028
1028
|
|
|
1029
1029
|
// ../../packages/types/src/agents/index.ts
|
|
@@ -2322,6 +2322,31 @@ function resolveSessionCleanupConfig(flags, env = process.env) {
|
|
|
2322
2322
|
return { enabled, maxAgeMs, maxCount, intervalMs, warnings };
|
|
2323
2323
|
}
|
|
2324
2324
|
|
|
2325
|
+
// src/lib/opencode/session-db-size.ts
|
|
2326
|
+
import { statSync as statSync2 } from "fs";
|
|
2327
|
+
import { join as join2 } from "path";
|
|
2328
|
+
var LARGE_DB_THRESHOLD_BYTES = 268435456;
|
|
2329
|
+
function statSessionDbBytes(homeDir) {
|
|
2330
|
+
const dbPath = join2(homeDir, ".local", "share", "opencode", "opencode.db");
|
|
2331
|
+
try {
|
|
2332
|
+
return statSync2(dbPath).size;
|
|
2333
|
+
} catch (err) {
|
|
2334
|
+
const isMissingFile = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
2335
|
+
if (!isMissingFile) {
|
|
2336
|
+
console.error(
|
|
2337
|
+
`[statSessionDbBytes] could not stat ${dbPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
function buildSessionStoreSizeWarning(input) {
|
|
2344
|
+
const { dbBytes, cleanupEnabled } = input;
|
|
2345
|
+
if (dbBytes === null || dbBytes <= LARGE_DB_THRESHOLD_BYTES || cleanupEnabled) return null;
|
|
2346
|
+
const mib = Math.round(dbBytes / 1024 / 1024);
|
|
2347
|
+
return `Session store is large: opencode.db is ${mib} MiB and automatic session cleanup is off. Enable it with --session-cleanup-max-age 24h (env EVIDENT_SESSION_CLEANUP_MAX_AGE) to stop it growing \u2014 a store much larger than this can exceed a hosted runner's session-history restore budget on the next start, losing this runner's session history.`;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2325
2350
|
// src/lib/tunnel/connection.ts
|
|
2326
2351
|
import WebSocket2 from "ws";
|
|
2327
2352
|
|
|
@@ -2757,7 +2782,7 @@ import { homedir as homedir2 } from "os";
|
|
|
2757
2782
|
// src/lib/file-push.ts
|
|
2758
2783
|
import { randomUUID } from "crypto";
|
|
2759
2784
|
import { chmod, mkdir, open as open2, realpath, rename, unlink } from "fs/promises";
|
|
2760
|
-
import { basename, dirname as dirname2, isAbsolute, join as
|
|
2785
|
+
import { basename, dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2, sep } from "path";
|
|
2761
2786
|
var FILE_MODE = 384;
|
|
2762
2787
|
var DIRECTORY_MODE = 448;
|
|
2763
2788
|
async function writePushedFile(request) {
|
|
@@ -2790,7 +2815,7 @@ async function writePushedFile(request) {
|
|
|
2790
2815
|
const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
|
|
2791
2816
|
dirname2(candidate)
|
|
2792
2817
|
);
|
|
2793
|
-
const realTarget =
|
|
2818
|
+
const realTarget = join3(existingAncestor, ...missingSegments, basename(candidate));
|
|
2794
2819
|
const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
|
|
2795
2820
|
if (allowedDirectory === null) {
|
|
2796
2821
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
@@ -2826,7 +2851,7 @@ function expandAndValidate(requestedPath, homeDir) {
|
|
|
2826
2851
|
if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
|
|
2827
2852
|
return null;
|
|
2828
2853
|
}
|
|
2829
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
2854
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join3(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
2830
2855
|
if (expanded.split(/[/\\]/).includes("..")) {
|
|
2831
2856
|
return null;
|
|
2832
2857
|
}
|
|
@@ -2899,13 +2924,13 @@ function contains(realDirectory, realTarget) {
|
|
|
2899
2924
|
async function createMissingDirectories(existingAncestor, missingSegments) {
|
|
2900
2925
|
let current = existingAncestor;
|
|
2901
2926
|
for (const segment of missingSegments) {
|
|
2902
|
-
current =
|
|
2927
|
+
current = join3(current, segment);
|
|
2903
2928
|
await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
|
|
2904
2929
|
await chmod(current, DIRECTORY_MODE);
|
|
2905
2930
|
}
|
|
2906
2931
|
}
|
|
2907
2932
|
async function writeAtomically(realTarget, content) {
|
|
2908
|
-
const temporaryPath =
|
|
2933
|
+
const temporaryPath = join3(dirname2(realTarget), `.evident-push-${randomUUID()}.tmp`);
|
|
2909
2934
|
let handle;
|
|
2910
2935
|
try {
|
|
2911
2936
|
handle = await open2(temporaryPath, "wx", FILE_MODE);
|
|
@@ -6662,7 +6687,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
6662
6687
|
if (trimmed === "") {
|
|
6663
6688
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
6664
6689
|
}
|
|
6665
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
6690
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join4(homeDir, trimmed.slice(2)) : trimmed;
|
|
6666
6691
|
if (!isAbsolute2(expanded)) {
|
|
6667
6692
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
6668
6693
|
}
|
|
@@ -7025,6 +7050,13 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
7025
7050
|
for (const warning2 of config.warnings) {
|
|
7026
7051
|
logActivity(state, { type: "info", level: "warn", message: `Session cleanup: ${warning2}` });
|
|
7027
7052
|
}
|
|
7053
|
+
const sizeWarning = buildSessionStoreSizeWarning({
|
|
7054
|
+
dbBytes: statSessionDbBytes(homedir3()),
|
|
7055
|
+
cleanupEnabled: config.enabled
|
|
7056
|
+
});
|
|
7057
|
+
if (sizeWarning !== null) {
|
|
7058
|
+
logActivity(state, { type: "info", level: "warn", message: sizeWarning });
|
|
7059
|
+
}
|
|
7028
7060
|
if (!config.enabled) return;
|
|
7029
7061
|
logActivity(state, {
|
|
7030
7062
|
type: "info",
|