@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 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 join3, parse, resolve as resolvePath } from "path";
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 join2, relative, resolve as resolve2, sep } from "path";
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 = join2(existingAncestor, ...missingSegments, basename(candidate));
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("~/") ? join2(homeDir, requestedPath.slice(2)) : requestedPath;
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 = join2(current, segment);
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 = join2(dirname2(realTarget), `.evident-push-${randomUUID()}.tmp`);
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("~/") ? join3(homeDir, trimmed.slice(2)) : trimmed;
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",