nolo-cli 0.173.0-alpha.2 → 0.174.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-Z46YYASI.js} +2 -2
  2. package/{agentEmailCommands-7YX27N6M.js → agentEmailCommands-ZSNW7MNV.js} +3 -3
  3. package/{agentListCommands-AT5IGY5W.js → agentListCommands-3XYURQNT.js} +1 -1
  4. package/{agentMachineCommands-WR3IFBB6.js → agentMachineCommands-EWMEPQSX.js} +2 -2
  5. package/{agentRecordCommands-T6PXLK4B.js → agentRecordCommands-JO7YAN2P.js} +3 -3
  6. package/{agentRunCommand-RB3TFF6O.js → agentRunCommand-EOUATYYD.js} +3 -3
  7. package/{agentRuntimeLocal-72TUBESS.js → agentRuntimeLocal-J5RQ3K7G.js} +1 -1
  8. package/{appDeleteCommands-THIHX7BV.js → appDeleteCommands-OF7LWX5L.js} +1 -1
  9. package/{appDeployCommands-5JA5DDMR.js → appDeployCommands-73I3WQXX.js} +1 -1
  10. package/{appDeployStatusCommands-EHFPCQHM.js → appDeployStatusCommands-AV3B5FIN.js} +1 -1
  11. package/{appGetCommands-4HSRXSYD.js → appGetCommands-UDQ5RCCL.js} +1 -1
  12. package/{appListCommands-TD7BQFWQ.js → appListCommands-66J7KUEQ.js} +1 -1
  13. package/{chunk-2OWYKHKI.js → chunk-DRQTD2FX.js} +1 -1
  14. package/{chunk-FDPODXE3.js → chunk-EDF4FPCQ.js} +1 -1
  15. package/{chunk-EY3QEZZR.js → chunk-I2NZPJAT.js} +3 -3
  16. package/{chunk-IVPXG433.js → chunk-IFRQFQ2C.js} +255 -151
  17. package/{chunk-EVW2U6RH.js → chunk-XOTLB33F.js} +2 -2
  18. package/{commandRegistry-PJ3EQ6U7.js → commandRegistry-QZOWJ4SJ.js} +40 -40
  19. package/{dialogCommands-CPK6MUIJ.js → dialogCommands-FMWGAGIM.js} +1 -1
  20. package/{docCreateCommands-GLR2HLYH.js → docCreateCommands-NAVBZ3AV.js} +1 -1
  21. package/{docDeleteCommands-MPE53Z5W.js → docDeleteCommands-V3DDFY72.js} +1 -1
  22. package/{docReadCommands-MFKWAFDI.js → docReadCommands-AHN3F4MJ.js} +1 -1
  23. package/{docUpdateCommands-3QBT2XTK.js → docUpdateCommands-2AEUHRAW.js} +1 -1
  24. package/index.js +3 -3
  25. package/{localRuntimeAdapter-KQHAGAXR.js → localRuntimeAdapter-D2RWVWNO.js} +2 -2
  26. package/{machineCommands-DMPHPCQS.js → machineCommands-WM3Y5FEW.js} +3 -3
  27. package/{machineWsRunDispatch-736CCGUF.js → machineWsRunDispatch-BC2NIKFO.js} +3 -3
  28. package/package.json +1 -1
  29. package/{readlineWorkspace-K4WNNW2D.js → readlineWorkspace-S2KV2QPV.js} +3 -3
  30. package/{spaceCommands-U4UYF6IB.js → spaceCommands-MCDPNA5Y.js} +1 -1
@@ -15723,11 +15723,187 @@ 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/localWorkspaceTools.ts
15904
+ 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
15905
  import { homedir } from "node:os";
15730
- import { dirname as dirname2, isAbsolute as isAbsolute2, relative as relative3, resolve as resolve4, sep } from "node:path";
15906
+ import { dirname as dirname3, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve4, sep } from "node:path";
15731
15907
  import { spawn as spawnChildProcess3 } from "node:child_process";
15732
15908
 
15733
15909
  // packages/core/readRangeLedger.ts
@@ -15763,15 +15939,15 @@ function isLineRangeCovered(request3, covered) {
15763
15939
  }
15764
15940
 
15765
15941
  // 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";
