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.
- package/README.md +6 -4
- package/dist/server.js +173 -124
- 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
|
+

|
|
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
|
-

|
|
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
|
-
|
|
91
|
-
|
|
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
|
|
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
|
|
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:
|
|
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 ?
|
|
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:
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
11397
|
+
const folder = path31.join(cwd, MEMORY_DIR);
|
|
11350
11398
|
const folderExists = await isDirectory2(folder);
|
|
11351
|
-
const indexExists = folderExists && await isFile(
|
|
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(
|
|
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 (
|
|
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(
|
|
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(
|
|
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(
|
|
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", "",
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
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 =
|
|
11958
|
-
const geminiTarget =
|
|
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:
|
|
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:
|
|
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 =
|
|
12037
|
-
const absoluteCommonDir =
|
|
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 (
|
|
12042
|
-
const mainRoot =
|
|
12043
|
-
const inner =
|
|
12044
|
-
const sameFolder = inner.length === 0 || inner.startsWith("..") ||
|
|
12045
|
-
const mainPath = sameFolder ? mainRoot :
|
|
12046
|
-
const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(
|
|
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
|
|
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:
|
|
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 =
|
|
12174
|
-
|
|
12175
|
-
`.${
|
|
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 =
|
|
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 =
|
|
12465
|
+
const relative = path32.relative(cwd, candidate);
|
|
12418
12466
|
if (relative.length === 0) return true;
|
|
12419
|
-
if (relative.startsWith("..") ||
|
|
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
|
|
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(
|
|
12955
|
-
return style === "at-quoted" ? fileReference(
|
|
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 ??
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
13911
|
+
import path35 from "node:path";
|
|
13864
13912
|
function defaultVaultDir() {
|
|
13865
|
-
return
|
|
13913
|
+
return path35.join(appConfigDir(), "vault");
|
|
13866
13914
|
}
|
|
13867
13915
|
function vaultMarkerFile(dir) {
|
|
13868
|
-
return
|
|
13916
|
+
return path35.join(dir, "vault.json");
|
|
13869
13917
|
}
|
|
13870
13918
|
function sessionsRoot2(dir) {
|
|
13871
|
-
return
|
|
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
|
|
13933
|
+
return path35.join(sessionsRoot2(dir), agent);
|
|
13886
13934
|
}
|
|
13887
13935
|
function sessionFile(dir, agent, sessionId) {
|
|
13888
13936
|
assertSessionKey(agent, sessionId);
|
|
13889
|
-
return
|
|
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
|
|
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
|
|
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" ?
|
|
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
|
|
13954
|
+
return path35.join(dir, "memory", projectFolderName(cwd, platform));
|
|
13907
13955
|
}
|
|
13908
13956
|
function projectExportDir(dir, cwd, platform) {
|
|
13909
|
-
return
|
|
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
|
|
13963
|
+
return path35.join(dir, "export", `${name}-${hash}`);
|
|
13916
13964
|
}
|
|
13917
13965
|
function sessionExportFile(dir, agent, sessionId) {
|
|
13918
13966
|
assertSessionKey(agent, sessionId);
|
|
13919
|
-
return
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
14401
|
-
const temporary =
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
15701
|
+
const resolved = path40.resolve(target);
|
|
15654
15702
|
return dirs.some((dir) => {
|
|
15655
|
-
const protectedDir =
|
|
15656
|
-
return resolved === protectedDir || resolved.startsWith(protectedDir +
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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:
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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) =>
|
|
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(
|
|
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(
|
|
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 =
|
|
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(),
|
|
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(
|
|
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(
|
|
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 ?
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
19702
|
+
var serverDir = path47.dirname(fileURLToPath2(import.meta.url));
|
|
19655
19703
|
var isPackaged = true;
|
|
19656
|
-
var webRoot = isPackaged ? serverDir :
|
|
19657
|
-
var webDist = isPackaged ?
|
|
19658
|
-
var repoRoot =
|
|
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
|
|
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 =
|
|
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