codeam-cli 2.39.8 → 2.39.9

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/CHANGELOG.md +6 -0
  2. package/dist/index.js +372 -167
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.39.8] — 2026-06-13
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Beads watcher trigger — watch .beads/last-touched + initial push
12
+
7
13
  ## [2.39.7] — 2026-06-13
8
14
 
9
15
  ### Fixed
package/dist/index.js CHANGED
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
498
498
  // package.json
499
499
  var package_default = {
500
500
  name: "codeam-cli",
501
- version: "2.39.8",
501
+ version: "2.39.9",
502
502
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
503
503
  type: "commonjs",
504
504
  main: "dist/index.js",
@@ -1186,8 +1186,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
1186
1186
  return decodedFile;
1187
1187
  };
1188
1188
  }
1189
- function normalizeWindowsPath(path53) {
1190
- return path53.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1189
+ function normalizeWindowsPath(path54) {
1190
+ return path54.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
1191
1191
  }
1192
1192
 
1193
1193
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3667,9 +3667,9 @@ async function addSourceContext(frames) {
3667
3667
  LRU_FILE_CONTENTS_CACHE.reduce();
3668
3668
  return frames;
3669
3669
  }
3670
- function getContextLinesFromFile(path53, ranges, output) {
3670
+ function getContextLinesFromFile(path54, ranges, output) {
3671
3671
  return new Promise((resolve7) => {
3672
- const stream = (0, import_node_fs.createReadStream)(path53);
3672
+ const stream = (0, import_node_fs.createReadStream)(path54);
3673
3673
  const lineReaded = (0, import_node_readline.createInterface)({
3674
3674
  input: stream
3675
3675
  });
@@ -3684,7 +3684,7 @@ function getContextLinesFromFile(path53, ranges, output) {
3684
3684
  let rangeStart = range[0];
3685
3685
  let rangeEnd = range[1];
3686
3686
  function onStreamError() {
3687
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path53, 1);
3687
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path54, 1);
3688
3688
  lineReaded.close();
3689
3689
  lineReaded.removeAllListeners();
3690
3690
  destroyStreamAndResolve();
@@ -3745,8 +3745,8 @@ function clearLineContext(frame) {
3745
3745
  delete frame.context_line;
3746
3746
  delete frame.post_context;
3747
3747
  }
3748
- function shouldSkipContextLinesForFile(path53) {
3749
- return path53.startsWith("node:") || path53.endsWith(".min.js") || path53.endsWith(".min.cjs") || path53.endsWith(".min.mjs") || path53.startsWith("data:");
3748
+ function shouldSkipContextLinesForFile(path54) {
3749
+ return path54.startsWith("node:") || path54.endsWith(".min.js") || path54.endsWith(".min.cjs") || path54.endsWith(".min.mjs") || path54.startsWith("data:");
3750
3750
  }
3751
3751
  function shouldSkipContextLinesForFrame(frame) {
3752
3752
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -5900,7 +5900,7 @@ function readAnonId() {
5900
5900
  }
5901
5901
  function superProperties() {
5902
5902
  return {
5903
- cliVersion: true ? "2.39.8" : "0.0.0-dev",
5903
+ cliVersion: true ? "2.39.9" : "0.0.0-dev",
5904
5904
  nodeVersion: process.version,
5905
5905
  platform: process.platform,
5906
5906
  arch: process.arch,
@@ -9978,13 +9978,13 @@ function detectStartupBanner(lines) {
9978
9978
  while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
9979
9979
  if (metaIdx - artStart < 2) return null;
9980
9980
  const pathLine = (lines[metaIdx + 1] ?? "").trim();
9981
- const path53 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9981
+ const path54 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9982
9982
  return {
9983
9983
  title: "",
9984
9984
  subtitle: lines[metaIdx].trim(),
9985
- path: path53,
9985
+ path: path54,
9986
9986
  startIdx: artStart,
9987
- endIdx: metaIdx + (path53 ? 1 : 0)
9987
+ endIdx: metaIdx + (path54 ? 1 : 0)
9988
9988
  };
9989
9989
  }
9990
9990
 
@@ -11342,11 +11342,11 @@ function parseReview(stdout) {
11342
11342
  for (const line of lines) {
11343
11343
  const m = line.match(HUNK_LINE_RE);
11344
11344
  if (!m) continue;
11345
- const [, path53, lineNo, sevToken, message] = m;
11346
- if (!path53 || !lineNo || !message) continue;
11345
+ const [, path54, lineNo, sevToken, message] = m;
11346
+ if (!path54 || !lineNo || !message) continue;
11347
11347
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
11348
11348
  hunks.push({
11349
- path: path53.trim(),
11349
+ path: path54.trim(),
11350
11350
  line: Number(lineNo),
11351
11351
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
11352
11352
  message: cleanedMessage
@@ -15896,9 +15896,9 @@ function extractSelectPrompt(text) {
15896
15896
  }
15897
15897
 
15898
15898
  // src/commands/start/handlers.ts
15899
- var fs34 = __toESM(require("fs"));
15899
+ var fs35 = __toESM(require("fs"));
15900
15900
  var os27 = __toESM(require("os"));
15901
- var path41 = __toESM(require("path"));
15901
+ var path42 = __toESM(require("path"));
15902
15902
  var import_crypto3 = require("crypto");
15903
15903
  var import_child_process18 = require("child_process");
15904
15904
 
@@ -17349,6 +17349,10 @@ async function waitForPortListening(port, opts) {
17349
17349
  }
17350
17350
  }
17351
17351
 
17352
+ // src/services/preview/provision-deps.ts
17353
+ var import_fs2 = require("fs");
17354
+ var import_path6 = __toESM(require("path"));
17355
+
17352
17356
  // src/services/preview/run-setup.ts
17353
17357
  var import_child_process12 = require("child_process");
17354
17358
  function runSetupCommand(cmd, args2, cwd, env, opts) {
@@ -17393,19 +17397,216 @@ function runSetupCommand(cmd, args2, cwd, env, opts) {
17393
17397
  });
17394
17398
  }
17395
17399
 
17400
+ // src/services/preview/provision-deps.ts
17401
+ var COMPOSE_FILES = [
17402
+ "docker-compose.yml",
17403
+ "docker-compose.yaml",
17404
+ "compose.yaml",
17405
+ "compose.yml"
17406
+ ];
17407
+ var ENV_SAMPLES = [".env.example", ".env.sample", ".env.local.example", ".env.template"];
17408
+ var POSTGRES = {
17409
+ name: "postgres",
17410
+ image: "postgres:16",
17411
+ port: 5432,
17412
+ environment: { POSTGRES_USER: "postgres", POSTGRES_PASSWORD: "postgres", POSTGRES_DB: "app" },
17413
+ healthTest: ["CMD-SHELL", "pg_isready -U postgres"],
17414
+ envLines: [
17415
+ "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app",
17416
+ "DB_HOST=localhost",
17417
+ "DB_PORT=5432",
17418
+ "DB_USERNAME=postgres",
17419
+ "DB_PASSWORD=postgres",
17420
+ "DB_NAME=app"
17421
+ ]
17422
+ };
17423
+ var MYSQL = {
17424
+ name: "mysql",
17425
+ image: "mysql:8",
17426
+ port: 3306,
17427
+ environment: { MYSQL_ROOT_PASSWORD: "mysql", MYSQL_DATABASE: "app" },
17428
+ healthTest: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-pmysql"],
17429
+ envLines: ["DATABASE_URL=mysql://root:mysql@localhost:3306/app"]
17430
+ };
17431
+ var MONGO = {
17432
+ name: "mongo",
17433
+ image: "mongo:7",
17434
+ port: 27017,
17435
+ environment: {},
17436
+ healthTest: ["CMD", "mongosh", "--eval", 'db.adminCommand("ping")'],
17437
+ envLines: ["MONGODB_URI=mongodb://localhost:27017/app", "MONGO_URL=mongodb://localhost:27017/app"]
17438
+ };
17439
+ var REDIS = {
17440
+ name: "redis",
17441
+ image: "redis:7",
17442
+ port: 6379,
17443
+ environment: {},
17444
+ healthTest: ["CMD", "redis-cli", "ping"],
17445
+ envLines: ["REDIS_URL=redis://localhost:6379", "REDIS_HOST=localhost", "REDIS_PORT=6379"]
17446
+ };
17447
+ function detectServicesFromDeps(pkg) {
17448
+ const deps = /* @__PURE__ */ new Set([
17449
+ ...Object.keys(pkg.dependencies ?? {}),
17450
+ ...Object.keys(pkg.devDependencies ?? {})
17451
+ ]);
17452
+ const has = (...names) => names.some((n) => deps.has(n));
17453
+ const out2 = [];
17454
+ if (has("pg", "typeorm", "@nestjs/typeorm", "sequelize", "postgres", "postgres.js", "pg-promise"))
17455
+ out2.push(POSTGRES);
17456
+ if (has("mysql", "mysql2")) out2.push(MYSQL);
17457
+ if (has("mongoose", "mongodb")) out2.push(MONGO);
17458
+ if (has("ioredis", "redis", "cache-manager-ioredis-yet", "cache-manager-redis-store"))
17459
+ out2.push(REDIS);
17460
+ return out2;
17461
+ }
17462
+ function renderComposeYaml(services) {
17463
+ const blocks = services.map((s) => {
17464
+ const env = Object.entries(s.environment);
17465
+ const envYaml = env.length ? " environment:\n" + env.map(([k2, v]) => ` ${k2}: "${v}"`).join("\n") + "\n" : "";
17466
+ const health = JSON.stringify(s.healthTest);
17467
+ return ` ${s.name}:
17468
+ image: ${s.image}
17469
+ restart: unless-stopped
17470
+ ports:
17471
+ - "${s.port}:${s.port}"
17472
+ ` + envYaml + ` healthcheck:
17473
+ test: ${health}
17474
+ interval: 5s
17475
+ timeout: 3s
17476
+ retries: 12
17477
+ `;
17478
+ });
17479
+ return `# Generated by codeam \u2014 auto-provisioned project dependencies.
17480
+ # Do not edit; regenerated on each provision. See .codeam/provision/.
17481
+ services:
17482
+ ${blocks.join("")}`;
17483
+ }
17484
+ function pickMigrationScript(scripts) {
17485
+ const preferred = [
17486
+ "migration:run",
17487
+ "db:migrate",
17488
+ "migrate:deploy",
17489
+ "migrate:latest",
17490
+ "migrate:up",
17491
+ "prisma:migrate",
17492
+ "migrate"
17493
+ ];
17494
+ for (const k2 of preferred) if (scripts[k2]) return k2;
17495
+ for (const k2 of Object.keys(scripts)) {
17496
+ if (/migrat/i.test(k2) && /(run|deploy|latest|up)/i.test(k2) && !/(generate|create|revert|rollback|undo|down|reset|drop)/i.test(k2)) {
17497
+ return k2;
17498
+ }
17499
+ }
17500
+ return null;
17501
+ }
17502
+ async function exists(p2) {
17503
+ try {
17504
+ await import_fs2.promises.access(p2);
17505
+ return true;
17506
+ } catch {
17507
+ return false;
17508
+ }
17509
+ }
17510
+ async function firstExisting(cwd, names) {
17511
+ for (const n of names) if (await exists(import_path6.default.join(cwd, n))) return n;
17512
+ return null;
17513
+ }
17514
+ async function ensureEnvFile(cwd, generated) {
17515
+ if (await exists(import_path6.default.join(cwd, ".env"))) {
17516
+ log.info("provision", ".env already present \u2014 leaving it untouched");
17517
+ return;
17518
+ }
17519
+ const sample = await firstExisting(cwd, ENV_SAMPLES);
17520
+ if (sample) {
17521
+ const body = await import_fs2.promises.readFile(import_path6.default.join(cwd, sample), "utf8");
17522
+ await import_fs2.promises.writeFile(import_path6.default.join(cwd, ".env"), body);
17523
+ log.info("provision", `wrote .env from ${sample}`);
17524
+ return;
17525
+ }
17526
+ if (generated.length > 0) {
17527
+ const body = "# Generated by codeam \u2014 points at the auto-provisioned local services.\n" + generated.flatMap((s) => s.envLines).join("\n") + "\n";
17528
+ await import_fs2.promises.writeFile(import_path6.default.join(cwd, ".env"), body);
17529
+ log.info("provision", `generated .env for ${generated.map((s) => s.name).join("+")}`);
17530
+ }
17531
+ }
17532
+ async function readPackageJson(cwd) {
17533
+ try {
17534
+ return JSON.parse(await import_fs2.promises.readFile(import_path6.default.join(cwd, "package.json"), "utf8"));
17535
+ } catch {
17536
+ return null;
17537
+ }
17538
+ }
17539
+ async function runMigrationsIfPresent(cwd, scripts) {
17540
+ const script = pickMigrationScript(scripts);
17541
+ if (!script) return;
17542
+ log.info("provision", `running migrations: npm run ${script}`);
17543
+ const res = await runSetupCommand("npm", ["run", script], cwd, void 0, { timeoutMs: 12e4 });
17544
+ if (res.status !== "ok") log.warn("provision", `migration script "${script}" \u2192 ${res.status} (non-fatal)`);
17545
+ }
17546
+ function runDockerComposeUp(cwd, composeArgs) {
17547
+ return runSetupCommand(
17548
+ "nice",
17549
+ ["-n", "19", "ionice", "-c", "3", "docker", "compose", ...composeArgs],
17550
+ cwd,
17551
+ void 0,
17552
+ { timeoutMs: 18e4 }
17553
+ );
17554
+ }
17555
+ async function provisionProjectDependencies(cwd) {
17556
+ try {
17557
+ const docker = await runSetupCommand("docker", ["info"], cwd, void 0, { timeoutMs: 15e3 });
17558
+ if (docker.status !== "ok") {
17559
+ log.info("provision", "docker not usable \u2014 skipping dependency provisioning");
17560
+ return;
17561
+ }
17562
+ const pkg = await readPackageJson(cwd);
17563
+ let started = false;
17564
+ let generated = [];
17565
+ const composeFile = await firstExisting(cwd, COMPOSE_FILES);
17566
+ if (composeFile) {
17567
+ log.info("provision", `compose found (${composeFile}) \u2014 docker compose up -d --wait (idle prio)`);
17568
+ const up = await runDockerComposeUp(cwd, ["up", "-d", "--wait"]);
17569
+ started = up.status === "ok";
17570
+ if (!started) log.warn("provision", `compose up \u2192 ${up.status} (non-fatal)`);
17571
+ } else if (pkg) {
17572
+ generated = detectServicesFromDeps(pkg);
17573
+ if (generated.length > 0) {
17574
+ const dir = import_path6.default.join(cwd, ".codeam", "provision");
17575
+ await import_fs2.promises.mkdir(dir, { recursive: true });
17576
+ const file = import_path6.default.join(dir, "compose.generated.yaml");
17577
+ await import_fs2.promises.writeFile(file, renderComposeYaml(generated));
17578
+ log.info(
17579
+ "provision",
17580
+ `no compose in repo \u2014 generated ${generated.map((s) => s.name).join("+")}`
17581
+ );
17582
+ const up = await runDockerComposeUp(cwd, ["-f", file, "up", "-d", "--wait"]);
17583
+ started = up.status === "ok";
17584
+ if (!started) log.warn("provision", `generated compose up \u2192 ${up.status} (non-fatal)`);
17585
+ } else {
17586
+ log.info("provision", "no compose + no known service deps \u2014 nothing to provision");
17587
+ }
17588
+ }
17589
+ await ensureEnvFile(cwd, generated);
17590
+ if (started && pkg?.scripts) await runMigrationsIfPresent(cwd, pkg.scripts);
17591
+ log.info("provision", "project dependency provisioning complete");
17592
+ } catch (err) {
17593
+ log.warn("provision", "provisionProjectDependencies failed (non-fatal)", err);
17594
+ }
17595
+ }
17596
+
17396
17597
  // src/services/preview/setup-deps.ts
17397
- var import_fs2 = __toESM(require("fs"));
17398
- var import_path6 = __toESM(require("path"));
17598
+ var import_fs3 = __toESM(require("fs"));
17599
+ var import_path7 = __toESM(require("path"));
17399
17600
  function detectMissingNodeDeps(cwd) {
17400
- if (!import_fs2.default.existsSync(import_path6.default.join(cwd, "package.json"))) return null;
17401
- if (import_fs2.default.existsSync(import_path6.default.join(cwd, "node_modules"))) return null;
17402
- if (import_fs2.default.existsSync(import_path6.default.join(cwd, "pnpm-lock.yaml"))) {
17601
+ if (!import_fs3.default.existsSync(import_path7.default.join(cwd, "package.json"))) return null;
17602
+ if (import_fs3.default.existsSync(import_path7.default.join(cwd, "node_modules"))) return null;
17603
+ if (import_fs3.default.existsSync(import_path7.default.join(cwd, "pnpm-lock.yaml"))) {
17403
17604
  return { cmd: "pnpm", args: ["install"] };
17404
17605
  }
17405
- if (import_fs2.default.existsSync(import_path6.default.join(cwd, "yarn.lock"))) {
17606
+ if (import_fs3.default.existsSync(import_path7.default.join(cwd, "yarn.lock"))) {
17406
17607
  return { cmd: "yarn", args: ["install"] };
17407
17608
  }
17408
- if (import_fs2.default.existsSync(import_path6.default.join(cwd, "bun.lockb")) || import_fs2.default.existsSync(import_path6.default.join(cwd, "bun.lock"))) {
17609
+ if (import_fs3.default.existsSync(import_path7.default.join(cwd, "bun.lockb")) || import_fs3.default.existsSync(import_path7.default.join(cwd, "bun.lock"))) {
17409
17610
  return { cmd: "bun", args: ["install"] };
17410
17611
  }
17411
17612
  return { cmd: "npm", args: ["install"] };
@@ -17460,8 +17661,8 @@ function activePreviewSessionIds() {
17460
17661
 
17461
17662
  // src/beads/bd-adapter.ts
17462
17663
  var import_child_process13 = require("child_process");
17463
- var fs30 = __toESM(require("fs"));
17464
- var path36 = __toESM(require("path"));
17664
+ var fs31 = __toESM(require("fs"));
17665
+ var path37 = __toESM(require("path"));
17465
17666
  var BD_PACKAGE = "@beads/bd";
17466
17667
  function resolveBundledBdBinary() {
17467
17668
  return _resolveSeam.resolveBundled();
@@ -17473,11 +17674,11 @@ function _defaultResolveBundled() {
17473
17674
  } catch {
17474
17675
  return null;
17475
17676
  }
17476
- const binDir = path36.join(path36.dirname(pkgJsonPath), "bin");
17677
+ const binDir = path37.join(path37.dirname(pkgJsonPath), "bin");
17477
17678
  const binaryName = process.platform === "win32" ? "bd.exe" : "bd";
17478
- const binaryPath = path36.join(binDir, binaryName);
17679
+ const binaryPath = path37.join(binDir, binaryName);
17479
17680
  try {
17480
- fs30.accessSync(binaryPath, fs30.constants.F_OK);
17681
+ fs31.accessSync(binaryPath, fs31.constants.F_OK);
17481
17682
  return binaryPath;
17482
17683
  } catch {
17483
17684
  return null;
@@ -17487,13 +17688,13 @@ function resolveBdOnPath() {
17487
17688
  return _resolveSeam.resolveOnPath();
17488
17689
  }
17489
17690
  function _defaultResolveOnPath() {
17490
- const dirs = (process.env.PATH ?? "").split(path36.delimiter).filter(Boolean);
17691
+ const dirs = (process.env.PATH ?? "").split(path37.delimiter).filter(Boolean);
17491
17692
  const candidates = process.platform === "win32" ? ["bd.exe", "bd.cmd", "bd"] : ["bd"];
17492
17693
  for (const dir of dirs) {
17493
17694
  for (const candidate of candidates) {
17494
- const full = path36.join(dir, candidate);
17695
+ const full = path37.join(dir, candidate);
17495
17696
  try {
17496
- fs30.accessSync(full, fs30.constants.F_OK);
17697
+ fs31.accessSync(full, fs31.constants.F_OK);
17497
17698
  return full;
17498
17699
  } catch {
17499
17700
  }
@@ -17660,9 +17861,9 @@ function coerceIssue(row, projectKey) {
17660
17861
 
17661
17862
  // src/beads/provisioner.ts
17662
17863
  var import_child_process17 = require("child_process");
17663
- var fs33 = __toESM(require("fs"));
17864
+ var fs34 = __toESM(require("fs"));
17664
17865
  var os26 = __toESM(require("os"));
17665
- var path39 = __toESM(require("path"));
17866
+ var path40 = __toESM(require("path"));
17666
17867
 
17667
17868
  // src/beads/install-bd.ts
17668
17869
  var import_child_process14 = require("child_process");
@@ -17727,9 +17928,9 @@ async function installBd(platform2 = process.platform) {
17727
17928
 
17728
17929
  // src/beads/install-dolt.ts
17729
17930
  var import_child_process15 = require("child_process");
17730
- var fs31 = __toESM(require("fs"));
17931
+ var fs32 = __toESM(require("fs"));
17731
17932
  var os25 = __toESM(require("os"));
17732
- var path37 = __toESM(require("path"));
17933
+ var path38 = __toESM(require("path"));
17733
17934
  var DOLT_INSTALL_SH_URL = "https://github.com/dolthub/dolt/releases/latest/download/install.sh";
17734
17935
  var DOLT_MSI_URL = "https://github.com/dolthub/dolt/releases/latest/download/dolt-windows-amd64.msi";
17735
17936
  function resolveDoltInstallStrategy(platform2) {
@@ -17825,7 +18026,7 @@ var _doltPathSeam = {
17825
18026
  },
17826
18027
  exists: (p2) => {
17827
18028
  try {
17828
- fs31.accessSync(p2, fs31.constants.F_OK);
18029
+ fs32.accessSync(p2, fs32.constants.F_OK);
17829
18030
  return true;
17830
18031
  } catch {
17831
18032
  return false;
@@ -17836,7 +18037,7 @@ function doltBinaryNames(platform2) {
17836
18037
  return platform2 === "win32" ? ["dolt.exe", "dolt.cmd", "dolt"] : ["dolt"];
17837
18038
  }
17838
18039
  function knownDoltDirs(platform2) {
17839
- const P3 = platform2 === "win32" ? path37.win32 : path37.posix;
18040
+ const P3 = platform2 === "win32" ? path38.win32 : path38.posix;
17840
18041
  const home = _doltPathSeam.homedir();
17841
18042
  if (platform2 === "win32") {
17842
18043
  return [
@@ -17852,7 +18053,7 @@ function knownDoltDirs(platform2) {
17852
18053
  ].filter(Boolean);
17853
18054
  }
17854
18055
  function ensureDoltResolvable(platform2 = process.platform) {
17855
- const P3 = platform2 === "win32" ? path37.win32 : path37.posix;
18056
+ const P3 = platform2 === "win32" ? path38.win32 : path38.posix;
17856
18057
  const delim = platform2 === "win32" ? ";" : ":";
17857
18058
  const names = doltBinaryNames(platform2);
17858
18059
  const pathDirs = _doltPathSeam.getPath().split(delim).filter(Boolean);
@@ -17963,8 +18164,8 @@ async function ensureSharedServer(adapter) {
17963
18164
  // src/beads/project-key.ts
17964
18165
  var import_child_process16 = require("child_process");
17965
18166
  var crypto2 = __toESM(require("crypto"));
17966
- var fs32 = __toESM(require("fs"));
17967
- var path38 = __toESM(require("path"));
18167
+ var fs33 = __toESM(require("fs"));
18168
+ var path39 = __toESM(require("path"));
17968
18169
  function normalizeOrigin(raw) {
17969
18170
  const trimmed = raw.trim();
17970
18171
  if (!trimmed) return null;
@@ -17990,17 +18191,17 @@ function normalizeOrigin(raw) {
17990
18191
  return `${host}/${pathPart}`;
17991
18192
  }
17992
18193
  function findRepoRoot(cwd) {
17993
- let dir = path38.resolve(cwd);
18194
+ let dir = path39.resolve(cwd);
17994
18195
  const seen = /* @__PURE__ */ new Set();
17995
18196
  for (let i = 0; i < 256; i++) {
17996
18197
  if (seen.has(dir)) return null;
17997
18198
  seen.add(dir);
17998
18199
  try {
17999
- const stat3 = fs32.statSync(path38.join(dir, ".git"), { throwIfNoEntry: false });
18200
+ const stat3 = fs33.statSync(path39.join(dir, ".git"), { throwIfNoEntry: false });
18000
18201
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
18001
18202
  } catch {
18002
18203
  }
18003
- const parent = path38.dirname(dir);
18204
+ const parent = path39.dirname(dir);
18004
18205
  if (parent === dir) return null;
18005
18206
  dir = parent;
18006
18207
  }
@@ -18011,7 +18212,7 @@ var _execSeam2 = {
18011
18212
  const out2 = (0, import_child_process16.execFileSync)(file, args2, opts);
18012
18213
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
18013
18214
  },
18014
- realpath: (p2) => fs32.realpathSync(p2)
18215
+ realpath: (p2) => fs33.realpathSync(p2)
18015
18216
  };
18016
18217
  function readOrigin(cwd) {
18017
18218
  try {
@@ -18040,7 +18241,7 @@ function deriveProjectIdentity(cwd = process.cwd()) {
18040
18241
  } catch {
18041
18242
  }
18042
18243
  const hash = crypto2.createHash("sha256").update(real).digest("hex");
18043
- return { projectKey: `path:${hash}`, projectLabel: path38.basename(real) || "project" };
18244
+ return { projectKey: `path:${hash}`, projectLabel: path39.basename(real) || "project" };
18044
18245
  }
18045
18246
 
18046
18247
  // src/beads/project-prefix.ts
@@ -18085,14 +18286,14 @@ var _linkSeam = {
18085
18286
  homedir: () => os26.homedir(),
18086
18287
  isWritableDir: (dir) => {
18087
18288
  try {
18088
- fs33.accessSync(dir, fs33.constants.W_OK);
18289
+ fs34.accessSync(dir, fs34.constants.W_OK);
18089
18290
  return true;
18090
18291
  } catch {
18091
18292
  return false;
18092
18293
  }
18093
18294
  },
18094
18295
  ensureDir: (dir) => {
18095
- fs33.mkdirSync(dir, { recursive: true });
18296
+ fs34.mkdirSync(dir, { recursive: true });
18096
18297
  },
18097
18298
  /**
18098
18299
  * A directory to symlink `bd` into so the AGENT's shell + Claude Code's
@@ -18113,9 +18314,9 @@ var _linkSeam = {
18113
18314
  * which `linkBdOntoPath` creates if missing.
18114
18315
  */
18115
18316
  cliBinDir: () => {
18116
- const pathDirs = (process.env.PATH ?? "").split(path39.delimiter).filter(Boolean);
18317
+ const pathDirs = (process.env.PATH ?? "").split(path40.delimiter).filter(Boolean);
18117
18318
  const home = _linkSeam.homedir();
18118
- const localBin = home ? path39.join(home, ".local", "bin") : null;
18319
+ const localBin = home ? path40.join(home, ".local", "bin") : null;
18119
18320
  if (localBin) {
18120
18321
  try {
18121
18322
  _linkSeam.ensureDir(localBin);
@@ -18125,16 +18326,16 @@ var _linkSeam = {
18125
18326
  const candidates = [];
18126
18327
  if (localBin) candidates.push(localBin);
18127
18328
  try {
18128
- candidates.push(path39.dirname(process.execPath));
18329
+ candidates.push(path40.dirname(process.execPath));
18129
18330
  } catch {
18130
18331
  }
18131
18332
  candidates.push("/usr/local/bin");
18132
18333
  const entry = process.argv[1];
18133
18334
  if (entry) {
18134
18335
  try {
18135
- candidates.push(path39.dirname(fs33.realpathSync(entry)));
18336
+ candidates.push(path40.dirname(fs34.realpathSync(entry)));
18136
18337
  } catch {
18137
- candidates.push(path39.dirname(entry));
18338
+ candidates.push(path40.dirname(entry));
18138
18339
  }
18139
18340
  }
18140
18341
  const onPathWritable = candidates.find(
@@ -18146,20 +18347,20 @@ var _linkSeam = {
18146
18347
  /** Current symlink target at `linkPath`, or null when absent / not a link. */
18147
18348
  readlink: (linkPath) => {
18148
18349
  try {
18149
- return fs33.readlinkSync(linkPath);
18350
+ return fs34.readlinkSync(linkPath);
18150
18351
  } catch {
18151
18352
  return null;
18152
18353
  }
18153
18354
  },
18154
- unlink: (linkPath) => fs33.unlinkSync(linkPath),
18155
- symlink: (target, linkPath) => fs33.symlinkSync(target, linkPath)
18355
+ unlink: (linkPath) => fs34.unlinkSync(linkPath),
18356
+ symlink: (target, linkPath) => fs34.symlinkSync(target, linkPath)
18156
18357
  };
18157
18358
  function linkBdOntoPath(binaryPath) {
18158
18359
  if (_linkSeam.platform() === "win32") return;
18159
18360
  const binDir = _linkSeam.cliBinDir();
18160
18361
  if (!binDir) return;
18161
18362
  _linkSeam.ensureDir(binDir);
18162
- const linkPath = path39.join(binDir, "bd");
18363
+ const linkPath = path40.join(binDir, "bd");
18163
18364
  if (linkPath === binaryPath) return;
18164
18365
  const current = _linkSeam.readlink(linkPath);
18165
18366
  if (current === binaryPath) return;
@@ -18300,7 +18501,7 @@ function dedupeRecipes(agents) {
18300
18501
 
18301
18502
  // src/beads/watcher.ts
18302
18503
  var crypto4 = __toESM(require("crypto"));
18303
- var path40 = __toESM(require("path"));
18504
+ var path41 = __toESM(require("path"));
18304
18505
 
18305
18506
  // src/services/file-watcher/transport.ts
18306
18507
  var http5 = __toESM(require("http"));
@@ -18375,7 +18576,7 @@ var BeadsWatcher = class {
18375
18576
  constructor(opts) {
18376
18577
  this.opts = opts;
18377
18578
  this.bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
18378
- this.feedPath = opts.feedPath ?? path40.join(opts.cwd ?? process.cwd(), ".beads", "last-touched");
18579
+ this.feedPath = opts.feedPath ?? path41.join(opts.cwd ?? process.cwd(), ".beads", "last-touched");
18379
18580
  this.apiBase = opts.apiBaseUrl ?? API_BASE4;
18380
18581
  }
18381
18582
  opts;
@@ -18699,7 +18900,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
18699
18900
  function cleanupAttachmentTempFiles() {
18700
18901
  for (const p2 of pendingAttachmentFiles) {
18701
18902
  try {
18702
- fs34.unlinkSync(p2);
18903
+ fs35.unlinkSync(p2);
18703
18904
  } catch {
18704
18905
  }
18705
18906
  }
@@ -18708,8 +18909,8 @@ function cleanupAttachmentTempFiles() {
18708
18909
  function saveFilesTemp(files) {
18709
18910
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
18710
18911
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
18711
- const tmpPath = path41.join(os27.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
18712
- fs34.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
18912
+ const tmpPath = path42.join(os27.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
18913
+ fs35.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
18713
18914
  pendingAttachmentFiles.add(tmpPath);
18714
18915
  return tmpPath;
18715
18916
  });
@@ -18729,7 +18930,7 @@ var startTask = (ctx, _cmd, parsed) => {
18729
18930
  setTimeout(() => {
18730
18931
  for (const p2 of paths) {
18731
18932
  try {
18732
- fs34.unlinkSync(p2);
18933
+ fs35.unlinkSync(p2);
18733
18934
  } catch {
18734
18935
  }
18735
18936
  pendingAttachmentFiles.delete(p2);
@@ -19334,8 +19535,8 @@ function normalizeDetectionForSpawn(detection, cwd) {
19334
19535
  if (args2.length === 0) return detection;
19335
19536
  const binName = args2[0];
19336
19537
  if (binName.startsWith("-")) return detection;
19337
- const binPath = path41.join(cwd, "node_modules", ".bin", binName);
19338
- if (!fs34.existsSync(binPath)) return detection;
19538
+ const binPath = path42.join(cwd, "node_modules", ".bin", binName);
19539
+ if (!fs35.existsSync(binPath)) return detection;
19339
19540
  return {
19340
19541
  ...detection,
19341
19542
  command: binPath,
@@ -19772,9 +19973,9 @@ async function dispatchCommand(ctx, cmd) {
19772
19973
 
19773
19974
  // src/services/file-watcher.service.ts
19774
19975
  var import_child_process19 = require("child_process");
19775
- var fs35 = __toESM(require("fs"));
19976
+ var fs36 = __toESM(require("fs"));
19776
19977
  var os28 = __toESM(require("os"));
19777
- var path42 = __toESM(require("path"));
19978
+ var path43 = __toESM(require("path"));
19778
19979
  var import_ignore = __toESM(require("ignore"));
19779
19980
 
19780
19981
  // src/services/file-watcher/diff-parser.ts
@@ -19917,18 +20118,18 @@ var _findGitRootSeam = {
19917
20118
  resolve: _defaultFindGitRoot
19918
20119
  };
19919
20120
  function _defaultFindGitRoot(startDir) {
19920
- let dir = path42.resolve(startDir);
20121
+ let dir = path43.resolve(startDir);
19921
20122
  const seen = /* @__PURE__ */ new Set();
19922
20123
  for (let i = 0; i < 256; i++) {
19923
20124
  if (seen.has(dir)) return null;
19924
20125
  seen.add(dir);
19925
20126
  try {
19926
- const gitPath = path42.join(dir, ".git");
19927
- const stat3 = fs35.statSync(gitPath, { throwIfNoEntry: false });
20127
+ const gitPath = path43.join(dir, ".git");
20128
+ const stat3 = fs36.statSync(gitPath, { throwIfNoEntry: false });
19928
20129
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
19929
20130
  } catch {
19930
20131
  }
19931
- const parent = path42.dirname(dir);
20132
+ const parent = path43.dirname(dir);
19932
20133
  if (parent === dir) return null;
19933
20134
  dir = parent;
19934
20135
  }
@@ -20173,7 +20374,7 @@ var FileWatcherService = class {
20173
20374
  }
20174
20375
  async emitForFile(absPath, changeType) {
20175
20376
  if (this.stopped) return;
20176
- const fileDir = path42.dirname(absPath);
20377
+ const fileDir = path43.dirname(absPath);
20177
20378
  let gitRoot = this.gitRootByDir.get(fileDir);
20178
20379
  if (gitRoot === void 0) {
20179
20380
  gitRoot = findGitRoot2(fileDir);
@@ -20186,19 +20387,19 @@ var FileWatcherService = class {
20186
20387
  );
20187
20388
  return;
20188
20389
  }
20189
- const relPathInRepo = path42.relative(gitRoot, absPath);
20390
+ const relPathInRepo = path43.relative(gitRoot, absPath);
20190
20391
  if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
20191
20392
  const matcher = this.getGitIgnoreMatcher(gitRoot);
20192
20393
  if (matcher && matcher.ignores(relPathInRepo)) {
20193
20394
  log.trace(
20194
20395
  "fileWatcher",
20195
- `${relPathInRepo} ignored by ${path42.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
20396
+ `${relPathInRepo} ignored by ${path43.basename(gitRoot)}/.gitignore \u2014 suppressing emit`
20196
20397
  );
20197
20398
  return;
20198
20399
  }
20199
20400
  this.opts.onRepoDirty?.(gitRoot);
20200
- const repoPath = path42.relative(this.opts.workingDir, gitRoot);
20201
- const repoName = path42.basename(gitRoot);
20401
+ const repoPath = path43.relative(this.opts.workingDir, gitRoot);
20402
+ const repoName = path43.basename(gitRoot);
20202
20403
  let diffText = "";
20203
20404
  let fileStatus = "modified";
20204
20405
  if (changeType === "unlink") {
@@ -20373,7 +20574,7 @@ var FileWatcherService = class {
20373
20574
  collectGitignoreFiles(repoRoot, dir, matcher) {
20374
20575
  let entries;
20375
20576
  try {
20376
- entries = fs35.readdirSync(dir, { withFileTypes: true });
20577
+ entries = fs36.readdirSync(dir, { withFileTypes: true });
20377
20578
  } catch {
20378
20579
  return;
20379
20580
  }
@@ -20382,16 +20583,16 @@ var FileWatcherService = class {
20382
20583
  );
20383
20584
  if (gitignoreEntry) {
20384
20585
  try {
20385
- const body = fs35.readFileSync(path42.join(dir, ".gitignore"), "utf8");
20386
- const rel = path42.relative(repoRoot, dir).replace(/\\/g, "/");
20586
+ const body = fs36.readFileSync(path43.join(dir, ".gitignore"), "utf8");
20587
+ const rel = path43.relative(repoRoot, dir).replace(/\\/g, "/");
20387
20588
  const prefixed = body.split(/\r?\n/).map((line) => {
20388
20589
  const trimmed = line.trim();
20389
20590
  if (!trimmed || trimmed.startsWith("#")) return line;
20390
20591
  if (!rel) return line;
20391
20592
  if (trimmed.startsWith("!")) {
20392
- return "!" + path42.posix.join(rel, trimmed.slice(1));
20593
+ return "!" + path43.posix.join(rel, trimmed.slice(1));
20393
20594
  }
20394
- return path42.posix.join(rel, trimmed);
20595
+ return path43.posix.join(rel, trimmed);
20395
20596
  }).join("\n");
20396
20597
  matcher.add(prefixed);
20397
20598
  } catch {
@@ -20400,7 +20601,7 @@ var FileWatcherService = class {
20400
20601
  for (const entry of entries) {
20401
20602
  if (!entry.isDirectory()) continue;
20402
20603
  if (entry.name === ".git") continue;
20403
- const childAbs = path42.join(dir, entry.name);
20604
+ const childAbs = path43.join(dir, entry.name);
20404
20605
  if (isIgnoredFilePath(childAbs)) continue;
20405
20606
  this.collectGitignoreFiles(repoRoot, childAbs, matcher);
20406
20607
  }
@@ -20570,7 +20771,7 @@ var import_crypto4 = require("crypto");
20570
20771
 
20571
20772
  // src/services/turn-files/git-changeset.ts
20572
20773
  var import_child_process20 = require("child_process");
20573
- var path43 = __toESM(require("path"));
20774
+ var path44 = __toESM(require("path"));
20574
20775
  async function collectRepoChangeset(opts) {
20575
20776
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
20576
20777
  if (status2 === null) return null;
@@ -20681,7 +20882,7 @@ function defaultRunGit(cwd, args2) {
20681
20882
  });
20682
20883
  }
20683
20884
  async function discoverRepos(workingDir, maxDepth = 4) {
20684
- const fs41 = await import("fs/promises");
20885
+ const fs42 = await import("fs/promises");
20685
20886
  const out2 = [];
20686
20887
  await walk(workingDir, 0);
20687
20888
  return out2;
@@ -20689,7 +20890,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
20689
20890
  if (depth > maxDepth) return;
20690
20891
  let entries = [];
20691
20892
  try {
20692
- const dirents = await fs41.readdir(dir, { withFileTypes: true });
20893
+ const dirents = await fs42.readdir(dir, { withFileTypes: true });
20693
20894
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
20694
20895
  } catch {
20695
20896
  return;
@@ -20700,8 +20901,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
20700
20901
  if (hasGit) {
20701
20902
  out2.push({
20702
20903
  repoRoot: dir,
20703
- repoPath: path43.relative(workingDir, dir),
20704
- repoName: path43.basename(dir)
20904
+ repoPath: path44.relative(workingDir, dir),
20905
+ repoName: path44.basename(dir)
20705
20906
  });
20706
20907
  return;
20707
20908
  }
@@ -20709,14 +20910,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
20709
20910
  if (!entry.isDirectory) continue;
20710
20911
  if (entry.name === "node_modules") continue;
20711
20912
  if (entry.name === "dist" || entry.name === "build") continue;
20712
- await walk(path43.join(dir, entry.name), depth + 1);
20913
+ await walk(path44.join(dir, entry.name), depth + 1);
20713
20914
  }
20714
20915
  }
20715
20916
  }
20716
20917
 
20717
20918
  // src/services/turn-files/files-outbox.ts
20718
- var fs36 = __toESM(require("fs/promises"));
20719
- var path44 = __toESM(require("path"));
20919
+ var fs37 = __toESM(require("fs/promises"));
20920
+ var path45 = __toESM(require("path"));
20720
20921
  var import_os7 = require("os");
20721
20922
  var HOME_OUTBOX_DIR = ".codeam/outbox";
20722
20923
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -20749,16 +20950,16 @@ var FilesOutbox = class {
20749
20950
  backoffIndex = 0;
20750
20951
  stopped = false;
20751
20952
  constructor(opts) {
20752
- const base = opts.baseDir ?? path44.join(homeDir(), HOME_OUTBOX_DIR);
20753
- this.filePath = path44.join(base, `${opts.sessionId}.jsonl`);
20953
+ const base = opts.baseDir ?? path45.join(homeDir(), HOME_OUTBOX_DIR);
20954
+ this.filePath = path45.join(base, `${opts.sessionId}.jsonl`);
20754
20955
  this.post = opts.post;
20755
20956
  this.autoSchedule = opts.autoSchedule !== false;
20756
20957
  }
20757
20958
  /** Persist the entry to disk and trigger a flush. Returns once the
20758
20959
  * line is durable on disk (not once the POST succeeds). */
20759
20960
  async enqueue(entry) {
20760
- await fs36.mkdir(path44.dirname(this.filePath), { recursive: true });
20761
- await fs36.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
20961
+ await fs37.mkdir(path45.dirname(this.filePath), { recursive: true });
20962
+ await fs37.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
20762
20963
  this.backoffIndex = 0;
20763
20964
  if (this.autoSchedule) this.scheduleFlush(0);
20764
20965
  }
@@ -20847,7 +21048,7 @@ var FilesOutbox = class {
20847
21048
  async readAll() {
20848
21049
  let raw = "";
20849
21050
  try {
20850
- raw = await fs36.readFile(this.filePath, "utf8");
21051
+ raw = await fs37.readFile(this.filePath, "utf8");
20851
21052
  } catch {
20852
21053
  return [];
20853
21054
  }
@@ -20871,12 +21072,12 @@ var FilesOutbox = class {
20871
21072
  async rewrite(entries) {
20872
21073
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
20873
21074
  if (entries.length === 0) {
20874
- await fs36.unlink(this.filePath).catch(() => void 0);
21075
+ await fs37.unlink(this.filePath).catch(() => void 0);
20875
21076
  return;
20876
21077
  }
20877
21078
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
20878
- await fs36.writeFile(tmpPath, payload, "utf8");
20879
- await fs36.rename(tmpPath, this.filePath);
21079
+ await fs37.writeFile(tmpPath, payload, "utf8");
21080
+ await fs37.rename(tmpPath, this.filePath);
20880
21081
  }
20881
21082
  };
20882
21083
  function applyJitter(ms) {
@@ -21974,10 +22175,10 @@ var ChromeStepTracker = class {
21974
22175
  const visible = lines.map((l) => parseLine2(l)).filter((s) => s !== null);
21975
22176
  if (visible.length === 0) return;
21976
22177
  for (const step of visible) {
21977
- const exists = this.history.some(
22178
+ const exists2 = this.history.some(
21978
22179
  (s) => s.tool === step.tool && s.label === step.label
21979
22180
  );
21980
- if (!exists) this.history.push(step);
22181
+ if (!exists2) this.history.push(step);
21981
22182
  }
21982
22183
  }
21983
22184
  /**
@@ -22722,8 +22923,8 @@ var OutputService = class _OutputService {
22722
22923
  };
22723
22924
 
22724
22925
  // src/services/history.service.ts
22725
- var fs37 = __toESM(require("fs"));
22726
- var path45 = __toESM(require("path"));
22926
+ var fs38 = __toESM(require("fs"));
22927
+ var path46 = __toESM(require("path"));
22727
22928
  var os29 = __toESM(require("os"));
22728
22929
  var https7 = __toESM(require("https"));
22729
22930
  var http7 = __toESM(require("http"));
@@ -22751,7 +22952,7 @@ function parseJsonl(filePath) {
22751
22952
  const messages = [];
22752
22953
  let raw;
22753
22954
  try {
22754
- raw = fs37.readFileSync(filePath, "utf8");
22955
+ raw = fs38.readFileSync(filePath, "utf8");
22755
22956
  } catch (err) {
22756
22957
  if (err.code !== "ENOENT") {
22757
22958
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -22886,7 +23087,7 @@ var HistoryService = class _HistoryService {
22886
23087
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
22887
23088
  }
22888
23089
  get projectDir() {
22889
- return this.runtime.resolveHistoryDir(this.cwd) ?? path45.join(os29.homedir(), ".claude", "projects", encodeCwd(this.cwd));
23090
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path46.join(os29.homedir(), ".claude", "projects", encodeCwd(this.cwd));
22890
23091
  }
22891
23092
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
22892
23093
  setCurrentConversationId(id) {
@@ -22898,7 +23099,7 @@ var HistoryService = class _HistoryService {
22898
23099
  /** Return the current message count in the active conversation. */
22899
23100
  getCurrentMessageCount() {
22900
23101
  if (!this.currentConversationId) return 0;
22901
- const filePath = path45.join(this.projectDir, `${this.currentConversationId}.jsonl`);
23102
+ const filePath = path46.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22902
23103
  return parseJsonl(filePath).length;
22903
23104
  }
22904
23105
  /**
@@ -22909,7 +23110,7 @@ var HistoryService = class _HistoryService {
22909
23110
  const deadline = Date.now() + timeoutMs;
22910
23111
  while (Date.now() < deadline) {
22911
23112
  if (!this.currentConversationId) return null;
22912
- const filePath = path45.join(this.projectDir, `${this.currentConversationId}.jsonl`);
23113
+ const filePath = path46.join(this.projectDir, `${this.currentConversationId}.jsonl`);
22913
23114
  const messages = parseJsonl(filePath);
22914
23115
  if (messages.length > previousCount) {
22915
23116
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -22935,16 +23136,16 @@ var HistoryService = class _HistoryService {
22935
23136
  const dir = this.projectDir;
22936
23137
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
22937
23138
  try {
22938
- const files = fs37.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
23139
+ const files = fs38.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22939
23140
  try {
22940
- const stat3 = fs37.statSync(path45.join(dir, e.name));
23141
+ const stat3 = fs38.statSync(path46.join(dir, e.name));
22941
23142
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
22942
23143
  } catch {
22943
23144
  return { name: e.name, mtime: 0, birthtime: 0 };
22944
23145
  }
22945
23146
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
22946
23147
  if (files.length > 0) {
22947
- this.currentConversationId = path45.basename(files[0].name, ".jsonl");
23148
+ this.currentConversationId = path46.basename(files[0].name, ".jsonl");
22948
23149
  }
22949
23150
  } catch {
22950
23151
  }
@@ -22978,13 +23179,13 @@ var HistoryService = class _HistoryService {
22978
23179
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
22979
23180
  let entries;
22980
23181
  try {
22981
- entries = fs37.readdirSync(dir, { withFileTypes: true });
23182
+ entries = fs38.readdirSync(dir, { withFileTypes: true });
22982
23183
  } catch {
22983
23184
  return null;
22984
23185
  }
22985
23186
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
22986
23187
  try {
22987
- const stat3 = fs37.statSync(path45.join(dir, e.name));
23188
+ const stat3 = fs38.statSync(path46.join(dir, e.name));
22988
23189
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
22989
23190
  } catch {
22990
23191
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -22993,12 +23194,12 @@ var HistoryService = class _HistoryService {
22993
23194
  if (files.length === 0) return null;
22994
23195
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
22995
23196
  if (!files.some((f) => f.name === targetFile)) return null;
22996
- return this.extractUsageFromFile(path45.join(dir, targetFile));
23197
+ return this.extractUsageFromFile(path46.join(dir, targetFile));
22997
23198
  }
22998
23199
  extractUsageFromFile(filePath) {
22999
23200
  let raw;
23000
23201
  try {
23001
- raw = fs37.readFileSync(filePath, "utf8");
23202
+ raw = fs38.readFileSync(filePath, "utf8");
23002
23203
  } catch {
23003
23204
  return null;
23004
23205
  }
@@ -23043,9 +23244,9 @@ var HistoryService = class _HistoryService {
23043
23244
  let totalCost = 0;
23044
23245
  let files;
23045
23246
  try {
23046
- files = fs37.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
23247
+ files = fs38.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
23047
23248
  try {
23048
- return fs37.statSync(path45.join(projectDir, f)).mtimeMs >= monthStartMs;
23249
+ return fs38.statSync(path46.join(projectDir, f)).mtimeMs >= monthStartMs;
23049
23250
  } catch {
23050
23251
  return false;
23051
23252
  }
@@ -23056,7 +23257,7 @@ var HistoryService = class _HistoryService {
23056
23257
  for (const file of files) {
23057
23258
  let raw;
23058
23259
  try {
23059
- raw = fs37.readFileSync(path45.join(projectDir, file), "utf8");
23260
+ raw = fs38.readFileSync(path46.join(projectDir, file), "utf8");
23060
23261
  } catch {
23061
23262
  continue;
23062
23263
  }
@@ -23120,7 +23321,7 @@ var HistoryService = class _HistoryService {
23120
23321
  * showing an empty conversation.
23121
23322
  */
23122
23323
  async loadConversation(sessionId) {
23123
- const filePath = path45.join(this.projectDir, `${sessionId}.jsonl`);
23324
+ const filePath = path46.join(this.projectDir, `${sessionId}.jsonl`);
23124
23325
  const messages = parseJsonl(filePath);
23125
23326
  if (messages.length === 0) return;
23126
23327
  const totalBatches = Math.ceil(messages.length / CONVERSATION_BATCH_SIZE);
@@ -23174,7 +23375,7 @@ var HistoryService = class _HistoryService {
23174
23375
  if (!this.currentConversationId) return 0;
23175
23376
  }
23176
23377
  const sessionId = this.currentConversationId;
23177
- const filePath = path45.join(this.projectDir, `${sessionId}.jsonl`);
23378
+ const filePath = path46.join(this.projectDir, `${sessionId}.jsonl`);
23178
23379
  const messages = parseJsonl(filePath);
23179
23380
  if (messages.length === 0) return 0;
23180
23381
  const marker = this.lastUploadedUuid.get(sessionId);
@@ -23710,17 +23911,21 @@ async function start(requestedAgent) {
23710
23911
  beads = started;
23711
23912
  return started;
23712
23913
  });
23914
+ const depsReady = process.env.CODESPACES === "true" ? provisionProjectDependencies(cwd).catch(() => void 0) : Promise.resolve();
23713
23915
  if (process.env.CODESPACES === "true") {
23714
- const BEADS_GATE_TIMEOUT_MS = 6e4;
23916
+ const GATE_TIMEOUT_MS = 24e4;
23715
23917
  let gateTimer;
23716
23918
  await Promise.race([
23717
- beadsReady.catch(() => null),
23919
+ Promise.all([beadsReady.catch(() => null), depsReady]),
23718
23920
  new Promise((resolve7) => {
23719
- gateTimer = setTimeout(resolve7, BEADS_GATE_TIMEOUT_MS);
23921
+ gateTimer = setTimeout(resolve7, GATE_TIMEOUT_MS);
23720
23922
  })
23721
23923
  ]);
23722
23924
  if (gateTimer) clearTimeout(gateTimer);
23723
- log.info("beads", `agent-spawn gate released \u2014 beads ${beads ? "ready" : "not ready (timed out)"}`);
23925
+ log.info(
23926
+ "beads",
23927
+ `agent-spawn gate released \u2014 beads ${beads ? "ready" : "pending"}; project deps provisioned`
23928
+ );
23724
23929
  }
23725
23930
  const acpDisabled = process.env.CODEAM_ACP_DISABLED === "1";
23726
23931
  if (!acpDisabled && session.pluginAuthToken) {
@@ -24107,9 +24312,9 @@ async function autoLinkAfterPair(opts) {
24107
24312
  }
24108
24313
 
24109
24314
  // src/commands/pair-auto.ts
24110
- var fs38 = __toESM(require("fs"));
24315
+ var fs39 = __toESM(require("fs"));
24111
24316
  var os30 = __toESM(require("os"));
24112
- var path46 = __toESM(require("path"));
24317
+ var path47 = __toESM(require("path"));
24113
24318
  var import_crypto7 = require("crypto");
24114
24319
 
24115
24320
  // src/commands/start-infra-only.ts
@@ -24304,12 +24509,12 @@ function readTokenFromArgs(args2) {
24304
24509
  }
24305
24510
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
24306
24511
  if (fileFlag) {
24307
- const path53 = fileFlag.slice("--token-file=".length);
24512
+ const path54 = fileFlag.slice("--token-file=".length);
24308
24513
  try {
24309
- const content = fs38.readFileSync(path53, "utf8").trim();
24310
- if (content.length === 0) fail(`--token-file ${path53} is empty`);
24514
+ const content = fs39.readFileSync(path54, "utf8").trim();
24515
+ if (content.length === 0) fail(`--token-file ${path54} is empty`);
24311
24516
  try {
24312
- fs38.unlinkSync(path53);
24517
+ fs39.unlinkSync(path54);
24313
24518
  } catch {
24314
24519
  }
24315
24520
  return content;
@@ -24392,7 +24597,7 @@ async function claim(token, pluginId) {
24392
24597
  }
24393
24598
  }
24394
24599
  function pairAutoLockPath() {
24395
- return path46.join(os30.homedir(), ".codeam", "pair-auto.lock");
24600
+ return path47.join(os30.homedir(), ".codeam", "pair-auto.lock");
24396
24601
  }
24397
24602
  function isLivePairAuto(pid) {
24398
24603
  if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
@@ -24402,7 +24607,7 @@ function isLivePairAuto(pid) {
24402
24607
  if (e.code !== "EPERM") return false;
24403
24608
  }
24404
24609
  try {
24405
- return fs38.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
24610
+ return fs39.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
24406
24611
  } catch {
24407
24612
  return true;
24408
24613
  }
@@ -24410,19 +24615,19 @@ function isLivePairAuto(pid) {
24410
24615
  function acquireSingletonLock() {
24411
24616
  const lockPath = pairAutoLockPath();
24412
24617
  try {
24413
- fs38.mkdirSync(path46.dirname(lockPath), { recursive: true });
24618
+ fs39.mkdirSync(path47.dirname(lockPath), { recursive: true });
24414
24619
  try {
24415
- fs38.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
24620
+ fs39.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
24416
24621
  } catch (e) {
24417
24622
  if (e.code !== "EEXIST") throw e;
24418
- const holder = Number(fs38.readFileSync(lockPath, "utf8").trim());
24623
+ const holder = Number(fs39.readFileSync(lockPath, "utf8").trim());
24419
24624
  if (isLivePairAuto(holder)) return false;
24420
- fs38.writeFileSync(lockPath, String(process.pid));
24625
+ fs39.writeFileSync(lockPath, String(process.pid));
24421
24626
  }
24422
24627
  process.once("exit", () => {
24423
24628
  try {
24424
- if (fs38.existsSync(lockPath) && Number(fs38.readFileSync(lockPath, "utf8").trim()) === process.pid) {
24425
- fs38.unlinkSync(lockPath);
24629
+ if (fs39.existsSync(lockPath) && Number(fs39.readFileSync(lockPath, "utf8").trim()) === process.pid) {
24630
+ fs39.unlinkSync(lockPath);
24426
24631
  }
24427
24632
  } catch {
24428
24633
  }
@@ -24621,7 +24826,7 @@ var import_picocolors10 = __toESM(require("picocolors"));
24621
24826
  var import_child_process22 = require("child_process");
24622
24827
  var import_util4 = require("util");
24623
24828
  var import_picocolors8 = __toESM(require("picocolors"));
24624
- var path47 = __toESM(require("path"));
24829
+ var path48 = __toESM(require("path"));
24625
24830
  var execFileP5 = (0, import_util4.promisify)(import_child_process22.execFile);
24626
24831
  var MAX_BUFFER = 8 * 1024 * 1024;
24627
24832
  function resetStdinForChild() {
@@ -25110,7 +25315,7 @@ var GitHubCodespacesProvider = class {
25110
25315
  });
25111
25316
  }
25112
25317
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
25113
- const remoteDir = path47.posix.dirname(remotePath);
25318
+ const remoteDir = path48.posix.dirname(remotePath);
25114
25319
  const parts = [
25115
25320
  `mkdir -p ${shellQuote(remoteDir)}`,
25116
25321
  `cat > ${shellQuote(remotePath)}`
@@ -25180,7 +25385,7 @@ function shellQuote(s) {
25180
25385
  // src/services/providers/gitpod.ts
25181
25386
  var import_child_process23 = require("child_process");
25182
25387
  var import_util5 = require("util");
25183
- var path48 = __toESM(require("path"));
25388
+ var path49 = __toESM(require("path"));
25184
25389
  var import_picocolors9 = __toESM(require("picocolors"));
25185
25390
  var execFileP6 = (0, import_util5.promisify)(import_child_process23.execFile);
25186
25391
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -25420,7 +25625,7 @@ var GitpodProvider = class {
25420
25625
  });
25421
25626
  }
25422
25627
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
25423
- const remoteDir = path48.posix.dirname(remotePath);
25628
+ const remoteDir = path49.posix.dirname(remotePath);
25424
25629
  const parts = [
25425
25630
  `mkdir -p ${shellQuote2(remoteDir)}`,
25426
25631
  `cat > ${shellQuote2(remotePath)}`
@@ -25456,7 +25661,7 @@ function shellQuote2(s) {
25456
25661
  // src/services/providers/gitlab-workspaces.ts
25457
25662
  var import_child_process24 = require("child_process");
25458
25663
  var import_util6 = require("util");
25459
- var path49 = __toESM(require("path"));
25664
+ var path50 = __toESM(require("path"));
25460
25665
  var execFileP7 = (0, import_util6.promisify)(import_child_process24.execFile);
25461
25666
  var MAX_BUFFER3 = 8 * 1024 * 1024;
25462
25667
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -25716,7 +25921,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
25716
25921
  }
25717
25922
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
25718
25923
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
25719
- const remoteDir = path49.posix.dirname(remotePath);
25924
+ const remoteDir = path50.posix.dirname(remotePath);
25720
25925
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
25721
25926
  if (options.mode != null) {
25722
25927
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -25784,7 +25989,7 @@ function shellQuote3(s) {
25784
25989
  // src/services/providers/railway.ts
25785
25990
  var import_child_process25 = require("child_process");
25786
25991
  var import_util7 = require("util");
25787
- var path50 = __toESM(require("path"));
25992
+ var path51 = __toESM(require("path"));
25788
25993
  var execFileP8 = (0, import_util7.promisify)(import_child_process25.execFile);
25789
25994
  var MAX_BUFFER4 = 8 * 1024 * 1024;
25790
25995
  function resetStdinForChild4() {
@@ -26020,7 +26225,7 @@ var RailwayProvider = class {
26020
26225
  if (!projectId || !serviceId) {
26021
26226
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
26022
26227
  }
26023
- const remoteDir = path50.posix.dirname(remotePath);
26228
+ const remoteDir = path51.posix.dirname(remotePath);
26024
26229
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
26025
26230
  if (options.mode != null) {
26026
26231
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -26563,8 +26768,8 @@ async function stopWorkspaceFromLocal(target) {
26563
26768
  var import_node_dns = require("dns");
26564
26769
  var import_node_util4 = require("util");
26565
26770
  var import_node_crypto8 = require("crypto");
26566
- var fs39 = __toESM(require("fs"));
26567
- var path51 = __toESM(require("path"));
26771
+ var fs40 = __toESM(require("fs"));
26772
+ var path52 = __toESM(require("path"));
26568
26773
  var import_picocolors12 = __toESM(require("picocolors"));
26569
26774
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
26570
26775
  async function checkDns(apiBase) {
@@ -26620,13 +26825,13 @@ async function checkHealth(apiBase) {
26620
26825
  }
26621
26826
  }
26622
26827
  function checkConfigDir() {
26623
- const dir = path51.join(require("os").homedir(), ".codeam");
26828
+ const dir = path52.join(require("os").homedir(), ".codeam");
26624
26829
  try {
26625
- fs39.mkdirSync(dir, { recursive: true, mode: 448 });
26626
- const probe = path51.join(dir, ".doctor-probe");
26627
- fs39.writeFileSync(probe, "ok", { mode: 384 });
26628
- const read = fs39.readFileSync(probe, "utf8");
26629
- fs39.unlinkSync(probe);
26830
+ fs40.mkdirSync(dir, { recursive: true, mode: 448 });
26831
+ const probe = path52.join(dir, ".doctor-probe");
26832
+ fs40.writeFileSync(probe, "ok", { mode: 384 });
26833
+ const read = fs40.readFileSync(probe, "utf8");
26834
+ fs40.unlinkSync(probe);
26630
26835
  if (read !== "ok") throw new Error("write/read round-trip mismatch");
26631
26836
  return {
26632
26837
  id: "config-dir",
@@ -26690,7 +26895,7 @@ function checkNodePty() {
26690
26895
  detail: "not required on this platform"
26691
26896
  };
26692
26897
  }
26693
- const vendoredPath = path51.join(__dirname, "vendor", "node-pty");
26898
+ const vendoredPath = path52.join(__dirname, "vendor", "node-pty");
26694
26899
  for (const target of [vendoredPath, "node-pty"]) {
26695
26900
  try {
26696
26901
  require(target);
@@ -26732,7 +26937,7 @@ function checkChokidar() {
26732
26937
  }
26733
26938
  async function doctor(args2 = []) {
26734
26939
  const json = args2.includes("--json");
26735
- const cliVersion = true ? "2.39.8" : "0.0.0-dev";
26940
+ const cliVersion = true ? "2.39.9" : "0.0.0-dev";
26736
26941
  const apiBase = resolveApiBaseUrl();
26737
26942
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
26738
26943
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -26931,7 +27136,7 @@ async function completion(args2) {
26931
27136
  // src/commands/version.ts
26932
27137
  var import_picocolors13 = __toESM(require("picocolors"));
26933
27138
  function version2() {
26934
- const v = true ? "2.39.8" : "unknown";
27139
+ const v = true ? "2.39.9" : "unknown";
26935
27140
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
26936
27141
  }
26937
27142
 
@@ -27059,9 +27264,9 @@ function tryShowSubcommandHelp(cmd, args2) {
27059
27264
  var _subcommandHelpKeys = Object.keys(HELPS);
27060
27265
 
27061
27266
  // src/lib/updateNotifier.ts
27062
- var fs40 = __toESM(require("fs"));
27267
+ var fs41 = __toESM(require("fs"));
27063
27268
  var os31 = __toESM(require("os"));
27064
- var path52 = __toESM(require("path"));
27269
+ var path53 = __toESM(require("path"));
27065
27270
  var https8 = __toESM(require("https"));
27066
27271
  var import_node_child_process12 = require("child_process");
27067
27272
  var import_picocolors16 = __toESM(require("picocolors"));
@@ -27070,12 +27275,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
27070
27275
  var TTL_MS = 24 * 60 * 60 * 1e3;
27071
27276
  var REQUEST_TIMEOUT_MS = 1500;
27072
27277
  function cachePath() {
27073
- const dir = path52.join(os31.homedir(), ".codeam");
27074
- return path52.join(dir, "update-check.json");
27278
+ const dir = path53.join(os31.homedir(), ".codeam");
27279
+ return path53.join(dir, "update-check.json");
27075
27280
  }
27076
27281
  function readCache() {
27077
27282
  try {
27078
- const raw = fs40.readFileSync(cachePath(), "utf8");
27283
+ const raw = fs41.readFileSync(cachePath(), "utf8");
27079
27284
  const parsed = JSON.parse(raw);
27080
27285
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
27081
27286
  return parsed;
@@ -27086,10 +27291,10 @@ function readCache() {
27086
27291
  function writeCache(cache) {
27087
27292
  try {
27088
27293
  const file = cachePath();
27089
- fs40.mkdirSync(path52.dirname(file), { recursive: true });
27294
+ fs41.mkdirSync(path53.dirname(file), { recursive: true });
27090
27295
  const tmp = `${file}.${process.pid}.tmp`;
27091
- fs40.writeFileSync(tmp, JSON.stringify(cache));
27092
- fs40.renameSync(tmp, file);
27296
+ fs41.writeFileSync(tmp, JSON.stringify(cache));
27297
+ fs41.renameSync(tmp, file);
27093
27298
  } catch {
27094
27299
  }
27095
27300
  }
@@ -27163,8 +27368,8 @@ function isLinkedInstall() {
27163
27368
  timeout: 2e3
27164
27369
  }).trim();
27165
27370
  if (!root) return false;
27166
- const pkgPath = path52.join(root, PKG_NAME);
27167
- return fs40.lstatSync(pkgPath).isSymbolicLink();
27371
+ const pkgPath = path53.join(root, PKG_NAME);
27372
+ return fs41.lstatSync(pkgPath).isSymbolicLink();
27168
27373
  } catch {
27169
27374
  return false;
27170
27375
  }
@@ -27200,7 +27405,7 @@ function maybeAutoUpdate(currentVersion, latest) {
27200
27405
  return;
27201
27406
  }
27202
27407
  try {
27203
- fs40.unlinkSync(cachePath());
27408
+ fs41.unlinkSync(cachePath());
27204
27409
  } catch {
27205
27410
  }
27206
27411
  process.stderr.write(` ${import_picocolors16.default.green("\u2713")} Updated. Resuming session...
@@ -27217,7 +27422,7 @@ function checkForUpdates() {
27217
27422
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
27218
27423
  if (process.env.CI) return;
27219
27424
  if (!process.stdout.isTTY) return;
27220
- const current = true ? "2.39.8" : null;
27425
+ const current = true ? "2.39.9" : null;
27221
27426
  if (!current) return;
27222
27427
  const cache = readCache();
27223
27428
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.39.8",
3
+ "version": "2.39.9",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",