@taskless-app/swarm 0.6.38 → 0.6.39

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.
Files changed (3) hide show
  1. package/dist/cli.js +265 -148
  2. package/dist/overmind.js +613 -252
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -2018,10 +2018,94 @@ var init_claudeResult = __esm({
2018
2018
  }
2019
2019
  });
2020
2020
 
2021
- // src/executors/claudeCode.ts
2022
- import { readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
2021
+ // src/transcriptSpend.ts
2022
+ import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync } from "fs";
2023
2023
  import { homedir as homedir2 } from "os";
2024
2024
  import { join as join2 } from "path";
2025
+ function transcriptDir(worktree, projectsDir) {
2026
+ return join2(projectsDir, worktree.replace(/[/.]/g, "-"));
2027
+ }
2028
+ function transcriptSpendInWindow(worktree, fromMs, toMs, opts = {}) {
2029
+ const projectsDir = opts.projectsDir ?? join2(homedir2(), ".claude", "projects");
2030
+ const empty = { costUsd: 0, tokensIn: 0, tokensOut: 0, durationMs: 0, replies: 0, unpriced: 0, unpricedModel: null };
2031
+ const dir = transcriptDir(worktree, projectsDir);
2032
+ let files;
2033
+ try {
2034
+ files = readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
2035
+ } catch {
2036
+ return empty;
2037
+ }
2038
+ const seen = /* @__PURE__ */ new Set();
2039
+ let costUsd = 0;
2040
+ let tokensIn = 0;
2041
+ let tokensOut = 0;
2042
+ let firstAt = null;
2043
+ let lastAt = null;
2044
+ let replies = 0;
2045
+ let unpriced = 0;
2046
+ let unpricedModel = null;
2047
+ for (const f of files) {
2048
+ const path = join2(dir, f);
2049
+ let raw;
2050
+ try {
2051
+ if (statSync(path).mtimeMs < fromMs) continue;
2052
+ raw = readFileSync2(path, "utf8");
2053
+ } catch {
2054
+ continue;
2055
+ }
2056
+ for (const line of raw.split("\n")) {
2057
+ if (!line.startsWith("{") || !line.includes('"usage"')) continue;
2058
+ let entry;
2059
+ try {
2060
+ entry = JSON.parse(line);
2061
+ } catch {
2062
+ continue;
2063
+ }
2064
+ if (entry.type !== "assistant") continue;
2065
+ const usage2 = entry.message?.usage;
2066
+ if (!usage2) continue;
2067
+ const at = entry.timestamp ? Date.parse(entry.timestamp) : NaN;
2068
+ if (!Number.isFinite(at) || at < fromMs || at >= toMs) continue;
2069
+ const key = entry.requestId ?? entry.message?.id ?? entry.uuid;
2070
+ if (!key || seen.has(key)) continue;
2071
+ seen.add(key);
2072
+ const model = entry.message?.model ?? null;
2073
+ const est = costFromUsage(usage2, model, opts.table);
2074
+ costUsd += est.costUsd;
2075
+ if (est.warning) {
2076
+ unpriced += 1;
2077
+ unpricedModel ??= model;
2078
+ }
2079
+ const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2080
+ tokensIn += num(usage2.input_tokens) + num(usage2.cache_creation_input_tokens) + num(usage2.cache_read_input_tokens);
2081
+ tokensOut += num(usage2.output_tokens);
2082
+ if (firstAt === null || at < firstAt) firstAt = at;
2083
+ if (lastAt === null || at > lastAt) lastAt = at;
2084
+ replies += 1;
2085
+ }
2086
+ }
2087
+ if (replies === 0) return empty;
2088
+ return {
2089
+ costUsd,
2090
+ tokensIn,
2091
+ tokensOut,
2092
+ durationMs: firstAt !== null && lastAt !== null ? lastAt - firstAt : 0,
2093
+ replies,
2094
+ unpriced,
2095
+ unpricedModel
2096
+ };
2097
+ }
2098
+ var init_transcriptSpend = __esm({
2099
+ "src/transcriptSpend.ts"() {
2100
+ "use strict";
2101
+ init_pricing();
2102
+ }
2103
+ });
2104
+
2105
+ // src/executors/claudeCode.ts
2106
+ import { readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
2107
+ import { homedir as homedir3 } from "os";
2108
+ import { join as join3 } from "path";
2025
2109
  function claudeArgs(cfgPath, prompt, continueSession = false, model, pluginDirs = [], settings, maxBudgetUsd, effort) {
2026
2110
  const pluginFlags = pluginDirs.flatMap((dir) => ["--plugin-dir", dir]);
2027
2111
  const budgetFlags = typeof maxBudgetUsd === "number" && maxBudgetUsd > 0 ? ["--max-budget-usd", String(maxBudgetUsd)] : [];
@@ -2053,21 +2137,21 @@ function droneSettingsJson(paths, mergeGateHook = null) {
2053
2137
  if (mergeGateHook) settings.hooks = { PreToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: mergeGateHook }] }] };
2054
2138
  return JSON.stringify(settings);
2055
2139
  }
