agent-workbench 0.3.0 → 0.3.1

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/README.md +6 -4
  2. package/dist/server.js +173 -124
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,6 +12,8 @@ credentials, and makes no network calls of its own.
12
12
  npx agent-workbench
13
13
  ```
14
14
 
15
+ ![A tour: the conversation as cards, the changed files and a diff, one tab per CLI, and continuing a conversation with another CLI](https://raw.githubusercontent.com/cvelasquez/agent-workbench/main/assets/demo.webp)
16
+
15
17
  It doesn't talk to any API. It launches the CLI you already have installed and
16
18
  logged in —`claude`, `codex`, `opencode` or `agy`— inside a pseudo-terminal, and
17
19
  adds around it what a terminal alone doesn't give you: tabs, browsable history,
@@ -20,8 +22,6 @@ the conversation as cards, a context meter, git status and a file tree.
20
22
  The terminal is still the terminal. Everything you type reaches the CLI without
21
23
  the app touching it.
22
24
 
23
- ![The conversation in the center; around it, the projects with their history, the tabs of the four CLIs and the file tree](https://raw.githubusercontent.com/cvelasquez/agent-workbench/main/assets/captura-conversacion.png)
24
-
25
25
  ---
26
26
 
27
27
  ## What it does
@@ -87,8 +87,10 @@ Agent Workbench does **not** bundle or download any CLI: it uses the ones you
87
87
  already have in your `PATH`. If it doesn't find any, it tells you so and doesn't
88
88
  open sessions.
89
89
 
90
- Tested on Windows 11 with PowerShell, which is the main platform. macOS and
91
- Linux work the same. On Linux, the `node-pty` dependency doesn't ship a prebuilt
90
+ Developed and used daily on Windows 11 with PowerShell, which is the main
91
+ platform. On macOS and Linux, CI installs the package and starts it on every
92
+ push —server up, a pseudo-terminal spawning a process—, but it sees less daily
93
+ use there: reports are welcome. On Linux, the `node-pty` dependency doesn't ship a prebuilt
92
94
  binary and is compiled on install: you need `python3`, `make` and a C++ compiler
93
95
  (`build-essential` on Debian and Ubuntu).
94
96
 
package/dist/server.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // packages/server/src/index.ts
4
4
  import { existsSync as existsSync4 } from "node:fs";
5
5
  import { createServer } from "node:http";
6
- import path46 from "node:path";
6
+ import path47 from "node:path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8
8
  import express from "express";
9
9
 
@@ -10617,6 +10617,54 @@ function openBrowser(url) {
10617
10617
  }
10618
10618
  }
10619
10619
 
10620
+ // packages/server/src/pty-helper.ts
10621
+ import { chmodSync, statSync } from "node:fs";
10622
+ import { createRequire } from "node:module";
10623
+ import path29 from "node:path";
10624
+ function helperCandidates(packageDir, platform, arch) {
10625
+ return [
10626
+ path29.join(packageDir, "build", "Release", "spawn-helper"),
10627
+ path29.join(packageDir, "build", "Debug", "spawn-helper"),
10628
+ path29.join(packageDir, "prebuilds", `${platform}-${arch}`, "spawn-helper")
10629
+ ];
10630
+ }
10631
+ function locateNodePty() {
10632
+ try {
10633
+ const require2 = createRequire(import.meta.url);
10634
+ return path29.resolve(path29.dirname(require2.resolve("node-pty")), "..");
10635
+ } catch {
10636
+ return null;
10637
+ }
10638
+ }
10639
+ function ensurePtyHelperExecutable(options = {}) {
10640
+ const platform = options.platform ?? process.platform;
10641
+ if (platform === "win32") return [];
10642
+ const packageDir = options.packageDir === void 0 ? locateNodePty() : options.packageDir;
10643
+ if (packageDir === null) return [];
10644
+ const warn = options.warn ?? ((message) => console.warn(message));
10645
+ const fixed = [];
10646
+ for (const file of helperCandidates(packageDir, platform, options.arch ?? process.arch)) {
10647
+ let mode;
10648
+ try {
10649
+ const stats = statSync(file);
10650
+ if (!stats.isFile()) continue;
10651
+ mode = stats.mode;
10652
+ } catch {
10653
+ continue;
10654
+ }
10655
+ if ((mode & 73) === 73) continue;
10656
+ try {
10657
+ chmodSync(file, mode & 511 | 493);
10658
+ fixed.push(file);
10659
+ } catch {
10660
+ warn(
10661
+ `[pty] ${file} is not executable and couldn't be fixed: no tab will be able to start a CLI. Run: chmod +x "${file}"`
10662
+ );
10663
+ }
10664
+ }
10665
+ return fixed;
10666
+ }
10667
+
10620
10668
  // packages/server/src/security.ts
