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.
- package/{agentDeleteCommand-HG5EDCO6.js → agentDeleteCommand-Z46YYASI.js} +2 -2
- package/{agentEmailCommands-7YX27N6M.js → agentEmailCommands-ZSNW7MNV.js} +3 -3
- package/{agentListCommands-AT5IGY5W.js → agentListCommands-3XYURQNT.js} +1 -1
- package/{agentMachineCommands-WR3IFBB6.js → agentMachineCommands-EWMEPQSX.js} +2 -2
- package/{agentRecordCommands-T6PXLK4B.js → agentRecordCommands-JO7YAN2P.js} +3 -3
- package/{agentRunCommand-RB3TFF6O.js → agentRunCommand-EOUATYYD.js} +3 -3
- package/{agentRuntimeLocal-72TUBESS.js → agentRuntimeLocal-J5RQ3K7G.js} +1 -1
- package/{appDeleteCommands-THIHX7BV.js → appDeleteCommands-OF7LWX5L.js} +1 -1
- package/{appDeployCommands-5JA5DDMR.js → appDeployCommands-73I3WQXX.js} +1 -1
- package/{appDeployStatusCommands-EHFPCQHM.js → appDeployStatusCommands-AV3B5FIN.js} +1 -1
- package/{appGetCommands-4HSRXSYD.js → appGetCommands-UDQ5RCCL.js} +1 -1
- package/{appListCommands-TD7BQFWQ.js → appListCommands-66J7KUEQ.js} +1 -1
- package/{chunk-2OWYKHKI.js → chunk-DRQTD2FX.js} +1 -1
- package/{chunk-FDPODXE3.js → chunk-EDF4FPCQ.js} +1 -1
- package/{chunk-EY3QEZZR.js → chunk-I2NZPJAT.js} +3 -3
- package/{chunk-IVPXG433.js → chunk-IFRQFQ2C.js} +255 -151
- package/{chunk-EVW2U6RH.js → chunk-XOTLB33F.js} +2 -2
- package/{commandRegistry-PJ3EQ6U7.js → commandRegistry-QZOWJ4SJ.js} +40 -40
- package/{dialogCommands-CPK6MUIJ.js → dialogCommands-FMWGAGIM.js} +1 -1
- package/{docCreateCommands-GLR2HLYH.js → docCreateCommands-NAVBZ3AV.js} +1 -1
- package/{docDeleteCommands-MPE53Z5W.js → docDeleteCommands-V3DDFY72.js} +1 -1
- package/{docReadCommands-MFKWAFDI.js → docReadCommands-AHN3F4MJ.js} +1 -1
- package/{docUpdateCommands-3QBT2XTK.js → docUpdateCommands-2AEUHRAW.js} +1 -1
- package/index.js +3 -3
- package/{localRuntimeAdapter-KQHAGAXR.js → localRuntimeAdapter-D2RWVWNO.js} +2 -2
- package/{machineCommands-DMPHPCQS.js → machineCommands-WM3Y5FEW.js} +3 -3
- package/{machineWsRunDispatch-736CCGUF.js → machineWsRunDispatch-BC2NIKFO.js} +3 -3
- package/package.json +1 -1
- package/{readlineWorkspace-K4WNNW2D.js → readlineWorkspace-S2KV2QPV.js} +3 -3
- 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
|
|
15726
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
15727
15727
|
import { createHash as createHash4 } from "node:crypto";
|
|
15728
|
-
|
|
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
|
|
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
|
|
15767
|
-
import { basename, relative as
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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) =>
|
|
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
|
|
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
|
|
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: "
|
|
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 =
|
|
16862
|
+
const relativePath = relative4(args2.workspaceRoot, args2.targetPath);
|
|
16686
16863
|
return relativePath || ".";
|
|
16687
16864
|
}
|
|
16688
16865
|
function isPathInsideWorkspace(args2) {
|
|
16689
|
-
const relativePath =
|
|
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 (
|
|
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 =
|
|
17032
|
-
return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !
|
|
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([
|
|
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
|
|
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
|
|
17225
|
-
await
|
|
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
|
|
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
|
|
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
|
|
17391
|
-
const relativePath =
|
|
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
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
17608
|
-
|
|
17609
|
-
"
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
captureArgs.
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17622
|
-
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17634
|
-
|
|
17635
|
-
|
|
17636
|
-
|
|
17637
|
-
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
17645
|
-
|
|
17646
|
-
|
|
17647
|
-
|
|
17648
|
-
|
|
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
|
|
22730
|
+
import { isAbsolute as isAbsolute4, relative as relative5, resolve as resolve5 } from "node:path";
|
|
22627
22731
|
function withinRoot(root, child) {
|
|
22628
|
-
if (!
|
|
22732
|
+
if (!isAbsolute4(root) || !isAbsolute4(child)) return false;
|
|
22629
22733
|
const canonicalRoot = resolve5(root);
|
|
22630
22734
|
const canonicalChild = resolve5(child);
|
|
22631
|
-
const suffix =
|
|
22632
|
-
return suffix === "" || suffix !== ".." && !suffix.startsWith("../") && !suffix.startsWith("..\\") && !
|
|
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) =>
|
|
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-
|
|
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-
|
|
13
|
+
} from "./chunk-IFRQFQ2C.js";
|
|
14
14
|
import {
|
|
15
15
|
applyToolSurfaceConstraints
|
|
16
16
|
} from "./chunk-2GRXE3YJ.js";
|