15942
+ import { readdir, readFile as readFile3, stat as stat3 } from "node:fs/promises";
15943
+ import { basename, relative as relative3, resolve as resolve3 } from "node:path";
15768
15944
  import { spawn as spawnChildProcess2 } from "node:child_process";
15769
15945
 
15770
15946
  // packages/agent-runtime/gitignore.ts
15771
- import { readFile, stat } from "node:fs/promises";
15947
+ import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
15772
15948
  import { resolve as resolve2 } from "node:path";
15773
15949
  async function readRootGitignorePatterns(workspaceRoot) {
15774
- const content = await readFile(resolve2(workspaceRoot, ".gitignore"), "utf8").catch(() => null);
15950
+ const content = await readFile2(resolve2(workspaceRoot, ".gitignore"), "utf8").catch(() => null);
15775
15951
  if (content === null) return [];
15776
15952
  return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!")).flatMap((line) => {
15777
15953
  const unanchored = line.replace(/^\//, "");
@@ -15782,7 +15958,7 @@ async function readRootGitignorePatterns(workspaceRoot) {
15782
15958
  }
15783
15959
  async function hasRootGitignore(workspaceRoot) {
15784
15960
  try {
15785
- await stat(resolve2(workspaceRoot, ".gitignore"));
15961
+ await stat2(resolve2(workspaceRoot, ".gitignore"));
15786
15962
  return true;
15787
15963
  } catch {
15788
15964
  return false;
@@ -15791,7 +15967,7 @@ async function hasRootGitignore(workspaceRoot) {
15791
15967
 
15792
15968
  // packages/agent-runtime/fileTextSearch.ts
15793
15969
  var normalizePath = (value) => value.replaceAll("\\", "/");
15794
- var pathFor = (root, file, absolute = false) => absolute ? normalizePath(file) : normalizePath(relative2(root, file) || basename(file));
15970
+ var pathFor = (root, file, absolute = false) => absolute ? normalizePath(file) : normalizePath(relative3(root, file) || basename(file));
15795
15971
  var COMMAND_LIMIT_HEADROOM = 1;
15796
15972
  async function runCommand(binary, args2, cwd, maxResults) {
15797
15973
  const recordLimit = maxResults === void 0 ? 0 : Math.max(0, maxResults) + COMMAND_LIMIT_HEADROOM;
@@ -15851,7 +16027,7 @@ async function decorate(root, raw, context3, absolute = false) {
15851
16027
  let lines = cache.get(x.file);
15852
16028
  if (!lines) {
15853
16029
  try {
15854
- lines = (await readFile2(x.file, "utf8")).split(/\r\n|\r|\n/);
16030
+ lines = (await readFile3(x.file, "utf8")).split(/\r\n|\r|\n/);
15855
16031
  } catch {
15856
16032
  continue;
15857
16033
  }
@@ -15880,13 +16056,13 @@ function globMatch(pattern, value) {
15880
16056
  const r = new RegExp(`^${source}$`);
15881
16057
  return r.test(value) || r.test(basename(value));
15882
16058
  }
15883
- var defaultReadTextFile = (filePath) => readFile2(filePath, "utf8").catch(() => "");
16059
+ var defaultReadTextFile = (filePath) => readFile3(filePath, "utf8").catch(() => "");
15884
16060
  async function jsSearch(o, targetPath) {
15885
16061
  const max4 = o.maxResults ?? Number.MAX_SAFE_INTEGER;
15886
16062
  const excludes = o.exclude ?? [];
15887
16063
  const readText = o.readTextFile ?? defaultReadTextFile;
15888
16064
  const files = [];
15889
- const targetStat = await stat2(targetPath).catch(() => null);
16065
+ const targetStat = await stat3(targetPath).catch(() => null);
15890
16066
  if (!targetStat) return { matches: [], totalMatches: 0, truncated: false, engine: "js" };
15891
16067
  const ignorePatterns = o.includeIgnored ? [] : await readRootGitignorePatterns(o.root);
15892
16068
  const isIgnored = (rel) => ignorePatterns.some((pattern) => globMatch(pattern, rel));
@@ -15932,7 +16108,7 @@ var COMMAND_ENGINES = [
15932
16108
  ];
15933
16109
  async function searchFileTextMatches(o) {
15934
16110
  const targetPath = resolve3(o.root, o.target || ".");
15935
- const targetStat = await stat2(targetPath).catch(() => null);
16111
+ const targetStat = await stat3(targetPath).catch(() => null);
15936
16112
  if (!targetStat) return { matches: [], totalMatches: 0, truncated: false, engine: "js" };
15937
16113
  if (targetStat.isFile()) return jsSearch(o, targetPath);
15938
16114
  const run2 = o.runCommand ?? runCommand, max4 = o.maxResults;
@@ -16397,7 +16573,7 @@ function buildCaptureVisualStateTool() {
16397
16573
  type: "function",
16398
16574
  function: {
16399
16575
  name: "captureVisualState",
16400
- description: "Capture a real local app screenshot and DOM/computed-style metrics for a selected UI state.",
16576
+ 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
16577
  parameters: {
16402
16578
  type: "object",
16403
16579
  properties: {
@@ -16668,6 +16844,7 @@ function buildLocalWorkspaceOpenAiTools(args2) {
16668
16844
  const declaredTools = new Set(args2.toolNames ?? []);
16669
16845
  return WORKSPACE_TOOL_NAMES.filter((toolName) => {
16670
16846
  if (!declaredTools.has(toolName)) return false;
16847
+ if (toolName === "captureVisualState" && !visualRendererCapability().available) return false;
16671
16848
  if (!args2.exposeShellTools && SHELL_TOOL_NAMES.includes(toolName)) {
16672
16849
  return false;
16673
16850
  }
@@ -16682,11 +16859,11 @@ function parseWorkspaceToolArguments(raw) {
16682
16859
  }
16683
16860
  }
16684
16861
  function normalizeWorkspaceRelativePath(args2) {
16685
- const relativePath = relative3(args2.workspaceRoot, args2.targetPath);
16862
+ const relativePath = relative4(args2.workspaceRoot, args2.targetPath);
16686
16863
  return relativePath || ".";
16687
16864
  }
16688
16865
  function isPathInsideWorkspace(args2) {
16689
- const relativePath = relative3(args2.workspaceRoot, args2.targetPath);
16866
+ const relativePath = relative4(args2.workspaceRoot, args2.targetPath);
16690
16867
  return relativePath === "" || !relativePath.startsWith("..") && !relativePath.includes(`..${sep}`);
16691
16868
  }
16692
16869
  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 +17127,11 @@ function readVisualStateCaptureArgs(args2) {
16950
17127
  metricsPath
16951
17128
  };
16952
17129
  }
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
17130
  function resolveRipgrepBinary(env = process.env) {
17020
17131
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
17021
17132
  if (fromEnv) {
17022
17133
  try {
17023
- if (existsSync4(fromEnv)) return fromEnv;
17134
+ if (existsSync5(fromEnv)) return fromEnv;
17024
17135
  } catch {
17025
17136
  }
17026
17137
  }
@@ -17028,8 +17139,8 @@ function resolveRipgrepBinary(env = process.env) {
17028
17139
  }
17029
17140
  var SEARCH_OUTPUT_CHAR_LIMIT = 2e4;
17030
17141
  function isPathInsideRoot(root, targetPath) {
17031
- const rel = relative3(resolve4(root), resolve4(targetPath));
17032
- return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute2(rel);
17142
+ const rel = relative4(resolve4(root), resolve4(targetPath));
17143
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute3(rel);
17033
17144
  }
17034
17145
  async function resolveRuntimeOwnedSpillPath(targetPath) {
17035
17146
  const spillRoot = resolveNoloStateDir("spills");
@@ -17037,7 +17148,7 @@ async function resolveRuntimeOwnedSpillPath(targetPath) {
17037
17148
  const rootStat = await lstat(spillRoot).catch(() => void 0);
17038
17149
  if (rootStat?.isDirectory() !== true) return null;
17039
17150
  try {
17040
- const [realRoot, realTarget] = await Promise.all([realpath(spillRoot), realpath(targetPath)]);
17151
+ const [realRoot, realTarget] = await Promise.all([realpath2(spillRoot), realpath2(targetPath)]);
17041
17152
  return isPathInsideRoot(realRoot, realTarget) ? realTarget : null;
17042
17153
  } catch {
17043
17154
  return null;
@@ -17137,7 +17248,7 @@ async function readFileTool(args2) {
17137
17248
  allowRuntimeOwnedSpill: true,
17138
17249
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17139
17250
  });
17140
- const content = await readFile3(absolutePath, "utf8");
17251
+ const content = await readFile4(absolutePath, "utf8");
17141
17252
  const activity = extractActivity(parsed);
17142
17253
  const sliced = sliceReadFileContent({
17143
17254
  content,
@@ -17221,8 +17332,8 @@ async function writeFileTool(args2) {
17221
17332
  requestedPath,
17222
17333
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17223
17334
  });
17224
- await mkdir(dirname2(absolutePath), { recursive: true });
17225
- await writeFile(absolutePath, content, "utf8");
17335
+ await mkdir2(dirname3(absolutePath), { recursive: true });
17336
+ await writeFile2(absolutePath, content, "utf8");
17226
17337
  const relativePath = normalizeWorkspaceRelativePath({
17227
17338
  workspaceRoot: resolve4(args2.workspaceRoot),
17228
17339
  targetPath: absolutePath
@@ -17330,7 +17441,7 @@ async function editFileTool(args2) {
17330
17441
  requestedPath,
17331
17442
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17332
17443
  });
17333
- const content = await readFile3(absolutePath, "utf8");
17444
+ const content = await readFile4(absolutePath, "utf8");
17334
17445
  const { count: replacementCount, result: nextContent } = countAndReplaceEolTolerant({
17335
17446
  content,
17336
17447
  oldText,
@@ -17344,7 +17455,7 @@ async function editFileTool(args2) {
17344
17455
  ${hint}` : "")
17345
17456
  );
17346
17457
  }
17347
- await writeFile(absolutePath, nextContent, "utf8");
17458
+ await writeFile2(absolutePath, nextContent, "utf8");
17348
17459
  const relativePath = normalizeWorkspaceRelativePath({
17349
17460
  workspaceRoot: resolve4(args2.workspaceRoot),
17350
17461
  targetPath: absolutePath
@@ -17387,8 +17498,8 @@ async function listWorkspaceEntries(args2) {
17387
17498
  for (const name of names) {
17388
17499
  if (truncated) break;
17389
17500
  const absolutePath = resolve4(dirPath, name);
17390
- const info = await stat3(absolutePath);
17391
- const relativePath = relative3(args2.dirPath, absolutePath) || ".";
17501
+ const info = await stat4(absolutePath);
17502
+ const relativePath = relative4(args2.dirPath, absolutePath) || ".";
17392
17503
  const isDirectory = info.isDirectory();
17393
17504
  entries.push(isDirectory ? `${relativePath}/` : relativePath);
17394
17505
  if (args2.maxResults && entries.length >= args2.maxResults) {
@@ -17600,59 +17711,52 @@ async function resolveVisualStateBaseUrl(args2) {
17600
17711
  return DEFAULT_LOCAL_API_ORIGIN;
17601
17712
  }
17602
17713
  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
- };
17714
+ try {
17715
+ const parsed = parseWorkspaceToolArguments(args2.call.arguments);
17716
+ const captureArgs = readVisualStateCaptureArgs(parsed);
17717
+ const capability = visualRendererCapability();
17718
+ if (!capability.available) return { content: `captureVisualState unavailable: ${capability.reason}`, metadata: { ok: false, rendererAvailable: false } };
17719
+ const baseUrl = await resolveVisualStateBaseUrl({ explicitBaseUrl: captureArgs.baseUrl });
17720
+ const pageUrl = new URL(captureArgs.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).href;
17721
+ const screenshotPath = resolveRequestedWorkspacePath(args2.workspaceRoot, captureArgs.screenshotPath);
17722
+ const metricsPath = resolveRequestedWorkspacePath(args2.workspaceRoot, captureArgs.metricsPath);
17723
+ const rendered = await renderVisualPage({ ...captureArgs, workspaceRoot: args2.workspaceRoot, url: pageUrl, screenshotPath, timeoutMs: args2.commandTimeoutMs });
17724
+ const metrics = {
17725
+ ...captureArgs,
17726
+ ...rendered,
17727
+ scrollSelector: captureArgs.scrollSelector ?? null,
17728
+ focusSelector: captureArgs.focusSelector ?? null,
17729
+ expectText: captureArgs.expectText ?? null
17730
+ };
17731
+ await mkdir2(dirname3(metricsPath), { recursive: true });
17732
+ await writeFile2(metricsPath, JSON.stringify(metrics, null, 2));
17733
+ const image = await readLocalImage(screenshotPath);
17734
+ return {
17735
+ content: [
17736
+ `pageUrl: ${metrics.pageUrl}`,
17737
+ `screenshotPath: ${captureArgs.screenshotPath}`,
17738
+ `metricsPath: ${captureArgs.metricsPath}`,
17739
+ `waitSelector: ${captureArgs.waitSelector}`,
17740
+ ...captureArgs.focusSelector ? [`focusSelector: ${captureArgs.focusSelector}`] : [],
17741
+ ...captureArgs.expectText ? [`expectText: ${captureArgs.expectText}`] : [],
17742
+ image.content.replace("[image ", "[screenshot ").replace(/(\d+) bytes/, (_, bytes) => `${Math.ceil(Number(bytes) / 1024)}KB`)
17743
+ ].join("\n"),
17744
+ images: image.images,
17745
+ metadata: {
17746
+ ...captureArgs,
17747
+ ...metrics,
17748
+ ok: true,
17749
+ exitCode: 0,
17750
+ baseUrl,
17751
+ script: null,
17752
+ args: [],
17753
+ compatibilityVersion: 2,
17754
+ compatibilityNote: "Platform rendering replaces workspace scripts; script/args/stdoutTail no longer describe a subprocess. Existing page metrics and path fields are retained."
17755
+ }
17756
+ };
17757
+ } catch (error) {
17758
+ return { content: `captureVisualState failed: ${toErrorMessage(error)}`, metadata: { ok: false } };
17759
+ }
17656
17760
  }
17657
17761
  async function execShellTool(args2) {
17658
17762
  return invokeCapability(
@@ -22623,17 +22727,17 @@ function createDevinProvider(options) {
22623
22727
  }
22624
22728
 
22625
22729
  // packages/agent-runtime/executionLeaseValidation.ts
22626
- import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5 } from "node:path";
22730
+ import { isAbsolute as isAbsolute4, relative as relative5, resolve as resolve5 } from "node:path";
22627
22731
  function withinRoot(root, child) {
22628
- if (!isAbsolute3(root) || !isAbsolute3(child)) return false;
22732
+ if (!isAbsolute4(root) || !isAbsolute4(child)) return false;
22629
22733
  const canonicalRoot = resolve5(root);
22630
22734
  const canonicalChild = resolve5(child);
22631
- const suffix = relative4(canonicalRoot, canonicalChild);
22632
- return suffix === "" || suffix !== ".." && !suffix.startsWith("../") && !suffix.startsWith("..\\") && !isAbsolute3(suffix);
22735
+ const suffix = relative5(canonicalRoot, canonicalChild);
22736
+ return suffix === "" || suffix !== ".." && !suffix.startsWith("../") && !suffix.startsWith("..\\") && !isAbsolute4(suffix);
22633
22737
  }
22634
22738
  function subsetOf(allowed, actual) {
22635
22739
  return actual.every(
22636
- (root) => isAbsolute3(root) && allowed.some((parent) => withinRoot(parent, root))
22740
+ (root) => isAbsolute4(root) && allowed.some((parent) => withinRoot(parent, root))
22637
22741
  );
22638
22742
  }
22639
22743
  function validateConfiguredRootExecutionLease(ticket, lease) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createCliLocalRuntimeAdapter,
3
3
  summarizeEndpoint
4
- } from "./chunk-2OWYKHKI.js";
4
+ } from "./chunk-DRQTD2FX.js";
5
5
  import {
6
6
  t
7
7
  } from "./chunk-TDX2L4W2.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-KXIJYPVC.js";
11
11
  import {
12
12
  runLocalAgentTurn
13
- } from "./chunk-IVPXG433.js";
13
+ } from "./chunk-IFRQFQ2C.js";
14
14
  import {
15
15
  applyToolSurfaceConstraints
16
16
  } from "./chunk-2GRXE3YJ.js";