@ricsam/r5d-worker 0.0.11 → 0.0.13
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/dist/cjs/main.cjs +277 -224
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/main.mjs +276 -224
- package/dist/mjs/package.json +1 -1
- package/dist/types/main.d.ts +8 -0
- package/package.json +1 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -31,14 +31,14 @@ var main_exports = {};
|
|
|
31
31
|
__export(main_exports, {
|
|
32
32
|
ensureVisibleGitCheckout: () => ensureVisibleGitCheckout,
|
|
33
33
|
isArtifactEnvPath: () => isArtifactEnvPath,
|
|
34
|
+
prepareArtifactEnvForShell: () => prepareArtifactEnvForShell,
|
|
34
35
|
syncSessionArtifacts: () => syncSessionArtifacts
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(main_exports);
|
|
37
38
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
38
|
-
var import_node_os = __toESM(require("node:os"), 1);
|
|
39
39
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
40
40
|
var import_node_crypto = require("node:crypto");
|
|
41
|
-
var
|
|
41
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
42
42
|
var import_node_child_process = require("node:child_process");
|
|
43
43
|
var import_git_identity = require("./git-identity.cjs");
|
|
44
44
|
const import_meta = {};
|
|
@@ -47,6 +47,7 @@ const WORKER_PACKAGE_NAME = "@ricsam/r5d-worker";
|
|
|
47
47
|
const LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
|
|
48
48
|
const BRANCH_RE = /^[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9]$|^[A-Za-z0-9]$/;
|
|
49
49
|
const DEFAULT_READ_LIMIT = 2e3;
|
|
50
|
+
const DEFAULT_READ_MAX_BYTES = 5e4;
|
|
50
51
|
const MAX_LINE_LENGTH = 2e3;
|
|
51
52
|
const MAX_SYNC_DIFF_BYTES = 5 * 1024 * 1024;
|
|
52
53
|
const R5D_REMOTE_NAME = "r5d";
|
|
@@ -410,6 +411,29 @@ function rejectArtifactWritePath(filePath) {
|
|
|
410
411
|
throw new Error(`${R5D_ARTIFACTS_DIR_REF} attachments are read-only. Copy them into a project path before editing or creating files.`);
|
|
411
412
|
}
|
|
412
413
|
}
|
|
414
|
+
async function prepareArtifactEnvForShell(input) {
|
|
415
|
+
if (!input.sessionId) {
|
|
416
|
+
return {};
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
await syncSessionArtifacts({
|
|
420
|
+
baseUrl: input.baseUrl,
|
|
421
|
+
token: input.token,
|
|
422
|
+
projectId: input.projectId,
|
|
423
|
+
branchName: input.branchName,
|
|
424
|
+
sessionId: input.sessionId,
|
|
425
|
+
artifactRoot: input.artifactRoot
|
|
426
|
+
});
|
|
427
|
+
} catch (error) {
|
|
428
|
+
process.stderr.write(`[r5d-worker] artifact sync skipped for ${input.sessionId}: ${error instanceof Error ? error.message : String(error)}
|
|
429
|
+
`);
|
|
430
|
+
}
|
|
431
|
+
const artifactsDir = sessionArtifactDir(input.artifactRoot, input.sessionId);
|
|
432
|
+
import_node_fs.default.mkdirSync(artifactsDir, { recursive: true });
|
|
433
|
+
return {
|
|
434
|
+
R5D_ARTIFACTS_DIR: artifactsDir
|
|
435
|
+
};
|
|
436
|
+
}
|
|
413
437
|
async function verifyR5dctlAuth(baseUrl, token) {
|
|
414
438
|
const statusUrl = new URL("/api/r5dctl/auth/status", baseUrl);
|
|
415
439
|
let response;
|
|
@@ -921,16 +945,36 @@ function formatLineNumberedContent(content, offset, limit) {
|
|
|
921
945
|
const endLine = Math.min(startLine + readLimit - 1, totalLines);
|
|
922
946
|
const selectedLines = allLines.slice(startLine - 1, endLine);
|
|
923
947
|
const padWidth = String(endLine).length;
|
|
924
|
-
const formattedLines =
|
|
948
|
+
const formattedLines = [];
|
|
949
|
+
let actualEndLine = endLine;
|
|
950
|
+
let budgetTruncated = false;
|
|
951
|
+
for (let index = 0; index < selectedLines.length; index += 1) {
|
|
952
|
+
const line = selectedLines[index] ?? "";
|
|
925
953
|
const lineNumber = startLine + index;
|
|
926
954
|
const truncatedLine = line.length > MAX_LINE_LENGTH ? `${line.slice(0, MAX_LINE_LENGTH)}...` : line;
|
|
927
|
-
|
|
928
|
-
|
|
955
|
+
const formattedLine = `${String(lineNumber).padStart(padWidth, " ")}\u2192${truncatedLine}`;
|
|
956
|
+
const candidateLength = formattedLines.length === 0 ? formattedLine.length : formattedLine.length + 1;
|
|
957
|
+
const currentLength = formattedLines.join("\n").length;
|
|
958
|
+
if (currentLength + candidateLength > DEFAULT_READ_MAX_BYTES) {
|
|
959
|
+
budgetTruncated = true;
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
formattedLines.push(formattedLine);
|
|
963
|
+
actualEndLine = lineNumber;
|
|
964
|
+
}
|
|
965
|
+
let formattedContent = formattedLines.join("\n");
|
|
966
|
+
if (budgetTruncated) {
|
|
967
|
+
formattedContent = `${formattedContent}
|
|
968
|
+
... (truncated by byte budget)`;
|
|
969
|
+
}
|
|
970
|
+
const truncated = budgetTruncated || actualEndLine < totalLines;
|
|
929
971
|
return {
|
|
930
|
-
content:
|
|
972
|
+
content: formattedContent,
|
|
931
973
|
totalLines,
|
|
932
974
|
startLine,
|
|
933
|
-
endLine
|
|
975
|
+
endLine: actualEndLine,
|
|
976
|
+
truncated,
|
|
977
|
+
continuation: truncated ? `Call read with offset ${actualEndLine + 1} to continue.` : void 0
|
|
934
978
|
};
|
|
935
979
|
}
|
|
936
980
|
function readPngDimensions(bytes) {
|
|
@@ -997,6 +1041,27 @@ function readImageDimensions(bytes, mediaType) {
|
|
|
997
1041
|
}
|
|
998
1042
|
return dimensions;
|
|
999
1043
|
}
|
|
1044
|
+
const fileMutationQueues = /* @__PURE__ */ new Map();
|
|
1045
|
+
async function withFileMutationQueue(key, operation) {
|
|
1046
|
+
const previous = fileMutationQueues.get(key) ?? Promise.resolve();
|
|
1047
|
+
let release;
|
|
1048
|
+
const current = previous.catch(() => void 0).then(() => new Promise((resolve) => {
|
|
1049
|
+
release = resolve;
|
|
1050
|
+
}));
|
|
1051
|
+
fileMutationQueues.set(key, current);
|
|
1052
|
+
await previous.catch(() => void 0);
|
|
1053
|
+
try {
|
|
1054
|
+
return await operation();
|
|
1055
|
+
} finally {
|
|
1056
|
+
release();
|
|
1057
|
+
if (fileMutationQueues.get(key) === current) {
|
|
1058
|
+
fileMutationQueues.delete(key);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
function mutationQueueKey(projectId, branchName, filePath) {
|
|
1063
|
+
return `${projectId}:${branchName}:${import_node_path.default.posix.normalize(filePath)}`;
|
|
1064
|
+
}
|
|
1000
1065
|
async function executeReadFileOperation(input) {
|
|
1001
1066
|
const artifact = await resolveArtifactEnvFilePath({
|
|
1002
1067
|
filePath: input.message.filePath,
|
|
@@ -1014,7 +1079,8 @@ async function executeReadFileOperation(input) {
|
|
|
1014
1079
|
const buffer2 = import_node_fs.default.readFileSync(artifact.absolutePath);
|
|
1015
1080
|
const formatted2 = formatLineNumberedContent(buffer2.toString("utf8"), input.message.offset, input.message.limit);
|
|
1016
1081
|
return {
|
|
1017
|
-
type: "
|
|
1082
|
+
type: "read",
|
|
1083
|
+
kind: "text",
|
|
1018
1084
|
file: artifact.virtualPath,
|
|
1019
1085
|
gitBlobHash: getGitBlobHashForContent(buffer2),
|
|
1020
1086
|
...formatted2
|
|
@@ -1028,222 +1094,209 @@ async function executeReadFileOperation(input) {
|
|
|
1028
1094
|
const buffer = import_node_fs.default.readFileSync(resolved.absolutePath);
|
|
1029
1095
|
const formatted = formatLineNumberedContent(buffer.toString("utf8"), input.message.offset, input.message.limit);
|
|
1030
1096
|
return {
|
|
1031
|
-
type: "
|
|
1097
|
+
type: "read",
|
|
1098
|
+
kind: "text",
|
|
1032
1099
|
file: resolved.virtualPath,
|
|
1033
1100
|
gitBlobHash: getGitBlobHashForContent(buffer),
|
|
1034
1101
|
...formatted
|
|
1035
1102
|
};
|
|
1036
1103
|
}
|
|
1037
|
-
async function
|
|
1104
|
+
async function executeWriteFileOperation(input) {
|
|
1038
1105
|
rejectArtifactWritePath(input.message.filePath);
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
};
|
|
1106
|
+
return withFileMutationQueue(mutationQueueKey(input.projectId, input.message.branchName, input.message.filePath), async () => {
|
|
1107
|
+
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1108
|
+
const resolved = resolveProjectFilePath(workspace.branchPath, input.message.filePath);
|
|
1109
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(resolved.absolutePath), { recursive: true });
|
|
1110
|
+
import_node_fs.default.writeFileSync(resolved.absolutePath, input.message.content, "utf8");
|
|
1111
|
+
return {
|
|
1112
|
+
type: "write",
|
|
1113
|
+
file: resolved.virtualPath,
|
|
1114
|
+
gitBlobHash: getGitBlobHashForContent(input.message.content)
|
|
1115
|
+
};
|
|
1116
|
+
});
|
|
1051
1117
|
}
|
|
1052
1118
|
async function executeEditFileOperation(input) {
|
|
1053
1119
|
rejectArtifactWritePath(input.message.filePath);
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1120
|
+
return withFileMutationQueue(mutationQueueKey(input.projectId, input.message.branchName, input.message.filePath), async () => {
|
|
1121
|
+
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1122
|
+
const resolved = resolveProjectFilePath(workspace.branchPath, input.message.filePath);
|
|
1123
|
+
if (!Array.isArray(input.message.edits) || input.message.edits.length === 0) {
|
|
1124
|
+
throw new Error("edit requires at least one replacement");
|
|
1125
|
+
}
|
|
1126
|
+
if (!import_node_fs.default.existsSync(resolved.absolutePath) || !import_node_fs.default.statSync(resolved.absolutePath).isFile()) {
|
|
1127
|
+
throw new Error(`File not found: ${resolved.virtualPath}`);
|
|
1128
|
+
}
|
|
1129
|
+
const originalContent = import_node_fs.default.readFileSync(resolved.absolutePath, "utf8");
|
|
1130
|
+
const ranges = input.message.edits.map((edit, index) => {
|
|
1131
|
+
if (edit.oldText === edit.newText) {
|
|
1132
|
+
throw new Error(`edits[${index}].oldText and edits[${index}].newText must be different`);
|
|
1133
|
+
}
|
|
1134
|
+
if (edit.oldText.length === 0) {
|
|
1135
|
+
throw new Error(`edits[${index}].oldText cannot be empty`);
|
|
1136
|
+
}
|
|
1137
|
+
const occurrences = originalContent.split(edit.oldText).length - 1;
|
|
1138
|
+
if (occurrences === 0) {
|
|
1139
|
+
throw new Error(`Could not find edits[${index}].oldText: "${edit.oldText.slice(0, 100)}${edit.oldText.length > 100 ? "..." : ""}"`);
|
|
1140
|
+
}
|
|
1141
|
+
if (occurrences > 1) {
|
|
1142
|
+
throw new Error(`edits[${index}].oldText is not unique in the file (found ${occurrences} occurrences). Include more surrounding context.`);
|
|
1143
|
+
}
|
|
1144
|
+
const start = originalContent.indexOf(edit.oldText);
|
|
1145
|
+
return { index, start, end: start + edit.oldText.length, edit };
|
|
1146
|
+
});
|
|
1147
|
+
const sortedRanges = [...ranges].sort((a, b) => a.start - b.start);
|
|
1148
|
+
for (let index = 1; index < sortedRanges.length; index += 1) {
|
|
1149
|
+
const previous = sortedRanges[index - 1];
|
|
1150
|
+
const current = sortedRanges[index];
|
|
1151
|
+
if (current.start < previous.end) {
|
|
1152
|
+
throw new Error(`edits[${current.index}] overlaps edits[${previous.index}]. Merge nearby changes into one replacement.`);
|
|
1153
|
+
}
|
|
1074
1154
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1155
|
+
let nextContent = originalContent;
|
|
1156
|
+
for (const range of [...sortedRanges].reverse()) {
|
|
1157
|
+
nextContent = nextContent.slice(0, range.start) + range.edit.newText + nextContent.slice(range.end);
|
|
1158
|
+
}
|
|
1159
|
+
import_node_fs.default.writeFileSync(resolved.absolutePath, nextContent, "utf8");
|
|
1160
|
+
return {
|
|
1161
|
+
type: "edit",
|
|
1162
|
+
file: resolved.virtualPath,
|
|
1163
|
+
edits: input.message.edits.length
|
|
1164
|
+
};
|
|
1165
|
+
});
|
|
1082
1166
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
if (
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1167
|
+
const IGNORED_ENTRY_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".cache", "coverage", "target"]);
|
|
1168
|
+
function normalizeToolLimit(value, fallback, max) {
|
|
1169
|
+
if (!Number.isFinite(value) || value === void 0) return fallback;
|
|
1170
|
+
return Math.max(1, Math.min(max, Math.floor(value)));
|
|
1171
|
+
}
|
|
1172
|
+
function escapeRegex(value) {
|
|
1173
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1174
|
+
}
|
|
1175
|
+
function wildcardToRegex(pattern) {
|
|
1176
|
+
const escaped = escapeRegex(pattern).replace(/\\\*/g, ".*").replace(/\\\?/g, ".");
|
|
1177
|
+
return new RegExp(`^${escaped}$`);
|
|
1178
|
+
}
|
|
1179
|
+
function normalizeFindPattern(pattern) {
|
|
1180
|
+
return /[*?]/.test(pattern) ? pattern : `*${pattern}*`;
|
|
1181
|
+
}
|
|
1182
|
+
function toVirtualPath(branchPath, absolutePath) {
|
|
1183
|
+
const relative = import_node_path.default.relative(branchPath, absolutePath).split(import_node_path.default.sep).join(import_node_path.default.posix.sep);
|
|
1184
|
+
return relative ? `/${relative}` : "/";
|
|
1185
|
+
}
|
|
1186
|
+
function walkProjectEntries(branchPath, startPath) {
|
|
1187
|
+
const entries = [];
|
|
1188
|
+
const visit = (absolutePath) => {
|
|
1189
|
+
const stat = import_node_fs.default.statSync(absolutePath);
|
|
1190
|
+
const virtualPath = toVirtualPath(branchPath, absolutePath);
|
|
1191
|
+
entries.push({ absolutePath, virtualPath, stat });
|
|
1192
|
+
if (!stat.isDirectory()) return;
|
|
1193
|
+
const children = import_node_fs.default.readdirSync(absolutePath, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1194
|
+
for (const child of children) {
|
|
1195
|
+
if (IGNORED_ENTRY_NAMES.has(child.name)) continue;
|
|
1196
|
+
visit(import_node_path.default.join(absolutePath, child.name));
|
|
1090
1197
|
}
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1198
|
+
};
|
|
1199
|
+
visit(startPath);
|
|
1200
|
+
return entries;
|
|
1093
1201
|
}
|
|
1094
|
-
function
|
|
1095
|
-
|
|
1096
|
-
let patchPath = decodePatchPath(withoutMetadata);
|
|
1097
|
-
if (patchPath === "/dev/null") {
|
|
1098
|
-
return null;
|
|
1099
|
-
}
|
|
1100
|
-
if (patchPath.startsWith("a/") || patchPath.startsWith("b/")) {
|
|
1101
|
-
patchPath = patchPath.slice(2);
|
|
1102
|
-
}
|
|
1103
|
-
rejectArtifactWritePath(patchPath);
|
|
1104
|
-
if (!patchPath || import_node_path.default.isAbsolute(patchPath)) {
|
|
1105
|
-
throw new Error(`Invalid patch path: ${rawPath}`);
|
|
1106
|
-
}
|
|
1107
|
-
const normalized = import_node_path.default.posix.normalize(patchPath);
|
|
1108
|
-
if (normalized === "." || normalized.startsWith("../") || normalized === "..") {
|
|
1109
|
-
throw new Error(`Invalid patch path: ${rawPath}`);
|
|
1110
|
-
}
|
|
1111
|
-
return normalized;
|
|
1202
|
+
function isProbablyText(bytes) {
|
|
1203
|
+
return !bytes.subarray(0, Math.min(bytes.length, 4096)).includes(0);
|
|
1112
1204
|
}
|
|
1113
|
-
function
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1205
|
+
async function executeGrepOperation(input) {
|
|
1206
|
+
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1207
|
+
const start = resolveProjectFilePath(workspace.branchPath, input.message.path ?? "/");
|
|
1208
|
+
if (!import_node_fs.default.existsSync(start.absolutePath)) {
|
|
1209
|
+
throw new Error(`Path not found: ${start.virtualPath}`);
|
|
1210
|
+
}
|
|
1211
|
+
const limit = normalizeToolLimit(input.message.limit, 100, 1e3);
|
|
1212
|
+
const flags = input.message.caseSensitive === false ? "i" : "";
|
|
1213
|
+
const regex = new RegExp(input.message.pattern, flags);
|
|
1214
|
+
const globRegex = input.message.glob ? wildcardToRegex(input.message.glob) : null;
|
|
1215
|
+
const lines = [];
|
|
1216
|
+
let matchCount = 0;
|
|
1217
|
+
let truncated = false;
|
|
1218
|
+
for (const entry of walkProjectEntries(workspace.branchPath, start.absolutePath)) {
|
|
1219
|
+
if (!entry.stat.isFile()) continue;
|
|
1220
|
+
const repoRelative = entry.virtualPath.slice(1);
|
|
1221
|
+
if (globRegex && !globRegex.test(repoRelative)) continue;
|
|
1222
|
+
const bytes = import_node_fs.default.readFileSync(entry.absolutePath);
|
|
1223
|
+
if (!isProbablyText(bytes)) continue;
|
|
1224
|
+
const fileLines = bytes.toString("utf8").split(/\r?\n/);
|
|
1225
|
+
for (let index = 0; index < fileLines.length; index += 1) {
|
|
1226
|
+
const line = fileLines[index] ?? "";
|
|
1227
|
+
regex.lastIndex = 0;
|
|
1228
|
+
const match = regex.exec(line);
|
|
1229
|
+
if (!match) continue;
|
|
1230
|
+
matchCount += 1;
|
|
1231
|
+
if (lines.length < limit) {
|
|
1232
|
+
const column = Math.max(1, (match.index ?? 0) + 1);
|
|
1233
|
+
lines.push(`${entry.virtualPath}:${index + 1}:${column}: ${line.slice(0, MAX_LINE_LENGTH)}`);
|
|
1234
|
+
} else {
|
|
1235
|
+
truncated = true;
|
|
1236
|
+
}
|
|
1140
1237
|
}
|
|
1141
|
-
index += 1;
|
|
1142
|
-
}
|
|
1143
|
-
throw new Error("Unterminated quoted path in patch header");
|
|
1144
|
-
}
|
|
1145
|
-
function parseDiffGitPaths(line) {
|
|
1146
|
-
if (!line.startsWith("diff --git ")) {
|
|
1147
|
-
throw new Error(`Invalid patch header: ${line}`);
|
|
1148
|
-
}
|
|
1149
|
-
const rest = line.slice("diff --git ".length);
|
|
1150
|
-
const first = readDiffPathToken(rest, 0);
|
|
1151
|
-
const second = readDiffPathToken(rest, first.nextIndex);
|
|
1152
|
-
if (rest.slice(second.nextIndex).trim()) {
|
|
1153
|
-
throw new Error(`Invalid patch header: ${line}`);
|
|
1154
1238
|
}
|
|
1239
|
+
const hint = truncated ? `
|
|
1240
|
+
... truncated. Narrow path/glob or increase limit to continue.` : "";
|
|
1155
1241
|
return {
|
|
1156
|
-
|
|
1157
|
-
|
|
1242
|
+
type: "grep",
|
|
1243
|
+
content: lines.length > 0 ? `${lines.join("\n")}${hint}` : "No matches.",
|
|
1244
|
+
matchCount,
|
|
1245
|
+
truncated
|
|
1158
1246
|
};
|
|
1159
1247
|
}
|
|
1160
|
-
function
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1186
|
-
if (line.startsWith("new file mode ")) {
|
|
1187
|
-
current.operation = "add";
|
|
1188
|
-
continue;
|
|
1189
|
-
}
|
|
1190
|
-
if (line.startsWith("deleted file mode ")) {
|
|
1191
|
-
current.operation = "delete";
|
|
1192
|
-
if (current.oldPath) {
|
|
1193
|
-
current.path = current.oldPath;
|
|
1194
|
-
delete current.oldPath;
|
|
1195
|
-
}
|
|
1196
|
-
continue;
|
|
1197
|
-
}
|
|
1198
|
-
if (line.startsWith("rename from ")) {
|
|
1199
|
-
current.operation = "move";
|
|
1200
|
-
current.oldPath = normalizePatchPath(line.slice("rename from ".length)) ?? void 0;
|
|
1201
|
-
continue;
|
|
1202
|
-
}
|
|
1203
|
-
if (line.startsWith("rename to ")) {
|
|
1204
|
-
current.operation = "move";
|
|
1205
|
-
current.path = normalizePatchPath(line.slice("rename to ".length)) ?? current.path;
|
|
1206
|
-
continue;
|
|
1207
|
-
}
|
|
1208
|
-
if (line.startsWith("@@")) {
|
|
1209
|
-
current.hunks += 1;
|
|
1210
|
-
continue;
|
|
1211
|
-
}
|
|
1212
|
-
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
1213
|
-
current.additions += 1;
|
|
1214
|
-
continue;
|
|
1215
|
-
}
|
|
1216
|
-
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
1217
|
-
current.deletions += 1;
|
|
1248
|
+
async function executeFindOperation(input) {
|
|
1249
|
+
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1250
|
+
const start = resolveProjectFilePath(workspace.branchPath, input.message.path ?? "/");
|
|
1251
|
+
if (!import_node_fs.default.existsSync(start.absolutePath) || !import_node_fs.default.statSync(start.absolutePath).isDirectory()) {
|
|
1252
|
+
throw new Error(`Directory not found: ${start.virtualPath}`);
|
|
1253
|
+
}
|
|
1254
|
+
const pattern = normalizeFindPattern(input.message.pattern ?? "*");
|
|
1255
|
+
const matcher = wildcardToRegex(pattern);
|
|
1256
|
+
const entryType = input.message.entryType ?? "file";
|
|
1257
|
+
const limit = normalizeToolLimit(input.message.limit, 200, 1e3);
|
|
1258
|
+
const matches = [];
|
|
1259
|
+
let count = 0;
|
|
1260
|
+
let truncated = false;
|
|
1261
|
+
for (const entry of walkProjectEntries(workspace.branchPath, start.absolutePath)) {
|
|
1262
|
+
if (entry.virtualPath === start.virtualPath) continue;
|
|
1263
|
+
const isDirectory = entry.stat.isDirectory();
|
|
1264
|
+
if (entryType === "file" && !entry.stat.isFile()) continue;
|
|
1265
|
+
if (entryType === "directory" && !isDirectory) continue;
|
|
1266
|
+
const label = isDirectory ? `${entry.virtualPath}/` : entry.virtualPath;
|
|
1267
|
+
if (!matcher.test(import_node_path.default.posix.basename(entry.virtualPath)) && !matcher.test(entry.virtualPath.slice(1))) continue;
|
|
1268
|
+
count += 1;
|
|
1269
|
+
if (matches.length < limit) {
|
|
1270
|
+
matches.push(label);
|
|
1271
|
+
} else {
|
|
1272
|
+
truncated = true;
|
|
1218
1273
|
}
|
|
1219
1274
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1275
|
+
const hint = truncated ? "\n... truncated. Narrow path/pattern or increase limit to continue." : "";
|
|
1276
|
+
return {
|
|
1277
|
+
type: "find",
|
|
1278
|
+
content: matches.length > 0 ? `${matches.join("\n")}${hint}` : "No paths found.",
|
|
1279
|
+
count,
|
|
1280
|
+
truncated
|
|
1281
|
+
};
|
|
1224
1282
|
}
|
|
1225
|
-
async function
|
|
1283
|
+
async function executeLsOperation(input) {
|
|
1226
1284
|
const workspace = ensureOperationBranch({ ...input, branchName: input.message.branchName });
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
import_node_fs.default.rmSync(patchPath, { force: true });
|
|
1237
|
-
}
|
|
1285
|
+
const resolved = resolveProjectFilePath(workspace.branchPath, input.message.path ?? "/");
|
|
1286
|
+
if (!import_node_fs.default.existsSync(resolved.absolutePath) || !import_node_fs.default.statSync(resolved.absolutePath).isDirectory()) {
|
|
1287
|
+
throw new Error(`Directory not found: ${resolved.virtualPath}`);
|
|
1288
|
+
}
|
|
1289
|
+
const limit = normalizeToolLimit(input.message.limit, 200, 1e3);
|
|
1290
|
+
const entries = import_node_fs.default.readdirSync(resolved.absolutePath, { withFileTypes: true }).filter((entry) => !IGNORED_ENTRY_NAMES.has(entry.name)).sort((a, b) => Number(b.isDirectory()) - Number(a.isDirectory()) || a.name.localeCompare(b.name));
|
|
1291
|
+
const displayed = entries.slice(0, limit).map((entry) => `${entry.name}${entry.isDirectory() ? "/" : ""}`);
|
|
1292
|
+
const truncated = entries.length > displayed.length;
|
|
1293
|
+
const hint = truncated ? "\n... truncated. Increase limit to continue." : "";
|
|
1238
1294
|
return {
|
|
1239
|
-
type: "
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
deletions: files.reduce((sum, file) => sum + file.deletions, 0),
|
|
1245
|
-
hunks: files.reduce((sum, file) => sum + file.hunks, 0)
|
|
1246
|
-
}
|
|
1295
|
+
type: "ls",
|
|
1296
|
+
path: resolved.virtualPath,
|
|
1297
|
+
content: displayed.length > 0 ? `${displayed.join("\n")}${hint}` : "(empty directory)",
|
|
1298
|
+
count: entries.length,
|
|
1299
|
+
truncated
|
|
1247
1300
|
};
|
|
1248
1301
|
}
|
|
1249
1302
|
async function executeViewFileBytesOperation(input) {
|
|
@@ -1263,7 +1316,7 @@ async function executeViewFileBytesOperation(input) {
|
|
|
1263
1316
|
const extension2 = import_node_path.default.extname(artifact.absolutePath).toLowerCase();
|
|
1264
1317
|
const mediaType2 = extension2 === ".jpg" || extension2 === ".jpeg" ? "image/jpeg" : extension2 === ".png" ? "image/png" : null;
|
|
1265
1318
|
if (!mediaType2) {
|
|
1266
|
-
throw new Error("
|
|
1319
|
+
throw new Error("read supports PNG and JPEG image inputs only");
|
|
1267
1320
|
}
|
|
1268
1321
|
const bytes2 = import_node_fs.default.readFileSync(artifact.absolutePath);
|
|
1269
1322
|
const dimensions2 = readImageDimensions(bytes2, mediaType2);
|
|
@@ -1285,7 +1338,7 @@ async function executeViewFileBytesOperation(input) {
|
|
|
1285
1338
|
const extension = import_node_path.default.extname(resolved.absolutePath).toLowerCase();
|
|
1286
1339
|
const mediaType = extension === ".jpg" || extension === ".jpeg" ? "image/jpeg" : extension === ".png" ? "image/png" : null;
|
|
1287
1340
|
if (!mediaType) {
|
|
1288
|
-
throw new Error("
|
|
1341
|
+
throw new Error("read supports PNG and JPEG image inputs only");
|
|
1289
1342
|
}
|
|
1290
1343
|
const bytes = import_node_fs.default.readFileSync(resolved.absolutePath);
|
|
1291
1344
|
const dimensions = readImageDimensions(bytes, mediaType);
|
|
@@ -1391,14 +1444,18 @@ function pullBranch(input) {
|
|
|
1391
1444
|
}
|
|
1392
1445
|
async function executeOperation(input) {
|
|
1393
1446
|
switch (input.message.type) {
|
|
1394
|
-
case "
|
|
1447
|
+
case "read":
|
|
1395
1448
|
return executeReadFileOperation({ ...input, message: input.message });
|
|
1396
|
-
case "
|
|
1397
|
-
return
|
|
1398
|
-
case "
|
|
1449
|
+
case "write":
|
|
1450
|
+
return executeWriteFileOperation({ ...input, message: input.message });
|
|
1451
|
+
case "edit":
|
|
1399
1452
|
return executeEditFileOperation({ ...input, message: input.message });
|
|
1400
|
-
case "
|
|
1401
|
-
return
|
|
1453
|
+
case "grep":
|
|
1454
|
+
return executeGrepOperation({ ...input, message: input.message });
|
|
1455
|
+
case "find":
|
|
1456
|
+
return executeFindOperation({ ...input, message: input.message });
|
|
1457
|
+
case "ls":
|
|
1458
|
+
return executeLsOperation({ ...input, message: input.message });
|
|
1402
1459
|
case "view_file_bytes":
|
|
1403
1460
|
return executeViewFileBytesOperation({ ...input, message: input.message });
|
|
1404
1461
|
case "sync":
|
|
@@ -1453,18 +1510,14 @@ async function executeCommand(input) {
|
|
|
1453
1510
|
branchName: input.message.branchName,
|
|
1454
1511
|
manifest: input.manifest
|
|
1455
1512
|
});
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
artifactRoot: input.artifactRoot
|
|
1465
|
-
});
|
|
1466
|
-
artifactProcessEnv = artifactEnv(input.artifactRoot, input.message.sessionId);
|
|
1467
|
-
}
|
|
1513
|
+
const artifactProcessEnv = await prepareArtifactEnvForShell({
|
|
1514
|
+
baseUrl: input.baseUrl,
|
|
1515
|
+
token: input.token,
|
|
1516
|
+
projectId: input.projectId,
|
|
1517
|
+
branchName: input.message.branchName,
|
|
1518
|
+
sessionId: input.message.sessionId,
|
|
1519
|
+
artifactRoot: input.artifactRoot
|
|
1520
|
+
});
|
|
1468
1521
|
const cwd = resolveCommandCwd(workspace.branchPath, input.message.cwd);
|
|
1469
1522
|
const subprocess = Bun.spawn(input.message.argv, {
|
|
1470
1523
|
cwd,
|
|
@@ -1538,7 +1591,7 @@ async function executeStreamingCommand(input) {
|
|
|
1538
1591
|
branchName: input.message.branchName,
|
|
1539
1592
|
manifest: input.manifest
|
|
1540
1593
|
});
|
|
1541
|
-
await
|
|
1594
|
+
const artifactProcessEnv = await prepareArtifactEnvForShell({
|
|
1542
1595
|
baseUrl: input.baseUrl,
|
|
1543
1596
|
token: input.token,
|
|
1544
1597
|
projectId: input.projectId,
|
|
@@ -1546,7 +1599,6 @@ async function executeStreamingCommand(input) {
|
|
|
1546
1599
|
sessionId: input.message.sessionId,
|
|
1547
1600
|
artifactRoot: input.artifactRoot
|
|
1548
1601
|
});
|
|
1549
|
-
const artifactProcessEnv = artifactEnv(input.artifactRoot, input.message.sessionId);
|
|
1550
1602
|
const cwd = resolveCommandCwd(workspace.branchPath, input.message.cwd);
|
|
1551
1603
|
const subprocess = Bun.spawn(input.message.argv, {
|
|
1552
1604
|
cwd,
|
|
@@ -1958,7 +2010,7 @@ async function startWorker(options) {
|
|
|
1958
2010
|
const hello = {
|
|
1959
2011
|
type: "hello",
|
|
1960
2012
|
hostInfo: {
|
|
1961
|
-
hostname: (0,
|
|
2013
|
+
hostname: (0, import_node_os.hostname)(),
|
|
1962
2014
|
platform: process.platform,
|
|
1963
2015
|
arch: process.arch,
|
|
1964
2016
|
pid: process.pid,
|
|
@@ -2099,7 +2151,7 @@ async function startWorker(options) {
|
|
|
2099
2151
|
}
|
|
2100
2152
|
return;
|
|
2101
2153
|
}
|
|
2102
|
-
if (message.type === "
|
|
2154
|
+
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "sync" || message.type === "confirm_large_diff" || message.type === "pull_branch") {
|
|
2103
2155
|
try {
|
|
2104
2156
|
const manifest = manifestByProjectId.get(message.projectId);
|
|
2105
2157
|
const projectRoot = projectRootFor(projectsRoot, message.projectId, manifestByProjectId);
|
|
@@ -2185,5 +2237,6 @@ if (isCliEntrypoint()) {
|
|
|
2185
2237
|
0 && (module.exports = {
|
|
2186
2238
|
ensureVisibleGitCheckout,
|
|
2187
2239
|
isArtifactEnvPath,
|
|
2240
|
+
prepareArtifactEnvForShell,
|
|
2188
2241
|
syncSessionArtifacts
|
|
2189
2242
|
});
|