@xiaoso/dsh-tool-plus 0.1.0-beta.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.mjs CHANGED
@@ -3,7 +3,7 @@ __installBunShim();
3
3
  import { n as fs_promises_shim_exports, t as exists } from "./fs-promises-shim-Bx0fpxuk.mjs";
4
4
  import { i as setRuntimeLogger, n as systemChromiumCandidatesForTest, r as runtimeLogger } from "./launch-DwpE70Pv.mjs";
5
5
  import { $ as peekFile, A as formatAge, C as error, F as $envpos, I as $flag, M as formatCount, P as $env, Q as isProbablyBinary, S as debug, T as warn, U as getAutoQaDbPath, V as VERSION, X as getRemoteDir, Y as getPuppeteerDir, b as isEnotdir, c as combineSignals, d as $which, j as formatBytes$1, k as installBunShim, l as exec, p as readImageMetadata, q as getInstallId, tt as untilAborted, u as render, v as hasFsCode, y as isEnoent } from "./src-D_e1qMwi.mjs";
6
- import { C as truncateToWidth$1, S as replaceTabs, _ as formatCodeFrameLine, a as fetchBinary, b as LRUCache, c as getParallelExtractContent, d as convertFileWithMarkit, f as Settings, g as capParseErrors, h as OMP_DEFAULTS, i as convertWithMarkit, l as ensureTool, m as settings, o as extractWithParallel, p as isSettingsInitialized, s as findParallelApiKey, u as CONVERTIBLE_EXTENSIONS, v as formatParseErrors, x as padding, y as shortenPath } from "./utils-D1QPFbRk.mjs";
6
+ import { C as truncateToWidth$1, S as replaceTabs, _ as formatCodeFrameLine, a as fetchBinary, b as LRUCache, c as getParallelExtractContent, d as convertFileWithMarkit, f as Settings, g as capParseErrors, h as OMP_DEFAULTS, i as convertWithMarkit, l as ensureTool, m as settings, o as extractWithParallel, p as isSettingsInitialized, s as findParallelApiKey, u as CONVERTIBLE_EXTENSIONS, v as formatParseErrors, x as padding, y as shortenPath } from "./utils-D1p8-H8L.mjs";
7
7
  import { t as sanitizeText } from "./sanitize-text-CfcXzw5X.mjs";
8
8
  import { n as isRecord$2 } from "./type-guards-B9VVjRDK.mjs";
9
9
  import { n as ToolError, r as throwIfAborted, t as ToolAbortError } from "./tool-errors-DoyxivAR.mjs";
@@ -719,7 +719,26 @@ async function getOrCreateSnapshot(shell, env, timeoutMs = SNAPSHOT_TIMEOUT_MS)
719
719
  const cached = cachedSnapshotPaths.get(cacheKey);
720
720
  if (cached && fs.existsSync(cached)) return cached;
721
721
  if (cached) cachedSnapshotPaths.delete(cacheKey);
722
- if (process.platform === "win32") return null;
722
+ if (process.platform === "win32") {
723
+ const uid = process.getuid?.();
724
+ const snapshotDir = path.join(os.tmpdir(), uid === void 0 ? "omp-shell-snapshots" : `omp-shell-snapshots-${uid}`);
725
+ const snapshotPath = path.join(snapshotDir, `snapshot-bash-${crypto.randomUUID()}.sh`);
726
+ try {
727
+ fs.mkdirSync(snapshotDir, {
728
+ recursive: true,
729
+ mode: 448
730
+ });
731
+ fs.writeFileSync(snapshotPath, "# Shell snapshot - generated by omp agent (windows minimal)\n", { mode: 384 });
732
+ cachedSnapshotPaths.set(cacheKey, snapshotPath);
733
+ return snapshotPath;
734
+ } catch (err) {
735
+ runtimeLogger().debug("shell-snapshot: windows snapshot dir unusable", {
736
+ dir: snapshotDir,
737
+ err: String(err)
738
+ });
739
+ return null;
740
+ }
741
+ }
723
742
  const rcFile = getShellConfigFile(shell, env);
724
743
  const uid = process.getuid?.();
725
744
  const snapshotDir = path.join(os.tmpdir(), uid === void 0 ? "omp-shell-snapshots" : `omp-shell-snapshots-${uid}`);
