@youtyan/code-viewer 0.11.0 → 0.11.2

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.
@@ -1173,7 +1173,7 @@ function runBytesSync(args, cwd2, options = {}) {
1173
1173
  }
1174
1174
  function runBytesAsync(args, cwd2, options = {}) {
1175
1175
  const maxBuffer = options.maxBuffer ?? 64 * 1024 * 1024;
1176
- return new Promise((resolve3) => {
1176
+ return new Promise((resolve4) => {
1177
1177
  const proc = spawn(args[0], args.slice(1), {
1178
1178
  cwd: cwd2,
1179
1179
  stdio: [options.stdin === void 0 ? "ignore" : "pipe", "pipe", "pipe"]
@@ -1214,7 +1214,7 @@ function runBytesAsync(args, cwd2, options = {}) {
1214
1214
  } else {
1215
1215
  stderr = appendProcessError(stderr, processError);
1216
1216
  }
1217
- resolve3({
1217
+ resolve4({
1218
1218
  code,
1219
1219
  stdout: concatBytes(stdoutChunks),
1220
1220
  stderr
@@ -1260,41 +1260,28 @@ function concatBytes(chunks) {
1260
1260
  }
1261
1261
  return out;
1262
1262
  }
1263
- function spawnDetached(args) {
1264
- const child = spawn(args[0], args.slice(1), {
1265
- detached: true,
1266
- stdio: "ignore"
1267
- });
1268
- child.on("error", (err) => {
1269
- console.warn(
1270
- "[code-viewer] failed to start detached command:",
1271
- err.message
1272
- );
1273
- });
1274
- child.unref();
1275
- }
1276
1263
  function spawnStream(args, cwd2) {
1277
1264
  const proc = spawn(args[0], args.slice(1), {
1278
1265
  cwd: cwd2,
1279
1266
  stdio: ["ignore", "pipe", "ignore"]
1280
1267
  });
1281
- let errorCode = 0;
1268
+ let errorCode2 = 0;
1282
1269
  proc.on("error", () => {
1283
- errorCode = 1;
1270
+ errorCode2 = 1;
1284
1271
  });
1285
1272
  return {
1286
1273
  stream: Readable.toWeb(
1287
1274
  proc.stdout
1288
1275
  ),
1289
- exited: new Promise((resolve3) => {
1276
+ exited: new Promise((resolve4) => {
1290
1277
  let settled = false;
1291
1278
  const done = (code) => {
1292
1279
  if (settled) return;
1293
1280
  settled = true;
1294
- resolve3(code);
1281
+ resolve4(code);
1295
1282
  };
1296
1283
  proc.on("error", () => done(1));
1297
- proc.on("close", (code) => done(errorCode || (code ?? 1)));
1284
+ proc.on("close", (code) => done(errorCode2 || (code ?? 1)));
1298
1285
  }),
1299
1286
  kill: (signal) => proc.kill(signal)
1300
1287
  };
@@ -1350,7 +1337,7 @@ function startServer(options) {
1350
1337
  res.end("internal server error");
1351
1338
  }
1352
1339
  });
1353
- return new Promise((resolve3, reject) => {
1340
+ return new Promise((resolve4, reject) => {
1354
1341
  server2.once("error", reject);
1355
1342
  server2.listen(options.port, options.hostname, () => {
1356
1343
  server2.off("error", reject);
@@ -1359,7 +1346,7 @@ function startServer(options) {
1359
1346
  });
1360
1347
  const address = server2.address();
1361
1348
  const port = typeof address === "object" && address ? address.port : options.port;
1362
- resolve3({
1349
+ resolve4({
1363
1350
  port,
1364
1351
  close: () => new Promise((resolveClose, rejectClose) => {
1365
1352
  let settled = false;
@@ -1418,7 +1405,7 @@ async function writeWebResponse(res, response) {
1418
1405
  res.end();
1419
1406
  return;
1420
1407
  }
1421
- await new Promise((resolve3, reject) => {
1408
+ await new Promise((resolve4, reject) => {
1422
1409
  const body = Readable.fromWeb(
1423
1410
  response.body
1424
1411
  );
@@ -1435,12 +1422,12 @@ async function writeWebResponse(res, response) {
1435
1422
  reject(error);
1436
1423
  })
1437
1424
  );
1438
- res.on("finish", () => settle(resolve3));
1425
+ res.on("finish", () => settle(resolve4));
1439
1426
  res.on(
1440
1427
  "close",
1441
1428
  () => settle(() => {
1442
1429
  body.destroy();
1443
- resolve3();
1430
+ resolve4();
1444
1431
  })
1445
1432
  );
1446
1433
  body.pipe(res);
@@ -2444,7 +2431,7 @@ async function worktreeFilesystemEntriesAsync(cwd2, path, recursive, omitDirName
2444
2431
  const yieldIfNeeded = async () => {
2445
2432
  visitedDirs++;
2446
2433
  if (visitedDirs % 25 === 0) {
2447
- await new Promise((resolve3) => setTimeout(resolve3, 0));
2434
+ await new Promise((resolve4) => setTimeout(resolve4, 0));
2448
2435
  }
2449
2436
  };
2450
2437
  const walk = async (dir, prefix, depth) => {
@@ -7925,7 +7912,7 @@ function searchPollIntervalMs() {
7925
7912
  }
7926
7913
  function sleep(ms) {
7927
7914
  if (ms <= 0) return Promise.resolve();
7928
- return new Promise((resolve3) => setTimeout(resolve3, ms));
7915
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
7929
7916
  }
7930
7917
  async function cancelSearchJobBestEffort(serverUrl, jobId) {
7931
7918
  try {
@@ -8707,8 +8694,8 @@ function computeFuzzyMatch(query, path) {
8707
8694
  const first = indices[0];
8708
8695
  score -= Math.min(first, 40);
8709
8696
  if (indices[0] >= baseStart) score += 20;
8710
- const basename5 = lowerPath.slice(baseStart);
8711
- const tier = pathMatchTier(q, lowerPath, basename5);
8697
+ const basename6 = lowerPath.slice(baseStart);
8698
+ const tier = pathMatchTier(q, lowerPath, basename6);
8712
8699
  const contiguousRange = contiguousPathRange(q, lowerPath, baseStart);
8713
8700
  return {
8714
8701
  score,
@@ -8860,8 +8847,8 @@ function createGlobPathMatcher(query) {
8860
8847
  const suffix = query.replace(/^\*+/, "").toLowerCase();
8861
8848
  return (path) => {
8862
8849
  const baseStart = basenameStart(path);
8863
- const basename5 = path.slice(baseStart);
8864
- if (!regex.test(path) && (query.includes("/") || !regex.test(basename5)))
8850
+ const basename6 = path.slice(baseStart);
8851
+ if (!regex.test(path) && (query.includes("/") || !regex.test(basename6)))
8865
8852
  return null;
8866
8853
  const ranges = [];
8867
8854
  const lowerPath = path.toLowerCase();
@@ -10840,7 +10827,7 @@ function send(message) {
10840
10827
  `);
10841
10828
  }
10842
10829
  function readConfigLine() {
10843
- return new Promise((resolve3, reject) => {
10830
+ return new Promise((resolve4, reject) => {
10844
10831
  let buffer = "";
10845
10832
  const stdin = process.stdin;
10846
10833
  stdin.setEncoding("utf8");
@@ -10850,7 +10837,7 @@ function readConfigLine() {
10850
10837
  if (newline === -1) return;
10851
10838
  stdin.off("data", onData);
10852
10839
  stdin.off("end", onEnd);
10853
- resolve3(buffer.slice(0, newline));
10840
+ resolve4(buffer.slice(0, newline));
10854
10841
  };
10855
10842
  const onEnd = () => {
10856
10843
  stdin.off("data", onData);
@@ -10861,12 +10848,12 @@ function readConfigLine() {
10861
10848
  });
10862
10849
  }
10863
10850
  function hashFile(path) {
10864
- return new Promise((resolve3) => {
10851
+ return new Promise((resolve4) => {
10865
10852
  const hash = createHash2("sha256");
10866
10853
  const stream = createReadStream2(path);
10867
10854
  stream.on("data", (chunk) => hash.update(chunk));
10868
- stream.on("end", () => resolve3(hash.digest("hex")));
10869
- stream.on("error", () => resolve3("unreadable"));
10855
+ stream.on("end", () => resolve4(hash.digest("hex")));
10856
+ stream.on("error", () => resolve4("unreadable"));
10870
10857
  });
10871
10858
  }
10872
10859
  function parsePorcelainV2(raw) {
@@ -11391,7 +11378,7 @@ function waitForAbortableResource(promise, signal, dispose, message = "operation
11391
11378
  void promise.then(disposeSafely, () => void 0);
11392
11379
  return Promise.reject(abortError(message));
11393
11380
  }
11394
- return new Promise((resolve3, reject) => {
11381
+ return new Promise((resolve4, reject) => {
11395
11382
  let aborted = false;
11396
11383
  const onAbort = () => {
11397
11384
  aborted = true;
@@ -11406,7 +11393,7 @@ function waitForAbortableResource(promise, signal, dispose, message = "operation
11406
11393
  disposeSafely(resource);
11407
11394
  return;
11408
11395
  }
11409
- resolve3(resource);
11396
+ resolve4(resource);
11410
11397
  },
11411
11398
  (error) => {
11412
11399
  signal.removeEventListener("abort", onAbort);
@@ -11436,7 +11423,7 @@ function appendMessage(buffer, message) {
11436
11423
  function spawnCollectAsync(opts) {
11437
11424
  throwIfAborted(opts.signal, opts.abortMessage);
11438
11425
  const killSignal = opts.killSignal ?? "SIGTERM";
11439
- return new Promise((resolve3, reject) => {
11426
+ return new Promise((resolve4, reject) => {
11440
11427
  const child = spawn2(opts.command, opts.args, {
11441
11428
  cwd: opts.cwd,
11442
11429
  env: opts.env,
@@ -11453,7 +11440,7 @@ function spawnCollectAsync(opts) {
11453
11440
  opts.signal?.removeEventListener("abort", abort);
11454
11441
  let stderr = Buffer.concat(stderrChunks);
11455
11442
  if (fallbackStderr) stderr = appendMessage(stderr, fallbackStderr);
11456
- resolve3({
11443
+ resolve4({
11457
11444
  stdout: Buffer.concat(stdoutChunks),
11458
11445
  stderr,
11459
11446
  code
@@ -15062,7 +15049,7 @@ function guardedS3Transport(signal, operation, deadline) {
15062
15049
  controller.abort(err);
15063
15050
  reject(err);
15064
15051
  };
15065
- const guarded = new Promise((resolve3, reject) => {
15052
+ const guarded = new Promise((resolve4, reject) => {
15066
15053
  const onParentAbort = () => abort(new S3HttpError(503, "S3 HTTP transport aborted"), reject);
15067
15054
  if (signal) {
15068
15055
  signal.addEventListener("abort", onParentAbort, { once: true });
@@ -15076,7 +15063,7 @@ function guardedS3Transport(signal, operation, deadline) {
15076
15063
  (value) => {
15077
15064
  if (settled) return;
15078
15065
  settled = true;
15079
- resolve3(value);
15066
+ resolve4(value);
15080
15067
  },
15081
15068
  (err) => {
15082
15069
  if (settled) return;
@@ -18180,7 +18167,7 @@ function guardedDynamoDbTransport(signal, operation, deadline) {
18180
18167
  controller.abort(err);
18181
18168
  reject(err);
18182
18169
  };
18183
- const guarded = new Promise((resolve3, reject) => {
18170
+ const guarded = new Promise((resolve4, reject) => {
18184
18171
  const onParentAbort = () => abort(
18185
18172
  new DynamoDbHttpError(503, "DynamoDB HTTP transport aborted"),
18186
18173
  reject
@@ -18197,7 +18184,7 @@ function guardedDynamoDbTransport(signal, operation, deadline) {
18197
18184
  (value) => {
18198
18185
  if (settled) return;
18199
18186
  settled = true;
18200
- resolve3(value);
18187
+ resolve4(value);
18201
18188
  },
18202
18189
  (err) => {
18203
18190
  if (settled) return;
@@ -19345,7 +19332,7 @@ async function parseBoundedJsonBody(req, maxBytes, tooLargeMessage) {
19345
19332
  function waitForCallerAbort(promise, signal, message) {
19346
19333
  if (!signal) return promise;
19347
19334
  if (signal.aborted) return Promise.reject(abortError(message));
19348
- return new Promise((resolve3, reject) => {
19335
+ return new Promise((resolve4, reject) => {
19349
19336
  let settled = false;
19350
19337
  const cleanup = () => signal.removeEventListener("abort", onAbort);
19351
19338
  const onAbort = () => {
@@ -19360,7 +19347,7 @@ function waitForCallerAbort(promise, signal, message) {
19360
19347
  if (settled) return;
19361
19348
  settled = true;
19362
19349
  cleanup();
19363
- resolve3(value);
19350
+ resolve4(value);
19364
19351
  },
19365
19352
  (err) => {
19366
19353
  if (settled) return;
@@ -23248,8 +23235,8 @@ async function handleSnapshotCreate(cwd2, req, sendSse2, omitDirNames) {
23248
23235
  let activeSnapshotId;
23249
23236
  let resolveIdAck;
23250
23237
  let rejectIdAck;
23251
- const idAck = new Promise((resolve3, reject) => {
23252
- resolveIdAck = resolve3;
23238
+ const idAck = new Promise((resolve4, reject) => {
23239
+ resolveIdAck = resolve4;
23253
23240
  rejectIdAck = reject;
23254
23241
  });
23255
23242
  (async () => {
@@ -24361,8 +24348,8 @@ function writeToShellWhenReady(id, data) {
24361
24348
  const entry = sessions.get(id);
24362
24349
  if (!entry || entry.meta.exited) return Promise.resolve({ status: "gone" });
24363
24350
  if (entry.ready) return Promise.resolve(writeToShellEntry(entry, data));
24364
- return new Promise((resolve3) => {
24365
- entry.queued.push({ data, resolve: resolve3 });
24351
+ return new Promise((resolve4) => {
24352
+ entry.queued.push({ data, resolve: resolve4 });
24366
24353
  });
24367
24354
  }
24368
24355
  function writeToShell(id, data) {
@@ -24391,14 +24378,14 @@ function resizeShell(id, cols, rows) {
24391
24378
  }
24392
24379
  function waitForShellExit(entry) {
24393
24380
  if (entry.meta.exited) return Promise.resolve(true);
24394
- return new Promise((resolve3) => {
24381
+ return new Promise((resolve4) => {
24395
24382
  let settled = false;
24396
24383
  const finish = (exited) => {
24397
24384
  if (settled) return;
24398
24385
  settled = true;
24399
24386
  clearTimeout(timer2);
24400
24387
  entry.exitListeners.delete(onExit);
24401
- resolve3(exited);
24388
+ resolve4(exited);
24402
24389
  };
24403
24390
  const onExit = () => finish(true);
24404
24391
  entry.exitListeners.add(onExit);
@@ -25582,10 +25569,10 @@ async function runProbeWithTimeout(probe, file, cwd2, timeoutMs, parentSignal) {
25582
25569
  timedOut: false
25583
25570
  })
25584
25571
  );
25585
- const timeoutPromise = new Promise((resolve3) => {
25572
+ const timeoutPromise = new Promise((resolve4) => {
25586
25573
  timer2 = setTimeout(() => {
25587
25574
  controller.abort();
25588
- resolve3({
25575
+ resolve4({
25589
25576
  kind: "fail",
25590
25577
  reason: `timed out after ${timeoutMs}ms`,
25591
25578
  timedOut: true
@@ -26018,6 +26005,102 @@ var init_dev_assets = __esm({
26018
26005
  }
26019
26006
  });
26020
26007
 
26008
+ // web-src/server/file-upload.ts
26009
+ import {
26010
+ closeSync as closeSync2,
26011
+ constants as constants3,
26012
+ openSync as openSync2,
26013
+ unlinkSync as unlinkSync2,
26014
+ writeFileSync as writeFileSync2
26015
+ } from "node:fs";
26016
+ function errorCode(error) {
26017
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
26018
+ return error.code;
26019
+ }
26020
+ return void 0;
26021
+ }
26022
+ function withErrorCode(error, code) {
26023
+ return code === void 0 ? error : Object.assign(error, { code });
26024
+ }
26025
+ function writeAndClose(upload, fd, bytes, fileSystem) {
26026
+ let writeError;
26027
+ try {
26028
+ fileSystem.write(fd, bytes);
26029
+ } catch (error) {
26030
+ writeError = error;
26031
+ }
26032
+ try {
26033
+ fileSystem.close(fd);
26034
+ } catch (closeError) {
26035
+ if (writeError !== void 0) {
26036
+ throw withErrorCode(
26037
+ errorWithCauses(`failed to write and close ${upload.target}`, [
26038
+ writeError,
26039
+ closeError
26040
+ ]),
26041
+ errorCode(writeError)
26042
+ );
26043
+ }
26044
+ throw errorWithCause(`failed to close ${upload.target}`, closeError);
26045
+ }
26046
+ if (writeError !== void 0) throw writeError;
26047
+ }
26048
+ async function writeUploadedFiles(uploads, fileSystem = NODE_FILE_SYSTEM) {
26049
+ const created = [];
26050
+ try {
26051
+ for (const upload of uploads) {
26052
+ const bytes = new Uint8Array(await upload.file.arrayBuffer());
26053
+ const fd = fileSystem.open(upload.target);
26054
+ created.push(upload.target);
26055
+ writeAndClose(upload, fd, bytes, fileSystem);
26056
+ }
26057
+ } catch (error) {
26058
+ const cleanupErrors = [];
26059
+ for (const path of created) {
26060
+ try {
26061
+ fileSystem.remove(path);
26062
+ } catch (cleanupError) {
26063
+ cleanupErrors.push(
26064
+ errorWithCause(
26065
+ `failed to remove partial upload ${path}`,
26066
+ cleanupError
26067
+ )
26068
+ );
26069
+ }
26070
+ }
26071
+ const code = errorCode(error);
26072
+ if (cleanupErrors.length > 0) {
26073
+ throw withErrorCode(
26074
+ errorWithCauses(
26075
+ "failed to write uploaded files and clean up partial files",
26076
+ [error, ...cleanupErrors]
26077
+ ),
26078
+ code
26079
+ );
26080
+ }
26081
+ throw withErrorCode(
26082
+ errorWithCause("failed to write uploaded files", error),
26083
+ code
26084
+ );
26085
+ }
26086
+ }
26087
+ var NODE_FILE_SYSTEM;
26088
+ var init_file_upload = __esm({
26089
+ "web-src/server/file-upload.ts"() {
26090
+ init_error_detail();
26091
+ NODE_FILE_SYSTEM = {
26092
+ open: (path) => openSync2(
26093
+ path,
26094
+ constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW || 0),
26095
+ 420
26096
+ ),
26097
+ write: (fd, bytes) => writeFileSync2(fd, bytes),
26098
+ close: (fd) => closeSync2(fd),
26099
+ remove: (path) => unlinkSync2(path)
26100
+ };
26101
+ }
26102
+ });
26103
+
26021
26104
  // web-src/server/journal.ts
26022
26105
  import { join as join18 } from "node:path";
26023
26106
  function dailyJournalFilePath(root) {
@@ -28960,6 +29043,367 @@ var init_mcp = __esm({
28960
29043
  }
28961
29044
  });
28962
29045
 
29046
+ // web-src/server/os-platform.ts
29047
+ function isWsl(platform, release) {
29048
+ return platform === "linux" && /wsl/i.test(release);
29049
+ }
29050
+ var init_os_platform = __esm({
29051
+ "web-src/server/os-platform.ts"() {
29052
+ }
29053
+ });
29054
+
29055
+ // web-src/server/os-opener.ts
29056
+ import { release as osRelease } from "node:os";
29057
+ function directoryCommands(path, platform) {
29058
+ if (platform === "darwin") {
29059
+ return [{ args: ["open", "--", path], cwd: path }];
29060
+ }
29061
+ if (platform === "win32") {
29062
+ return [{ args: ["explorer.exe", path], cwd: path }];
29063
+ }
29064
+ return [
29065
+ { args: ["xdg-open", path], cwd: path },
29066
+ { args: ["gio", "open", path], cwd: path }
29067
+ ];
29068
+ }
29069
+ function urlCommands(url, cwd2, platform) {
29070
+ if (platform === "darwin") {
29071
+ return [{ args: ["open", url], cwd: cwd2 }];
29072
+ }
29073
+ if (platform === "win32") {
29074
+ return [{ args: ["cmd.exe", "/c", "start", "", url], cwd: cwd2 }];
29075
+ }
29076
+ return [
29077
+ { args: ["xdg-open", url], cwd: cwd2 },
29078
+ { args: ["gio", "open", url], cwd: cwd2 }
29079
+ ];
29080
+ }
29081
+ function commandResultError(command, result) {
29082
+ return Object.assign(
29083
+ new Error(
29084
+ result.code === 0 ? `${command.args[0]} wrote to stderr` : `${command.args[0]} exited with ${result.code}`
29085
+ ),
29086
+ {
29087
+ command: command.args,
29088
+ cwd: command.cwd,
29089
+ result
29090
+ }
29091
+ );
29092
+ }
29093
+ async function executeOpenCommand(command) {
29094
+ try {
29095
+ return await runAsync(command.args, command.cwd, {
29096
+ timeout: OPEN_TIMEOUT_MS
29097
+ });
29098
+ } catch (error) {
29099
+ throw commandExecutionError(command, error);
29100
+ }
29101
+ }
29102
+ function commandSucceeded(result) {
29103
+ return result.code === 0 && result.stderr.trim() === "";
29104
+ }
29105
+ function commandExecutionError(command, cause) {
29106
+ return Object.assign(
29107
+ errorWithCause(`failed to execute ${command.args[0]}`, cause),
29108
+ {
29109
+ command: command.args,
29110
+ cwd: command.cwd
29111
+ }
29112
+ );
29113
+ }
29114
+ async function runOpenCommands(commands, operation, errors = []) {
29115
+ for (const command of commands) {
29116
+ try {
29117
+ const result = await executeOpenCommand(command);
29118
+ if (commandSucceeded(result)) return;
29119
+ errors.push(commandResultError(command, result));
29120
+ } catch (error) {
29121
+ errors.push(
29122
+ error instanceof Error ? error : commandExecutionError(command, error)
29123
+ );
29124
+ }
29125
+ }
29126
+ throw errorWithCauses(`failed to ${operation}`, errors);
29127
+ }
29128
+ async function commandOutput(command) {
29129
+ const result = await executeOpenCommand(command);
29130
+ if (!commandSucceeded(result)) throw commandResultError(command, result);
29131
+ const output = result.stdout.trim();
29132
+ if (!output) {
29133
+ throw Object.assign(new Error(`${command.args[0]} returned no path`), {
29134
+ command: command.args,
29135
+ cwd: command.cwd,
29136
+ result
29137
+ });
29138
+ }
29139
+ return output;
29140
+ }
29141
+ async function wslWindowsCommandCwd(cwd2) {
29142
+ return commandOutput({ args: ["wslpath", "-u", "C:\\"], cwd: cwd2 });
29143
+ }
29144
+ async function openWslDirectory(path) {
29145
+ const errors = [];
29146
+ try {
29147
+ const windowsPath = await commandOutput({
29148
+ args: ["wslpath", "-w", path],
29149
+ cwd: path
29150
+ });
29151
+ const windowsCwd = await wslWindowsCommandCwd(path);
29152
+ const command = {
29153
+ args: ["cmd.exe", "/c", "start", "", windowsPath],
29154
+ cwd: windowsCwd
29155
+ };
29156
+ const result = await executeOpenCommand(command);
29157
+ if (commandSucceeded(result)) return;
29158
+ errors.push(commandResultError(command, result));
29159
+ } catch (error) {
29160
+ errors.push(
29161
+ error instanceof Error ? error : errorWithCause("failed to open directory through WSL", error)
29162
+ );
29163
+ }
29164
+ return runOpenCommands(
29165
+ [
29166
+ { args: ["gio", "open", path], cwd: path },
29167
+ { args: ["xdg-open", path], cwd: path }
29168
+ ],
29169
+ "open directory in OS",
29170
+ errors
29171
+ );
29172
+ }
29173
+ async function openWslUrl(url, cwd2) {
29174
+ const errors = [];
29175
+ try {
29176
+ const windowsCwd = await wslWindowsCommandCwd(cwd2);
29177
+ const command = {
29178
+ args: ["cmd.exe", "/c", "start", "", url],
29179
+ cwd: windowsCwd
29180
+ };
29181
+ const result = await executeOpenCommand(command);
29182
+ if (commandSucceeded(result)) return;
29183
+ errors.push(commandResultError(command, result));
29184
+ } catch (error) {
29185
+ errors.push(
29186
+ error instanceof Error ? error : errorWithCause("failed to open URL through WSL", error)
29187
+ );
29188
+ }
29189
+ return runOpenCommands(
29190
+ [
29191
+ { args: ["gio", "open", url], cwd: cwd2 },
29192
+ { args: ["xdg-open", url], cwd: cwd2 }
29193
+ ],
29194
+ "open URL in OS",
29195
+ errors
29196
+ );
29197
+ }
29198
+ function openDirectoryInOs(path, platform = process.platform, release = osRelease()) {
29199
+ if (isWsl(platform, release)) return openWslDirectory(path);
29200
+ return runOpenCommands(
29201
+ directoryCommands(path, platform),
29202
+ "open directory in OS"
29203
+ );
29204
+ }
29205
+ function openUrlInOs(url, cwd2, platform = process.platform, release = osRelease()) {
29206
+ if (isWsl(platform, release)) return openWslUrl(url, cwd2);
29207
+ return runOpenCommands(urlCommands(url, cwd2, platform), "open URL in OS");
29208
+ }
29209
+ var OPEN_TIMEOUT_MS;
29210
+ var init_os_opener = __esm({
29211
+ "web-src/server/os-opener.ts"() {
29212
+ init_error_detail();
29213
+ init_os_platform();
29214
+ init_runtime();
29215
+ OPEN_TIMEOUT_MS = 15e3;
29216
+ }
29217
+ });
29218
+
29219
+ // web-src/server/os-trash.ts
29220
+ import { randomUUID as randomUUID2 } from "node:crypto";
29221
+ import { existsSync as existsSync8, lstatSync as lstatSync5, mkdirSync as mkdirSync4, renameSync } from "node:fs";
29222
+ import { homedir as homedir3, release as osRelease2 } from "node:os";
29223
+ import { basename as basename3, dirname as dirname6, join as join21, resolve as resolve2 } from "node:path";
29224
+ function windowsTrashScript(path) {
29225
+ const quotedPath = path.replace(/'/g, "''");
29226
+ return [
29227
+ "$ErrorActionPreference = 'Stop';",
29228
+ `$path = '${quotedPath}';`,
29229
+ "Add-Type -TypeDefinition @'",
29230
+ "using System;",
29231
+ "using System.Runtime.InteropServices;",
29232
+ "public static class CodeViewerRecycleBin {",
29233
+ " [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]",
29234
+ " public struct SHFILEOPSTRUCT {",
29235
+ " public IntPtr hwnd;",
29236
+ " public uint wFunc;",
29237
+ " public string pFrom;",
29238
+ " public string pTo;",
29239
+ " public ushort fFlags;",
29240
+ " [MarshalAs(UnmanagedType.Bool)] public bool fAnyOperationsAborted;",
29241
+ " public IntPtr hNameMappings;",
29242
+ " public string lpszProgressTitle;",
29243
+ " }",
29244
+ ' [DllImport("shell32.dll", CharSet = CharSet.Unicode)]',
29245
+ " private static extern int SHFileOperationW(ref SHFILEOPSTRUCT lpFileOp);",
29246
+ " public static void MoveToRecycleBin(string path) {",
29247
+ " const uint FO_DELETE = 0x0003;",
29248
+ " const ushort FOF_SILENT = 0x0004;",
29249
+ " const ushort FOF_NOCONFIRMATION = 0x0010;",
29250
+ " const ushort FOF_ALLOWUNDO = 0x0040;",
29251
+ " const ushort FOF_NOERRORUI = 0x0400;",
29252
+ " var op = new SHFILEOPSTRUCT {",
29253
+ " hwnd = IntPtr.Zero,",
29254
+ " wFunc = FO_DELETE,",
29255
+ ' pFrom = path + "\\0\\0",',
29256
+ " pTo = null,",
29257
+ " fFlags = (ushort)(FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT),",
29258
+ " fAnyOperationsAborted = false,",
29259
+ " hNameMappings = IntPtr.Zero,",
29260
+ " lpszProgressTitle = null",
29261
+ " };",
29262
+ " int result = SHFileOperationW(ref op);",
29263
+ ' if (result != 0) throw new InvalidOperationException("SHFileOperationW failed: " + result);',
29264
+ ' if (op.fAnyOperationsAborted) throw new OperationCanceledException("SHFileOperationW aborted");',
29265
+ " }",
29266
+ "}",
29267
+ "'@;",
29268
+ "[CodeViewerRecycleBin]::MoveToRecycleBin($path);"
29269
+ ].join(" ");
29270
+ }
29271
+ function windowsRestoreTrashScript(originalPath) {
29272
+ const quotedPath = originalPath.replace(/'/g, "''");
29273
+ return [
29274
+ "$ErrorActionPreference = 'Stop';",
29275
+ `$original = '${quotedPath}';`,
29276
+ "$parent = [System.IO.Path]::GetDirectoryName($original);",
29277
+ "$name = [System.IO.Path]::GetFileName($original);",
29278
+ "$shell = New-Object -ComObject Shell.Application;",
29279
+ "$bin = $shell.Namespace(10);",
29280
+ "$restored = $false;",
29281
+ "foreach ($item in $bin.Items()) {",
29282
+ " $deletedFrom = $item.ExtendedProperty('System.Recycle.DeletedFrom');",
29283
+ " if ($item.Name -eq $name -and $deletedFrom -eq $parent) {",
29284
+ " $item.InvokeVerb('ESTORE');",
29285
+ " $restored = $true;",
29286
+ " break;",
29287
+ " }",
29288
+ "}",
29289
+ "if (-not $restored) { throw 'recycle bin item not found'; }"
29290
+ ].join(" ");
29291
+ }
29292
+ function commandResultError2(operation, command, cwd2, result) {
29293
+ return Object.assign(new Error(`${operation} failed`), {
29294
+ command,
29295
+ cwd: cwd2,
29296
+ result
29297
+ });
29298
+ }
29299
+ async function runRequiredCommand(operation, command, cwd2) {
29300
+ let result;
29301
+ try {
29302
+ result = await runAsync(command, cwd2, { timeout: TRASH_TIMEOUT_MS });
29303
+ } catch (cause) {
29304
+ throw Object.assign(errorWithCause(`${operation} failed`, cause), {
29305
+ command,
29306
+ cwd: cwd2
29307
+ });
29308
+ }
29309
+ if (result.code !== 0) {
29310
+ throw commandResultError2(operation, command, cwd2, result);
29311
+ }
29312
+ }
29313
+ function managedTrashRoot(cwd2) {
29314
+ return join21(cwd2, ".code-viewer", "trash");
29315
+ }
29316
+ function movePathIntoTrashDirectory(path, trashRoot) {
29317
+ mkdirSync4(trashRoot, { recursive: true });
29318
+ const name = basename3(path) || "trash-item";
29319
+ const trashPath = join21(trashRoot, `${name}-${randomUUID2()}`);
29320
+ if (existsSync8(trashPath)) {
29321
+ throw Object.assign(new Error("trash destination already exists"), {
29322
+ trashPath
29323
+ });
29324
+ }
29325
+ renameSync(path, trashPath);
29326
+ return { trashPath };
29327
+ }
29328
+ function trashRootForHandle(cwd2, platform, release) {
29329
+ if (platform === "darwin") return join21(homedir3(), ".Trash");
29330
+ if (isWsl(platform, release)) return managedTrashRoot(cwd2);
29331
+ return null;
29332
+ }
29333
+ function unsupportedTrashError(operation, platform, release) {
29334
+ return Object.assign(new Error(`${operation} unsupported`), {
29335
+ platform,
29336
+ release
29337
+ });
29338
+ }
29339
+ async function movePathToTrash(path, cwd2, platform = process.platform, release = osRelease2()) {
29340
+ lstatSync5(path);
29341
+ if (platform === "darwin") {
29342
+ return movePathIntoTrashDirectory(path, join21(homedir3(), ".Trash"));
29343
+ }
29344
+ if (isWsl(platform, release)) {
29345
+ return movePathIntoTrashDirectory(path, managedTrashRoot(cwd2));
29346
+ }
29347
+ if (platform === "win32") {
29348
+ await runRequiredCommand(
29349
+ "move path to Recycle Bin",
29350
+ [
29351
+ "powershell.exe",
29352
+ "-NoProfile",
29353
+ "-NonInteractive",
29354
+ "-ExecutionPolicy",
29355
+ "Bypass",
29356
+ "-Command",
29357
+ windowsTrashScript(path)
29358
+ ],
29359
+ cwd2
29360
+ );
29361
+ return {};
29362
+ }
29363
+ throw unsupportedTrashError("trash", platform, release);
29364
+ }
29365
+ async function restorePathFromTrash(originalPath, trashPath, cwd2, platform = process.platform, release = osRelease2()) {
29366
+ if (existsSync8(originalPath)) {
29367
+ throw new Error("restore target exists");
29368
+ }
29369
+ if (trashPath) {
29370
+ const trashRoot = trashRootForHandle(cwd2, platform, release);
29371
+ if (!trashRoot || dirname6(resolve2(trashPath)) !== resolve2(trashRoot)) {
29372
+ throw new Error("invalid trash handle");
29373
+ }
29374
+ if (!existsSync8(trashPath)) throw new Error("trash item not found");
29375
+ mkdirSync4(dirname6(originalPath), { recursive: true });
29376
+ renameSync(trashPath, originalPath);
29377
+ return;
29378
+ }
29379
+ if (platform === "win32") {
29380
+ await runRequiredCommand(
29381
+ "restore path from Recycle Bin",
29382
+ [
29383
+ "powershell.exe",
29384
+ "-NoProfile",
29385
+ "-NonInteractive",
29386
+ "-ExecutionPolicy",
29387
+ "Bypass",
29388
+ "-Command",
29389
+ windowsRestoreTrashScript(originalPath)
29390
+ ],
29391
+ cwd2
29392
+ );
29393
+ return;
29394
+ }
29395
+ throw unsupportedTrashError("restore from trash", platform, release);
29396
+ }
29397
+ var TRASH_TIMEOUT_MS;
29398
+ var init_os_trash = __esm({
29399
+ "web-src/server/os-trash.ts"() {
29400
+ init_error_detail();
29401
+ init_os_platform();
29402
+ init_runtime();
29403
+ TRASH_TIMEOUT_MS = 6e4;
29404
+ }
29405
+ });
29406
+
28963
29407
  // web-src/server/request-origin.ts
28964
29408
  function requestAllowed(req) {
28965
29409
  const host = req.headers.get("host") || "";
@@ -28984,12 +29428,12 @@ var init_request_origin = __esm({
28984
29428
 
28985
29429
  // web-src/server/watch-supervisor.ts
28986
29430
  import { spawn as spawn3 } from "node:child_process";
28987
- import { join as join21 } from "node:path";
29431
+ import { join as join22 } from "node:path";
28988
29432
  import { fileURLToPath as fileURLToPath3 } from "node:url";
28989
29433
  function watchChildCommand() {
28990
29434
  const entry = process.argv[1] ?? "";
28991
29435
  const isTypeScriptEntry = entry.endsWith(".ts");
28992
- const script = isTypeScriptEntry ? join21(fileURLToPath3(new URL(".", import.meta.url)), "cli.ts") : entry;
29436
+ const script = isTypeScriptEntry ? join22(fileURLToPath3(new URL(".", import.meta.url)), "cli.ts") : entry;
28993
29437
  const loaderArgs = isTypeScriptEntry ? process.execArgv : [];
28994
29438
  return [process.argv[0], ...loaderArgs, script, "watch-child"];
28995
29439
  }
@@ -29766,14 +30210,14 @@ var init_terminal_images = __esm({
29766
30210
 
29767
30211
  // web-src/server/terminal/images.ts
29768
30212
  import { realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
29769
- import { homedir as homedir3 } from "node:os";
29770
- import { basename as basename3, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
30213
+ import { homedir as homedir4 } from "node:os";
30214
+ import { basename as basename4, isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
29771
30215
  function resolveTerminalImage(cwd2, candidate) {
29772
30216
  if (typeof candidate !== "string" || candidate === "") return null;
29773
30217
  if (candidate.includes("\0")) return null;
29774
30218
  if (!terminalImageExtension(candidate)) return null;
29775
- const expanded = candidate.startsWith("~/") ? resolve2(homedir3(), candidate.slice(2)) : candidate;
29776
- const full = isAbsolute2(expanded) ? expanded : resolve2(cwd2, expanded);
30219
+ const expanded = candidate.startsWith("~/") ? resolve3(homedir4(), candidate.slice(2)) : candidate;
30220
+ const full = isAbsolute2(expanded) ? expanded : resolve3(cwd2, expanded);
29777
30221
  try {
29778
30222
  const real = realpathSync7(full);
29779
30223
  const stat3 = statSync7(real);
@@ -29798,7 +30242,7 @@ function resolveTerminalImages(cwd2, candidates) {
29798
30242
  path: image.path,
29799
30243
  // 画面で探すのは、渡された綴りそのもの。実体のパスとは違うことがある。
29800
30244
  candidate,
29801
- name: basename3(image.path),
30245
+ name: basename4(image.path),
29802
30246
  url: terminalImageUrl(image.path)
29803
30247
  });
29804
30248
  }
@@ -29815,7 +30259,7 @@ var init_images = __esm({
29815
30259
 
29816
30260
  // web-src/server/terminal/paste.ts
29817
30261
  import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
29818
- import { join as join22 } from "node:path";
30262
+ import { join as join23 } from "node:path";
29819
30263
  async function savePastedImage(cwd2, mime, base64) {
29820
30264
  const extension = pasteImageExtension(mime);
29821
30265
  if (!extension) {
@@ -29840,8 +30284,8 @@ async function savePastedImage(cwd2, mime, base64) {
29840
30284
  return { status: "invalid", message: "image too large" };
29841
30285
  }
29842
30286
  const name = `${makeTimedId("paste")}.${extension}`;
29843
- const dir = join22(cwd2, PASTE_DIR);
29844
- const path = join22(dir, name);
30287
+ const dir = join23(cwd2, PASTE_DIR);
30288
+ const path = join23(dir, name);
29845
30289
  try {
29846
30290
  await mkdir2(dir, { recursive: true });
29847
30291
  await writeFile2(path, bytes);
@@ -29858,7 +30302,7 @@ var init_paste = __esm({
29858
30302
  "web-src/server/terminal/paste.ts"() {
29859
30303
  init_id();
29860
30304
  init_terminal_paste();
29861
- PASTE_DIR = join22(".code-viewer", "pasted");
30305
+ PASTE_DIR = join23(".code-viewer", "pasted");
29862
30306
  }
29863
30307
  });
29864
30308
 
@@ -30261,22 +30705,14 @@ var init_activity = __esm({
30261
30705
  // web-src/server/preview.ts
30262
30706
  var preview_exports = {};
30263
30707
  import {
30264
- closeSync as closeSync2,
30265
- constants as constants3,
30266
- existsSync as existsSync8,
30267
- lstatSync as lstatSync5,
30268
- mkdirSync as mkdirSync4,
30269
- openSync as openSync2,
30708
+ existsSync as existsSync9,
30709
+ mkdirSync as mkdirSync5,
30270
30710
  readFileSync as readFileSync8,
30271
30711
  realpathSync as realpathSync8,
30272
- renameSync,
30273
30712
  statSync as statSync8,
30274
- unlinkSync as unlinkSync2,
30275
- watch,
30276
- writeFileSync as writeFileSync2
30713
+ watch
30277
30714
  } from "node:fs";
30278
- import { homedir as homedir4 } from "node:os";
30279
- import { basename as basename4, dirname as dirname6, extname as extname2, join as join23, relative as relative8 } from "node:path";
30715
+ import { basename as basename5, dirname as dirname7, extname as extname2, join as join24, relative as relative8 } from "node:path";
30280
30716
  function parseCli() {
30281
30717
  const rest = [];
30282
30718
  for (let i = 2; i < process.argv.length; i++) {
@@ -30400,7 +30836,7 @@ Examples:
30400
30836
  }
30401
30837
  function warnIfLegacyConfigPresent() {
30402
30838
  try {
30403
- if (existsSync8(join23(cwd, ".code-viewer.json"))) {
30839
+ if (existsSync9(join24(cwd, ".code-viewer.json"))) {
30404
30840
  console.warn(
30405
30841
  "[code-viewer] .code-viewer.json is no longer used; configure scope and upload from Viewer Settings instead. The file can be safely removed."
30406
30842
  );
@@ -30507,8 +30943,8 @@ function staticFile(pathname) {
30507
30943
  }
30508
30944
  const spec = map[pathname];
30509
30945
  if (!spec) return null;
30510
- const full = join23(WEB_ROOT, spec[0]);
30511
- if (!existsSync8(full)) return text("not found", 404);
30946
+ const full = join24(WEB_ROOT, spec[0]);
30947
+ if (!existsSync9(full)) return text("not found", 404);
30512
30948
  return new Response(readFileSync8(full), {
30513
30949
  headers: { "Content-Type": spec[1], "Cache-Control": "no-store" }
30514
30950
  });
@@ -30602,7 +31038,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
30602
31038
  files: [],
30603
31039
  totals: { files: 0, additions: 0, deletions: 0 },
30604
31040
  range: "worktree .. worktree",
30605
- project: basename4(cwd),
31041
+ project: basename5(cwd),
30606
31042
  branch: await currentBranchMetadata(),
30607
31043
  generation: responseGeneration
30608
31044
  };
@@ -30645,7 +31081,7 @@ async function computePayload(extras, range, pathFilter = "", responseGeneration
30645
31081
  files: meta,
30646
31082
  totals,
30647
31083
  range: label || "HEAD",
30648
- project: basename4(cwd),
31084
+ project: basename5(cwd),
30649
31085
  branch: await currentBranchMetadata(),
30650
31086
  generation: responseGeneration,
30651
31087
  ...metaResult.error ? { error: metaResult.error } : {}
@@ -30766,7 +31202,7 @@ function safeWorktreePath2(path) {
30766
31202
  return safeWorktreePath(currentSearchEnv(), path);
30767
31203
  }
30768
31204
  function worktreePath(path) {
30769
- return join23(cwd, path);
31205
+ return join24(cwd, path);
30770
31206
  }
30771
31207
  function safeOpenWorktreePath(path) {
30772
31208
  if (path === "") {
@@ -30781,7 +31217,7 @@ function safeOpenWorktreePath(path) {
30781
31217
  return safeWorktreePath2(path);
30782
31218
  }
30783
31219
  function parentRepoPath(path) {
30784
- const parent = dirname6(path);
31220
+ const parent = dirname7(path);
30785
31221
  return parent === "." ? "" : parent;
30786
31222
  }
30787
31223
  function isoDate(ms) {
@@ -30930,7 +31366,7 @@ async function handleTree(url) {
30930
31366
  return json2({
30931
31367
  ref: target,
30932
31368
  path,
30933
- project: basename4(cwd),
31369
+ project: basename5(cwd),
30934
31370
  branch: await currentBranchMetadata(),
30935
31371
  entries: recursive ? entries.map(withStatus) : [
30936
31372
  ...await Promise.all(
@@ -30946,7 +31382,7 @@ async function handleTree(url) {
30946
31382
  }
30947
31383
  async function handleSettings() {
30948
31384
  return json2({
30949
- project: basename4(cwd),
31385
+ project: basename5(cwd),
30950
31386
  branch: await currentBranchMetadata(),
30951
31387
  repo_web_url: cwdHasGitRepository ? await remoteWebUrlAsync(cwd) : null,
30952
31388
  scope: {
@@ -31103,7 +31539,7 @@ async function handleLog(url) {
31103
31539
  }
31104
31540
  function blamePathKey(p) {
31105
31541
  try {
31106
- const st = statSync8(join23(cwd, p));
31542
+ const st = statSync8(join24(cwd, p));
31107
31543
  return `${st.mtimeMs}:${st.size}`;
31108
31544
  } catch {
31109
31545
  return "missing";
@@ -31591,9 +32027,6 @@ function safeUploadFileName(name) {
31591
32027
  if (!SAFE_UPLOAD_EXTENSIONS.has(extname2(trimmed).toLowerCase())) return null;
31592
32028
  return trimmed;
31593
32029
  }
31594
- function uploadOpenFlags() {
31595
- return constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW || 0);
31596
- }
31597
32030
  async function handleUploadFiles(req) {
31598
32031
  if (!uploadEnabled) return text("upload disabled by viewer settings", 403);
31599
32032
  if (req.method !== "POST") return text("method not allowed", 405);
@@ -31637,33 +32070,18 @@ async function handleUploadFiles(req) {
31637
32070
  if (file.size > MAX_UPLOAD_FILE_BYTES) return text("file too large", 413);
31638
32071
  total += file.size;
31639
32072
  if (total > MAX_UPLOAD_TOTAL_BYTES) return text("upload too large", 413);
31640
- const target = join23(realDir, safeName);
31641
- if (relative8(realDir, dirname6(target)) !== "")
32073
+ const target = join24(realDir, safeName);
32074
+ if (relative8(realDir, dirname7(target)) !== "")
31642
32075
  return text("invalid filename", 400);
31643
- if (existsSync8(target)) return text("file exists", 409);
32076
+ if (existsSync9(target)) return text("file exists", 409);
31644
32077
  uploads.push({ file, name: safeName, target });
31645
32078
  }
31646
- const written = [];
31647
32079
  try {
31648
- for (const upload of uploads) {
31649
- const fd = openSync2(upload.target, uploadOpenFlags(), 420);
31650
- try {
31651
- writeFileSync2(fd, new Uint8Array(await upload.file.arrayBuffer()));
31652
- } finally {
31653
- closeSync2(fd);
31654
- }
31655
- written.push(upload.target);
31656
- }
32080
+ await writeUploadedFiles(uploads);
31657
32081
  } catch (error) {
31658
- for (const path of written) {
31659
- try {
31660
- unlinkSync2(path);
31661
- } catch {
31662
- }
31663
- }
31664
32082
  if (error.code === "EEXIST")
31665
- return text("file exists", 409);
31666
- return text("upload failed", 500);
32083
+ return text(formatErrorDetail(error), 409);
32084
+ return text(formatErrorDetail(error), 500);
31667
32085
  }
31668
32086
  triggerUpdate(
31669
32087
  uploads.map((upload) => dir ? `${dir}/${upload.name}` : upload.name)
@@ -31674,78 +32092,6 @@ async function handleUploadFiles(req) {
31674
32092
  generation
31675
32093
  });
31676
32094
  }
31677
- function openOsPath(path) {
31678
- const cmd = process.platform === "darwin" ? ["open", "--", path] : process.platform === "win32" ? ["explorer.exe", path] : ["xdg-open", path];
31679
- spawnDetached(cmd);
31680
- }
31681
- function windowsTrashScript(path) {
31682
- const quotedPath = path.replace(/'/g, "''");
31683
- return [
31684
- "$ErrorActionPreference = 'Stop';",
31685
- `$path = '${quotedPath}';`,
31686
- "Add-Type -TypeDefinition @'",
31687
- "using System;",
31688
- "using System.Runtime.InteropServices;",
31689
- "public static class CodeViewerRecycleBin {",
31690
- " [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]",
31691
- " public struct SHFILEOPSTRUCT {",
31692
- " public IntPtr hwnd;",
31693
- " public uint wFunc;",
31694
- " public string pFrom;",
31695
- " public string pTo;",
31696
- " public ushort fFlags;",
31697
- " [MarshalAs(UnmanagedType.Bool)] public bool fAnyOperationsAborted;",
31698
- " public IntPtr hNameMappings;",
31699
- " public string lpszProgressTitle;",
31700
- " }",
31701
- ' [DllImport("shell32.dll", CharSet = CharSet.Unicode)]',
31702
- " private static extern int SHFileOperationW(ref SHFILEOPSTRUCT lpFileOp);",
31703
- " public static void MoveToRecycleBin(string path) {",
31704
- " const uint FO_DELETE = 0x0003;",
31705
- " const ushort FOF_SILENT = 0x0004;",
31706
- " const ushort FOF_NOCONFIRMATION = 0x0010;",
31707
- " const ushort FOF_ALLOWUNDO = 0x0040;",
31708
- " const ushort FOF_NOERRORUI = 0x0400;",
31709
- " var op = new SHFILEOPSTRUCT {",
31710
- " hwnd = IntPtr.Zero,",
31711
- " wFunc = FO_DELETE,",
31712
- ' pFrom = path + "\\0\\0",',
31713
- " pTo = null,",
31714
- " fFlags = (ushort)(FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT),",
31715
- " fAnyOperationsAborted = false,",
31716
- " hNameMappings = IntPtr.Zero,",
31717
- " lpszProgressTitle = null",
31718
- " };",
31719
- " int result = SHFileOperationW(ref op);",
31720
- ' if (result != 0) throw new InvalidOperationException("SHFileOperationW failed: " + result);',
31721
- ' if (op.fAnyOperationsAborted) throw new OperationCanceledException("SHFileOperationW aborted");',
31722
- " }",
31723
- "}",
31724
- "'@;",
31725
- "[CodeViewerRecycleBin]::MoveToRecycleBin($path);"
31726
- ].join(" ");
31727
- }
31728
- function windowsRestoreTrashScript(originalPath) {
31729
- const quotedPath = originalPath.replace(/'/g, "''");
31730
- return [
31731
- "$ErrorActionPreference = 'Stop';",
31732
- `$original = '${quotedPath}';`,
31733
- "$parent = [System.IO.Path]::GetDirectoryName($original);",
31734
- "$name = [System.IO.Path]::GetFileName($original);",
31735
- "$shell = New-Object -ComObject Shell.Application;",
31736
- "$bin = $shell.Namespace(10);",
31737
- "$restored = $false;",
31738
- "foreach ($item in $bin.Items()) {",
31739
- " $deletedFrom = $item.ExtendedProperty('System.Recycle.DeletedFrom');",
31740
- " if ($item.Name -eq $name -and $deletedFrom -eq $parent) {",
31741
- " $item.InvokeVerb('ESTORE');",
31742
- " $restored = $true;",
31743
- " break;",
31744
- " }",
31745
- "}",
31746
- "if (-not $restored) { throw 'recycle bin item not found'; }"
31747
- ].join(" ");
31748
- }
31749
32095
  function makeUndoId() {
31750
32096
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
31751
32097
  }
@@ -31760,86 +32106,6 @@ function triggerUpdate(changedPaths) {
31760
32106
  const data = changedPaths?.length && changedPaths.length <= 50 ? JSON.stringify({ generation, paths: changedPaths }) : "tick";
31761
32107
  sendSse("update", data);
31762
32108
  }
31763
- function moveMacPathIntoTrash(path) {
31764
- const trashDir = join23(homedir4(), ".Trash");
31765
- const base = basename4(path) || "code-viewer-trash-item";
31766
- const target = join23(
31767
- trashDir,
31768
- `${base}-${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`
31769
- );
31770
- try {
31771
- mkdirSync4(trashDir, { recursive: true });
31772
- renameSync(path, target);
31773
- return { ok: true, trashPath: target };
31774
- } catch (error) {
31775
- return { ok: false, error: String(error) };
31776
- }
31777
- }
31778
- async function movePathToTrash(path) {
31779
- lstatSync5(path);
31780
- if (process.platform === "darwin") {
31781
- return moveMacPathIntoTrash(path);
31782
- }
31783
- if (process.platform === "win32") {
31784
- const res = await runAsync(
31785
- [
31786
- "powershell.exe",
31787
- "-NoProfile",
31788
- "-NonInteractive",
31789
- "-ExecutionPolicy",
31790
- "Bypass",
31791
- "-Command",
31792
- windowsTrashScript(path)
31793
- ],
31794
- cwd,
31795
- { timeout: 6e4 }
31796
- );
31797
- return res.code === 0 ? { ok: true } : { ok: false, error: res.stderr || res.stdout };
31798
- }
31799
- return { ok: false, error: "trash unsupported" };
31800
- }
31801
- async function restoreTrashPath(originalPath, trashPath) {
31802
- const parent = parentRepoPath(originalPath);
31803
- const parentFullPath = safeOpenWorktreePath(parent);
31804
- if (!parentFullPath) return { ok: false, error: "invalid restore target" };
31805
- const original = worktreePath(originalPath);
31806
- if (existsSync8(original))
31807
- return { ok: false, error: "restore target exists" };
31808
- if (trashPath) {
31809
- if (process.platform !== "darwin")
31810
- return { ok: false, error: "invalid trash handle" };
31811
- if (!existsSync8(trashPath))
31812
- return { ok: false, error: "trash item not found" };
31813
- try {
31814
- const trashRoot = join23(homedir4(), ".Trash");
31815
- const trashRelative = relative8(trashRoot, trashPath);
31816
- if (trashRelative === "" || trashRelative.startsWith("..") || trashRelative.startsWith("/") || trashRelative.startsWith("\\"))
31817
- return { ok: false, error: "invalid trash handle" };
31818
- mkdirSync4(dirname6(original), { recursive: true });
31819
- renameSync(trashPath, original);
31820
- return { ok: true };
31821
- } catch (error) {
31822
- return { ok: false, error: String(error) };
31823
- }
31824
- }
31825
- if (process.platform === "win32") {
31826
- const res = await runAsync(
31827
- [
31828
- "powershell.exe",
31829
- "-NoProfile",
31830
- "-NonInteractive",
31831
- "-ExecutionPolicy",
31832
- "Bypass",
31833
- "-Command",
31834
- windowsRestoreTrashScript(original)
31835
- ],
31836
- cwd,
31837
- { timeout: 6e4 }
31838
- );
31839
- return res.code === 0 ? { ok: true } : { ok: false, error: res.stderr || res.stdout };
31840
- }
31841
- return { ok: false, error: "undo unavailable for this trash operation" };
31842
- }
31843
32109
  async function handleOpenPath(req) {
31844
32110
  if (req.method !== "POST") return text("method not allowed", 405);
31845
32111
  if (!sideEffectRequestAllowed2(req)) return text("forbidden", 403);
@@ -31868,7 +32134,12 @@ async function handleOpenPath(req) {
31868
32134
  if (!target) return text("not found", 404);
31869
32135
  const stats = statSync8(target);
31870
32136
  if (!stats.isDirectory()) return text("not a directory", 400);
31871
- openOsPath(target);
32137
+ try {
32138
+ await openDirectoryInOs(target);
32139
+ } catch (error) {
32140
+ console.error("[code-viewer] failed to open path in OS:", error);
32141
+ return text(formatErrorDetail(error), 500);
32142
+ }
31872
32143
  return json2({ ok: true });
31873
32144
  }
31874
32145
  async function handleTrashPath(req) {
@@ -31890,17 +32161,23 @@ async function handleTrashPath(req) {
31890
32161
  const path = typeof body.path === "string" ? body.path.replace(/^\/+|\/+$/g, "") : "";
31891
32162
  if (!path) return text("invalid path", 400);
31892
32163
  if (!safeRepoPath(path)) return text("invalid path", 400);
31893
- if (isGitInternalPath(path)) return text("forbidden", 403);
32164
+ if (isGitInternalPath(path) || isCodeViewerInternalPath(path))
32165
+ return text("forbidden", 403);
31894
32166
  const originalFullPath = safeWorktreePath2(path);
31895
32167
  if (!originalFullPath) return text("not found", 404);
31896
32168
  let changedPaths;
31897
32169
  try {
31898
32170
  const stats = statSync8(originalFullPath);
31899
32171
  if (!stats.isDirectory()) changedPaths = [path];
31900
- } catch {
32172
+ } catch (error) {
32173
+ return text(formatErrorDetail(error), 500);
32174
+ }
32175
+ let moved;
32176
+ try {
32177
+ moved = await movePathToTrash(worktreePath(path), cwd);
32178
+ } catch (error) {
32179
+ return text(formatErrorDetail(error), 500);
31901
32180
  }
31902
- const moved = await movePathToTrash(worktreePath(path));
31903
- if (!moved.ok) return text(moved.error || "trash failed", 500);
31904
32181
  const undo = {
31905
32182
  id: makeUndoId(),
31906
32183
  type: "trash",
@@ -31944,10 +32221,10 @@ async function handleCreateDirectory(req) {
31944
32221
  const targetPath = dir ? `${dir}/${name}` : name;
31945
32222
  if (!safeRepoPath(targetPath) || isGitInternalPath(targetPath))
31946
32223
  return text("invalid target", 400);
31947
- const target = join23(parent, name);
31948
- if (existsSync8(target)) return text("already exists", 409);
32224
+ const target = join24(parent, name);
32225
+ if (existsSync9(target)) return text("already exists", 409);
31949
32226
  try {
31950
- mkdirSync4(target, { recursive: false });
32227
+ mkdirSync5(target, { recursive: false });
31951
32228
  } catch (error) {
31952
32229
  if (error.code === "EEXIST")
31953
32230
  return text("already exists", 409);
@@ -31976,14 +32253,22 @@ async function handleRestoreTrash(req) {
31976
32253
  const trashPath = typeof body.trashPath === "string" ? body.trashPath : "";
31977
32254
  if (!originalPath || !safeRepoPath(originalPath))
31978
32255
  return text("invalid restore target", 400);
31979
- if (isGitInternalPath(originalPath)) return text("forbidden", 403);
31980
- const restored = await restoreTrashPath(originalPath, trashPath || void 0);
31981
- if (!restored.ok) return text(restored.error || "undo failed", 409);
32256
+ if (isGitInternalPath(originalPath) || isCodeViewerInternalPath(originalPath))
32257
+ return text("forbidden", 403);
32258
+ const parent = parentRepoPath(originalPath);
32259
+ if (!safeOpenWorktreePath(parent)) return text("invalid restore target", 400);
32260
+ const original = worktreePath(originalPath);
32261
+ try {
32262
+ await restorePathFromTrash(original, trashPath || void 0, cwd);
32263
+ } catch (error) {
32264
+ return text(formatErrorDetail(error), 409);
32265
+ }
31982
32266
  let changedPaths;
31983
32267
  try {
31984
- const stats = statSync8(worktreePath(originalPath));
32268
+ const stats = statSync8(original);
31985
32269
  if (!stats.isDirectory()) changedPaths = [originalPath];
31986
- } catch {
32270
+ } catch (error) {
32271
+ return text(formatErrorDetail(error), 500);
31987
32272
  }
31988
32273
  triggerUpdate(changedPaths);
31989
32274
  return json2({ ok: true, generation });
@@ -32480,10 +32765,6 @@ function closeSseClients() {
32480
32765
  }
32481
32766
  }
32482
32767
  }
32483
- function openBrowser(url) {
32484
- const cmd = process.platform === "darwin" ? ["open", url] : process.platform === "win32" ? ["cmd.exe", "/c", "start", "", url] : ["xdg-open", url];
32485
- spawnDetached(cmd);
32486
- }
32487
32768
  async function shutdown(exitCode = 0) {
32488
32769
  if (shuttingDown) {
32489
32770
  process.exit(1);
@@ -32574,10 +32855,13 @@ var init_preview = __esm({
32574
32855
  init_command_resolver();
32575
32856
  init_dev_assets();
32576
32857
  init_doctor();
32858
+ init_file_upload();
32577
32859
  init_git();
32578
32860
  init_github_issues();
32579
32861
  init_journal2();
32580
32862
  init_mcp();
32863
+ init_os_opener();
32864
+ init_os_trash();
32581
32865
  init_range();
32582
32866
  init_raw_file_headers();
32583
32867
  init_request_origin();
@@ -32589,8 +32873,8 @@ var init_preview = __esm({
32589
32873
  init_state_store();
32590
32874
  init_watch_supervisor();
32591
32875
  init_worktree_watcher();
32592
- WEB_ROOT = join23(ROOT, "web");
32593
- VERSION = JSON.parse(readFileSync8(join23(ROOT, "package.json"), "utf8")).version;
32876
+ WEB_ROOT = join24(ROOT, "web");
32877
+ VERSION = JSON.parse(readFileSync8(join24(ROOT, "package.json"), "utf8")).version;
32594
32878
  DEFAULT_ARGS = ["HEAD"];
32595
32879
  PREVIEW_HUNKS_DEFAULT = 3;
32596
32880
  PREVIEW_LINES_DEFAULT = 1200;
@@ -32828,7 +33112,7 @@ data: ${watchLimitReached}
32828
33112
  });
32829
33113
  listenPort = server.port;
32830
33114
  if (openAfterStart) {
32831
- openBrowser(`http://127.0.0.1:${server.port}/`);
33115
+ await openUrlInOs(`http://127.0.0.1:${server.port}/`, cwd);
32832
33116
  }
32833
33117
  writeServerRegistry({
32834
33118
  url: `http://127.0.0.1:${server.port}/`,