2056
- function transcriptDir(worktree, projectsDir) {
2057
- return join2(projectsDir, worktree.replace(/[/.]/g, "-"));
2140
+ function transcriptDir2(worktree, projectsDir) {
2141
+ return join3(projectsDir, worktree.replace(/[/.]/g, "-"));
2058
2142
  }
2059
- function sessionTranscriptExists(worktree, projectsDir = join2(homedir2(), ".claude", "projects")) {
2143
+ function sessionTranscriptExists(worktree, projectsDir = join3(homedir3(), ".claude", "projects")) {
2060
2144
  try {
2061
- return readdirSync2(transcriptDir(worktree, projectsDir)).some((f) => f.endsWith(".jsonl"));
2145
+ return readdirSync3(transcriptDir2(worktree, projectsDir)).some((f) => f.endsWith(".jsonl"));
2062
2146
  } catch {
2063
2147
  return false;
2064
2148
  }
2065
2149
  }
2066
- function transcriptMtimeMs(worktree, projectsDir = join2(homedir2(), ".claude", "projects")) {
2067
- const dir = transcriptDir(worktree, projectsDir);
2150
+ function transcriptMtimeMs(worktree, projectsDir = join3(homedir3(), ".claude", "projects")) {
2151
+ const dir = transcriptDir2(worktree, projectsDir);
2068
2152
  let files;
2069
2153
  try {
2070
- files = readdirSync2(dir);
2154
+ files = readdirSync3(dir);
2071
2155
  } catch {
2072
2156
  return null;
2073
2157
  }
@@ -2075,7 +2159,7 @@ function transcriptMtimeMs(worktree, projectsDir = join2(homedir2(), ".claude",
2075
2159
  for (const f of files) {
2076
2160
  if (!f.endsWith(".jsonl")) continue;
2077
2161
  try {
2078
- const mtime2 = statSync(join2(dir, f)).mtimeMs;
2162
+ const mtime2 = statSync2(join3(dir, f)).mtimeMs;
2079
2163
  if (latest === null || mtime2 > latest) latest = mtime2;
2080
2164
  } catch {
2081
2165
  }
@@ -2088,7 +2172,7 @@ function writeMcpConfig(spec) {
2088
2172
  [spec.mcp.name]: { command: spec.mcp.command, args: spec.mcp.args, env: spec.mcp.env }
2089
2173
  }
2090
2174
  };
2091
- const path = join2(spec.worktree, MCP_CONFIG_FILE);
2175
+ const path = join3(spec.worktree, MCP_CONFIG_FILE);
2092
2176
  writeFileSync2(path, JSON.stringify(cfg, null, 2));
2093
2177
  return path;
2094
2178
  }
@@ -2103,6 +2187,7 @@ var init_claudeCode = __esm({
2103
2187
  init_swarmFiles();
2104
2188
  init_mergeGateHook();
2105
2189
  init_claudeResult();
2190
+ init_transcriptSpend();
2106
2191
  ISOLATION_FLAGS = ["--setting-sources", "project", "--strict-mcp-config"];
2107
2192
  MCP_CONFIG_FILE = SWARM_MCP_CONFIG_FILE;
2108
2193
  claudeCodeExecutor = {
@@ -2141,6 +2226,26 @@ var init_claudeCode = __esm({
2141
2226
  ...abortField(sessionAbort(result))
2142
2227
  };
2143
2228
  },
2229
+ // TASWM-548: итога нет — берём трату из транскрипта сессии. Тот пишется по ходу работы и
2230
+ // переживает SIGTERM разгрузки, из-за которого итог и не был напечатан; формат usage там
2231
+ // тот же, что в итоге, поэтому цена считается тем же прайсингом (см. transcriptSpend.ts).
2232
+ rescueSpend(worktree, fromMs, toMs) {
2233
+ const spend = transcriptSpendInWindow(worktree, fromMs, toMs);
2234
+ if (spend.replies === 0) return null;
2235
+ return {
2236
+ costUsd: spend.costUsd,
2237
+ tokensIn: spend.tokensIn,
2238
+ tokensOut: spend.tokensOut,
2239
+ durationMs: spend.durationMs,
2240
+ // Часть ответов не по чему было тарифицировать (модели нет в MODEL_PRICING) — их
2241
+ // токены в трату вошли, а деньги НЕТ. Тот же канал, что и у warning штатного итога
2242
+ // (cost.accountRunCost его печатает): иначе спасённые $0 при сожжённых токенах
2243
+ // выглядели бы как честный ноль.
2244
+ ...spend.unpriced > 0 ? {
2245
+ warning: `\u0437\u0430\u043F\u0438\u0441\u044C \u0441\u0435\u0441\u0441\u0438\u0438: ${spend.unpriced} \u0438\u0437 ${spend.replies} \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u0431\u0435\u0437 \u0441\u0442\u0440\u043E\u043A\u0438 \u0432 \u043F\u0440\u0430\u0439\u0441\u0438\u043D\u0433\u0435 (\u043C\u043E\u0434\u0435\u043B\u044C "${spend.unpricedModel ?? ""}") \u2014 \u0438\u0445 \u0446\u0435\u043D\u0430 \u0432 \u0441\u043F\u0430\u0441\u0451\u043D\u043D\u0443\u044E \u0442\u0440\u0430\u0442\u0443 \u041D\u0415 \u0432\u043E\u0448\u043B\u0430`
2246
+ } : {}
2247
+ };
2248
+ },
2144
2249
  session: {
2145
2250
  exists: (worktree) => sessionTranscriptExists(worktree),
2146
2251
  mtimeMs: (worktree) => transcriptMtimeMs(worktree)
@@ -2158,34 +2263,34 @@ var init_claudeCode = __esm({
2158
2263
  });
2159
2264
 
2160
2265
  // src/swarmHome.ts
2161
- import { homedir as homedir3 } from "os";
2162
- import { join as join3 } from "path";
2266
+ import { homedir as homedir4 } from "os";
2267
+ import { join as join4 } from "path";
2163
2268
  import { mkdirSync as mkdirSync2 } from "fs";
2164
2269
  var SWARM_HOME;
2165
2270
  var init_swarmHome = __esm({
2166
2271
  "src/swarmHome.ts"() {
2167
2272
  "use strict";
2168
- SWARM_HOME = process.env.SWARM_HOME || join3(homedir3(), ".taskless-swarm");
2273
+ SWARM_HOME = process.env.SWARM_HOME || join4(homedir4(), ".taskless-swarm");
2169
2274
  mkdirSync2(SWARM_HOME, { recursive: true });
2170
2275
  }
2171
2276
  });
2172
2277
 
2173
2278
  // src/executors/codex.ts
2174
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync3, statSync as statSync2, symlinkSync } from "fs";
2175
- import { join as join4 } from "path";
2279
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2280
+ import { join as join5 } from "path";
2176
2281
  function resolveCodexBin(env = process.env) {
2177
2282
  return (env.SWARM_CODEX_BIN ?? "").trim() || "codex";
2178
2283
  }
2179
2284
  function codexHomePath(worktree, swarmHome = SWARM_HOME) {
2180
- return join4(swarmHome, "executor-state", "codex", worktree.replace(/[/.]/g, "-"));
2285
+ return join5(swarmHome, "executor-state", "codex", worktree.replace(/[/.]/g, "-"));
2181
2286
  }
2182
2287
  function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2183
2288
  const home = codexHomePath(worktree, swarmHome);
2184
2289
  mkdirSync3(home, { recursive: true });
2185
- const real = (env.CODEX_HOME ?? "").trim() || join4(realHome, ".codex");
2290
+ const real = (env.CODEX_HOME ?? "").trim() || join5(realHome, ".codex");
2186
2291
  for (const file of ["auth.json", "config.toml"]) {
2187
- const target = join4(real, file);
2188
- const link = join4(home, file);
2292
+ const target = join5(real, file);
2293
+ const link = join5(home, file);
2189
2294
  if (!existsSync3(target) || existsSync3(link)) continue;
2190
2295
  try {
2191
2296
  symlinkSync(target, link);
@@ -2244,16 +2349,16 @@ function parseCodexStream(raw) {
2244
2349
  function newestRolloutMtime(dir, depth = 4) {
2245
2350
  let entries;
2246
2351
  try {
2247
- entries = readdirSync3(dir);
2352
+ entries = readdirSync4(dir);
2248
2353
  } catch {
2249
2354
  return null;
2250
2355
  }
2251
2356
  let latest = null;
2252
2357
  for (const name of entries) {
2253
- const full = join4(dir, name);
2358
+ const full = join5(dir, name);
2254
2359
  let st;
2255
2360
  try {
2256
- st = statSync2(full);
2361
+ st = statSync3(full);
2257
2362
  } catch {
2258
2363
  continue;
2259
2364
  }
@@ -2324,8 +2429,8 @@ ${notice}` : spec.prompt;
2324
2429
  return est.warning ? { costUsd: est.costUsd, warning: est.warning } : { costUsd: est.costUsd };
2325
2430
  },
2326
2431
  session: {
2327
- exists: (worktree) => newestRolloutMtime(join4(codexHomePath(worktree), "sessions")) !== null,
2328
- mtimeMs: (worktree) => newestRolloutMtime(join4(codexHomePath(worktree), "sessions"))
2432
+ exists: (worktree) => newestRolloutMtime(join5(codexHomePath(worktree), "sessions")) !== null,
2433
+ mtimeMs: (worktree) => newestRolloutMtime(join5(codexHomePath(worktree), "sessions"))
2329
2434
  },
2330
2435
  probe: {
2331
2436
  // Тот же принцип, что у claude: дешёвый реальный прогон — он проверяет и auth, и живость
@@ -2372,8 +2477,8 @@ var init_executors = __esm({
2372
2477
  });
2373
2478
 
2374
2479
  // src/config.ts
2375
- import { basename, join as join5, resolve } from "path";
2376
- import { readFileSync as readFileSync2, realpathSync, renameSync, writeFileSync as writeFileSync3 } from "fs";
2480
+ import { basename, join as join6, resolve } from "path";
2481
+ import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync3 } from "fs";
2377
2482
  function normalizeRepo(repo) {
2378
2483
  const r = (repo ?? "").trim();
2379
2484
  if (!r) return "";
@@ -2387,10 +2492,10 @@ function effectiveRepoOf(repo, fallback) {
2387
2492
  return normalizeRepo(repo || fallback || "");
2388
2493
  }
2389
2494
  function droneWorktreePath(worktreesDir, repo, slug) {
2390
- return join5(droneWorktreeRoot(worktreesDir, repo), slug);
2495
+ return join6(droneWorktreeRoot(worktreesDir, repo), slug);
2391
2496
  }
2392
2497
  function droneWorktreeRoot(worktreesDir, repo) {
2393
- return join5(worktreesDir, basename(normalizeRepo(repo)));
2498
+ return join6(worktreesDir, basename(normalizeRepo(repo)));
2394
2499
  }
2395
2500
  function resolveEffectiveWorktreesDir(deps) {
2396
2501
  if (deps.envOverride) return deps.envOverride;
@@ -2417,7 +2522,7 @@ function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
2417
2522
  }
2418
2523
  function tokenFromFile() {
2419
2524
  try {
2420
- return readFileSync2(join5(SWARM_HOME, "token"), "utf8").trim();
2525
+ return readFileSync3(join6(SWARM_HOME, "token"), "utf8").trim();
2421
2526
  } catch {
2422
2527
  return "";
2423
2528
  }
@@ -2430,25 +2535,25 @@ var init_config = __esm({
2430
2535
  init_executors();
2431
2536
  init_swarmHome();
2432
2537
  init_swarmHome();
2433
- DB_PATH = process.env.SWARM_DB || join5(SWARM_HOME, "swarm.db");
2538
+ DB_PATH = process.env.SWARM_DB || join6(SWARM_HOME, "swarm.db");
2434
2539
  EXECUTOR = resolveExecutor(process.env.SWARM_EXECUTOR);
2435
- DRONE_HOME = process.env.SWARM_DRONE_HOME || join5(SWARM_HOME, "drone-home");
2540
+ DRONE_HOME = process.env.SWARM_DRONE_HOME || join6(SWARM_HOME, "drone-home");
2436
2541
  DRONE_HOME_ISOLATION = process.env.SWARM_DRONE_ISOLATE_HOME !== "0" && process.platform !== "win32";
2437
2542
  DRONE_ISOLATION = DRONE_HOME_ISOLATION ? { home: DRONE_HOME, swarmHome: SWARM_HOME } : void 0;
2438
- WORKTREES_NOINDEX_DIR = join5(SWARM_HOME, "worktrees.noindex");
2439
- WORKTREES_LEGACY_DIR = join5(SWARM_HOME, "worktrees");
2543
+ WORKTREES_NOINDEX_DIR = join6(SWARM_HOME, "worktrees.noindex");
2544
+ WORKTREES_LEGACY_DIR = join6(SWARM_HOME, "worktrees");
2440
2545
  WORKTREES_DIR = process.env.SWARM_WORKTREES || WORKTREES_NOINDEX_DIR;
2441
- RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join5(SWARM_HOME, "run-logs");
2442
- DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join5(SWARM_HOME, "overmind.log");
2546
+ RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join6(SWARM_HOME, "run-logs");
2547
+ DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join6(SWARM_HOME, "overmind.log");
2443
2548
  DAEMON_LOG_MAX_BYTES = envNum("SWARM_DAEMON_LOG_MAX_BYTES", 16 * 1024 * 1024);
2444
2549
  DAEMON_LOG_KEEP = envNum("SWARM_DAEMON_LOG_KEEP", 2);
2445
2550
  DAEMON_LOG_CHECK_MS = envNum("SWARM_DAEMON_LOG_CHECK_MS", 6e4);
2446
2551
  DIST_BUILD = true;
2447
2552
  SWARM_BACKEND = process.env.SWARM_BACKEND === "client" ? "client" : process.env.SWARM_BACKEND === "sqlite" ? "sqlite" : DIST_BUILD ? "client" : "sqlite";
2448
- SWARM_OUTBOX = process.env.SWARM_OUTBOX || join5(SWARM_HOME, "outbox.jsonl");
2553
+ SWARM_OUTBOX = process.env.SWARM_OUTBOX || join6(SWARM_HOME, "outbox.jsonl");
2449
2554
  SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
2450
2555
  SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
2451
- ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join5(SWARM_HOME, "env-cache");
2556
+ ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join6(SWARM_HOME, "env-cache");
2452
2557
  ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
2453
2558
  ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
2454
2559
  SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
@@ -2478,15 +2583,15 @@ var init_config = __esm({
2478
2583
  DRONE_CHANNEL_ENABLED = process.env.SWARM_DRONE_CHANNEL !== "0";
2479
2584
  DRONE_CHANNEL_PORT = Number(process.env.SWARM_DRONE_CHANNEL_PORT) || 7421;
2480
2585
  DRONE_CHANNEL_URL = process.env.SWARM_DRONE_CHANNEL_URL || `http://127.0.0.1:${DRONE_CHANNEL_PORT}`;
2481
- DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join5(SWARM_HOME, "drone-channel.key");
2586
+ DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join6(SWARM_HOME, "drone-channel.key");
2482
2587
  DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
2483
- HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join5(SWARM_HOME, "overmind.heartbeat");
2588
+ HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join6(SWARM_HOME, "overmind.heartbeat");
2484
2589
  HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
2485
2590
  SWARM_REPO = process.env.SWARM_REPO || "";
2486
2591
  SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
2487
2592
  SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
2488
2593
  SWARM_SCAN_ROOTS_RAW = process.env.SWARM_SCAN_ROOTS || "";
2489
- REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join5(SWARM_HOME, "repo-map.json");
2594
+ REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join6(SWARM_HOME, "repo-map.json");
2490
2595
  SWARM_REPO_MAP_MS = envNum("SWARM_REPO_MAP_MS", 15 * 6e4);
2491
2596
  SWARM_REPO_SCAN_TTL_MS = envNum("SWARM_REPO_SCAN_TTL_MS", 24 * 60 * 6e4);
2492
2597
  TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
@@ -2501,7 +2606,7 @@ var init_config = __esm({
2501
2606
  SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
2502
2607
  SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
2503
2608
  SWARM_MERGE_QUEUE = process.env.SWARM_MERGE_QUEUE !== "0";
2504
- REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join5(SWARM_HOME, "review-cutoff.json");
2609
+ REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join6(SWARM_HOME, "review-cutoff.json");
2505
2610
  SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
2506
2611
  SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
2507
2612
  WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
@@ -2541,7 +2646,7 @@ var init_quietExec = __esm({
2541
2646
 
2542
2647
  // src/resources.ts
2543
2648
  import os from "os";
2544
- import { readFileSync as readFileSync3, statfsSync } from "fs";
2649
+ import { readFileSync as readFileSync4, statfsSync } from "fs";
2545
2650
  function isTransientResourceError(e) {
2546
2651
  const code = e?.code;
2547
2652
  return code === "ENFILE" || code === "EMFILE" || code === "EAGAIN";
@@ -2593,7 +2698,7 @@ function fileDescriptorLoadRatio() {
2593
2698
  }
2594
2699
  if (process.platform === "linux") {
2595
2700
  try {
2596
- const [allocated, , max] = readFileSync3("/proc/sys/fs/file-nr", "utf8").trim().split(/\s+/).map(Number);
2701
+ const [allocated, , max] = readFileSync4("/proc/sys/fs/file-nr", "utf8").trim().split(/\s+/).map(Number);
2597
2702
  if (Number.isFinite(allocated ?? NaN) && Number.isFinite(max ?? NaN) && max) return allocated / max;
2598
2703
  } catch {
2599
2704
  }
@@ -2817,11 +2922,11 @@ var init_zoneNotes = __esm({
2817
2922
  });
2818
2923
 
2819
2924
  // src/agentToken.ts
2820
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync } from "fs";
2821
- import { join as join6 } from "path";
2925
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, unlinkSync } from "fs";
2926
+ import { join as join7 } from "path";
2822
2927
  function loadAgentToken() {
2823
2928
  try {
2824
- const s = JSON.parse(readFileSync4(STORE_PATH, "utf8"));
2929
+ const s = JSON.parse(readFileSync5(STORE_PATH, "utf8"));
2825
2930
  return s.token ? s : null;
2826
2931
  } catch {
2827
2932
  return null;
@@ -2899,7 +3004,7 @@ var init_agentToken = __esm({
2899
3004
  "src/agentToken.ts"() {
2900
3005
  "use strict";
2901
3006
  init_config();
2902
- STORE_PATH = join6(SWARM_HOME, "agent.json");
3007
+ STORE_PATH = join7(SWARM_HOME, "agent.json");
2903
3008
  base = (api) => api.replace(/\/$/, "");
2904
3009
  TOKEN_PROBE_TIMEOUT_MS = 8e3;
2905
3010
  }
@@ -3285,7 +3390,7 @@ var init_package = __esm({
3285
3390
  "package.json"() {
3286
3391
  package_default = {
3287
3392
  name: "@taskless-app/swarm",
3288
- version: "0.6.38",
3393
+ version: "0.6.39",
3289
3394
  type: "module",
3290
3395
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3291
3396
  keywords: [
@@ -3336,7 +3441,7 @@ var init_package = __esm({
3336
3441
  execa: "^10.0.0",
3337
3442
  zod: "3.25.76"
3338
3443
  },
3339
- gitHead: "b605569670600b022ae6eec5d042c385cc4c77dd"
3444
+ gitHead: "0c0b1bc0ee05add0d2b5b5d503a05ed7b7834dd8"
3340
3445
  };
3341
3446
  }
3342
3447
  });
@@ -3502,12 +3607,12 @@ var init_netTimeout = __esm({
3502
3607
 
3503
3608
  // src/droneChannel.ts
3504
3609
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
3505
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
3610
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
3506
3611
  import { dirname as dirname3 } from "path";
3507
3612
  import { serve } from "@hono/node-server";
3508
3613
  function loadChannelSecret(path) {
3509
3614
  try {
3510
- const s = readFileSync8(path, "utf8").trim();
3615
+ const s = readFileSync9(path, "utf8").trim();
3511
3616
  if (s.length >= 32) return s;
3512
3617
  } catch {
3513
3618
  }
@@ -3590,8 +3695,8 @@ var init_authState = __esm({
3590
3695
 
3591
3696
  // src/oauth.ts
3592
3697
  import { createHash, randomBytes as randomBytes2 } from "crypto";
3593
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3 } from "fs";
3594
- import { join as join11 } from "path";
3698
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3 } from "fs";
3699
+ import { join as join12 } from "path";
3595
3700
  function makePkce() {
3596
3701
  const verifier = randomBytes2(48).toString("base64url");
3597
3702
  const challenge = createHash("sha256").update(verifier).digest("base64url");
@@ -3675,7 +3780,7 @@ function toStore(clientId, t, now) {
3675
3780
  }
3676
3781
  function loadStore() {
3677
3782
  try {
3678
- return JSON.parse(readFileSync9(STORE_PATH2, "utf8"));
3783
+ return JSON.parse(readFileSync10(STORE_PATH2, "utf8"));
3679
3784
  } catch {
3680
3785
  return null;
3681
3786
  }
@@ -3691,7 +3796,7 @@ function clearStore() {
3691
3796
  }
3692
3797
  function loadClientId() {
3693
3798
  try {
3694
- return JSON.parse(readFileSync9(CLIENT_PATH, "utf8")).clientId;
3799
+ return JSON.parse(readFileSync10(CLIENT_PATH, "utf8")).clientId;
3695
3800
  } catch {
3696
3801
  return null;
3697
3802
  }
@@ -3726,16 +3831,16 @@ var init_oauth = __esm({
3726
3831
  AUTHORIZE = "/api/oauth/authorize";
3727
3832
  TOKEN = "/api/oauth/token";
3728
3833
  SCOPE = "openid profile email taskless:full";
3729
- STORE_PATH2 = join11(SWARM_HOME, "oauth.json");
3730
- CLIENT_PATH = join11(SWARM_HOME, "oauth-client.json");
3834
+ STORE_PATH2 = join12(SWARM_HOME, "oauth.json");
3835
+ CLIENT_PATH = join12(SWARM_HOME, "oauth-client.json");
3731
3836
  base2 = (api) => api.replace(/\/$/, "");
3732
3837
  }
3733
3838
  });
3734
3839
 
3735
3840
  // src/git.ts
3736
- import { existsSync as existsSync10, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
3841
+ import { existsSync as existsSync10, readdirSync as readdirSync9, statSync as statSync9 } from "fs";
3737
3842
  import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
3738
- import { isAbsolute, join as join14, sep as sep2 } from "path";
3843
+ import { isAbsolute, join as join15, sep as sep2 } from "path";
3739
3844
  import { $ as $5 } from "execa";
3740
3845
  function gitNet(repo, args, timeoutMs) {
3741
3846
  return withHardTimeout(
@@ -3750,7 +3855,7 @@ async function reclaimStaleWorktree(repo, worktree) {
3750
3855
  }
3751
3856
  function isLinkedWorktree(dir) {
3752
3857
  try {
3753
- return statSync8(join14(dir, ".git")).isFile();
3858
+ return statSync9(join15(dir, ".git")).isFile();
3754
3859
  } catch {
3755
3860
  return false;
3756
3861
  }
@@ -3855,8 +3960,8 @@ async function ensureGitExcluded(worktree, pattern) {
3855
3960
  try {
3856
3961
  const commonDir = await gitPath(worktree, "--git-common-dir");
3857
3962
  if (!commonDir) return false;
3858
- const infoDir = join14(commonDir, "info");
3859
- const excludeFile = join14(infoDir, "exclude");
3963
+ const infoDir = join15(commonDir, "info");
3964
+ const excludeFile = join15(infoDir, "exclude");
3860
3965
  let current = "";
3861
3966
  try {
3862
3967
  current = await readFile(excludeFile, "utf8");
@@ -3876,7 +3981,7 @@ async function gitPath(worktree, flag) {
3876
3981
  const out = await $5({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
3877
3982
  const raw = out.stdout.trim();
3878
3983
  if (out.exitCode !== 0 || !raw) return null;
3879
- return isAbsolute(raw) ? raw : join14(worktree, raw);
3984
+ return isAbsolute(raw) ? raw : join15(worktree, raw);
3880
3985
  }
3881
3986
  async function ensureWorktreeHygiene(worktree) {
3882
3987
  for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
@@ -3886,12 +3991,12 @@ async function installGuardHooks(worktree) {
3886
3991
  try {
3887
3992
  const gitDir = await gitPath(worktree, "--git-dir");
3888
3993
  if (!gitDir) return false;
3889
- const hooksDir = join14(gitDir, "swarm-hooks");
3994
+ const hooksDir = join15(gitDir, "swarm-hooks");
3890
3995
  const prev = await previousHooksPath(worktree);
3891
3996
  await mkdir(hooksDir, { recursive: true });
3892
3997
  for (const hook of GUARD_HOOKS) {
3893
- await writeFile(join14(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
3894
- await chmod(join14(hooksDir, hook), 493);
3998
+ await writeFile(join15(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
3999
+ await chmod(join15(hooksDir, hook), 493);
3895
4000
  }
3896
4001
  await $5`git -C ${worktree} config extensions.worktreeConfig true`;
3897
4002
  await $5`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
@@ -3910,7 +4015,7 @@ async function previousHooksPath(worktree) {
3910
4015
  if (out.exitCode === 0 && value) return value;
3911
4016
  }
3912
4017
  const commonDir = await gitPath(worktree, "--git-common-dir");
3913
- return commonDir ? join14(commonDir, "hooks") : null;
4018
+ return commonDir ? join15(commonDir, "hooks") : null;
3914
4019
  }
3915
4020
  async function removeWorktree(repo, path) {
3916
4021
  const branch = (await $5({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
@@ -3967,7 +4072,7 @@ var init_git = __esm({
3967
4072
 
3968
4073
  // src/attachments.ts
3969
4074
  import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
3970
- import { join as join15 } from "path";
4075
+ import { join as join16 } from "path";
3971
4076
  var ATTACHMENTS_DIR, ATTACH_MAX_TOTAL_BYTES, ATTACH_MAX_FILE_BYTES, IMAGE_EXT, DOC_EXT, TEXT_EXT, KNOWN_EXT;
3972
4077
  var init_attachments = __esm({
3973
4078
  "src/attachments.ts"() {
@@ -3986,7 +4091,7 @@ var init_attachments = __esm({
3986
4091
 
3987
4092
  // src/shotUpload.ts
3988
4093
  import { readFile as fsReadFile } from "fs/promises";
3989
- import { basename as basename5, isAbsolute as isAbsolute2, join as join16 } from "path";
4094
+ import { basename as basename5, isAbsolute as isAbsolute2, join as join17 } from "path";
3990
4095
  var SHOT_MAX_FILE_BYTES;
3991
4096
  var init_shotUpload = __esm({
3992
4097
  "src/shotUpload.ts"() {
@@ -3996,11 +4101,11 @@ var init_shotUpload = __esm({
3996
4101
  });
3997
4102
 
3998
4103
  // src/workspace.ts
3999
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, unlinkSync as unlinkSync4 } from "fs";
4000
- import { join as join17 } from "path";
4104
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, unlinkSync as unlinkSync4 } from "fs";
4105
+ import { join as join18 } from "path";
4001
4106
  function loadWorkspace() {
4002
4107
  try {
4003
- const w = JSON.parse(readFileSync10(WORKSPACE_PATH, "utf8"));
4108
+ const w = JSON.parse(readFileSync11(WORKSPACE_PATH, "utf8"));
4004
4109
  return w && typeof w.id === "string" && w.id ? w : null;
4005
4110
  } catch {
4006
4111
  return null;
@@ -4023,7 +4128,7 @@ var init_workspace = __esm({
4023
4128
  "src/workspace.ts"() {
4024
4129
  "use strict";
4025
4130
  init_config();
4026
- WORKSPACE_PATH = join17(SWARM_HOME, "workspace.json");
4131
+ WORKSPACE_PATH = join18(SWARM_HOME, "workspace.json");
4027
4132
  }
4028
4133
  });
4029
4134
 
@@ -4441,14 +4546,14 @@ var init_daemonLink = __esm({
4441
4546
  });
4442
4547
 
4443
4548
  // src/pauseState.ts
4444
- import { existsSync as existsSync11, readFileSync as readFileSync11, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
4445
- import { join as join18 } from "path";
4549
+ import { existsSync as existsSync11, readFileSync as readFileSync12, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
4550
+ import { join as join19 } from "path";
4446
4551
  function pauseStatePath(home) {
4447
- return join18(home, "pause.json");
4552
+ return join19(home, "pause.json");
4448
4553
  }
4449
4554
  function loadPauseState(home, io = {}) {
4450
4555
  const exists = io.exists ?? existsSync11;
4451
- const readFile2 = io.readFile ?? ((p) => readFileSync11(p, "utf8"));
4556
+ const readFile2 = io.readFile ?? ((p) => readFileSync12(p, "utf8"));
4452
4557
  const path = pauseStatePath(home);
4453
4558
  if (!exists(path)) return ACTIVE;
4454
4559
  try {
@@ -4509,10 +4614,10 @@ var init_pauseState = __esm({
4509
4614
  });
4510
4615
 
4511
4616
  // src/heartbeat.ts
4512
- import { readFileSync as readFileSync12, renameSync as renameSync6, writeFileSync as writeFileSync11 } from "fs";
4617
+ import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync11 } from "fs";
4513
4618
  function readHeartbeat(path) {
4514
4619
  try {
4515
- const ms = Number(readFileSync12(path, "utf8").trim());
4620
+ const ms = Number(readFileSync13(path, "utf8").trim());
4516
4621
  return Number.isFinite(ms) ? ms : null;
4517
4622
  } catch {
4518
4623
  return null;
@@ -4574,6 +4679,7 @@ function rowToRun(row) {
4574
4679
  tokens_in: row.tokens_in ?? 0,
4575
4680
  tokens_out: row.tokens_out ?? 0,
4576
4681
  duration_ms: row.duration_ms ?? 0,
4682
+ cost_unmeasured: row.cost_unmeasured ?? 0,
4577
4683
  pr_url: row.pr_url ?? null,
4578
4684
  // TASWM-506: файлы, занятые прогоном с открытым PR — JSON-массив, как depends_on.
4579
4685
  changed_paths: row.changed_paths ? JSON.parse(row.changed_paths) : [],
@@ -4654,6 +4760,7 @@ CREATE TABLE IF NOT EXISTS runs (
4654
4760
  tokens_in INTEGER NOT NULL DEFAULT 0,
4655
4761
  tokens_out INTEGER NOT NULL DEFAULT 0,
4656
4762
  duration_ms INTEGER NOT NULL DEFAULT 0,
4763
+ cost_unmeasured INTEGER NOT NULL DEFAULT 0,
4657
4764
  pr_url TEXT,
4658
4765
  superseded_by INTEGER,
4659
4766
  decision TEXT,
@@ -4769,6 +4876,10 @@ CREATE TABLE IF NOT EXISTS imports (
4769
4876
  "ALTER TABLE runs ADD COLUMN tokens_in INTEGER NOT NULL DEFAULT 0",
4770
4877
  "ALTER TABLE runs ADD COLUMN tokens_out INTEGER NOT NULL DEFAULT 0",
4771
4878
  "ALTER TABLE runs ADD COLUMN duration_ms INTEGER NOT NULL DEFAULT 0",
4879
+ // TASWM-548: сколько запусков прогона остались без снятой цены — отличает «бесплатно»
4880
+ // от «не измерено». Прогоны до фичи получают 0: их нули мы перепроверить уже не можем,
4881
+ // а задним числом объявлять их неизмеренными значило бы утверждать непроверенное.
4882
+ "ALTER TABLE runs ADD COLUMN cost_unmeasured INTEGER NOT NULL DEFAULT 0",
4772
4883
  // TASWM-395: усилие рассуждения прогона (`claude --effort`). Nullable без DEFAULT —
4773
4884
  // NULL это и все прогоны до фичи, и прогоны на дефолте харнесса.
4774
4885
  "ALTER TABLE runs ADD COLUMN effort TEXT",
@@ -5486,8 +5597,8 @@ CREATE TABLE IF NOT EXISTS imports (
5486
5597
  });
5487
5598
 
5488
5599
  // src/repoScan.ts
5489
- import { readdirSync as readdirSync9, readFileSync as readFileSync13, statSync as statSync9 } from "fs";
5490
- import { join as join19 } from "path";
5600
+ import { readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync10 } from "fs";
5601
+ import { join as join20 } from "path";
5491
5602
  var init_repoScan = __esm({
5492
5603
  "src/repoScan.ts"() {
5493
5604
  "use strict";
@@ -5496,9 +5607,9 @@ var init_repoScan = __esm({
5496
5607
  });
5497
5608
 
5498
5609
  // src/repoDiscovery.ts
5499
- import { homedir as homedir5 } from "os";
5500
- import { readFileSync as readFileSync14, renameSync as renameSync7, writeFileSync as writeFileSync12 } from "fs";
5501
- import { isAbsolute as isAbsolute3, join as join20, resolve as resolve3 } from "path";
5610
+ import { homedir as homedir6 } from "os";
5611
+ import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync12 } from "fs";
5612
+ import { isAbsolute as isAbsolute3, join as join21, resolve as resolve3 } from "path";
5502
5613
  function repoMapLines(rows) {
5503
5614
  if (!rows?.length) return [];
5504
5615
  const missing = rows.filter((r) => !r.path).length;
@@ -5513,7 +5624,7 @@ function repoMapLines(rows) {
5513
5624
  }
5514
5625
  function loadRepoMapCache(path) {
5515
5626
  try {
5516
- const raw = JSON.parse(readFileSync14(path, "utf8"));
5627
+ const raw = JSON.parse(readFileSync15(path, "utf8"));
5517
5628
  if (!Array.isArray(raw?.entries)) return { scannedAt: null, entries: [] };
5518
5629
  return {
5519
5630
  scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt ? raw.scannedAt : null,
@@ -5882,11 +5993,11 @@ __export(statusCli_exports, {
5882
5993
  probeLink: () => probeLink,
5883
5994
  runStatusCli: () => runStatusCli
5884
5995
  });
5885
- import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
5996
+ import { existsSync as existsSync13, readFileSync as readFileSync16 } from "fs";
5886
5997
  function outboxPending(path) {
5887
5998
  try {
5888
5999
  if (!existsSync13(path)) return 0;
5889
- return readFileSync15(path, "utf8").split("\n").filter((l) => l.trim()).length;
6000
+ return readFileSync16(path, "utf8").split("\n").filter((l) => l.trim()).length;
5890
6001
  } catch {
5891
6002
  return null;
5892
6003
  }
@@ -6093,11 +6204,11 @@ var init_prodMergeGate = __esm({
6093
6204
  });
6094
6205
 
6095
6206
  // src/platform.ts
6096
- import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
6097
- import { delimiter, join as join21 } from "path";
6207
+ import { existsSync as existsSync14, readFileSync as readFileSync17 } from "fs";
6208
+ import { delimiter, join as join22 } from "path";
6098
6209
  function safeReadText(path) {
6099
6210
  try {
6100
- return readFileSync16(path, "utf8");
6211
+ return readFileSync17(path, "utf8");
6101
6212
  } catch {
6102
6213
  return null;
6103
6214
  }
@@ -6108,7 +6219,7 @@ function resolveCommandPath(name, env = process.env, platform = process.platform
6108
6219
  const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
6109
6220
  for (const dir of dirs) {
6110
6221
  for (const ext of exts) {
6111
- const candidate = join21(dir, name + ext);
6222
+ const candidate = join22(dir, name + ext);
6112
6223
  if (existsSync14(candidate)) return candidate;
6113
6224
  }
6114
6225
  }
@@ -6213,12 +6324,12 @@ var init_platform = __esm({
6213
6324
 
6214
6325
  // src/envGate.ts
6215
6326
  import { existsSync as existsSync15 } from "fs";
6216
- import { join as join22 } from "path";
6327
+ import { join as join23 } from "path";
6217
6328
  import { createConnection } from "net";
6218
6329
  import { execa as execa3 } from "execa";
6219
6330
  function detectRequirements(repo, io = {}) {
6220
6331
  const exists = io.exists ?? existsSync15;
6221
- const marker = COMPOSE_MARKERS.find((f) => exists(join22(repo, f)));
6332
+ const marker = COMPOSE_MARKERS.find((f) => exists(join23(repo, f)));
6222
6333
  return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
6223
6334
  }
6224
6335
  async function dockerAlive() {
@@ -6250,7 +6361,7 @@ var init_envGate = __esm({
6250
6361
  });
6251
6362
 
6252
6363
  // src/doctor.ts
6253
- import { isAbsolute as isAbsolute4, join as join23 } from "path";
6364
+ import { isAbsolute as isAbsolute4, join as join24 } from "path";
6254
6365
  function ok(id, title, detail) {
6255
6366
  return { id, title, status: "ok", detail, remedies: [], critical: false };
6256
6367
  }
@@ -6457,7 +6568,7 @@ function checkRepos(io, snap) {
6457
6568
  critical: true
6458
6569
  };
6459
6570
  }
6460
- if (env && !io.exists(join23(env, ".git"))) {
6571
+ if (env && !io.exists(join24(env, ".git"))) {
6461
6572
  return {
6462
6573
  id: "repos",
6463
6574
  title,
@@ -6467,7 +6578,7 @@ function checkRepos(io, snap) {
6467
6578
  critical: true
6468
6579
  };
6469
6580
  }
6470
- const broken = snap.paths.filter((p) => !io.exists(join23(p, ".git")));
6581
+ const broken = snap.paths.filter((p) => !io.exists(join24(p, ".git")));
6471
6582
  if (broken.length) {
6472
6583
  return {
6473
6584
  id: "repos",
@@ -6657,12 +6768,12 @@ var init_doctor = __esm({
6657
6768
  });
6658
6769
 
6659
6770
  // src/repoMemory.ts
6660
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "fs";
6661
- import { join as join24 } from "path";
6771
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
6772
+ import { join as join25 } from "path";
6662
6773
  function loadRememberedRepos(home) {
6663
6774
  let raw;
6664
6775
  try {
6665
- raw = readFileSync17(join24(home, FILE), "utf8");
6776
+ raw = readFileSync18(join25(home, FILE), "utf8");
6666
6777
  } catch {
6667
6778
  return [];
6668
6779
  }
@@ -6981,8 +7092,8 @@ var init_updateCli = __esm({
6981
7092
 
6982
7093
  // src/cli.ts
6983
7094
  init_cliIo();
6984
- import { basename as basename6, dirname as dirname5, join as join25 } from "path";
6985
- import { existsSync as existsSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync14, mkdirSync as mkdirSync10, readdirSync as readdirSync10, statSync as statSync10, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
7095
+ import { basename as basename6, dirname as dirname5, join as join26 } from "path";
7096
+ import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync14, mkdirSync as mkdirSync10, readdirSync as readdirSync11, statSync as statSync11, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
6986
7097
  import { setTimeout as sleep2 } from "timers/promises";
6987
7098
 
6988
7099
  // src/swarmClient.ts
@@ -6990,7 +7101,7 @@ init_config();
6990
7101
  init_resources();
6991
7102
  init_names2();
6992
7103
  init_storm();
6993
- import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
7104
+ import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
6994
7105
  import { dirname as dirname2 } from "path";
6995
7106
  import { randomUUID } from "crypto";
6996
7107
 
@@ -7022,19 +7133,19 @@ init_zoneNotes();
7022
7133
  init_toolFailure();
7023
7134
 
7024
7135
  // src/logTail.ts
7025
- import { closeSync, existsSync as existsSync5, openSync, readSync, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
7026
- import { join as join8 } from "path";
7136
+ import { closeSync, existsSync as existsSync5, openSync, readSync, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
7137
+ import { join as join9 } from "path";
7027
7138
 
7028
7139
  // src/runLogDir.ts
7029
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync4, renameSync as renameSync2, statSync as statSync3 } from "fs";
7030
- import { join as join7 } from "path";
7140
+ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync5, renameSync as renameSync2, statSync as statSync4 } from "fs";
7141
+ import { join as join8 } from "path";
7031
7142
  function runLogDirName(runLogKey) {
7032
7143
  const safe = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
7033
7144
  if (!safe) throw new Error(`\u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0432 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 run-log'\u043E\u0432: ${JSON.stringify(runLogKey)}`);
7034
7145
  return /^\d+$/.test(safe) ? `k-${safe}` : safe;
7035
7146
  }
7036
7147
  function runLogDirPath(runLogsDir, runLogKey) {
7037
- return join7(runLogsDir, runLogDirName(runLogKey));
7148
+ return join8(runLogsDir, runLogDirName(runLogKey));
7038
7149
  }
7039
7150
  function findRunLogDir(runLogsDir, keys) {
7040
7151
  for (const key of keys) {
@@ -7246,13 +7357,13 @@ var SwarmClient = class _SwarmClient {
7246
7357
  recover() {
7247
7358
  if (existsSync6(this.mapPath)) {
7248
7359
  try {
7249
- const obj = JSON.parse(readFileSync5(this.mapPath, "utf8"));
7360
+ const obj = JSON.parse(readFileSync6(this.mapPath, "utf8"));
7250
7361
  for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
7251
7362
  } catch {
7252
7363
  }
7253
7364
  }
7254
7365
  if (existsSync6(this.outboxPath)) {
7255
- const txt = readFileSync5(this.outboxPath, "utf8");
7366
+ const txt = readFileSync6(this.outboxPath, "utf8");
7256
7367
  for (const line of txt.split("\n")) {
7257
7368
  const s = line.trim();
7258
7369
  if (!s) continue;
@@ -7275,7 +7386,7 @@ var SwarmClient = class _SwarmClient {
7275
7386
  if (!existsSync6(this.stormPath)) return;
7276
7387
  let txt;
7277
7388
  try {
7278
- txt = readFileSync5(this.stormPath, "utf8");
7389
+ txt = readFileSync6(this.stormPath, "utf8");
7279
7390
  } catch {
7280
7391
  return;
7281
7392
  }
@@ -7359,6 +7470,7 @@ var SwarmClient = class _SwarmClient {
7359
7470
  if ("tokens_in" in patch && patch.tokens_in !== void 0) out.tokensIn = patch.tokens_in;
7360
7471
  if ("tokens_out" in patch && patch.tokens_out !== void 0) out.tokensOut = patch.tokens_out;
7361
7472
  if ("duration_ms" in patch && patch.duration_ms !== void 0) out.durationMs = patch.duration_ms;
7473
+ if ("cost_unmeasured" in patch && patch.cost_unmeasured !== void 0) out.costUnmeasured = patch.cost_unmeasured;
7362
7474
  if ("respawns" in patch && patch.respawns !== void 0) out.respawns = patch.respawns;
7363
7475
  if ("pr_url" in patch) out.prUrl = patch.pr_url ?? null;
7364
7476
  if ("changed_paths" in patch && patch.changed_paths) out.changedPaths = patch.changed_paths;
@@ -7441,6 +7553,8 @@ var SwarmClient = class _SwarmClient {
7441
7553
  // копятся учётом запусков claude (cost.ts), как cost_usd — TASWM-353
7442
7554
  tokens_out: 0,
7443
7555
  duration_ms: 0,
7556
+ cost_unmeasured: 0,
7557
+ // запусков без снятой цены (TASWM-548) — копится тем же учётом
7444
7558
  pr_url: null,
7445
7559
  changed_paths: [],
7446
7560
  // файлы прогона снимаются при открытии PR (TASWM-506)
@@ -9061,6 +9175,8 @@ var SwarmClient = class _SwarmClient {
9061
9175
  // токены и время работы (TASWM-353) — прод-поля, заполняются ниже
9062
9176
  tokens_out: 0,
9063
9177
  duration_ms: 0,
9178
+ cost_unmeasured: 0,
9179
+ // запусков без снятой цены (TASWM-548) — прод-поле, заполняется ниже
9064
9180
  pr_url: null,
9065
9181
  changed_paths: [],
9066
9182
  // занятые файлы (TASWM-506) — прод-поле, заполняется ниже
@@ -9142,6 +9258,7 @@ var SwarmClient = class _SwarmClient {
9142
9258
  local.tokens_in = typeof pr.tokensIn === "number" ? pr.tokensIn : 0;
9143
9259
  local.tokens_out = typeof pr.tokensOut === "number" ? pr.tokensOut : 0;
9144
9260
  local.duration_ms = typeof pr.durationMs === "number" ? pr.durationMs : 0;
9261
+ local.cost_unmeasured = typeof pr.costUnmeasured === "number" ? pr.costUnmeasured : 0;
9145
9262
  local.pr_url = pr.prUrl ?? null;
9146
9263
  local.changed_paths = Array.isArray(pr.changedPaths) ? pr.changedPaths : local.changed_paths ?? [];
9147
9264
  local.stop_reason = pr.stopReason ?? null;
@@ -9539,7 +9656,7 @@ init_config();
9539
9656
  import { $ as $6 } from "execa";
9540
9657
 
9541
9658
  // src/daemonLaunch.ts
9542
- import { openSync as openSync2, closeSync as closeSync2, readFileSync as readFileSync6 } from "fs";
9659
+ import { openSync as openSync2, closeSync as closeSync2, readFileSync as readFileSync7 } from "fs";
9543
9660
  import { spawn } from "child_process";
9544
9661
  var FOREGROUND_FLAGS = /* @__PURE__ */ new Set(["--foreground", "-f", "--attach"]);
9545
9662
  function parseStartMode(argv) {
@@ -9599,18 +9716,18 @@ function launchDetached(opts) {
9599
9716
  }
9600
9717
  function tailFile(path, lines) {
9601
9718
  try {
9602
- return tailText(readFileSync6(path, "utf8"), lines);
9719
+ return tailText(readFileSync7(path, "utf8"), lines);
9603
9720
  } catch {
9604
9721
  return "";
9605
9722
  }
9606
9723
  }
9607
9724
 
9608
9725
  // src/logRotate.ts
9609
- import { copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync5, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
9726
+ import { copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync6, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
9610
9727
  var realIo = {
9611
9728
  size: (path) => {
9612
9729
  try {
9613
- return statSync5(path).size;
9730
+ return statSync6(path).size;
9614
9731
  } catch {
9615
9732
  return null;
9616
9733
  }
@@ -9647,15 +9764,15 @@ init_repoRegistry();
9647
9764
 
9648
9765
  // src/hostIdentity.ts
9649
9766
  import { hostname } from "os";
9650
- import { existsSync as existsSync8, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
9651
- import { join as join9 } from "path";
9767
+ import { existsSync as existsSync8, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
9768
+ import { join as join10 } from "path";
9652
9769
  import { randomUUID as randomUUID2 } from "crypto";
9653
9770
  function hostIdentityPath(home) {
9654
- return join9(home, "host.json");
9771
+ return join10(home, "host.json");
9655
9772
  }
9656
9773
  function loadHostIdentity(deps) {
9657
9774
  const exists = deps.exists ?? existsSync8;
9658
- const readFile2 = deps.readFile ?? ((p) => readFileSync7(p, "utf8"));
9775
+ const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
9659
9776
  const writeFile3 = deps.writeFile ?? ((p, data) => {
9660
9777
  writeFileSync6(`${p}.tmp`, data);
9661
9778
  renameSync4(`${p}.tmp`, p);
@@ -9772,8 +9889,8 @@ import { basename as basename4 } from "path";
9772
9889
  import { serve as serve2 } from "@hono/node-server";
9773
9890
 
9774
9891
  // src/diff.ts
9775
- import { statSync as statSync6 } from "fs";
9776
- import { join as join10 } from "path";
9892
+ import { statSync as statSync7 } from "fs";
9893
+ import { join as join11 } from "path";
9777
9894
  import { $ as $3 } from "execa";
9778
9895
  var MAX_FILES = 200;
9779
9896
  var MAX_LINES_PER_FILE = 1500;
@@ -9916,7 +10033,7 @@ async function resolveBase(wt, baseBranch) {
9916
10033
  }
9917
10034
  function mtime(path) {
9918
10035
  try {
9919
- return statSync6(path).mtimeMs;
10036
+ return statSync7(path).mtimeMs;
9920
10037
  } catch {
9921
10038
  return 0;
9922
10039
  }
@@ -9941,7 +10058,7 @@ async function collectRunDiff(worktree, opts = {}) {
9941
10058
  files.push(pf);
9942
10059
  }
9943
10060
  }
9944
- files.sort((a, b) => mtime(join10(worktree, b.path)) - mtime(join10(worktree, a.path)));
10061
+ files.sort((a, b) => mtime(join11(worktree, b.path)) - mtime(join11(worktree, a.path)));
9945
10062
  const truncatedFiles = files.length > MAX_FILES;
9946
10063
  if (truncatedFiles) files = files.slice(0, MAX_FILES);
9947
10064
  const added = files.reduce((s, f) => s + f.added, 0);
@@ -11832,15 +11949,15 @@ import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDronePro
11832
11949
 
11833
11950
  // src/launcher.ts
11834
11951
  import { spawn as spawn2 } from "child_process";
11835
- import { closeSync as closeSync3, existsSync as existsSync9, mkdirSync as mkdirSync9, openSync as openSync3, readdirSync as readdirSync7 } from "fs";
11952
+ import { closeSync as closeSync3, existsSync as existsSync9, mkdirSync as mkdirSync9, openSync as openSync3, readdirSync as readdirSync8 } from "fs";
11836
11953
  import { createRequire } from "module";
11837
- import { homedir as homedir4 } from "os";
11838
- import { dirname as dirname4, join as join13 } from "path";
11954
+ import { homedir as homedir5 } from "os";
11955
+ import { dirname as dirname4, join as join14 } from "path";
11839
11956
  import { fileURLToPath as fileURLToPath2 } from "url";
11840
11957
 
11841
11958
  // src/droneHome.ts
11842
- import { lstatSync, mkdirSync as mkdirSync8, readdirSync as readdirSync6, rmSync, statSync as statSync7, symlinkSync as symlinkSync2 } from "fs";
11843
- import { join as join12, relative, sep } from "path";
11959
+ import { lstatSync, mkdirSync as mkdirSync8, readdirSync as readdirSync7, rmSync, statSync as statSync8, symlinkSync as symlinkSync2 } from "fs";
11960
+ import { join as join13, relative, sep } from "path";
11844
11961
  var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
11845
11962
  function droneHomeDeny(env = process.env) {
11846
11963
  const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
@@ -11859,28 +11976,28 @@ function buildDroneHome(opts) {
11859
11976
  const deny = opts.deny ?? droneHomeDeny();
11860
11977
  const platform = opts.platform ?? process.platform;
11861
11978
  mkdirSync8(droneHome, { recursive: true, mode: 448 });
11862
- const wanted = new Set(mirroredEntries(readdirSync6(realHome), deny, platform));
11979
+ const wanted = new Set(mirroredEntries(readdirSync7(realHome), deny, platform));
11863
11980
  const rel = relative(realHome, droneHome);
11864
11981
  if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
11865
- for (const existing of readdirSync6(droneHome)) {
11982
+ for (const existing of readdirSync7(droneHome)) {
11866
11983
  if (wanted.has(existing)) continue;
11867
- const p = join12(droneHome, existing);
11984
+ const p = join13(droneHome, existing);
11868
11985
  try {
11869
11986
  if (lstatSync(p).isSymbolicLink()) rmSync(p);
11870
11987
  } catch {
11871
11988
  }
11872
11989
  }
11873
11990
  for (const name of wanted) {
11874
- const link = join12(droneHome, name);
11991
+ const link = join13(droneHome, name);
11875
11992
  try {
11876
11993
  lstatSync(link);
11877
11994
  continue;
11878
11995
  } catch {
11879
11996
  }
11880
- const target = join12(realHome, name);
11997
+ const target = join13(realHome, name);
11881
11998
  let isDir;
11882
11999
  try {
11883
- isDir = statSync7(target).isDirectory();
12000
+ isDir = statSync8(target).isDirectory();
11884
12001
  } catch {
11885
12002
  continue;
11886
12003
  }
@@ -11898,7 +12015,7 @@ var REAL_PATH_ENV_PINS = [
11898
12015
  { env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
11899
12016
  ];
11900
12017
  function realPathEnvPins(realHome) {
11901
- return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join12(realHome, ...segments)]));
12018
+ return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join13(realHome, ...segments)]));
11902
12019
  }
11903
12020
  function dronePrivatePaths(realHome, swarmHome) {
11904
12021
  return [
@@ -12010,7 +12127,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
12010
12127
  const env = { ...base3 };
12011
12128
  const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
12012
12129
  if (isolate && !base3.TURBO_CACHE_DIR) {
12013
- env.TURBO_CACHE_DIR = join13(worktree, ".turbo", "cache");
12130
+ env.TURBO_CACHE_DIR = join14(worktree, ".turbo", "cache");
12014
12131
  }
12015
12132
  Object.assign(env, droneTestEnv(droneCap, base3));
12016
12133
  for (const key of DAEMON_STATE_ENV) delete env[key];
@@ -12038,10 +12155,10 @@ var DAEMON_STATE_ENV = [
12038
12155
  "TASKLESS_TOKEN"
12039
12156
  ];
12040
12157
  function bundledRoleDir(kind) {
12041
- return join13(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
12158
+ return join14(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
12042
12159
  }
12043
12160
  function roleDir(worktree, kind) {
12044
- return join13(worktree, ".claude", "roles", kind);
12161
+ return join14(worktree, ".claude", "roles", kind);
12045
12162
  }
12046
12163
  function roleDirs(worktree, kind) {
12047
12164
  return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync9(dir));
@@ -12052,7 +12169,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
12052
12169
  mkdirSync9(dir, { recursive: true });
12053
12170
  let files = [];
12054
12171
  try {
12055
- files = readdirSync7(dir);
12172
+ files = readdirSync8(dir);
12056
12173
  } catch {
12057
12174
  }
12058
12175
  let next = 0;
@@ -12060,7 +12177,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
12060
12177
  const m = /^(\d+)\.json/.exec(f);
12061
12178
  if (m) next = Math.max(next, Number(m[1]) + 1);
12062
12179
  }
12063
- return join13(dir, `${next}.json`);
12180
+ return join14(dir, `${next}.json`);
12064
12181
  }
12065
12182
  function openNextRunLog(runLogsDir, runLogKeys) {
12066
12183
  for (let attempt = 0; attempt < 50; attempt++) {
@@ -12141,11 +12258,11 @@ function makeLauncher(cfg) {
12141
12258
  } catch (e) {
12142
12259
  console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0443\u0441\u0438\u043B\u0438\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
12143
12260
  }
12144
- const realHome = cfg.droneIsolation?.realHome ?? homedir4();
12261
+ const realHome = cfg.droneIsolation?.realHome ?? homedir5();
12145
12262
  const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
12146
- if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync9(join13(cfg.droneIsolation.swarmHome, "token"))) {
12263
+ if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync9(join14(cfg.droneIsolation.swarmHome, "token"))) {
12147
12264
  console.error(
12148
- `[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join13(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
12265
+ `[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join14(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
12149
12266
  );
12150
12267
  }
12151
12268
  const executor = cfg.executor ?? resolveExecutor();
@@ -13051,12 +13168,12 @@ async function runDaemonLogs(argv) {
13051
13168
  const head = tailFile(DAEMON_LOG_PATH, lines);
13052
13169
  if (head) console.log(head);
13053
13170
  if (!follow) return 0;
13054
- let pos = statSync10(DAEMON_LOG_PATH).size;
13171
+ let pos = statSync11(DAEMON_LOG_PATH).size;
13055
13172
  for (; ; ) {
13056
13173
  await sleep2(500);
13057
13174
  let size;
13058
13175
  try {
13059
- size = statSync10(DAEMON_LOG_PATH).size;
13176
+ size = statSync11(DAEMON_LOG_PATH).size;
13060
13177
  } catch {
13061
13178
  continue;
13062
13179
  }
@@ -13328,7 +13445,7 @@ function effectiveWorktreesDir() {
13328
13445
  exists: (d) => existsSync17(d),
13329
13446
  isNonEmptyDir: (d) => {
13330
13447
  try {
13331
- return readdirSync10(d).length > 0;
13448
+ return readdirSync11(d).length > 0;
13332
13449
  } catch {
13333
13450
  return false;
13334
13451
  }
@@ -13384,12 +13501,12 @@ async function scoutRepo(repo) {
13384
13501
  await $6({ reject: false })`git -C ${worktree} reset --hard ${baseRef}`;
13385
13502
  }
13386
13503
  await setWorktreeIdentity(worktree, name);
13387
- const abs = (rel) => join25(worktree, rel);
13504
+ const abs = (rel) => join26(worktree, rel);
13388
13505
  const code = await runRepoScout({
13389
13506
  repoFullName,
13390
13507
  repoPath: worktree,
13391
13508
  name,
13392
- readFile: (rel) => existsSync17(abs(rel)) ? readFileSync18(abs(rel), "utf8") : null,
13509
+ readFile: (rel) => existsSync17(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
13393
13510
  writeFile: (rel, text) => {
13394
13511
  mkdirSync10(dirname5(abs(rel)), { recursive: true });
13395
13512
  writeFileSync14(abs(rel), text);