nolo-cli 0.173.0-alpha.3 → 0.175.0-alpha.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.
Files changed (30) hide show
  1. package/{agentDeleteCommand-HG5EDCO6.js → agentDeleteCommand-WI6U4NFI.js} +2 -2
  2. package/{agentEmailCommands-7YX27N6M.js → agentEmailCommands-Z3TNB4JY.js} +3 -3
  3. package/{agentListCommands-AT5IGY5W.js → agentListCommands-UHHP6HFB.js} +1 -1
  4. package/{agentMachineCommands-WR3IFBB6.js → agentMachineCommands-FIUBHYRT.js} +2 -2
  5. package/{agentRecordCommands-T6PXLK4B.js → agentRecordCommands-EQDWG4RY.js} +3 -3
  6. package/{agentRunCommand-RB3TFF6O.js → agentRunCommand-WZCHXCYS.js} +3 -3
  7. package/{agentRuntimeLocal-72TUBESS.js → agentRuntimeLocal-VQZCOFTL.js} +1 -1
  8. package/{appDeleteCommands-THIHX7BV.js → appDeleteCommands-3QISRABE.js} +1 -1
  9. package/{appDeployCommands-5JA5DDMR.js → appDeployCommands-UIMXDPOG.js} +1 -1
  10. package/{appDeployStatusCommands-EHFPCQHM.js → appDeployStatusCommands-D2ZA54DA.js} +1 -1
  11. package/{appGetCommands-4HSRXSYD.js → appGetCommands-QH2XYWOK.js} +1 -1
  12. package/{appListCommands-TD7BQFWQ.js → appListCommands-HH2HYJDZ.js} +1 -1
  13. package/{chunk-IVPXG433.js → chunk-FGSVT353.js} +425 -153
  14. package/{chunk-EVW2U6RH.js → chunk-MG63GUYM.js} +2 -2
  15. package/{chunk-EY3QEZZR.js → chunk-RNIZNIVT.js} +3 -3
  16. package/{chunk-2OWYKHKI.js → chunk-UILL43UU.js} +1 -1
  17. package/{chunk-FDPODXE3.js → chunk-Z4UP2EDE.js} +1 -1
  18. package/{commandRegistry-PJ3EQ6U7.js → commandRegistry-S54U5ZHX.js} +40 -40
  19. package/{dialogCommands-CPK6MUIJ.js → dialogCommands-A5CHG67A.js} +1 -1
  20. package/{docCreateCommands-GLR2HLYH.js → docCreateCommands-BF354FQW.js} +1 -1
  21. package/{docDeleteCommands-MPE53Z5W.js → docDeleteCommands-3K5K4XFO.js} +1 -1
  22. package/{docReadCommands-MFKWAFDI.js → docReadCommands-A7ZDZIQV.js} +1 -1
  23. package/{docUpdateCommands-3QBT2XTK.js → docUpdateCommands-5MGDR5ME.js} +1 -1
  24. package/index.js +3 -3
  25. package/{localRuntimeAdapter-KQHAGAXR.js → localRuntimeAdapter-ZAXRSVWI.js} +2 -2
  26. package/{machineCommands-DMPHPCQS.js → machineCommands-MT4FDVVG.js} +3 -3
  27. package/{machineWsRunDispatch-736CCGUF.js → machineWsRunDispatch-7T3SEMB6.js} +3 -3
  28. package/package.json +1 -1
  29. package/{readlineWorkspace-K4WNNW2D.js → readlineWorkspace-MTYLMWBM.js} +3 -3
  30. package/{spaceCommands-U4UYF6IB.js → spaceCommands-M44RQPCK.js} +1 -1
@@ -15723,11 +15723,334 @@ async function invokeCapability(capabilityOrName, input, ctx = {}) {
15723
15723
  }
15724
15724
 
15725
15725
  // packages/agent-runtime/localWorkspaceTools.ts
15726
- import { existsSync as existsSync4 } from "node:fs";
15726
+ import { existsSync as existsSync5 } from "node:fs";
15727
15727
  import { createHash as createHash4 } from "node:crypto";
