@youtyan/code-viewer 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/code-viewer.js +792 -651
- package/package.json +1 -1
- package/web/app.js +125 -42
- package/web/style.css +46 -0
package/dist/code-viewer.js
CHANGED
|
@@ -94,6 +94,157 @@ var init_sqlite_driver = __esm(() => {
|
|
|
94
94
|
NPX_CACHE_GUIDE = 'Stale npx cache likely contains a binary compiled for a different Node.js version. Fix: `rm -rf ~/.npm/_npx` (macOS / Linux) or `Remove-Item -Recurse -Force "$(npm config get cache)\\_npx"` (Windows), ' + "then re-run `npx -y @youtyan/code-viewer@latest …`.";
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
// web-src/core/id.ts
|
|
98
|
+
function bytesToHex(bytes) {
|
|
99
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
100
|
+
}
|
|
101
|
+
function bytesToBase36(bytes) {
|
|
102
|
+
return Array.from(bytes, (b) => BASE36_ALPHABET[b % BASE36_ALPHABET.length]).join("");
|
|
103
|
+
}
|
|
104
|
+
function randomBase36(length) {
|
|
105
|
+
const cryptoApi = globalThis.crypto;
|
|
106
|
+
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
107
|
+
const bytes = new Uint8Array(length);
|
|
108
|
+
cryptoApi.getRandomValues(bytes);
|
|
109
|
+
return bytesToBase36(bytes);
|
|
110
|
+
}
|
|
111
|
+
return Math.random().toString(36).slice(2, 2 + length).padEnd(length, "0");
|
|
112
|
+
}
|
|
113
|
+
function makeId(prefix) {
|
|
114
|
+
const cryptoApi = globalThis.crypto;
|
|
115
|
+
if (typeof cryptoApi?.randomUUID === "function") {
|
|
116
|
+
return `${prefix}-${cryptoApi.randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
117
|
+
}
|
|
118
|
+
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
119
|
+
const bytes = new Uint8Array(8);
|
|
120
|
+
cryptoApi.getRandomValues(bytes);
|
|
121
|
+
return `${prefix}-${bytesToHex(bytes)}`;
|
|
122
|
+
}
|
|
123
|
+
return `${prefix}-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
124
|
+
}
|
|
125
|
+
function makeTimedId(prefix) {
|
|
126
|
+
const time = Date.now().toString(36);
|
|
127
|
+
const random = randomBase36(6);
|
|
128
|
+
return `${prefix}-${time}${random}`;
|
|
129
|
+
}
|
|
130
|
+
var BASE36_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
131
|
+
|
|
132
|
+
// web-src/core/routes.ts
|
|
133
|
+
function assertNever(value) {
|
|
134
|
+
throw new Error(`unhandled route: ${JSON.stringify(value)}`);
|
|
135
|
+
}
|
|
136
|
+
function parseLineTarget(value) {
|
|
137
|
+
const raw = value || "";
|
|
138
|
+
const range = /^(\d+)-(\d+)$/.exec(raw);
|
|
139
|
+
if (range) {
|
|
140
|
+
const a = Number(range[1]);
|
|
141
|
+
const b = Number(range[2]);
|
|
142
|
+
const start = Math.min(a, b);
|
|
143
|
+
const end = Math.max(a, b);
|
|
144
|
+
if (start > 0)
|
|
145
|
+
return { start, end };
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const line = Number(raw);
|
|
149
|
+
return Number.isInteger(line) && line > 0 ? line : undefined;
|
|
150
|
+
}
|
|
151
|
+
function formatLineTarget(line) {
|
|
152
|
+
return typeof line === "number" ? String(line) : `${line.start}-${line.end}`;
|
|
153
|
+
}
|
|
154
|
+
function buildRoute(route) {
|
|
155
|
+
switch (route.screen) {
|
|
156
|
+
case "repo": {
|
|
157
|
+
const params = new URLSearchParams;
|
|
158
|
+
if (route.ref && route.ref !== "worktree")
|
|
159
|
+
params.set("ref", route.ref);
|
|
160
|
+
if (route.path)
|
|
161
|
+
params.set("path", route.path);
|
|
162
|
+
const qs = params.toString();
|
|
163
|
+
return `/${qs ? `?${qs}` : ""}`;
|
|
164
|
+
}
|
|
165
|
+
case "file":
|
|
166
|
+
if (route.view === "blob") {
|
|
167
|
+
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=blob" + (route.preview ? "&preview=1" : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
168
|
+
}
|
|
169
|
+
if (route.view === "blame") {
|
|
170
|
+
const ref = route.ref || "worktree";
|
|
171
|
+
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(ref) + "&view=blame" + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
172
|
+
}
|
|
173
|
+
if (route.view === "history") {
|
|
174
|
+
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=history" + (route.commit ? `&commit=${encodeURIComponent(route.commit)}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
175
|
+
}
|
|
176
|
+
return "/file?path=" + encodeURIComponent(route.path) + "&ref=" + encodeURIComponent(route.ref || "worktree") + "&from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
177
|
+
case "diff":
|
|
178
|
+
return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.path ? `&path=${encodeURIComponent(route.path)}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
179
|
+
case "help": {
|
|
180
|
+
const params = new URLSearchParams;
|
|
181
|
+
if (route.lang && route.lang !== "en")
|
|
182
|
+
params.set("lang", route.lang);
|
|
183
|
+
if (route.section && route.section !== "overview")
|
|
184
|
+
params.set("section", route.section);
|
|
185
|
+
const qs = params.toString();
|
|
186
|
+
return `/help${qs ? `?${qs}` : ""}`;
|
|
187
|
+
}
|
|
188
|
+
case "history": {
|
|
189
|
+
const params = new URLSearchParams;
|
|
190
|
+
if (route.ref && route.ref !== "HEAD")
|
|
191
|
+
params.set("ref", route.ref);
|
|
192
|
+
if (route.commit)
|
|
193
|
+
params.set("commit", route.commit);
|
|
194
|
+
const qs = params.toString();
|
|
195
|
+
return `/history${qs ? `?${qs}` : ""}`;
|
|
196
|
+
}
|
|
197
|
+
case "journal": {
|
|
198
|
+
const params = new URLSearchParams;
|
|
199
|
+
if (route.tab && route.tab !== "journal")
|
|
200
|
+
params.set("tab", route.tab);
|
|
201
|
+
if (route.date)
|
|
202
|
+
params.set("date", route.date);
|
|
203
|
+
if (route.label)
|
|
204
|
+
params.set("label", route.label);
|
|
205
|
+
if (route.task)
|
|
206
|
+
params.set("task", route.task);
|
|
207
|
+
const qs = params.toString();
|
|
208
|
+
return `/journal${qs ? `?${qs}` : ""}`;
|
|
209
|
+
}
|
|
210
|
+
case "database": {
|
|
211
|
+
const params = new URLSearchParams;
|
|
212
|
+
if (route.db)
|
|
213
|
+
params.set("db", route.db);
|
|
214
|
+
if (route.schema)
|
|
215
|
+
params.set("schema", route.schema);
|
|
216
|
+
if (route.table)
|
|
217
|
+
params.set("table", route.table);
|
|
218
|
+
if (route.tab)
|
|
219
|
+
params.set("tab", route.tab);
|
|
220
|
+
if (route.diffBefore)
|
|
221
|
+
params.set("diffBefore", route.diffBefore);
|
|
222
|
+
if (route.diffAfter)
|
|
223
|
+
params.set("diffAfter", route.diffAfter);
|
|
224
|
+
const qs = params.toString();
|
|
225
|
+
return `/database${qs ? `?${qs}` : ""}`;
|
|
226
|
+
}
|
|
227
|
+
case "unknown":
|
|
228
|
+
return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree");
|
|
229
|
+
default:
|
|
230
|
+
return assertNever(route);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
var SPA_PATHS, APP_ENTRY_PATHS;
|
|
234
|
+
var init_routes = __esm(() => {
|
|
235
|
+
SPA_PATHS = [
|
|
236
|
+
"/todif",
|
|
237
|
+
"/todiff",
|
|
238
|
+
"/file",
|
|
239
|
+
"/help",
|
|
240
|
+
"/history",
|
|
241
|
+
"/journal",
|
|
242
|
+
"/database",
|
|
243
|
+
"/doctor"
|
|
244
|
+
];
|
|
245
|
+
APP_ENTRY_PATHS = ["/", "/index.html"];
|
|
246
|
+
});
|
|
247
|
+
|
|
97
248
|
// web-src/server/json-store.ts
|
|
98
249
|
import { randomBytes } from "node:crypto";
|
|
99
250
|
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
@@ -205,6 +356,11 @@ function createJsonFileStore(options) {
|
|
|
205
356
|
}
|
|
206
357
|
var init_json_store = () => {};
|
|
207
358
|
|
|
359
|
+
// web-src/server/ordered-insert.ts
|
|
360
|
+
function orderedInsertOptionCount(input) {
|
|
361
|
+
return (input.before_id ? 1 : 0) + (input.after_id ? 1 : 0) + (input.position !== undefined ? 1 : 0);
|
|
362
|
+
}
|
|
363
|
+
|
|
208
364
|
// web-src/server/annotations.ts
|
|
209
365
|
import { join } from "node:path";
|
|
210
366
|
function annotationsFilePath(root) {
|
|
@@ -214,9 +370,7 @@ function emptyAnnotationsState() {
|
|
|
214
370
|
return { version: 1, sessions: [] };
|
|
215
371
|
}
|
|
216
372
|
function makeAnnotationId(prefix) {
|
|
217
|
-
|
|
218
|
-
const time = Date.now().toString(36);
|
|
219
|
-
return `${prefix}-${time}${random}`;
|
|
373
|
+
return makeTimedId(prefix);
|
|
220
374
|
}
|
|
221
375
|
function normalizeLineRange(raw) {
|
|
222
376
|
if (!raw || typeof raw !== "object")
|
|
@@ -229,16 +383,10 @@ function normalizeLineRange(raw) {
|
|
|
229
383
|
return { start, end: endValue };
|
|
230
384
|
}
|
|
231
385
|
function parseAnnotationLine(raw) {
|
|
232
|
-
const
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const start = Math.min(a, b);
|
|
237
|
-
const end = Math.max(a, b);
|
|
238
|
-
return start > 0 ? { start, end } : undefined;
|
|
239
|
-
}
|
|
240
|
-
const line = Number(raw);
|
|
241
|
-
return Number.isInteger(line) && line > 0 ? { start: line, end: line } : undefined;
|
|
386
|
+
const target = parseLineTarget(raw);
|
|
387
|
+
if (target === undefined)
|
|
388
|
+
return;
|
|
389
|
+
return typeof target === "number" ? { start: target, end: target } : target;
|
|
242
390
|
}
|
|
243
391
|
function normalizeRange(raw) {
|
|
244
392
|
const from = raw && typeof raw === "object" && typeof raw.from === "string" ? raw.from || "HEAD" : "HEAD";
|
|
@@ -441,11 +589,8 @@ function startAnnotationSession(state, title, now, id = makeAnnotationId("s")) {
|
|
|
441
589
|
session
|
|
442
590
|
};
|
|
443
591
|
}
|
|
444
|
-
function insertOptionCount(input) {
|
|
445
|
-
return (input.before_id ? 1 : 0) + (input.after_id ? 1 : 0) + (input.position !== undefined ? 1 : 0);
|
|
446
|
-
}
|
|
447
592
|
function entryInsertIndex(entries, input) {
|
|
448
|
-
if (
|
|
593
|
+
if (orderedInsertOptionCount(input) > 1)
|
|
449
594
|
return { ok: false, error: "use only one of before, after, or position" };
|
|
450
595
|
if (input.before_id) {
|
|
451
596
|
const index = entries.findIndex((entry) => entry.id === input.before_id);
|
|
@@ -471,7 +616,7 @@ function entryInsertIndex(entries, input) {
|
|
|
471
616
|
function findSessionByEntryId(sessions, entryId) {
|
|
472
617
|
return sessions.find((session) => session.entries.some((entry) => entry.id === entryId));
|
|
473
618
|
}
|
|
474
|
-
function addAnnotationEntry(state, input, now,
|
|
619
|
+
function addAnnotationEntry(state, input, now, makeId2 = makeAnnotationId) {
|
|
475
620
|
const target = normalizeAnnotationTarget(input.target);
|
|
476
621
|
if (target?.kind === "database" && !target.db)
|
|
477
622
|
return { ok: false, error: "database annotation requires db" };
|
|
@@ -514,13 +659,13 @@ function addAnnotationEntry(state, input, now, makeId = makeAnnotationId) {
|
|
|
514
659
|
session = sessions[sessions.length - 1];
|
|
515
660
|
}
|
|
516
661
|
if (!session) {
|
|
517
|
-
const started = startAnnotationSession(state, input.session_title || "", now,
|
|
662
|
+
const started = startAnnotationSession(state, input.session_title || "", now, makeId2("s"));
|
|
518
663
|
sessions = started.state.sessions;
|
|
519
664
|
session = started.session;
|
|
520
665
|
createdSession = true;
|
|
521
666
|
}
|
|
522
667
|
const entry = {
|
|
523
|
-
id:
|
|
668
|
+
id: makeId2("a"),
|
|
524
669
|
created_at: now,
|
|
525
670
|
path,
|
|
526
671
|
range: normalizeRange(input.range),
|
|
@@ -554,7 +699,7 @@ function addAnnotationEntry(state, input, now, makeId = makeAnnotationId) {
|
|
|
554
699
|
};
|
|
555
700
|
}
|
|
556
701
|
function moveAnnotationEntry(state, id, input) {
|
|
557
|
-
if (
|
|
702
|
+
if (orderedInsertOptionCount(input) !== 1)
|
|
558
703
|
return { ok: false, error: "move requires before, after, or position" };
|
|
559
704
|
const sourceSession = findSessionByEntryId(state.sessions, id);
|
|
560
705
|
const entry = sourceSession?.entries.find((e) => e.id === id);
|
|
@@ -654,6 +799,7 @@ function deleteAnnotationById(state, id) {
|
|
|
654
799
|
}
|
|
655
800
|
var CODE_VIEWER_DIR = ".code-viewer", ANNOTATIONS_FILE_NAME = "annotations.json", ANNOTATION_BODY_MAX_BYTES, ANNOTATION_TITLE_MAX_CHARS = 300, MAX_ANNOTATIONS_JSON_BYTES = 5000000, annotationsStore;
|
|
656
801
|
var init_annotations = __esm(() => {
|
|
802
|
+
init_routes();
|
|
657
803
|
init_json_store();
|
|
658
804
|
ANNOTATION_BODY_MAX_BYTES = 64 * 1024;
|
|
659
805
|
annotationsStore = createJsonFileStore({
|
|
@@ -835,6 +981,44 @@ var init_command_resolver = __esm(() => {
|
|
|
835
981
|
activeOverrides = new Map;
|
|
836
982
|
});
|
|
837
983
|
|
|
984
|
+
// web-src/server/cache.ts
|
|
985
|
+
import { lstatSync } from "node:fs";
|
|
986
|
+
import { join as join3 } from "node:path";
|
|
987
|
+
function cacheFresh(cached, now = Date.now(), ttlMs = CACHE_TTL_MS) {
|
|
988
|
+
return !!cached && now - cached.storedAt <= ttlMs;
|
|
989
|
+
}
|
|
990
|
+
function setTimedCacheEntry(cache, key, value, now = Date.now(), maxEntries = MAX_TIMED_CACHE_ENTRIES) {
|
|
991
|
+
cache.set(key, { ...value, storedAt: now });
|
|
992
|
+
while (cache.size > maxEntries) {
|
|
993
|
+
const oldest = cache.keys().next().value;
|
|
994
|
+
if (oldest === undefined)
|
|
995
|
+
break;
|
|
996
|
+
cache.delete(oldest);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function worktreeFileSignature(path, cwd) {
|
|
1000
|
+
try {
|
|
1001
|
+
const stats = lstatSync(join3(cwd, path));
|
|
1002
|
+
const inode = "ino" in stats ? stats.ino : 0;
|
|
1003
|
+
return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
|
|
1004
|
+
} catch {
|
|
1005
|
+
return "state:missing";
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function fileDiffCacheKey(options) {
|
|
1009
|
+
const worktreeTarget = options.range.from === "worktree" || !options.range.to || options.range.to === "worktree";
|
|
1010
|
+
if (options.isUntracked && !worktreeTarget) {
|
|
1011
|
+
throw new Error("untracked file diffs require a worktree range");
|
|
1012
|
+
}
|
|
1013
|
+
const signature = worktreeTarget ? `\x00${worktreeFileSignature(options.path, options.cwd)}` : "";
|
|
1014
|
+
if (options.isUntracked) {
|
|
1015
|
+
return `u\x00${options.path}${signature}\x00${options.extras.join("\x00")}`;
|
|
1016
|
+
}
|
|
1017
|
+
return `t\x00${options.path}\x00${options.oldPath || ""}${signature}\x00${[...options.extras, ...options.args].join("\x00")}`;
|
|
1018
|
+
}
|
|
1019
|
+
var CACHE_TTL_MS = 1500, MAX_TIMED_CACHE_ENTRIES = 200;
|
|
1020
|
+
var init_cache = () => {};
|
|
1021
|
+
|
|
838
1022
|
// web-src/server/name-pattern.ts
|
|
839
1023
|
function parseGlobSegment(pattern) {
|
|
840
1024
|
const matchers = [];
|
|
@@ -947,6 +1131,21 @@ import {
|
|
|
947
1131
|
} from "node:http";
|
|
948
1132
|
import { Readable } from "node:stream";
|
|
949
1133
|
function runSync(args, cwd, options = {}) {
|
|
1134
|
+
const proc = runBytesSync(args, cwd, options);
|
|
1135
|
+
return {
|
|
1136
|
+
code: proc.code,
|
|
1137
|
+
stdout: new TextDecoder().decode(proc.stdout),
|
|
1138
|
+
stderr: proc.stderr
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
function runAsync(args, cwd, options = {}) {
|
|
1142
|
+
return runBytesAsync(args, cwd, options).then((proc) => ({
|
|
1143
|
+
code: proc.code,
|
|
1144
|
+
stdout: new TextDecoder().decode(proc.stdout),
|
|
1145
|
+
stderr: proc.stderr
|
|
1146
|
+
}));
|
|
1147
|
+
}
|
|
1148
|
+
function runBytesSync(args, cwd, options = {}) {
|
|
950
1149
|
const proc = spawnSync(args[0], args.slice(1), {
|
|
951
1150
|
cwd,
|
|
952
1151
|
encoding: "buffer",
|
|
@@ -957,17 +1156,10 @@ function runSync(args, cwd, options = {}) {
|
|
|
957
1156
|
});
|
|
958
1157
|
return {
|
|
959
1158
|
code: proc.status ?? (proc.error ? 1 : 0),
|
|
960
|
-
stdout: new
|
|
1159
|
+
stdout: new Uint8Array(proc.stdout || new Uint8Array),
|
|
961
1160
|
stderr: appendProcessError(new TextDecoder().decode(proc.stderr || new Uint8Array), proc.error)
|
|
962
1161
|
};
|
|
963
1162
|
}
|
|
964
|
-
function runAsync(args, cwd, options = {}) {
|
|
965
|
-
return runBytesAsync(args, cwd, options).then((proc) => ({
|
|
966
|
-
code: proc.code,
|
|
967
|
-
stdout: new TextDecoder().decode(proc.stdout),
|
|
968
|
-
stderr: proc.stderr
|
|
969
|
-
}));
|
|
970
|
-
}
|
|
971
1163
|
function runBytesAsync(args, cwd, options = {}) {
|
|
972
1164
|
const maxBuffer = options.maxBuffer ?? 64 * 1024 * 1024;
|
|
973
1165
|
return new Promise((resolve) => {
|
|
@@ -1217,14 +1409,16 @@ var init_runtime = () => {};
|
|
|
1217
1409
|
import {
|
|
1218
1410
|
closeSync,
|
|
1219
1411
|
existsSync,
|
|
1220
|
-
lstatSync,
|
|
1412
|
+
lstatSync as lstatSync2,
|
|
1221
1413
|
openSync,
|
|
1222
1414
|
readdirSync,
|
|
1223
1415
|
readFileSync,
|
|
1416
|
+
readlinkSync,
|
|
1224
1417
|
readSync,
|
|
1418
|
+
realpathSync as realpathSync2,
|
|
1225
1419
|
statSync as statSync2
|
|
1226
1420
|
} from "node:fs";
|
|
1227
|
-
import { join as
|
|
1421
|
+
import { dirname as dirname3, join as join4, posix, relative as relative2 } from "node:path";
|
|
1228
1422
|
function normalizeBlameRef(ref, base) {
|
|
1229
1423
|
const rawRef = ref || "worktree";
|
|
1230
1424
|
if (base === "worktree" && rawRef !== "worktree") {
|
|
@@ -1322,12 +1516,76 @@ async function statusPorcelainForPathAsync(path, cwd) {
|
|
|
1322
1516
|
error: gitFailureMessage(res, "git status failed")
|
|
1323
1517
|
};
|
|
1324
1518
|
}
|
|
1519
|
+
async function repoStatusMapAsync(cwd, now = Date.now()) {
|
|
1520
|
+
const cached = repoStatusMapCache.get(cwd);
|
|
1521
|
+
if (cacheFresh(cached, now))
|
|
1522
|
+
return cached.map;
|
|
1523
|
+
const map = new Map;
|
|
1524
|
+
const res = await runGitAsync([
|
|
1525
|
+
"git",
|
|
1526
|
+
"-c",
|
|
1527
|
+
"core.quotepath=false",
|
|
1528
|
+
"status",
|
|
1529
|
+
"--porcelain=v1",
|
|
1530
|
+
"-z",
|
|
1531
|
+
"--untracked-files=all"
|
|
1532
|
+
], cwd);
|
|
1533
|
+
if (res.code !== 0)
|
|
1534
|
+
return map;
|
|
1535
|
+
const records = res.stdout.split("\x00").filter(Boolean);
|
|
1536
|
+
for (let i = 0;i < records.length; i++) {
|
|
1537
|
+
const record = records[i];
|
|
1538
|
+
const xy = record.slice(0, 2);
|
|
1539
|
+
const path = record.slice(3);
|
|
1540
|
+
if (!path)
|
|
1541
|
+
continue;
|
|
1542
|
+
if (xy === "??") {
|
|
1543
|
+
map.set(path, "A");
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (xy[0] === "R" || xy[0] === "C" || xy[1] === "R" || xy[1] === "C") {
|
|
1547
|
+
i++;
|
|
1548
|
+
map.set(path, "R");
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
const code = xy[0] !== " " ? xy[0] : xy[1];
|
|
1552
|
+
if (code && code !== " ")
|
|
1553
|
+
map.set(path, code);
|
|
1554
|
+
}
|
|
1555
|
+
setTimedCacheEntry(repoStatusMapCache, cwd, { map }, now);
|
|
1556
|
+
return map;
|
|
1557
|
+
}
|
|
1325
1558
|
function show(ref, path, cwd) {
|
|
1326
1559
|
return run(["git", "show", `${ref}:${path}`], cwd);
|
|
1327
1560
|
}
|
|
1328
1561
|
function showAsync(ref, path, cwd) {
|
|
1329
1562
|
return runGitAsync(["git", "show", `${ref}:${path}`], cwd);
|
|
1330
1563
|
}
|
|
1564
|
+
function resolveSymlinkPath(linkPath, target) {
|
|
1565
|
+
if (!target || target.startsWith("/") || target.includes("\x00"))
|
|
1566
|
+
return null;
|
|
1567
|
+
const baseDir = dirname3(linkPath);
|
|
1568
|
+
const combined = baseDir === "." ? target : `${baseDir}/${target}`;
|
|
1569
|
+
const normalized = posix.normalize(combined);
|
|
1570
|
+
if (normalized === "." || normalized === "")
|
|
1571
|
+
return "";
|
|
1572
|
+
if (normalized === ".." || normalized.startsWith("../"))
|
|
1573
|
+
return null;
|
|
1574
|
+
return normalized;
|
|
1575
|
+
}
|
|
1576
|
+
async function gitSymlinkTargetMetadataAsync(ref, path, cwd) {
|
|
1577
|
+
const res = await showAsync(ref, path, cwd);
|
|
1578
|
+
if (res.code !== 0)
|
|
1579
|
+
return { symlink_target_type: "missing" };
|
|
1580
|
+
const target = res.stdout;
|
|
1581
|
+
const resolved = resolveSymlinkPath(path, target);
|
|
1582
|
+
if (resolved === null)
|
|
1583
|
+
return { symlink_target: target, symlink_target_type: "missing" };
|
|
1584
|
+
const type = await runGitAsync(["git", "cat-file", "-t", `${ref}:${resolved}`], cwd);
|
|
1585
|
+
const kind = type.stdout.trim();
|
|
1586
|
+
const symlink_target_type = kind === "tree" ? "tree" : kind === "blob" ? "blob" : "missing";
|
|
1587
|
+
return symlink_target_type === "missing" ? { symlink_target: target, symlink_target_type } : { symlink_target: target, symlink_target_type, resolved_path: resolved };
|
|
1588
|
+
}
|
|
1331
1589
|
function catFileBlobStream(oid, cwd) {
|
|
1332
1590
|
return spawnStream(resolveGitArgs(["git", "cat-file", "blob", oid]), cwd);
|
|
1333
1591
|
}
|
|
@@ -1958,7 +2216,7 @@ function isGitInternalPath(path) {
|
|
|
1958
2216
|
return pathHasSegment(path, ".git");
|
|
1959
2217
|
}
|
|
1960
2218
|
function syntheticUncommittedBlameFromWorktree(cwd, path) {
|
|
1961
|
-
const filePath =
|
|
2219
|
+
const filePath = join4(cwd, path);
|
|
1962
2220
|
try {
|
|
1963
2221
|
const stat = statSync2(filePath);
|
|
1964
2222
|
if (!stat.isFile())
|
|
@@ -2217,7 +2475,7 @@ function omittedWorktreeDirectoryReason(name, omitDirNames) {
|
|
|
2217
2475
|
return omitDirNames.matches(name) ? "heavy" : undefined;
|
|
2218
2476
|
}
|
|
2219
2477
|
function worktreeSubmodulePaths(cwd) {
|
|
2220
|
-
if (!existsSync(
|
|
2478
|
+
if (!existsSync(join4(cwd, ".gitmodules")))
|
|
2221
2479
|
return new Set;
|
|
2222
2480
|
const res = run(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
2223
2481
|
if (res.code !== 0)
|
|
@@ -2229,7 +2487,7 @@ function worktreeSubmodulePaths(cwd) {
|
|
|
2229
2487
|
}).filter(Boolean));
|
|
2230
2488
|
}
|
|
2231
2489
|
async function worktreeSubmodulePathsAsync(cwd) {
|
|
2232
|
-
if (!existsSync(
|
|
2490
|
+
if (!existsSync(join4(cwd, ".gitmodules")))
|
|
2233
2491
|
return new Set;
|
|
2234
2492
|
const res = await runGitAsync(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
2235
2493
|
if (res.code !== 0)
|
|
@@ -2240,7 +2498,48 @@ async function worktreeSubmodulePathsAsync(cwd) {
|
|
|
2240
2498
|
return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
|
|
2241
2499
|
}).filter(Boolean));
|
|
2242
2500
|
}
|
|
2243
|
-
function
|
|
2501
|
+
function realpathWithinRepo(cwd, full, allowRoot) {
|
|
2502
|
+
try {
|
|
2503
|
+
const realCwd = realpathSync2(cwd);
|
|
2504
|
+
const realFull = realpathSync2(full);
|
|
2505
|
+
const rel = relative2(realCwd, realFull);
|
|
2506
|
+
if (rel.startsWith("..") || rel.startsWith("/") || rel.startsWith("\\"))
|
|
2507
|
+
return null;
|
|
2508
|
+
if (rel === "" && !allowRoot)
|
|
2509
|
+
return null;
|
|
2510
|
+
return realFull;
|
|
2511
|
+
} catch {
|
|
2512
|
+
return null;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
function resolveWorktreeSymlinkTarget(cwd, full) {
|
|
2516
|
+
let symlink_target;
|
|
2517
|
+
try {
|
|
2518
|
+
symlink_target = readlinkSync(full);
|
|
2519
|
+
} catch {
|
|
2520
|
+
symlink_target = undefined;
|
|
2521
|
+
}
|
|
2522
|
+
let symlink_target_type = "missing";
|
|
2523
|
+
if (realpathWithinRepo(cwd, full, false) !== null) {
|
|
2524
|
+
try {
|
|
2525
|
+
const stat = statSync2(full);
|
|
2526
|
+
symlink_target_type = stat.isDirectory() ? "tree" : stat.isFile() ? "blob" : "missing";
|
|
2527
|
+
} catch {
|
|
2528
|
+
symlink_target_type = "missing";
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
return symlink_target === undefined ? { symlink_target_type } : { symlink_target, symlink_target_type };
|
|
2532
|
+
}
|
|
2533
|
+
function recursiveWorktreeFileEntry(cwd, full, name, path, isSymlink) {
|
|
2534
|
+
const symlinkInfo = isSymlink ? resolveWorktreeSymlinkTarget(cwd, full) : null;
|
|
2535
|
+
return {
|
|
2536
|
+
name,
|
|
2537
|
+
path,
|
|
2538
|
+
type: "blob",
|
|
2539
|
+
...symlinkInfo ? { is_symlink: true, ...symlinkInfo } : {}
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
function worktreeEntryFromDirent(cwd, base, dir, name, isDirectory, isSymlink, omitDirNames, excludeNames, submodulePaths) {
|
|
2244
2543
|
if (excludeNames.matches(name))
|
|
2245
2544
|
return {
|
|
2246
2545
|
name,
|
|
@@ -2248,10 +2547,18 @@ function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, exc
|
|
|
2248
2547
|
type: isDirectory ? "tree" : "blob"
|
|
2249
2548
|
};
|
|
2250
2549
|
const entryPath = base ? `${base}/${name}` : name;
|
|
2251
|
-
const
|
|
2550
|
+
const symlinkInfo = isSymlink ? resolveWorktreeSymlinkTarget(cwd, join4(dir, name)) : null;
|
|
2551
|
+
const resolvedIsDirectory = symlinkInfo ? symlinkInfo.symlink_target_type === "tree" : isDirectory;
|
|
2552
|
+
const type = symlinkInfo && symlinkInfo.symlink_target_type === "missing" ? "blob" : resolvedIsDirectory ? hasDotGitEntry(join4(dir, name)) ? "commit" : "tree" : "blob";
|
|
2252
2553
|
const omittedReason = type === "tree" ? omittedWorktreeDirectoryReason(name, omitDirNames) : undefined;
|
|
2253
2554
|
const submodule = type === "commit" && submodulePaths.has(entryPath) ? true : undefined;
|
|
2254
|
-
const baseEntry =
|
|
2555
|
+
const baseEntry = {
|
|
2556
|
+
name,
|
|
2557
|
+
path: entryPath,
|
|
2558
|
+
type,
|
|
2559
|
+
...submodule ? { submodule } : {},
|
|
2560
|
+
...symlinkInfo ? { is_symlink: true, ...symlinkInfo } : {}
|
|
2561
|
+
};
|
|
2255
2562
|
return omittedReason ? {
|
|
2256
2563
|
...baseEntry,
|
|
2257
2564
|
children_omitted: true,
|
|
@@ -2260,14 +2567,16 @@ function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, exc
|
|
|
2260
2567
|
}
|
|
2261
2568
|
function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2262
2569
|
const base = normalizeTreePath(path);
|
|
2263
|
-
const root =
|
|
2570
|
+
const root = join4(cwd, base);
|
|
2571
|
+
if (realpathWithinRepo(cwd, root, true) === null)
|
|
2572
|
+
return [];
|
|
2264
2573
|
const omitDirNameSet = compileNamePatterns(omitDirNames);
|
|
2265
2574
|
const excludeNameSet = compileNamePatterns(excludeNames);
|
|
2266
2575
|
const submodulePaths = worktreeSubmodulePaths(cwd);
|
|
2267
2576
|
let directEntries;
|
|
2268
2577
|
try {
|
|
2269
2578
|
const dirents = readdirSync(root, { withFileTypes: true });
|
|
2270
|
-
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2579
|
+
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(cwd, base, root, entry.name, entry.isDirectory(), entry.isSymbolicLink(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2271
2580
|
} catch {
|
|
2272
2581
|
return [];
|
|
2273
2582
|
}
|
|
@@ -2307,7 +2616,7 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
2307
2616
|
if (excludeNameSet.matches(entry.name))
|
|
2308
2617
|
continue;
|
|
2309
2618
|
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2310
|
-
const full =
|
|
2619
|
+
const full = join4(dir, entry.name);
|
|
2311
2620
|
if (entry.isDirectory()) {
|
|
2312
2621
|
const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
|
|
2313
2622
|
if (omittedReason) {
|
|
@@ -2325,11 +2634,7 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
2325
2634
|
continue;
|
|
2326
2635
|
walk(full, entryPath, depth + 1);
|
|
2327
2636
|
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
2328
|
-
if (!pushRecursiveEntry(
|
|
2329
|
-
name: entry.name,
|
|
2330
|
-
path: entryPath,
|
|
2331
|
-
type: "blob"
|
|
2332
|
-
}))
|
|
2637
|
+
if (!pushRecursiveEntry(recursiveWorktreeFileEntry(cwd, full, entry.name, entryPath, entry.isSymbolicLink())))
|
|
2333
2638
|
return;
|
|
2334
2639
|
}
|
|
2335
2640
|
}
|
|
@@ -2339,14 +2644,16 @@ function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_
|
|
|
2339
2644
|
}
|
|
2340
2645
|
async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
2341
2646
|
const base = normalizeTreePath(path);
|
|
2342
|
-
const root =
|
|
2647
|
+
const root = join4(cwd, base);
|
|
2648
|
+
if (realpathWithinRepo(cwd, root, true) === null)
|
|
2649
|
+
return [];
|
|
2343
2650
|
const omitDirNameSet = compileNamePatterns(omitDirNames);
|
|
2344
2651
|
const excludeNameSet = compileNamePatterns(excludeNames);
|
|
2345
2652
|
const submodulePaths = await worktreeSubmodulePathsAsync(cwd);
|
|
2346
2653
|
let directEntries;
|
|
2347
2654
|
try {
|
|
2348
2655
|
const dirents = readdirSync(root, { withFileTypes: true });
|
|
2349
|
-
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2656
|
+
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(cwd, base, root, entry.name, entry.isDirectory(), entry.isSymbolicLink(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
2350
2657
|
} catch {
|
|
2351
2658
|
return [];
|
|
2352
2659
|
}
|
|
@@ -2394,7 +2701,7 @@ async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames
|
|
|
2394
2701
|
if (excludeNameSet.matches(entry.name))
|
|
2395
2702
|
continue;
|
|
2396
2703
|
const entryPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2397
|
-
const full =
|
|
2704
|
+
const full = join4(dir, entry.name);
|
|
2398
2705
|
if (entry.isDirectory()) {
|
|
2399
2706
|
const omittedReason = omittedWorktreeDirectoryReason(entry.name, omitDirNameSet);
|
|
2400
2707
|
if (omittedReason) {
|
|
@@ -2412,11 +2719,7 @@ async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames
|
|
|
2412
2719
|
continue;
|
|
2413
2720
|
await walk(full, entryPath, depth + 1);
|
|
2414
2721
|
} else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
2415
|
-
if (!pushRecursiveEntry(
|
|
2416
|
-
name: entry.name,
|
|
2417
|
-
path: entryPath,
|
|
2418
|
-
type: "blob"
|
|
2419
|
-
}))
|
|
2722
|
+
if (!pushRecursiveEntry(recursiveWorktreeFileEntry(cwd, full, entry.name, entryPath, entry.isSymbolicLink())))
|
|
2420
2723
|
return;
|
|
2421
2724
|
}
|
|
2422
2725
|
}
|
|
@@ -2426,12 +2729,24 @@ async function worktreeFilesystemEntriesAsync(cwd, path, recursive, omitDirNames
|
|
|
2426
2729
|
}
|
|
2427
2730
|
function hasDotGitEntry(dir) {
|
|
2428
2731
|
try {
|
|
2429
|
-
|
|
2732
|
+
lstatSync2(join4(dir, ".git"));
|
|
2430
2733
|
return true;
|
|
2431
2734
|
} catch (err) {
|
|
2432
2735
|
return !!err && typeof err === "object" && "code" in err && err.code !== "ENOENT";
|
|
2433
2736
|
}
|
|
2434
2737
|
}
|
|
2738
|
+
function parseLsTreeRecord(rec, allowedTypes) {
|
|
2739
|
+
const match = rec.match(new RegExp(`^(\\d+)\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2740
|
+
if (!match)
|
|
2741
|
+
return null;
|
|
2742
|
+
const [, mode, type, entryPath] = match;
|
|
2743
|
+
return {
|
|
2744
|
+
name: entryPath.split("/").pop() || entryPath,
|
|
2745
|
+
path: entryPath,
|
|
2746
|
+
type,
|
|
2747
|
+
...mode === LS_TREE_SYMLINK_MODE ? { is_symlink: true } : {}
|
|
2748
|
+
};
|
|
2749
|
+
}
|
|
2435
2750
|
function gitTreeEntries(ref, path, cwd, recursive) {
|
|
2436
2751
|
const base = normalizeTreePath(path);
|
|
2437
2752
|
const args = ["git", "-c", "core.quotepath=false", "ls-tree"];
|
|
@@ -2444,17 +2759,7 @@ function gitTreeEntries(ref, path, cwd, recursive) {
|
|
|
2444
2759
|
if (res.code !== 0)
|
|
2445
2760
|
return { code: res.code, entries: [], stderr: res.stderr };
|
|
2446
2761
|
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
2447
|
-
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) =>
|
|
2448
|
-
const match = rec.match(new RegExp(`^\\d+\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2449
|
-
if (!match)
|
|
2450
|
-
return null;
|
|
2451
|
-
const entryPath = match[2];
|
|
2452
|
-
return {
|
|
2453
|
-
name: entryPath.split("/").pop() || entryPath,
|
|
2454
|
-
path: entryPath,
|
|
2455
|
-
type: match[1]
|
|
2456
|
-
};
|
|
2457
|
-
}).filter((entry) => !!entry);
|
|
2762
|
+
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
|
|
2458
2763
|
if (recursive)
|
|
2459
2764
|
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
2460
2765
|
else
|
|
@@ -2473,17 +2778,7 @@ async function gitTreeEntriesAsync(ref, path, cwd, recursive) {
|
|
|
2473
2778
|
if (res.code !== 0)
|
|
2474
2779
|
return { code: res.code, entries: [], stderr: res.stderr };
|
|
2475
2780
|
const allowedTypes = recursive ? "blob|commit" : "tree|blob|commit";
|
|
2476
|
-
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) =>
|
|
2477
|
-
const match = rec.match(new RegExp(`^\\d+\\s+(${allowedTypes})\\s+[0-9a-fA-F]+\\t(.+)$`));
|
|
2478
|
-
if (!match)
|
|
2479
|
-
return null;
|
|
2480
|
-
const entryPath = match[2];
|
|
2481
|
-
return {
|
|
2482
|
-
name: entryPath.split("/").pop() || entryPath,
|
|
2483
|
-
path: entryPath,
|
|
2484
|
-
type: match[1]
|
|
2485
|
-
};
|
|
2486
|
-
}).filter((entry) => !!entry);
|
|
2781
|
+
let entries = res.stdout.split("\x00").filter(Boolean).map((rec) => parseLsTreeRecord(rec, allowedTypes)).filter((entry) => !!entry);
|
|
2487
2782
|
if (recursive)
|
|
2488
2783
|
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
2489
2784
|
else
|
|
@@ -2547,7 +2842,7 @@ async function listTreeResultAsync(ref, path, cwd, options = {}) {
|
|
|
2547
2842
|
}
|
|
2548
2843
|
function untrackedMeta(cwd) {
|
|
2549
2844
|
return untracked(cwd).flatMap((path) => {
|
|
2550
|
-
const full =
|
|
2845
|
+
const full = join4(cwd, path);
|
|
2551
2846
|
let fileExists = false;
|
|
2552
2847
|
try {
|
|
2553
2848
|
fileExists = existsSync(full) && statSync2(full).isFile();
|
|
@@ -2579,7 +2874,7 @@ function untrackedMeta(cwd) {
|
|
|
2579
2874
|
async function untrackedMetaAsync(cwd) {
|
|
2580
2875
|
const paths = await untrackedAsync(cwd);
|
|
2581
2876
|
return paths.flatMap((path) => {
|
|
2582
|
-
const full =
|
|
2877
|
+
const full = join4(cwd, path);
|
|
2583
2878
|
let fileExists = false;
|
|
2584
2879
|
try {
|
|
2585
2880
|
fileExists = existsSync(full) && statSync2(full).isFile();
|
|
@@ -2827,8 +3122,9 @@ function truncateToNHunks(diffText, n, maxLines = Number.POSITIVE_INFINITY) {
|
|
|
2827
3122
|
lineTruncated
|
|
2828
3123
|
};
|
|
2829
3124
|
}
|
|
2830
|
-
var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200;
|
|
3125
|
+
var BLAME_ZERO_SHA = "0000000000000000000000000000000000000000", WORKTREE_RECURSIVE_DEPTH_LIMIT = 32, WORKTREE_RECURSIVE_ENTRY_LIMIT = 50000, DEFAULT_REF_COMMIT_LIMIT = 100, MAX_REF_COMMIT_LIMIT = 500, COMMIT_FORMAT = "%H%x00%s%x00%an%x00%aI", ALWAYS_WORKTREE_OMIT_DIR_NAMES, DEFAULT_WORKTREE_OMIT_DIR_NAMES, GIT_COMMAND_TIMEOUT_MS = 20000, repoStatusMapCache, HISTORY_FORMAT = "%H%x00%s%x00%an%x00%aI%x00%P%x00%b", MAX_HISTORY_LIMIT = 200, LS_TREE_SYMLINK_MODE = "120000";
|
|
2831
3126
|
var init_git = __esm(() => {
|
|
3127
|
+
init_cache();
|
|
2832
3128
|
init_command_resolver();
|
|
2833
3129
|
init_name_pattern();
|
|
2834
3130
|
init_runtime();
|
|
@@ -2877,6 +3173,7 @@ var init_git = __esm(() => {
|
|
|
2877
3173
|
"bin",
|
|
2878
3174
|
"obj"
|
|
2879
3175
|
];
|
|
3176
|
+
repoStatusMapCache = new Map;
|
|
2880
3177
|
});
|
|
2881
3178
|
|
|
2882
3179
|
// web-src/server/server-registry.ts
|
|
@@ -2889,16 +3186,16 @@ import {
|
|
|
2889
3186
|
writeFileSync
|
|
2890
3187
|
} from "node:fs";
|
|
2891
3188
|
import { homedir } from "node:os";
|
|
2892
|
-
import { join as
|
|
3189
|
+
import { join as join5 } from "node:path";
|
|
2893
3190
|
function registryDir() {
|
|
2894
3191
|
const override = process.env.CODE_VIEWER_TEST_SERVER_REGISTRY_DIR;
|
|
2895
3192
|
if (override)
|
|
2896
3193
|
return override;
|
|
2897
|
-
return
|
|
3194
|
+
return join5(homedir(), ".cache", "code-viewer", "servers");
|
|
2898
3195
|
}
|
|
2899
3196
|
function serverRegistryFilePath(root) {
|
|
2900
3197
|
const hash = createHash("sha256").update(root).digest("hex").slice(0, 16);
|
|
2901
|
-
return
|
|
3198
|
+
return join5(registryDir(), `${hash}.json`);
|
|
2902
3199
|
}
|
|
2903
3200
|
function writeServerRegistry(entry) {
|
|
2904
3201
|
try {
|
|
@@ -2939,13 +3236,40 @@ function removeServerRegistry(root, pid) {
|
|
|
2939
3236
|
var init_server_registry = () => {};
|
|
2940
3237
|
|
|
2941
3238
|
// web-src/server/cli-helpers.ts
|
|
2942
|
-
import { realpathSync as
|
|
3239
|
+
import { realpathSync as realpathSync3 } from "node:fs";
|
|
2943
3240
|
function takeValue(argv, index, flag) {
|
|
2944
3241
|
const value = argv[index + 1];
|
|
2945
3242
|
if (value === undefined)
|
|
2946
3243
|
return { error: `${flag} requires a value` };
|
|
2947
3244
|
return { value, next: index + 1 };
|
|
2948
3245
|
}
|
|
3246
|
+
function takeGlobalCliOption(argv, index, options) {
|
|
3247
|
+
const flag = argv[index];
|
|
3248
|
+
if (flag === "--cwd" || flag === "--server" && options.allowServer) {
|
|
3249
|
+
const taken = takeValue(argv, index, flag);
|
|
3250
|
+
if ("error" in taken)
|
|
3251
|
+
return { kind: "error", error: taken.error };
|
|
3252
|
+
return {
|
|
3253
|
+
kind: flag === "--cwd" ? "cwd" : "server",
|
|
3254
|
+
value: taken.value,
|
|
3255
|
+
next: taken.next
|
|
3256
|
+
};
|
|
3257
|
+
}
|
|
3258
|
+
if (flag === "--bin" && options.allowedCommands) {
|
|
3259
|
+
const taken = takeValue(argv, index, flag);
|
|
3260
|
+
if ("error" in taken)
|
|
3261
|
+
return { kind: "error", error: taken.error };
|
|
3262
|
+
const parsed = parseExternalCommandOverride(taken.value, "--bin", options.allowedCommands);
|
|
3263
|
+
if (parsed.ok === false)
|
|
3264
|
+
return { kind: "error", error: parsed.error };
|
|
3265
|
+
return {
|
|
3266
|
+
kind: "command-override",
|
|
3267
|
+
override: parsed.override,
|
|
3268
|
+
next: taken.next
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
return { kind: "unhandled" };
|
|
3272
|
+
}
|
|
2949
3273
|
function shellSingleQuote(value) {
|
|
2950
3274
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2951
3275
|
}
|
|
@@ -2965,7 +3289,7 @@ function isUnsafeText(value) {
|
|
|
2965
3289
|
return true;
|
|
2966
3290
|
return false;
|
|
2967
3291
|
}
|
|
2968
|
-
function
|
|
3292
|
+
function validateSafeCliValue(value, flag) {
|
|
2969
3293
|
if (!value)
|
|
2970
3294
|
return `${flag} requires a non-empty value`;
|
|
2971
3295
|
if (isUnsafeText(value))
|
|
@@ -2974,13 +3298,16 @@ function validateRefValue(value, flag) {
|
|
|
2974
3298
|
return `${flag} must not start with '-'`;
|
|
2975
3299
|
return;
|
|
2976
3300
|
}
|
|
3301
|
+
function validateRefValue(value, flag) {
|
|
3302
|
+
const error = validateSafeCliValue(value, flag);
|
|
3303
|
+
if (error)
|
|
3304
|
+
return error;
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
2977
3307
|
function validateRepoRelativePathValue(value, flag) {
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
return `${flag} must be single-line and must not contain NUL`;
|
|
2982
|
-
if (value.startsWith("-"))
|
|
2983
|
-
return `${flag} must not start with '-'`;
|
|
3308
|
+
const error = validateSafeCliValue(value, flag);
|
|
3309
|
+
if (error)
|
|
3310
|
+
return error;
|
|
2984
3311
|
if (value.startsWith("/") || value.startsWith("\\"))
|
|
2985
3312
|
return `${flag} must be repo-relative`;
|
|
2986
3313
|
const parts = value.split(/[\\/]+/);
|
|
@@ -2998,7 +3325,7 @@ function resolveRepoRootSafe(cwdOption) {
|
|
|
2998
3325
|
const base = cwdOption || process.cwd();
|
|
2999
3326
|
let baseReal;
|
|
3000
3327
|
try {
|
|
3001
|
-
baseReal =
|
|
3328
|
+
baseReal = realpathSync3(base);
|
|
3002
3329
|
} catch {
|
|
3003
3330
|
return {
|
|
3004
3331
|
ok: false,
|
|
@@ -3094,6 +3421,7 @@ function extractErrorDetail(rawBody, isJson, status) {
|
|
|
3094
3421
|
return trimmed;
|
|
3095
3422
|
}
|
|
3096
3423
|
var init_cli_helpers = __esm(() => {
|
|
3424
|
+
init_command_resolver();
|
|
3097
3425
|
init_git();
|
|
3098
3426
|
init_server_registry();
|
|
3099
3427
|
});
|
|
@@ -3113,6 +3441,28 @@ function parsePosition(value) {
|
|
|
3113
3441
|
const n = Number(value);
|
|
3114
3442
|
return Number.isInteger(n) && n > 0 ? n : Number.NaN;
|
|
3115
3443
|
}
|
|
3444
|
+
function parseAnnotationAddOptions(options) {
|
|
3445
|
+
const body = options.get("--body");
|
|
3446
|
+
const bodyFile = options.get("--body-file");
|
|
3447
|
+
if (body !== undefined && bodyFile !== undefined)
|
|
3448
|
+
return { ok: false, error: "use either --body or --body-file" };
|
|
3449
|
+
const position = parsePosition(options.get("--position"));
|
|
3450
|
+
if (Number.isNaN(position))
|
|
3451
|
+
return { ok: false, error: "--position must be a positive integer" };
|
|
3452
|
+
return {
|
|
3453
|
+
ok: true,
|
|
3454
|
+
options: {
|
|
3455
|
+
title: options.get("--title"),
|
|
3456
|
+
session: options.get("--session"),
|
|
3457
|
+
sessionTitle: options.get("--session-title"),
|
|
3458
|
+
body,
|
|
3459
|
+
bodyFile,
|
|
3460
|
+
before: options.get("--before"),
|
|
3461
|
+
after: options.get("--after"),
|
|
3462
|
+
position
|
|
3463
|
+
}
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3116
3466
|
function parseFilter(value) {
|
|
3117
3467
|
const idx = value.indexOf("=");
|
|
3118
3468
|
if (idx <= 0)
|
|
@@ -3167,15 +3517,15 @@ function parseAnnotateArgs(argv) {
|
|
|
3167
3517
|
const arg = argv[i];
|
|
3168
3518
|
if (arg === "--help" || arg === "-h")
|
|
3169
3519
|
return { ok: true, args: { command: { kind: "help" } } };
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
i =
|
|
3520
|
+
const global = takeGlobalCliOption(argv, i, { allowServer: true });
|
|
3521
|
+
if (global.kind === "error")
|
|
3522
|
+
return { ok: false, error: global.error };
|
|
3523
|
+
if (global.kind === "cwd") {
|
|
3524
|
+
cwd = global.value;
|
|
3525
|
+
i = global.next;
|
|
3526
|
+
} else if (global.kind === "server") {
|
|
3527
|
+
server = global.value;
|
|
3528
|
+
i = global.next;
|
|
3179
3529
|
} else if (valueFlags.has(arg)) {
|
|
3180
3530
|
const taken = takeValue(argv, i, arg);
|
|
3181
3531
|
if ("error" in taken)
|
|
@@ -3220,13 +3570,9 @@ function parseAnnotateArgs(argv) {
|
|
|
3220
3570
|
if (!line)
|
|
3221
3571
|
return { ok: false, error: "--line must be <n> or <n>-<m>" };
|
|
3222
3572
|
}
|
|
3223
|
-
const
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
return { ok: false, error: "use either --body or --body-file" };
|
|
3227
|
-
const position = parsePosition(options.get("--position"));
|
|
3228
|
-
if (Number.isNaN(position))
|
|
3229
|
-
return { ok: false, error: "--position must be a positive integer" };
|
|
3573
|
+
const commonOptions = parseAnnotationAddOptions(options);
|
|
3574
|
+
if (commonOptions.ok === false)
|
|
3575
|
+
return { ok: false, error: commonOptions.error };
|
|
3230
3576
|
return {
|
|
3231
3577
|
ok: true,
|
|
3232
3578
|
args: {
|
|
@@ -3236,14 +3582,7 @@ function parseAnnotateArgs(argv) {
|
|
|
3236
3582
|
line,
|
|
3237
3583
|
from: options.get("--from"),
|
|
3238
3584
|
to: options.get("--to"),
|
|
3239
|
-
|
|
3240
|
-
session: options.get("--session"),
|
|
3241
|
-
sessionTitle: options.get("--session-title"),
|
|
3242
|
-
body,
|
|
3243
|
-
bodyFile,
|
|
3244
|
-
before: options.get("--before"),
|
|
3245
|
-
after: options.get("--after"),
|
|
3246
|
-
position
|
|
3585
|
+
...commonOptions.options
|
|
3247
3586
|
},
|
|
3248
3587
|
cwd,
|
|
3249
3588
|
server
|
|
@@ -3253,13 +3592,9 @@ function parseAnnotateArgs(argv) {
|
|
|
3253
3592
|
if (subcommand === "add-db") {
|
|
3254
3593
|
if (!options.get("--db"))
|
|
3255
3594
|
return { ok: false, error: "add-db requires --db <id>" };
|
|
3256
|
-
const
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
return { ok: false, error: "use either --body or --body-file" };
|
|
3260
|
-
const position = parsePosition(options.get("--position"));
|
|
3261
|
-
if (Number.isNaN(position))
|
|
3262
|
-
return { ok: false, error: "--position must be a positive integer" };
|
|
3595
|
+
const commonOptions = parseAnnotationAddOptions(options);
|
|
3596
|
+
if (commonOptions.ok === false)
|
|
3597
|
+
return { ok: false, error: commonOptions.error };
|
|
3263
3598
|
const rawTab = options.get("--tab");
|
|
3264
3599
|
const tab = normalizeDatabaseTab(rawTab);
|
|
3265
3600
|
if (rawTab !== undefined && tab === undefined)
|
|
@@ -3334,14 +3669,7 @@ function parseAnnotateArgs(argv) {
|
|
|
3334
3669
|
searchTerm: options.get("--search-term"),
|
|
3335
3670
|
includeNonText: flags.has("--include-non-text") || undefined,
|
|
3336
3671
|
searchAutoRun: flags.has("--run-search"),
|
|
3337
|
-
|
|
3338
|
-
session: options.get("--session"),
|
|
3339
|
-
sessionTitle: options.get("--session-title"),
|
|
3340
|
-
body,
|
|
3341
|
-
bodyFile,
|
|
3342
|
-
before: options.get("--before"),
|
|
3343
|
-
after: options.get("--after"),
|
|
3344
|
-
position
|
|
3672
|
+
...commonOptions.options
|
|
3345
3673
|
},
|
|
3346
3674
|
cwd,
|
|
3347
3675
|
server
|
|
@@ -3454,6 +3782,14 @@ function printList(state) {
|
|
|
3454
3782
|
});
|
|
3455
3783
|
}
|
|
3456
3784
|
}
|
|
3785
|
+
function printAddedAnnotation(result, location, serverUrl) {
|
|
3786
|
+
const sessionTitle = result.session_title || "Untitled session";
|
|
3787
|
+
if (result.created_session) {
|
|
3788
|
+
console.error(`created new annotation session ${result.session_id} (${sessionTitle})`);
|
|
3789
|
+
}
|
|
3790
|
+
console.log(`annotated ${location} ` + `[${result.entry.id}] in session ${result.session_id} (${sessionTitle})`);
|
|
3791
|
+
console.error(`view annotations at ${serverUrl}/ with the code annotations panel`);
|
|
3792
|
+
}
|
|
3457
3793
|
async function annotationBodyFromCommand(command) {
|
|
3458
3794
|
let body = command.body;
|
|
3459
3795
|
if (body === undefined && command.bodyFile !== undefined) {
|
|
@@ -3514,11 +3850,7 @@ async function runAnnotateCli(argv) {
|
|
|
3514
3850
|
after_id: command.after,
|
|
3515
3851
|
position: command.position
|
|
3516
3852
|
});
|
|
3517
|
-
|
|
3518
|
-
console.error(`created new annotation session ${result.session_id} (${result.session_title || "Untitled session"})`);
|
|
3519
|
-
}
|
|
3520
|
-
console.log(`annotated ${result.entry.path}${formatLine(result.entry.line)} ` + `[${result.entry.id}] in session ${result.session_id} (${result.session_title || "Untitled session"})`);
|
|
3521
|
-
console.error(`view annotations at ${serverUrl}/ with the code annotations panel`);
|
|
3853
|
+
printAddedAnnotation(result, `${result.entry.path}${formatLine(result.entry.line)}`, serverUrl);
|
|
3522
3854
|
return;
|
|
3523
3855
|
}
|
|
3524
3856
|
if (command.kind === "add-db") {
|
|
@@ -3568,11 +3900,7 @@ async function runAnnotateCli(argv) {
|
|
|
3568
3900
|
after_id: command.after,
|
|
3569
3901
|
position: command.position
|
|
3570
3902
|
});
|
|
3571
|
-
|
|
3572
|
-
console.error(`created new annotation session ${result.session_id} (${result.session_title || "Untitled session"})`);
|
|
3573
|
-
}
|
|
3574
|
-
console.log(`annotated ${result.entry.path} ` + `[${result.entry.id}] in session ${result.session_id} (${result.session_title || "Untitled session"})`);
|
|
3575
|
-
console.error(`view annotations at ${serverUrl}/ with the code annotations panel`);
|
|
3903
|
+
printAddedAnnotation(result, result.entry.path, serverUrl);
|
|
3576
3904
|
return;
|
|
3577
3905
|
}
|
|
3578
3906
|
if (command.kind === "list") {
|
|
@@ -4108,8 +4436,8 @@ __export(exports_file_cli, {
|
|
|
4108
4436
|
FILE_DEFAULT_HISTORY_LIMIT: () => FILE_DEFAULT_HISTORY_LIMIT,
|
|
4109
4437
|
FILE_AGENT_HELP: () => FILE_AGENT_HELP
|
|
4110
4438
|
});
|
|
4111
|
-
import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as
|
|
4112
|
-
import { join as
|
|
4439
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
|
|
4440
|
+
import { join as join6, relative as relative3 } from "node:path";
|
|
4113
4441
|
function validatePath(value) {
|
|
4114
4442
|
return validateRepoRelativePathValue(value, "--path");
|
|
4115
4443
|
}
|
|
@@ -4133,23 +4461,17 @@ function parseFileArgs(argv) {
|
|
|
4133
4461
|
if (arg === "--help" || arg === "-h") {
|
|
4134
4462
|
return { ok: true, args: { command: { kind: "help" } } };
|
|
4135
4463
|
}
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
"git"
|
|
4148
|
-
]);
|
|
4149
|
-
if (parsed.ok === false)
|
|
4150
|
-
return { ok: false, error: parsed.error };
|
|
4151
|
-
commandOverrides.push(parsed.override);
|
|
4152
|
-
i = taken.next;
|
|
4464
|
+
const global = takeGlobalCliOption(argv, i, {
|
|
4465
|
+
allowedCommands: ["git"]
|
|
4466
|
+
});
|
|
4467
|
+
if (global.kind === "error")
|
|
4468
|
+
return { ok: false, error: global.error };
|
|
4469
|
+
if (global.kind === "cwd") {
|
|
4470
|
+
cwd = global.value;
|
|
4471
|
+
i = global.next;
|
|
4472
|
+
} else if (global.kind === "command-override") {
|
|
4473
|
+
commandOverrides.push(global.override);
|
|
4474
|
+
i = global.next;
|
|
4153
4475
|
} else if (VALUE_FLAGS.has(arg)) {
|
|
4154
4476
|
const taken = takeValue(argv, i, arg);
|
|
4155
4477
|
if ("error" in taken)
|
|
@@ -4513,13 +4835,13 @@ function sliceLines(text, start, end) {
|
|
|
4513
4835
|
function safeWorktreePathFromRoot(root, path) {
|
|
4514
4836
|
if (validatePath(path))
|
|
4515
4837
|
return null;
|
|
4516
|
-
const full =
|
|
4838
|
+
const full = join6(root, path);
|
|
4517
4839
|
if (!existsSync3(full))
|
|
4518
4840
|
return null;
|
|
4519
4841
|
try {
|
|
4520
|
-
const realRoot =
|
|
4521
|
-
const realFull =
|
|
4522
|
-
const rel =
|
|
4842
|
+
const realRoot = realpathSync4(root);
|
|
4843
|
+
const realFull = realpathSync4(full);
|
|
4844
|
+
const rel = relative3(realRoot, realFull);
|
|
4523
4845
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/") || rel.startsWith("\\")) {
|
|
4524
4846
|
return null;
|
|
4525
4847
|
}
|
|
@@ -5391,26 +5713,21 @@ function parseJournalArgs(argv) {
|
|
|
5391
5713
|
const arg = argv[i];
|
|
5392
5714
|
if (arg === "--help" || arg === "-h")
|
|
5393
5715
|
return { ok: true, args: { command: { kind: "help" }, dryRun: false } };
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
i =
|
|
5403
|
-
} else if (
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
]);
|
|
5410
|
-
if (parsed.ok === false)
|
|
5411
|
-
return { ok: false, error: parsed.error };
|
|
5412
|
-
commandOverrides.push(parsed.override);
|
|
5413
|
-
i = taken.next;
|
|
5716
|
+
const global = takeGlobalCliOption(argv, i, {
|
|
5717
|
+
allowServer: true,
|
|
5718
|
+
allowedCommands: ["gh"]
|
|
5719
|
+
});
|
|
5720
|
+
if (global.kind === "error")
|
|
5721
|
+
return { ok: false, error: global.error };
|
|
5722
|
+
if (global.kind === "cwd") {
|
|
5723
|
+
cwd = global.value;
|
|
5724
|
+
i = global.next;
|
|
5725
|
+
} else if (global.kind === "server") {
|
|
5726
|
+
server = global.value;
|
|
5727
|
+
i = global.next;
|
|
5728
|
+
} else if (global.kind === "command-override") {
|
|
5729
|
+
commandOverrides.push(global.override);
|
|
5730
|
+
i = global.next;
|
|
5414
5731
|
} else if (valueFlags.has(arg)) {
|
|
5415
5732
|
const taken = takeValue(argv, i, arg);
|
|
5416
5733
|
if ("error" in taken)
|
|
@@ -6138,162 +6455,61 @@ Usage:
|
|
|
6138
6455
|
[--priority <p0|p1|p2|p3>] [--label <label>...] [--clear-labels]
|
|
6139
6456
|
[--due <YYYY-MM-DD|none>] [--source-date <YYYY-MM-DD|today|none>]
|
|
6140
6457
|
[--body <markdown> | --body-file <path>]
|
|
6141
|
-
code-viewer journal task-next [--label <label>...] [--status <status>]
|
|
6142
|
-
[--limit <n>] [--json]
|
|
6143
|
-
code-viewer journal github-issues [--repo <owner/repo>]
|
|
6144
|
-
[--state <open|closed|all>] [--gh-label <label>...]
|
|
6145
|
-
[--search <query>] [--limit <n>] [--json] [--bin gh=<path>]
|
|
6146
|
-
code-viewer journal task-link-issue <number> [--repo <owner/repo>]
|
|
6147
|
-
[--status <status>] [--priority <p0|p1|p2|p3>] [--label <label>...]
|
|
6148
|
-
[--before <id> | --after <id> | --position <n>] [--json]
|
|
6149
|
-
[--dry-run] [--bin gh=<path>]
|
|
6150
|
-
code-viewer journal task-claim <id> [--by <agent>] [--lease-minutes <n>]
|
|
6151
|
-
[--wip-limit <n>]
|
|
6152
|
-
code-viewer journal task-done <id> --by <agent>
|
|
6153
|
-
[--note <markdown> | --note-file <path>]
|
|
6154
|
-
code-viewer journal task-delete <id>
|
|
6155
|
-
|
|
6156
|
-
Global options:
|
|
6157
|
-
--cwd <dir> repository root (default: current directory)
|
|
6158
|
-
--server <url> code-viewer server URL (default: auto-discovered)
|
|
6159
|
-
--bin gh=<p> override gh executable path for github-issues
|
|
6160
|
-
--dry-run print the write payload without sending it
|
|
6161
|
-
`, JOURNAL_AGENT_HELP = `code-viewer journal — agent guide
|
|
6162
|
-
|
|
6163
|
-
You are an AI coding agent. Use this tool to write daily work notes and process
|
|
6164
|
-
explicit task queues without guessing from memory.
|
|
6165
|
-
|
|
6166
|
-
## Workflow
|
|
6167
|
-
|
|
6168
|
-
1. Check the queue before doing label-scoped work:
|
|
6169
|
-
code-viewer journal task-next --label ai-ready --limit 5 --json
|
|
6170
|
-
2. Claim exactly one task before editing code:
|
|
6171
|
-
code-viewer journal task-claim <task-id> --by agent --wip-limit 1
|
|
6172
|
-
3. When finished, mark it done with a short note:
|
|
6173
|
-
code-viewer journal task-done <task-id> --by agent --note "Implemented and verified."
|
|
6174
|
-
4. Add a daily journal entry for work that is not already represented by a task:
|
|
6175
|
-
code-viewer journal add --date today --label ai --body "..."
|
|
6176
|
-
5. Inspect GitHub issues read-only before deciding what local task to create:
|
|
6177
|
-
code-viewer journal github-issues --repo owner/repo --json
|
|
6178
|
-
6. Link a GitHub issue to the local board without updating GitHub:
|
|
6179
|
-
code-viewer journal task-link-issue 123 --repo owner/repo --status draft --label ai-ready
|
|
6180
|
-
|
|
6181
|
-
## Rules
|
|
6182
|
-
|
|
6183
|
-
- Treat labels as filters, priority as importance, and card order as human order.
|
|
6184
|
-
- Do not process draft tasks unless the human asked for drafts.
|
|
6185
|
-
- Use task-next for ordering; do not sort the JSON yourself unless asked.
|
|
6186
|
-
- Use --dry-run before large generated entries.
|
|
6187
|
-
- GitHub issue listing is read-only. Create or update local tasks explicitly.
|
|
6188
|
-
- task-link-issue reads issue metadata only and stores a local task link plus
|
|
6189
|
-
your local labels. It does not copy the issue body or update GitHub.
|
|
6190
|
-
`;
|
|
6191
|
-
var init_journal_cli = __esm(() => {
|
|
6192
|
-
init_journal();
|
|
6193
|
-
init_cli_helpers();
|
|
6194
|
-
init_command_resolver();
|
|
6195
|
-
init_github_issues();
|
|
6196
|
-
});
|
|
6197
|
-
|
|
6198
|
-
// web-src/core/routes.ts
|
|
6199
|
-
function assertNever(value) {
|
|
6200
|
-
throw new Error(`unhandled route: ${JSON.stringify(value)}`);
|
|
6201
|
-
}
|
|
6202
|
-
function formatLineTarget(line) {
|
|
6203
|
-
return typeof line === "number" ? String(line) : `${line.start}-${line.end}`;
|
|
6204
|
-
}
|
|
6205
|
-
function buildRoute(route) {
|
|
6206
|
-
switch (route.screen) {
|
|
6207
|
-
case "repo": {
|
|
6208
|
-
const params = new URLSearchParams;
|
|
6209
|
-
if (route.ref && route.ref !== "worktree")
|
|
6210
|
-
params.set("ref", route.ref);
|
|
6211
|
-
if (route.path)
|
|
6212
|
-
params.set("path", route.path);
|
|
6213
|
-
const qs = params.toString();
|
|
6214
|
-
return `/${qs ? `?${qs}` : ""}`;
|
|
6215
|
-
}
|
|
6216
|
-
case "file":
|
|
6217
|
-
if (route.view === "blob") {
|
|
6218
|
-
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=blob" + (route.preview ? "&preview=1" : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
6219
|
-
}
|
|
6220
|
-
if (route.view === "blame") {
|
|
6221
|
-
const ref = route.ref || "worktree";
|
|
6222
|
-
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(ref) + "&view=blame" + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
6223
|
-
}
|
|
6224
|
-
if (route.view === "history") {
|
|
6225
|
-
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=history" + (route.commit ? `&commit=${encodeURIComponent(route.commit)}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
6226
|
-
}
|
|
6227
|
-
return "/file?path=" + encodeURIComponent(route.path) + "&ref=" + encodeURIComponent(route.ref || "worktree") + "&from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
6228
|
-
case "diff":
|
|
6229
|
-
return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.path ? `&path=${encodeURIComponent(route.path)}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
6230
|
-
case "help": {
|
|
6231
|
-
const params = new URLSearchParams;
|
|
6232
|
-
if (route.lang && route.lang !== "en")
|
|
6233
|
-
params.set("lang", route.lang);
|
|
6234
|
-
if (route.section && route.section !== "overview")
|
|
6235
|
-
params.set("section", route.section);
|
|
6236
|
-
const qs = params.toString();
|
|
6237
|
-
return `/help${qs ? `?${qs}` : ""}`;
|
|
6238
|
-
}
|
|
6239
|
-
case "history": {
|
|
6240
|
-
const params = new URLSearchParams;
|
|
6241
|
-
if (route.ref && route.ref !== "HEAD")
|
|
6242
|
-
params.set("ref", route.ref);
|
|
6243
|
-
if (route.commit)
|
|
6244
|
-
params.set("commit", route.commit);
|
|
6245
|
-
const qs = params.toString();
|
|
6246
|
-
return `/history${qs ? `?${qs}` : ""}`;
|
|
6247
|
-
}
|
|
6248
|
-
case "journal": {
|
|
6249
|
-
const params = new URLSearchParams;
|
|
6250
|
-
if (route.tab && route.tab !== "journal")
|
|
6251
|
-
params.set("tab", route.tab);
|
|
6252
|
-
if (route.date)
|
|
6253
|
-
params.set("date", route.date);
|
|
6254
|
-
if (route.label)
|
|
6255
|
-
params.set("label", route.label);
|
|
6256
|
-
if (route.task)
|
|
6257
|
-
params.set("task", route.task);
|
|
6258
|
-
const qs = params.toString();
|
|
6259
|
-
return `/journal${qs ? `?${qs}` : ""}`;
|
|
6260
|
-
}
|
|
6261
|
-
case "database": {
|
|
6262
|
-
const params = new URLSearchParams;
|
|
6263
|
-
if (route.db)
|
|
6264
|
-
params.set("db", route.db);
|
|
6265
|
-
if (route.schema)
|
|
6266
|
-
params.set("schema", route.schema);
|
|
6267
|
-
if (route.table)
|
|
6268
|
-
params.set("table", route.table);
|
|
6269
|
-
if (route.tab)
|
|
6270
|
-
params.set("tab", route.tab);
|
|
6271
|
-
if (route.diffBefore)
|
|
6272
|
-
params.set("diffBefore", route.diffBefore);
|
|
6273
|
-
if (route.diffAfter)
|
|
6274
|
-
params.set("diffAfter", route.diffAfter);
|
|
6275
|
-
const qs = params.toString();
|
|
6276
|
-
return `/database${qs ? `?${qs}` : ""}`;
|
|
6277
|
-
}
|
|
6278
|
-
case "unknown":
|
|
6279
|
-
return "/todif?from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree");
|
|
6280
|
-
default:
|
|
6281
|
-
return assertNever(route);
|
|
6282
|
-
}
|
|
6283
|
-
}
|
|
6284
|
-
var SPA_PATHS, APP_ENTRY_PATHS;
|
|
6285
|
-
var init_routes = __esm(() => {
|
|
6286
|
-
SPA_PATHS = [
|
|
6287
|
-
"/todif",
|
|
6288
|
-
"/todiff",
|
|
6289
|
-
"/file",
|
|
6290
|
-
"/help",
|
|
6291
|
-
"/history",
|
|
6292
|
-
"/journal",
|
|
6293
|
-
"/database",
|
|
6294
|
-
"/doctor"
|
|
6295
|
-
];
|
|
6296
|
-
APP_ENTRY_PATHS = ["/", "/index.html"];
|
|
6458
|
+
code-viewer journal task-next [--label <label>...] [--status <status>]
|
|
6459
|
+
[--limit <n>] [--json]
|
|
6460
|
+
code-viewer journal github-issues [--repo <owner/repo>]
|
|
6461
|
+
[--state <open|closed|all>] [--gh-label <label>...]
|
|
6462
|
+
[--search <query>] [--limit <n>] [--json] [--bin gh=<path>]
|
|
6463
|
+
code-viewer journal task-link-issue <number> [--repo <owner/repo>]
|
|
6464
|
+
[--status <status>] [--priority <p0|p1|p2|p3>] [--label <label>...]
|
|
6465
|
+
[--before <id> | --after <id> | --position <n>] [--json]
|
|
6466
|
+
[--dry-run] [--bin gh=<path>]
|
|
6467
|
+
code-viewer journal task-claim <id> [--by <agent>] [--lease-minutes <n>]
|
|
6468
|
+
[--wip-limit <n>]
|
|
6469
|
+
code-viewer journal task-done <id> --by <agent>
|
|
6470
|
+
[--note <markdown> | --note-file <path>]
|
|
6471
|
+
code-viewer journal task-delete <id>
|
|
6472
|
+
|
|
6473
|
+
Global options:
|
|
6474
|
+
--cwd <dir> repository root (default: current directory)
|
|
6475
|
+
--server <url> code-viewer server URL (default: auto-discovered)
|
|
6476
|
+
--bin gh=<p> override gh executable path for github-issues
|
|
6477
|
+
--dry-run print the write payload without sending it
|
|
6478
|
+
`, JOURNAL_AGENT_HELP = `code-viewer journal — agent guide
|
|
6479
|
+
|
|
6480
|
+
You are an AI coding agent. Use this tool to write daily work notes and process
|
|
6481
|
+
explicit task queues without guessing from memory.
|
|
6482
|
+
|
|
6483
|
+
## Workflow
|
|
6484
|
+
|
|
6485
|
+
1. Check the queue before doing label-scoped work:
|
|
6486
|
+
code-viewer journal task-next --label ai-ready --limit 5 --json
|
|
6487
|
+
2. Claim exactly one task before editing code:
|
|
6488
|
+
code-viewer journal task-claim <task-id> --by agent --wip-limit 1
|
|
6489
|
+
3. When finished, mark it done with a short note:
|
|
6490
|
+
code-viewer journal task-done <task-id> --by agent --note "Implemented and verified."
|
|
6491
|
+
4. Add a daily journal entry for work that is not already represented by a task:
|
|
6492
|
+
code-viewer journal add --date today --label ai --body "..."
|
|
6493
|
+
5. Inspect GitHub issues read-only before deciding what local task to create:
|
|
6494
|
+
code-viewer journal github-issues --repo owner/repo --json
|
|
6495
|
+
6. Link a GitHub issue to the local board without updating GitHub:
|
|
6496
|
+
code-viewer journal task-link-issue 123 --repo owner/repo --status draft --label ai-ready
|
|
6497
|
+
|
|
6498
|
+
## Rules
|
|
6499
|
+
|
|
6500
|
+
- Treat labels as filters, priority as importance, and card order as human order.
|
|
6501
|
+
- Do not process draft tasks unless the human asked for drafts.
|
|
6502
|
+
- Use task-next for ordering; do not sort the JSON yourself unless asked.
|
|
6503
|
+
- Use --dry-run before large generated entries.
|
|
6504
|
+
- GitHub issue listing is read-only. Create or update local tasks explicitly.
|
|
6505
|
+
- task-link-issue reads issue metadata only and stores a local task link plus
|
|
6506
|
+
your local labels. It does not copy the issue body or update GitHub.
|
|
6507
|
+
`;
|
|
6508
|
+
var init_journal_cli = __esm(() => {
|
|
6509
|
+
init_journal();
|
|
6510
|
+
init_cli_helpers();
|
|
6511
|
+
init_command_resolver();
|
|
6512
|
+
init_github_issues();
|
|
6297
6513
|
});
|
|
6298
6514
|
|
|
6299
6515
|
// web-src/server/query-cli.ts
|
|
@@ -6338,26 +6554,21 @@ function parseQueryArgs(argv) {
|
|
|
6338
6554
|
const arg = argv[i];
|
|
6339
6555
|
if (arg === "--help" || arg === "-h")
|
|
6340
6556
|
return { ok: true, args: { command: { kind: "help" } } };
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
i =
|
|
6350
|
-
} else if (
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
]);
|
|
6357
|
-
if (parsed.ok === false)
|
|
6358
|
-
return { ok: false, error: parsed.error };
|
|
6359
|
-
commandOverrides.push(parsed.override);
|
|
6360
|
-
i = taken.next;
|
|
6557
|
+
const global = takeGlobalCliOption(argv, i, {
|
|
6558
|
+
allowServer: true,
|
|
6559
|
+
allowedCommands: ["git"]
|
|
6560
|
+
});
|
|
6561
|
+
if (global.kind === "error")
|
|
6562
|
+
return { ok: false, error: global.error };
|
|
6563
|
+
if (global.kind === "cwd") {
|
|
6564
|
+
cwd = global.value;
|
|
6565
|
+
i = global.next;
|
|
6566
|
+
} else if (global.kind === "server") {
|
|
6567
|
+
server = global.value;
|
|
6568
|
+
i = global.next;
|
|
6569
|
+
} else if (global.kind === "command-override") {
|
|
6570
|
+
commandOverrides.push(global.override);
|
|
6571
|
+
i = global.next;
|
|
6361
6572
|
} else if (VALUE_FLAGS2.has(arg)) {
|
|
6362
6573
|
const taken = takeValue(argv, i, arg);
|
|
6363
6574
|
if ("error" in taken)
|
|
@@ -8996,6 +9207,18 @@ __export(exports_search_cli, {
|
|
|
8996
9207
|
SEARCH_AGENT_HELP: () => SEARCH_AGENT_HELP,
|
|
8997
9208
|
FILE_NAME_SEARCH_DEFAULT_MAX: () => FILE_NAME_SEARCH_DEFAULT_MAX
|
|
8998
9209
|
});
|
|
9210
|
+
function parseSearchMax(raw, hardCap) {
|
|
9211
|
+
if (raw === undefined)
|
|
9212
|
+
return { value: undefined };
|
|
9213
|
+
const value = Number(raw);
|
|
9214
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
9215
|
+
return { error: `--max must be a positive integer (got ${raw})` };
|
|
9216
|
+
}
|
|
9217
|
+
if (value > hardCap) {
|
|
9218
|
+
return { error: `--max must be <= ${hardCap} (got ${value})` };
|
|
9219
|
+
}
|
|
9220
|
+
return { value };
|
|
9221
|
+
}
|
|
8999
9222
|
function parseSearchArgs(argv) {
|
|
9000
9223
|
const rest = [];
|
|
9001
9224
|
let cwd;
|
|
@@ -9009,26 +9232,21 @@ function parseSearchArgs(argv) {
|
|
|
9009
9232
|
if (arg === "--help" || arg === "-h") {
|
|
9010
9233
|
return { ok: true, args: { command: { kind: "help" } } };
|
|
9011
9234
|
}
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
i =
|
|
9021
|
-
} else if (
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
]);
|
|
9028
|
-
if (parsed.ok === false)
|
|
9029
|
-
return { ok: false, error: parsed.error };
|
|
9030
|
-
commandOverrides.push(parsed.override);
|
|
9031
|
-
i = taken.next;
|
|
9235
|
+
const global = takeGlobalCliOption(argv, i, {
|
|
9236
|
+
allowServer: true,
|
|
9237
|
+
allowedCommands: ["git"]
|
|
9238
|
+
});
|
|
9239
|
+
if (global.kind === "error")
|
|
9240
|
+
return { ok: false, error: global.error };
|
|
9241
|
+
if (global.kind === "cwd") {
|
|
9242
|
+
cwd = global.value;
|
|
9243
|
+
i = global.next;
|
|
9244
|
+
} else if (global.kind === "server") {
|
|
9245
|
+
server = global.value;
|
|
9246
|
+
i = global.next;
|
|
9247
|
+
} else if (global.kind === "command-override") {
|
|
9248
|
+
commandOverrides.push(global.override);
|
|
9249
|
+
i = global.next;
|
|
9032
9250
|
} else if (REPEATABLE_VALUE_FLAGS.has(arg)) {
|
|
9033
9251
|
const taken = takeValue(argv, i, arg);
|
|
9034
9252
|
if ("error" in taken)
|
|
@@ -9087,24 +9305,10 @@ function parseSearchArgs(argv) {
|
|
|
9087
9305
|
error: "search files does not accept --path"
|
|
9088
9306
|
};
|
|
9089
9307
|
}
|
|
9090
|
-
const
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
9095
|
-
return {
|
|
9096
|
-
ok: false,
|
|
9097
|
-
error: `--max must be a positive integer (got ${maxRaw2})`
|
|
9098
|
-
};
|
|
9099
|
-
}
|
|
9100
|
-
if (n > FILE_SEARCH_ABSOLUTE_MAX) {
|
|
9101
|
-
return {
|
|
9102
|
-
ok: false,
|
|
9103
|
-
error: `--max must be <= ${FILE_SEARCH_ABSOLUTE_MAX} (got ${n})`
|
|
9104
|
-
};
|
|
9105
|
-
}
|
|
9106
|
-
max2 = n;
|
|
9107
|
-
}
|
|
9308
|
+
const parsedMax2 = parseSearchMax(options.get("--max"), FILE_SEARCH_ABSOLUTE_MAX);
|
|
9309
|
+
if ("error" in parsedMax2)
|
|
9310
|
+
return { ok: false, error: parsedMax2.error };
|
|
9311
|
+
const max = parsedMax2.value ?? FILE_NAME_SEARCH_DEFAULT_MAX;
|
|
9108
9312
|
return {
|
|
9109
9313
|
ok: true,
|
|
9110
9314
|
args: {
|
|
@@ -9112,7 +9316,7 @@ function parseSearchArgs(argv) {
|
|
|
9112
9316
|
kind: "files",
|
|
9113
9317
|
term,
|
|
9114
9318
|
ref: options.get("--ref"),
|
|
9115
|
-
max
|
|
9319
|
+
max,
|
|
9116
9320
|
json: flags.has("--json")
|
|
9117
9321
|
},
|
|
9118
9322
|
cwd,
|
|
@@ -9121,24 +9325,9 @@ function parseSearchArgs(argv) {
|
|
|
9121
9325
|
}
|
|
9122
9326
|
};
|
|
9123
9327
|
}
|
|
9124
|
-
const
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
const n = Number(maxRaw);
|
|
9128
|
-
if (!Number.isInteger(n) || n <= 0) {
|
|
9129
|
-
return {
|
|
9130
|
-
ok: false,
|
|
9131
|
-
error: `--max must be a positive integer (got ${maxRaw})`
|
|
9132
|
-
};
|
|
9133
|
-
}
|
|
9134
|
-
if (n > GREP_ABSOLUTE_MAX) {
|
|
9135
|
-
return {
|
|
9136
|
-
ok: false,
|
|
9137
|
-
error: `--max must be <= ${GREP_ABSOLUTE_MAX} (got ${n})`
|
|
9138
|
-
};
|
|
9139
|
-
}
|
|
9140
|
-
max = n;
|
|
9141
|
-
}
|
|
9328
|
+
const parsedMax = parseSearchMax(options.get("--max"), GREP_ABSOLUTE_MAX);
|
|
9329
|
+
if ("error" in parsedMax)
|
|
9330
|
+
return { ok: false, error: parsedMax.error };
|
|
9142
9331
|
return {
|
|
9143
9332
|
ok: true,
|
|
9144
9333
|
args: {
|
|
@@ -9148,7 +9337,7 @@ function parseSearchArgs(argv) {
|
|
|
9148
9337
|
ref: options.get("--ref"),
|
|
9149
9338
|
paths,
|
|
9150
9339
|
regex: flags.has("--regex"),
|
|
9151
|
-
max,
|
|
9340
|
+
max: parsedMax.value,
|
|
9152
9341
|
json: flags.has("--json")
|
|
9153
9342
|
},
|
|
9154
9343
|
cwd,
|
|
@@ -9423,24 +9612,24 @@ Parse failures and unreachable servers exit 1.
|
|
|
9423
9612
|
|
|
9424
9613
|
// web-src/server/root.ts
|
|
9425
9614
|
import { existsSync as existsSync4 } from "node:fs";
|
|
9426
|
-
import { dirname as
|
|
9615
|
+
import { dirname as dirname4, join as join7, normalize } from "node:path";
|
|
9427
9616
|
import { fileURLToPath } from "node:url";
|
|
9428
9617
|
function findRoot(start) {
|
|
9429
9618
|
let current = start;
|
|
9430
9619
|
for (let i = 0;i < 5; i++) {
|
|
9431
|
-
if (existsSync4(
|
|
9620
|
+
if (existsSync4(join7(current, "package.json")) && existsSync4(join7(current, "web"))) {
|
|
9432
9621
|
return normalize(current);
|
|
9433
9622
|
}
|
|
9434
|
-
const parent =
|
|
9623
|
+
const parent = dirname4(current);
|
|
9435
9624
|
if (parent === current)
|
|
9436
9625
|
break;
|
|
9437
9626
|
current = parent;
|
|
9438
9627
|
}
|
|
9439
|
-
return normalize(
|
|
9628
|
+
return normalize(join7(start, "..", ".."));
|
|
9440
9629
|
}
|
|
9441
9630
|
var ROOT;
|
|
9442
9631
|
var init_root = __esm(() => {
|
|
9443
|
-
ROOT = findRoot(
|
|
9632
|
+
ROOT = findRoot(dirname4(fileURLToPath(import.meta.url)));
|
|
9444
9633
|
});
|
|
9445
9634
|
|
|
9446
9635
|
// web-src/server/skill-cli.ts
|
|
@@ -9455,7 +9644,7 @@ __export(exports_skill_cli, {
|
|
|
9455
9644
|
});
|
|
9456
9645
|
import { cpSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
9457
9646
|
import { homedir as homedir2 } from "node:os";
|
|
9458
|
-
import { join as
|
|
9647
|
+
import { join as join8, resolve } from "node:path";
|
|
9459
9648
|
function parseAgentList(value) {
|
|
9460
9649
|
if (value === "all")
|
|
9461
9650
|
return [...AGENT_NAMES];
|
|
@@ -9515,7 +9704,7 @@ function parseSkillArgs(argv) {
|
|
|
9515
9704
|
function discoverBundledSkills(skillsRoot) {
|
|
9516
9705
|
if (!existsSync5(skillsRoot))
|
|
9517
9706
|
return [];
|
|
9518
|
-
return readdirSync2(skillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync5(
|
|
9707
|
+
return readdirSync2(skillsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync5(join8(skillsRoot, entry.name, "SKILL.md"))).map((entry) => entry.name).sort();
|
|
9519
9708
|
}
|
|
9520
9709
|
function installSkill(args, deps) {
|
|
9521
9710
|
const skills = discoverBundledSkills(deps.skillsRoot);
|
|
@@ -9529,8 +9718,8 @@ function installSkill(args, deps) {
|
|
|
9529
9718
|
const results = [];
|
|
9530
9719
|
for (const agent of args.agents) {
|
|
9531
9720
|
for (const skill of skills) {
|
|
9532
|
-
const sourceDir =
|
|
9533
|
-
const target =
|
|
9721
|
+
const sourceDir = join8(deps.skillsRoot, skill);
|
|
9722
|
+
const target = join8(base, AGENT_SKILL_DIRS[agent], "skills", skill);
|
|
9534
9723
|
const action = existsSync5(target) ? "updated" : "installed";
|
|
9535
9724
|
try {
|
|
9536
9725
|
mkdirSync2(target, { recursive: true });
|
|
@@ -9559,7 +9748,7 @@ function runSkillCli(argv) {
|
|
|
9559
9748
|
return;
|
|
9560
9749
|
}
|
|
9561
9750
|
const result = installSkill(parsed.args, {
|
|
9562
|
-
skillsRoot:
|
|
9751
|
+
skillsRoot: join8(ROOT, "skills"),
|
|
9563
9752
|
homeDir: homedir2(),
|
|
9564
9753
|
projectDir: process.cwd()
|
|
9565
9754
|
});
|
|
@@ -9700,23 +9889,17 @@ function parseStatusArgs(argv) {
|
|
|
9700
9889
|
if (arg === "--help" || arg === "-h") {
|
|
9701
9890
|
return { ok: true, args: { command: { kind: "help" } } };
|
|
9702
9891
|
}
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
"git"
|
|
9715
|
-
]);
|
|
9716
|
-
if (parsed.ok === false)
|
|
9717
|
-
return { ok: false, error: parsed.error };
|
|
9718
|
-
commandOverrides.push(parsed.override);
|
|
9719
|
-
i = taken.next;
|
|
9892
|
+
const global = takeGlobalCliOption(argv, i, {
|
|
9893
|
+
allowedCommands: ["git"]
|
|
9894
|
+
});
|
|
9895
|
+
if (global.kind === "error")
|
|
9896
|
+
return { ok: false, error: global.error };
|
|
9897
|
+
if (global.kind === "cwd") {
|
|
9898
|
+
cwd = global.value;
|
|
9899
|
+
i = global.next;
|
|
9900
|
+
} else if (global.kind === "command-override") {
|
|
9901
|
+
commandOverrides.push(global.override);
|
|
9902
|
+
i = global.next;
|
|
9720
9903
|
} else if (VALUE_FLAGS4.has(arg)) {
|
|
9721
9904
|
const taken = takeValue(argv, i, arg);
|
|
9722
9905
|
if ("error" in taken)
|
|
@@ -10278,6 +10461,9 @@ function placeValue(coerced, kind, useParams, params) {
|
|
|
10278
10461
|
function coerceCell(cell, columnType) {
|
|
10279
10462
|
return coerceDbValue(cell.value, columnType);
|
|
10280
10463
|
}
|
|
10464
|
+
function formatWriteComparisons(cells, columnTypes, kind, useParams, params, separator) {
|
|
10465
|
+
return cells.map((cell) => `${sanitizeIdentifier(cell.column, kind)} = ${placeValue(coerceCell(cell, columnTypes.get(cell.column) ?? "TEXT"), kind, useParams, params)}`).join(separator);
|
|
10466
|
+
}
|
|
10281
10467
|
function buildInsertSql(table, cells, columnTypes, kind) {
|
|
10282
10468
|
if (cells.length === 0) {
|
|
10283
10469
|
throw new Error("insert requires at least one column value");
|
|
@@ -10298,8 +10484,8 @@ function buildUpdateSql(table, set, pk, columnTypes, kind) {
|
|
|
10298
10484
|
}
|
|
10299
10485
|
const useParams = useParamsFor(kind);
|
|
10300
10486
|
const params = [];
|
|
10301
|
-
const setSql = set
|
|
10302
|
-
const whereSql = pk
|
|
10487
|
+
const setSql = formatWriteComparisons(set, columnTypes, kind, useParams, params, ", ");
|
|
10488
|
+
const whereSql = formatWriteComparisons(pk, columnTypes, kind, useParams, params, " AND ");
|
|
10303
10489
|
const sql = `UPDATE ${sanitizeIdentifier(table, kind)} SET ${setSql} WHERE ${whereSql}`;
|
|
10304
10490
|
return { sql, params };
|
|
10305
10491
|
}
|
|
@@ -10309,7 +10495,7 @@ function buildDeleteSql(table, pk, columnTypes, kind) {
|
|
|
10309
10495
|
}
|
|
10310
10496
|
const useParams = useParamsFor(kind);
|
|
10311
10497
|
const params = [];
|
|
10312
|
-
const whereSql = pk
|
|
10498
|
+
const whereSql = formatWriteComparisons(pk, columnTypes, kind, useParams, params, " AND ");
|
|
10313
10499
|
const sql = `DELETE FROM ${sanitizeIdentifier(table, kind)} WHERE ${whereSql}`;
|
|
10314
10500
|
return { sql, params };
|
|
10315
10501
|
}
|
|
@@ -14623,11 +14809,11 @@ import {
|
|
|
14623
14809
|
existsSync as existsSync6,
|
|
14624
14810
|
openSync as openSync2,
|
|
14625
14811
|
readSync as readSync2,
|
|
14626
|
-
realpathSync as
|
|
14812
|
+
realpathSync as realpathSync5,
|
|
14627
14813
|
statSync as statSync4
|
|
14628
14814
|
} from "node:fs";
|
|
14629
14815
|
import { lstat, open, readdir, readFile as readFile2, stat } from "node:fs/promises";
|
|
14630
|
-
import { basename, join as
|
|
14816
|
+
import { basename, join as join9, relative as relative4 } from "node:path";
|
|
14631
14817
|
function isSqliteFile(fullPath) {
|
|
14632
14818
|
try {
|
|
14633
14819
|
const stat2 = statSync4(fullPath);
|
|
@@ -14698,7 +14884,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
14698
14884
|
return;
|
|
14699
14885
|
if (omitSet.has(entry.toLowerCase()))
|
|
14700
14886
|
continue;
|
|
14701
|
-
const full =
|
|
14887
|
+
const full = join9(dir, entry);
|
|
14702
14888
|
let entryStat;
|
|
14703
14889
|
try {
|
|
14704
14890
|
entryStat = await lstat(full);
|
|
@@ -14715,7 +14901,7 @@ async function discoverSqliteFilesAsync(cwd, omitDirNames, signal) {
|
|
|
14715
14901
|
continue;
|
|
14716
14902
|
if (!await isSqliteFileAsync(full))
|
|
14717
14903
|
continue;
|
|
14718
|
-
const rel =
|
|
14904
|
+
const rel = relative4(cwd, full);
|
|
14719
14905
|
if (rel.startsWith("..") || rel.startsWith("/"))
|
|
14720
14906
|
continue;
|
|
14721
14907
|
results.push({
|
|
@@ -14742,18 +14928,18 @@ function validateDbPath(cwd, dbPath) {
|
|
|
14742
14928
|
const parts = dbPath.split(/[\\/]+/);
|
|
14743
14929
|
if (parts.some((p) => p === ".." || p.toLowerCase() === ".git" || p.toLowerCase() === ".code-viewer"))
|
|
14744
14930
|
return null;
|
|
14745
|
-
const full =
|
|
14931
|
+
const full = join9(cwd, dbPath);
|
|
14746
14932
|
if (!existsSync6(full))
|
|
14747
14933
|
return null;
|
|
14748
14934
|
let realCwd;
|
|
14749
14935
|
let realFull;
|
|
14750
14936
|
try {
|
|
14751
|
-
realCwd =
|
|
14752
|
-
realFull =
|
|
14937
|
+
realCwd = realpathSync5(cwd);
|
|
14938
|
+
realFull = realpathSync5(full);
|
|
14753
14939
|
} catch {
|
|
14754
14940
|
return null;
|
|
14755
14941
|
}
|
|
14756
|
-
const rel =
|
|
14942
|
+
const rel = relative4(realCwd, realFull);
|
|
14757
14943
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/"))
|
|
14758
14944
|
return null;
|
|
14759
14945
|
if (!isSqliteFile(realFull))
|
|
@@ -14912,7 +15098,7 @@ function resolveEnvValue(raw, composeDirEnv = {}) {
|
|
|
14912
15098
|
}
|
|
14913
15099
|
async function readDotenvAsync(composeDir) {
|
|
14914
15100
|
try {
|
|
14915
|
-
const content = await readFile2(
|
|
15101
|
+
const content = await readFile2(join9(composeDir, ".env"), "utf-8");
|
|
14916
15102
|
return parseDotenvContent(content);
|
|
14917
15103
|
} catch {
|
|
14918
15104
|
return {};
|
|
@@ -15034,7 +15220,7 @@ function parseComposeContent(content, filepath, composeDir, cwd, composeDirEnv,
|
|
|
15034
15220
|
for (let match = serviceRegex.exec(servicesBlock);match !== null; match = serviceRegex.exec(servicesBlock)) {
|
|
15035
15221
|
servicePositions.push({ name: match[1], start: match.index });
|
|
15036
15222
|
}
|
|
15037
|
-
const relDir =
|
|
15223
|
+
const relDir = relative4(cwd, composeDir);
|
|
15038
15224
|
const isRoot = relDir === "" || relDir === ".";
|
|
15039
15225
|
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
15040
15226
|
const filename = basename(filepath);
|
|
@@ -15139,7 +15325,7 @@ async function walkForMarkerFileAsync(dir, depth, omitSet, hasCapacity, visitDir
|
|
|
15139
15325
|
return;
|
|
15140
15326
|
if (omitSet.has(entry.toLowerCase()))
|
|
15141
15327
|
continue;
|
|
15142
|
-
const full =
|
|
15328
|
+
const full = join9(dir, entry);
|
|
15143
15329
|
let entryStat;
|
|
15144
15330
|
try {
|
|
15145
15331
|
entryStat = await lstat(full);
|
|
@@ -15166,7 +15352,7 @@ async function discoverDockerDatabasesAsync(cwd, omitDirNames = [], signal) {
|
|
|
15166
15352
|
omitSet.add("node_modules");
|
|
15167
15353
|
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_DOCKER_SERVICES, async (dir) => {
|
|
15168
15354
|
for (const filename of COMPOSE_FILENAMES) {
|
|
15169
|
-
const filepath =
|
|
15355
|
+
const filepath = join9(dir, filename);
|
|
15170
15356
|
if (await pathExistsAsync(filepath)) {
|
|
15171
15357
|
await parseComposeFileAsync(filepath, dir, cwd, results);
|
|
15172
15358
|
break;
|
|
@@ -15330,7 +15516,7 @@ async function discoverSupabaseCliProjectsAsync(cwd, omitDirNames = [], signal)
|
|
|
15330
15516
|
omitSet.add("node_modules");
|
|
15331
15517
|
const results = [];
|
|
15332
15518
|
await walkForMarkerFileAsync(cwd, 0, omitSet, () => results.length < MAX_SUPABASE_PROJECTS, async (dir) => {
|
|
15333
|
-
const configPath =
|
|
15519
|
+
const configPath = join9(dir, "supabase", "config.toml");
|
|
15334
15520
|
if (!await pathExistsAsync(configPath))
|
|
15335
15521
|
return;
|
|
15336
15522
|
try {
|
|
@@ -15338,7 +15524,7 @@ async function discoverSupabaseCliProjectsAsync(cwd, omitDirNames = [], signal)
|
|
|
15338
15524
|
const parsed = parseSupabaseConfigToml(content);
|
|
15339
15525
|
if (!parsed)
|
|
15340
15526
|
return;
|
|
15341
|
-
const relDir =
|
|
15527
|
+
const relDir = relative4(cwd, dir);
|
|
15342
15528
|
const isRoot = relDir === "" || relDir === ".";
|
|
15343
15529
|
const relDirSlash = relDir.replace(/\\/g, "/");
|
|
15344
15530
|
const id = isRoot ? `supabase:${parsed.projectId}` : `supabase:${parsed.projectId}@${encodeURIComponent(relDirSlash)}`;
|
|
@@ -15420,35 +15606,18 @@ var init_discovery = __esm(() => {
|
|
|
15420
15606
|
supabaseDiscoveryCache = new Map;
|
|
15421
15607
|
});
|
|
15422
15608
|
|
|
15423
|
-
// web-src/core/id.ts
|
|
15424
|
-
function bytesToHex(bytes) {
|
|
15425
|
-
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
15426
|
-
}
|
|
15427
|
-
function makeId(prefix) {
|
|
15428
|
-
const cryptoApi = globalThis.crypto;
|
|
15429
|
-
if (typeof cryptoApi?.randomUUID === "function") {
|
|
15430
|
-
return `${prefix}-${cryptoApi.randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
15431
|
-
}
|
|
15432
|
-
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
15433
|
-
const bytes = new Uint8Array(8);
|
|
15434
|
-
cryptoApi.getRandomValues(bytes);
|
|
15435
|
-
return `${prefix}-${bytesToHex(bytes)}`;
|
|
15436
|
-
}
|
|
15437
|
-
return `${prefix}-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
15438
|
-
}
|
|
15439
|
-
|
|
15440
15609
|
// web-src/server/worktree-watcher.ts
|
|
15441
15610
|
import {
|
|
15442
|
-
lstatSync as
|
|
15611
|
+
lstatSync as lstatSync3,
|
|
15443
15612
|
readdirSync as nodeReaddirSync,
|
|
15444
15613
|
watch as nodeWatch
|
|
15445
15614
|
} from "node:fs";
|
|
15446
|
-
import { join as
|
|
15615
|
+
import { join as join10, relative as relative5 } from "node:path";
|
|
15447
15616
|
function normalizeRelativePath(path) {
|
|
15448
15617
|
return path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
15449
15618
|
}
|
|
15450
15619
|
function isInsideRoot(root, path) {
|
|
15451
|
-
const rel =
|
|
15620
|
+
const rel = relative5(root, path).replace(/\\/g, "/");
|
|
15452
15621
|
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
|
|
15453
15622
|
}
|
|
15454
15623
|
function startWorktreeUpdateWatch(options) {
|
|
@@ -15456,14 +15625,14 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15456
15625
|
const readDirs = options.readdirSync || ((path) => nodeReaddirSync(path, { withFileTypes: true }));
|
|
15457
15626
|
const isDirectory = options.isDirectory || ((path) => {
|
|
15458
15627
|
try {
|
|
15459
|
-
return
|
|
15628
|
+
return lstatSync3(path).isDirectory();
|
|
15460
15629
|
} catch {
|
|
15461
15630
|
return false;
|
|
15462
15631
|
}
|
|
15463
15632
|
});
|
|
15464
15633
|
const directorySignature = options.directorySignature || ((path) => {
|
|
15465
15634
|
try {
|
|
15466
|
-
const stats =
|
|
15635
|
+
const stats = lstatSync3(path);
|
|
15467
15636
|
if (!stats.isDirectory())
|
|
15468
15637
|
return null;
|
|
15469
15638
|
return `${stats.dev}:${stats.ino}`;
|
|
@@ -15486,7 +15655,7 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15486
15655
|
const pendingChangedPaths = new Set;
|
|
15487
15656
|
let watchLimitReported = false;
|
|
15488
15657
|
const ignored = (path) => isSkippableSearchPath(normalizeRelativePath(path), options.omitDirNames, options.excludeNames);
|
|
15489
|
-
const directoryRelativePath = (dir) => normalizeRelativePath(
|
|
15658
|
+
const directoryRelativePath = (dir) => normalizeRelativePath(relative5(options.root, dir));
|
|
15490
15659
|
const ignoredDirectory = (dir) => {
|
|
15491
15660
|
const rel = directoryRelativePath(dir);
|
|
15492
15661
|
return Boolean(rel && ignored(rel));
|
|
@@ -15552,7 +15721,7 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15552
15721
|
for (const entry of entries) {
|
|
15553
15722
|
if (!entry.isDirectory())
|
|
15554
15723
|
continue;
|
|
15555
|
-
const child =
|
|
15724
|
+
const child = join10(dir, entry.name);
|
|
15556
15725
|
if (ignoredDirectory(child))
|
|
15557
15726
|
continue;
|
|
15558
15727
|
children.push(child);
|
|
@@ -15636,10 +15805,10 @@ function startWorktreeUpdateWatch(options) {
|
|
|
15636
15805
|
scheduleUpdate();
|
|
15637
15806
|
return;
|
|
15638
15807
|
}
|
|
15639
|
-
const changed = normalizeRelativePath(
|
|
15808
|
+
const changed = normalizeRelativePath(join10(rel, filename.toString()));
|
|
15640
15809
|
if (ignored(changed))
|
|
15641
15810
|
return;
|
|
15642
|
-
const fullChangedPath =
|
|
15811
|
+
const fullChangedPath = join10(options.root, changed);
|
|
15643
15812
|
if (!isInsideRoot(options.root, fullChangedPath))
|
|
15644
15813
|
return;
|
|
15645
15814
|
if (initialScanAsync) {
|
|
@@ -15688,9 +15857,9 @@ var init_worktree_watcher = __esm(() => {
|
|
|
15688
15857
|
});
|
|
15689
15858
|
|
|
15690
15859
|
// web-src/server/state-store.ts
|
|
15691
|
-
import { join as
|
|
15860
|
+
import { join as join11 } from "node:path";
|
|
15692
15861
|
function codeViewerPath(root, fileName) {
|
|
15693
|
-
return
|
|
15862
|
+
return join11(root, CODE_VIEWER_DIR2, fileName);
|
|
15694
15863
|
}
|
|
15695
15864
|
function isRecord(value) {
|
|
15696
15865
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -15707,15 +15876,17 @@ function optionalString(value, maxLen) {
|
|
|
15707
15876
|
function optionalBoolean(value) {
|
|
15708
15877
|
return typeof value === "boolean" ? value : undefined;
|
|
15709
15878
|
}
|
|
15710
|
-
function
|
|
15879
|
+
function optionalFiniteNumber(value, min, max, round) {
|
|
15711
15880
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
15712
15881
|
return;
|
|
15713
|
-
|
|
15882
|
+
const normalized = round ? Math.round(value) : value;
|
|
15883
|
+
return Math.max(min, Math.min(max, normalized));
|
|
15884
|
+
}
|
|
15885
|
+
function optionalNumber(value, min, max) {
|
|
15886
|
+
return optionalFiniteNumber(value, min, max, true);
|
|
15714
15887
|
}
|
|
15715
15888
|
function optionalFloat(value, min, max) {
|
|
15716
|
-
|
|
15717
|
-
return;
|
|
15718
|
-
return Math.max(min, Math.min(max, value));
|
|
15889
|
+
return optionalFiniteNumber(value, min, max, false);
|
|
15719
15890
|
}
|
|
15720
15891
|
function optionalFontSize(value) {
|
|
15721
15892
|
return value === "compact" || value === "regular" || value === "large" || value === "xlarge" ? value : undefined;
|
|
@@ -16547,6 +16718,25 @@ function asStringArray(value) {
|
|
|
16547
16718
|
function asNumber(value) {
|
|
16548
16719
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
16549
16720
|
}
|
|
16721
|
+
function asDynamoDbItemsResult(raw) {
|
|
16722
|
+
return {
|
|
16723
|
+
items: Array.isArray(raw.Items) ? raw.Items : [],
|
|
16724
|
+
count: asNumber(raw.Count),
|
|
16725
|
+
scannedCount: asNumber(raw.ScannedCount),
|
|
16726
|
+
...raw.LastEvaluatedKey ? { lastEvaluatedKey: asObject(raw.LastEvaluatedKey) } : {}
|
|
16727
|
+
};
|
|
16728
|
+
}
|
|
16729
|
+
function dynamoDbItemsRequestFields(opts) {
|
|
16730
|
+
return {
|
|
16731
|
+
...opts.limit ? { Limit: Math.min(1000, Math.max(1, opts.limit)) } : {},
|
|
16732
|
+
...opts.exclusiveStartKey ? { ExclusiveStartKey: opts.exclusiveStartKey } : {},
|
|
16733
|
+
...opts.indexName ? { IndexName: opts.indexName } : {},
|
|
16734
|
+
...opts.projectionExpression ? { ProjectionExpression: opts.projectionExpression } : {},
|
|
16735
|
+
...opts.filterExpression ? { FilterExpression: opts.filterExpression } : {},
|
|
16736
|
+
...opts.expressionAttributeNames ? { ExpressionAttributeNames: opts.expressionAttributeNames } : {},
|
|
16737
|
+
...opts.expressionAttributeValues ? { ExpressionAttributeValues: opts.expressionAttributeValues } : {}
|
|
16738
|
+
};
|
|
16739
|
+
}
|
|
16550
16740
|
function createDynamoDbAdapter(config) {
|
|
16551
16741
|
async function signedJsonRequest(action, body, signal, deadline = createDynamoDbTransportDeadline(config)) {
|
|
16552
16742
|
const requestBody = JSON.stringify(body);
|
|
@@ -16629,41 +16819,19 @@ function createDynamoDbAdapter(config) {
|
|
|
16629
16819
|
assertTableName(opts.tableName);
|
|
16630
16820
|
const raw = await signedJsonRequest("Scan", {
|
|
16631
16821
|
TableName: opts.tableName,
|
|
16632
|
-
...
|
|
16633
|
-
...opts.exclusiveStartKey ? { ExclusiveStartKey: opts.exclusiveStartKey } : {},
|
|
16634
|
-
...opts.indexName ? { IndexName: opts.indexName } : {},
|
|
16635
|
-
...opts.projectionExpression ? { ProjectionExpression: opts.projectionExpression } : {},
|
|
16636
|
-
...opts.filterExpression ? { FilterExpression: opts.filterExpression } : {},
|
|
16637
|
-
...opts.expressionAttributeNames ? { ExpressionAttributeNames: opts.expressionAttributeNames } : {},
|
|
16638
|
-
...opts.expressionAttributeValues ? { ExpressionAttributeValues: opts.expressionAttributeValues } : {}
|
|
16822
|
+
...dynamoDbItemsRequestFields(opts)
|
|
16639
16823
|
}, opts.signal);
|
|
16640
|
-
return
|
|
16641
|
-
items: Array.isArray(raw.Items) ? raw.Items : [],
|
|
16642
|
-
count: asNumber(raw.Count),
|
|
16643
|
-
scannedCount: asNumber(raw.ScannedCount),
|
|
16644
|
-
...raw.LastEvaluatedKey ? { lastEvaluatedKey: asObject(raw.LastEvaluatedKey) } : {}
|
|
16645
|
-
};
|
|
16824
|
+
return asDynamoDbItemsResult(raw);
|
|
16646
16825
|
}
|
|
16647
16826
|
async function queryAsync(opts) {
|
|
16648
16827
|
assertTableName(opts.tableName);
|
|
16649
16828
|
const raw = await signedJsonRequest("Query", {
|
|
16650
16829
|
TableName: opts.tableName,
|
|
16651
16830
|
KeyConditionExpression: opts.keyConditionExpression,
|
|
16652
|
-
...
|
|
16653
|
-
...opts.exclusiveStartKey ? { ExclusiveStartKey: opts.exclusiveStartKey } : {},
|
|
16654
|
-
...opts.indexName ? { IndexName: opts.indexName } : {},
|
|
16655
|
-
...opts.projectionExpression ? { ProjectionExpression: opts.projectionExpression } : {},
|
|
16656
|
-
...opts.filterExpression ? { FilterExpression: opts.filterExpression } : {},
|
|
16657
|
-
...opts.expressionAttributeNames ? { ExpressionAttributeNames: opts.expressionAttributeNames } : {},
|
|
16658
|
-
...opts.expressionAttributeValues ? { ExpressionAttributeValues: opts.expressionAttributeValues } : {},
|
|
16831
|
+
...dynamoDbItemsRequestFields(opts),
|
|
16659
16832
|
...opts.scanIndexForward !== undefined ? { ScanIndexForward: opts.scanIndexForward } : {}
|
|
16660
16833
|
}, opts.signal);
|
|
16661
|
-
return
|
|
16662
|
-
items: Array.isArray(raw.Items) ? raw.Items : [],
|
|
16663
|
-
count: asNumber(raw.Count),
|
|
16664
|
-
scannedCount: asNumber(raw.ScannedCount),
|
|
16665
|
-
...raw.LastEvaluatedKey ? { lastEvaluatedKey: asObject(raw.LastEvaluatedKey) } : {}
|
|
16666
|
-
};
|
|
16834
|
+
return asDynamoDbItemsResult(raw);
|
|
16667
16835
|
}
|
|
16668
16836
|
async function getItemAsync(opts) {
|
|
16669
16837
|
assertTableName(opts.tableName);
|
|
@@ -16806,7 +16974,7 @@ var init_connection_pool = __esm(() => {
|
|
|
16806
16974
|
// web-src/server/database/connections-store.ts
|
|
16807
16975
|
import { randomUUID } from "node:crypto";
|
|
16808
16976
|
import { chmod } from "node:fs/promises";
|
|
16809
|
-
import { join as
|
|
16977
|
+
import { join as join12 } from "node:path";
|
|
16810
16978
|
function secretKey(cwd, id) {
|
|
16811
16979
|
return `${cwd}\x00${id}`;
|
|
16812
16980
|
}
|
|
@@ -16827,7 +16995,7 @@ function withRuntimeSecrets(cwd, connection) {
|
|
|
16827
16995
|
};
|
|
16828
16996
|
}
|
|
16829
16997
|
function connectionsFilePath(root) {
|
|
16830
|
-
return
|
|
16998
|
+
return join12(root, ".code-viewer", CONNECTIONS_FILE_NAME);
|
|
16831
16999
|
}
|
|
16832
17000
|
function emptyState() {
|
|
16833
17001
|
return { version: 1, connections: [] };
|
|
@@ -18609,9 +18777,9 @@ var init_handle_s3 = __esm(() => {
|
|
|
18609
18777
|
});
|
|
18610
18778
|
|
|
18611
18779
|
// web-src/server/database/query-history.ts
|
|
18612
|
-
import { join as
|
|
18780
|
+
import { join as join13 } from "node:path";
|
|
18613
18781
|
function historyFilePath(root) {
|
|
18614
|
-
return
|
|
18782
|
+
return join13(root, CODE_VIEWER_DIR3, HISTORY_FILE_NAME);
|
|
18615
18783
|
}
|
|
18616
18784
|
function emptyState2() {
|
|
18617
18785
|
return { version: 1, entries: [] };
|
|
@@ -18761,9 +18929,9 @@ var init_query_history = __esm(() => {
|
|
|
18761
18929
|
// web-src/server/database/snapshot-store.ts
|
|
18762
18930
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
18763
18931
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
18764
|
-
import { join as
|
|
18932
|
+
import { join as join14 } from "node:path";
|
|
18765
18933
|
async function getStoreDb(cwd) {
|
|
18766
|
-
const dbPath =
|
|
18934
|
+
const dbPath = join14(cwd, CODE_VIEWER_DIR4, SNAPSHOT_DB_NAME);
|
|
18767
18935
|
if (storeDb && storeDbPath === dbPath)
|
|
18768
18936
|
return storeDb;
|
|
18769
18937
|
if (storeDb) {
|
|
@@ -18771,7 +18939,7 @@ async function getStoreDb(cwd) {
|
|
|
18771
18939
|
storeDb.close();
|
|
18772
18940
|
} catch {}
|
|
18773
18941
|
}
|
|
18774
|
-
mkdirSync3(
|
|
18942
|
+
mkdirSync3(join14(cwd, CODE_VIEWER_DIR4), { recursive: true });
|
|
18775
18943
|
const DbClass = await loadSqliteClass();
|
|
18776
18944
|
storeDb = new DbClass(dbPath);
|
|
18777
18945
|
storeDbPath = dbPath;
|
|
@@ -19377,9 +19545,9 @@ var init_snapshot_runner = __esm(() => {
|
|
|
19377
19545
|
});
|
|
19378
19546
|
|
|
19379
19547
|
// web-src/server/database/tabs-store.ts
|
|
19380
|
-
import { join as
|
|
19548
|
+
import { join as join15 } from "node:path";
|
|
19381
19549
|
function tabsFilePath(root) {
|
|
19382
|
-
return
|
|
19550
|
+
return join15(root, CODE_VIEWER_DIR5, TABS_FILE_NAME);
|
|
19383
19551
|
}
|
|
19384
19552
|
function emptyState3() {
|
|
19385
19553
|
return { version: 1, tabs: [], activeTabId: null };
|
|
@@ -21316,7 +21484,7 @@ var init_handle = __esm(() => {
|
|
|
21316
21484
|
|
|
21317
21485
|
// web-src/server/doctor.ts
|
|
21318
21486
|
import { accessSync as accessSync2, constants as constants2, readFileSync as readFileSync6, statSync as statSync5 } from "node:fs";
|
|
21319
|
-
import { dirname as
|
|
21487
|
+
import { dirname as dirname5, join as join16, relative as relative6 } from "node:path";
|
|
21320
21488
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
21321
21489
|
function statusWorse(a, b) {
|
|
21322
21490
|
const rank = { ok: 0, warn: 1, error: 2 };
|
|
@@ -21405,12 +21573,12 @@ function findCodeViewerPackageJson() {
|
|
|
21405
21573
|
try {
|
|
21406
21574
|
let cursor;
|
|
21407
21575
|
try {
|
|
21408
|
-
cursor =
|
|
21576
|
+
cursor = dirname5(fileURLToPath2(import.meta.url));
|
|
21409
21577
|
} catch {
|
|
21410
|
-
cursor =
|
|
21578
|
+
cursor = dirname5(process.argv[1] || ".");
|
|
21411
21579
|
}
|
|
21412
21580
|
for (let depth = 0;depth < 8; depth += 1) {
|
|
21413
|
-
const candidate =
|
|
21581
|
+
const candidate = join16(cursor, "package.json");
|
|
21414
21582
|
try {
|
|
21415
21583
|
const raw = readFileSync6(candidate, "utf8");
|
|
21416
21584
|
const pkg = JSON.parse(raw);
|
|
@@ -21418,7 +21586,7 @@ function findCodeViewerPackageJson() {
|
|
|
21418
21586
|
return { version: pkg.version, path: candidate };
|
|
21419
21587
|
}
|
|
21420
21588
|
} catch {}
|
|
21421
|
-
const next =
|
|
21589
|
+
const next = dirname5(cursor);
|
|
21422
21590
|
if (next === cursor)
|
|
21423
21591
|
break;
|
|
21424
21592
|
cursor = next;
|
|
@@ -21506,7 +21674,7 @@ async function checkSqlite(cwd) {
|
|
|
21506
21674
|
return { id: "sqlite", title: "SQLite driver", rows };
|
|
21507
21675
|
}
|
|
21508
21676
|
async function trySnapshotDbOpen(cwd) {
|
|
21509
|
-
const dbPath =
|
|
21677
|
+
const dbPath = join16(cwd, SNAPSHOT_DB_REL);
|
|
21510
21678
|
try {
|
|
21511
21679
|
statSync5(dbPath);
|
|
21512
21680
|
} catch {
|
|
@@ -21527,8 +21695,8 @@ async function trySnapshotDbOpen(cwd) {
|
|
|
21527
21695
|
}
|
|
21528
21696
|
}
|
|
21529
21697
|
function checkSnapshotStore(cwd) {
|
|
21530
|
-
const dbPath =
|
|
21531
|
-
const dir =
|
|
21698
|
+
const dbPath = join16(cwd, SNAPSHOT_DB_REL);
|
|
21699
|
+
const dir = dirname5(dbPath);
|
|
21532
21700
|
let dirStatus = "ok";
|
|
21533
21701
|
let dirDetail = dir;
|
|
21534
21702
|
let dirHint;
|
|
@@ -21601,7 +21769,7 @@ async function checkGit(cwd, signal) {
|
|
|
21601
21769
|
if (repoCheck && repoCheck.code === 0 && /true/.test(repoCheck.stdout)) {
|
|
21602
21770
|
const topRes = await runCached(gitCache, TTL.gitRepo, commandForExternal("git"), ["rev-parse", "--show-toplevel"], TIMEOUT.git, signal, cwd);
|
|
21603
21771
|
const top = topRes?.stdout.trim() || cwd;
|
|
21604
|
-
const insideCwd =
|
|
21772
|
+
const insideCwd = relative6(top, cwd) || ".";
|
|
21605
21773
|
rows.push({
|
|
21606
21774
|
id: "git.repo",
|
|
21607
21775
|
title: "Working tree",
|
|
@@ -22354,29 +22522,20 @@ function parseDoctorCliArgs(argv) {
|
|
|
22354
22522
|
json2 = true;
|
|
22355
22523
|
continue;
|
|
22356
22524
|
}
|
|
22357
|
-
|
|
22358
|
-
|
|
22359
|
-
|
|
22360
|
-
|
|
22361
|
-
|
|
22525
|
+
const global = takeGlobalCliOption(argv, i, {
|
|
22526
|
+
allowedCommands: ["git", "docker", "gh"]
|
|
22527
|
+
});
|
|
22528
|
+
if (global.kind === "error") {
|
|
22529
|
+
return { kind: "error", message: global.error };
|
|
22530
|
+
}
|
|
22531
|
+
if (global.kind === "cwd") {
|
|
22532
|
+
cwd = global.value;
|
|
22533
|
+
i = global.next;
|
|
22362
22534
|
continue;
|
|
22363
22535
|
}
|
|
22364
|
-
if (
|
|
22365
|
-
|
|
22366
|
-
|
|
22367
|
-
return {
|
|
22368
|
-
kind: "error",
|
|
22369
|
-
message: "--bin requires <name>=<absolute-path>"
|
|
22370
|
-
};
|
|
22371
|
-
}
|
|
22372
|
-
const parsed = parseExternalCommandOverride(next, "--bin", [
|
|
22373
|
-
"git",
|
|
22374
|
-
"docker",
|
|
22375
|
-
"gh"
|
|
22376
|
-
]);
|
|
22377
|
-
if (parsed.ok === false)
|
|
22378
|
-
return { kind: "error", message: parsed.error };
|
|
22379
|
-
commandOverrides.push(parsed.override);
|
|
22536
|
+
if (global.kind === "command-override") {
|
|
22537
|
+
commandOverrides.push(global.override);
|
|
22538
|
+
i = global.next;
|
|
22380
22539
|
continue;
|
|
22381
22540
|
}
|
|
22382
22541
|
if (arg === "--port") {
|
|
@@ -22503,6 +22662,7 @@ Exit codes:
|
|
|
22503
22662
|
`, STATUS_SYMBOL;
|
|
22504
22663
|
var init_doctor_cli = __esm(() => {
|
|
22505
22664
|
init_command_resolver();
|
|
22665
|
+
init_cli_helpers();
|
|
22506
22666
|
init_doctor();
|
|
22507
22667
|
init_git();
|
|
22508
22668
|
STATUS_SYMBOL = {
|
|
@@ -22529,44 +22689,6 @@ function normalizeNewDirectoryName(name) {
|
|
|
22529
22689
|
return trimmed;
|
|
22530
22690
|
}
|
|
22531
22691
|
|
|
22532
|
-
// web-src/server/cache.ts
|
|
22533
|
-
import { lstatSync as lstatSync3 } from "node:fs";
|
|
22534
|
-
import { join as join16 } from "node:path";
|
|
22535
|
-
function cacheFresh(cached, now = Date.now(), ttlMs = CACHE_TTL_MS) {
|
|
22536
|
-
return !!cached && now - cached.storedAt <= ttlMs;
|
|
22537
|
-
}
|
|
22538
|
-
function setTimedCacheEntry(cache, key, value, now = Date.now(), maxEntries = MAX_TIMED_CACHE_ENTRIES) {
|
|
22539
|
-
cache.set(key, { ...value, storedAt: now });
|
|
22540
|
-
while (cache.size > maxEntries) {
|
|
22541
|
-
const oldest = cache.keys().next().value;
|
|
22542
|
-
if (oldest === undefined)
|
|
22543
|
-
break;
|
|
22544
|
-
cache.delete(oldest);
|
|
22545
|
-
}
|
|
22546
|
-
}
|
|
22547
|
-
function worktreeFileSignature(path, cwd) {
|
|
22548
|
-
try {
|
|
22549
|
-
const stats = lstatSync3(join16(cwd, path));
|
|
22550
|
-
const inode = "ino" in stats ? stats.ino : 0;
|
|
22551
|
-
return `state:file|size:${stats.size}|mtime:${stats.mtimeMs}|ctime:${stats.ctimeMs}|ino:${inode}`;
|
|
22552
|
-
} catch {
|
|
22553
|
-
return "state:missing";
|
|
22554
|
-
}
|
|
22555
|
-
}
|
|
22556
|
-
function fileDiffCacheKey(options) {
|
|
22557
|
-
const worktreeTarget = options.range.from === "worktree" || !options.range.to || options.range.to === "worktree";
|
|
22558
|
-
if (options.isUntracked && !worktreeTarget) {
|
|
22559
|
-
throw new Error("untracked file diffs require a worktree range");
|
|
22560
|
-
}
|
|
22561
|
-
const signature = worktreeTarget ? `\x00${worktreeFileSignature(options.path, options.cwd)}` : "";
|
|
22562
|
-
if (options.isUntracked) {
|
|
22563
|
-
return `u\x00${options.path}${signature}\x00${options.extras.join("\x00")}`;
|
|
22564
|
-
}
|
|
22565
|
-
return `t\x00${options.path}\x00${options.oldPath || ""}${signature}\x00${[...options.extras, ...options.args].join("\x00")}`;
|
|
22566
|
-
}
|
|
22567
|
-
var CACHE_TTL_MS = 1500, MAX_TIMED_CACHE_ENTRIES = 200;
|
|
22568
|
-
var init_cache = () => {};
|
|
22569
|
-
|
|
22570
22692
|
// web-src/server/dev-assets.ts
|
|
22571
22693
|
import { basename as basename2 } from "node:path";
|
|
22572
22694
|
function startDevAssetReload(options) {
|
|
@@ -22606,9 +22728,7 @@ function emptyJournalTaskState() {
|
|
|
22606
22728
|
return { version: 1, tasks: [] };
|
|
22607
22729
|
}
|
|
22608
22730
|
function makeJournalId(prefix) {
|
|
22609
|
-
|
|
22610
|
-
const time = Date.now().toString(36);
|
|
22611
|
-
return `${prefix}-${time}${random}`;
|
|
22731
|
+
return makeTimedId(prefix);
|
|
22612
22732
|
}
|
|
22613
22733
|
function optionalString4(value, maxLen) {
|
|
22614
22734
|
if (typeof value !== "string")
|
|
@@ -22762,11 +22882,8 @@ async function updateDailyJournalState(root, updater) {
|
|
|
22762
22882
|
async function updateJournalTaskState(root, updater) {
|
|
22763
22883
|
return journalTaskStore.update(root, updater);
|
|
22764
22884
|
}
|
|
22765
|
-
function insertOptionCount2(input) {
|
|
22766
|
-
return (input.before_id ? 1 : 0) + (input.after_id ? 1 : 0) + (input.position !== undefined ? 1 : 0);
|
|
22767
|
-
}
|
|
22768
22885
|
function taskInsertIndex(tasks, status, input) {
|
|
22769
|
-
if (
|
|
22886
|
+
if (orderedInsertOptionCount(input) > 1)
|
|
22770
22887
|
return { ok: false, error: "use only one of before, after, or position" };
|
|
22771
22888
|
if (input.before_id || input.after_id) {
|
|
22772
22889
|
const anchorId = input.before_id || input.after_id || "";
|
|
@@ -22934,6 +23051,16 @@ function addJournalTask(state, input, now, makeId3 = makeJournalId) {
|
|
|
22934
23051
|
tasks.splice(insertAt.index, 0, task);
|
|
22935
23052
|
return { ok: true, state: { version: 1, tasks }, task };
|
|
22936
23053
|
}
|
|
23054
|
+
function journalTaskResult(state, task) {
|
|
23055
|
+
return {
|
|
23056
|
+
ok: true,
|
|
23057
|
+
state: {
|
|
23058
|
+
version: 1,
|
|
23059
|
+
tasks: state.tasks.map((item) => item.id === task.id ? task : item)
|
|
23060
|
+
},
|
|
23061
|
+
task
|
|
23062
|
+
};
|
|
23063
|
+
}
|
|
22937
23064
|
function updateJournalTask(state, id, patch, now) {
|
|
22938
23065
|
const task = state.tasks.find((item) => item.id === id);
|
|
22939
23066
|
if (!task)
|
|
@@ -22988,14 +23115,7 @@ function updateJournalTask(state, id, patch, now) {
|
|
|
22988
23115
|
else
|
|
22989
23116
|
delete next.journal_entry_id;
|
|
22990
23117
|
}
|
|
22991
|
-
return
|
|
22992
|
-
ok: true,
|
|
22993
|
-
state: {
|
|
22994
|
-
version: 1,
|
|
22995
|
-
tasks: state.tasks.map((item) => item.id === id ? next : item)
|
|
22996
|
-
},
|
|
22997
|
-
task: next
|
|
22998
|
-
};
|
|
23118
|
+
return journalTaskResult(state, next);
|
|
22999
23119
|
}
|
|
23000
23120
|
function moveJournalTask(state, id, input, now) {
|
|
23001
23121
|
const source = state.tasks.find((task) => task.id === id);
|
|
@@ -23105,8 +23225,7 @@ function claimJournalTask(state, id, input, now) {
|
|
|
23105
23225
|
if (!task)
|
|
23106
23226
|
return { ok: false, error: "task not found" };
|
|
23107
23227
|
const nowMs = Date.parse(now);
|
|
23108
|
-
|
|
23109
|
-
if (activeClaim)
|
|
23228
|
+
if (taskClaimActive(task, nowMs))
|
|
23110
23229
|
return { ok: false, error: "task is already claimed" };
|
|
23111
23230
|
if (task.status !== "todo" && task.status !== "doing")
|
|
23112
23231
|
return {
|
|
@@ -23117,12 +23236,9 @@ function claimJournalTask(state, id, input, now) {
|
|
|
23117
23236
|
const wipLimit = input.wip_limit;
|
|
23118
23237
|
if (wipLimit !== undefined && wipLimit > 0) {
|
|
23119
23238
|
const activeDoing = state.tasks.filter((item) => {
|
|
23120
|
-
if (item.status !== "doing" ||
|
|
23239
|
+
if (item.status !== "doing" || item.claim?.by !== by)
|
|
23121
23240
|
return false;
|
|
23122
|
-
|
|
23123
|
-
return false;
|
|
23124
|
-
const expires = Date.parse(item.claim.lease_expires_at);
|
|
23125
|
-
return Number.isFinite(expires) && expires > nowMs;
|
|
23241
|
+
return taskClaimActive(item, nowMs);
|
|
23126
23242
|
}).length;
|
|
23127
23243
|
if (activeDoing >= wipLimit)
|
|
23128
23244
|
return { ok: false, error: "WIP limit reached" };
|
|
@@ -23139,14 +23255,7 @@ function claimJournalTask(state, id, input, now) {
|
|
|
23139
23255
|
lease_expires_at: leaseExpiresAt
|
|
23140
23256
|
}
|
|
23141
23257
|
};
|
|
23142
|
-
return
|
|
23143
|
-
ok: true,
|
|
23144
|
-
state: {
|
|
23145
|
-
version: 1,
|
|
23146
|
-
tasks: state.tasks.map((item) => item.id === id ? next : item)
|
|
23147
|
-
},
|
|
23148
|
-
task: next
|
|
23149
|
-
};
|
|
23258
|
+
return journalTaskResult(state, next);
|
|
23150
23259
|
}
|
|
23151
23260
|
function completeJournalTask(state, id, input, now, makeId3 = makeJournalId) {
|
|
23152
23261
|
const task = state.tasks.find((item) => item.id === id);
|
|
@@ -23155,8 +23264,7 @@ function completeJournalTask(state, id, input, now, makeId3 = makeJournalId) {
|
|
|
23155
23264
|
if (task.status !== "doing")
|
|
23156
23265
|
return { ok: false, error: "only doing tasks can be completed" };
|
|
23157
23266
|
const nowMs = Date.parse(now);
|
|
23158
|
-
|
|
23159
|
-
if (!activeClaim)
|
|
23267
|
+
if (!taskClaimActive(task, nowMs))
|
|
23160
23268
|
return { ok: false, error: "task must be claimed before completion" };
|
|
23161
23269
|
const by = optionalString4(input.by, 128);
|
|
23162
23270
|
if (!by)
|
|
@@ -23186,14 +23294,7 @@ function completeJournalTask(state, id, input, now, makeId3 = makeJournalId) {
|
|
|
23186
23294
|
...notes.length ? { notes } : {}
|
|
23187
23295
|
};
|
|
23188
23296
|
delete next.claim;
|
|
23189
|
-
return
|
|
23190
|
-
ok: true,
|
|
23191
|
-
state: {
|
|
23192
|
-
version: 1,
|
|
23193
|
-
tasks: state.tasks.map((item) => item.id === id ? next : item)
|
|
23194
|
-
},
|
|
23195
|
-
task: next
|
|
23196
|
-
};
|
|
23297
|
+
return journalTaskResult(state, next);
|
|
23197
23298
|
}
|
|
23198
23299
|
function deleteJournalTask(state, id) {
|
|
23199
23300
|
const tasks = state.tasks.filter((task) => task.id !== id);
|
|
@@ -23229,8 +23330,8 @@ var init_journal2 = __esm(() => {
|
|
|
23229
23330
|
});
|
|
23230
23331
|
|
|
23231
23332
|
// web-src/server/search-service.ts
|
|
23232
|
-
import { existsSync as existsSync7, lstatSync as lstatSync4, readFileSync as readFileSync7, realpathSync as
|
|
23233
|
-
import { join as join18, relative as
|
|
23333
|
+
import { existsSync as existsSync7, lstatSync as lstatSync4, readFileSync as readFileSync7, realpathSync as realpathSync6 } from "node:fs";
|
|
23334
|
+
import { join as join18, relative as relative7 } from "node:path";
|
|
23234
23335
|
async function rgAvailableAsync(cwd) {
|
|
23235
23336
|
if (rgAvailableCache !== null)
|
|
23236
23337
|
return rgAvailableCache;
|
|
@@ -23266,12 +23367,12 @@ function safeWorktreePath(env, path) {
|
|
|
23266
23367
|
let realCwd;
|
|
23267
23368
|
let realFull;
|
|
23268
23369
|
try {
|
|
23269
|
-
realCwd =
|
|
23270
|
-
realFull =
|
|
23370
|
+
realCwd = realpathSync6(env.cwd);
|
|
23371
|
+
realFull = realpathSync6(full);
|
|
23271
23372
|
} catch {
|
|
23272
23373
|
return null;
|
|
23273
23374
|
}
|
|
23274
|
-
const rel =
|
|
23375
|
+
const rel = relative7(realCwd, realFull);
|
|
23275
23376
|
if (rel === "" || rel.startsWith("..") || rel.startsWith("/") || rel.startsWith("\\"))
|
|
23276
23377
|
return null;
|
|
23277
23378
|
if (isGitInternalPath(rel))
|
|
@@ -25015,7 +25116,7 @@ import {
|
|
|
25015
25116
|
mkdirSync as mkdirSync4,
|
|
25016
25117
|
openSync as openSync3,
|
|
25017
25118
|
readFileSync as readFileSync9,
|
|
25018
|
-
realpathSync as
|
|
25119
|
+
realpathSync as realpathSync7,
|
|
25019
25120
|
renameSync,
|
|
25020
25121
|
statSync as statSync6,
|
|
25021
25122
|
unlinkSync as unlinkSync2,
|
|
@@ -25023,7 +25124,7 @@ import {
|
|
|
25023
25124
|
writeFileSync as writeFileSync2
|
|
25024
25125
|
} from "node:fs";
|
|
25025
25126
|
import { homedir as homedir3 } from "node:os";
|
|
25026
|
-
import { basename as basename3, dirname as
|
|
25127
|
+
import { basename as basename3, dirname as dirname6, extname as extname2, join as join20, relative as relative8 } from "node:path";
|
|
25027
25128
|
function parseCli() {
|
|
25028
25129
|
const rest = [];
|
|
25029
25130
|
for (let i = 2;i < process.argv.length; i++) {
|
|
@@ -25077,7 +25178,7 @@ Examples:
|
|
|
25077
25178
|
process.exit(1);
|
|
25078
25179
|
}
|
|
25079
25180
|
try {
|
|
25080
|
-
cwd =
|
|
25181
|
+
cwd = realpathSync7(next);
|
|
25081
25182
|
cwdWasExplicit = true;
|
|
25082
25183
|
} catch {
|
|
25083
25184
|
console.error("--cwd must point to an existing directory");
|
|
@@ -25508,7 +25609,7 @@ function worktreePath(path) {
|
|
|
25508
25609
|
function safeOpenWorktreePath(path) {
|
|
25509
25610
|
if (path === "") {
|
|
25510
25611
|
try {
|
|
25511
|
-
const realCwd =
|
|
25612
|
+
const realCwd = realpathSync7(cwd);
|
|
25512
25613
|
if (isGitInternalPath(realCwd))
|
|
25513
25614
|
return null;
|
|
25514
25615
|
return realCwd;
|
|
@@ -25519,7 +25620,7 @@ function safeOpenWorktreePath(path) {
|
|
|
25519
25620
|
return safeWorktreePath2(path);
|
|
25520
25621
|
}
|
|
25521
25622
|
function parentRepoPath(path) {
|
|
25522
|
-
const parent =
|
|
25623
|
+
const parent = dirname6(path);
|
|
25523
25624
|
return parent === "." ? "" : parent;
|
|
25524
25625
|
}
|
|
25525
25626
|
function isoDate(ms) {
|
|
@@ -25577,6 +25678,21 @@ async function attachTreeEntryMetadata(target, entry) {
|
|
|
25577
25678
|
return { ...entry, ...await directoryMetadata(target, entry.path) };
|
|
25578
25679
|
if (entry.type !== "blob")
|
|
25579
25680
|
return entry;
|
|
25681
|
+
if (entry.is_symlink && target !== "worktree" && target !== "") {
|
|
25682
|
+
const symlinkMeta = await gitSymlinkTargetMetadataAsync(target, entry.path, cwd);
|
|
25683
|
+
if (symlinkMeta.symlink_target_type === "tree")
|
|
25684
|
+
return {
|
|
25685
|
+
...entry,
|
|
25686
|
+
...symlinkMeta,
|
|
25687
|
+
type: "tree",
|
|
25688
|
+
...await directoryMetadata(target, entry.path)
|
|
25689
|
+
};
|
|
25690
|
+
return {
|
|
25691
|
+
...entry,
|
|
25692
|
+
...symlinkMeta,
|
|
25693
|
+
...await fileMetadataForTarget(target, entry.path)
|
|
25694
|
+
};
|
|
25695
|
+
}
|
|
25580
25696
|
return { ...entry, ...await fileMetadataForTarget(target, entry.path) };
|
|
25581
25697
|
}
|
|
25582
25698
|
async function readReadme(target, dirPath) {
|
|
@@ -25599,6 +25715,22 @@ async function readReadme(target, dirPath) {
|
|
|
25599
25715
|
}
|
|
25600
25716
|
return null;
|
|
25601
25717
|
}
|
|
25718
|
+
function deletedTreeEntriesForPath(statusMap, basePath) {
|
|
25719
|
+
const entries = [];
|
|
25720
|
+
for (const [path, status] of statusMap) {
|
|
25721
|
+
if (status !== "D")
|
|
25722
|
+
continue;
|
|
25723
|
+
if (basePath) {
|
|
25724
|
+
if (!path.startsWith(`${basePath}/`))
|
|
25725
|
+
continue;
|
|
25726
|
+
}
|
|
25727
|
+
const rel = basePath ? path.slice(basePath.length + 1) : path;
|
|
25728
|
+
if (!rel || rel.includes("/"))
|
|
25729
|
+
continue;
|
|
25730
|
+
entries.push({ name: rel, path, type: "blob", status: "D" });
|
|
25731
|
+
}
|
|
25732
|
+
return entries;
|
|
25733
|
+
}
|
|
25602
25734
|
async function handleTree(url) {
|
|
25603
25735
|
const target = url.searchParams.get("ref") || url.searchParams.get("target") || "worktree";
|
|
25604
25736
|
const path = (url.searchParams.get("path") || "").replace(/^\/+|\/+$/g, "");
|
|
@@ -25625,12 +25757,21 @@ async function handleTree(url) {
|
|
|
25625
25757
|
if (tree.error)
|
|
25626
25758
|
return text(tree.error, tree.status ?? 500);
|
|
25627
25759
|
const entries = tree.entries.filter((entry) => !isExcludedScopePath(entry.path, excludeNames));
|
|
25760
|
+
const statusMap = target === "worktree" || target === "" ? await repoStatusMapAsync(cwd) : null;
|
|
25761
|
+
const withStatus = (entry) => {
|
|
25762
|
+
const status = statusMap?.get(entry.path);
|
|
25763
|
+
return status ? { ...entry, status } : entry;
|
|
25764
|
+
};
|
|
25765
|
+
const deletedEntries = !recursive && statusMap ? deletedTreeEntriesForPath(statusMap, path) : [];
|
|
25628
25766
|
return json2({
|
|
25629
25767
|
ref: target,
|
|
25630
25768
|
path,
|
|
25631
25769
|
project: basename3(cwd),
|
|
25632
25770
|
branch: await currentBranchMetadata(),
|
|
25633
|
-
entries: recursive ? entries
|
|
25771
|
+
entries: recursive ? entries.map(withStatus) : [
|
|
25772
|
+
...await Promise.all(entries.map((entry) => attachTreeEntryMetadata(target, entry).then(withStatus))),
|
|
25773
|
+
...deletedEntries
|
|
25774
|
+
],
|
|
25634
25775
|
readme: await readReadme(target, path),
|
|
25635
25776
|
upload_enabled: uploadEnabled && (target === "worktree" || target === "")
|
|
25636
25777
|
});
|
|
@@ -26326,7 +26467,7 @@ async function handleUploadFiles(req) {
|
|
|
26326
26467
|
if (total > MAX_UPLOAD_TOTAL_BYTES)
|
|
26327
26468
|
return text("upload too large", 413);
|
|
26328
26469
|
const target = join20(realDir, safeName);
|
|
26329
|
-
if (
|
|
26470
|
+
if (relative8(realDir, dirname6(target)) !== "")
|
|
26330
26471
|
return text("invalid filename", 400);
|
|
26331
26472
|
if (existsSync8(target))
|
|
26332
26473
|
return text("file exists", 409);
|
|
@@ -26492,10 +26633,10 @@ async function restoreTrashPath(originalPath, trashPath) {
|
|
|
26492
26633
|
return { ok: false, error: "trash item not found" };
|
|
26493
26634
|
try {
|
|
26494
26635
|
const trashRoot = join20(homedir3(), ".Trash");
|
|
26495
|
-
const trashRelative =
|
|
26636
|
+
const trashRelative = relative8(trashRoot, trashPath);
|
|
26496
26637
|
if (trashRelative === "" || trashRelative.startsWith("..") || trashRelative.startsWith("/") || trashRelative.startsWith("\\"))
|
|
26497
26638
|
return { ok: false, error: "invalid trash handle" };
|
|
26498
|
-
mkdirSync4(
|
|
26639
|
+
mkdirSync4(dirname6(original), { recursive: true });
|
|
26499
26640
|
renameSync(trashPath, original);
|
|
26500
26641
|
return { ok: true };
|
|
26501
26642
|
} catch (error) {
|