agent-workbench 0.4.1 → 0.5.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 +17 -16
- package/dist/server.js +712 -374
- package/dist/web/assets/de-DlErJERu.js +1 -0
- package/dist/web/assets/es-C_iXI_cL.js +1 -0
- package/dist/web/assets/fr-DQrvpEX-.js +1 -0
- package/dist/web/assets/{highlight-nRm-DNTr.js → highlight-BxIlYHXS.js} +1 -1
- package/dist/web/assets/index-4vO1q72v.css +32 -0
- package/dist/web/assets/index-peCduls6.js +139 -0
- package/dist/web/assets/ja-D5vZsdVh.js +1 -0
- package/dist/web/assets/ko-BpH6AFJf.js +1 -0
- package/dist/web/assets/pt-BR-B9DfN4SI.js +1 -0
- package/dist/web/assets/ru-DLtRgmP7.js +1 -0
- package/dist/web/assets/zh-CN-qo3wh4w3.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/de-CXvJ7uwt.js +0 -1
- package/dist/web/assets/es-BrWQ_4kO.js +0 -1
- package/dist/web/assets/fr-Cv_qM9E8.js +0 -1
- package/dist/web/assets/index-BFpUqUWT.js +0 -139
- package/dist/web/assets/index-BnrUWT0F.css +0 -32
- package/dist/web/assets/ja-h9u8sCMK.js +0 -1
- package/dist/web/assets/ko-m0EzMLN6.js +0 -1
- package/dist/web/assets/pt-BR-BAHKndFr.js +0 -1
- package/dist/web/assets/ru-D1S49VN_.js +0 -1
- package/dist/web/assets/zh-CN-CH6r3KGh.js +0 -1
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 path51 from "node:path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8
8
|
import express from "express";
|
|
9
9
|
|
|
@@ -153,7 +153,8 @@ function parseSessionSummary(value) {
|
|
|
153
153
|
var CONVERSATION_ROLES = ["user", "assistant"];
|
|
154
154
|
var CONVERSATION_IMAGE_SOURCES = [
|
|
155
155
|
"content",
|
|
156
|
-
"attachment"
|
|
156
|
+
"attachment",
|
|
157
|
+
"pasted"
|
|
157
158
|
];
|
|
158
159
|
var CONVERSATION_NOTICES = [
|
|
159
160
|
"compacted",
|
|
@@ -277,7 +278,7 @@ function parseConversationPart(value) {
|
|
|
277
278
|
const index = asFiniteNumber(record["index"]);
|
|
278
279
|
const mediaType = asString(record["mediaType"]);
|
|
279
280
|
if (index === null || mediaType === null) return null;
|
|
280
|
-
const source = record["source"]
|
|
281
|
+
const source = asLiteral(record["source"], CONVERSATION_IMAGE_SOURCES) ?? "content";
|
|
281
282
|
return { kind: "image", index, mediaType, source };
|
|
282
283
|
}
|
|
283
284
|
case "notice": {
|
|
@@ -680,6 +681,38 @@ function cleanDeviceLabel(label) {
|
|
|
680
681
|
return label.replace(/\s+/g, " ").trim().slice(0, REMOTE_DEVICE_LABEL_MAX_CHARS);
|
|
681
682
|
}
|
|
682
683
|
|
|
684
|
+
// packages/shared/src/settings.ts
|
|
685
|
+
var CONSOLE_SHELL_IDS = [
|
|
686
|
+
"auto",
|
|
687
|
+
"pwsh",
|
|
688
|
+
"powershell",
|
|
689
|
+
"cmd",
|
|
690
|
+
"git-bash",
|
|
691
|
+
"login",
|
|
692
|
+
"bash",
|
|
693
|
+
"sh",
|
|
694
|
+
"zsh"
|
|
695
|
+
];
|
|
696
|
+
function asConsoleShellId(value) {
|
|
697
|
+
return asLiteral(value, CONSOLE_SHELL_IDS);
|
|
698
|
+
}
|
|
699
|
+
function asAgentIdList(value) {
|
|
700
|
+
if (!Array.isArray(value)) return null;
|
|
701
|
+
return [...new Set(value.filter((item) => typeof item === "string" && isAgentId(item)))];
|
|
702
|
+
}
|
|
703
|
+
function parseAppSettingsChange(record) {
|
|
704
|
+
const rawShell = record["consoleShell"];
|
|
705
|
+
const rawAgents = record["disabledAgents"];
|
|
706
|
+
const consoleShell = rawShell === void 0 ? void 0 : asConsoleShellId(rawShell);
|
|
707
|
+
const disabledAgents = rawAgents === void 0 ? void 0 : asAgentIdList(rawAgents);
|
|
708
|
+
if (consoleShell === null || disabledAgents === null) return null;
|
|
709
|
+
if (consoleShell === void 0 && disabledAgents === void 0) return null;
|
|
710
|
+
return {
|
|
711
|
+
...consoleShell !== void 0 ? { consoleShell } : {},
|
|
712
|
+
...disabledAgents !== void 0 ? { disabledAgents } : {}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
683
716
|
// packages/shared/src/protocol.ts
|
|
684
717
|
var PROTOCOL_VERSION = 8;
|
|
685
718
|
var WS_PATH = "/ws";
|
|
@@ -1070,6 +1103,10 @@ function parseClientMessage(raw) {
|
|
|
1070
1103
|
const draft = parseComposerDraft(record["draft"]);
|
|
1071
1104
|
return terminalId === null || draft === null ? null : { type: "composer.draft", terminalId, draft };
|
|
1072
1105
|
}
|
|
1106
|
+
case "settings.update": {
|
|
1107
|
+
const change = parseAppSettingsChange(record);
|
|
1108
|
+
return change === null ? null : { type: "settings.update", ...change };
|
|
1109
|
+
}
|
|
1073
1110
|
default:
|
|
1074
1111
|
return null;
|
|
1075
1112
|
}
|
|
@@ -4183,7 +4220,7 @@ function createAntigravityAdapter(options = {}) {
|
|
|
4183
4220
|
|
|
4184
4221
|
// packages/server/src/agents/claude-code/index.ts
|
|
4185
4222
|
import { homedir as homedir5 } from "node:os";
|
|
4186
|
-
import
|
|
4223
|
+
import path21 from "node:path";
|
|
4187
4224
|
|
|
4188
4225
|
// packages/server/src/agents/claude-code/agent-defaults.ts
|
|
4189
4226
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
@@ -4519,8 +4556,8 @@ function claudeCodeEnvironment(base) {
|
|
|
4519
4556
|
}
|
|
4520
4557
|
|
|
4521
4558
|
// packages/server/src/agents/claude-code/history.ts
|
|
4522
|
-
import { readdir as readdir7, stat as
|
|
4523
|
-
import
|
|
4559
|
+
import { readdir as readdir7, stat as stat12 } from "node:fs/promises";
|
|
4560
|
+
import path20 from "node:path";
|
|
4524
4561
|
|
|
4525
4562
|
// packages/server/src/agents/claude-code/plans-store.ts
|
|
4526
4563
|
import { readFile as readFile8, stat as stat9 } from "node:fs/promises";
|
|
@@ -4694,10 +4731,30 @@ async function readPlan(target, ref) {
|
|
|
4694
4731
|
}
|
|
4695
4732
|
|
|
4696
4733
|
// packages/server/src/agents/claude-code/session-follower.ts
|
|
4697
|
-
import
|
|
4734
|
+
import path19 from "node:path";
|
|
4698
4735
|
|
|
4699
4736
|
// packages/server/src/agents/claude-code/jsonl-events.ts
|
|
4737
|
+
import path17 from "node:path";
|
|
4738
|
+
|
|
4739
|
+
// packages/server/src/paste-paths.ts
|
|
4740
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
4700
4741
|
import path16 from "node:path";
|
|
4742
|
+
function pasteRoot() {
|
|
4743
|
+
return path16.join(tmpdir3(), "agent-workbench", "pasted");
|
|
4744
|
+
}
|
|
4745
|
+
var PASTED_IMAGE = /(?:^|[\\/])agent-workbench[\\/]pasted[\\/][A-Za-z0-9_-]+[\\/]pegada-\d+-[0-9a-f]{8}\.(png|jpg|gif|webp)$/i;
|
|
4746
|
+
var MEDIA_TYPES = {
|
|
4747
|
+
png: "image/png",
|
|
4748
|
+
jpg: "image/jpeg",
|
|
4749
|
+
gif: "image/gif",
|
|
4750
|
+
webp: "image/webp"
|
|
4751
|
+
};
|
|
4752
|
+
function pastedImageMediaType(filePath) {
|
|
4753
|
+
const extension = PASTED_IMAGE.exec(filePath)?.[1];
|
|
4754
|
+
return extension === void 0 ? null : MEDIA_TYPES[extension.toLowerCase()] ?? null;
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4757
|
+
// packages/server/src/agents/claude-code/jsonl-events.ts
|
|
4701
4758
|
var HARNESS_TAGS = [
|
|
4702
4759
|
"system-reminder",
|
|
4703
4760
|
"task-notification",
|
|
@@ -4880,9 +4937,18 @@ function toQueuedUserEvent(record, lineNumber, limits) {
|
|
|
4880
4937
|
if (!isHumanOrigin(attachmentRecord["origin"])) return null;
|
|
4881
4938
|
const prompt = attachmentRecord["prompt"];
|
|
4882
4939
|
if (typeof prompt !== "string") return null;
|
|
4883
|
-
const cleaned = cleanUserText(prompt);
|
|
4884
|
-
|
|
4885
|
-
|
|
4940
|
+
const { text: cleaned, images } = takePastedImages(cleanUserText(prompt));
|
|
4941
|
+
const parts = images.map((image, index) => ({
|
|
4942
|
+
kind: "image",
|
|
4943
|
+
index,
|
|
4944
|
+
mediaType: image.mediaType,
|
|
4945
|
+
source: "pasted"
|
|
4946
|
+
}));
|
|
4947
|
+
if (cleaned.length > 0) {
|
|
4948
|
+
const { text, truncated } = cut(cleaned, limits.textMaxChars);
|
|
4949
|
+
parts.push({ kind: "text", text, truncated });
|
|
4950
|
+
}
|
|
4951
|
+
if (parts.length === 0) return null;
|
|
4886
4952
|
const uuid = record["uuid"];
|
|
4887
4953
|
return {
|
|
4888
4954
|
eventId: typeof uuid === "string" && uuid.length > 0 ? uuid : `line-${lineNumber}`,
|
|
@@ -4890,7 +4956,7 @@ function toQueuedUserEvent(record, lineNumber, limits) {
|
|
|
4890
4956
|
// El `timestamp` de la linea es el de cuando se escribio, no el de cuando
|
|
4891
4957
|
// se absorbio. Es el que corresponde: es cuando el usuario lo mando.
|
|
4892
4958
|
at: readTimestamp(record["timestamp"] ?? attachmentRecord["timestamp"]),
|
|
4893
|
-
parts
|
|
4959
|
+
parts,
|
|
4894
4960
|
model: null,
|
|
4895
4961
|
usage: null,
|
|
4896
4962
|
effort: null,
|
|
@@ -4956,6 +5022,30 @@ function stripFileReference(text, filename) {
|
|
|
4956
5022
|
}
|
|
4957
5023
|
return text;
|
|
4958
5024
|
}
|
|
5025
|
+
function sameFilePath(a, b) {
|
|
5026
|
+
return normalizePath(a) === normalizePath(b);
|
|
5027
|
+
}
|
|
5028
|
+
function takePastedImages(text) {
|
|
5029
|
+
const images = [];
|
|
5030
|
+
const tokens = /(^|\s)@(?:"([^"]*)"|(\S+))/g;
|
|
5031
|
+
let match = tokens.exec(text);
|
|
5032
|
+
while (match !== null) {
|
|
5033
|
+
const raw = match[2] ?? match[3] ?? "";
|
|
5034
|
+
const mediaType = pastedImageMediaType(raw);
|
|
5035
|
+
if (mediaType !== null) images.push({ path: raw, mediaType });
|
|
5036
|
+
match = tokens.exec(text);
|
|
5037
|
+
}
|
|
5038
|
+
let rest = text;
|
|
5039
|
+
for (const image of images) rest = stripFileReference(rest, image.path);
|
|
5040
|
+
return { text: rest, images };
|
|
5041
|
+
}
|
|
5042
|
+
function queuedPastedImages(record) {
|
|
5043
|
+
const attachment = recordOf(record["attachment"]);
|
|
5044
|
+
if (attachment === null || attachment["type"] !== "queued_command") return [];
|
|
5045
|
+
const prompt = attachment["prompt"];
|
|
5046
|
+
if (typeof prompt !== "string") return [];
|
|
5047
|
+
return takePastedImages(cleanUserText(prompt)).images.map((image) => image.path);
|
|
5048
|
+
}
|
|
4959
5049
|
function toConversationEvent(record, lineNumber, limits = TRANSPORT_LIMITS) {
|
|
4960
5050
|
const type = record["type"];
|
|
4961
5051
|
if (record["isMeta"] === true) return null;
|
|
@@ -5033,7 +5123,7 @@ function documentRefOf(value, target) {
|
|
|
5033
5123
|
if (!value.toLowerCase().endsWith(".md")) return null;
|
|
5034
5124
|
if (value.includes("\0")) return null;
|
|
5035
5125
|
if (target.cwd.length === 0) return null;
|
|
5036
|
-
const absolute =
|
|
5126
|
+
const absolute = path17.isAbsolute(value) ? value : path17.resolve(target.cwd, value);
|
|
5037
5127
|
if (target.sessionId.length > 0) {
|
|
5038
5128
|
const inScratch = relativeInside(scratchRoot(target.cwd, target.sessionId), absolute);
|
|
5039
5129
|
if (inScratch !== null) return `tmp:${inScratch}`;
|
|
@@ -5042,15 +5132,15 @@ function documentRefOf(value, target) {
|
|
|
5042
5132
|
return inProject === null ? null : `proj:${inProject}`;
|
|
5043
5133
|
}
|
|
5044
5134
|
function relativeInside(root, absolute) {
|
|
5045
|
-
const relative =
|
|
5135
|
+
const relative = path17.relative(root, absolute);
|
|
5046
5136
|
if (relative.length === 0) return null;
|
|
5047
|
-
if (relative.startsWith("..") ||
|
|
5137
|
+
if (relative.startsWith("..") || path17.isAbsolute(relative)) {
|
|
5048
5138
|
if (process.platform !== "win32") return null;
|
|
5049
|
-
const lower =
|
|
5050
|
-
if (lower.length === 0 || lower.startsWith("..") ||
|
|
5051
|
-
return lower.split(
|
|
5139
|
+
const lower = path17.relative(root.toLowerCase(), absolute.toLowerCase());
|
|
5140
|
+
if (lower.length === 0 || lower.startsWith("..") || path17.isAbsolute(lower)) return null;
|
|
5141
|
+
return lower.split(path17.sep).join("/");
|
|
5052
5142
|
}
|
|
5053
|
-
return relative.split(
|
|
5143
|
+
return relative.split(path17.sep).join("/");
|
|
5054
5144
|
}
|
|
5055
5145
|
function planFileNameOf(value) {
|
|
5056
5146
|
if (typeof value !== "string" || value.length === 0) return null;
|
|
@@ -5600,13 +5690,50 @@ var ConversationFollower = class {
|
|
|
5600
5690
|
};
|
|
5601
5691
|
|
|
5602
5692
|
// packages/server/src/agents/claude-code/conversation-image.ts
|
|
5603
|
-
import { open as open5 } from "node:fs/promises";
|
|
5693
|
+
import { open as open5, readFile as readFile9, stat as stat10 } from "node:fs/promises";
|
|
5694
|
+
import path18 from "node:path";
|
|
5695
|
+
|
|
5696
|
+
// packages/server/src/image-signature.ts
|
|
5697
|
+
var FORMATS = [
|
|
5698
|
+
{
|
|
5699
|
+
format: { ext: "png", mediaType: "image/png" },
|
|
5700
|
+
test: (b) => b.length > 8 && b.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex"))
|
|
5701
|
+
},
|
|
5702
|
+
{
|
|
5703
|
+
format: { ext: "jpg", mediaType: "image/jpeg" },
|
|
5704
|
+
test: (b) => b.length > 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
|
|
5705
|
+
},
|
|
5706
|
+
{
|
|
5707
|
+
format: { ext: "gif", mediaType: "image/gif" },
|
|
5708
|
+
test: (b) => b.length > 6 && b.subarray(0, 4).toString("ascii") === "GIF8"
|
|
5709
|
+
},
|
|
5710
|
+
{
|
|
5711
|
+
format: { ext: "webp", mediaType: "image/webp" },
|
|
5712
|
+
test: (b) => b.length > 12 && b.subarray(0, 4).toString("ascii") === "RIFF" && b.subarray(8, 12).toString("ascii") === "WEBP"
|
|
5713
|
+
}
|
|
5714
|
+
];
|
|
5715
|
+
var IMAGE_MEDIA_TYPES = new Set(
|
|
5716
|
+
FORMATS.map((entry) => entry.format.mediaType)
|
|
5717
|
+
);
|
|
5718
|
+
function detectImageFormat(bytes) {
|
|
5719
|
+
return FORMATS.find((entry) => entry.test(bytes))?.format ?? null;
|
|
5720
|
+
}
|
|
5721
|
+
|
|
5722
|
+
// packages/server/src/agents/claude-code/conversation-image.ts
|
|
5604
5723
|
var CHUNK_SIZE2 = 256 * 1024;
|
|
5605
5724
|
var NEWLINE2 = 10;
|
|
5606
5725
|
var MAX_IMAGE_BYTES = MAX_SUBMIT_IMAGE_BYTES;
|
|
5607
|
-
async function loadConversationImage(filePath, eventId, index, source = "content") {
|
|
5726
|
+
async function loadConversationImage(filePath, eventId, index, source = "content", pastedDir = pasteRoot()) {
|
|
5608
5727
|
const handle = await open5(filePath, "r").catch(() => null);
|
|
5609
5728
|
if (handle === null) return null;
|
|
5729
|
+
try {
|
|
5730
|
+
if (source === "pasted") return await findPastedImage(linesOf(handle), eventId, index, pastedDir);
|
|
5731
|
+
return await findInLines(linesOf(handle), eventId, index, source);
|
|
5732
|
+
} finally {
|
|
5733
|
+
await handle.close();
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
async function findInLines(lines, eventId, index, source) {
|
|
5610
5737
|
let seen = 0;
|
|
5611
5738
|
const chain = /* @__PURE__ */ new Set([eventId]);
|
|
5612
5739
|
const consider = (line) => {
|
|
@@ -5629,56 +5756,126 @@ async function loadConversationImage(filePath, eventId, index, source = "content
|
|
|
5629
5756
|
if (typeof ownId === "string" && ownId.length > 0) chain.add(ownId);
|
|
5630
5757
|
return seen++ === index ? found : null;
|
|
5631
5758
|
};
|
|
5632
|
-
|
|
5633
|
-
const
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5759
|
+
for await (const line of lines) {
|
|
5760
|
+
const found = consider(line);
|
|
5761
|
+
if (found !== null) return found;
|
|
5762
|
+
}
|
|
5763
|
+
return null;
|
|
5764
|
+
}
|
|
5765
|
+
async function* linesOf(handle) {
|
|
5766
|
+
const buffer = Buffer.allocUnsafe(CHUNK_SIZE2);
|
|
5767
|
+
let pending = Buffer.alloc(0);
|
|
5768
|
+
let offset = 0;
|
|
5769
|
+
for (; ; ) {
|
|
5770
|
+
const { bytesRead } = await handle.read(buffer, 0, CHUNK_SIZE2, offset);
|
|
5771
|
+
if (bytesRead === 0) break;
|
|
5772
|
+
offset += bytesRead;
|
|
5773
|
+
const combined = Buffer.concat([pending, buffer.subarray(0, bytesRead)]);
|
|
5774
|
+
let start = 0;
|
|
5775
|
+
let newlineIndex = combined.indexOf(NEWLINE2, start);
|
|
5776
|
+
while (newlineIndex !== -1) {
|
|
5777
|
+
yield combined.subarray(start, newlineIndex).toString("utf8");
|
|
5778
|
+
start = newlineIndex + 1;
|
|
5779
|
+
newlineIndex = combined.indexOf(NEWLINE2, start);
|
|
5780
|
+
}
|
|
5781
|
+
pending = Buffer.from(combined.subarray(start));
|
|
5782
|
+
}
|
|
5783
|
+
if (pending.length > 0) yield pending.toString("utf8");
|
|
5784
|
+
}
|
|
5785
|
+
async function findPastedImage(lines, eventId, index, pastedDir) {
|
|
5786
|
+
let target = null;
|
|
5787
|
+
let readId = null;
|
|
5788
|
+
for await (const line of lines) {
|
|
5789
|
+
if (target === null) {
|
|
5790
|
+
if (!line.includes(eventId)) continue;
|
|
5791
|
+
const record = parseJsonlLine(line);
|
|
5792
|
+
if (record === null || record["uuid"] !== eventId) continue;
|
|
5793
|
+
target = queuedPastedImages(record)[index] ?? null;
|
|
5794
|
+
if (target === null) return null;
|
|
5795
|
+
const fromDisk = await readPastedFile(target, pastedDir);
|
|
5796
|
+
if (fromDisk !== null) return fromDisk;
|
|
5797
|
+
continue;
|
|
5798
|
+
}
|
|
5799
|
+
if (readId === null) {
|
|
5800
|
+
if (line.includes('"Read"')) readId = readToolUseOf(parseJsonlLine(line), target);
|
|
5801
|
+
continue;
|
|
5651
5802
|
}
|
|
5652
|
-
if (
|
|
5803
|
+
if (!line.includes(readId)) continue;
|
|
5804
|
+
const result = toolResultOf(parseJsonlLine(line), readId);
|
|
5805
|
+
if (result === null) continue;
|
|
5806
|
+
const content = result["content"];
|
|
5807
|
+
const image = Array.isArray(content) ? firstImage(content) : null;
|
|
5808
|
+
if (image !== null) return image;
|
|
5809
|
+
readId = null;
|
|
5810
|
+
}
|
|
5811
|
+
return null;
|
|
5812
|
+
}
|
|
5813
|
+
async function readPastedFile(target, pastedDir) {
|
|
5814
|
+
try {
|
|
5815
|
+
const inside = await resolveInside(pastedDir, path18.relative(pastedDir, target), { mustExist: true });
|
|
5816
|
+
const info = await stat10(inside);
|
|
5817
|
+
if (!info.isFile() || info.size === 0 || info.size > MAX_IMAGE_BYTES) return null;
|
|
5818
|
+
const bytes = await readFile9(inside);
|
|
5819
|
+
const format = detectImageFormat(bytes);
|
|
5820
|
+
return format === null ? null : { mediaType: format.mediaType, data: bytes.toString("base64") };
|
|
5821
|
+
} catch {
|
|
5653
5822
|
return null;
|
|
5654
|
-
} finally {
|
|
5655
|
-
await handle.close();
|
|
5656
5823
|
}
|
|
5657
5824
|
}
|
|
5825
|
+
function readToolUseOf(record, target) {
|
|
5826
|
+
if (record === null || record["type"] !== "assistant") return null;
|
|
5827
|
+
const content = recordOf(record["message"])?.["content"];
|
|
5828
|
+
if (!Array.isArray(content)) return null;
|
|
5829
|
+
for (const block of content) {
|
|
5830
|
+
const tool = recordOf(block);
|
|
5831
|
+
if (tool === null || tool["type"] !== "tool_use" || tool["name"] !== "Read") continue;
|
|
5832
|
+
const id = tool["id"];
|
|
5833
|
+
const filePath = recordOf(tool["input"])?.["file_path"];
|
|
5834
|
+
if (typeof id === "string" && id.length > 0 && typeof filePath === "string" && sameFilePath(filePath, target)) {
|
|
5835
|
+
return id;
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
return null;
|
|
5839
|
+
}
|
|
5840
|
+
function toolResultOf(record, toolUseId) {
|
|
5841
|
+
if (record === null || record["type"] !== "user") return null;
|
|
5842
|
+
const content = recordOf(record["message"])?.["content"];
|
|
5843
|
+
if (!Array.isArray(content)) return null;
|
|
5844
|
+
for (const block of content) {
|
|
5845
|
+
const result = recordOf(block);
|
|
5846
|
+
if (result !== null && result["type"] === "tool_result" && result["tool_use_id"] === toolUseId) return result;
|
|
5847
|
+
}
|
|
5848
|
+
return null;
|
|
5849
|
+
}
|
|
5658
5850
|
function tooBig(data) {
|
|
5659
5851
|
return data.length > MAX_IMAGE_BYTES * 1.4;
|
|
5660
5852
|
}
|
|
5661
|
-
function
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
const
|
|
5668
|
-
if (typeof block !== "object" || block === null) return null;
|
|
5669
|
-
const blockRecord = block;
|
|
5670
|
-
if (blockRecord["type"] !== "image") return null;
|
|
5671
|
-
const source = blockRecord["source"];
|
|
5672
|
-
if (typeof source !== "object" || source === null) return null;
|
|
5673
|
-
const sourceRecord = source;
|
|
5674
|
-
const data = sourceRecord["data"];
|
|
5675
|
-
const mediaType = sourceRecord["media_type"];
|
|
5853
|
+
function imageFromBlock(value) {
|
|
5854
|
+
const block = recordOf(value);
|
|
5855
|
+
if (block === null || block["type"] !== "image") return null;
|
|
5856
|
+
const source = recordOf(block["source"]);
|
|
5857
|
+
if (source === null) return null;
|
|
5858
|
+
const data = source["data"];
|
|
5859
|
+
const mediaType = source["media_type"];
|
|
5676
5860
|
if (typeof data !== "string" || data.length === 0 || tooBig(data)) return null;
|
|
5677
5861
|
return {
|
|
5678
5862
|
mediaType: typeof mediaType === "string" ? mediaType : "image/png",
|
|
5679
5863
|
data
|
|
5680
5864
|
};
|
|
5681
5865
|
}
|
|
5866
|
+
function firstImage(blocks) {
|
|
5867
|
+
for (const block of blocks) {
|
|
5868
|
+
const image = imageFromBlock(block);
|
|
5869
|
+
if (image !== null) return image;
|
|
5870
|
+
}
|
|
5871
|
+
return null;
|
|
5872
|
+
}
|
|
5873
|
+
function imageFromContent(record, eventId, index) {
|
|
5874
|
+
if (record["uuid"] !== eventId) return null;
|
|
5875
|
+
const content = recordOf(record["message"])?.["content"];
|
|
5876
|
+
if (!Array.isArray(content)) return null;
|
|
5877
|
+
return imageFromBlock(content[index]);
|
|
5878
|
+
}
|
|
5682
5879
|
function imageFromAttachment(record) {
|
|
5683
5880
|
if (record["type"] !== "attachment") return null;
|
|
5684
5881
|
const attachment = record["attachment"];
|
|
@@ -5750,7 +5947,7 @@ var ClaudeCodeSessionFollower = class {
|
|
|
5750
5947
|
}
|
|
5751
5948
|
noticeChange(filePath) {
|
|
5752
5949
|
if (isSameFilePath(this.inner.filePath, filePath, process.platform)) return true;
|
|
5753
|
-
if (this.inner.getState() === "waiting" &&
|
|
5950
|
+
if (this.inner.getState() === "waiting" && path19.basename(filePath).toLowerCase() === `${this.sessionId.toLowerCase()}.jsonl`) {
|
|
5754
5951
|
console.warn(
|
|
5755
5952
|
`[conversation] session ${this.sessionId.slice(0, 8)} wrote to ${filePath}, not to the computed path; moving there.`
|
|
5756
5953
|
);
|
|
@@ -5797,7 +5994,7 @@ var ClaudeCodeSessionFollower = class {
|
|
|
5797
5994
|
};
|
|
5798
5995
|
|
|
5799
5996
|
// packages/server/src/agents/claude-code/session-scan.ts
|
|
5800
|
-
import { stat as
|
|
5997
|
+
import { stat as stat11 } from "node:fs/promises";
|
|
5801
5998
|
var HEAD_MAX_LINES = 40;
|
|
5802
5999
|
var HEAD_MAX_BYTES = 512 * 1024;
|
|
5803
6000
|
var TAIL_MAX_BYTES = 64 * 1024;
|
|
@@ -5822,7 +6019,7 @@ function isRealUserMessage(record) {
|
|
|
5822
6019
|
return true;
|
|
5823
6020
|
}
|
|
5824
6021
|
async function scanSessionFile(filePath, sessionId) {
|
|
5825
|
-
const info = await
|
|
6022
|
+
const info = await stat11(filePath);
|
|
5826
6023
|
const [headLines, tailLines] = await Promise.all([
|
|
5827
6024
|
readHeadLines(filePath, { maxLines: HEAD_MAX_LINES, maxBytes: HEAD_MAX_BYTES }),
|
|
5828
6025
|
readTailLines(filePath, { maxBytes: TAIL_MAX_BYTES })
|
|
@@ -5895,7 +6092,7 @@ async function scanSessionFile(filePath, sessionId) {
|
|
|
5895
6092
|
var SESSION_EXTENSION = /\.jsonl$/i;
|
|
5896
6093
|
async function pathExists(candidate) {
|
|
5897
6094
|
try {
|
|
5898
|
-
await
|
|
6095
|
+
await stat12(candidate);
|
|
5899
6096
|
return true;
|
|
5900
6097
|
} catch {
|
|
5901
6098
|
return false;
|
|
@@ -5904,8 +6101,8 @@ async function pathExists(candidate) {
|
|
|
5904
6101
|
function toItem(ref, info) {
|
|
5905
6102
|
return {
|
|
5906
6103
|
ref,
|
|
5907
|
-
sessionId:
|
|
5908
|
-
group:
|
|
6104
|
+
sessionId: path20.basename(ref).replace(SESSION_EXTENSION, ""),
|
|
6105
|
+
group: path20.basename(path20.dirname(ref)),
|
|
5909
6106
|
mtimeMs: info.mtimeMs,
|
|
5910
6107
|
sizeBytes: info.size
|
|
5911
6108
|
};
|
|
@@ -5935,14 +6132,14 @@ function createClaudeCodeHistory(variants) {
|
|
|
5935
6132
|
for (const slug of directories) {
|
|
5936
6133
|
let files;
|
|
5937
6134
|
try {
|
|
5938
|
-
files = (await readdir7(
|
|
6135
|
+
files = (await readdir7(path20.join(root, slug))).filter((name) => name.endsWith(".jsonl"));
|
|
5939
6136
|
} catch {
|
|
5940
6137
|
continue;
|
|
5941
6138
|
}
|
|
5942
6139
|
for (const file of files) {
|
|
5943
|
-
const ref =
|
|
6140
|
+
const ref = path20.join(root, slug, file);
|
|
5944
6141
|
try {
|
|
5945
|
-
items.push(toItem(ref, await
|
|
6142
|
+
items.push(toItem(ref, await stat12(ref)));
|
|
5946
6143
|
} catch {
|
|
5947
6144
|
}
|
|
5948
6145
|
}
|
|
@@ -5950,15 +6147,15 @@ function createClaudeCodeHistory(variants) {
|
|
|
5950
6147
|
return items;
|
|
5951
6148
|
},
|
|
5952
6149
|
async changedRefs(filePath) {
|
|
5953
|
-
const relative =
|
|
5954
|
-
const [slug] = relative.split(
|
|
6150
|
+
const relative = path20.relative(sessionsRoot(), filePath);
|
|
6151
|
+
const [slug] = relative.split(path20.sep);
|
|
5955
6152
|
if (slug === void 0 || slug.length === 0 || slug.startsWith("..")) return null;
|
|
5956
6153
|
if (!filePath.endsWith(".jsonl")) return null;
|
|
5957
6154
|
return [filePath];
|
|
5958
6155
|
},
|
|
5959
6156
|
async item(ref) {
|
|
5960
6157
|
try {
|
|
5961
|
-
return toItem(ref, await
|
|
6158
|
+
return toItem(ref, await stat12(ref));
|
|
5962
6159
|
} catch {
|
|
5963
6160
|
return null;
|
|
5964
6161
|
}
|
|
@@ -6145,18 +6342,18 @@ function createClaudeCodeAdapter() {
|
|
|
6145
6342
|
status,
|
|
6146
6343
|
defaults: (cwd) => readAgentDefaults(cwd),
|
|
6147
6344
|
/** La carpeta de la CLI. No se lista ni se entra: regla 2.1. */
|
|
6148
|
-
protectedDirs: () => [
|
|
6345
|
+
protectedDirs: () => [path21.join(homedir5(), ".claude")],
|
|
6149
6346
|
dispose: () => status.dispose()
|
|
6150
6347
|
};
|
|
6151
6348
|
}
|
|
6152
6349
|
|
|
6153
6350
|
// packages/server/src/agents/codex/index.ts
|
|
6154
6351
|
import { homedir as homedir7 } from "node:os";
|
|
6155
|
-
import
|
|
6352
|
+
import path27 from "node:path";
|
|
6156
6353
|
|
|
6157
6354
|
// packages/server/src/agents/codex/discovery.ts
|
|
6158
|
-
import { stat as
|
|
6159
|
-
import
|
|
6355
|
+
import { stat as stat13 } from "node:fs/promises";
|
|
6356
|
+
import path24 from "node:path";
|
|
6160
6357
|
|
|
6161
6358
|
// packages/server/src/debug.ts
|
|
6162
6359
|
var enabled = process.env["AGENT_WORKBENCH_DEBUG"] === "1";
|
|
@@ -6167,11 +6364,11 @@ function debugLog(scope, message) {
|
|
|
6167
6364
|
|
|
6168
6365
|
// packages/server/src/agents/codex/find-rollout.ts
|
|
6169
6366
|
import { readdir as readdir8 } from "node:fs/promises";
|
|
6170
|
-
import
|
|
6367
|
+
import path23 from "node:path";
|
|
6171
6368
|
|
|
6172
6369
|
// packages/server/src/agents/codex/paths.ts
|
|
6173
6370
|
import { homedir as homedir6 } from "node:os";
|
|
6174
|
-
import
|
|
6371
|
+
import path22 from "node:path";
|
|
6175
6372
|
var INTERACTIVE_SOURCES = ["cli", "vscode"];
|
|
6176
6373
|
var LISTED_HISTORY_MODES = ["legacy", "paginated"];
|
|
6177
6374
|
var ROLLOUT_FILE_PATTERN = /^rollout-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
@@ -6183,13 +6380,13 @@ function isUuid(value) {
|
|
|
6183
6380
|
}
|
|
6184
6381
|
var warnedHomes = /* @__PURE__ */ new Set();
|
|
6185
6382
|
function isFullyQualified(candidate) {
|
|
6186
|
-
if (!
|
|
6383
|
+
if (!path22.isAbsolute(candidate)) return false;
|
|
6187
6384
|
return process.platform !== "win32" || /^([a-zA-Z]:[\\/]|[\\/]{2})/.test(candidate);
|
|
6188
6385
|
}
|
|
6189
6386
|
function codexHome() {
|
|
6190
6387
|
const configured = process.env["CODEX_HOME"];
|
|
6191
6388
|
if (configured === void 0 || configured.length === 0) {
|
|
6192
|
-
return
|
|
6389
|
+
return path22.join(homedir6(), ".codex");
|
|
6193
6390
|
}
|
|
6194
6391
|
if (isFullyQualified(configured)) return configured;
|
|
6195
6392
|
if (!warnedHomes.has(configured)) {
|
|
@@ -6202,11 +6399,11 @@ function codexHome() {
|
|
|
6202
6399
|
}
|
|
6203
6400
|
function codexSessionsRoot() {
|
|
6204
6401
|
const home = codexHome();
|
|
6205
|
-
return home === null ? null :
|
|
6402
|
+
return home === null ? null : path22.join(home, "sessions");
|
|
6206
6403
|
}
|
|
6207
6404
|
function codexArchivedRoot() {
|
|
6208
6405
|
const home = codexHome();
|
|
6209
|
-
return home === null ? null :
|
|
6406
|
+
return home === null ? null : path22.join(home, "archived_sessions");
|
|
6210
6407
|
}
|
|
6211
6408
|
function parseRolloutFileName(name) {
|
|
6212
6409
|
const match = ROLLOUT_FILE_PATTERN.exec(name);
|
|
@@ -6233,7 +6430,7 @@ function localDayFolder(root, epochMs) {
|
|
|
6233
6430
|
const year = String(date.getFullYear()).padStart(4, "0");
|
|
6234
6431
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
6235
6432
|
const day = String(date.getDate()).padStart(2, "0");
|
|
6236
|
-
return
|
|
6433
|
+
return path22.join(root, year, month, day);
|
|
6237
6434
|
}
|
|
6238
6435
|
function localDayFolders(root, fromMs, toMs) {
|
|
6239
6436
|
const folders = [];
|
|
@@ -6265,14 +6462,14 @@ async function numberedFolders(folder, pattern) {
|
|
|
6265
6462
|
return (await entriesOf(folder)).filter((entry) => entry.isDirectory() && pattern.test(entry.name)).map((entry) => entry.name).sort();
|
|
6266
6463
|
}
|
|
6267
6464
|
async function rolloutFilesIn(folder) {
|
|
6268
|
-
return (await entriesOf(folder)).filter((entry) => entry.isFile() && parseRolloutFileName(entry.name) !== null).map((entry) => entry.name).sort().map((name) =>
|
|
6465
|
+
return (await entriesOf(folder)).filter((entry) => entry.isFile() && parseRolloutFileName(entry.name) !== null).map((entry) => entry.name).sort().map((name) => path23.join(folder, name));
|
|
6269
6466
|
}
|
|
6270
6467
|
async function walkSessionFiles(root, limit = Number.POSITIVE_INFINITY) {
|
|
6271
6468
|
const files = [];
|
|
6272
6469
|
for (const year of await numberedFolders(root, YEAR)) {
|
|
6273
|
-
for (const month of await numberedFolders(
|
|
6274
|
-
for (const day of await numberedFolders(
|
|
6275
|
-
for (const file of await rolloutFilesIn(
|
|
6470
|
+
for (const month of await numberedFolders(path23.join(root, year), TWO_DIGITS)) {
|
|
6471
|
+
for (const day of await numberedFolders(path23.join(root, year, month), TWO_DIGITS)) {
|
|
6472
|
+
for (const file of await rolloutFilesIn(path23.join(root, year, month, day))) {
|
|
6276
6473
|
files.push(file);
|
|
6277
6474
|
if (files.length >= limit) return files;
|
|
6278
6475
|
}
|
|
@@ -6283,7 +6480,7 @@ async function walkSessionFiles(root, limit = Number.POSITIVE_INFINITY) {
|
|
|
6283
6480
|
}
|
|
6284
6481
|
function matching(files, sessionId) {
|
|
6285
6482
|
for (const file of files) {
|
|
6286
|
-
if (parseRolloutFileName(
|
|
6483
|
+
if (parseRolloutFileName(path23.basename(file))?.sessionId === sessionId) return file;
|
|
6287
6484
|
}
|
|
6288
6485
|
return null;
|
|
6289
6486
|
}
|
|
@@ -6651,7 +6848,7 @@ var CodexSessionDiscovery = class {
|
|
|
6651
6848
|
if (this.seen.has(filePath)) continue;
|
|
6652
6849
|
let info;
|
|
6653
6850
|
try {
|
|
6654
|
-
info = await
|
|
6851
|
+
info = await stat13(filePath);
|
|
6655
6852
|
} catch {
|
|
6656
6853
|
continue;
|
|
6657
6854
|
}
|
|
@@ -6690,7 +6887,7 @@ var CodexSessionDiscovery = class {
|
|
|
6690
6887
|
this.stopTimerIfIdle();
|
|
6691
6888
|
}
|
|
6692
6889
|
async readHead(filePath, size) {
|
|
6693
|
-
const name = parseRolloutFileName(
|
|
6890
|
+
const name = parseRolloutFileName(path24.basename(filePath));
|
|
6694
6891
|
if (name === null) return { kind: "rejected" };
|
|
6695
6892
|
let lines;
|
|
6696
6893
|
try {
|
|
@@ -6738,8 +6935,8 @@ var CodexSessionDiscovery = class {
|
|
|
6738
6935
|
};
|
|
6739
6936
|
|
|
6740
6937
|
// packages/server/src/agents/codex/history.ts
|
|
6741
|
-
import { stat as
|
|
6742
|
-
import
|
|
6938
|
+
import { stat as stat14 } from "node:fs/promises";
|
|
6939
|
+
import path26 from "node:path";
|
|
6743
6940
|
|
|
6744
6941
|
// packages/server/src/agents/codex/rollout-scan.ts
|
|
6745
6942
|
var TAIL_MAX_BYTES2 = 256 * 1024;
|
|
@@ -6813,7 +7010,7 @@ async function scanRollout(item) {
|
|
|
6813
7010
|
}
|
|
6814
7011
|
|
|
6815
7012
|
// packages/server/src/agents/codex/session-follower.ts
|
|
6816
|
-
import
|
|
7013
|
+
import path25 from "node:path";
|
|
6817
7014
|
|
|
6818
7015
|
// packages/server/src/agents/codex/rollout-image.ts
|
|
6819
7016
|
import { createReadStream } from "node:fs";
|
|
@@ -7181,7 +7378,7 @@ var CodexSessionFollower = class {
|
|
|
7181
7378
|
return own === filePath || normalizeCwdKey(own, process.platform) === normalizeCwdKey(filePath, process.platform);
|
|
7182
7379
|
}
|
|
7183
7380
|
if (this.sessionId.length === 0) return false;
|
|
7184
|
-
if (parseRolloutFileName(
|
|
7381
|
+
if (parseRolloutFileName(path25.basename(filePath))?.sessionId !== this.sessionId.toLowerCase()) return false;
|
|
7185
7382
|
this.jsonl.setPath(filePath);
|
|
7186
7383
|
return true;
|
|
7187
7384
|
}
|
|
@@ -7219,26 +7416,26 @@ var CodexSessionFollower = class {
|
|
|
7219
7416
|
// packages/server/src/agents/codex/history.ts
|
|
7220
7417
|
async function pathExists2(candidate) {
|
|
7221
7418
|
try {
|
|
7222
|
-
await
|
|
7419
|
+
await stat14(candidate);
|
|
7223
7420
|
return true;
|
|
7224
7421
|
} catch {
|
|
7225
7422
|
return false;
|
|
7226
7423
|
}
|
|
7227
7424
|
}
|
|
7228
|
-
var accepts = (filePath) => ROLLOUT_FILE_PATTERN.test(
|
|
7425
|
+
var accepts = (filePath) => ROLLOUT_FILE_PATTERN.test(path26.basename(filePath));
|
|
7229
7426
|
var CODEX_FOLLOW_POLL_MS = 1e3;
|
|
7230
7427
|
function isInside2(root, filePath) {
|
|
7231
|
-
const relative =
|
|
7232
|
-
return relative.length > 0 && !relative.startsWith("..") && !
|
|
7428
|
+
const relative = path26.relative(root, filePath);
|
|
7429
|
+
return relative.length > 0 && !relative.startsWith("..") && !path26.isAbsolute(relative);
|
|
7233
7430
|
}
|
|
7234
7431
|
function toItem2(ref, info) {
|
|
7235
|
-
const name = parseRolloutFileName(
|
|
7432
|
+
const name = parseRolloutFileName(path26.basename(ref));
|
|
7236
7433
|
if (name === null) return null;
|
|
7237
7434
|
return { ref, sessionId: name.sessionId, group: name.sessionId, mtimeMs: info.mtimeMs, sizeBytes: info.size };
|
|
7238
7435
|
}
|
|
7239
7436
|
async function itemOf(ref) {
|
|
7240
7437
|
try {
|
|
7241
|
-
return toItem2(ref, await
|
|
7438
|
+
return toItem2(ref, await stat14(ref));
|
|
7242
7439
|
} catch {
|
|
7243
7440
|
return null;
|
|
7244
7441
|
}
|
|
@@ -7432,7 +7629,7 @@ function createCodexAdapter() {
|
|
|
7432
7629
|
* credenciales de antes de cambiarla.
|
|
7433
7630
|
*/
|
|
7434
7631
|
protectedDirs: () => {
|
|
7435
|
-
const dirs = [
|
|
7632
|
+
const dirs = [path27.join(homedir7(), ".codex")];
|
|
7436
7633
|
const home = codexHome();
|
|
7437
7634
|
if (home !== null && !dirs.includes(home)) dirs.unshift(home);
|
|
7438
7635
|
return dirs;
|
|
@@ -7625,7 +7822,7 @@ var DbChangeSignal = class {
|
|
|
7625
7822
|
};
|
|
7626
7823
|
|
|
7627
7824
|
// packages/server/src/agents/opencode/history.ts
|
|
7628
|
-
import { stat as
|
|
7825
|
+
import { stat as stat15 } from "node:fs/promises";
|
|
7629
7826
|
|
|
7630
7827
|
// packages/server/src/agents/opencode/events.ts
|
|
7631
7828
|
var TOOL_INPUT_PARSE_MAX_CHARS = 16e3;
|
|
@@ -8576,7 +8773,7 @@ function createOpenCodeHistory(deps) {
|
|
|
8576
8773
|
async rootExists() {
|
|
8577
8774
|
if (dbFile === null) return false;
|
|
8578
8775
|
try {
|
|
8579
|
-
await
|
|
8776
|
+
await stat15(dbFile);
|
|
8580
8777
|
return true;
|
|
8581
8778
|
} catch {
|
|
8582
8779
|
return false;
|
|
@@ -8608,13 +8805,13 @@ function createOpenCodeHistory(deps) {
|
|
|
8608
8805
|
var OPENCODE_SESSION_ID_PATTERN = /^ses_[0-9A-Za-z]{20,40}$/;
|
|
8609
8806
|
|
|
8610
8807
|
// packages/server/src/agents/opencode/paths.ts
|
|
8611
|
-
import
|
|
8808
|
+
import path28 from "node:path";
|
|
8612
8809
|
function isFullyQualified2(candidate, platform) {
|
|
8613
8810
|
if (platform === "win32") return /^([a-zA-Z]:[\\/]|[\\/]{2})/.test(candidate);
|
|
8614
|
-
return
|
|
8811
|
+
return path28.posix.isAbsolute(candidate);
|
|
8615
8812
|
}
|
|
8616
8813
|
function resolveOpenCodePaths(env, home, platform) {
|
|
8617
|
-
const flavor = platform === "win32" ?
|
|
8814
|
+
const flavor = platform === "win32" ? path28.win32 : path28.posix;
|
|
8618
8815
|
const variable = (name) => {
|
|
8619
8816
|
const value = env[name];
|
|
8620
8817
|
return value === void 0 || value.length === 0 ? null : value;
|
|
@@ -9062,8 +9259,8 @@ function launchWithServe(deps, input) {
|
|
|
9062
9259
|
import { execFile as execFile2, spawn as nodeSpawn } from "node:child_process";
|
|
9063
9260
|
import { randomBytes } from "node:crypto";
|
|
9064
9261
|
import { mkdir as mkdir4 } from "node:fs/promises";
|
|
9065
|
-
import { tmpdir as
|
|
9066
|
-
import
|
|
9262
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
9263
|
+
import path29 from "node:path";
|
|
9067
9264
|
var OPENCODE_SERVE_HOST = "127.0.0.1";
|
|
9068
9265
|
var OPENCODE_SERVE_START_TIMEOUT_MS = 2e4;
|
|
9069
9266
|
var OPENCODE_SERVE_IDLE_STOP_MS = 3e5;
|
|
@@ -9095,7 +9292,7 @@ function parseListeningLine(line) {
|
|
|
9095
9292
|
return `http://${OPENCODE_SERVE_HOST}:${found.port}`;
|
|
9096
9293
|
}
|
|
9097
9294
|
function defaultServeCwd() {
|
|
9098
|
-
return
|
|
9295
|
+
return path29.join(tmpdir4(), "agent-workbench", "opencode-serve");
|
|
9099
9296
|
}
|
|
9100
9297
|
var ServeStartError = class extends Error {
|
|
9101
9298
|
};
|
|
@@ -10114,28 +10311,48 @@ var AgentRegistry = class {
|
|
|
10114
10311
|
agents = /* @__PURE__ */ new Map();
|
|
10115
10312
|
/** Las suscripciones a cambios de configuracion que siguen abiertas, para soltarlas al apagar. */
|
|
10116
10313
|
changeSubscriptions = /* @__PURE__ */ new Set();
|
|
10314
|
+
/**
|
|
10315
|
+
* Las que el usuario apago en Ajustes (Hito 41, §6.32), tal como estaban al
|
|
10316
|
+
* arrancar. Una apagada es, para todo el resto, una CLI que no esta: no se
|
|
10317
|
+
* busca —no se corre su `--version`—, no se lanza, y su historial no se lee
|
|
10318
|
+
* (`historySources`). Cambiarlo vale al reiniciar.
|
|
10319
|
+
*/
|
|
10320
|
+
disabled;
|
|
10117
10321
|
/** El orden del array es el orden de preferencia para `defaultAgent`. */
|
|
10118
|
-
constructor(adapters) {
|
|
10322
|
+
constructor(adapters, disabled = []) {
|
|
10119
10323
|
for (const adapter of adapters) {
|
|
10120
10324
|
if (this.agents.has(adapter.id)) {
|
|
10121
10325
|
throw new Error(`Adapter registered twice: ${adapter.id}`);
|
|
10122
10326
|
}
|
|
10123
10327
|
this.agents.set(adapter.id, { adapter, location: null });
|
|
10124
10328
|
}
|
|
10329
|
+
this.disabled = new Set([...disabled].filter((id) => this.agents.has(id)));
|
|
10125
10330
|
}
|
|
10126
10331
|
/**
|
|
10127
10332
|
* Localiza todos a la vez. Se llama una vez al arrancar.
|
|
10128
10333
|
*
|
|
10129
10334
|
* En paralelo porque cada uno puede correr `--version`, y eso son segundos
|
|
10130
|
-
* que el arranque no tiene por que sumar.
|
|
10335
|
+
* que el arranque no tiene por que sumar. Una apagada no se busca.
|
|
10131
10336
|
*/
|
|
10132
10337
|
async locateAll() {
|
|
10133
10338
|
await Promise.all(
|
|
10134
10339
|
[...this.agents.values()].map(async (entry) => {
|
|
10135
|
-
entry.location = await entry.adapter.locate();
|
|
10340
|
+
entry.location = this.disabled.has(entry.adapter.id) ? null : await entry.adapter.locate();
|
|
10136
10341
|
})
|
|
10137
10342
|
);
|
|
10138
10343
|
}
|
|
10344
|
+
/** Las que este arranque dejo apagadas. */
|
|
10345
|
+
disabledIds() {
|
|
10346
|
+
return [...this.agents.keys()].filter((id) => this.disabled.has(id));
|
|
10347
|
+
}
|
|
10348
|
+
/**
|
|
10349
|
+
* Las CLIs cuyo historial se lee: todas menos las apagadas. Instalada o no da
|
|
10350
|
+
* igual —el historial de una ausente se lee igual, apagado en la barra—; una
|
|
10351
|
+
* apagada es lo que el usuario pidio no ver.
|
|
10352
|
+
*/
|
|
10353
|
+
historySources() {
|
|
10354
|
+
return this.all().filter((entry) => !this.disabled.has(entry.adapter.id));
|
|
10355
|
+
}
|
|
10139
10356
|
/**
|
|
10140
10357
|
* Lo que cada CLI encontrada prepara en las carpetas de la app
|
|
10141
10358
|
* (`AgentAdapter.prepare`). Se llama una vez al arrancar, despues de
|
|
@@ -10287,13 +10504,13 @@ function resolveAgentForOpen(input) {
|
|
|
10287
10504
|
}
|
|
10288
10505
|
return fromTabs ?? input.defaultAgent;
|
|
10289
10506
|
}
|
|
10290
|
-
function createAgentRegistry() {
|
|
10507
|
+
function createAgentRegistry(disabled = []) {
|
|
10291
10508
|
const registry = new AgentRegistry([
|
|
10292
10509
|
createClaudeCodeAdapter(),
|
|
10293
10510
|
createCodexAdapter(),
|
|
10294
10511
|
createOpenCodeAdapter({ serveEnv: () => registry.composedEnvironment(process.env) }),
|
|
10295
10512
|
createAntigravityAdapter()
|
|
10296
|
-
]);
|
|
10513
|
+
], disabled);
|
|
10297
10514
|
return registry;
|
|
10298
10515
|
}
|
|
10299
10516
|
|
|
@@ -10310,7 +10527,7 @@ async function migrateLegacyConfigDir() {
|
|
|
10310
10527
|
|
|
10311
10528
|
// packages/server/src/conversation-hub.ts
|
|
10312
10529
|
import { EventEmitter } from "node:events";
|
|
10313
|
-
import
|
|
10530
|
+
import path30 from "node:path";
|
|
10314
10531
|
var INITIAL_EVENT_LIMIT = 300;
|
|
10315
10532
|
var MAX_PAGE_LIMIT = 500;
|
|
10316
10533
|
var PENDING_QUESTION_LOOKBACK = 50;
|
|
@@ -10371,7 +10588,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10371
10588
|
await entry.follower.start();
|
|
10372
10589
|
debugLog(
|
|
10373
10590
|
"conversation",
|
|
10374
|
-
`following ${
|
|
10591
|
+
`following ${path30.basename(entry.follower.label)} for ${terminalId.slice(0, 8)}`
|
|
10375
10592
|
);
|
|
10376
10593
|
entry = this.entries.get(terminalId) ?? entry;
|
|
10377
10594
|
}
|
|
@@ -10536,7 +10753,7 @@ var ConversationHub = class extends EventEmitter {
|
|
|
10536
10753
|
this.startFollowTicker(terminalId, next);
|
|
10537
10754
|
debugLog(
|
|
10538
10755
|
"conversation",
|
|
10539
|
-
`${terminalId.slice(0, 8)} changed session: following ${
|
|
10756
|
+
`${terminalId.slice(0, 8)} changed session: following ${path30.basename(next.follower.label)}`
|
|
10540
10757
|
);
|
|
10541
10758
|
await next.follower.start();
|
|
10542
10759
|
next.primed = true;
|
|
@@ -10900,18 +11117,18 @@ function openBrowser(url) {
|
|
|
10900
11117
|
// packages/server/src/pty-helper.ts
|
|
10901
11118
|
import { chmodSync, statSync } from "node:fs";
|
|
10902
11119
|
import { createRequire } from "node:module";
|
|
10903
|
-
import
|
|
11120
|
+
import path31 from "node:path";
|
|
10904
11121
|
function helperCandidates(packageDir, platform, arch) {
|
|
10905
11122
|
return [
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
|
|
11123
|
+
path31.join(packageDir, "build", "Release", "spawn-helper"),
|
|
11124
|
+
path31.join(packageDir, "build", "Debug", "spawn-helper"),
|
|
11125
|
+
path31.join(packageDir, "prebuilds", `${platform}-${arch}`, "spawn-helper")
|
|
10909
11126
|
];
|
|
10910
11127
|
}
|
|
10911
11128
|
function locateNodePty() {
|
|
10912
11129
|
try {
|
|
10913
11130
|
const require2 = createRequire(import.meta.url);
|
|
10914
|
-
return
|
|
11131
|
+
return path31.resolve(path31.dirname(require2.resolve("node-pty")), "..");
|
|
10915
11132
|
} catch {
|
|
10916
11133
|
return null;
|
|
10917
11134
|
}
|
|
@@ -11205,6 +11422,7 @@ var OPENS_ON_HOST = /* @__PURE__ */ new Set([
|
|
|
11205
11422
|
]);
|
|
11206
11423
|
function remoteRefusal(type, remoteClient) {
|
|
11207
11424
|
if (!remoteClient) return null;
|
|
11425
|
+
if (type === "settings.update") return serverText("settingsHostOnly");
|
|
11208
11426
|
if (HOST_ONLY.has(type)) return serverText("remoteHostOnly");
|
|
11209
11427
|
if (OPENS_ON_HOST.has(type)) return serverText("remoteOpensOnHost");
|
|
11210
11428
|
return null;
|
|
@@ -11571,24 +11789,24 @@ var RemoteAccessService = class {
|
|
|
11571
11789
|
};
|
|
11572
11790
|
|
|
11573
11791
|
// packages/server/src/remote-devices-store.ts
|
|
11574
|
-
import { chmod, mkdir as mkdir6, readFile as
|
|
11575
|
-
import
|
|
11792
|
+
import { chmod, mkdir as mkdir6, readFile as readFile10, stat as stat17 } from "node:fs/promises";
|
|
11793
|
+
import path33 from "node:path";
|
|
11576
11794
|
|
|
11577
11795
|
// packages/server/src/vault/write.ts
|
|
11578
11796
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
11579
|
-
import { mkdir as mkdir5, rename as rename3, rm as rm2, stat as
|
|
11797
|
+
import { mkdir as mkdir5, rename as rename3, rm as rm2, stat as stat16, writeFile as writeFile3 } from "node:fs/promises";
|
|
11580
11798
|
|
|
11581
11799
|
// packages/server/src/vault/paths.ts
|
|
11582
11800
|
import { createHash as createHash2 } from "node:crypto";
|
|
11583
|
-
import
|
|
11801
|
+
import path32 from "node:path";
|
|
11584
11802
|
function defaultVaultDir() {
|
|
11585
|
-
return
|
|
11803
|
+
return path32.join(appConfigDir(), "vault");
|
|
11586
11804
|
}
|
|
11587
11805
|
function vaultMarkerFile(dir) {
|
|
11588
|
-
return
|
|
11806
|
+
return path32.join(dir, "vault.json");
|
|
11589
11807
|
}
|
|
11590
11808
|
function sessionsRoot2(dir) {
|
|
11591
|
-
return
|
|
11809
|
+
return path32.join(dir, "sessions");
|
|
11592
11810
|
}
|
|
11593
11811
|
function isSafeSessionId(sessionId) {
|
|
11594
11812
|
return isVaultSessionId(sessionId);
|
|
@@ -11602,41 +11820,41 @@ function assertSessionKey(agent, sessionId) {
|
|
|
11602
11820
|
}
|
|
11603
11821
|
function agentSessionsDir(dir, agent) {
|
|
11604
11822
|
if (!isSessionAgentId(agent)) throw new Error(`Unknown source for the local copy: ${JSON.stringify(agent)}`);
|
|
11605
|
-
return
|
|
11823
|
+
return path32.join(sessionsRoot2(dir), agent);
|
|
11606
11824
|
}
|
|
11607
11825
|
function sessionFile(dir, agent, sessionId) {
|
|
11608
11826
|
assertSessionKey(agent, sessionId);
|
|
11609
|
-
return
|
|
11827
|
+
return path32.join(sessionsRoot2(dir), agent, `${sessionId}.jsonl`);
|
|
11610
11828
|
}
|
|
11611
11829
|
function assetsDir(dir, agent, sessionId) {
|
|
11612
11830
|
assertSessionKey(agent, sessionId);
|
|
11613
|
-
return
|
|
11831
|
+
return path32.join(sessionsRoot2(dir), agent, `${sessionId}.assets`);
|
|
11614
11832
|
}
|
|
11615
11833
|
function assetFile(dir, agent, sessionId, asset) {
|
|
11616
11834
|
if (!VAULT_ASSET_NAME_PATTERN.test(asset)) throw new Error(`Invalid asset name: ${JSON.stringify(asset)}`);
|
|
11617
|
-
return
|
|
11835
|
+
return path32.join(assetsDir(dir, agent, sessionId), asset);
|
|
11618
11836
|
}
|
|
11619
11837
|
function projectFolderName(cwd, platform) {
|
|
11620
11838
|
const hash = createHash2("sha256").update(normalizeCwdKey(cwd, platform)).digest("hex").slice(0, 8);
|
|
11621
|
-
const base = (platform === "win32" ?
|
|
11839
|
+
const base = (platform === "win32" ? path32.win32 : path32.posix).basename(cwd);
|
|
11622
11840
|
const name = base.length === 0 ? "proyecto" : sanitizeFileName(base, 60);
|
|
11623
11841
|
return `${name}-${hash}`;
|
|
11624
11842
|
}
|
|
11625
11843
|
function memoryDir(dir, cwd, platform) {
|
|
11626
|
-
return
|
|
11844
|
+
return path32.join(dir, "memory", projectFolderName(cwd, platform));
|
|
11627
11845
|
}
|
|
11628
11846
|
function projectExportDir(dir, cwd, platform) {
|
|
11629
|
-
return
|
|
11847
|
+
return path32.join(dir, "export", projectFolderName(cwd, platform));
|
|
11630
11848
|
}
|
|
11631
11849
|
function projectExportDirFor(dir, project, platform) {
|
|
11632
11850
|
if (project.cwd.length > 0) return projectExportDir(dir, project.cwd, platform);
|
|
11633
11851
|
const hash = createHash2("sha256").update(project.key).digest("hex").slice(0, 8);
|
|
11634
11852
|
const name = project.fallbackName.length > 0 ? sanitizeFileName(project.fallbackName, 60) : "proyecto";
|
|
11635
|
-
return
|
|
11853
|
+
return path32.join(dir, "export", `${name}-${hash}`);
|
|
11636
11854
|
}
|
|
11637
11855
|
function sessionExportFile(dir, agent, sessionId) {
|
|
11638
11856
|
assertSessionKey(agent, sessionId);
|
|
11639
|
-
return
|
|
11857
|
+
return path32.join(dir, "export", "sesiones", `${agent}-${sessionId}.md`);
|
|
11640
11858
|
}
|
|
11641
11859
|
var RESERVED_WINDOWS_NAME = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
|
|
11642
11860
|
function sanitizeFileName(text, max = 80) {
|
|
@@ -11691,7 +11909,7 @@ async function writeFileAtomic(target, data, deps = DEFAULT_WRITE_DEPS) {
|
|
|
11691
11909
|
async function ensureVaultMarker(dir, deps = DEFAULT_WRITE_DEPS) {
|
|
11692
11910
|
const marker = vaultMarkerFile(dir);
|
|
11693
11911
|
try {
|
|
11694
|
-
await
|
|
11912
|
+
await stat16(marker);
|
|
11695
11913
|
return;
|
|
11696
11914
|
} catch {
|
|
11697
11915
|
}
|
|
@@ -11701,7 +11919,7 @@ async function ensureVaultMarker(dir, deps = DEFAULT_WRITE_DEPS) {
|
|
|
11701
11919
|
}
|
|
11702
11920
|
async function assetPresent(file, size) {
|
|
11703
11921
|
try {
|
|
11704
|
-
const info = await
|
|
11922
|
+
const info = await stat16(file);
|
|
11705
11923
|
return info.isFile() && info.size === size;
|
|
11706
11924
|
} catch {
|
|
11707
11925
|
return false;
|
|
@@ -11743,7 +11961,7 @@ var RemoteDevicesStore = class {
|
|
|
11743
11961
|
async load() {
|
|
11744
11962
|
let raw;
|
|
11745
11963
|
try {
|
|
11746
|
-
raw = await
|
|
11964
|
+
raw = await readFile10(this.filePath, "utf8");
|
|
11747
11965
|
} catch {
|
|
11748
11966
|
return [];
|
|
11749
11967
|
}
|
|
@@ -11764,7 +11982,7 @@ var RemoteDevicesStore = class {
|
|
|
11764
11982
|
save(devices) {
|
|
11765
11983
|
const text = serializeRemoteDevices(devices);
|
|
11766
11984
|
const run = async () => {
|
|
11767
|
-
await mkdir6(
|
|
11985
|
+
await mkdir6(path33.dirname(this.filePath), { recursive: true });
|
|
11768
11986
|
await writeFileAtomic(this.filePath, text);
|
|
11769
11987
|
await chmod(this.filePath, 384).catch(() => void 0);
|
|
11770
11988
|
};
|
|
@@ -11783,7 +12001,7 @@ var RemoteDevicesStore = class {
|
|
|
11783
12001
|
*/
|
|
11784
12002
|
async stamp() {
|
|
11785
12003
|
try {
|
|
11786
|
-
const info = await
|
|
12004
|
+
const info = await stat17(this.filePath);
|
|
11787
12005
|
return `${info.mtimeMs}:${info.size}`;
|
|
11788
12006
|
} catch {
|
|
11789
12007
|
return "";
|
|
@@ -11897,8 +12115,8 @@ import chokidar from "chokidar";
|
|
|
11897
12115
|
|
|
11898
12116
|
// packages/server/src/git-repo.ts
|
|
11899
12117
|
import { execFile as execFile4 } from "node:child_process";
|
|
11900
|
-
import { readFile as
|
|
11901
|
-
import
|
|
12118
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
12119
|
+
import path34 from "node:path";
|
|
11902
12120
|
function gitOutputText(output, command) {
|
|
11903
12121
|
return output.length > 0 ? serverText("raw", { text: output }) : serverText("gitCommandFailed", { command });
|
|
11904
12122
|
}
|
|
@@ -11959,7 +12177,7 @@ async function resolveRepo(cwd) {
|
|
|
11959
12177
|
const result = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
11960
12178
|
if (result.code === 0) {
|
|
11961
12179
|
const root = result.stdout.trim();
|
|
11962
|
-
if (root.length > 0) return { state: "ready", root:
|
|
12180
|
+
if (root.length > 0) return { state: "ready", root: path34.resolve(root), message: null };
|
|
11963
12181
|
}
|
|
11964
12182
|
const stderr = result.stderr.trim();
|
|
11965
12183
|
if (/not a git repository|no es un repositorio/i.test(stderr)) {
|
|
@@ -11986,7 +12204,7 @@ async function findGitDir(cwd) {
|
|
|
11986
12204
|
const result = await runGit(cwd, ["rev-parse", "--absolute-git-dir"]);
|
|
11987
12205
|
if (result.code !== 0) return null;
|
|
11988
12206
|
const dir = result.stdout.trim();
|
|
11989
|
-
return dir.length > 0 ?
|
|
12207
|
+
return dir.length > 0 ? path34.resolve(dir) : null;
|
|
11990
12208
|
}
|
|
11991
12209
|
function letterToKind(letter) {
|
|
11992
12210
|
switch (letter) {
|
|
@@ -12089,7 +12307,7 @@ function parseWorktrees(raw, currentRoot) {
|
|
|
12089
12307
|
worktrees.push({
|
|
12090
12308
|
path: current.path,
|
|
12091
12309
|
branch: current.branch,
|
|
12092
|
-
isCurrent:
|
|
12310
|
+
isCurrent: path34.resolve(current.path) === path34.resolve(currentRoot)
|
|
12093
12311
|
});
|
|
12094
12312
|
current = null;
|
|
12095
12313
|
};
|
|
@@ -12232,7 +12450,7 @@ function looksBinary(buffer) {
|
|
|
12232
12450
|
async function readUntrackedAsDiff(absolutePath, relativePath) {
|
|
12233
12451
|
let content;
|
|
12234
12452
|
try {
|
|
12235
|
-
content = await
|
|
12453
|
+
content = await readFile11(absolutePath);
|
|
12236
12454
|
} catch {
|
|
12237
12455
|
return {
|
|
12238
12456
|
path: relativePath,
|
|
@@ -12280,7 +12498,7 @@ async function readUntrackedAsDiff(absolutePath, relativePath) {
|
|
|
12280
12498
|
async function readDiff(options) {
|
|
12281
12499
|
const { repoRoot: repoRoot2, relativePath, staged, untracked } = options;
|
|
12282
12500
|
if (untracked && !staged) {
|
|
12283
|
-
return readUntrackedAsDiff(
|
|
12501
|
+
return readUntrackedAsDiff(path34.join(repoRoot2, relativePath), relativePath);
|
|
12284
12502
|
}
|
|
12285
12503
|
const args = [
|
|
12286
12504
|
"diff",
|
|
@@ -12500,7 +12718,7 @@ var RepoHub = class extends EventEmitter2 {
|
|
|
12500
12718
|
// packages/server/src/memory-hub.ts
|
|
12501
12719
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
12502
12720
|
import { homedir as homedir10 } from "node:os";
|
|
12503
|
-
import
|
|
12721
|
+
import path36 from "node:path";
|
|
12504
12722
|
import chokidar2 from "chokidar";
|
|
12505
12723
|
|
|
12506
12724
|
// packages/server/src/memory-bridge.ts
|
|
@@ -12510,14 +12728,14 @@ import {
|
|
|
12510
12728
|
mkdir as mkdir7,
|
|
12511
12729
|
open as open6,
|
|
12512
12730
|
readdir as readdir9,
|
|
12513
|
-
readFile as
|
|
12731
|
+
readFile as readFile12,
|
|
12514
12732
|
realpath as realpath2,
|
|
12515
12733
|
rename as rename4,
|
|
12516
|
-
stat as
|
|
12734
|
+
stat as stat18,
|
|
12517
12735
|
unlink as unlink3
|
|
12518
12736
|
} from "node:fs/promises";
|
|
12519
12737
|
import { homedir as homedir9 } from "node:os";
|
|
12520
|
-
import
|
|
12738
|
+
import path35 from "node:path";
|
|
12521
12739
|
var MemoryBridgeError = class extends ServerTextError {
|
|
12522
12740
|
};
|
|
12523
12741
|
var MAX_LISTED_NOTES = 500;
|
|
@@ -12535,9 +12753,9 @@ var GITIGNORE_MEMORY_LINE = `${MEMORY_DIR}/`;
|
|
|
12535
12753
|
var BOM2 = Buffer.from([239, 187, 191]);
|
|
12536
12754
|
async function inspectMemory(cwd, home = homedir9()) {
|
|
12537
12755
|
await assertMemoryDirInside(cwd);
|
|
12538
|
-
const folder =
|
|
12756
|
+
const folder = path35.join(cwd, MEMORY_DIR);
|
|
12539
12757
|
const folderExists = await isDirectory2(folder);
|
|
12540
|
-
const indexExists = folderExists && await isFile(
|
|
12758
|
+
const indexExists = folderExists && await isFile(path35.join(folder, MEMORY_INDEX_FILE));
|
|
12541
12759
|
const noteNames = folderExists ? await listNoteFiles(folder) : [];
|
|
12542
12760
|
const notes = await describeNotes(folder, noteNames);
|
|
12543
12761
|
const files = await Promise.all(
|
|
@@ -12569,7 +12787,7 @@ async function installMemory(cwd, options, home = homedir9()) {
|
|
|
12569
12787
|
}
|
|
12570
12788
|
async function importNativeMemory(cwd, home = homedir9()) {
|
|
12571
12789
|
await assertMemoryDirInside(cwd);
|
|
12572
|
-
if (!await isDirectory2(
|
|
12790
|
+
if (!await isDirectory2(path35.join(cwd, MEMORY_DIR))) {
|
|
12573
12791
|
throw new MemoryBridgeError(serverText("memoryNotInstalled"));
|
|
12574
12792
|
}
|
|
12575
12793
|
const plan = new PlanBuilder(cwd);
|
|
@@ -12587,9 +12805,9 @@ async function readMemoryNote(cwd, name) {
|
|
|
12587
12805
|
}
|
|
12588
12806
|
const absolute = await resolveInside(cwd, `${MEMORY_DIR}/${name}`);
|
|
12589
12807
|
try {
|
|
12590
|
-
const info = await
|
|
12808
|
+
const info = await stat18(absolute);
|
|
12591
12809
|
if (!info.isFile()) return null;
|
|
12592
|
-
const content = await
|
|
12810
|
+
const content = await readFile12(absolute);
|
|
12593
12811
|
return {
|
|
12594
12812
|
name,
|
|
12595
12813
|
text: content.subarray(0, MAX_NOTE_BYTES).toString("utf8"),
|
|
@@ -12604,7 +12822,7 @@ function isNoteName(name) {
|
|
|
12604
12822
|
if (name.length === 0 || name.length > 255) return false;
|
|
12605
12823
|
if (name.includes("\0")) return false;
|
|
12606
12824
|
if (name === "." || name === "..") return false;
|
|
12607
|
-
if (
|
|
12825
|
+
if (path35.basename(name) !== name) return false;
|
|
12608
12826
|
if (/[\\/:*?"<>|\u0000-\u001f]/.test(name)) return false;
|
|
12609
12827
|
return name.toLowerCase().endsWith(".md");
|
|
12610
12828
|
}
|
|
@@ -12641,7 +12859,7 @@ var PlanBuilder = class {
|
|
|
12641
12859
|
/** Nombres de nota que habria en la carpeta, en minusculas. */
|
|
12642
12860
|
async noteNames() {
|
|
12643
12861
|
const names = /* @__PURE__ */ new Map();
|
|
12644
|
-
for (const name of await listNoteFiles(
|
|
12862
|
+
for (const name of await listNoteFiles(path35.join(this.cwd, MEMORY_DIR))) {
|
|
12645
12863
|
names.set(name.toLowerCase(), name);
|
|
12646
12864
|
}
|
|
12647
12865
|
for (const rel of this.files.keys()) {
|
|
@@ -12684,7 +12902,7 @@ var PlanBuilder = class {
|
|
|
12684
12902
|
continue;
|
|
12685
12903
|
}
|
|
12686
12904
|
const target = await realpath2(absolute);
|
|
12687
|
-
const info = await
|
|
12905
|
+
const info = await stat18(target);
|
|
12688
12906
|
if (info.nlink > 1) {
|
|
12689
12907
|
throw new MemoryBridgeError(serverText("memoryHardLinks", { file: file.rel }));
|
|
12690
12908
|
}
|
|
@@ -12703,7 +12921,7 @@ var PlanBuilder = class {
|
|
|
12703
12921
|
);
|
|
12704
12922
|
if (pending.length === 0) return;
|
|
12705
12923
|
if (pending.some((file) => file.rel.startsWith(`${MEMORY_DIR}/`))) {
|
|
12706
|
-
await mkdir7(
|
|
12924
|
+
await mkdir7(path35.join(this.cwd, MEMORY_DIR), { recursive: true });
|
|
12707
12925
|
}
|
|
12708
12926
|
for (const file of pending) {
|
|
12709
12927
|
if (file.target === null) throw new Error(`Unvalidated plan: ${file.rel}`);
|
|
@@ -12814,7 +13032,7 @@ async function planNativeImport(plan, cwd, home) {
|
|
|
12814
13032
|
const local = await plan.noteNames();
|
|
12815
13033
|
for (const name of await listNoteFiles(folder)) {
|
|
12816
13034
|
const key = name.toLowerCase();
|
|
12817
|
-
const content = await readIfFile(
|
|
13035
|
+
const content = await readIfFile(path35.join(folder, name), name);
|
|
12818
13036
|
if (content === null) continue;
|
|
12819
13037
|
const already = chosen.get(key);
|
|
12820
13038
|
if (already !== void 0) {
|
|
@@ -12828,7 +13046,7 @@ async function planNativeImport(plan, cwd, home) {
|
|
|
12828
13046
|
if (existing !== null && !existing.equals(content)) skipped.push(name);
|
|
12829
13047
|
continue;
|
|
12830
13048
|
}
|
|
12831
|
-
plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "",
|
|
13049
|
+
plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", path35.join(folder, name));
|
|
12832
13050
|
copied.push(name);
|
|
12833
13051
|
}
|
|
12834
13052
|
}
|
|
@@ -12838,7 +13056,7 @@ async function planNativeImport(plan, cwd, home) {
|
|
|
12838
13056
|
const known = new Set(linkTargets(index.toString("utf8")));
|
|
12839
13057
|
const additions = [];
|
|
12840
13058
|
for (const folder of folders) {
|
|
12841
|
-
const nativeIndex = await readIfFile(
|
|
13059
|
+
const nativeIndex = await readIfFile(path35.join(folder, MEMORY_INDEX_FILE), MEMORY_INDEX_FILE);
|
|
12842
13060
|
if (nativeIndex === null) continue;
|
|
12843
13061
|
for (const line of nativeIndex.toString("utf8").replace(/^\uFEFF/, "").split(/\r?\n/)) {
|
|
12844
13062
|
const target = linkTargets(line)[0];
|
|
@@ -13063,7 +13281,7 @@ async function describeNotes(folder, names) {
|
|
|
13063
13281
|
const stated = [];
|
|
13064
13282
|
for (const name of names) {
|
|
13065
13283
|
try {
|
|
13066
|
-
const info = await
|
|
13284
|
+
const info = await stat18(path35.join(folder, name));
|
|
13067
13285
|
if (info.isFile()) stated.push({ name, sizeBytes: info.size, modifiedAt: info.mtimeMs });
|
|
13068
13286
|
} catch {
|
|
13069
13287
|
}
|
|
@@ -13071,7 +13289,7 @@ async function describeNotes(folder, names) {
|
|
|
13071
13289
|
stated.sort((a, b) => b.modifiedAt - a.modifiedAt || a.name.localeCompare(b.name));
|
|
13072
13290
|
const notes = [];
|
|
13073
13291
|
for (const item of stated.slice(0, MAX_LISTED_NOTES)) {
|
|
13074
|
-
const head = await readHead(
|
|
13292
|
+
const head = await readHead(path35.join(folder, item.name));
|
|
13075
13293
|
notes.push({ ...item, ...describeNoteText(head, item.name) });
|
|
13076
13294
|
}
|
|
13077
13295
|
return notes;
|
|
@@ -13129,28 +13347,28 @@ async function countNativeMemory(cwd, home, sharedNames) {
|
|
|
13129
13347
|
return { available: names.size, pending };
|
|
13130
13348
|
}
|
|
13131
13349
|
async function nativeMemoryFolders(cwd, home) {
|
|
13132
|
-
const root =
|
|
13350
|
+
const root = path35.join(home, ".claude", "projects");
|
|
13133
13351
|
const slug = projectSlugFor(cwd).toLowerCase();
|
|
13134
13352
|
try {
|
|
13135
13353
|
const entries = await readdir9(root, { withFileTypes: true });
|
|
13136
|
-
return entries.filter((entry) => entry.isDirectory() && entry.name.toLowerCase() === slug).map((entry) => entry.name).sort().map((name) =>
|
|
13354
|
+
return entries.filter((entry) => entry.isDirectory() && entry.name.toLowerCase() === slug).map((entry) => entry.name).sort().map((name) => path35.join(root, name, "memory"));
|
|
13137
13355
|
} catch {
|
|
13138
13356
|
return [];
|
|
13139
13357
|
}
|
|
13140
13358
|
}
|
|
13141
13359
|
function buildGlobalFragments(home) {
|
|
13142
13360
|
const windows = process.platform === "win32";
|
|
13143
|
-
const globalFile =
|
|
13361
|
+
const globalFile = path35.join(home, ...GLOBAL_MEMORY_RELATIVE.split("/"));
|
|
13144
13362
|
const hardLink = (link) => windows ? `mklink /H "${link}" "${globalFile}"` : `ln "${globalFile}" "${link}"`;
|
|
13145
13363
|
const hardLinkNote = windows ? serverText("memoryNoteHardLinkCmd", { path: GLOBAL_MEMORY_RELATIVE }) : serverText("memoryNoteHardLink", { path: GLOBAL_MEMORY_RELATIVE });
|
|
13146
|
-
const codexTarget =
|
|
13147
|
-
const geminiTarget =
|
|
13364
|
+
const codexTarget = path35.join(home, ".codex", "AGENTS.md");
|
|
13365
|
+
const geminiTarget = path35.join(home, ".gemini", "GEMINI.md");
|
|
13148
13366
|
const homeSlashes = home.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
13149
13367
|
return [
|
|
13150
13368
|
{
|
|
13151
13369
|
agent: "claude-code",
|
|
13152
13370
|
label: MEMORY_AGENT_LABELS["claude-code"],
|
|
13153
|
-
target:
|
|
13371
|
+
target: path35.join(home, ".claude", "CLAUDE.md"),
|
|
13154
13372
|
kind: "line",
|
|
13155
13373
|
text: `@~/${GLOBAL_MEMORY_RELATIVE}`,
|
|
13156
13374
|
note: serverText("memoryNoteClaude")
|
|
@@ -13174,7 +13392,7 @@ function buildGlobalFragments(home) {
|
|
|
13174
13392
|
{
|
|
13175
13393
|
agent: "opencode",
|
|
13176
13394
|
label: MEMORY_AGENT_LABELS.opencode,
|
|
13177
|
-
target:
|
|
13395
|
+
target: path35.join(home, ".config", "opencode", "opencode.json"),
|
|
13178
13396
|
kind: "json",
|
|
13179
13397
|
text: `"instructions": ["${homeSlashes}/${GLOBAL_MEMORY_RELATIVE}"]`,
|
|
13180
13398
|
note: serverText("memoryNoteOpenCode")
|
|
@@ -13222,17 +13440,17 @@ async function readGit(cwd) {
|
|
|
13222
13440
|
if (!toplevel || !gitDir || !commonDir) return fail(serverText("gitUnexpected", { command: "rev-parse" }));
|
|
13223
13441
|
const ignored = await checkIgnored(cwd, INDEX_REL);
|
|
13224
13442
|
if (typeof ignored !== "boolean") return fail(ignored);
|
|
13225
|
-
const absoluteGitDir =
|
|
13226
|
-
const absoluteCommonDir =
|
|
13443
|
+
const absoluteGitDir = path35.resolve(cwd, gitDir);
|
|
13444
|
+
const absoluteCommonDir = path35.resolve(cwd, commonDir);
|
|
13227
13445
|
let worktree = null;
|
|
13228
13446
|
let main2 = null;
|
|
13229
13447
|
if (!await samePath(absoluteGitDir, absoluteCommonDir)) {
|
|
13230
|
-
if (
|
|
13231
|
-
const mainRoot =
|
|
13232
|
-
const inner =
|
|
13233
|
-
const sameFolder = inner.length === 0 || inner.startsWith("..") ||
|
|
13234
|
-
const mainPath = sameFolder ? mainRoot :
|
|
13235
|
-
const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(
|
|
13448
|
+
if (path35.basename(absoluteCommonDir).toLowerCase() === ".git") {
|
|
13449
|
+
const mainRoot = path35.dirname(absoluteCommonDir);
|
|
13450
|
+
const inner = path35.relative(path35.resolve(toplevel), path35.resolve(cwd));
|
|
13451
|
+
const sameFolder = inner.length === 0 || inner.startsWith("..") || path35.isAbsolute(inner);
|
|
13452
|
+
const mainPath = sameFolder ? mainRoot : path35.join(mainRoot, inner);
|
|
13453
|
+
const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(path35.sep), MEMORY_DIR].join("/");
|
|
13236
13454
|
main2 = { root: mainRoot, memoryRel };
|
|
13237
13455
|
worktree = {
|
|
13238
13456
|
mainPath,
|
|
@@ -13257,7 +13475,7 @@ async function samePath(a, b) {
|
|
|
13257
13475
|
try {
|
|
13258
13476
|
return await realpath2(value);
|
|
13259
13477
|
} catch {
|
|
13260
|
-
return
|
|
13478
|
+
return path35.resolve(value);
|
|
13261
13479
|
}
|
|
13262
13480
|
};
|
|
13263
13481
|
const [left, right] = await Promise.all([resolve(a), resolve(b)]);
|
|
@@ -13299,9 +13517,9 @@ async function listNoteFiles(folder) {
|
|
|
13299
13517
|
}
|
|
13300
13518
|
async function readIfFile(absolute, label) {
|
|
13301
13519
|
try {
|
|
13302
|
-
const info = await
|
|
13520
|
+
const info = await stat18(absolute);
|
|
13303
13521
|
if (!info.isFile()) throw new MemoryBridgeError(serverText("memoryNotAFile", { label }));
|
|
13304
|
-
return await
|
|
13522
|
+
return await readFile12(absolute);
|
|
13305
13523
|
} catch (error) {
|
|
13306
13524
|
if (isNotFound(error)) return null;
|
|
13307
13525
|
throw error;
|
|
@@ -13323,14 +13541,14 @@ async function readHead(absolute) {
|
|
|
13323
13541
|
}
|
|
13324
13542
|
async function isFile(absolute) {
|
|
13325
13543
|
try {
|
|
13326
|
-
return (await
|
|
13544
|
+
return (await stat18(absolute)).isFile();
|
|
13327
13545
|
} catch {
|
|
13328
13546
|
return false;
|
|
13329
13547
|
}
|
|
13330
13548
|
}
|
|
13331
13549
|
async function isDirectory2(absolute) {
|
|
13332
13550
|
try {
|
|
13333
|
-
return (await
|
|
13551
|
+
return (await stat18(absolute)).isDirectory();
|
|
13334
13552
|
} catch {
|
|
13335
13553
|
return false;
|
|
13336
13554
|
}
|
|
@@ -13348,7 +13566,7 @@ async function writeAtomic(target, content, mustNotExist) {
|
|
|
13348
13566
|
await handle.close();
|
|
13349
13567
|
}
|
|
13350
13568
|
if (mustNotExist && await exists2(target)) {
|
|
13351
|
-
throw new MemoryBridgeError(serverText("memoryAppeared", { file:
|
|
13569
|
+
throw new MemoryBridgeError(serverText("memoryAppeared", { file: path35.basename(target) }));
|
|
13352
13570
|
}
|
|
13353
13571
|
await renameWithRetry(temporary, target);
|
|
13354
13572
|
} catch (error) {
|
|
@@ -13359,9 +13577,9 @@ async function writeAtomic(target, content, mustNotExist) {
|
|
|
13359
13577
|
async function createTemporary(target) {
|
|
13360
13578
|
for (let attempt = 1; ; attempt += 1) {
|
|
13361
13579
|
const suffix = randomBytes4(6).toString("hex");
|
|
13362
|
-
const temporary =
|
|
13363
|
-
|
|
13364
|
-
`.${
|
|
13580
|
+
const temporary = path35.join(
|
|
13581
|
+
path35.dirname(target),
|
|
13582
|
+
`.${path35.basename(target)}.${process.pid}.${suffix}.tmp`
|
|
13365
13583
|
);
|
|
13366
13584
|
try {
|
|
13367
13585
|
return { temporary, handle: await open6(temporary, "wx") };
|
|
@@ -13373,7 +13591,7 @@ async function createTemporary(target) {
|
|
|
13373
13591
|
}
|
|
13374
13592
|
async function exists2(absolute) {
|
|
13375
13593
|
try {
|
|
13376
|
-
await
|
|
13594
|
+
await stat18(absolute);
|
|
13377
13595
|
return true;
|
|
13378
13596
|
} catch {
|
|
13379
13597
|
return false;
|
|
@@ -13599,13 +13817,13 @@ var MemoryHub = class extends EventEmitter3 {
|
|
|
13599
13817
|
}
|
|
13600
13818
|
};
|
|
13601
13819
|
function keyFor(cwd) {
|
|
13602
|
-
const resolved =
|
|
13820
|
+
const resolved = path36.resolve(cwd);
|
|
13603
13821
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
13604
13822
|
}
|
|
13605
13823
|
function isRelevantPath(cwd, candidate) {
|
|
13606
|
-
const relative =
|
|
13824
|
+
const relative = path36.relative(cwd, candidate);
|
|
13607
13825
|
if (relative.length === 0) return true;
|
|
13608
|
-
if (relative.startsWith("..") ||
|
|
13826
|
+
if (relative.startsWith("..") || path36.isAbsolute(relative)) return false;
|
|
13609
13827
|
const same = (a, b) => process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
13610
13828
|
const parts = relative.split(/[\\/]/);
|
|
13611
13829
|
const [first = "", second = "", third = ""] = parts;
|
|
@@ -13623,9 +13841,8 @@ function isRelevantPath(cwd, candidate) {
|
|
|
13623
13841
|
|
|
13624
13842
|
// packages/server/src/paste-store.ts
|
|
13625
13843
|
import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
|
|
13626
|
-
import { mkdir as mkdir8, readdir as readdir10, rm as rm3, stat as
|
|
13627
|
-
import
|
|
13628
|
-
import path35 from "node:path";
|
|
13844
|
+
import { mkdir as mkdir8, readdir as readdir10, rm as rm3, stat as stat19, writeFile as writeFile4 } from "node:fs/promises";
|
|
13845
|
+
import path37 from "node:path";
|
|
13629
13846
|
|
|
13630
13847
|
// packages/server/src/vault/markdown.ts
|
|
13631
13848
|
var pad2 = (value) => String(value).padStart(2, "0");
|
|
@@ -14140,8 +14357,8 @@ function buildContinuationMessage(input) {
|
|
|
14140
14357
|
}
|
|
14141
14358
|
return sanitizeForPaste(lines.join("\n"));
|
|
14142
14359
|
}
|
|
14143
|
-
function transcriptReferenceFor(
|
|
14144
|
-
return style === "at-quoted" ? fileReference(
|
|
14360
|
+
function transcriptReferenceFor(path52, style) {
|
|
14361
|
+
return style === "at-quoted" ? fileReference(path52, "at-quoted") : `"${path52}"`;
|
|
14145
14362
|
}
|
|
14146
14363
|
function planTranscript(input) {
|
|
14147
14364
|
const turns = splitTurns(input.events);
|
|
@@ -14265,32 +14482,6 @@ function textWithAttachments(text, lines) {
|
|
|
14265
14482
|
return lines.length === 0 ? text : [...lines, text].filter((piece) => piece.trim().length > 0).join("\n");
|
|
14266
14483
|
}
|
|
14267
14484
|
|
|
14268
|
-
// packages/server/src/image-signature.ts
|
|
14269
|
-
var FORMATS = [
|
|
14270
|
-
{
|
|
14271
|
-
format: { ext: "png", mediaType: "image/png" },
|
|
14272
|
-
test: (b) => b.length > 8 && b.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex"))
|
|
14273
|
-
},
|
|
14274
|
-
{
|
|
14275
|
-
format: { ext: "jpg", mediaType: "image/jpeg" },
|
|
14276
|
-
test: (b) => b.length > 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
|
|
14277
|
-
},
|
|
14278
|
-
{
|
|
14279
|
-
format: { ext: "gif", mediaType: "image/gif" },
|
|
14280
|
-
test: (b) => b.length > 6 && b.subarray(0, 4).toString("ascii") === "GIF8"
|
|
14281
|
-
},
|
|
14282
|
-
{
|
|
14283
|
-
format: { ext: "webp", mediaType: "image/webp" },
|
|
14284
|
-
test: (b) => b.length > 12 && b.subarray(0, 4).toString("ascii") === "RIFF" && b.subarray(8, 12).toString("ascii") === "WEBP"
|
|
14285
|
-
}
|
|
14286
|
-
];
|
|
14287
|
-
var IMAGE_MEDIA_TYPES = new Set(
|
|
14288
|
-
FORMATS.map((entry) => entry.format.mediaType)
|
|
14289
|
-
);
|
|
14290
|
-
function detectImageFormat(bytes) {
|
|
14291
|
-
return FORMATS.find((entry) => entry.test(bytes))?.format ?? null;
|
|
14292
|
-
}
|
|
14293
|
-
|
|
14294
14485
|
// packages/server/src/paste-store.ts
|
|
14295
14486
|
var EXTENSIONS = /* @__PURE__ */ new Map([
|
|
14296
14487
|
["image/png", "png"],
|
|
@@ -14316,7 +14507,7 @@ var PasteStore = class {
|
|
|
14316
14507
|
root;
|
|
14317
14508
|
counter = 0;
|
|
14318
14509
|
constructor(root) {
|
|
14319
|
-
this.root = root ??
|
|
14510
|
+
this.root = root ?? pasteRoot();
|
|
14320
14511
|
}
|
|
14321
14512
|
/**
|
|
14322
14513
|
* Guarda una imagen y devuelve su ruta.
|
|
@@ -14343,11 +14534,11 @@ var PasteStore = class {
|
|
|
14343
14534
|
if (signature === null) {
|
|
14344
14535
|
throw new PasteImageError(serverText("imageUnknownFormat"));
|
|
14345
14536
|
}
|
|
14346
|
-
const directory =
|
|
14537
|
+
const directory = path37.join(this.root, safeSegment(terminalId));
|
|
14347
14538
|
await mkdir8(directory, { recursive: true, mode: DIRECTORY_MODE });
|
|
14348
14539
|
this.counter += 1;
|
|
14349
14540
|
const name = `pegada-${this.counter}-${randomUUID3().slice(0, 8)}.${signature.ext}`;
|
|
14350
|
-
const file =
|
|
14541
|
+
const file = path37.join(directory, name);
|
|
14351
14542
|
await writeFile4(file, bytes, { mode: FILE_MODE });
|
|
14352
14543
|
return { path: file, bytes: bytes.length };
|
|
14353
14544
|
}
|
|
@@ -14365,11 +14556,11 @@ var PasteStore = class {
|
|
|
14365
14556
|
if (bytes.length > MAX_TEXT_BYTES) {
|
|
14366
14557
|
throw new PasteTextError(`The text is ${bytes.length} bytes; the maximum is ${MAX_TEXT_BYTES}.`);
|
|
14367
14558
|
}
|
|
14368
|
-
const directory =
|
|
14559
|
+
const directory = path37.join(this.root, safeSegment(terminalId));
|
|
14369
14560
|
await mkdir8(directory, { recursive: true, mode: DIRECTORY_MODE });
|
|
14370
14561
|
this.counter += 1;
|
|
14371
14562
|
const name = `${baseName}-${this.counter}-${randomUUID3().slice(0, 8)}.md`;
|
|
14372
|
-
const file =
|
|
14563
|
+
const file = path37.join(directory, name);
|
|
14373
14564
|
await writeFile4(file, bytes, { mode: FILE_MODE });
|
|
14374
14565
|
return { path: file, bytes: bytes.length };
|
|
14375
14566
|
}
|
|
@@ -14399,17 +14590,17 @@ var PasteStore = class {
|
|
|
14399
14590
|
})
|
|
14400
14591
|
);
|
|
14401
14592
|
}
|
|
14402
|
-
const directory =
|
|
14593
|
+
const directory = path37.join(this.root, safeSegment(terminalId));
|
|
14403
14594
|
await mkdir8(directory, { recursive: true, mode: DIRECTORY_MODE });
|
|
14404
14595
|
this.counter += 1;
|
|
14405
14596
|
const name = `adjunto-${this.counter}-${randomUUID3().slice(0, 8)}-${safe}`;
|
|
14406
|
-
const file =
|
|
14597
|
+
const file = path37.join(directory, name);
|
|
14407
14598
|
await writeFile4(file, bytes, { mode: FILE_MODE });
|
|
14408
14599
|
return { path: file, bytes: bytes.length, name };
|
|
14409
14600
|
}
|
|
14410
14601
|
/** Borra lo de una pestana. Se llama al cerrarla. */
|
|
14411
14602
|
async clearTerminal(terminalId) {
|
|
14412
|
-
await rm3(
|
|
14603
|
+
await rm3(path37.join(this.root, safeSegment(terminalId)), {
|
|
14413
14604
|
recursive: true,
|
|
14414
14605
|
force: true
|
|
14415
14606
|
}).catch(() => {
|
|
@@ -14431,9 +14622,9 @@ var PasteStore = class {
|
|
|
14431
14622
|
}
|
|
14432
14623
|
await Promise.all(
|
|
14433
14624
|
entries.map(async (entry) => {
|
|
14434
|
-
const full =
|
|
14625
|
+
const full = path37.join(this.root, entry);
|
|
14435
14626
|
try {
|
|
14436
|
-
const info = await
|
|
14627
|
+
const info = await stat19(full);
|
|
14437
14628
|
if (now - info.mtimeMs > STALE_AFTER_MS) {
|
|
14438
14629
|
await rm3(full, { recursive: true, force: true });
|
|
14439
14630
|
}
|
|
@@ -14450,8 +14641,8 @@ function safeSegment(value) {
|
|
|
14450
14641
|
|
|
14451
14642
|
// packages/server/src/session-index.ts
|
|
14452
14643
|
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
14453
|
-
import { mkdir as mkdir9, readFile as
|
|
14454
|
-
import
|
|
14644
|
+
import { mkdir as mkdir9, readFile as readFile13, stat as stat20, writeFile as writeFile5 } from "node:fs/promises";
|
|
14645
|
+
import path38 from "node:path";
|
|
14455
14646
|
var CACHE_VERSION = 4;
|
|
14456
14647
|
var vaultPairKey = (agent, sessionId) => `${agent}/${sessionId}`;
|
|
14457
14648
|
function vaultOnlySessions(natives, vault, listState, ready) {
|
|
@@ -14463,7 +14654,10 @@ function vaultOnlySessions(natives, vault, listState, ready) {
|
|
|
14463
14654
|
for (const copy of vault) {
|
|
14464
14655
|
const pair = vaultPairKey(copy.agent, copy.sessionId);
|
|
14465
14656
|
if (native.has(pair) || seen.has(pair)) continue;
|
|
14466
|
-
if (!isImportedAgentId(copy.agent)
|
|
14657
|
+
if (!isImportedAgentId(copy.agent)) {
|
|
14658
|
+
const state = listState.get(copy.agent) ?? "missing";
|
|
14659
|
+
if (state === "unreadable" || state === "off") continue;
|
|
14660
|
+
}
|
|
14467
14661
|
seen.add(pair);
|
|
14468
14662
|
const hasCwd = copy.cwd.length > 0;
|
|
14469
14663
|
result.push({
|
|
@@ -14500,7 +14694,7 @@ function cacheKey(agent, ref) {
|
|
|
14500
14694
|
}
|
|
14501
14695
|
async function pathExists3(candidate) {
|
|
14502
14696
|
try {
|
|
14503
|
-
await
|
|
14697
|
+
await stat20(candidate);
|
|
14504
14698
|
return true;
|
|
14505
14699
|
} catch {
|
|
14506
14700
|
return false;
|
|
@@ -14550,7 +14744,7 @@ function migrateEntryV3(filePath, value) {
|
|
|
14550
14744
|
return {
|
|
14551
14745
|
agent: "claude-code",
|
|
14552
14746
|
ref: filePath,
|
|
14553
|
-
group:
|
|
14747
|
+
group: path38.basename(path38.dirname(filePath)),
|
|
14554
14748
|
mtimeMs,
|
|
14555
14749
|
sizeBytes,
|
|
14556
14750
|
cwd,
|
|
@@ -14815,7 +15009,7 @@ var SessionIndex = class extends EventEmitter4 {
|
|
|
14815
15009
|
}
|
|
14816
15010
|
async loadCache() {
|
|
14817
15011
|
try {
|
|
14818
|
-
const raw = await
|
|
15012
|
+
const raw = await readFile13(sessionIndexCachePath(), "utf8");
|
|
14819
15013
|
const parsed = JSON.parse(raw);
|
|
14820
15014
|
const migrated = migrateIndexCache(parsed);
|
|
14821
15015
|
if (migrated === null) return;
|
|
@@ -14845,7 +15039,7 @@ var SessionIndex = class extends EventEmitter4 {
|
|
|
14845
15039
|
}
|
|
14846
15040
|
this.scanning = true;
|
|
14847
15041
|
try {
|
|
14848
|
-
const sources = this.agents.all().map(({ adapter }) => ({
|
|
15042
|
+
const sources = (this.agents.historySources?.() ?? this.agents.all()).map(({ adapter }) => ({
|
|
14849
15043
|
agent: adapter.id,
|
|
14850
15044
|
history: adapter.history
|
|
14851
15045
|
}));
|
|
@@ -14865,6 +15059,9 @@ var SessionIndex = class extends EventEmitter4 {
|
|
|
14865
15059
|
const outcome = outcomes[position] ?? { items: null, threw: true };
|
|
14866
15060
|
states.set(source.agent, await nativeListState(source.history, outcome));
|
|
14867
15061
|
}
|
|
15062
|
+
for (const { adapter } of this.agents.all()) {
|
|
15063
|
+
if (!states.has(adapter.id)) states.set(adapter.id, "off");
|
|
15064
|
+
}
|
|
14868
15065
|
this.listState = states;
|
|
14869
15066
|
if (lists.every((items) => items === null)) {
|
|
14870
15067
|
this.sessions.clear();
|
|
@@ -15040,8 +15237,8 @@ var SessionIndex = class extends EventEmitter4 {
|
|
|
15040
15237
|
};
|
|
15041
15238
|
|
|
15042
15239
|
// packages/server/src/settings-store.ts
|
|
15043
|
-
import { mkdir as mkdir10, readFile as
|
|
15044
|
-
import
|
|
15240
|
+
import { mkdir as mkdir10, readFile as readFile14, stat as stat21 } from "node:fs/promises";
|
|
15241
|
+
import path39 from "node:path";
|
|
15045
15242
|
var SETTINGS_VERSION = 1;
|
|
15046
15243
|
var VAULT_TOOL_RESULT_DEFAULT_CHARS = 64e3;
|
|
15047
15244
|
var VAULT_TOOL_RESULT_MIN_CHARS = 4e3;
|
|
@@ -15050,13 +15247,15 @@ function defaultAppSettings() {
|
|
|
15050
15247
|
return {
|
|
15051
15248
|
version: SETTINGS_VERSION,
|
|
15052
15249
|
vault: { enabled: false, dir: null, toolResultMaxChars: VAULT_TOOL_RESULT_DEFAULT_CHARS },
|
|
15053
|
-
remote: { enabled: false, port: REMOTE_DEFAULT_PORT }
|
|
15250
|
+
remote: { enabled: false, port: REMOTE_DEFAULT_PORT },
|
|
15251
|
+
console: { shell: "auto" },
|
|
15252
|
+
agents: { disabled: [] }
|
|
15054
15253
|
};
|
|
15055
15254
|
}
|
|
15056
15255
|
function isAbsoluteDir(dir, platform) {
|
|
15057
15256
|
if (dir.length === 0 || dir.includes("\0")) return false;
|
|
15058
15257
|
if (platform === "win32") return /^[A-Za-z]:[\\/]/.test(dir) || /^\\\\[^\\/]+[\\/][^\\/]+/.test(dir);
|
|
15059
|
-
return
|
|
15258
|
+
return path39.posix.isAbsolute(dir);
|
|
15060
15259
|
}
|
|
15061
15260
|
function clampToolResultMaxChars(value) {
|
|
15062
15261
|
if (typeof value !== "number" || !Number.isFinite(value)) return VAULT_TOOL_RESULT_DEFAULT_CHARS;
|
|
@@ -15066,6 +15265,12 @@ function parseAppSettings(value, platform, warn = () => void 0) {
|
|
|
15066
15265
|
const record = asRecord(value);
|
|
15067
15266
|
if (record === null || record["version"] !== SETTINGS_VERSION) return null;
|
|
15068
15267
|
const settings2 = defaultAppSettings();
|
|
15268
|
+
const consoleRecord = asRecord(record["console"]);
|
|
15269
|
+
const shell = consoleRecord === null ? null : asLiteral(consoleRecord["shell"], CONSOLE_SHELL_IDS);
|
|
15270
|
+
if (shell !== null) settings2.console.shell = shell;
|
|
15271
|
+
const agentsRecord = asRecord(record["agents"]);
|
|
15272
|
+
const disabled = agentsRecord === null ? null : asAgentIdList(agentsRecord["disabled"]);
|
|
15273
|
+
if (disabled !== null) settings2.agents.disabled = disabled;
|
|
15069
15274
|
const remote = asRecord(record["remote"]);
|
|
15070
15275
|
if (remote !== null) {
|
|
15071
15276
|
settings2.remote.enabled = asBoolean(remote["enabled"]) ?? false;
|
|
@@ -15078,7 +15283,7 @@ function parseAppSettings(value, platform, warn = () => void 0) {
|
|
|
15078
15283
|
if (typeof dir === "string" && isAbsoluteDir(dir, platform)) {
|
|
15079
15284
|
settings2.vault.dir = dir;
|
|
15080
15285
|
} else if (dir !== null && dir !== void 0) {
|
|
15081
|
-
warn(`[settings] the
|
|
15286
|
+
warn(`[settings] the backup folder ${JSON.stringify(dir)} isn't an absolute path: using the default one`);
|
|
15082
15287
|
}
|
|
15083
15288
|
settings2.vault.toolResultMaxChars = clampToolResultMaxChars(vault["toolResultMaxChars"]);
|
|
15084
15289
|
return settings2;
|
|
@@ -15087,7 +15292,9 @@ function frozen(settings2) {
|
|
|
15087
15292
|
return Object.freeze({
|
|
15088
15293
|
...settings2,
|
|
15089
15294
|
vault: Object.freeze({ ...settings2.vault }),
|
|
15090
|
-
remote: Object.freeze({ ...settings2.remote })
|
|
15295
|
+
remote: Object.freeze({ ...settings2.remote }),
|
|
15296
|
+
console: Object.freeze({ ...settings2.console }),
|
|
15297
|
+
agents: Object.freeze({ disabled: Object.freeze([...settings2.agents.disabled]) })
|
|
15091
15298
|
});
|
|
15092
15299
|
}
|
|
15093
15300
|
var SettingsStore = class {
|
|
@@ -15107,7 +15314,7 @@ var SettingsStore = class {
|
|
|
15107
15314
|
async load() {
|
|
15108
15315
|
let raw;
|
|
15109
15316
|
try {
|
|
15110
|
-
raw = await
|
|
15317
|
+
raw = await readFile14(this.filePath, "utf8");
|
|
15111
15318
|
} catch {
|
|
15112
15319
|
this.current = frozen(defaultAppSettings());
|
|
15113
15320
|
return;
|
|
@@ -15135,7 +15342,7 @@ var SettingsStore = class {
|
|
|
15135
15342
|
async reloadRemote() {
|
|
15136
15343
|
let parsed;
|
|
15137
15344
|
try {
|
|
15138
|
-
parsed = JSON.parse(await
|
|
15345
|
+
parsed = JSON.parse(await readFile14(this.filePath, "utf8"));
|
|
15139
15346
|
} catch {
|
|
15140
15347
|
return;
|
|
15141
15348
|
}
|
|
@@ -15146,7 +15353,7 @@ var SettingsStore = class {
|
|
|
15146
15353
|
/** Una marca que cambia cuando el archivo cambia (fecha y tamano), o '' si no esta. */
|
|
15147
15354
|
async stamp() {
|
|
15148
15355
|
try {
|
|
15149
|
-
const info = await
|
|
15356
|
+
const info = await stat21(this.filePath);
|
|
15150
15357
|
return `${info.mtimeMs}:${info.size}`;
|
|
15151
15358
|
} catch {
|
|
15152
15359
|
return "";
|
|
@@ -15167,26 +15374,30 @@ var SettingsStore = class {
|
|
|
15167
15374
|
const next = {
|
|
15168
15375
|
version: SETTINGS_VERSION,
|
|
15169
15376
|
vault: { ...this.current.vault },
|
|
15170
|
-
remote: { ...this.current.remote }
|
|
15377
|
+
remote: { ...this.current.remote },
|
|
15378
|
+
console: { ...this.current.console },
|
|
15379
|
+
agents: { disabled: [...this.current.agents.disabled] }
|
|
15171
15380
|
};
|
|
15172
15381
|
const vault = patch.vault ?? {};
|
|
15173
15382
|
if (vault.enabled !== void 0) next.vault.enabled = vault.enabled;
|
|
15174
15383
|
if (vault.dir !== void 0) {
|
|
15175
15384
|
if (vault.dir !== null && !isAbsoluteDir(vault.dir, this.platform)) {
|
|
15176
|
-
throw new Error(`The
|
|
15385
|
+
throw new Error(`The backup folder has to be an absolute path: ${JSON.stringify(vault.dir)}`);
|
|
15177
15386
|
}
|
|
15178
15387
|
next.vault.dir = vault.dir;
|
|
15179
15388
|
}
|
|
15180
15389
|
if (vault.toolResultMaxChars !== void 0) {
|
|
15181
15390
|
next.vault.toolResultMaxChars = clampToolResultMaxChars(vault.toolResultMaxChars);
|
|
15182
15391
|
}
|
|
15392
|
+
if (patch.console?.shell !== void 0) next.console.shell = patch.console.shell;
|
|
15393
|
+
if (patch.agents?.disabled !== void 0) next.agents.disabled = [...new Set(patch.agents.disabled)];
|
|
15183
15394
|
const remote = patch.remote ?? {};
|
|
15184
15395
|
if (remote.enabled !== void 0) next.remote.enabled = remote.enabled;
|
|
15185
15396
|
if (remote.port !== void 0) {
|
|
15186
15397
|
if (!isRemotePort(remote.port)) throw new Error(`The remote access port isn't valid: ${JSON.stringify(remote.port)}`);
|
|
15187
15398
|
next.remote.port = remote.port;
|
|
15188
15399
|
}
|
|
15189
|
-
await mkdir10(
|
|
15400
|
+
await mkdir10(path39.dirname(this.filePath), { recursive: true });
|
|
15190
15401
|
await writeFileAtomic(this.filePath, `${JSON.stringify(next, null, 2)}
|
|
15191
15402
|
`);
|
|
15192
15403
|
this.current = frozen(next);
|
|
@@ -15274,8 +15485,8 @@ function supportStartupLineColored() {
|
|
|
15274
15485
|
|
|
15275
15486
|
// packages/server/src/notes-store.ts
|
|
15276
15487
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
15277
|
-
import { mkdir as mkdir11, readFile as
|
|
15278
|
-
import
|
|
15488
|
+
import { mkdir as mkdir11, readFile as readFile15, rename as rename5, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
|
|
15489
|
+
import path40 from "node:path";
|
|
15279
15490
|
var STATE_VERSION2 = 1;
|
|
15280
15491
|
var WRITE_DEBOUNCE_MS2 = 500;
|
|
15281
15492
|
var NotesError = class extends ServerTextError {
|
|
@@ -15318,7 +15529,7 @@ var NotesStore = class {
|
|
|
15318
15529
|
}
|
|
15319
15530
|
async load() {
|
|
15320
15531
|
try {
|
|
15321
|
-
const raw = await
|
|
15532
|
+
const raw = await readFile15(this.statePath, "utf8");
|
|
15322
15533
|
this.notes = parseState2(raw) ?? [];
|
|
15323
15534
|
} catch {
|
|
15324
15535
|
this.notes = [];
|
|
@@ -15435,7 +15646,7 @@ var NotesStore = class {
|
|
|
15435
15646
|
const image = this.findImage(imageId);
|
|
15436
15647
|
if (image === null) return null;
|
|
15437
15648
|
try {
|
|
15438
|
-
const bytes = await
|
|
15649
|
+
const bytes = await readFile15(this.imagePath(image));
|
|
15439
15650
|
return { mediaType: image.mediaType, data: bytes.toString("base64") };
|
|
15440
15651
|
} catch {
|
|
15441
15652
|
return null;
|
|
@@ -15455,7 +15666,7 @@ var NotesStore = class {
|
|
|
15455
15666
|
const images = [];
|
|
15456
15667
|
for (const image of note.images) {
|
|
15457
15668
|
try {
|
|
15458
|
-
const bytes = await
|
|
15669
|
+
const bytes = await readFile15(this.imagePath(image));
|
|
15459
15670
|
images.push({ mediaType: image.mediaType, data: bytes.toString("base64") });
|
|
15460
15671
|
} catch {
|
|
15461
15672
|
}
|
|
@@ -15479,7 +15690,7 @@ var NotesStore = class {
|
|
|
15479
15690
|
}
|
|
15480
15691
|
imagePath(image) {
|
|
15481
15692
|
const safe = image.imageId.replace(/[^a-zA-Z0-9-]/g, "");
|
|
15482
|
-
return
|
|
15693
|
+
return path40.join(this.imagesDir, `${safe}.${extensionFor(image.mediaType)}`);
|
|
15483
15694
|
}
|
|
15484
15695
|
async unlinkImage(image) {
|
|
15485
15696
|
await rm4(this.imagePath(image), { force: true }).catch(() => {
|
|
@@ -15497,8 +15708,8 @@ var NotesStore = class {
|
|
|
15497
15708
|
async writeNow() {
|
|
15498
15709
|
this.dirty = false;
|
|
15499
15710
|
try {
|
|
15500
|
-
await mkdir11(
|
|
15501
|
-
const temporary =
|
|
15711
|
+
await mkdir11(path40.dirname(this.statePath), { recursive: true });
|
|
15712
|
+
const temporary = path40.join(path40.dirname(this.statePath), `notes.${process.pid}.tmp`);
|
|
15502
15713
|
await writeFile6(
|
|
15503
15714
|
temporary,
|
|
15504
15715
|
JSON.stringify({ version: STATE_VERSION2, notes: this.notes }, null, 2),
|
|
@@ -15512,8 +15723,8 @@ var NotesStore = class {
|
|
|
15512
15723
|
};
|
|
15513
15724
|
|
|
15514
15725
|
// packages/server/src/draft-store.ts
|
|
15515
|
-
import { mkdir as mkdir12, readFile as
|
|
15516
|
-
import
|
|
15726
|
+
import { mkdir as mkdir12, readFile as readFile16, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
|
|
15727
|
+
import path41 from "node:path";
|
|
15517
15728
|
var STATE_VERSION3 = 1;
|
|
15518
15729
|
var WRITE_DEBOUNCE_MS3 = 500;
|
|
15519
15730
|
var MAX_STORED_DRAFTS = 100;
|
|
@@ -15607,7 +15818,7 @@ var DraftStore = class {
|
|
|
15607
15818
|
}
|
|
15608
15819
|
async load() {
|
|
15609
15820
|
try {
|
|
15610
|
-
this.drafts = parseState3(await
|
|
15821
|
+
this.drafts = parseState3(await readFile16(this.statePath, "utf8"));
|
|
15611
15822
|
} catch {
|
|
15612
15823
|
this.drafts = /* @__PURE__ */ new Map();
|
|
15613
15824
|
}
|
|
@@ -15698,7 +15909,7 @@ var DraftStore = class {
|
|
|
15698
15909
|
async writeNow() {
|
|
15699
15910
|
let onDisk = /* @__PURE__ */ new Map();
|
|
15700
15911
|
try {
|
|
15701
|
-
onDisk = parseState3(await
|
|
15912
|
+
onDisk = parseState3(await readFile16(this.statePath, "utf8"));
|
|
15702
15913
|
} catch {
|
|
15703
15914
|
}
|
|
15704
15915
|
const changed = this.changed;
|
|
@@ -15712,8 +15923,8 @@ var DraftStore = class {
|
|
|
15712
15923
|
trimOldest(merged);
|
|
15713
15924
|
this.drafts = new Map(merged);
|
|
15714
15925
|
try {
|
|
15715
|
-
await mkdir12(
|
|
15716
|
-
const temporary =
|
|
15926
|
+
await mkdir12(path41.dirname(this.statePath), { recursive: true });
|
|
15927
|
+
const temporary = path41.join(path41.dirname(this.statePath), `composer-drafts.${process.pid}.tmp`);
|
|
15717
15928
|
const payload = {
|
|
15718
15929
|
version: STATE_VERSION3,
|
|
15719
15930
|
drafts: [...merged.values()].map(({ agent, sessionId, updatedAt, draft }) => ({ agent, sessionId, updatedAt, draft }))
|
|
@@ -15730,7 +15941,7 @@ var DraftStore = class {
|
|
|
15730
15941
|
// packages/server/src/shell-locator.ts
|
|
15731
15942
|
import { access as access2 } from "node:fs/promises";
|
|
15732
15943
|
import { constants as fsConstants2 } from "node:fs";
|
|
15733
|
-
import
|
|
15944
|
+
import path42 from "node:path";
|
|
15734
15945
|
async function isExecutable(candidate) {
|
|
15735
15946
|
try {
|
|
15736
15947
|
await access2(candidate, process.platform === "win32" ? fsConstants2.R_OK : fsConstants2.X_OK);
|
|
@@ -15739,47 +15950,139 @@ async function isExecutable(candidate) {
|
|
|
15739
15950
|
return false;
|
|
15740
15951
|
}
|
|
15741
15952
|
}
|
|
15742
|
-
|
|
15743
|
-
|
|
15953
|
+
var SYSTEM_PROBE = {
|
|
15954
|
+
platform: process.platform,
|
|
15955
|
+
env: process.env,
|
|
15956
|
+
findInPath,
|
|
15957
|
+
isExecutable
|
|
15958
|
+
};
|
|
15959
|
+
function windowsPowerShellPath(env) {
|
|
15960
|
+
const systemRoot = env["SystemRoot"] ?? env["windir"];
|
|
15744
15961
|
if (systemRoot === void 0 || systemRoot.length === 0) return null;
|
|
15745
|
-
return
|
|
15962
|
+
return path42.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
15746
15963
|
}
|
|
15747
|
-
|
|
15748
|
-
const
|
|
15749
|
-
if (
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
|
|
15964
|
+
function gitBashCandidates(gitPath, env) {
|
|
15965
|
+
const candidates = [];
|
|
15966
|
+
if (gitPath !== null) {
|
|
15967
|
+
let dir = path42.win32.dirname(gitPath);
|
|
15968
|
+
for (let level = 0; level < 3; level++) {
|
|
15969
|
+
candidates.push(path42.win32.join(dir, "bin", "bash.exe"));
|
|
15970
|
+
const parent = path42.win32.dirname(dir);
|
|
15971
|
+
if (parent === dir) break;
|
|
15972
|
+
dir = parent;
|
|
15973
|
+
}
|
|
15755
15974
|
}
|
|
15756
|
-
const
|
|
15757
|
-
|
|
15758
|
-
return { file: comSpec, args: [], name: "cmd" };
|
|
15975
|
+
for (const base of [env["ProgramFiles"], env["LOCALAPPDATA"] === void 0 ? void 0 : path42.win32.join(env["LOCALAPPDATA"], "Programs")]) {
|
|
15976
|
+
if (base !== void 0 && base.length > 0) candidates.push(path42.win32.join(base, "Git", "bin", "bash.exe"));
|
|
15759
15977
|
}
|
|
15760
|
-
|
|
15978
|
+
const systemRoot = (env["SystemRoot"] ?? env["windir"] ?? "").toLowerCase();
|
|
15979
|
+
return [...new Set(candidates)].filter((candidate) => systemRoot.length === 0 || !candidate.toLowerCase().startsWith(systemRoot));
|
|
15761
15980
|
}
|
|
15762
|
-
async function
|
|
15763
|
-
const
|
|
15764
|
-
|
|
15765
|
-
|
|
15981
|
+
async function locateWindowsShells(probe) {
|
|
15982
|
+
const found = [];
|
|
15983
|
+
const pwsh = await probe.findInPath("pwsh");
|
|
15984
|
+
if (pwsh !== null) found.push({ id: "pwsh", label: "PowerShell 7", location: { file: pwsh, args: ["-NoLogo"], name: "PowerShell" } });
|
|
15985
|
+
const fromPath = await probe.findInPath("powershell");
|
|
15986
|
+
const systemCopy = windowsPowerShellPath(probe.env);
|
|
15987
|
+
const windowsPowerShell = fromPath ?? (systemCopy !== null && await probe.isExecutable(systemCopy) ? systemCopy : null);
|
|
15988
|
+
if (windowsPowerShell !== null) {
|
|
15989
|
+
found.push({ id: "powershell", label: "Windows PowerShell", location: { file: windowsPowerShell, args: ["-NoLogo"], name: "PowerShell" } });
|
|
15990
|
+
}
|
|
15991
|
+
const comSpec = probe.env["ComSpec"];
|
|
15992
|
+
if (comSpec !== void 0 && comSpec.length > 0 && await probe.isExecutable(comSpec)) {
|
|
15993
|
+
found.push({ id: "cmd", label: "cmd", location: { file: comSpec, args: [], name: "cmd" } });
|
|
15994
|
+
}
|
|
15995
|
+
for (const candidate of gitBashCandidates(await probe.findInPath("git"), probe.env)) {
|
|
15996
|
+
if (await probe.isExecutable(candidate)) {
|
|
15997
|
+
found.push({ id: "git-bash", label: "Git Bash", location: { file: candidate, args: ["--login", "-i"], name: "Git Bash" } });
|
|
15998
|
+
break;
|
|
15999
|
+
}
|
|
15766
16000
|
}
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
16001
|
+
return found;
|
|
16002
|
+
}
|
|
16003
|
+
async function locatePosixShells(probe) {
|
|
16004
|
+
const found = [];
|
|
16005
|
+
const preferred = probe.env["SHELL"];
|
|
16006
|
+
if (preferred !== void 0 && preferred.length > 0 && await probe.isExecutable(preferred)) {
|
|
16007
|
+
const name = path42.posix.basename(preferred);
|
|
16008
|
+
found.push({ id: "login", label: name, location: { file: preferred, args: [], name } });
|
|
16009
|
+
}
|
|
16010
|
+
for (const [id, file] of [["bash", "/bin/bash"], ["sh", "/bin/sh"], ["zsh", "/bin/zsh"]]) {
|
|
16011
|
+
if (found.some((shell) => shell.location.file === file)) continue;
|
|
16012
|
+
if (await probe.isExecutable(file)) found.push({ id, label: id, location: { file, args: [], name: id } });
|
|
16013
|
+
}
|
|
16014
|
+
return found;
|
|
15770
16015
|
}
|
|
15771
|
-
async function
|
|
15772
|
-
return
|
|
16016
|
+
async function locateShells(probe = SYSTEM_PROBE) {
|
|
16017
|
+
return probe.platform === "win32" ? locateWindowsShells(probe) : locatePosixShells(probe);
|
|
15773
16018
|
}
|
|
16019
|
+
var NEVER_AUTO = /* @__PURE__ */ new Set(["git-bash", "zsh"]);
|
|
16020
|
+
function pickShell(candidates, choice) {
|
|
16021
|
+
if (choice !== "auto") {
|
|
16022
|
+
const chosen = candidates.find((candidate) => candidate.id === choice);
|
|
16023
|
+
if (chosen !== void 0) return chosen.location;
|
|
16024
|
+
}
|
|
16025
|
+
return candidates.find((candidate) => !NEVER_AUTO.has(candidate.id))?.location ?? null;
|
|
16026
|
+
}
|
|
16027
|
+
|
|
16028
|
+
// packages/server/src/app-settings-service.ts
|
|
16029
|
+
import { EventEmitter as EventEmitter5 } from "node:events";
|
|
16030
|
+
var AppSettingsChangeError = class extends Error {
|
|
16031
|
+
constructor(reason2) {
|
|
16032
|
+
super(reason2);
|
|
16033
|
+
this.reason = reason2;
|
|
16034
|
+
}
|
|
16035
|
+
reason;
|
|
16036
|
+
};
|
|
16037
|
+
var AppSettingsService = class extends EventEmitter5 {
|
|
16038
|
+
constructor(options) {
|
|
16039
|
+
super();
|
|
16040
|
+
this.options = options;
|
|
16041
|
+
this.disabledAtStart = options.agents.disabledIds();
|
|
16042
|
+
}
|
|
16043
|
+
options;
|
|
16044
|
+
disabledAtStart;
|
|
16045
|
+
/** La consola que abre una nueva con lo que dice `settings.json`. */
|
|
16046
|
+
currentShell() {
|
|
16047
|
+
return pickShell(this.options.shells, this.options.settings.get().console.shell);
|
|
16048
|
+
}
|
|
16049
|
+
status() {
|
|
16050
|
+
const settings2 = this.options.settings.get();
|
|
16051
|
+
return {
|
|
16052
|
+
consoleShell: settings2.console.shell,
|
|
16053
|
+
consoleShells: this.options.shells.map(({ id, label }) => ({ id, label })),
|
|
16054
|
+
shellName: this.currentShell()?.name ?? null,
|
|
16055
|
+
disabledAgents: [...settings2.agents.disabled],
|
|
16056
|
+
disabledAtStart: [...this.disabledAtStart]
|
|
16057
|
+
};
|
|
16058
|
+
}
|
|
16059
|
+
/** Guarda el cambio, avisa al registro si cambio la consola, y emite `status`. */
|
|
16060
|
+
async update(change) {
|
|
16061
|
+
if (change.consoleShell !== void 0 && !this.offered(change.consoleShell)) {
|
|
16062
|
+
throw new AppSettingsChangeError("shell-not-installed");
|
|
16063
|
+
}
|
|
16064
|
+
const before = this.currentShell();
|
|
16065
|
+
await this.options.settings.update({
|
|
16066
|
+
...change.consoleShell !== void 0 ? { console: { shell: change.consoleShell } } : {},
|
|
16067
|
+
...change.disabledAgents !== void 0 ? { agents: { disabled: change.disabledAgents } } : {}
|
|
16068
|
+
});
|
|
16069
|
+
const after = this.currentShell();
|
|
16070
|
+
if (after !== before) this.options.onShellChange(after);
|
|
16071
|
+
this.emit("status", this.status());
|
|
16072
|
+
}
|
|
16073
|
+
offered(choice) {
|
|
16074
|
+
return choice === "auto" || this.options.shells.some((shell) => shell.id === choice);
|
|
16075
|
+
}
|
|
16076
|
+
};
|
|
15774
16077
|
|
|
15775
16078
|
// packages/server/src/session-watcher.ts
|
|
15776
|
-
import { stat as
|
|
16079
|
+
import { stat as stat22 } from "node:fs/promises";
|
|
15777
16080
|
import chokidar3 from "chokidar";
|
|
15778
16081
|
var INDEX_DEBOUNCE_MS = 750;
|
|
15779
16082
|
var ROOT_PROBE_MS = 5e3;
|
|
15780
16083
|
async function isDirectory3(target) {
|
|
15781
16084
|
try {
|
|
15782
|
-
return (await
|
|
16085
|
+
return (await stat22(target)).isDirectory();
|
|
15783
16086
|
} catch {
|
|
15784
16087
|
return false;
|
|
15785
16088
|
}
|
|
@@ -15849,7 +16152,7 @@ function watchSessions(index, hub, agents, options = {}) {
|
|
|
15849
16152
|
probe.unref();
|
|
15850
16153
|
probes.add(probe);
|
|
15851
16154
|
};
|
|
15852
|
-
for (const { adapter } of agents.all()) {
|
|
16155
|
+
for (const { adapter } of agents.historySources?.() ?? agents.all()) {
|
|
15853
16156
|
for (const root of adapter.history.roots()) {
|
|
15854
16157
|
if (root.watch !== void 0) {
|
|
15855
16158
|
subscribeRoot(adapter.id, root, root.watch);
|
|
@@ -15881,8 +16184,8 @@ function watchSessions(index, hub, agents, options = {}) {
|
|
|
15881
16184
|
|
|
15882
16185
|
// packages/server/src/terminal-registry.ts
|
|
15883
16186
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
15884
|
-
import { EventEmitter as
|
|
15885
|
-
import { stat as
|
|
16187
|
+
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
16188
|
+
import { stat as stat23 } from "node:fs/promises";
|
|
15886
16189
|
|
|
15887
16190
|
// packages/server/src/launch-hook-slot.ts
|
|
15888
16191
|
var LaunchHookSlot = class {
|
|
@@ -16144,8 +16447,8 @@ async function resolveLaunchPlan(launch) {
|
|
|
16144
16447
|
}
|
|
16145
16448
|
|
|
16146
16449
|
// packages/server/src/workspace-store.ts
|
|
16147
|
-
import { mkdir as mkdir13, readFile as
|
|
16148
|
-
import
|
|
16450
|
+
import { mkdir as mkdir13, readFile as readFile17, writeFile as writeFile8 } from "node:fs/promises";
|
|
16451
|
+
import path43 from "node:path";
|
|
16149
16452
|
var STATE_VERSION4 = 1;
|
|
16150
16453
|
var WRITE_DEBOUNCE_MS4 = 400;
|
|
16151
16454
|
var LEGACY_TAB_AGENT = "claude-code";
|
|
@@ -16267,7 +16570,7 @@ var WorkspaceStore = class {
|
|
|
16267
16570
|
pending = null;
|
|
16268
16571
|
async load() {
|
|
16269
16572
|
try {
|
|
16270
|
-
const raw = await
|
|
16573
|
+
const raw = await readFile17(workspaceStatePath(), "utf8");
|
|
16271
16574
|
return parseState4(raw) ?? EMPTY_STATE();
|
|
16272
16575
|
} catch {
|
|
16273
16576
|
return EMPTY_STATE();
|
|
@@ -16299,7 +16602,7 @@ var WorkspaceStore = class {
|
|
|
16299
16602
|
try {
|
|
16300
16603
|
await mkdir13(appConfigDir(), { recursive: true });
|
|
16301
16604
|
const target = workspaceStatePath();
|
|
16302
|
-
const temporary =
|
|
16605
|
+
const temporary = path43.join(path43.dirname(target), `workspace.${process.pid}.tmp`);
|
|
16303
16606
|
await writeFile8(temporary, JSON.stringify(serializeState(state), null, 2), "utf8");
|
|
16304
16607
|
const { rename: rename7 } = await import("node:fs/promises");
|
|
16305
16608
|
await rename7(temporary, target);
|
|
@@ -16314,7 +16617,7 @@ var MAX_TERMINALS = 24;
|
|
|
16314
16617
|
var RESTART_EXIT_TIMEOUT_MS = 1e4;
|
|
16315
16618
|
var DEFAULT_COLS = 80;
|
|
16316
16619
|
var DEFAULT_ROWS = 24;
|
|
16317
|
-
var TerminalRegistry = class extends
|
|
16620
|
+
var TerminalRegistry = class extends EventEmitter6 {
|
|
16318
16621
|
constructor(agents, shell, store, sessionAgentOf = () => null) {
|
|
16319
16622
|
super();
|
|
16320
16623
|
this.agents = agents;
|
|
@@ -16338,6 +16641,10 @@ var TerminalRegistry = class extends EventEmitter5 {
|
|
|
16338
16641
|
unavailableTabs = [];
|
|
16339
16642
|
/** Pestanas guardadas de una CLI que esta build no conoce. Idem, crudas. */
|
|
16340
16643
|
foreignTabs = [];
|
|
16644
|
+
/** Otra consola para las que se abran desde ahora (Ajustes, Hito 41). Las abiertas no cambian. */
|
|
16645
|
+
setShell(shell) {
|
|
16646
|
+
this.shell = shell;
|
|
16647
|
+
}
|
|
16341
16648
|
list() {
|
|
16342
16649
|
return this.order.map((id) => this.terminals.get(id)?.descriptor).filter((descriptor) => descriptor !== void 0);
|
|
16343
16650
|
}
|
|
@@ -16427,7 +16734,9 @@ var TerminalRegistry = class extends EventEmitter5 {
|
|
|
16427
16734
|
cwd: options.cwd,
|
|
16428
16735
|
// Provisional: `spawn` lo confirma con lo que diga el adaptador.
|
|
16429
16736
|
sessionId: kind === "agent" ? options.resumeSessionId ?? randomUUID5() : "",
|
|
16430
|
-
|
|
16737
|
+
// Una consola lleva el nombre de la suya: desde el Hito 41 pueden convivir
|
|
16738
|
+
// PowerShell y Git Bash, y el nombre de la consola nueva ya no es el de todas.
|
|
16739
|
+
label: options.label ?? (kind === "shell" ? this.shell?.name ?? "" : ""),
|
|
16431
16740
|
resumed,
|
|
16432
16741
|
createdAt: Date.now(),
|
|
16433
16742
|
// Lo pone `spawn`. Una entrada que no llega a tener proceso no queda en
|
|
@@ -16530,7 +16839,7 @@ var TerminalRegistry = class extends EventEmitter5 {
|
|
|
16530
16839
|
async assertDirectory(cwd) {
|
|
16531
16840
|
let info;
|
|
16532
16841
|
try {
|
|
16533
|
-
info = await
|
|
16842
|
+
info = await stat23(cwd);
|
|
16534
16843
|
} catch {
|
|
16535
16844
|
throw new TerminalOpenError("invalid-cwd", serverText("cwdMissing", { cwd }));
|
|
16536
16845
|
}
|
|
@@ -16957,19 +17266,19 @@ import { WebSocketServer } from "ws";
|
|
|
16957
17266
|
|
|
16958
17267
|
// packages/server/src/directory-picker.ts
|
|
16959
17268
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
16960
|
-
import { mkdir as mkdir14, readdir as readdir11, stat as
|
|
17269
|
+
import { mkdir as mkdir14, readdir as readdir11, stat as stat24 } from "node:fs/promises";
|
|
16961
17270
|
import { homedir as homedir11 } from "node:os";
|
|
16962
|
-
import
|
|
17271
|
+
import path44 from "node:path";
|
|
16963
17272
|
var MAX_ENTRIES = 500;
|
|
16964
17273
|
var MAX_PICKERS = 8;
|
|
16965
17274
|
var SKIPPED = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "bin", "obj", "$RECYCLE.BIN"]);
|
|
16966
17275
|
var DirectoryPickerError = class extends ServerTextError {
|
|
16967
17276
|
};
|
|
16968
17277
|
function isInsideProtected(target, dirs) {
|
|
16969
|
-
const resolved =
|
|
17278
|
+
const resolved = path44.resolve(target);
|
|
16970
17279
|
return dirs.some((dir) => {
|
|
16971
|
-
const protectedDir =
|
|
16972
|
-
return resolved === protectedDir || resolved.startsWith(protectedDir +
|
|
17280
|
+
const protectedDir = path44.resolve(dir);
|
|
17281
|
+
return resolved === protectedDir || resolved.startsWith(protectedDir + path44.sep);
|
|
16973
17282
|
});
|
|
16974
17283
|
}
|
|
16975
17284
|
async function listRoots() {
|
|
@@ -16981,7 +17290,7 @@ async function listRoots() {
|
|
|
16981
17290
|
letters.map(async (letter) => {
|
|
16982
17291
|
const drive = `${letter}:\\`;
|
|
16983
17292
|
try {
|
|
16984
|
-
await
|
|
17293
|
+
await stat24(drive);
|
|
16985
17294
|
return drive;
|
|
16986
17295
|
} catch {
|
|
16987
17296
|
return null;
|
|
@@ -17033,14 +17342,14 @@ var DirectoryPickers = class {
|
|
|
17033
17342
|
async enter(pickerId, name) {
|
|
17034
17343
|
const picker = this.require(pickerId);
|
|
17035
17344
|
if (name === "..") {
|
|
17036
|
-
const parent =
|
|
17345
|
+
const parent = path44.dirname(picker.current);
|
|
17037
17346
|
if (parent !== picker.current) picker.current = parent;
|
|
17038
17347
|
return this.listing(picker);
|
|
17039
17348
|
}
|
|
17040
17349
|
if (!picker.entries.has(name)) {
|
|
17041
17350
|
throw new DirectoryPickerError(serverText("pickerNotListed"));
|
|
17042
17351
|
}
|
|
17043
|
-
const target =
|
|
17352
|
+
const target = path44.join(picker.current, name);
|
|
17044
17353
|
if (isInsideProtected(target, this.protectedDirs)) {
|
|
17045
17354
|
throw new DirectoryPickerError(serverText("pickerCliFolder"));
|
|
17046
17355
|
}
|
|
@@ -17080,7 +17389,7 @@ var DirectoryPickers = class {
|
|
|
17080
17389
|
throw new DirectoryPickerError(serverText("pickerNameChars"));
|
|
17081
17390
|
}
|
|
17082
17391
|
}
|
|
17083
|
-
const target =
|
|
17392
|
+
const target = path44.join(picker.current, clean);
|
|
17084
17393
|
if (isInsideProtected(target, this.protectedDirs)) {
|
|
17085
17394
|
throw new DirectoryPickerError(serverText("pickerNoProjectsHere"));
|
|
17086
17395
|
}
|
|
@@ -17117,7 +17426,7 @@ var DirectoryPickers = class {
|
|
|
17117
17426
|
let truncated = false;
|
|
17118
17427
|
try {
|
|
17119
17428
|
const found = await readdir11(picker.current, { withFileTypes: true });
|
|
17120
|
-
const dirs = found.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !SKIPPED.has(name)).filter((name) => !isInsideProtected(
|
|
17429
|
+
const dirs = found.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !SKIPPED.has(name)).filter((name) => !isInsideProtected(path44.join(picker.current, name), this.protectedDirs)).sort((a, b) => a.localeCompare(b));
|
|
17121
17430
|
truncated = dirs.length > MAX_ENTRIES;
|
|
17122
17431
|
names = dirs.slice(0, MAX_ENTRIES);
|
|
17123
17432
|
} catch {
|
|
@@ -17128,15 +17437,15 @@ var DirectoryPickers = class {
|
|
|
17128
17437
|
path: picker.current,
|
|
17129
17438
|
entries: names,
|
|
17130
17439
|
roots,
|
|
17131
|
-
canGoUp:
|
|
17440
|
+
canGoUp: path44.dirname(picker.current) !== picker.current,
|
|
17132
17441
|
truncated
|
|
17133
17442
|
};
|
|
17134
17443
|
}
|
|
17135
17444
|
};
|
|
17136
17445
|
|
|
17137
17446
|
// packages/server/src/file-browser.ts
|
|
17138
|
-
import { readFile as
|
|
17139
|
-
import
|
|
17447
|
+
import { readFile as readFile18, readdir as readdir12, stat as stat25 } from "node:fs/promises";
|
|
17448
|
+
import path45 from "node:path";
|
|
17140
17449
|
var MAX_ENTRIES2 = 1e3;
|
|
17141
17450
|
var SEARCH_MAX_RESULTS = 200;
|
|
17142
17451
|
var SEARCH_MAX_DIRECTORIES = 4e3;
|
|
@@ -17199,7 +17508,7 @@ function languageFor(fileName) {
|
|
|
17199
17508
|
const lower = fileName.toLowerCase();
|
|
17200
17509
|
const byName = LANGUAGE_BY_NAME[lower];
|
|
17201
17510
|
if (byName !== void 0) return byName;
|
|
17202
|
-
const extension =
|
|
17511
|
+
const extension = path45.extname(lower);
|
|
17203
17512
|
return LANGUAGE_BY_EXTENSION[extension] ?? null;
|
|
17204
17513
|
}
|
|
17205
17514
|
function looksBinary2(buffer) {
|
|
@@ -17231,7 +17540,7 @@ async function listDirectory(cwd, relativePath, options = {}) {
|
|
|
17231
17540
|
let sizeBytes = 0;
|
|
17232
17541
|
let modifiedAt = 0;
|
|
17233
17542
|
try {
|
|
17234
|
-
const info = await
|
|
17543
|
+
const info = await stat25(path45.join(absolute, candidate.name));
|
|
17235
17544
|
sizeBytes = candidate.isDirectory ? 0 : info.size;
|
|
17236
17545
|
modifiedAt = info.mtimeMs;
|
|
17237
17546
|
} catch {
|
|
@@ -17264,7 +17573,7 @@ async function readLevel(absolute) {
|
|
|
17264
17573
|
const always = ALWAYS_SKIP.has(dirent.name.toLowerCase());
|
|
17265
17574
|
if (dirent.isSymbolicLink()) {
|
|
17266
17575
|
try {
|
|
17267
|
-
const target = await
|
|
17576
|
+
const target = await stat25(path45.join(absolute, dirent.name));
|
|
17268
17577
|
candidates.push({
|
|
17269
17578
|
name: dirent.name,
|
|
17270
17579
|
isDirectory: target.isDirectory(),
|
|
@@ -17293,7 +17602,7 @@ async function markIgnored(repoRoot2, levels) {
|
|
|
17293
17602
|
for (const level of levels) {
|
|
17294
17603
|
for (const entry of level.entries) {
|
|
17295
17604
|
if (entry.hidden !== null) continue;
|
|
17296
|
-
keys.set(entry, toPosixPath(
|
|
17605
|
+
keys.set(entry, toPosixPath(path45.relative(repoRoot2, path45.join(level.absolute, entry.name))));
|
|
17297
17606
|
}
|
|
17298
17607
|
}
|
|
17299
17608
|
if (keys.size === 0) return;
|
|
@@ -17354,7 +17663,7 @@ async function searchFiles(cwd, query, options = {}) {
|
|
|
17354
17663
|
let sizeBytes = 0;
|
|
17355
17664
|
let modifiedAt = 0;
|
|
17356
17665
|
try {
|
|
17357
|
-
const info = await
|
|
17666
|
+
const info = await stat25(path45.join(level.absolute, candidate.name));
|
|
17358
17667
|
sizeBytes = candidate.isDirectory ? 0 : info.size;
|
|
17359
17668
|
modifiedAt = info.mtimeMs;
|
|
17360
17669
|
} catch {
|
|
@@ -17388,12 +17697,12 @@ async function searchFiles(cwd, query, options = {}) {
|
|
|
17388
17697
|
}
|
|
17389
17698
|
async function readPreview(cwd, relativePath) {
|
|
17390
17699
|
const absolute = await resolveInside(cwd, relativePath, { mustExist: true });
|
|
17391
|
-
const info = await
|
|
17700
|
+
const info = await stat25(absolute);
|
|
17392
17701
|
if (info.isDirectory()) {
|
|
17393
17702
|
throw new Error("It's a directory, not a file.");
|
|
17394
17703
|
}
|
|
17395
|
-
const content = await
|
|
17396
|
-
const fileName =
|
|
17704
|
+
const content = await readFile18(absolute);
|
|
17705
|
+
const fileName = path45.basename(absolute);
|
|
17397
17706
|
if (looksBinary2(content)) {
|
|
17398
17707
|
return {
|
|
17399
17708
|
path: relativePath,
|
|
@@ -17982,14 +18291,14 @@ var TerminalWriteQueue = class {
|
|
|
17982
18291
|
};
|
|
17983
18292
|
|
|
17984
18293
|
// packages/server/src/vault/service.ts
|
|
17985
|
-
import { mkdir as mkdir17, stat as
|
|
18294
|
+
import { mkdir as mkdir17, stat as stat29 } from "node:fs/promises";
|
|
17986
18295
|
import { homedir as homedir12 } from "node:os";
|
|
17987
|
-
import
|
|
18296
|
+
import path49 from "node:path";
|
|
17988
18297
|
|
|
17989
18298
|
// packages/server/src/vault/move.ts
|
|
17990
18299
|
import { constants } from "node:fs";
|
|
17991
|
-
import { copyFile as copyFile2, lstat, mkdir as mkdir15, readdir as readdir13, rm as rm5, stat as
|
|
17992
|
-
import
|
|
18300
|
+
import { copyFile as copyFile2, lstat, mkdir as mkdir15, readdir as readdir13, rm as rm5, stat as stat26, utimes, writeFile as writeFile9 } from "node:fs/promises";
|
|
18301
|
+
import path46 from "node:path";
|
|
17993
18302
|
var MOVED_ENTRIES = ["vault.json", "sessions", "memory"];
|
|
17994
18303
|
var OWN_TEMP_FILE = /\.\d+\.[0-9a-f]{12}\.tmp$/;
|
|
17995
18304
|
async function copyTree(source, target, result) {
|
|
@@ -18002,12 +18311,12 @@ async function copyTree(source, target, result) {
|
|
|
18002
18311
|
if (info.isDirectory()) {
|
|
18003
18312
|
await mkdir15(target, { recursive: true });
|
|
18004
18313
|
for (const name of await readdir13(source)) {
|
|
18005
|
-
await copyTree(
|
|
18314
|
+
await copyTree(path46.join(source, name), path46.join(target, name), result);
|
|
18006
18315
|
}
|
|
18007
18316
|
return;
|
|
18008
18317
|
}
|
|
18009
18318
|
if (!info.isFile() || OWN_TEMP_FILE.test(source)) return;
|
|
18010
|
-
await mkdir15(
|
|
18319
|
+
await mkdir15(path46.dirname(target), { recursive: true });
|
|
18011
18320
|
try {
|
|
18012
18321
|
await copyFile2(source, target, constants.COPYFILE_EXCL);
|
|
18013
18322
|
} catch (error) {
|
|
@@ -18024,7 +18333,7 @@ async function moveVault(from, to) {
|
|
|
18024
18333
|
const result = { copied: 0, skipped: 0 };
|
|
18025
18334
|
await mkdir15(to, { recursive: true });
|
|
18026
18335
|
for (const entry of MOVED_ENTRIES) {
|
|
18027
|
-
await copyTree(
|
|
18336
|
+
await copyTree(path46.join(from, entry), path46.join(to, entry), result);
|
|
18028
18337
|
}
|
|
18029
18338
|
return result;
|
|
18030
18339
|
}
|
|
@@ -18056,10 +18365,10 @@ function checkVaultTarget(current, target, options) {
|
|
|
18056
18365
|
return { kind: "ok" };
|
|
18057
18366
|
}
|
|
18058
18367
|
async function canWriteInto(dir) {
|
|
18059
|
-
const probe = temporaryPathFor(
|
|
18368
|
+
const probe = temporaryPathFor(path46.join(dir, ".escritura"));
|
|
18060
18369
|
try {
|
|
18061
18370
|
await mkdir15(dir, { recursive: true });
|
|
18062
|
-
if (!(await
|
|
18371
|
+
if (!(await stat26(dir)).isDirectory()) return false;
|
|
18063
18372
|
await writeFile9(probe, "");
|
|
18064
18373
|
return true;
|
|
18065
18374
|
} catch {
|
|
@@ -18162,12 +18471,12 @@ function serializeSession(input) {
|
|
|
18162
18471
|
}
|
|
18163
18472
|
|
|
18164
18473
|
// packages/server/src/vault/writer.ts
|
|
18165
|
-
import { readdir as readdir15, rm as rm6, stat as
|
|
18166
|
-
import
|
|
18474
|
+
import { readdir as readdir15, rm as rm6, stat as stat28 } from "node:fs/promises";
|
|
18475
|
+
import path48 from "node:path";
|
|
18167
18476
|
|
|
18168
18477
|
// packages/server/src/vault/memory-copy.ts
|
|
18169
|
-
import { mkdir as mkdir16, readdir as readdir14, readFile as
|
|
18170
|
-
import
|
|
18478
|
+
import { mkdir as mkdir16, readdir as readdir14, readFile as readFile19, stat as stat27, utimes as utimes2 } from "node:fs/promises";
|
|
18479
|
+
import path47 from "node:path";
|
|
18171
18480
|
var MEMORY_FILE_MAX_BYTES = 1024 * 1024;
|
|
18172
18481
|
var MEMORY_MAX_FILES = 500;
|
|
18173
18482
|
var MEMORY_SOURCE_FILE = "source.json";
|
|
@@ -18192,7 +18501,7 @@ async function listMemoryNotes(cwd) {
|
|
|
18192
18501
|
let absolute;
|
|
18193
18502
|
try {
|
|
18194
18503
|
absolute = await resolveInside(cwd, `${MEMORY_DIR}/${name}`, { mustExist: true });
|
|
18195
|
-
const info = await
|
|
18504
|
+
const info = await stat27(absolute);
|
|
18196
18505
|
if (!info.isFile() || info.size > MEMORY_FILE_MAX_BYTES) continue;
|
|
18197
18506
|
notes.push({ name, absolute, size: info.size, mtimeMs: info.mtimeMs });
|
|
18198
18507
|
} catch {
|
|
@@ -18206,7 +18515,7 @@ async function measureProjectMemory(cwd) {
|
|
|
18206
18515
|
}
|
|
18207
18516
|
async function isUpToDate(target, note) {
|
|
18208
18517
|
try {
|
|
18209
|
-
const info = await
|
|
18518
|
+
const info = await stat27(target);
|
|
18210
18519
|
return info.isFile() && info.size === note.size && Math.abs(info.mtimeMs - note.mtimeMs) < SAME_MTIME_MS;
|
|
18211
18520
|
} catch {
|
|
18212
18521
|
return false;
|
|
@@ -18214,7 +18523,7 @@ async function isUpToDate(target, note) {
|
|
|
18214
18523
|
}
|
|
18215
18524
|
async function exists3(file) {
|
|
18216
18525
|
try {
|
|
18217
|
-
await
|
|
18526
|
+
await stat27(file);
|
|
18218
18527
|
return true;
|
|
18219
18528
|
} catch {
|
|
18220
18529
|
return false;
|
|
@@ -18233,11 +18542,11 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
|
|
|
18233
18542
|
await mkdir16(target, { recursive: true });
|
|
18234
18543
|
};
|
|
18235
18544
|
for (const note of notes) {
|
|
18236
|
-
const destination =
|
|
18545
|
+
const destination = path47.join(target, note.name);
|
|
18237
18546
|
if (await isUpToDate(destination, note)) continue;
|
|
18238
18547
|
let content;
|
|
18239
18548
|
try {
|
|
18240
|
-
content = await
|
|
18549
|
+
content = await readFile19(note.absolute);
|
|
18241
18550
|
} catch {
|
|
18242
18551
|
continue;
|
|
18243
18552
|
}
|
|
@@ -18249,7 +18558,7 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
|
|
|
18249
18558
|
result.files += 1;
|
|
18250
18559
|
result.bytes += content.length;
|
|
18251
18560
|
}
|
|
18252
|
-
const source =
|
|
18561
|
+
const source = path47.join(target, MEMORY_SOURCE_FILE);
|
|
18253
18562
|
if (result.files > 0 || !await exists3(source)) {
|
|
18254
18563
|
await prepare();
|
|
18255
18564
|
await writeFileAtomic(source, `${JSON.stringify({ cwd, copiedAt: deps.now() })}
|
|
@@ -18743,7 +19052,7 @@ var VaultWriter = class {
|
|
|
18743
19052
|
}
|
|
18744
19053
|
async assetOnDisk(dir, agent, sessionId, asset, bytes) {
|
|
18745
19054
|
try {
|
|
18746
|
-
const info = await
|
|
19055
|
+
const info = await stat28(assetFile(dir, agent, sessionId, asset));
|
|
18747
19056
|
return info.isFile() && info.size === bytes;
|
|
18748
19057
|
} catch {
|
|
18749
19058
|
return false;
|
|
@@ -18843,17 +19152,17 @@ var VaultWriter = class {
|
|
|
18843
19152
|
const folders = [dir];
|
|
18844
19153
|
const subfolders = async (parent) => {
|
|
18845
19154
|
try {
|
|
18846
|
-
return (await readdir15(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
19155
|
+
return (await readdir15(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path48.join(parent, entry.name));
|
|
18847
19156
|
} catch {
|
|
18848
19157
|
return [];
|
|
18849
19158
|
}
|
|
18850
19159
|
};
|
|
18851
19160
|
for (const agentDir of await subfolders(sessionsRoot2(dir))) {
|
|
18852
|
-
if (!SESSION_AGENT_IDS.includes(
|
|
19161
|
+
if (!SESSION_AGENT_IDS.includes(path48.basename(agentDir))) continue;
|
|
18853
19162
|
folders.push(agentDir);
|
|
18854
19163
|
folders.push(...(await subfolders(agentDir)).filter((folder) => folder.endsWith(".assets")));
|
|
18855
19164
|
}
|
|
18856
|
-
folders.push(...await subfolders(
|
|
19165
|
+
folders.push(...await subfolders(path48.join(dir, "memory")));
|
|
18857
19166
|
for (const folder of folders) {
|
|
18858
19167
|
let names;
|
|
18859
19168
|
try {
|
|
@@ -18863,9 +19172,9 @@ var VaultWriter = class {
|
|
|
18863
19172
|
}
|
|
18864
19173
|
for (const name of names) {
|
|
18865
19174
|
if (!OWN_TEMP_FILE2.test(name)) continue;
|
|
18866
|
-
const file =
|
|
19175
|
+
const file = path48.join(folder, name);
|
|
18867
19176
|
try {
|
|
18868
|
-
const info = await
|
|
19177
|
+
const info = await stat28(file);
|
|
18869
19178
|
if (info.isFile() && now - info.mtimeMs > TEMP_MAX_AGE_MS) await rm6(file, { force: true });
|
|
18870
19179
|
} catch {
|
|
18871
19180
|
}
|
|
@@ -19053,14 +19362,14 @@ var VaultService = class {
|
|
|
19053
19362
|
async setDir(target) {
|
|
19054
19363
|
const { platform, agents, settings: settings2, catalog } = this.options;
|
|
19055
19364
|
const current = this.dir();
|
|
19056
|
-
const protectedDirs = [...agents.protectedDirs(),
|
|
19365
|
+
const protectedDirs = [...agents.protectedDirs(), path49.join(this.options.homeDir ?? homedir12(), ".gemini")];
|
|
19057
19366
|
const check = checkVaultTarget(current, target, { protectedDirs, platform });
|
|
19058
19367
|
if (check.kind === "refused") throw new VaultError(check.text);
|
|
19059
19368
|
if (check.kind === "same") return;
|
|
19060
19369
|
try {
|
|
19061
19370
|
await this.writer.exclusive("moving", async () => {
|
|
19062
19371
|
if (!await canWriteInto(target)) throw new VaultError(serverText("vaultCannotWrite"));
|
|
19063
|
-
const hadCopy = await
|
|
19372
|
+
const hadCopy = await stat29(vaultMarkerFile(current)).then(
|
|
19064
19373
|
() => true,
|
|
19065
19374
|
() => false
|
|
19066
19375
|
);
|
|
@@ -19094,7 +19403,7 @@ var VaultService = class {
|
|
|
19094
19403
|
/** Abre la carpeta de la copia con el explorador del sistema. */
|
|
19095
19404
|
async reveal() {
|
|
19096
19405
|
const dir = this.activeDir();
|
|
19097
|
-
const isFolder = await
|
|
19406
|
+
const isFolder = await stat29(dir).then(
|
|
19098
19407
|
(info) => info.isDirectory(),
|
|
19099
19408
|
() => false
|
|
19100
19409
|
);
|
|
@@ -19118,7 +19427,7 @@ var VaultService = class {
|
|
|
19118
19427
|
fromCopy: true
|
|
19119
19428
|
});
|
|
19120
19429
|
const file = sessionExportFile(dir, agent, sessionId);
|
|
19121
|
-
await mkdir17(
|
|
19430
|
+
await mkdir17(path49.dirname(file), { recursive: true });
|
|
19122
19431
|
await writeFileAtomic(file, text, this.writeDeps);
|
|
19123
19432
|
this.openWithSystem(file);
|
|
19124
19433
|
return file;
|
|
@@ -19222,7 +19531,7 @@ var VaultService = class {
|
|
|
19222
19531
|
continue;
|
|
19223
19532
|
}
|
|
19224
19533
|
const fileName = this.exportFileName(summary, names);
|
|
19225
|
-
await writeFileAtomic(
|
|
19534
|
+
await writeFileAtomic(path49.join(folder, fileName), text, this.writeDeps);
|
|
19226
19535
|
entries.push({
|
|
19227
19536
|
updatedAt: summary.updatedAt,
|
|
19228
19537
|
agentLabel: this.labelOf(summary.agent),
|
|
@@ -19233,9 +19542,9 @@ var VaultService = class {
|
|
|
19233
19542
|
});
|
|
19234
19543
|
await nextTurn3();
|
|
19235
19544
|
}
|
|
19236
|
-
const name = project.cwd.length > 0 ?
|
|
19545
|
+
const name = project.cwd.length > 0 ? path49.basename(project.cwd) || project.cwd : project.fallbackName;
|
|
19237
19546
|
const index = renderProjectIndex({ name, cwd: project.cwd, exportedAt: this.now(), entries, skipped });
|
|
19238
|
-
await writeFileAtomic(
|
|
19547
|
+
await writeFileAtomic(path49.join(folder, "index.md"), index, this.writeDeps);
|
|
19239
19548
|
this.openWithSystem(folder);
|
|
19240
19549
|
return { sessions: entries.length, skipped, folder };
|
|
19241
19550
|
}
|
|
@@ -19386,7 +19695,7 @@ function attachTerminalSocket(options) {
|
|
|
19386
19695
|
httpServer,
|
|
19387
19696
|
port,
|
|
19388
19697
|
token,
|
|
19389
|
-
|
|
19698
|
+
appSettings,
|
|
19390
19699
|
registry,
|
|
19391
19700
|
index,
|
|
19392
19701
|
archived,
|
|
@@ -19597,6 +19906,8 @@ function attachTerminalSocket(options) {
|
|
|
19597
19906
|
const stopRemoteStatus = remote.onChange(
|
|
19598
19907
|
() => broadcastToHost({ type: "remote.status", status: remote.status() })
|
|
19599
19908
|
);
|
|
19909
|
+
const onAppSettingsStatus = (settings2) => broadcast({ type: "settings.status", settings: settings2 });
|
|
19910
|
+
appSettings.on("status", onAppSettingsStatus);
|
|
19600
19911
|
const stopRemoteRevoked = remote.onRevoked((deviceId) => {
|
|
19601
19912
|
for (const [socket, id] of remoteSockets) {
|
|
19602
19913
|
if (deviceId === null || id === deviceId) socket.close(REMOTE_REVOKED_CLOSE_CODE, "revoked");
|
|
@@ -19709,7 +20020,7 @@ function attachTerminalSocket(options) {
|
|
|
19709
20020
|
defaultAgent: agents.defaultAgent(),
|
|
19710
20021
|
platform: process.platform,
|
|
19711
20022
|
defaultCwd,
|
|
19712
|
-
shellName:
|
|
20023
|
+
shellName: appSettings.currentShell()?.name ?? null,
|
|
19713
20024
|
remoteClient: deviceId !== null
|
|
19714
20025
|
});
|
|
19715
20026
|
send(socket, terminalListMessage());
|
|
@@ -19722,6 +20033,7 @@ function attachTerminalSocket(options) {
|
|
|
19722
20033
|
send(socket, { type: "composer.drafts", drafts: draftEntriesOf(registry.list()) });
|
|
19723
20034
|
send(socket, { type: "vault.status", status: vault.status() });
|
|
19724
20035
|
if (deviceId === null) send(socket, { type: "remote.status", status: remote.status() });
|
|
20036
|
+
send(socket, { type: "settings.status", settings: appSettings.status() });
|
|
19725
20037
|
socket.on("message", (raw) => {
|
|
19726
20038
|
const text = Array.isArray(raw) ? Buffer.concat(raw).toString("utf8") : Buffer.from(raw).toString("utf8");
|
|
19727
20039
|
const message = parseClientMessage(text);
|
|
@@ -20775,6 +21087,24 @@ function attachTerminalSocket(options) {
|
|
|
20775
21087
|
case "search.cancel":
|
|
20776
21088
|
globalSearch.cancel(message.searchId);
|
|
20777
21089
|
break;
|
|
21090
|
+
/*
|
|
21091
|
+
Ajustes (Hito 41, §6.32). Una consola que ya no esta, o un disco que no
|
|
21092
|
+
deja escribir, se dicen; lo demas lo valido el parser. A una ventana
|
|
21093
|
+
remota no llega: lo frena `remoteRefusal`.
|
|
21094
|
+
*/
|
|
21095
|
+
case "settings.update": {
|
|
21096
|
+
const { type: _type, ...change } = message;
|
|
21097
|
+
void appSettings.update(change).catch((error) => {
|
|
21098
|
+
if (error instanceof AppSettingsChangeError) {
|
|
21099
|
+
sendError(socket, "settings-failed", serverText("settingsShellMissing"));
|
|
21100
|
+
return;
|
|
21101
|
+
}
|
|
21102
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
21103
|
+
console.warn("[settings] a change failed:", detail);
|
|
21104
|
+
sendError(socket, "settings-failed", serverText("settingsFailed"), detail);
|
|
21105
|
+
});
|
|
21106
|
+
break;
|
|
21107
|
+
}
|
|
20778
21108
|
/*
|
|
20779
21109
|
El acceso remoto (hito 37). Hasta aca solo llega una ventana del
|
|
20780
21110
|
anfitrion: a un equipo remoto lo freno `remoteRefusal`. El estado
|
|
@@ -20875,6 +21205,7 @@ function attachTerminalSocket(options) {
|
|
|
20875
21205
|
memory.off("status", onMemoryStatus);
|
|
20876
21206
|
stopAgentChanges();
|
|
20877
21207
|
stopVaultStatus();
|
|
21208
|
+
appSettings.off("status", onAppSettingsStatus);
|
|
20878
21209
|
stopRemoteStatus();
|
|
20879
21210
|
stopRemoteRevoked();
|
|
20880
21211
|
for (const client of wss.clients) client.terminate();
|
|
@@ -20883,8 +21214,8 @@ function attachTerminalSocket(options) {
|
|
|
20883
21214
|
}
|
|
20884
21215
|
|
|
20885
21216
|
// packages/server/src/vault/catalog.ts
|
|
20886
|
-
import { open as open7, readdir as readdir16, stat as
|
|
20887
|
-
import
|
|
21217
|
+
import { open as open7, readdir as readdir16, stat as stat30 } from "node:fs/promises";
|
|
21218
|
+
import path50 from "node:path";
|
|
20888
21219
|
import { createInterface as createInterface2 } from "node:readline";
|
|
20889
21220
|
var HEADER_MAX_BYTES = 64 * 1024;
|
|
20890
21221
|
var LOAD_CONCURRENCY = 16;
|
|
@@ -20904,7 +21235,7 @@ async function assetBytesOf(dir, agent, sessionId) {
|
|
|
20904
21235
|
for (const name of names) {
|
|
20905
21236
|
if (!VAULT_ASSET_NAME_PATTERN.test(name)) continue;
|
|
20906
21237
|
try {
|
|
20907
|
-
const info = await
|
|
21238
|
+
const info = await stat30(path50.join(folder, name));
|
|
20908
21239
|
if (info.isFile()) total += info.size;
|
|
20909
21240
|
} catch {
|
|
20910
21241
|
}
|
|
@@ -20915,7 +21246,7 @@ async function readEntry(dir, agent, sessionId) {
|
|
|
20915
21246
|
const file = sessionFile(dir, agent, sessionId);
|
|
20916
21247
|
let sizeBytes;
|
|
20917
21248
|
try {
|
|
20918
|
-
const info = await
|
|
21249
|
+
const info = await stat30(file);
|
|
20919
21250
|
if (!info.isFile()) return { kind: "absent" };
|
|
20920
21251
|
sizeBytes = info.size;
|
|
20921
21252
|
} catch {
|
|
@@ -21124,15 +21455,15 @@ var VaultCatalog = class {
|
|
|
21124
21455
|
};
|
|
21125
21456
|
|
|
21126
21457
|
// packages/server/src/index.ts
|
|
21127
|
-
var serverDir =
|
|
21458
|
+
var serverDir = path51.dirname(fileURLToPath2(import.meta.url));
|
|
21128
21459
|
var isPackaged = true;
|
|
21129
|
-
var webRoot = isPackaged ? serverDir :
|
|
21130
|
-
var webDist = isPackaged ?
|
|
21131
|
-
var repoRoot =
|
|
21460
|
+
var webRoot = isPackaged ? serverDir : path51.resolve(serverDir, "../../web");
|
|
21461
|
+
var webDist = isPackaged ? path51.join(serverDir, "web") : path51.join(webRoot, "dist");
|
|
21462
|
+
var repoRoot = path51.resolve(serverDir, "../../..");
|
|
21132
21463
|
var isProduction = isPackaged || process.argv.includes("--prod");
|
|
21133
21464
|
function resolveDefaultCwd() {
|
|
21134
21465
|
const override = process.env["AGENT_WORKBENCH_CWD"];
|
|
21135
|
-
if (override !== void 0 && override.length > 0) return
|
|
21466
|
+
if (override !== void 0 && override.length > 0) return path51.resolve(override);
|
|
21136
21467
|
if (isPackaged) return process.cwd();
|
|
21137
21468
|
return repoRoot;
|
|
21138
21469
|
}
|
|
@@ -21207,7 +21538,7 @@ async function mountDevUi(app, httpServer) {
|
|
|
21207
21538
|
}
|
|
21208
21539
|
var LOOKS_LIKE_FILE = /\.[a-zA-Z0-9]{1,8}$/;
|
|
21209
21540
|
function mountBuiltUi(app) {
|
|
21210
|
-
const indexHtml =
|
|
21541
|
+
const indexHtml = path51.join(webDist, "index.html");
|
|
21211
21542
|
if (!existsSync4(indexHtml)) {
|
|
21212
21543
|
throw new Error(
|
|
21213
21544
|
`There's no compiled interface in ${webDist}.
|
|
@@ -21261,17 +21592,18 @@ ${line}`);
|
|
|
21261
21592
|
async function main() {
|
|
21262
21593
|
const token = createSessionToken();
|
|
21263
21594
|
const defaultCwd = resolveDefaultCwd();
|
|
21264
|
-
const agents = createAgentRegistry();
|
|
21265
|
-
const [, shell] = await Promise.all([agents.locateAll(), locateShell()]);
|
|
21266
21595
|
const movedFrom = await migrateLegacyConfigDir();
|
|
21267
21596
|
if (movedFrom !== null) console.log(`Configuration moved from ${movedFrom} to ${appConfigDir()}`);
|
|
21597
|
+
const settings2 = new SettingsStore();
|
|
21598
|
+
await settings2.load();
|
|
21599
|
+
const agents = createAgentRegistry(settings2.get().agents.disabled);
|
|
21600
|
+
const [, shells] = await Promise.all([agents.locateAll(), locateShells()]);
|
|
21601
|
+
const shell = pickShell(shells, settings2.get().console.shell);
|
|
21268
21602
|
await agents.prepareAll();
|
|
21269
21603
|
ensurePtyHelperExecutable();
|
|
21270
21604
|
const store = new WorkspaceStore();
|
|
21271
21605
|
const archived = new ArchivedSessions();
|
|
21272
21606
|
await archived.load();
|
|
21273
|
-
const settings2 = new SettingsStore();
|
|
21274
|
-
await settings2.load();
|
|
21275
21607
|
const catalog = new VaultCatalog();
|
|
21276
21608
|
await catalog.load(settings2.get().vault.dir ?? defaultVaultDir());
|
|
21277
21609
|
const index = new SessionIndex(agents, archived, catalog);
|
|
@@ -21283,6 +21615,12 @@ async function main() {
|
|
|
21283
21615
|
store,
|
|
21284
21616
|
(sessionId) => index.agentOf(sessionId)
|
|
21285
21617
|
);
|
|
21618
|
+
const appSettings = new AppSettingsService({
|
|
21619
|
+
settings: settings2,
|
|
21620
|
+
agents,
|
|
21621
|
+
shells,
|
|
21622
|
+
onShellChange: (next) => registry.setShell(next)
|
|
21623
|
+
});
|
|
21286
21624
|
const notes = new NotesStore();
|
|
21287
21625
|
await notes.load();
|
|
21288
21626
|
const drafts = new DraftStore();
|
|
@@ -21325,7 +21663,7 @@ async function main() {
|
|
|
21325
21663
|
httpServer,
|
|
21326
21664
|
port,
|
|
21327
21665
|
token,
|
|
21328
|
-
|
|
21666
|
+
appSettings,
|
|
21329
21667
|
registry,
|
|
21330
21668
|
index,
|
|
21331
21669
|
archived,
|