15728
- import { lstat, mkdir, readdir as readdir2, readFile as readFile3, realpath, stat as stat3, writeFile } from "node:fs/promises";
15728
+
15729
+ // packages/agent-runtime/visualRenderer.node.ts
15730
+ import { createRequire } from "node:module";
15731
+ import { existsSync as existsSync4 } from "node:fs";
15732
+ import { mkdir, realpath, stat, readFile, writeFile } from "node:fs/promises";
15733
+ import { dirname as dirname2, isAbsolute as isAbsolute2, relative as relative2, win32 } from "node:path";
15734
+ import { fileURLToPath, pathToFileURL } from "node:url";
15735
+ import { lookup as lookup2 } from "node:dns/promises";
15736
+ import { isIP } from "node:net";
15737
+ function isVisualPathOutside(rel) {
15738
+ return rel === ".." || /^\.\.[\\/]/.test(rel) || isAbsolute2(rel) || win32.isAbsolute(rel);
15739
+ }
15740
+ async function validateVisualUrl(input, workspaceRoot) {
15741
+ const url = new URL(input);
15742
+ if (url.protocol === "file:") {
15743
+ const [root, path] = await Promise.all([realpath(workspaceRoot), realpath(fileURLToPath(url))]);
15744
+ const rel = relative2(root, path);
15745
+ if (isVisualPathOutside(rel)) throw new Error("Renderer file URL is outside workspaceRoot.");
15746
+ if (!(await stat(path)).isFile()) throw new Error("Renderer file URL must point to a file, not a directory.");
15747
+ return pathToFileURL(path);
15748
+ }
15749
+ if (!["http:", "https:"].includes(url.protocol)) throw new Error("Renderer URL must use http:, https:, or file:.");
15750
+ await assertVisualNetworkHost(url.hostname);
15751
+ return url;
15752
+ }
15753
+ async function assertVisualNetworkHost(host) {
15754
+ const hostname = host.replace(/^\[|\]$/g, "");
15755
+ const addresses = isIP(hostname) ? [{ address: hostname }] : await lookup2(hostname, { all: true });
15756
+ for (const { address } of addresses) {
15757
+ const normalized = new URL(`http://${address.includes(":") ? `[${address}]` : address}`).hostname.replace(/^\[|\]$/g, "").toLowerCase();
15758
+ const mapped = normalized.match(/^::ffff:([a-f0-9]+):([a-f0-9]+)$/);
15759
+ const ipv4 = mapped ? `${parseInt(mapped[1], 16) >> 8}.${parseInt(mapped[1], 16) & 255}.${parseInt(mapped[2], 16) >> 8}.${parseInt(mapped[2], 16) & 255}` : normalized;
15760
+ const octets = ipv4.split(".").map(Number);
15761
+ if (ipv4.startsWith("169.254.") || normalized === "fd00:ec2::254" || /^fe[89ab][0-9a-f]:/.test(normalized) || ipv4 === "100.100.100.200") {
15762
+ throw new Error(`Renderer denies metadata/link-local address: ${hostname}`);
15763
+ }
15764
+ const privateV4 = octets.length === 4 && (octets[0] === 10 || octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31 || octets[0] === 192 && octets[1] === 168 || octets[0] === 100 && octets[1] >= 64 && octets[1] <= 127);
15765
+ const privateV6 = /^(fc|fd)[a-f0-9]{2}:/.test(normalized);
15766
+ if ((privateV4 || privateV6) && process.env.NOLO_VISUAL_RENDERER_ALLOW_PRIVATE !== "1") {
15767
+ throw new Error(`Renderer denies private address (LAN access requires NOLO_VISUAL_RENDERER_ALLOW_PRIVATE=1): ${hostname}`);
15768
+ }
15769
+ if (ipv4 === "0.0.0.0" || normalized === "::" || octets.length === 4 && octets[0] >= 224 || normalized.startsWith("ff")) {
15770
+ throw new Error(`Renderer denies non-unicast address: ${hostname}`);
15771
+ }
15772
+ }
15773
+ }
15774
+ var VisualResourceError = class extends Error {
15775
+ detectedAt = performance.now();
15776
+ };
15777
+ var VisualRenderSemaphore = class {
15778
+ constructor(limit) {
15779
+ this.limit = limit;
15780
+ }
15781
+ active = 0;
15782
+ queue = [];
15783
+ async run(task) {
15784
+ if (this.active >= this.limit) await new Promise((resolve6) => this.queue.push(resolve6));
15785
+ else this.active++;
15786
+ try {
15787
+ return await task();
15788
+ } finally {
15789
+ const next = this.queue.shift();
15790
+ if (next) next();
15791
+ else this.active--;
15792
+ }
15793
+ }
15794
+ };
15795
+ var configuredConcurrency = Number(process.env.NOLO_VISUAL_RENDERER_CONCURRENCY ?? 2);
15796
+ var renderSlots = new VisualRenderSemaphore(Number.isFinite(configuredConcurrency) ? Math.max(1, Math.min(2, Math.floor(configuredConcurrency))) : 2);
15797
+ var require2 = createRequire(import.meta.url);
15798
+ function visualRendererCapability() {
15799
+ if (process.env.NOLO_VISUAL_RENDERER_DISABLED === "1") return { available: false, reason: "Platform renderer disabled by NOLO_VISUAL_RENDERER_DISABLED=1 on this runtime host." };
15800
+ try {
15801
+ const { chromium } = require2("playwright");
15802
+ const executablePath = [chromium.executablePath(), resolveExecutableOnPath("google-chrome"), resolveExecutableOnPath("chromium"), resolveExecutableOnPath("chromium-browser")].find((path) => !!path && existsSync4(path));
15803
+ if (!executablePath) return { available: false, reason: "No Chromium renderer installed. Install Playwright Chromium (playwright install chromium) or system Chromium/Google Chrome on the runtime host." };
15804
+ return { available: true, chromium, executablePath };
15805
+ } catch {
15806
+ return { available: false, reason: "Platform renderer unavailable: install the platform Playwright dependency and Chromium on the runtime host (not a workspace npm script)." };
15807
+ }
15808
+ }
15809
+ async function renderVisualPage(args2) {
15810
+ const capability = visualRendererCapability();
15811
+ if (!capability.available) throw new Error(capability.reason);
15812
+ const url = await validateVisualUrl(args2.url, args2.workspaceRoot);
15813
+ return renderSlots.run(async () => {
15814
+ const timeout4 = Math.max(1, Math.min(args2.timeoutMs ?? 2e4, 2e4));
15815
+ const deadline = Date.now() + timeout4;
15816
+ const browser = await capability.chromium.launch({ executablePath: capability.executablePath, headless: true, timeout: timeout4 });
15817
+ let timer;
15818
+ try {
15819
+ const page = await browser.newPage({ viewport: { width: 1440, height: 1e3 }, serviceWorkers: "block" });
15820
+ const localOrigin = "https://nolo-workspace.invalid";
15821
+ const localFile = url.protocol === "file:";
15822
+ let routeError;
15823
+ let rejectRoute;
15824
+ const routeFailure = new Promise((_, reject) => {
15825
+ rejectRoute = reject;
15826
+ });
15827
+ await page.route("**/*", async (route) => {
15828
+ try {
15829
+ const requestUrl = new URL(route.request().url());
15830
+ if (localFile && requestUrl.origin === localOrigin) {
15831
+ const file = await validateVisualUrl(`file://${requestUrl.pathname}`, args2.workspaceRoot);
15832
+ const path = fileURLToPath(file);
15833
+ const type = path.endsWith(".html") ? "text/html; charset=utf-8" : path.endsWith(".css") ? "text/css; charset=utf-8" : path.endsWith(".js") ? "application/javascript; charset=utf-8" : void 0;
15834
+ await route.fulfill({ body: await readFile(path), ...type ? { contentType: type } : {} });
15835
+ } else {
15836
+ await validateVisualUrl(requestUrl.href, args2.workspaceRoot);
15837
+ if (requestUrl.protocol === "file:") throw new Error("Direct file requests are forbidden.");
15838
+ let next = requestUrl;
15839
+ for (let hop = 0; ; hop++) {
15840
+ await validateVisualUrl(next.href, args2.workspaceRoot);
15841
+ const response = await route.fetch({ url: next.href, maxRedirects: 0, timeout: Math.max(1, deadline - Date.now()) });
15842
+ const location = response.headers()["location"];
15843
+ if ([301, 302, 303, 307, 308].includes(response.status()) && location) {
15844
+ if (hop >= 10) throw new Error("Renderer redirect limit exceeded.");
15845
+ next = new URL(location, next);
15846
+ if (!["http:", "https:"].includes(next.protocol)) throw new Error("Renderer redirects must use http(s).");
15847
+ continue;
15848
+ }
15849
+ await route.fulfill({ response });
15850
+ break;
15851
+ }
15852
+ }
15853
+ } catch (error) {
15854
+ routeError = new VisualResourceError(error instanceof Error ? error.message : String(error));
15855
+ const closing = page.close({ runBeforeUnload: false }).catch(() => {
15856
+ });
15857
+ rejectRoute(routeError);
15858
+ await Promise.all([closing, route.abort().catch(() => {
15859
+ })]);
15860
+ }
15861
+ });
15862
+ const work = async () => {
15863
+ page.setDefaultTimeout(timeout4);
15864
+ await page.goto(localFile ? `${localOrigin}${url.pathname}` : url.href, { waitUntil: "load", timeout: timeout4 });
15865
+ await page.locator(args2.waitSelector).first().waitFor({ state: "visible" });
15866
+ if (args2.scrollSelector) await page.locator(args2.scrollSelector).first().scrollIntoViewIfNeeded();
15867
+ const targetSelector = args2.focusSelector ?? args2.waitSelector;
15868
+ await page.locator(targetSelector).first().scrollIntoViewIfNeeded();
15869
+ if (args2.expectText) await page.getByText(args2.expectText, { exact: false }).first().waitFor({ state: "visible" });
15870
+ const screenshot = await page.screenshot({ type: "png", timeout: timeout4 });
15871
+ const metrics = await page.evaluate(({ waitSelector, scrollSelector, targetSelector: targetSelector2 }) => {
15872
+ const target = document.querySelector(targetSelector2);
15873
+ const rect = target.getBoundingClientRect();
15874
+ const style = getComputedStyle(target);
15875
+ return {
15876
+ waitCount: document.querySelectorAll(waitSelector).length,
15877
+ scrollCount: scrollSelector ? document.querySelectorAll(scrollSelector).length : null,
15878
+ focusCount: document.querySelectorAll(targetSelector2).length,
15879
+ targetText: target.textContent?.trim().slice(0, 240) ?? "",
15880
+ targetRect: Object.fromEntries(["width", "height", "left", "right", "top", "bottom"].map((key) => [key, Math.round(rect[key])])),
15881
+ targetComputed: Object.fromEntries(["display", "visibility", "opacity", "position", "width", "height", "pointerEvents"].map((key) => [key, style[key]])),
15882
+ bodyTextSample: document.body.innerText.slice(0, 500)
15883
+ };
15884
+ }, { waitSelector: args2.waitSelector, scrollSelector: args2.scrollSelector, targetSelector });
15885
+ if (routeError) throw routeError;
15886
+ const result4 = { ...metrics, pageUrl: localFile ? url.href : page.url(), title: await page.title(), viewport: page.viewportSize(), renderer: "platform-playwright", capturedAt: (/* @__PURE__ */ new Date()).toISOString() };
15887
+ if (Date.now() >= deadline) throw new Error("Renderer timed out before screenshot persistence.");
15888
+ if (timer) clearTimeout(timer);
15889
+ await mkdir(dirname2(args2.screenshotPath), { recursive: true });
15890
+ await writeFile(args2.screenshotPath, screenshot);
15891
+ return result4;
15892
+ };
15893
+ return await Promise.race([routeFailure, work(), new Promise((_, reject) => {
15894
+ timer = setTimeout(() => reject(new Error(`Renderer timed out after ${timeout4}ms.`)), Math.max(1, deadline - Date.now()));
15895
+ })]);
15896
+ } finally {
15897
+ if (timer) clearTimeout(timer);
15898
+ await browser.close();
15899
+ }
15900
+ });
15901
+ }
15902
+
15903
+ // packages/agent-runtime/hostWindowCapture.ts
15904
+ import { spawn as spawn2 } from "node:child_process";
15905
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
15906
+ var HOST_CAPTURE_TIMEOUT_MS = 9e3;
15907
+ var UNAVAILABLE = "\u5F53\u524D\u73AF\u5883\u65E0\u53EF\u7528\u622A\u5C4F\u6743\u9650/\u670D\u52A1";
15908
+ var PORTAL_SCRIPT = String.raw`
15909
+ import json, signal
15910
+ from gi.repository import Gio, GLib
15911
+ bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
15912
+ loop = GLib.MainLoop()
15913
+ handle = None
15914
+ finished = False
15915
+ result = None
15916
+ responses = {}
15917
+ def response(conn, sender, path, interface, name, parameters, data):
15918
+ global result, finished
15919
+ responses[path] = parameters.unpack()
15920
+ if handle == path:
15921
+ result = responses[path]
15922
+ finished = True
15923
+ loop.quit()
15924
+ def stop(*args):
15925
+ loop.quit()
15926
+ return False
15927
+ subscription = bus.signal_subscribe('org.freedesktop.portal.Desktop', 'org.freedesktop.portal.Request', 'Response', None, None, Gio.DBusSignalFlags.NONE, response, None)
15928
+ signal.signal(signal.SIGTERM, stop)
15929
+ try:
15930
+ options = {'interactive': GLib.Variant('b', True), 'handle_token': GLib.Variant('s', 'nolo_capture')}
15931
+ handle = bus.call_sync('org.freedesktop.portal.Desktop', '/org/freedesktop/portal/desktop', 'org.freedesktop.portal.Screenshot', 'Screenshot', GLib.Variant('(sa{sv})', ('', options)), GLib.VariantType.new('(o)'), Gio.DBusCallFlags.NONE, 7000, None).unpack()[0]
15932
+ if handle in responses:
15933
+ result = responses[handle]
15934
+ finished = True
15935
+ else:
15936
+ GLib.timeout_add(8000, stop)
15937
+ loop.run()
15938
+ if result is None:
15939
+ print(json.dumps({'error': 'portal response timeout'}), flush=True)
15940
+ else:
15941
+ code, values = result
15942
+ print(json.dumps({'code': code, 'uri': values.get('uri')}), flush=True)
15943
+ finally:
15944
+ if handle and not finished:
15945
+ try:
15946
+ bus.call_sync('org.freedesktop.portal.Desktop', handle, 'org.freedesktop.portal.Request', 'Close', None, None, Gio.DBusCallFlags.NONE, 200, None)
15947
+ except Exception:
15948
+ pass
15949
+ bus.signal_unsubscribe(subscription)
15950
+ `;
15951
+ var capturePortalScreenshot = (signal) => new Promise((resolve6, reject) => {
15952
+ const child = spawn2("/usr/bin/python3", ["-c", PORTAL_SCRIPT], { stdio: ["ignore", "pipe", "pipe"] });
15953
+ let stdout = "";
15954
+ let stderr = "";
15955
+ const stop = () => {
15956
+ child.kill("SIGTERM");
15957
+ };
15958
+ signal.addEventListener("abort", stop, { once: true });
15959
+ if (signal.aborted) stop();
15960
+ child.stdout.on("data", (data) => {
15961
+ stdout = (stdout + data).slice(-16384);
15962
+ });
15963
+ child.stderr.on("data", (data) => {
15964
+ stderr = (stderr + data).slice(-4096);
15965
+ });
15966
+ child.once("error", reject);
15967
+ child.once("close", (code) => {
15968
+ signal.removeEventListener("abort", stop);
15969
+ if (signal.aborted) return reject(new Error("portal capture timeout"));
15970
+ if (code !== 0) return reject(new Error(stderr.trim() || `portal helper exited ${code}`));
15971
+ try {
15972
+ resolve6(JSON.parse(stdout));
15973
+ } catch {
15974
+ reject(new Error("invalid portal response"));
15975
+ }
15976
+ });
15977
+ });
15978
+ function createHostWindowCapture(options = {}) {
15979
+ const concurrency = Math.max(1, Math.min(2, Math.floor(options.concurrency || 1)));
15980
+ const timeoutMs = Math.max(1, Math.min(HOST_CAPTURE_TIMEOUT_MS, options.timeoutMs || HOST_CAPTURE_TIMEOUT_MS));
15981
+ let active = 0;
15982
+ const waiters = [];
15983
+ function acquire(signal) {
15984
+ return new Promise((resolve6, reject) => {
15985
+ const abort = () => {
15986
+ const index = waiters.indexOf(start);
15987
+ if (index >= 0) waiters.splice(index, 1);
15988
+ reject(new Error("portal capture timeout (queue)"));
15989
+ };
15990
+ const start = () => {
15991
+ signal.removeEventListener("abort", abort);
15992
+ active++;
15993
+ resolve6();
15994
+ };
15995
+ if (signal.aborted) return abort();
15996
+ if (active < concurrency) start();
15997
+ else {
15998
+ waiters.push(start);
15999
+ signal.addEventListener("abort", abort, { once: true });
16000
+ }
16001
+ });
16002
+ }
16003
+ return async () => {
16004
+ const controller = new AbortController();
16005
+ let timer;
16006
+ const work = async () => {
16007
+ await acquire(controller.signal);
16008
+ try {
16009
+ const reply = await (options.portal ?? capturePortalScreenshot)(controller.signal);
16010
+ if (controller.signal.aborted) throw new Error("portal capture timeout");
16011
+ if (reply.error) throw new Error(reply.error);
16012
+ if (reply.code !== 0) throw new Error(reply.code === 1 ? "portal denied/cancelled (Response 1)" : `portal failed (Response ${reply.code})`);
16013
+ if (!reply.uri) throw new Error("portal returned no file URI");
16014
+ const uri = new URL(reply.uri);
16015
+ if (uri.protocol !== "file:" || uri.hostname && uri.hostname !== "localhost") throw new Error("portal returned a non-local file URI");
16016
+ const path = fileURLToPath2(uri);
16017
+ const image = await (options.readImage ?? readLocalImage)(path);
16018
+ if (!image.images?.length) throw new Error("portal image could not be decoded");
16019
+ const dimensions = /\[image (\d+x\d+) (\d+) bytes/.exec(image.content);
16020
+ if (!dimensions) throw new Error("invalid image summary");
16021
+ return {
16022
+ ...image,
16023
+ content: `[screenshot ${dimensions[1]} ${Math.round(Number(dimensions[2]) / 1024)}KB \u2192 ${path}]
16024
+ \u6765\u6E90\uFF1A\u5BBF\u4E3B portal \u5168\u5C4F/\u4EA4\u4E92\u9009\u62E9\u7ED3\u679C\uFF1Bportal \u672A\u63D0\u4F9B\u5E94\u7528/\u7A97\u53E3\u8EAB\u4EFD\uFF0C\u65E0\u6CD5\u786E\u8BA4\u7A97\u53E3\u540D\u3002`
16025
+ };
16026
+ } finally {
16027
+ active--;
16028
+ waiters.shift()?.();
16029
+ }
16030
+ };
16031
+ try {
16032
+ return await Promise.race([
16033
+ work(),
16034
+ new Promise((_, reject) => {
16035
+ timer = setTimeout(() => {
16036
+ controller.abort();
16037
+ reject(new Error("portal capture timeout"));
16038
+ }, timeoutMs);
16039
+ })
16040
+ ]);
16041
+ } catch (error) {
16042
+ return { content: `${UNAVAILABLE}\uFF1A${error instanceof Error ? error.message : String(error)}` };
16043
+ } finally {
16044
+ clearTimeout(timer);
16045
+ }
16046
+ };
16047
+ }
16048
+ var captureHostWindow = createHostWindowCapture();
16049
+
16050
+ // packages/agent-runtime/localWorkspaceTools.ts
16051
+ import { lstat, mkdir as mkdir2, readdir as readdir2, readFile as readFile4, realpath as realpath2, stat as stat4, writeFile as writeFile2 } from "node:fs/promises";
15729
16052
  import { homedir } from "node:os";
15730
- import { dirname as dirname2, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve4, sep } from "node:path";
16053
+ import { dirname as dirname3, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve4, sep } from "node:path";
15731
16054
  import { spawn as spawnChildProcess3 } from "node:child_process";
15732
16055
 
15733
16056
  // packages/core/readRangeLedger.ts
@@ -15763,15 +16086,15 @@ function isLineRangeCovered(request3, covered) {
15763
16086
  }
15764
16087
 
15765
16088
  // packages/agent-runtime/fileTextSearch.ts
15766
- import { readdir, readFile as readFile2, stat as stat2 } from "node:fs/promises";
15767
- import { basename, relative as relative2, resolve as resolve3 } from "node:path";
16089
+ import { readdir, readFile as readFile3, stat as stat3 } from "node:fs/promises";
16090
+ import { basename, relative as relative3, resolve as resolve3 } from "node:path";
15768
16091
  import { spawn as spawnChildProcess2 } from "node:child_process";
15769
16092
 
15770
16093
  // packages/agent-runtime/gitignore.ts
15771
- import { readFile, stat } from "node:fs/promises";
16094
+ import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
15772
16095
  import { resolve as resolve2 } from "node:path";
15773
16096
  async function readRootGitignorePatterns(workspaceRoot) {
15774
- const content = await readFile(resolve2(workspaceRoot, ".gitignore"), "utf8").catch(() => null);
16097
+ const content = await readFile2(resolve2(workspaceRoot, ".gitignore"), "utf8").catch(() => null);
15775
16098
  if (content === null) return [];
15776
16099
  return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!")).flatMap((line) => {
15777
16100
  const unanchored = line.replace(/^\//, "");
@@ -15782,7 +16105,7 @@ async function readRootGitignorePatterns(workspaceRoot) {
15782
16105
  }
15783
16106
  async function hasRootGitignore(workspaceRoot) {
15784
16107
  try {
15785
- await stat(resolve2(workspaceRoot, ".gitignore"));
16108
+ await stat2(resolve2(workspaceRoot, ".gitignore"));
15786
16109
  return true;
15787
16110
  } catch {
15788
16111
  return false;
@@ -15791,7 +16114,7 @@ async function hasRootGitignore(workspaceRoot) {
15791
16114
 
15792
16115
  // packages/agent-runtime/fileTextSearch.ts
15793
16116
  var normalizePath = (value) => value.replaceAll("\\", "/");
15794
- var pathFor = (root, file, absolute = false) => absolute ? normalizePath(file) : normalizePath(relative2(root, file) || basename(file));
16117
+ var pathFor = (root, file, absolute = false) => absolute ? normalizePath(file) : normalizePath(relative3(root, file) || basename(file));
15795
16118
  var COMMAND_LIMIT_HEADROOM = 1;
15796
16119
  async function runCommand(binary, args2, cwd, maxResults) {
15797
16120
  const recordLimit = maxResults === void 0 ? 0 : Math.max(0, maxResults) + COMMAND_LIMIT_HEADROOM;
@@ -15851,7 +16174,7 @@ async function decorate(root, raw, context3, absolute = false) {
15851
16174
  let lines = cache.get(x.file);
15852
16175
  if (!lines) {
15853
16176
  try {
15854
- lines = (await readFile2(x.file, "utf8")).split(/\r\n|\r|\n/);
16177
+ lines = (await readFile3(x.file, "utf8")).split(/\r\n|\r|\n/);
15855
16178
  } catch {
15856
16179
  continue;
15857
16180
  }
@@ -15880,13 +16203,13 @@ function globMatch(pattern, value) {
15880
16203
  const r = new RegExp(`^${source}$`);
15881
16204
  return r.test(value) || r.test(basename(value));
15882
16205
  }
15883
- var defaultReadTextFile = (filePath) => readFile2(filePath, "utf8").catch(() => "");
16206
+ var defaultReadTextFile = (filePath) => readFile3(filePath, "utf8").catch(() => "");
15884
16207
  async function jsSearch(o, targetPath) {
15885
16208
  const max4 = o.maxResults ?? Number.MAX_SAFE_INTEGER;
15886
16209
  const excludes = o.exclude ?? [];
15887
16210
  const readText = o.readTextFile ?? defaultReadTextFile;
15888
16211
  const files = [];
15889
- const targetStat = await stat2(targetPath).catch(() => null);
16212
+ const targetStat = await stat3(targetPath).catch(() => null);
15890
16213
  if (!targetStat) return { matches: [], totalMatches: 0, truncated: false, engine: "js" };
15891
16214
  const ignorePatterns = o.includeIgnored ? [] : await readRootGitignorePatterns(o.root);
15892
16215
  const isIgnored = (rel) => ignorePatterns.some((pattern) => globMatch(pattern, rel));
@@ -15932,7 +16255,7 @@ var COMMAND_ENGINES = [
15932
16255
  ];
15933
16256
  async function searchFileTextMatches(o) {
15934
16257
  const targetPath = resolve3(o.root, o.target || ".");
15935
- const targetStat = await stat2(targetPath).catch(() => null);
16258
+ const targetStat = await stat3(targetPath).catch(() => null);
15936
16259
  if (!targetStat) return { matches: [], totalMatches: 0, truncated: false, engine: "js" };
15937
16260
  if (targetStat.isFile()) return jsSearch(o, targetPath);
15938
16261
  const run2 = o.runCommand ?? runCommand, max4 = o.maxResults;
@@ -16241,6 +16564,7 @@ function runTasks(args2) {
16241
16564
  var WORKSPACE_TOOL_NAMES = [
16242
16565
  "readFile",
16243
16566
  "read_image",
16567
+ "captureHostWindow",
16244
16568
  "writeFile",
16245
16569
  "editFile",
16246
16570
  "globFiles",
@@ -16397,7 +16721,7 @@ function buildCaptureVisualStateTool() {
16397
16721
  type: "function",
16398
16722
  function: {
16399
16723
  name: "captureVisualState",
16400
- description: "Capture a real local app screenshot and DOM/computed-style metrics for a selected UI state.",
16724
+ description: "Render a URL to an image and DOM/computed-style metrics using platform Playwright/Chromium on the runtime host. Requires an installed renderer; never runs workspace scripts.",
16401
16725
  parameters: {
16402
16726
  type: "object",
16403
16727
  properties: {
@@ -16546,6 +16870,16 @@ function buildTaskStopTool() {
16546
16870
  };
16547
16871
  }
16548
16872
  function buildWorkspaceToolDefinition(toolName) {
16873
+ if (toolName === "captureHostWindow") {
16874
+ return {
16875
+ type: "function",
16876
+ function: {
16877
+ name: "captureHostWindow",
16878
+ description: "Capture the host desktop through the Screenshot portal (interactive permission/selection may appear). Returns an image or a fail-closed reason within 9s. Portal does not identify the application/window: never infer a window name. Linux host requires /usr/bin/python3 with Gio. No arguments.",
16879
+ parameters: { type: "object", properties: {}, additionalProperties: false }
16880
+ }
16881
+ };
16882
+ }
16549
16883
  if (toolName === "read_image") {
16550
16884
  return {
16551
16885
  type: "function",
@@ -16668,6 +17002,7 @@ function buildLocalWorkspaceOpenAiTools(args2) {
16668
17002
  const declaredTools = new Set(args2.toolNames ?? []);
16669
17003
  return WORKSPACE_TOOL_NAMES.filter((toolName) => {
16670
17004
  if (!declaredTools.has(toolName)) return false;
17005
+ if (toolName === "captureVisualState" && !visualRendererCapability().available) return false;
16671
17006
  if (!args2.exposeShellTools && SHELL_TOOL_NAMES.includes(toolName)) {
16672
17007
  return false;
16673
17008
  }
@@ -16682,11 +17017,11 @@ function parseWorkspaceToolArguments(raw) {
16682
17017
  }
16683
17018
  }
16684
17019
  function normalizeWorkspaceRelativePath(args2) {
16685
- const relativePath = relative3(args2.workspaceRoot, args2.targetPath);
17020
+ const relativePath = relative4(args2.workspaceRoot, args2.targetPath);
16686
17021
  return relativePath || ".";
16687
17022
  }
16688
17023
  function isPathInsideWorkspace(args2) {
16689
- const relativePath = relative3(args2.workspaceRoot, args2.targetPath);
17024
+ const relativePath = relative4(args2.workspaceRoot, args2.targetPath);
16690
17025
  return relativePath === "" || !relativePath.startsWith("..") && !relativePath.includes(`..${sep}`);
16691
17026
  }
16692
17027
  var PATH_FIELD_ALIASES = ["path", "file_path", "filePath", "filename", "file", "target_file", "targetFile", "file_to_read", "fileToRead", "target_path", "read_path", "path_to_read", "pathToRead"];
@@ -16950,77 +17285,11 @@ function readVisualStateCaptureArgs(args2) {
16950
17285
  metricsPath
16951
17286
  };
16952
17287
  }
16953
- async function readWorkspacePackageScripts(workspaceRoot) {
16954
- try {
16955
- const raw = await readFile3(resolve4(workspaceRoot, "package.json"), "utf8");
16956
- const parsed = JSON.parse(raw);
16957
- const scripts = parsed && typeof parsed === "object" && parsed.scripts && typeof parsed.scripts === "object" ? Object.keys(parsed.scripts).sort() : [];
16958
- return { scripts };
16959
- } catch (error) {
16960
- return {
16961
- scripts: [],
16962
- error: toErrorMessage(error)
16963
- };
16964
- }
16965
- }
16966
- function formatAvailableScripts(scripts) {
16967
- return scripts.length ? scripts.join(", ") : "(none)";
16968
- }
16969
- async function runWorkspacePackageScript(args2) {
16970
- const script = args2.script.trim();
16971
- if (!script) throw new Error("package script name is required.");
16972
- const extraArgs = args2.extraArgs ?? [];
16973
- const packageScripts = await readWorkspacePackageScripts(args2.workspaceRoot);
16974
- if (!packageScripts.scripts.includes(script)) {
16975
- return {
16976
- content: [
16977
- `script not found: ${script}`,
16978
- `available scripts: ${formatAvailableScripts(packageScripts.scripts)}`,
16979
- ...packageScripts.error ? [`package.json read error: ${packageScripts.error}`] : []
16980
- ].join("\n"),
16981
- metadata: {
16982
- script,
16983
- exitCode: 1,
16984
- reason: "script-not-found",
16985
- availableScripts: packageScripts.scripts,
16986
- ...packageScripts.error ? { packageJsonError: packageScripts.error } : {}
16987
- }
16988
- };
16989
- }
16990
- const result4 = await runWorkspaceCommand({
16991
- workspaceRoot: args2.workspaceRoot,
16992
- command: ["bun", "run", script, ...extraArgs.length ? ["--", ...extraArgs] : []],
16993
- timeoutMs: args2.commandTimeoutMs
16994
- });
16995
- return {
16996
- content: result4.content,
16997
- metadata: {
16998
- script,
16999
- args: extraArgs,
17000
- exitCode: result4.exitCode,
17001
- reason: result4.exitCode === 0 ? "ok" : "script-failed",
17002
- timedOut: result4.timedOut,
17003
- stdoutTail: result4.stdout.trim().slice(-4e3),
17004
- stderrTail: result4.stderr.trim().slice(-4e3)
17005
- }
17006
- };
17007
- }
17008
- function parseLastJsonObject(text2) {
17009
- const trimmed = text2.trim();
17010
- for (let index = trimmed.lastIndexOf("{"); index >= 0; index = trimmed.lastIndexOf("{", index - 1)) {
17011
- try {
17012
- const parsed = JSON.parse(trimmed.slice(index));
17013
- return parsed && typeof parsed === "object" ? parsed : null;
17014
- } catch {
17015
- }
17016
- }
17017
- return null;
17018
- }
17019
17288
  function resolveRipgrepBinary(env = process.env) {
17020
17289
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
17021
17290
  if (fromEnv) {
17022
17291
  try {
17023
- if (existsSync4(fromEnv)) return fromEnv;
17292
+ if (existsSync5(fromEnv)) return fromEnv;
17024
17293
  } catch {
17025
17294
  }
17026
17295
  }
@@ -17028,8 +17297,8 @@ function resolveRipgrepBinary(env = process.env) {
17028
17297
  }
17029
17298
  var SEARCH_OUTPUT_CHAR_LIMIT = 2e4;
17030
17299
  function isPathInsideRoot(root, targetPath) {
17031
- const rel = relative3(resolve4(root), resolve4(targetPath));
17032
- return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute2(rel);
17300
+ const rel = relative4(resolve4(root), resolve4(targetPath));
17301
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute3(rel);
17033
17302
  }
17034
17303
  async function resolveRuntimeOwnedSpillPath(targetPath) {
17035
17304
  const spillRoot = resolveNoloStateDir("spills");
@@ -17037,7 +17306,7 @@ async function resolveRuntimeOwnedSpillPath(targetPath) {
17037
17306
  const rootStat = await lstat(spillRoot).catch(() => void 0);
17038
17307
  if (rootStat?.isDirectory() !== true) return null;
17039
17308
  try {
17040
- const [realRoot, realTarget] = await Promise.all([realpath(spillRoot), realpath(targetPath)]);
17309
+ const [realRoot, realTarget] = await Promise.all([realpath2(spillRoot), realpath2(targetPath)]);
17041
17310
  return isPathInsideRoot(realRoot, realTarget) ? realTarget : null;
17042
17311
  } catch {
17043
17312
  return null;
@@ -17137,7 +17406,7 @@ async function readFileTool(args2) {
17137
17406
  allowRuntimeOwnedSpill: true,
17138
17407
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17139
17408
  });
17140
- const content = await readFile3(absolutePath, "utf8");
17409
+ const content = await readFile4(absolutePath, "utf8");
17141
17410
  const activity = extractActivity(parsed);
17142
17411
  const sliced = sliceReadFileContent({
17143
17412
  content,
@@ -17221,8 +17490,8 @@ async function writeFileTool(args2) {
17221
17490
  requestedPath,
17222
17491
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17223
17492
  });
17224
- await mkdir(dirname2(absolutePath), { recursive: true });
17225
- await writeFile(absolutePath, content, "utf8");
17493
+ await mkdir2(dirname3(absolutePath), { recursive: true });
17494
+ await writeFile2(absolutePath, content, "utf8");
17226
17495
  const relativePath = normalizeWorkspaceRelativePath({
17227
17496
  workspaceRoot: resolve4(args2.workspaceRoot),
17228
17497
  targetPath: absolutePath
@@ -17330,7 +17599,7 @@ async function editFileTool(args2) {
17330
17599
  requestedPath,
17331
17600
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17332
17601
  });
17333
- const content = await readFile3(absolutePath, "utf8");
17602
+ const content = await readFile4(absolutePath, "utf8");
17334
17603
  const { count: replacementCount, result: nextContent } = countAndReplaceEolTolerant({
17335
17604
  content,
17336
17605
  oldText,
@@ -17344,7 +17613,7 @@ async function editFileTool(args2) {
17344
17613
  ${hint}` : "")
17345
17614
  );
17346
17615
  }
17347
- await writeFile(absolutePath, nextContent, "utf8");
17616
+ await writeFile2(absolutePath, nextContent, "utf8");
17348
17617
  const relativePath = normalizeWorkspaceRelativePath({
17349
17618
  workspaceRoot: resolve4(args2.workspaceRoot),
17350
17619
  targetPath: absolutePath
@@ -17387,8 +17656,8 @@ async function listWorkspaceEntries(args2) {
17387
17656
  for (const name of names) {
17388
17657
  if (truncated) break;
17389
17658
  const absolutePath = resolve4(dirPath, name);
17390
- const info = await stat3(absolutePath);
17391
- const relativePath = relative3(args2.dirPath, absolutePath) || ".";
17659
+ const info = await stat4(absolutePath);
17660
+ const relativePath = relative4(args2.dirPath, absolutePath) || ".";
17392
17661
  const isDirectory = info.isDirectory();
17393
17662
  entries.push(isDirectory ? `${relativePath}/` : relativePath);
17394
17663
  if (args2.maxResults && entries.length >= args2.maxResults) {
@@ -17600,59 +17869,52 @@ async function resolveVisualStateBaseUrl(args2) {
17600
17869
  return DEFAULT_LOCAL_API_ORIGIN;
17601
17870
  }
17602
17871
  async function captureVisualStateTool(args2) {
17603
- const parsed = parseWorkspaceToolArguments(args2.call.arguments);
17604
- const captureArgs = readVisualStateCaptureArgs(parsed);
17605
- const baseUrl = await resolveVisualStateBaseUrl({
17606
- explicitBaseUrl: captureArgs.baseUrl
17607
- });
17608
- const extraArgs = [
17609
- "--base",
17610
- baseUrl,
17611
- "--path",
17612
- captureArgs.path,
17613
- "--wait-selector",
17614
- captureArgs.waitSelector,
17615
- ...captureArgs.scrollSelector ? ["--scroll-selector", captureArgs.scrollSelector] : [],
17616
- ...captureArgs.focusSelector ? ["--focus-selector", captureArgs.focusSelector] : [],
17617
- ...captureArgs.expectText ? ["--expect-text", captureArgs.expectText] : [],
17618
- "--screenshot",
17619
- captureArgs.screenshotPath,
17620
- "--metrics",
17621
- captureArgs.metricsPath
17622
- ];
17623
- const result4 = await runWorkspacePackageScript({
17624
- workspaceRoot: args2.workspaceRoot,
17625
- script: "probe:visual-review",
17626
- extraArgs,
17627
- commandTimeoutMs: args2.commandTimeoutMs
17628
- });
17629
- const metrics = parseLastJsonObject(String(result4.metadata?.stdoutTail ?? result4.content));
17630
- return {
17631
- content: [
17632
- `pageUrl: ${typeof metrics?.pageUrl === "string" ? metrics.pageUrl : ""}`,
17633
- `screenshotPath: ${captureArgs.screenshotPath}`,
17634
- `metricsPath: ${captureArgs.metricsPath}`,
17635
- `waitSelector: ${captureArgs.waitSelector}`,
17636
- ...captureArgs.focusSelector ? [`focusSelector: ${captureArgs.focusSelector}`] : [],
17637
- ...captureArgs.expectText ? [`expectText: ${captureArgs.expectText}`] : [],
17638
- "",
17639
- result4.content
17640
- ].join("\n").trim(),
17641
- metadata: {
17642
- ...result4.metadata,
17643
- script: "probe:visual-review",
17644
- args: extraArgs,
17645
- baseUrl,
17646
- path: captureArgs.path,
17647
- waitSelector: captureArgs.waitSelector,
17648
- scrollSelector: captureArgs.scrollSelector,
17649
- focusSelector: captureArgs.focusSelector,
17650
- expectText: captureArgs.expectText,
17651
- screenshotPath: captureArgs.screenshotPath,
17652
- metricsPath: captureArgs.metricsPath,
17653
- ...metrics ?? {}
17654
- }
17655
- };
17872
+ try {
17873
+ const parsed = parseWorkspaceToolArguments(args2.call.arguments);
17874
+ const captureArgs = readVisualStateCaptureArgs(parsed);
17875
+ const capability = visualRendererCapability();
17876
+ if (!capability.available) return { content: `captureVisualState unavailable: ${capability.reason}`, metadata: { ok: false, rendererAvailable: false } };
17877
+ const baseUrl = await resolveVisualStateBaseUrl({ explicitBaseUrl: captureArgs.baseUrl });
17878
+ const pageUrl = new URL(captureArgs.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).href;
17879
+ const screenshotPath = resolveRequestedWorkspacePath(args2.workspaceRoot, captureArgs.screenshotPath);
17880
+ const metricsPath = resolveRequestedWorkspacePath(args2.workspaceRoot, captureArgs.metricsPath);
17881
+ const rendered = await renderVisualPage({ ...captureArgs, workspaceRoot: args2.workspaceRoot, url: pageUrl, screenshotPath, timeoutMs: args2.commandTimeoutMs });
17882
+ const metrics = {
17883
+ ...captureArgs,
17884
+ ...rendered,
17885
+ scrollSelector: captureArgs.scrollSelector ?? null,
17886
+ focusSelector: captureArgs.focusSelector ?? null,
17887
+ expectText: captureArgs.expectText ?? null
17888
+ };
17889
+ await mkdir2(dirname3(metricsPath), { recursive: true });
17890
+ await writeFile2(metricsPath, JSON.stringify(metrics, null, 2));
17891
+ const image = await readLocalImage(screenshotPath);
17892
+ return {
17893
+ content: [
17894
+ `pageUrl: ${metrics.pageUrl}`,
17895
+ `screenshotPath: ${captureArgs.screenshotPath}`,
17896
+ `metricsPath: ${captureArgs.metricsPath}`,
17897
+ `waitSelector: ${captureArgs.waitSelector}`,
17898
+ ...captureArgs.focusSelector ? [`focusSelector: ${captureArgs.focusSelector}`] : [],
17899
+ ...captureArgs.expectText ? [`expectText: ${captureArgs.expectText}`] : [],
17900
+ image.content.replace("[image ", "[screenshot ").replace(/(\d+) bytes/, (_, bytes) => `${Math.ceil(Number(bytes) / 1024)}KB`)
17901
+ ].join("\n"),
17902
+ images: image.images,
17903
+ metadata: {
17904
+ ...captureArgs,
17905
+ ...metrics,
17906
+ ok: true,
17907
+ exitCode: 0,
17908
+ baseUrl,
17909
+ script: null,
17910
+ args: [],
17911
+ compatibilityVersion: 2,
17912
+ compatibilityNote: "Platform rendering replaces workspace scripts; script/args/stdoutTail no longer describe a subprocess. Existing page metrics and path fields are retained."
17913
+ }
17914
+ };
17915
+ } catch (error) {
17916
+ return { content: `captureVisualState failed: ${toErrorMessage(error)}`, metadata: { ok: false } };
17917
+ }
17656
17918
  }
17657
17919
  async function execShellTool(args2) {
17658
17920
  return invokeCapability(
@@ -18023,6 +18285,16 @@ function createLocalWorkspaceToolExecutors(args2) {
18023
18285
  ...fileAccess,
18024
18286
  ...args2.resolveRipgrepBinary ? { resolveRipgrepBinary: args2.resolveRipgrepBinary } : {}
18025
18287
  }),
18288
+ captureHostWindow: async (call) => {
18289
+ let parsed;
18290
+ try {
18291
+ parsed = JSON.parse(call.arguments || "{}");
18292
+ } catch {
18293
+ return { content: "captureHostWindow accepts an empty JSON object" };
18294
+ }
18295
+ if (!isRecord(parsed) || Object.keys(parsed).length) return { content: "captureHostWindow accepts no arguments" };
18296
+ return captureHostWindow();
18297
+ },
18026
18298
  read_image: async (call) => {
18027
18299
  const parsed = parseWorkspaceToolArguments(call.arguments);
18028
18300
  if (Object.keys(parsed).some((key) => key !== "path")) throw new Error("read_image accepts exactly one path");
@@ -21893,11 +22165,11 @@ async function readNoloProcessStream(readable) {
21893
22165
  async function runNoloWorkspaceCliTool(call, args2) {
21894
22166
  const cliArgs = buildNoloWorkspaceCommandArgs(call);
21895
22167
  const bunSpawn = globalThis.Bun?.spawn;
21896
- const spawn2 = args2.spawn ?? (typeof bunSpawn === "function" ? bunSpawn : nodeSpawnFallback);
22168
+ const spawn3 = args2.spawn ?? (typeof bunSpawn === "function" ? bunSpawn : nodeSpawnFallback);
21897
22169
  const execPath = args2.processExecPath ?? process.execPath;
21898
22170
  const entrypoint = args2.cliEntrypoint;
21899
22171
  const cmd = entrypoint && entrypoint !== execPath ? [execPath, entrypoint, ...cliArgs] : [execPath, ...cliArgs];
21900
- const proc = spawn2({
22172
+ const proc = spawn3({
21901
22173
  cmd,
21902
22174
  stdout: "pipe",
21903
22175
  stderr: "pipe",
@@ -22623,17 +22895,17 @@ function createDevinProvider(options) {
22623
22895
  }
22624
22896
 
22625
22897
  // packages/agent-runtime/executionLeaseValidation.ts
22626
- import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5 } from "node:path";
22898
+ import { isAbsolute as isAbsolute4, relative as relative5, resolve as resolve5 } from "node:path";
22627
22899
  function withinRoot(root, child) {
22628
- if (!isAbsolute3(root) || !isAbsolute3(child)) return false;
22900
+ if (!isAbsolute4(root) || !isAbsolute4(child)) return false;
22629
22901
  const canonicalRoot = resolve5(root);
22630
22902
  const canonicalChild = resolve5(child);
22631
- const suffix = relative4(canonicalRoot, canonicalChild);
22632
- return suffix === "" || suffix !== ".." && !suffix.startsWith("../") && !suffix.startsWith("..\\") && !isAbsolute3(suffix);
22903
+ const suffix = relative5(canonicalRoot, canonicalChild);
22904
+ return suffix === "" || suffix !== ".." && !suffix.startsWith("../") && !suffix.startsWith("..\\") && !isAbsolute4(suffix);
22633
22905
  }
22634
22906
  function subsetOf(allowed, actual) {
22635
22907
  return actual.every(
22636
- (root) => isAbsolute3(root) && allowed.some((parent) => withinRoot(parent, root))
22908
+ (root) => isAbsolute4(root) && allowed.some((parent) => withinRoot(parent, root))
22637
22909
  );
22638
22910
  }
22639
22911
  function validateConfiguredRootExecutionLease(ticket, lease) {