@@ -788,6 +807,85 @@ function cleanupSnapshots() {
788
807
  cachedSnapshotPaths.clear();
789
808
  }
790
809
  //#endregion
810
+ //#region src/tools/bash/rm-safe.ts
811
+ /**
812
+ * rmSafe: rm 重定义脚本的生成与快照注入。
813
+ *
814
+ * 生成一个 bash 函数定义脚本(rm → `node <trash-cli.mjs> "$@"`),
815
+ * 通过快照文件(会话 shell 创建时 source 一次)注入:
816
+ * - POSIX:追加到用户环境快照末尾(幂等,带标记行);
817
+ * - Windows:getOrCreateSnapshot 生成"仅注入"快照文件后同样追加。
818
+ *
819
+ * rmSafe 关闭时调用方不注入,快照保持原样,系统 rm 生效。
820
+ * @module @xiaoso/dsh-tool-plus/bash/rm-safe
821
+ */
822
+ /** 注入标记行:injectRmSafe 以此判断是否已注入(幂等)。 */
823
+ const RM_SAFE_MARKER = "# dsh-tool-plus rmSafe";
824
+ /** rm-safe 脚本缓存目录(tmpdir 下,跨会话复用)。 */
825
+ function rmSafeScriptDir() {
826
+ return path.join(os.tmpdir(), "dsh-bash-plus");
827
+ }
828
+ /** trash-cli 构建产物路径(lib/trash-cli.mjs,与主 bundle 同目录)。 */
829
+ function rmSafeCliPath() {
830
+ return fileURLToPath(new URL("./trash-cli.mjs", import.meta.url));
831
+ }
832
+ /** 单引号转义(bash 单引号内 `'\''` 表示一个字面单引号)。 */
833
+ function quote(s) {
834
+ return `'${s.replace(/'/g, `'\\''`)}'`;
835
+ }
836
+ /**
837
+ * 生成 rm 重定义脚本内容:bash 函数 `rm()` 把参数原样转交 trash-cli。
838
+ * 逃生口:`command rm`(bash 的 command 关键字跳过函数/alias 查找)或
839
+ * 绝对路径(/usr/bin/rm)走系统真删除。注意 `\rm` 反斜杠只抑制 alias
840
+ * 展开、不抑制函数查找,对函数重定义无效——不是逃生口。
841
+ */
842
+ function rmSafeScript(nodePath, cliPath) {
843
+ return [
844
+ RM_SAFE_MARKER,
845
+ "rm() {",
846
+ ` ${quote(nodePath)} ${quote(cliPath)} "$@"`,
847
+ "}",
848
+ ""
849
+ ].join("\n");
850
+ }
851
+ /**
852
+ * 确保 rm-safe 脚本存在(内容不变时幂等,不重写文件)。
853
+ * 返回脚本路径;目录/写入失败时返回 null(不抛异常,调用方降级并告警)。
854
+ */
855
+ function ensureRmSafeScript(dir, nodePath, cliPath) {
856
+ const target = path.join(dir, "rm-safe.sh");
857
+ const content = rmSafeScript(nodePath, cliPath);
858
+ try {
859
+ if (fs.readFileSync(target, "utf8") === content) return target;
860
+ } catch {}
861
+ try {
862
+ fs.mkdirSync(dir, { recursive: true });
863
+ fs.writeFileSync(target, content, { mode: 384 });
864
+ return target;
865
+ } catch {
866
+ return null;
867
+ }
868
+ }
869
+ /**
870
+ * 向快照文件追加 `source <rm-safe.sh>`(幂等:已有标记则跳过)。
871
+ * 返回是否注入成功;快照文件不可读/不可写时返回 false(不抛异常)。
872
+ */
873
+ function injectRmSafe(snapshotPath, scriptPath) {
874
+ let content;
875
+ try {
876
+ content = fs.readFileSync(snapshotPath, "utf8");
877
+ } catch {
878
+ return false;
879
+ }
880
+ if (content.includes("# dsh-tool-plus rmSafe")) return true;
881
+ try {
882
+ fs.appendFileSync(snapshotPath, `\n${RM_SAFE_MARKER}\nsource ${quote(scriptPath)}\n`);
883
+ return true;
884
+ } catch {
885
+ return false;
886
+ }
887
+ }
888
+ //#endregion
791
889
  //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/path.ts
792
890
  const WINDOWS_DRIVE_EXTENDED_PREFIX = /^\\\\[?]\\([A-Za-z]:[\\/].*)$/;
793
891
  const WINDOWS_UNC_EXTENDED_PREFIX = /^\\\\[?]\\UNC[\\/]([^\\/]+)[\\/](.+)$/i;
@@ -2325,7 +2423,19 @@ async function executeBash(command, options) {
2325
2423
  const baseShellConfig = getShellConfig();
2326
2424
  const { shell, args, env: shellEnv, prefix } = options?.useUserShell === true ? resolveUserShellConfig(baseShellConfig) : baseShellConfig;
2327
2425
  const bashShell = isBashShell(shell);
2328
- const snapshotPath = bashShell && options?.snapshotEnabled !== false ? await getOrCreateSnapshot(shell, shellEnv) : null;
2426
+ let snapshotPath = bashShell && options?.snapshotEnabled !== false ? await getOrCreateSnapshot(shell, shellEnv) : null;
2427
+ let rmSafeInjectionFailed = false;
2428
+ if (snapshotPath !== null && options?.rmSafe === true) {
2429
+ const scriptPath = ensureRmSafeScript(rmSafeScriptDir(), process.execPath, rmSafeCliPath());
2430
+ if (scriptPath === null || !injectRmSafe(snapshotPath, scriptPath)) {
2431
+ rmSafeInjectionFailed = true;
2432
+ runtimeLogger().warn("rmSafe: injection failed, rm will delete permanently", {
2433
+ scriptPath: scriptPath ?? null,
2434
+ snapshotPath
2435
+ });
2436
+ }
2437
+ }
2438
+ const injectionFailedMarker = rmSafeInjectionFailed ? { rmSafeInjectionFailed: true } : {};
2329
2439
  const minimizerSettings = options?.minimizerSettings ?? DEFAULT_MINIMIZER_SETTINGS;
2330
2440
  const minimizer = options?.minimizerEnabled ?? minimizerSettings.enabled ? buildMinimizerOptions(minimizerSettings) : void 0;
2331
2441
  const commandCwd = resolveShellCwd(options?.cwd);
@@ -2354,6 +2464,7 @@ async function executeBash(command, options) {
2354
2464
  if (options?.signal?.aborted) return {
2355
2465
  exitCode: void 0,
2356
2466
  cancelled: true,
2467
+ ...injectionFailedMarker,
2357
2468
  ...await settle("Command cancelled")
2358
2469
  };
2359
2470
  const shellOptions = {
@@ -2432,6 +2543,7 @@ async function executeBash(command, options) {
2432
2543
  return {
2433
2544
  exitCode: void 0,
2434
2545
  cancelled: true,
2546
+ ...injectionFailedMarker,
2435
2547
  ...winner.kind === "timeout" ? { timedOut: true } : {},
2436
2548
  ...await settle(winner.kind === "timeout" && deadlineTimeoutMs !== void 0 ? `Command timed out after ${Math.round(deadlineTimeoutMs / 1e3)} seconds` : "Command cancelled")
2437
2549
  };
@@ -2448,6 +2560,7 @@ async function executeBash(command, options) {
2448
2560
  exitCode: void 0,
2449
2561
  cancelled: true,
2450
2562
  timedOut: true,
2563
+ ...injectionFailedMarker,
2451
2564
  ...await settle(annotation)
2452
2565
  };
2453
2566
  }
@@ -2457,6 +2570,7 @@ async function executeBash(command, options) {
2457
2570
  return {
2458
2571
  exitCode: void 0,
2459
2572
  cancelled: true,
2573
+ ...injectionFailedMarker,
2460
2574
  ...await settle("Command cancelled")
2461
2575
  };
2462
2576
  }
@@ -2473,6 +2587,7 @@ async function executeBash(command, options) {
2473
2587
  return {
2474
2588
  exitCode: winner.result.exitCode,
2475
2589
  cancelled: false,
2590
+ ...injectionFailedMarker,
2476
2591
  minimized: minimized && minimized.text !== minimized.originalText ? {
2477
2592
  filter: minimized.filter,
2478
2593
  inputBytes: minimized.inputBytes,
@@ -3166,6 +3281,7 @@ function renderBashResult(value) {
3166
3281
  const truncation = formatTruncationNotice(value.output);
3167
3282
  if (truncation !== void 0) notices.push(...truncation);
3168
3283
  else if (value.output.spillPath !== void 0) notices.push(`[full raw stream saved: ${value.output.spillPath}]`);
3284
+ if (value.rmSafeInjectionFailed === true) notices.push("[Safe rm injection failed: rm deletes permanently — use `command rm` to bypass]");
3169
3285
  const markers = [];
3170
3286
  if (value.timedOut) markers.push(`[timed out after ${value.timeoutMs ?? "?"}ms]`);
3171
3287
  else if (value.exitCode !== 0) markers.push(`[exit code: ${value.exitCode}]`);
@@ -3229,6 +3345,15 @@ const TOOL_PLUS_FIELDS = [
3229
3345
  group: "behavior",
3230
3346
  tool: "bash"
3231
3347
  },
3348
+ {
3349
+ name: "rmSafe",
3350
+ kind: "boolean",
3351
+ default: true,
3352
+ labelKey: "rmSafe",
3353
+ hintKey: "rmSafeHint",
3354
+ group: "behavior",
3355
+ tool: "bash"
3356
+ },
3232
3357
  {
3233
3358
  name: "useShellCommandWrapper",
3234
3359
  kind: "boolean",
@@ -3895,6 +4020,7 @@ const Config = z.object({
3895
4020
  interceptorEnabled: z.boolean().default(fieldDefault("interceptorEnabled", true)),
3896
4021
  nonInteractiveEnv: z.boolean().default(fieldDefault("nonInteractiveEnv", true)),
3897
4022
  snapshotEnabled: z.boolean().default(fieldDefault("snapshotEnabled", true)),
4023
+ rmSafe: z.boolean().default(fieldDefault("rmSafe", true)),
3898
4024
  useShellCommandWrapper: z.boolean().default(fieldDefault("useShellCommandWrapper", false)),
3899
4025
  maxBackgroundJobs: z.number().default(fieldDefault("maxBackgroundJobs", 15)),
3900
4026
  outputTruncateStrategy: z.union(["bytes", "lines"]).default(fieldDefault("outputTruncateStrategy", "bytes")),
@@ -3987,6 +4113,7 @@ function resolveConfig(config) {
3987
4113
  interceptorEnabled: config.interceptorEnabled ?? fieldDefault("interceptorEnabled", true),
3988
4114
  nonInteractiveEnv: config.nonInteractiveEnv ?? fieldDefault("nonInteractiveEnv", true),
3989
4115
  snapshotEnabled: config.snapshotEnabled ?? fieldDefault("snapshotEnabled", true),
4116
+ rmSafe: config.rmSafe ?? fieldDefault("rmSafe", true),
3990
4117
  useShellCommandWrapper: config.useShellCommandWrapper ?? fieldDefault("useShellCommandWrapper", false),
3991
4118
  maxBackgroundJobs: config.maxBackgroundJobs ?? fieldDefault("maxBackgroundJobs", 15),
3992
4119
  outputTruncate: {
@@ -4196,27 +4323,145 @@ function probeBrowsers(env = defaultBrowserDetectEnv()) {
4196
4323
  return found;
4197
4324
  }
4198
4325
  //#endregion
4326
+ //#region src/tools/bash/rm-safe-status.ts
4327
+ /**
4328
+ * rmSafe 注入状态查询:文件层(快照标记 + 脚本 + trash-cli 产物)与
4329
+ * 运行时层(一次性 bash 会话 source 快照后探测 `rm` 是否被重定义)。
4330
+ *
4331
+ * 分层语义:
4332
+ * - 文件层成功只证明"注入已写入快照",不证明"会话里生效"——快照是会话
4333
+ * 创建时 source 的,注入只对之后创建的会话生效;trash-cli 产物缺失或
4334
+ * node 路径不可用时函数调用会直接失败。
4335
+ * - 运行时层用一次性 bash 会话模拟"新会话创建"(source 快照 + `type rm`),
4336
+ * 验证的正是注入承诺的语义:新会话里 `rm` 是函数而非系统命令。探测
4337
+ * 无副作用,不触碰任何真实会话。
4338
+ * @module @xiaoso/dsh-tool-plus/bash/rm-safe-status
4339
+ */
4340
+ /**
4341
+ * 解析 `type rm` / `declare -f rm` 探测输出。
4342
+ * `rm is a function` → 函数重定义生效;其余(`rm is /usr/bin/rm`、
4343
+ * `rm is hashed (...)`、空输出)→ 系统命令。
4344
+ */
4345
+ function parseProbeOutput(stdout) {
4346
+ return /rm is a function/.test(stdout) ? "function" : "system";
4347
+ }
4348
+ /**
4349
+ * Windows 路径转 Git Bash 形式(`C:\Users\x` → `/c/Users/x`)。
4350
+ * 非 Windows 原样返回;非盘符路径仅把反斜杠转正斜杠。
4351
+ */
4352
+ function toGitBashPath(p) {
4353
+ if (process.platform !== "win32") return p;
4354
+ const drive = /^([A-Za-z]):[\\/](.*)$/.exec(p);
4355
+ if (drive === null) return p.replace(/\\/g, "/");
4356
+ return `/${drive[1].toLowerCase()}/${drive[2].replace(/\\/g, "/")}`;
4357
+ }
4358
+ /** 默认 spawn 实现:收集 stdout,超时 kill 并标记 timedOut。 */
4359
+ const defaultSpawnDeps = { spawnShell: (shell, args, timeoutMs) => new Promise((resolve, reject) => {
4360
+ const child = spawn(shell, args, { stdio: [
4361
+ "ignore",
4362
+ "pipe",
4363
+ "pipe"
4364
+ ] });
4365
+ let stdout = "";
4366
+ let timedOut = false;
4367
+ const timer = setTimeout(() => {
4368
+ timedOut = true;
4369
+ child.kill();
4370
+ }, timeoutMs);
4371
+ child.stdout.on("data", (chunk) => {
4372
+ stdout += chunk.toString();
4373
+ });
4374
+ child.on("error", (error) => {
4375
+ clearTimeout(timer);
4376
+ reject(error);
4377
+ });
4378
+ child.on("close", () => {
4379
+ clearTimeout(timer);
4380
+ resolve({
4381
+ stdout,
4382
+ timedOut
4383
+ });
4384
+ });
4385
+ }) };
4386
+ /**
4387
+ * 运行时探测:在一次性 bash 会话里 source 快照(模拟新会话创建),
4388
+ * 然后 `type rm` + `declare -f rm` 看 `rm` 是否被重定义为函数。
4389
+ * spawn 失败或超时 → 'unknown'(探测不可用,不代表注入失败)。
4390
+ */
4391
+ async function probeRmSafeRuntime(shell, snapshotPath, deps = defaultSpawnDeps, timeoutMs = 5e3) {
4392
+ const script = `source ${quote(toGitBashPath(snapshotPath))}; type rm; declare -f rm`;
4393
+ try {
4394
+ const { stdout, timedOut } = await deps.spawnShell(shell, ["-c", script], timeoutMs);
4395
+ if (timedOut) return "unknown";
4396
+ return parseProbeOutput(stdout);
4397
+ } catch {
4398
+ return "unknown";
4399
+ }
4400
+ }
4401
+ /**
4402
+ * 查询 rmSafe 注入状态(查询即确保注入:幂等,与 executeBash 的注入
4403
+ * 路径一致)。rmSafe 关闭时调用方不应调用本函数(返回 disabled 由
4404
+ * 调用方短路);此处仍兜底返回 disabled 语义由调用方保证。
4405
+ */
4406
+ async function queryRmSafeStatus(deps) {
4407
+ const snapshotPath = await deps.getOrCreateSnapshot();
4408
+ if (snapshotPath === null) return {
4409
+ status: "failed",
4410
+ reason: "snapshot-unavailable"
4411
+ };
4412
+ if (!deps.cliExists()) return {
4413
+ status: "failed",
4414
+ reason: "cli-missing"
4415
+ };
4416
+ const scriptPath = deps.ensureScript();
4417
+ if (scriptPath === null) return {
4418
+ status: "failed",
4419
+ reason: "script-write-failed"
4420
+ };
4421
+ if (!deps.inject(snapshotPath, scriptPath)) return {
4422
+ status: "failed",
4423
+ reason: "snapshot-write-failed"
4424
+ };
4425
+ const runtime = await deps.probe(snapshotPath);
4426
+ if (runtime === "system") return {
4427
+ status: "failed",
4428
+ reason: "runtime-not-effective"
4429
+ };
4430
+ return {
4431
+ status: "injected",
4432
+ runtime
4433
+ };
4434
+ }
4435
+ //#endregion
4199
4436
  //#region src/host/browser-probe-rpc.ts
4200
4437
  /**
4201
- * Install the browser/detect RPC endpoint. Returns a disposer that removes
4438
+ * Host half of the tool-plus RPC channel: registers the `/tool-plus` logical
4439
+ * channel on the official Connection transport and answers
4440
+ * `browser/detect` (probe the machine for usable browsers) and
4441
+ * `rmSafe/status` (query = ensure + verify the rmSafe injection). The
4442
+ * settings panel (client half) calls these through
4443
+ * `createWebConnectionRpc().call(...)` — the documented public RPC surface
4444
+ * of `@deepseek-ai/dsh-client-connection`, not a private plugin hook.
4445
+ *
4446
+ * Trust policy is `loopback`: the probe only reports local filesystem paths,
4447
+ * so it must never be reachable from a non-loopback host. Registration is
4448
+ * best-effort — if `connection` is missing (non-web deployments) or the
4449
+ * channel is already taken, the endpoints are simply unavailable and the
4450
+ * panel shows "Unavailable".
4451
+ * @module @xiaoso/dsh-tool-plus/host/browser-probe-rpc
4452
+ */
4453
+ /**
4454
+ * Install the tool-plus RPC endpoints. Returns a disposer that removes
4202
4455
  * the registration (call it from the plugin's `ctx.effect` teardown).
4203
4456
  * Never throws for "unavailable" — the return value tells the caller whether
4204
4457
  * the channel is actually live.
4205
4458
  */
4206
- function installBrowserProbeRpc(ctx) {
4459
+ function installBrowserProbeRpc(ctx, deps) {
4207
4460
  let disposer;
4208
4461
  ctx.inject(["connection"], (connectionCtx) => {
4209
4462
  try {
4210
4463
  const handler = async (endpoint, _payload, _signal) => {
4211
- if (endpoint !== "browser/detect") return {
4212
- ok: false,
4213
- error: {
4214
- code: "bad-request",
4215
- message: `Unknown tool-plus endpoint: ${endpoint}`,
4216
- details: { issues: [] }
4217
- }
4218
- };
4219
- try {
4464
+ if (endpoint === "browser/detect") try {
4220
4465
  return {
4221
4466
  ok: true,
4222
4467
  value: { found: probeBrowsers() }
@@ -4231,6 +4476,42 @@ function installBrowserProbeRpc(ctx) {
4231
4476
  }
4232
4477
  };
4233
4478
  }
4479
+ if (endpoint === "rmSafe/status") {
4480
+ if (!deps.getRmSafe()) return {
4481
+ ok: true,
4482
+ value: { status: "disabled" }
4483
+ };
4484
+ try {
4485
+ const { shell, env } = getShellConfig();
4486
+ return {
4487
+ ok: true,
4488
+ value: await queryRmSafeStatus({
4489
+ getOrCreateSnapshot: () => getOrCreateSnapshot(shell, env),
4490
+ cliExists: () => fs.existsSync(rmSafeCliPath()),
4491
+ ensureScript: () => ensureRmSafeScript(rmSafeScriptDir(), process.execPath, rmSafeCliPath()),
4492
+ inject: (snapshotPath, scriptPath) => injectRmSafe(snapshotPath, scriptPath),
4493
+ probe: (snapshotPath) => probeRmSafeRuntime(shell, snapshotPath)
4494
+ })
4495
+ };
4496
+ } catch (error) {
4497
+ return {
4498
+ ok: false,
4499
+ error: {
4500
+ code: "internal",
4501
+ message: error instanceof Error ? error.message : String(error),
4502
+ details: {}
4503
+ }
4504
+ };
4505
+ }
4506
+ }
4507
+ return {
4508
+ ok: false,
4509
+ error: {
4510
+ code: "bad-request",
4511
+ message: `Unknown tool-plus endpoint: ${endpoint}`,
4512
+ details: { issues: [] }
4513
+ }
4514
+ };
4234
4515
  };
4235
4516
  disposer = connectionCtx.connection.rpc.handle(TOOL_PLUS_RPC_CHANNEL, handler, { authority: "loopback" });
4236
4517
  } catch {
@@ -14555,7 +14836,7 @@ let specialHandlersPromise;
14555
14836
  * requires a special handler, so we keep them out of the cold-startup graph.
14556
14837
  */
14557
14838
  function loadSpecialHandlers() {
14558
- specialHandlersPromise ??= import("./scrapers-BlWQMGjU.mjs").then((m) => m.specialHandlers);
14839
+ specialHandlersPromise ??= import("./scrapers-DI8yWlu_.mjs").then((m) => m.specialHandlers);
14559
14840
  return specialHandlersPromise;
14560
14841
  }
14561
14842
  /**
@@ -25704,6 +25985,7 @@ function buildForeground(result, wallTimeMs, timeoutMs) {
25704
25985
  wallTimeMs,
25705
25986
  ...result.workingDir !== void 0 ? { workingDir: result.workingDir } : {},
25706
25987
  ...result.minimized !== void 0 ? { minimized: result.minimized } : {},
25988
+ ...result.rmSafeInjectionFailed === true ? { rmSafeInjectionFailed: true } : {},
25707
25989
  output: {
25708
25990
  text: result.output,
25709
25991
  truncated: result.truncated,
@@ -25772,7 +26054,7 @@ function apply(ctx, config = {}) {
25772
26054
  cfg = current();
25773
26055
  registerModeSensitive();
25774
26056
  });
25775
- const disposeBrowserProbe = installBrowserProbeRpc(ctx);
26057
+ const disposeBrowserProbe = installBrowserProbeRpc(ctx, { getRmSafe: () => cfg.rmSafe });
25776
26058
  ctx.systemPrompt.section({
25777
26059
  name: "tool:bash",
25778
26060
  order: 105,
@@ -26028,6 +26310,7 @@ function apply(ctx, config = {}) {
26028
26310
  headBytes: cfg.outputSinkHeadBytes,
26029
26311
  useShellCommandWrapper: cfg.useShellCommandWrapper,
26030
26312
  snapshotEnabled: cfg.snapshotEnabled,
26313
+ rmSafe: cfg.rmSafe,
26031
26314
  nonInteractiveEnv: cfg.nonInteractiveEnv,
26032
26315
  artifactPath: allocateSpillFile()
26033
26316
  });
@@ -26085,6 +26368,7 @@ function apply(ctx, config = {}) {
26085
26368
  headBytes: cfg.outputSinkHeadBytes,
26086
26369
  useShellCommandWrapper: cfg.useShellCommandWrapper,
26087
26370
  snapshotEnabled: cfg.snapshotEnabled,
26371
+ rmSafe: cfg.rmSafe,
26088
26372
  nonInteractiveEnv: cfg.nonInteractiveEnv,
26089
26373
  artifactPath: allocateSpillFile()
26090
26374
  });
@@ -3,7 +3,7 @@ __installBunShim();
3
3
  import { n as fs_promises_shim_exports } from "./fs-promises-shim-Bx0fpxuk.mjs";
4
4
  import { B as USER_AGENT, G as getDocsRsCacheDir, N as formatNumber, P as $env, T as warn, c as combineSignals, j as formatBytes, l as exec, s as Snowflake, x as parseFrontmatter, y as isEnoent } from "./src-D_e1qMwi.mjs";
5
5
  import { V as tryParseJson } from "./utils-fnV2zzp3.mjs";
6
- import { a as fetchBinary, c as getParallelExtractContent, i as convertWithMarkit, l as ensureTool, m as settings, n as asRecord, o as extractWithParallel, r as asString, s as findParallelApiKey, t as asNumber } from "./utils-D1QPFbRk.mjs";
6
+ import { a as fetchBinary, c as getParallelExtractContent, i as convertWithMarkit, l as ensureTool, m as settings, n as asRecord, o as extractWithParallel, r as asString, s as findParallelApiKey, t as asNumber } from "./utils-D1p8-H8L.mjs";
7
7
  import { n as isRecord } from "./type-guards-B9VVjRDK.mjs";
8
8
  import { r as throwIfAborted, t as ToolAbortError } from "./tool-errors-DoyxivAR.mjs";
9
9
  import { c as getLocalizedText, d as looksLikeHtml, i as decodeHtmlEntities, l as htmlToBasicMarkdown, o as formatIsoDate, r as buildResult, s as formatMediaDuration, u as loadPage } from "./types-BUO0JXO-.mjs";