10621
10669
  import { randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
10622
10670
  var LOOPBACK_HOST = "127.0.0.1";
@@ -10709,7 +10757,7 @@ import chokidar from "chokidar";
10709
10757
  // packages/server/src/git-repo.ts
10710
10758
  import { execFile as execFile3 } from "node:child_process";
10711
10759
  import { readFile as readFile9 } from "node:fs/promises";
10712
- import path29 from "node:path";
10760
+ import path30 from "node:path";
10713
10761
  function gitOutputText(output, command) {
10714
10762
  return output.length > 0 ? serverText("raw", { text: output }) : serverText("gitCommandFailed", { command });
10715
10763
  }
@@ -10770,7 +10818,7 @@ async function resolveRepo(cwd) {
10770
10818
  const result = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
10771
10819
  if (result.code === 0) {
10772
10820
  const root = result.stdout.trim();
10773
- if (root.length > 0) return { state: "ready", root: path29.resolve(root), message: null };
10821
+ if (root.length > 0) return { state: "ready", root: path30.resolve(root), message: null };
10774
10822
  }
10775
10823
  const stderr = result.stderr.trim();
10776
10824
  if (/not a git repository|no es un repositorio/i.test(stderr)) {
@@ -10797,7 +10845,7 @@ async function findGitDir(cwd) {
10797
10845
  const result = await runGit(cwd, ["rev-parse", "--absolute-git-dir"]);
10798
10846
  if (result.code !== 0) return null;
10799
10847
  const dir = result.stdout.trim();
10800
- return dir.length > 0 ? path29.resolve(dir) : null;
10848
+ return dir.length > 0 ? path30.resolve(dir) : null;
10801
10849
  }
10802
10850
  function letterToKind(letter) {
10803
10851
  switch (letter) {
@@ -10900,7 +10948,7 @@ function parseWorktrees(raw, currentRoot) {
10900
10948
  worktrees.push({
10901
10949
  path: current.path,
10902
10950
  branch: current.branch,
10903
- isCurrent: path29.resolve(current.path) === path29.resolve(currentRoot)
10951
+ isCurrent: path30.resolve(current.path) === path30.resolve(currentRoot)
10904
10952
  });
10905
10953
  current = null;
10906
10954
  };
@@ -11091,7 +11139,7 @@ async function readUntrackedAsDiff(absolutePath, relativePath) {
11091
11139
  async function readDiff(options) {
11092
11140
  const { repoRoot: repoRoot2, relativePath, staged, untracked } = options;
11093
11141
  if (untracked && !staged) {
11094
- return readUntrackedAsDiff(path29.join(repoRoot2, relativePath), relativePath);
11142
+ return readUntrackedAsDiff(path30.join(repoRoot2, relativePath), relativePath);
11095
11143
  }
11096
11144
  const args = [
11097
11145
  "diff",
@@ -11311,7 +11359,7 @@ var RepoHub = class extends EventEmitter2 {
11311
11359
  // packages/server/src/memory-hub.ts
11312
11360
  import { EventEmitter as EventEmitter3 } from "node:events";
11313
11361
  import { homedir as homedir10 } from "node:os";
11314
- import path31 from "node:path";
11362
+ import path32 from "node:path";
11315
11363
  import chokidar2 from "chokidar";
11316
11364
 
11317
11365
  // packages/server/src/memory-bridge.ts
@@ -11328,7 +11376,7 @@ import {
11328
11376
  unlink as unlink3
11329
11377
  } from "node:fs/promises";
11330
11378
  import { homedir as homedir9 } from "node:os";
11331
- import path30 from "node:path";
11379
+ import path31 from "node:path";
11332
11380
  var MemoryBridgeError = class extends ServerTextError {
11333
11381
  };
11334
11382
  var MAX_LISTED_NOTES = 500;
@@ -11346,9 +11394,9 @@ var GITIGNORE_MEMORY_LINE = `${MEMORY_DIR}/`;
11346
11394
  var BOM2 = Buffer.from([239, 187, 191]);
11347
11395
  async function inspectMemory(cwd, home = homedir9()) {
11348
11396
  await assertMemoryDirInside(cwd);
11349
- const folder = path30.join(cwd, MEMORY_DIR);
11397
+ const folder = path31.join(cwd, MEMORY_DIR);
11350
11398
  const folderExists = await isDirectory2(folder);
11351
- const indexExists = folderExists && await isFile(path30.join(folder, MEMORY_INDEX_FILE));
11399
+ const indexExists = folderExists && await isFile(path31.join(folder, MEMORY_INDEX_FILE));
11352
11400
  const noteNames = folderExists ? await listNoteFiles(folder) : [];
11353
11401
  const notes = await describeNotes(folder, noteNames);
11354
11402
  const files = await Promise.all(
@@ -11380,7 +11428,7 @@ async function installMemory(cwd, options, home = homedir9()) {
11380
11428
  }
11381
11429
  async function importNativeMemory(cwd, home = homedir9()) {
11382
11430
  await assertMemoryDirInside(cwd);
11383
- if (!await isDirectory2(path30.join(cwd, MEMORY_DIR))) {
11431
+ if (!await isDirectory2(path31.join(cwd, MEMORY_DIR))) {
11384
11432
  throw new MemoryBridgeError(serverText("memoryNotInstalled"));
11385
11433
  }
11386
11434
  const plan = new PlanBuilder(cwd);
@@ -11415,7 +11463,7 @@ function isNoteName(name) {
11415
11463
  if (name.length === 0 || name.length > 255) return false;
11416
11464
  if (name.includes("\0")) return false;
11417
11465
  if (name === "." || name === "..") return false;
11418
- if (path30.basename(name) !== name) return false;
11466
+ if (path31.basename(name) !== name) return false;
11419
11467
  if (/[\\/:*?"<>|\u0000-\u001f]/.test(name)) return false;
11420
11468
  return name.toLowerCase().endsWith(".md");
11421
11469
  }
@@ -11452,7 +11500,7 @@ var PlanBuilder = class {
11452
11500
  /** Nombres de nota que habria en la carpeta, en minusculas. */
11453
11501
  async noteNames() {
11454
11502
  const names = /* @__PURE__ */ new Map();
11455
- for (const name of await listNoteFiles(path30.join(this.cwd, MEMORY_DIR))) {
11503
+ for (const name of await listNoteFiles(path31.join(this.cwd, MEMORY_DIR))) {
11456
11504
  names.set(name.toLowerCase(), name);
11457
11505
  }
11458
11506
  for (const rel of this.files.keys()) {
@@ -11514,7 +11562,7 @@ var PlanBuilder = class {
11514
11562
  );
11515
11563
  if (pending.length === 0) return;
11516
11564
  if (pending.some((file) => file.rel.startsWith(`${MEMORY_DIR}/`))) {
11517
- await mkdir5(path30.join(this.cwd, MEMORY_DIR), { recursive: true });
11565
+ await mkdir5(path31.join(this.cwd, MEMORY_DIR), { recursive: true });
11518
11566
  }
11519
11567
  for (const file of pending) {
11520
11568
  if (file.target === null) throw new Error(`Unvalidated plan: ${file.rel}`);
@@ -11625,7 +11673,7 @@ async function planNativeImport(plan, cwd, home) {
11625
11673
  const local = await plan.noteNames();
11626
11674
  for (const name of await listNoteFiles(folder)) {
11627
11675
  const key = name.toLowerCase();
11628
- const content = await readIfFile(path30.join(folder, name), name);
11676
+ const content = await readIfFile(path31.join(folder, name), name);
11629
11677
  if (content === null) continue;
11630
11678
  const already = chosen.get(key);
11631
11679
  if (already !== void 0) {
@@ -11639,7 +11687,7 @@ async function planNativeImport(plan, cwd, home) {
11639
11687
  if (existing !== null && !existing.equals(content)) skipped.push(name);
11640
11688
  continue;
11641
11689
  }
11642
- plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", path30.join(folder, name));
11690
+ plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", path31.join(folder, name));
11643
11691
  copied.push(name);
11644
11692
  }
11645
11693
  }
@@ -11649,7 +11697,7 @@ async function planNativeImport(plan, cwd, home) {
11649
11697
  const known = new Set(linkTargets(index.toString("utf8")));
11650
11698
  const additions = [];
11651
11699
  for (const folder of folders) {
11652
- const nativeIndex = await readIfFile(path30.join(folder, MEMORY_INDEX_FILE), MEMORY_INDEX_FILE);
11700
+ const nativeIndex = await readIfFile(path31.join(folder, MEMORY_INDEX_FILE), MEMORY_INDEX_FILE);
11653
11701
  if (nativeIndex === null) continue;
11654
11702
  for (const line of nativeIndex.toString("utf8").replace(/^\uFEFF/, "").split(/\r?\n/)) {
11655
11703
  const target = linkTargets(line)[0];
@@ -11874,7 +11922,7 @@ async function describeNotes(folder, names) {
11874
11922
  const stated = [];
11875
11923
  for (const name of names) {
11876
11924
  try {
11877
- const info = await stat15(path30.join(folder, name));
11925
+ const info = await stat15(path31.join(folder, name));
11878
11926
  if (info.isFile()) stated.push({ name, sizeBytes: info.size, modifiedAt: info.mtimeMs });
11879
11927
  } catch {
11880
11928
  }
@@ -11882,7 +11930,7 @@ async function describeNotes(folder, names) {
11882
11930
  stated.sort((a, b) => b.modifiedAt - a.modifiedAt || a.name.localeCompare(b.name));
11883
11931
  const notes = [];
11884
11932
  for (const item of stated.slice(0, MAX_LISTED_NOTES)) {
11885
- const head = await readHead(path30.join(folder, item.name));
11933
+ const head = await readHead(path31.join(folder, item.name));
11886
11934
  notes.push({ ...item, ...describeNoteText(head, item.name) });
11887
11935
  }
11888
11936
  return notes;
@@ -11940,28 +11988,28 @@ async function countNativeMemory(cwd, home, sharedNames) {
11940
11988
  return { available: names.size, pending };
11941
11989
  }
11942
11990
  async function nativeMemoryFolders(cwd, home) {
11943
- const root = path30.join(home, ".claude", "projects");
11991
+ const root = path31.join(home, ".claude", "projects");
11944
11992
  const slug = projectSlugFor(cwd).toLowerCase();
11945
11993
  try {
11946
11994
  const entries = await readdir9(root, { withFileTypes: true });
11947
- return entries.filter((entry) => entry.isDirectory() && entry.name.toLowerCase() === slug).map((entry) => entry.name).sort().map((name) => path30.join(root, name, "memory"));
11995
+ return entries.filter((entry) => entry.isDirectory() && entry.name.toLowerCase() === slug).map((entry) => entry.name).sort().map((name) => path31.join(root, name, "memory"));
11948
11996
  } catch {
11949
11997
  return [];
11950
11998
  }
11951
11999
  }
11952
12000
  function buildGlobalFragments(home) {
11953
12001
  const windows = process.platform === "win32";
11954
- const globalFile = path30.join(home, ...GLOBAL_MEMORY_RELATIVE.split("/"));
12002
+ const globalFile = path31.join(home, ...GLOBAL_MEMORY_RELATIVE.split("/"));
11955
12003
  const hardLink = (link) => windows ? `mklink /H "${link}" "${globalFile}"` : `ln "${globalFile}" "${link}"`;
11956
12004
  const hardLinkNote = windows ? serverText("memoryNoteHardLinkCmd", { path: GLOBAL_MEMORY_RELATIVE }) : serverText("memoryNoteHardLink", { path: GLOBAL_MEMORY_RELATIVE });
11957
- const codexTarget = path30.join(home, ".codex", "AGENTS.md");
11958
- const geminiTarget = path30.join(home, ".gemini", "GEMINI.md");
12005
+ const codexTarget = path31.join(home, ".codex", "AGENTS.md");
12006
+ const geminiTarget = path31.join(home, ".gemini", "GEMINI.md");
11959
12007
  const homeSlashes = home.replace(/\\/g, "/").replace(/\/+$/, "");
11960
12008
  return [
11961
12009
  {
11962
12010
  agent: "claude-code",
11963
12011
  label: MEMORY_AGENT_LABELS["claude-code"],
11964
- target: path30.join(home, ".claude", "CLAUDE.md"),
12012
+ target: path31.join(home, ".claude", "CLAUDE.md"),
11965
12013
  kind: "line",
11966
12014
  text: `@~/${GLOBAL_MEMORY_RELATIVE}`,
11967
12015
  note: serverText("memoryNoteClaude")
@@ -11985,7 +12033,7 @@ function buildGlobalFragments(home) {
11985
12033
  {
11986
12034
  agent: "opencode",
11987
12035
  label: MEMORY_AGENT_LABELS.opencode,
11988
- target: path30.join(home, ".config", "opencode", "opencode.json"),
12036
+ target: path31.join(home, ".config", "opencode", "opencode.json"),
11989
12037
  kind: "json",
11990
12038
  text: `"instructions": ["${homeSlashes}/${GLOBAL_MEMORY_RELATIVE}"]`,
11991
12039
  note: serverText("memoryNoteOpenCode")
@@ -12033,17 +12081,17 @@ async function readGit(cwd) {
12033
12081
  if (!toplevel || !gitDir || !commonDir) return fail(serverText("gitUnexpected", { command: "rev-parse" }));
12034
12082
  const ignored = await checkIgnored(cwd, INDEX_REL);
12035
12083
  if (typeof ignored !== "boolean") return fail(ignored);
12036
- const absoluteGitDir = path30.resolve(cwd, gitDir);
12037
- const absoluteCommonDir = path30.resolve(cwd, commonDir);
12084
+ const absoluteGitDir = path31.resolve(cwd, gitDir);
12085
+ const absoluteCommonDir = path31.resolve(cwd, commonDir);
12038
12086
  let worktree = null;
12039
12087
  let main2 = null;
12040
12088
  if (!await samePath(absoluteGitDir, absoluteCommonDir)) {
12041
- if (path30.basename(absoluteCommonDir).toLowerCase() === ".git") {
12042
- const mainRoot = path30.dirname(absoluteCommonDir);
12043
- const inner = path30.relative(path30.resolve(toplevel), path30.resolve(cwd));
12044
- const sameFolder = inner.length === 0 || inner.startsWith("..") || path30.isAbsolute(inner);
12045
- const mainPath = sameFolder ? mainRoot : path30.join(mainRoot, inner);
12046
- const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(path30.sep), MEMORY_DIR].join("/");
12089
+ if (path31.basename(absoluteCommonDir).toLowerCase() === ".git") {
12090
+ const mainRoot = path31.dirname(absoluteCommonDir);
12091
+ const inner = path31.relative(path31.resolve(toplevel), path31.resolve(cwd));
12092
+ const sameFolder = inner.length === 0 || inner.startsWith("..") || path31.isAbsolute(inner);
12093
+ const mainPath = sameFolder ? mainRoot : path31.join(mainRoot, inner);
12094
+ const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(path31.sep), MEMORY_DIR].join("/");
12047
12095
  main2 = { root: mainRoot, memoryRel };
12048
12096
  worktree = {
12049
12097
  mainPath,
@@ -12068,7 +12116,7 @@ async function samePath(a, b) {
12068
12116
  try {
12069
12117
  return await realpath2(value);
12070
12118
  } catch {
12071
- return path30.resolve(value);
12119
+ return path31.resolve(value);
12072
12120
  }
12073
12121
  };
12074
12122
  const [left, right] = await Promise.all([resolve(a), resolve(b)]);
@@ -12159,7 +12207,7 @@ async function writeAtomic(target, content, mustNotExist) {
12159
12207
  await handle.close();
12160
12208
  }
12161
12209
  if (mustNotExist && await exists2(target)) {
12162
- throw new MemoryBridgeError(serverText("memoryAppeared", { file: path30.basename(target) }));
12210
+ throw new MemoryBridgeError(serverText("memoryAppeared", { file: path31.basename(target) }));
12163
12211
  }
12164
12212
  await renameWithRetry(temporary, target);
12165
12213
  } catch (error) {
@@ -12170,9 +12218,9 @@ async function writeAtomic(target, content, mustNotExist) {
12170
12218
  async function createTemporary(target) {
12171
12219
  for (let attempt = 1; ; attempt += 1) {
12172
12220
  const suffix = randomBytes3(6).toString("hex");
12173
- const temporary = path30.join(
12174
- path30.dirname(target),
12175
- `.${path30.basename(target)}.${process.pid}.${suffix}.tmp`
12221
+ const temporary = path31.join(
12222
+ path31.dirname(target),
12223
+ `.${path31.basename(target)}.${process.pid}.${suffix}.tmp`
12176
12224
  );
12177
12225
  try {
12178
12226
  return { temporary, handle: await open6(temporary, "wx") };
@@ -12410,13 +12458,13 @@ var MemoryHub = class extends EventEmitter3 {
12410
12458
  }
12411
12459
  };
12412
12460
  function keyFor(cwd) {
12413
- const resolved = path31.resolve(cwd);
12461
+ const resolved = path32.resolve(cwd);
12414
12462
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
12415
12463
  }
12416
12464
  function isRelevantPath(cwd, candidate) {
12417
- const relative = path31.relative(cwd, candidate);
12465
+ const relative = path32.relative(cwd, candidate);
12418
12466
  if (relative.length === 0) return true;
12419
- if (relative.startsWith("..") || path31.isAbsolute(relative)) return false;
12467
+ if (relative.startsWith("..") || path32.isAbsolute(relative)) return false;
12420
12468
  const same = (a, b) => process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
12421
12469
  const parts = relative.split(/[\\/]/);
12422
12470
  const [first = "", second = "", third = ""] = parts;
@@ -12436,7 +12484,7 @@ function isRelevantPath(cwd, candidate) {
12436
12484
  import { createHash, randomUUID as randomUUID2 } from "node:crypto";
12437
12485
  import { mkdir as mkdir6, readdir as readdir10, rm as rm2, stat as stat16, writeFile as writeFile3 } from "node:fs/promises";
12438
12486
  import { tmpdir as tmpdir4 } from "node:os";
12439
- import path32 from "node:path";
12487
+ import path33 from "node:path";
12440
12488
 
12441
12489
  // packages/server/src/vault/markdown.ts
12442
12490
  var pad2 = (value) => String(value).padStart(2, "0");
@@ -12951,8 +12999,8 @@ function buildContinuationMessage(input) {
12951
12999
  }
12952
13000
  return sanitizeForPaste(lines.join("\n"));
12953
13001
  }
12954
- function transcriptReferenceFor(path47, style) {
12955
- return style === "at-quoted" ? fileReference(path47, "at-quoted") : `"${path47}"`;
13002
+ function transcriptReferenceFor(path48, style) {
13003
+ return style === "at-quoted" ? fileReference(path48, "at-quoted") : `"${path48}"`;
12956
13004
  }
12957
13005
  function planTranscript(input) {
12958
13006
  const turns = splitTurns(input.events);
@@ -13127,7 +13175,7 @@ var PasteStore = class {
13127
13175
  root;
13128
13176
  counter = 0;
13129
13177
  constructor(root) {
13130
- this.root = root ?? path32.join(tmpdir4(), "agent-workbench", "pasted");
13178
+ this.root = root ?? path33.join(tmpdir4(), "agent-workbench", "pasted");
13131
13179
  }
13132
13180
  /**
13133
13181
  * Guarda una imagen y devuelve su ruta.
@@ -13154,11 +13202,11 @@ var PasteStore = class {
13154
13202
  if (signature === null) {
13155
13203
  throw new PasteImageError(serverText("imageUnknownFormat"));
13156
13204
  }
13157
- const directory = path32.join(this.root, safeSegment(terminalId));
13205
+ const directory = path33.join(this.root, safeSegment(terminalId));
13158
13206
  await mkdir6(directory, { recursive: true, mode: DIRECTORY_MODE });
13159
13207
  this.counter += 1;
13160
13208
  const name = `pegada-${this.counter}-${randomUUID2().slice(0, 8)}.${signature.ext}`;
13161
- const file = path32.join(directory, name);
13209
+ const file = path33.join(directory, name);
13162
13210
  await writeFile3(file, bytes, { mode: FILE_MODE });
13163
13211
  return { path: file, bytes: bytes.length };
13164
13212
  }
@@ -13176,11 +13224,11 @@ var PasteStore = class {
13176
13224
  if (bytes.length > MAX_TEXT_BYTES) {
13177
13225
  throw new PasteTextError(`The text is ${bytes.length} bytes; the maximum is ${MAX_TEXT_BYTES}.`);
13178
13226
  }
13179
- const directory = path32.join(this.root, safeSegment(terminalId));
13227
+ const directory = path33.join(this.root, safeSegment(terminalId));
13180
13228
  await mkdir6(directory, { recursive: true, mode: DIRECTORY_MODE });
13181
13229
  this.counter += 1;
13182
13230
  const name = `${baseName}-${this.counter}-${randomUUID2().slice(0, 8)}.md`;
13183
- const file = path32.join(directory, name);
13231
+ const file = path33.join(directory, name);
13184
13232
  await writeFile3(file, bytes, { mode: FILE_MODE });
13185
13233
  return { path: file, bytes: bytes.length };
13186
13234
  }
@@ -13210,17 +13258,17 @@ var PasteStore = class {
13210
13258
  })
13211
13259
  );
13212
13260
  }
13213
- const directory = path32.join(this.root, safeSegment(terminalId));
13261
+ const directory = path33.join(this.root, safeSegment(terminalId));
13214
13262
  await mkdir6(directory, { recursive: true, mode: DIRECTORY_MODE });
13215
13263
  this.counter += 1;
13216
13264
  const name = `adjunto-${this.counter}-${randomUUID2().slice(0, 8)}-${safe}`;
13217
- const file = path32.join(directory, name);
13265
+ const file = path33.join(directory, name);
13218
13266
  await writeFile3(file, bytes, { mode: FILE_MODE });
13219
13267
  return { path: file, bytes: bytes.length, name };
13220
13268
  }
13221
13269
  /** Borra lo de una pestana. Se llama al cerrarla. */
13222
13270
  async clearTerminal(terminalId) {
13223
- await rm2(path32.join(this.root, safeSegment(terminalId)), {
13271
+ await rm2(path33.join(this.root, safeSegment(terminalId)), {
13224
13272
  recursive: true,
13225
13273
  force: true
13226
13274
  }).catch(() => {
@@ -13242,7 +13290,7 @@ var PasteStore = class {
13242
13290
  }
13243
13291
  await Promise.all(
13244
13292
  entries.map(async (entry) => {
13245
- const full = path32.join(this.root, entry);
13293
+ const full = path33.join(this.root, entry);
13246
13294
  try {
13247
13295
  const info = await stat16(full);
13248
13296
  if (now - info.mtimeMs > STALE_AFTER_MS) {
@@ -13262,7 +13310,7 @@ function safeSegment(value) {
13262
13310
  // packages/server/src/session-index.ts
13263
13311
  import { EventEmitter as EventEmitter4 } from "node:events";
13264
13312
  import { mkdir as mkdir7, readFile as readFile11, stat as stat17, writeFile as writeFile4 } from "node:fs/promises";
13265
- import path33 from "node:path";
13313
+ import path34 from "node:path";
13266
13314
  var CACHE_VERSION = 4;
13267
13315
  var vaultPairKey = (agent, sessionId) => `${agent}/${sessionId}`;
13268
13316
  function vaultOnlySessions(natives, vault, listState, ready) {
@@ -13361,7 +13409,7 @@ function migrateEntryV3(filePath, value) {
13361
13409
  return {
13362
13410
  agent: "claude-code",
13363
13411
  ref: filePath,
13364
- group: path33.basename(path33.dirname(filePath)),
13412
+ group: path34.basename(path34.dirname(filePath)),
13365
13413
  mtimeMs,
13366
13414
  sizeBytes,
13367
13415
  cwd,
@@ -13852,7 +13900,7 @@ var SessionIndex = class extends EventEmitter4 {
13852
13900
 
13853
13901
  // packages/server/src/settings-store.ts
13854
13902
  import { mkdir as mkdir9, readFile as readFile12 } from "node:fs/promises";
13855
- import path35 from "node:path";
13903
+ import path36 from "node:path";
13856
13904
 
13857
13905
  // packages/server/src/vault/write.ts
13858
13906
  import { randomBytes as randomBytes4 } from "node:crypto";
@@ -13860,15 +13908,15 @@ import { mkdir as mkdir8, rename as rename4, rm as rm3, stat as stat18, writeFil
13860
13908
 
13861
13909
  // packages/server/src/vault/paths.ts
13862
13910
  import { createHash as createHash2 } from "node:crypto";
13863
- import path34 from "node:path";
13911
+ import path35 from "node:path";
13864
13912
  function defaultVaultDir() {
13865
- return path34.join(appConfigDir(), "vault");
13913
+ return path35.join(appConfigDir(), "vault");
13866
13914
  }
13867
13915
  function vaultMarkerFile(dir) {
13868
- return path34.join(dir, "vault.json");
13916
+ return path35.join(dir, "vault.json");
13869
13917
  }
13870
13918
  function sessionsRoot2(dir) {
13871
- return path34.join(dir, "sessions");
13919
+ return path35.join(dir, "sessions");
13872
13920
  }
13873
13921
  function isSafeSessionId(sessionId) {
13874
13922
  return isVaultSessionId(sessionId);
@@ -13882,41 +13930,41 @@ function assertSessionKey(agent, sessionId) {
13882
13930
  }
13883
13931
  function agentSessionsDir(dir, agent) {
13884
13932
  if (!isSessionAgentId(agent)) throw new Error(`Unknown source for the local copy: ${JSON.stringify(agent)}`);
13885
- return path34.join(sessionsRoot2(dir), agent);
13933
+ return path35.join(sessionsRoot2(dir), agent);
13886
13934
  }
13887
13935
  function sessionFile(dir, agent, sessionId) {
13888
13936
  assertSessionKey(agent, sessionId);
13889
- return path34.join(sessionsRoot2(dir), agent, `${sessionId}.jsonl`);
13937
+ return path35.join(sessionsRoot2(dir), agent, `${sessionId}.jsonl`);
13890
13938
  }
13891
13939
  function assetsDir(dir, agent, sessionId) {
13892
13940
  assertSessionKey(agent, sessionId);
13893
- return path34.join(sessionsRoot2(dir), agent, `${sessionId}.assets`);
13941
+ return path35.join(sessionsRoot2(dir), agent, `${sessionId}.assets`);
13894
13942
  }
13895
13943
  function assetFile(dir, agent, sessionId, asset) {
13896
13944
  if (!VAULT_ASSET_NAME_PATTERN.test(asset)) throw new Error(`Invalid asset name: ${JSON.stringify(asset)}`);
13897
- return path34.join(assetsDir(dir, agent, sessionId), asset);
13945
+ return path35.join(assetsDir(dir, agent, sessionId), asset);
13898
13946
  }
13899
13947
  function projectFolderName(cwd, platform) {
13900
13948
  const hash = createHash2("sha256").update(normalizeCwdKey(cwd, platform)).digest("hex").slice(0, 8);
13901
- const base = (platform === "win32" ? path34.win32 : path34.posix).basename(cwd);
13949
+ const base = (platform === "win32" ? path35.win32 : path35.posix).basename(cwd);
13902
13950
  const name = base.length === 0 ? "proyecto" : sanitizeFileName(base, 60);
13903
13951
  return `${name}-${hash}`;
13904
13952
  }
13905
13953
  function memoryDir(dir, cwd, platform) {
13906
- return path34.join(dir, "memory", projectFolderName(cwd, platform));
13954
+ return path35.join(dir, "memory", projectFolderName(cwd, platform));
13907
13955
  }
13908
13956
  function projectExportDir(dir, cwd, platform) {
13909
- return path34.join(dir, "export", projectFolderName(cwd, platform));
13957
+ return path35.join(dir, "export", projectFolderName(cwd, platform));
13910
13958
  }
13911
13959
  function projectExportDirFor(dir, project, platform) {
13912
13960
  if (project.cwd.length > 0) return projectExportDir(dir, project.cwd, platform);
13913
13961
  const hash = createHash2("sha256").update(project.key).digest("hex").slice(0, 8);
13914
13962
  const name = project.fallbackName.length > 0 ? sanitizeFileName(project.fallbackName, 60) : "proyecto";
13915
- return path34.join(dir, "export", `${name}-${hash}`);
13963
+ return path35.join(dir, "export", `${name}-${hash}`);
13916
13964
  }
13917
13965
  function sessionExportFile(dir, agent, sessionId) {
13918
13966
  assertSessionKey(agent, sessionId);
13919
- return path34.join(dir, "export", "sesiones", `${agent}-${sessionId}.md`);
13967
+ return path35.join(dir, "export", "sesiones", `${agent}-${sessionId}.md`);
13920
13968
  }
13921
13969
  var RESERVED_WINDOWS_NAME = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
13922
13970
  function sanitizeFileName(text, max = 80) {
@@ -14023,7 +14071,7 @@ function defaultAppSettings() {
14023
14071
  function isAbsoluteDir(dir, platform) {
14024
14072
  if (dir.length === 0 || dir.includes("\0")) return false;
14025
14073
  if (platform === "win32") return /^[A-Za-z]:[\\/]/.test(dir) || /^\\\\[^\\/]+[\\/][^\\/]+/.test(dir);
14026
- return path35.posix.isAbsolute(dir);
14074
+ return path36.posix.isAbsolute(dir);
14027
14075
  }
14028
14076
  function clampToolResultMaxChars(value) {
14029
14077
  if (typeof value !== "number" || !Number.isFinite(value)) return VAULT_TOOL_RESULT_DEFAULT_CHARS;
@@ -14106,7 +14154,7 @@ var SettingsStore = class {
14106
14154
  if (vault.toolResultMaxChars !== void 0) {
14107
14155
  next.vault.toolResultMaxChars = clampToolResultMaxChars(vault.toolResultMaxChars);
14108
14156
  }
14109
- await mkdir9(path35.dirname(this.filePath), { recursive: true });
14157
+ await mkdir9(path36.dirname(this.filePath), { recursive: true });
14110
14158
  await writeFileAtomic(this.filePath, `${JSON.stringify(next, null, 2)}
14111
14159
  `);
14112
14160
  this.current = frozen(next);
@@ -14175,7 +14223,7 @@ function startupAgentLines(agents) {
14175
14223
  // packages/server/src/notes-store.ts
14176
14224
  import { randomUUID as randomUUID3 } from "node:crypto";
14177
14225
  import { mkdir as mkdir10, readFile as readFile13, rename as rename5, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
14178
- import path36 from "node:path";
14226
+ import path37 from "node:path";
14179
14227
  var STATE_VERSION2 = 1;
14180
14228
  var WRITE_DEBOUNCE_MS2 = 500;
14181
14229
  var NotesError = class extends ServerTextError {
@@ -14379,7 +14427,7 @@ var NotesStore = class {
14379
14427
  }
14380
14428
  imagePath(image) {
14381
14429
  const safe = image.imageId.replace(/[^a-zA-Z0-9-]/g, "");
14382
- return path36.join(this.imagesDir, `${safe}.${extensionFor(image.mediaType)}`);
14430
+ return path37.join(this.imagesDir, `${safe}.${extensionFor(image.mediaType)}`);
14383
14431
  }
14384
14432
  async unlinkImage(image) {
14385
14433
  await rm4(this.imagePath(image), { force: true }).catch(() => {
@@ -14397,8 +14445,8 @@ var NotesStore = class {
14397
14445
  async writeNow() {
14398
14446
  this.dirty = false;
14399
14447
  try {
14400
- await mkdir10(path36.dirname(this.statePath), { recursive: true });
14401
- const temporary = path36.join(path36.dirname(this.statePath), `notes.${process.pid}.tmp`);
14448
+ await mkdir10(path37.dirname(this.statePath), { recursive: true });
14449
+ const temporary = path37.join(path37.dirname(this.statePath), `notes.${process.pid}.tmp`);
14402
14450
  await writeFile6(
14403
14451
  temporary,
14404
14452
  JSON.stringify({ version: STATE_VERSION2, notes: this.notes }, null, 2),
@@ -14414,7 +14462,7 @@ var NotesStore = class {
14414
14462
  // packages/server/src/shell-locator.ts
14415
14463
  import { access as access2 } from "node:fs/promises";
14416
14464
  import { constants as fsConstants2 } from "node:fs";
14417
- import path37 from "node:path";
14465
+ import path38 from "node:path";
14418
14466
  async function isExecutable(candidate) {
14419
14467
  try {
14420
14468
  await access2(candidate, process.platform === "win32" ? fsConstants2.R_OK : fsConstants2.X_OK);
@@ -14426,7 +14474,7 @@ async function isExecutable(candidate) {
14426
14474
  function windowsPowerShellPath() {
14427
14475
  const systemRoot = process.env["SystemRoot"] ?? process.env["windir"];
14428
14476
  if (systemRoot === void 0 || systemRoot.length === 0) return null;
14429
- return path37.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
14477
+ return path38.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
14430
14478
  }
14431
14479
  async function locateWindowsShell() {
14432
14480
  const pwsh = await findInPath("pwsh");
@@ -14446,7 +14494,7 @@ async function locateWindowsShell() {
14446
14494
  async function locatePosixShell() {
14447
14495
  const preferred = process.env["SHELL"];
14448
14496
  if (preferred !== void 0 && preferred.length > 0 && await isExecutable(preferred)) {
14449
- return { file: preferred, args: [], name: path37.basename(preferred) };
14497
+ return { file: preferred, args: [], name: path38.basename(preferred) };
14450
14498
  }
14451
14499
  if (await isExecutable("/bin/bash")) return { file: "/bin/bash", args: [], name: "bash" };
14452
14500
  if (await isExecutable("/bin/sh")) return { file: "/bin/sh", args: [], name: "sh" };
@@ -14829,7 +14877,7 @@ async function resolveLaunchPlan(launch) {
14829
14877
 
14830
14878
  // packages/server/src/workspace-store.ts
14831
14879
  import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile7 } from "node:fs/promises";
14832
- import path38 from "node:path";
14880
+ import path39 from "node:path";
14833
14881
  var STATE_VERSION3 = 1;
14834
14882
  var WRITE_DEBOUNCE_MS3 = 400;
14835
14883
  var LEGACY_TAB_AGENT = "claude-code";
@@ -14983,7 +15031,7 @@ var WorkspaceStore = class {
14983
15031
  try {
14984
15032
  await mkdir11(appConfigDir(), { recursive: true });
14985
15033
  const target = workspaceStatePath();
14986
- const temporary = path38.join(path38.dirname(target), `workspace.${process.pid}.tmp`);
15034
+ const temporary = path39.join(path39.dirname(target), `workspace.${process.pid}.tmp`);
14987
15035
  await writeFile7(temporary, JSON.stringify(serializeState(state), null, 2), "utf8");
14988
15036
  const { rename: rename6 } = await import("node:fs/promises");
14989
15037
  await rename6(temporary, target);
@@ -15643,17 +15691,17 @@ import { WebSocketServer } from "ws";
15643
15691
  import { randomUUID as randomUUID5 } from "node:crypto";
15644
15692
  import { mkdir as mkdir12, readdir as readdir11, stat as stat21 } from "node:fs/promises";
15645
15693
  import { homedir as homedir11 } from "node:os";
15646
- import path39 from "node:path";
15694
+ import path40 from "node:path";
15647
15695
  var MAX_ENTRIES = 500;
15648
15696
  var MAX_PICKERS = 8;
15649
15697
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "bin", "obj", "$RECYCLE.BIN"]);
15650
15698
  var DirectoryPickerError = class extends ServerTextError {
15651
15699
  };
15652
15700
  function isInsideProtected(target, dirs) {
15653
- const resolved = path39.resolve(target);
15701
+ const resolved = path40.resolve(target);
15654
15702
  return dirs.some((dir) => {
15655
- const protectedDir = path39.resolve(dir);
15656
- return resolved === protectedDir || resolved.startsWith(protectedDir + path39.sep);
15703
+ const protectedDir = path40.resolve(dir);
15704
+ return resolved === protectedDir || resolved.startsWith(protectedDir + path40.sep);
15657
15705
  });
15658
15706
  }
15659
15707
  async function listRoots() {
@@ -15717,14 +15765,14 @@ var DirectoryPickers = class {
15717
15765
  async enter(pickerId, name) {
15718
15766
  const picker = this.require(pickerId);
15719
15767
  if (name === "..") {
15720
- const parent = path39.dirname(picker.current);
15768
+ const parent = path40.dirname(picker.current);
15721
15769
  if (parent !== picker.current) picker.current = parent;
15722
15770
  return this.listing(picker);
15723
15771
  }
15724
15772
  if (!picker.entries.has(name)) {
15725
15773
  throw new DirectoryPickerError(serverText("pickerNotListed"));
15726
15774
  }
15727
- const target = path39.join(picker.current, name);
15775
+ const target = path40.join(picker.current, name);
15728
15776
  if (isInsideProtected(target, this.protectedDirs)) {
15729
15777
  throw new DirectoryPickerError(serverText("pickerCliFolder"));
15730
15778
  }
@@ -15764,7 +15812,7 @@ var DirectoryPickers = class {
15764
15812
  throw new DirectoryPickerError(serverText("pickerNameChars"));
15765
15813
  }
15766
15814
  }
15767
- const target = path39.join(picker.current, clean);
15815
+ const target = path40.join(picker.current, clean);
15768
15816
  if (isInsideProtected(target, this.protectedDirs)) {
15769
15817
  throw new DirectoryPickerError(serverText("pickerNoProjectsHere"));
15770
15818
  }
@@ -15801,7 +15849,7 @@ var DirectoryPickers = class {
15801
15849
  let truncated = false;
15802
15850
  try {
15803
15851
  const found = await readdir11(picker.current, { withFileTypes: true });
15804
- const dirs = found.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !SKIPPED.has(name)).filter((name) => !isInsideProtected(path39.join(picker.current, name), this.protectedDirs)).sort((a, b) => a.localeCompare(b));
15852
+ const dirs = found.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !SKIPPED.has(name)).filter((name) => !isInsideProtected(path40.join(picker.current, name), this.protectedDirs)).sort((a, b) => a.localeCompare(b));
15805
15853
  truncated = dirs.length > MAX_ENTRIES;
15806
15854
  names = dirs.slice(0, MAX_ENTRIES);
15807
15855
  } catch {
@@ -15812,7 +15860,7 @@ var DirectoryPickers = class {
15812
15860
  path: picker.current,
15813
15861
  entries: names,
15814
15862
  roots,
15815
- canGoUp: path39.dirname(picker.current) !== picker.current,
15863
+ canGoUp: path40.dirname(picker.current) !== picker.current,
15816
15864
  truncated
15817
15865
  };
15818
15866
  }
@@ -15820,7 +15868,7 @@ var DirectoryPickers = class {
15820
15868
 
15821
15869
  // packages/server/src/file-browser.ts
15822
15870
  import { readFile as readFile15, readdir as readdir12, stat as stat22 } from "node:fs/promises";
15823
- import path40 from "node:path";
15871
+ import path41 from "node:path";
15824
15872
  var MAX_ENTRIES2 = 1e3;
15825
15873
  var SEARCH_MAX_RESULTS = 200;
15826
15874
  var SEARCH_MAX_DIRECTORIES = 4e3;
@@ -15883,7 +15931,7 @@ function languageFor(fileName) {
15883
15931
  const lower = fileName.toLowerCase();
15884
15932
  const byName = LANGUAGE_BY_NAME[lower];
15885
15933
  if (byName !== void 0) return byName;
15886
- const extension = path40.extname(lower);
15934
+ const extension = path41.extname(lower);
15887
15935
  return LANGUAGE_BY_EXTENSION[extension] ?? null;
15888
15936
  }
15889
15937
  function looksBinary2(buffer) {
@@ -15915,7 +15963,7 @@ async function listDirectory(cwd, relativePath, options = {}) {
15915
15963
  let sizeBytes = 0;
15916
15964
  let modifiedAt = 0;
15917
15965
  try {
15918
- const info = await stat22(path40.join(absolute, candidate.name));
15966
+ const info = await stat22(path41.join(absolute, candidate.name));
15919
15967
  sizeBytes = candidate.isDirectory ? 0 : info.size;
15920
15968
  modifiedAt = info.mtimeMs;
15921
15969
  } catch {
@@ -15948,7 +15996,7 @@ async function readLevel(absolute) {
15948
15996
  const always = ALWAYS_SKIP.has(dirent.name.toLowerCase());
15949
15997
  if (dirent.isSymbolicLink()) {
15950
15998
  try {
15951
- const target = await stat22(path40.join(absolute, dirent.name));
15999
+ const target = await stat22(path41.join(absolute, dirent.name));
15952
16000
  candidates.push({
15953
16001
  name: dirent.name,
15954
16002
  isDirectory: target.isDirectory(),
@@ -15977,7 +16025,7 @@ async function markIgnored(repoRoot2, levels) {
15977
16025
  for (const level of levels) {
15978
16026
  for (const entry of level.entries) {
15979
16027
  if (entry.hidden !== null) continue;
15980
- keys.set(entry, toPosixPath(path40.relative(repoRoot2, path40.join(level.absolute, entry.name))));
16028
+ keys.set(entry, toPosixPath(path41.relative(repoRoot2, path41.join(level.absolute, entry.name))));
15981
16029
  }
15982
16030
  }
15983
16031
  if (keys.size === 0) return;
@@ -16038,7 +16086,7 @@ async function searchFiles(cwd, query, options = {}) {
16038
16086
  let sizeBytes = 0;
16039
16087
  let modifiedAt = 0;
16040
16088
  try {
16041
- const info = await stat22(path40.join(level.absolute, candidate.name));
16089
+ const info = await stat22(path41.join(level.absolute, candidate.name));
16042
16090
  sizeBytes = candidate.isDirectory ? 0 : info.size;
16043
16091
  modifiedAt = info.mtimeMs;
16044
16092
  } catch {
@@ -16077,7 +16125,7 @@ async function readPreview(cwd, relativePath) {
16077
16125
  throw new Error("It's a directory, not a file.");
16078
16126
  }
16079
16127
  const content = await readFile15(absolute);
16080
- const fileName = path40.basename(absolute);
16128
+ const fileName = path41.basename(absolute);
16081
16129
  if (looksBinary2(content)) {
16082
16130
  return {
16083
16131
  path: relativePath,
@@ -16668,12 +16716,12 @@ var TerminalWriteQueue = class {
16668
16716
  // packages/server/src/vault/service.ts
16669
16717
  import { mkdir as mkdir15, stat as stat26 } from "node:fs/promises";
16670
16718
  import { homedir as homedir12 } from "node:os";
16671
- import path44 from "node:path";
16719
+ import path45 from "node:path";
16672
16720
 
16673
16721
  // packages/server/src/vault/move.ts
16674
16722
  import { constants } from "node:fs";
16675
16723
  import { copyFile as copyFile2, lstat, mkdir as mkdir13, readdir as readdir13, rm as rm5, stat as stat23, utimes, writeFile as writeFile8 } from "node:fs/promises";
16676
- import path41 from "node:path";
16724
+ import path42 from "node:path";
16677
16725
  var MOVED_ENTRIES = ["vault.json", "sessions", "memory"];
16678
16726
  var OWN_TEMP_FILE = /\.\d+\.[0-9a-f]{12}\.tmp$/;
16679
16727
  async function copyTree(source, target, result) {
@@ -16686,12 +16734,12 @@ async function copyTree(source, target, result) {
16686
16734
  if (info.isDirectory()) {
16687
16735
  await mkdir13(target, { recursive: true });
16688
16736
  for (const name of await readdir13(source)) {
16689
- await copyTree(path41.join(source, name), path41.join(target, name), result);
16737
+ await copyTree(path42.join(source, name), path42.join(target, name), result);
16690
16738
  }
16691
16739
  return;
16692
16740
  }
16693
16741
  if (!info.isFile() || OWN_TEMP_FILE.test(source)) return;
16694
- await mkdir13(path41.dirname(target), { recursive: true });
16742
+ await mkdir13(path42.dirname(target), { recursive: true });
16695
16743
  try {
16696
16744
  await copyFile2(source, target, constants.COPYFILE_EXCL);
16697
16745
  } catch (error) {
@@ -16708,7 +16756,7 @@ async function moveVault(from, to) {
16708
16756
  const result = { copied: 0, skipped: 0 };
16709
16757
  await mkdir13(to, { recursive: true });
16710
16758
  for (const entry of MOVED_ENTRIES) {
16711
- await copyTree(path41.join(from, entry), path41.join(to, entry), result);
16759
+ await copyTree(path42.join(from, entry), path42.join(to, entry), result);
16712
16760
  }
16713
16761
  return result;
16714
16762
  }
@@ -16740,7 +16788,7 @@ function checkVaultTarget(current, target, options) {
16740
16788
  return { kind: "ok" };
16741
16789
  }
16742
16790
  async function canWriteInto(dir) {
16743
- const probe = temporaryPathFor(path41.join(dir, ".escritura"));
16791
+ const probe = temporaryPathFor(path42.join(dir, ".escritura"));
16744
16792
  try {
16745
16793
  await mkdir13(dir, { recursive: true });
16746
16794
  if (!(await stat23(dir)).isDirectory()) return false;
@@ -16847,11 +16895,11 @@ function serializeSession(input) {
16847
16895
 
16848
16896
  // packages/server/src/vault/writer.ts
16849
16897
  import { readdir as readdir15, rm as rm6, stat as stat25 } from "node:fs/promises";
16850
- import path43 from "node:path";
16898
+ import path44 from "node:path";
16851
16899
 
16852
16900
  // packages/server/src/vault/memory-copy.ts
16853
16901
  import { mkdir as mkdir14, readdir as readdir14, readFile as readFile16, stat as stat24, utimes as utimes2 } from "node:fs/promises";
16854
- import path42 from "node:path";
16902
+ import path43 from "node:path";
16855
16903
  var MEMORY_FILE_MAX_BYTES = 1024 * 1024;
16856
16904
  var MEMORY_MAX_FILES = 500;
16857
16905
  var MEMORY_SOURCE_FILE = "source.json";
@@ -16917,7 +16965,7 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
16917
16965
  await mkdir14(target, { recursive: true });
16918
16966
  };
16919
16967
  for (const note of notes) {
16920
- const destination = path42.join(target, note.name);
16968
+ const destination = path43.join(target, note.name);
16921
16969
  if (await isUpToDate(destination, note)) continue;
16922
16970
  let content;
16923
16971
  try {
@@ -16933,7 +16981,7 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
16933
16981
  result.files += 1;
16934
16982
  result.bytes += content.length;
16935
16983
  }
16936
- const source = path42.join(target, MEMORY_SOURCE_FILE);
16984
+ const source = path43.join(target, MEMORY_SOURCE_FILE);
16937
16985
  if (result.files > 0 || !await exists3(source)) {
16938
16986
  await prepare();
16939
16987
  await writeFileAtomic(source, `${JSON.stringify({ cwd, copiedAt: deps.now() })}
@@ -17527,17 +17575,17 @@ var VaultWriter = class {
17527
17575
  const folders = [dir];
17528
17576
  const subfolders = async (parent) => {
17529
17577
  try {
17530
- return (await readdir15(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path43.join(parent, entry.name));
17578
+ return (await readdir15(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path44.join(parent, entry.name));
17531
17579
  } catch {
17532
17580
  return [];
17533
17581
  }
17534
17582
  };
17535
17583
  for (const agentDir of await subfolders(sessionsRoot2(dir))) {
17536
- if (!SESSION_AGENT_IDS.includes(path43.basename(agentDir))) continue;
17584
+ if (!SESSION_AGENT_IDS.includes(path44.basename(agentDir))) continue;
17537
17585
  folders.push(agentDir);
17538
17586
  folders.push(...(await subfolders(agentDir)).filter((folder) => folder.endsWith(".assets")));
17539
17587
  }
17540
- folders.push(...await subfolders(path43.join(dir, "memory")));
17588
+ folders.push(...await subfolders(path44.join(dir, "memory")));
17541
17589
  for (const folder of folders) {
17542
17590
  let names;
17543
17591
  try {
@@ -17547,7 +17595,7 @@ var VaultWriter = class {
17547
17595
  }
17548
17596
  for (const name of names) {
17549
17597
  if (!OWN_TEMP_FILE2.test(name)) continue;
17550
- const file = path43.join(folder, name);
17598
+ const file = path44.join(folder, name);
17551
17599
  try {
17552
17600
  const info = await stat25(file);
17553
17601
  if (info.isFile() && now - info.mtimeMs > TEMP_MAX_AGE_MS) await rm6(file, { force: true });
@@ -17737,7 +17785,7 @@ var VaultService = class {
17737
17785
  async setDir(target) {
17738
17786
  const { platform, agents, settings: settings2, catalog } = this.options;
17739
17787
  const current = this.dir();
17740
- const protectedDirs = [...agents.protectedDirs(), path44.join(this.options.homeDir ?? homedir12(), ".gemini")];
17788
+ const protectedDirs = [...agents.protectedDirs(), path45.join(this.options.homeDir ?? homedir12(), ".gemini")];
17741
17789
  const check = checkVaultTarget(current, target, { protectedDirs, platform });
17742
17790
  if (check.kind === "refused") throw new VaultError(check.text);
17743
17791
  if (check.kind === "same") return;
@@ -17802,7 +17850,7 @@ var VaultService = class {
17802
17850
  fromCopy: true
17803
17851
  });
17804
17852
  const file = sessionExportFile(dir, agent, sessionId);
17805
- await mkdir15(path44.dirname(file), { recursive: true });
17853
+ await mkdir15(path45.dirname(file), { recursive: true });
17806
17854
  await writeFileAtomic(file, text, this.writeDeps);
17807
17855
  this.openWithSystem(file);
17808
17856
  return file;
@@ -17906,7 +17954,7 @@ var VaultService = class {
17906
17954
  continue;
17907
17955
  }
17908
17956
  const fileName = this.exportFileName(summary, names);
17909
- await writeFileAtomic(path44.join(folder, fileName), text, this.writeDeps);
17957
+ await writeFileAtomic(path45.join(folder, fileName), text, this.writeDeps);
17910
17958
  entries.push({
17911
17959
  updatedAt: summary.updatedAt,
17912
17960
  agentLabel: this.labelOf(summary.agent),
@@ -17917,9 +17965,9 @@ var VaultService = class {
17917
17965
  });
17918
17966
  await nextTurn3();
17919
17967
  }
17920
- const name = project.cwd.length > 0 ? path44.basename(project.cwd) || project.cwd : project.fallbackName;
17968
+ const name = project.cwd.length > 0 ? path45.basename(project.cwd) || project.cwd : project.fallbackName;
17921
17969
  const index = renderProjectIndex({ name, cwd: project.cwd, exportedAt: this.now(), entries, skipped });
17922
- await writeFileAtomic(path44.join(folder, "index.md"), index, this.writeDeps);
17970
+ await writeFileAtomic(path45.join(folder, "index.md"), index, this.writeDeps);
17923
17971
  this.openWithSystem(folder);
17924
17972
  return { sessions: entries.length, skipped, folder };
17925
17973
  }
@@ -19411,7 +19459,7 @@ function attachTerminalSocket(options) {
19411
19459
 
19412
19460
  // packages/server/src/vault/catalog.ts
19413
19461
  import { open as open7, readdir as readdir16, stat as stat27 } from "node:fs/promises";
19414
- import path45 from "node:path";
19462
+ import path46 from "node:path";
19415
19463
  import { createInterface as createInterface2 } from "node:readline";
19416
19464
  var HEADER_MAX_BYTES = 64 * 1024;
19417
19465
  var LOAD_CONCURRENCY = 16;
@@ -19431,7 +19479,7 @@ async function assetBytesOf(dir, agent, sessionId) {
19431
19479
  for (const name of names) {
19432
19480
  if (!VAULT_ASSET_NAME_PATTERN.test(name)) continue;
19433
19481
  try {
19434
- const info = await stat27(path45.join(folder, name));
19482
+ const info = await stat27(path46.join(folder, name));
19435
19483
  if (info.isFile()) total += info.size;
19436
19484
  } catch {
19437
19485
  }
@@ -19651,15 +19699,15 @@ var VaultCatalog = class {
19651
19699
  };
19652
19700
 
19653
19701
  // packages/server/src/index.ts
19654
- var serverDir = path46.dirname(fileURLToPath2(import.meta.url));
19702
+ var serverDir = path47.dirname(fileURLToPath2(import.meta.url));
19655
19703
  var isPackaged = true;
19656
- var webRoot = isPackaged ? serverDir : path46.resolve(serverDir, "../../web");
19657
- var webDist = isPackaged ? path46.join(serverDir, "web") : path46.join(webRoot, "dist");
19658
- var repoRoot = path46.resolve(serverDir, "../../..");
19704
+ var webRoot = isPackaged ? serverDir : path47.resolve(serverDir, "../../web");
19705
+ var webDist = isPackaged ? path47.join(serverDir, "web") : path47.join(webRoot, "dist");
19706
+ var repoRoot = path47.resolve(serverDir, "../../..");
19659
19707
  var isProduction = isPackaged || process.argv.includes("--prod");
19660
19708
  function resolveDefaultCwd() {
19661
19709
  const override = process.env["AGENT_WORKBENCH_CWD"];
19662
- if (override !== void 0 && override.length > 0) return path46.resolve(override);
19710
+ if (override !== void 0 && override.length > 0) return path47.resolve(override);
19663
19711
  if (isPackaged) return process.cwd();
19664
19712
  return repoRoot;
19665
19713
  }
@@ -19695,7 +19743,7 @@ async function mountDevUi(app, httpServer) {
19695
19743
  }
19696
19744
  var LOOKS_LIKE_FILE = /\.[a-zA-Z0-9]{1,8}$/;
19697
19745
  function mountBuiltUi(app) {
19698
- const indexHtml = path46.join(webDist, "index.html");
19746
+ const indexHtml = path47.join(webDist, "index.html");
19699
19747
  if (!existsSync4(indexHtml)) {
19700
19748
  throw new Error(
19701
19749
  `There's no compiled interface in ${webDist}.
@@ -19751,6 +19799,7 @@ async function main() {
19751
19799
  const movedFrom = await migrateLegacyConfigDir();
19752
19800
  if (movedFrom !== null) console.log(`Configuration moved from ${movedFrom} to ${appConfigDir()}`);
19753
19801
  await agents.prepareAll();
19802
+ ensurePtyHelperExecutable();
19754
19803
  const store = new WorkspaceStore();
19755
19804
  const archived = new ArchivedSessions();
19756
19805
  await archived.load();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-workbench",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Local visual interface for coding-agent CLIs: Claude Code, Codex, OpenCode and Antigravity CLI",
5
5
  "license": "MIT",
6
6
  "author": "Christian Velasquez",