@openclaw/diffs 2026.9.8 → 2026.10.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as getServedViewerAsset, i as getServedLanguagePackViewerAsset, n as VIEWER_ASSET_PREFIX, t as LANGUAGE_PACK_VIEWER_ASSET_PREFIX } from "./viewer-assets-
|
|
1
|
+
import { a as getServedViewerAsset, i as getServedLanguagePackViewerAsset, n as VIEWER_ASSET_PREFIX, t as LANGUAGE_PACK_VIEWER_ASSET_PREFIX } from "./viewer-assets-C646cpoV.mjs";
|
|
2
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
3
|
import { constants } from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
5
|
import fs$1 from "node:fs/promises";
|
|
6
6
|
import { writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
|
|
7
7
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
@@ -54,14 +54,8 @@ var PlaywrightDiffScreenshotter = class {
|
|
|
54
54
|
await route.continue();
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
parsed = new URL(requestUrl);
|
|
60
|
-
} catch {
|
|
61
|
-
await route.abort();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1") {
|
|
57
|
+
const parsed = URL.parse(requestUrl);
|
|
58
|
+
if (!parsed || parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1") {
|
|
65
59
|
await route.abort();
|
|
66
60
|
return;
|
|
67
61
|
}
|
|
@@ -89,7 +83,7 @@ var PlaywrightDiffScreenshotter = class {
|
|
|
89
83
|
return [...document.querySelectorAll("[data-openclaw-diff-host]")].every((element) => {
|
|
90
84
|
return element instanceof HTMLElement && element.shadowRoot?.querySelector("[data-diffs]");
|
|
91
85
|
});
|
|
92
|
-
}, { timeout: 1e4 });
|
|
86
|
+
}, void 0, { timeout: 1e4 });
|
|
93
87
|
await page.evaluate(async () => {
|
|
94
88
|
await document.fonts.ready;
|
|
95
89
|
});
|
|
@@ -25,16 +25,25 @@ function createViewerAssetHandler(prefix, relativePaths, optional = false) {
|
|
|
25
25
|
const loaderPath = `${prefix}viewer.js`;
|
|
26
26
|
const runtimePath = `${prefix}viewer-runtime.js`;
|
|
27
27
|
let cache = null;
|
|
28
|
+
const loadRuntimeAssets = async (runtimeUrl) => {
|
|
29
|
+
const cached = cache;
|
|
30
|
+
const runtimeFilePath = fileURLToPath(runtimeUrl);
|
|
31
|
+
const runtimeStat = await fs.stat(runtimeFilePath);
|
|
32
|
+
if (cached && cached.mtimeMs === runtimeStat.mtimeMs) return cached;
|
|
33
|
+
const runtimeBody = await fs.readFile(runtimeFilePath);
|
|
34
|
+
const hash = crypto.createHash("sha1").update(runtimeBody).digest("hex").slice(0, 12);
|
|
35
|
+
cache = {
|
|
36
|
+
mtimeMs: runtimeStat.mtimeMs,
|
|
37
|
+
runtimeBody,
|
|
38
|
+
loaderBody: `import "${VIEWER_RUNTIME_RELATIVE_IMPORT_PATH}?v=${hash}";\n`
|
|
39
|
+
};
|
|
40
|
+
return cache;
|
|
41
|
+
};
|
|
28
42
|
return async (pathname) => {
|
|
29
43
|
if (pathname !== loaderPath && pathname !== runtimePath) return null;
|
|
30
44
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
cache,
|
|
34
|
-
updateCache: (updated) => {
|
|
35
|
-
cache = updated;
|
|
36
|
-
}
|
|
37
|
-
});
|
|
45
|
+
const runtimeUrl = await resolveRuntimeFileUrl(relativePaths);
|
|
46
|
+
const assets = await loadRuntimeAssets(runtimeUrl);
|
|
38
47
|
return {
|
|
39
48
|
body: pathname === loaderPath ? assets.loaderBody : assets.runtimeBody,
|
|
40
49
|
contentType: "text/javascript; charset=utf-8"
|
|
@@ -45,20 +54,6 @@ function createViewerAssetHandler(prefix, relativePaths, optional = false) {
|
|
|
45
54
|
}
|
|
46
55
|
};
|
|
47
56
|
}
|
|
48
|
-
async function loadRuntimeAssets(params) {
|
|
49
|
-
const runtimePath = fileURLToPath(params.runtimeUrl);
|
|
50
|
-
const runtimeStat = await fs.stat(runtimePath);
|
|
51
|
-
if (params.cache && params.cache.mtimeMs === runtimeStat.mtimeMs) return params.cache;
|
|
52
|
-
const runtimeBody = await fs.readFile(runtimePath);
|
|
53
|
-
const hash = crypto.createHash("sha1").update(runtimeBody).digest("hex").slice(0, 12);
|
|
54
|
-
const cache = {
|
|
55
|
-
mtimeMs: runtimeStat.mtimeMs,
|
|
56
|
-
runtimeBody,
|
|
57
|
-
loaderBody: `import "${VIEWER_RUNTIME_RELATIVE_IMPORT_PATH}?v=${hash}";\n`
|
|
58
|
-
};
|
|
59
|
-
params.updateCache(cache);
|
|
60
|
-
return cache;
|
|
61
|
-
}
|
|
62
57
|
async function resolveRuntimeFileUrl(relativePaths) {
|
|
63
58
|
let missingFileError = null;
|
|
64
59
|
for (const relativePath of relativePaths) {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { a as getServedViewerAsset, r as VIEWER_RUNTIME_PATH } from "./.setup/viewer-assets-
|
|
1
|
+
import { a as getServedViewerAsset, r as VIEWER_RUNTIME_PATH } from "./.setup/viewer-assets-C646cpoV.mjs";
|
|
2
2
|
import { buildPluginConfigSchema, definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
3
|
import { mapPluginConfigIssues } from "openclaw/plugin-sdk/extension-shared";
|
|
4
|
+
import { asFiniteNumber, asNonArrayRecord, asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import { clampInt, clampNumber, escapeHtml } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
4
6
|
import { z } from "zod";
|
|
5
7
|
import { resolveGatewayPublicOrigin } from "openclaw/plugin-sdk/config-contracts";
|
|
6
8
|
import fs from "node:fs";
|
|
@@ -8,7 +10,6 @@ import path from "node:path";
|
|
|
8
10
|
import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
9
11
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
10
12
|
import { isLoopbackHost } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
11
|
-
import { asNonArrayRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
12
13
|
import { createAuthRateLimiter, resolveRequestClientIp } from "openclaw/plugin-sdk/webhook-ingress";
|
|
13
14
|
import crypto from "node:crypto";
|
|
14
15
|
import fs$1 from "node:fs/promises";
|
|
@@ -21,10 +22,10 @@ import { optionalFiniteNumberSchema, stringEnum } from "openclaw/plugin-sdk/chan
|
|
|
21
22
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
22
23
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
23
24
|
import { readFiniteNumberParam } from "openclaw/plugin-sdk/param-readers";
|
|
25
|
+
import { textResult } from "openclaw/plugin-sdk/tool-results";
|
|
24
26
|
import { Type } from "typebox";
|
|
25
27
|
import { parsePatchFiles, resolveLanguage } from "@pierre/diffs";
|
|
26
28
|
import { preloadDiffHTML } from "@pierre/diffs/ssr";
|
|
27
|
-
import { escapeHtml } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
28
29
|
//#region \0rolldown/runtime.js
|
|
29
30
|
var __defProp = Object.defineProperty;
|
|
30
31
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -224,12 +225,8 @@ function buildViewerUrl(params) {
|
|
|
224
225
|
return parsedBase.toString();
|
|
225
226
|
}
|
|
226
227
|
function normalizeViewerBaseUrl(raw, fieldName = "baseUrl") {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
parsed = new URL(raw);
|
|
230
|
-
} catch {
|
|
231
|
-
throw new Error(`Invalid ${fieldName}: ${raw}`);
|
|
232
|
-
}
|
|
228
|
+
const parsed = URL.parse(raw);
|
|
229
|
+
if (!parsed) throw new Error(`Invalid ${fieldName}: ${raw}`);
|
|
233
230
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`${fieldName} must use http or https: ${raw}`);
|
|
234
231
|
if (parsed.search || parsed.hash) throw new Error(`${fieldName} must not include query/hash: ${raw}`);
|
|
235
232
|
parsed.search = "";
|
|
@@ -359,36 +356,32 @@ function resolveDiffsPluginDefaults(config) {
|
|
|
359
356
|
if (!config || typeof config !== "object" || Array.isArray(config)) return { ...DEFAULT_DIFFS_TOOL_DEFAULTS };
|
|
360
357
|
const defaults = config.defaults;
|
|
361
358
|
if (!defaults || typeof defaults !== "object" || Array.isArray(defaults)) return { ...DEFAULT_DIFFS_TOOL_DEFAULTS };
|
|
362
|
-
const fileQuality = normalizeFileQuality
|
|
359
|
+
const fileQuality = normalizeFileQuality(defaults.fileQuality ?? defaults.imageQuality);
|
|
363
360
|
const profile = DEFAULT_IMAGE_QUALITY_PROFILES[fileQuality];
|
|
364
361
|
const fileFormat = defaults.fileFormat ?? (defaults.imageFormat !== void 0 ? defaults.imageFormat : defaults.format);
|
|
365
362
|
return {
|
|
366
363
|
fontFamily: normalizeFontFamily(defaults.fontFamily),
|
|
367
364
|
fontSize: normalizeDiffFontSize(defaults.fontSize),
|
|
368
365
|
lineSpacing: normalizeDiffLineSpacing(defaults.lineSpacing),
|
|
369
|
-
layout:
|
|
366
|
+
layout: DIFF_LAYOUTS.find((value) => value === defaults.layout) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.layout,
|
|
370
367
|
showLineNumbers: defaults.showLineNumbers !== false,
|
|
371
|
-
diffIndicators:
|
|
368
|
+
diffIndicators: DIFF_INDICATORS.find((value) => value === defaults.diffIndicators) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.diffIndicators,
|
|
372
369
|
wordWrap: defaults.wordWrap !== false,
|
|
373
370
|
background: defaults.background !== false,
|
|
374
|
-
theme:
|
|
371
|
+
theme: DIFF_THEMES.find((value) => value === defaults.theme) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.theme,
|
|
375
372
|
fileFormat: normalizeFileFormat(fileFormat),
|
|
376
373
|
fileQuality,
|
|
377
374
|
fileScale: normalizeFileScale(defaults.fileScale ?? defaults.imageScale, profile.scale),
|
|
378
375
|
fileMaxWidth: normalizeFileMaxWidth(defaults.fileMaxWidth ?? defaults.imageMaxWidth, profile.maxWidth),
|
|
379
|
-
mode:
|
|
376
|
+
mode: DIFF_MODES.find((value) => value === defaults.mode) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.mode,
|
|
380
377
|
ttlSeconds: normalizeTtlSeconds(defaults.ttlSeconds)
|
|
381
378
|
};
|
|
382
379
|
}
|
|
383
380
|
function resolveDiffsPluginSecurity(config) {
|
|
384
|
-
|
|
385
|
-
const security = config.security;
|
|
386
|
-
if (!security || typeof security !== "object" || Array.isArray(security)) return { ...DEFAULT_DIFFS_PLUGIN_SECURITY };
|
|
387
|
-
return { allowRemoteViewer: security.allowRemoteViewer === true };
|
|
381
|
+
return { allowRemoteViewer: asOptionalRecord(asOptionalRecord(config)?.security)?.allowRemoteViewer === true };
|
|
388
382
|
}
|
|
389
383
|
function resolveDiffsPluginViewerBaseUrl(config) {
|
|
390
|
-
|
|
391
|
-
const viewerBaseUrl = config.viewerBaseUrl;
|
|
384
|
+
const viewerBaseUrl = asOptionalRecord(config)?.viewerBaseUrl;
|
|
392
385
|
if (typeof viewerBaseUrl !== "string") return;
|
|
393
386
|
const normalized = viewerBaseUrl.trim();
|
|
394
387
|
return normalized ? normalizeViewerBaseUrl(normalized) : void 0;
|
|
@@ -397,48 +390,32 @@ function normalizeFontFamily(fontFamily) {
|
|
|
397
390
|
return fontFamily?.trim() || DEFAULT_DIFFS_TOOL_DEFAULTS.fontFamily;
|
|
398
391
|
}
|
|
399
392
|
function normalizeDiffFontSize(fontSize) {
|
|
400
|
-
|
|
401
|
-
return Math.min(Math.max(Math.floor(fontSize), 10), 24);
|
|
393
|
+
return clampInt(asFiniteNumber(fontSize) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.fontSize, 10, 24);
|
|
402
394
|
}
|
|
403
395
|
function normalizeDiffLineSpacing(lineSpacing) {
|
|
404
|
-
|
|
405
|
-
return Math.min(Math.max(lineSpacing, 1), 3);
|
|
406
|
-
}
|
|
407
|
-
function normalizeLayout$1(layout) {
|
|
408
|
-
return layout && DIFF_LAYOUTS.includes(layout) ? layout : DEFAULT_DIFFS_TOOL_DEFAULTS.layout;
|
|
409
|
-
}
|
|
410
|
-
function normalizeDiffIndicators(diffIndicators) {
|
|
411
|
-
return diffIndicators && DIFF_INDICATORS.includes(diffIndicators) ? diffIndicators : DEFAULT_DIFFS_TOOL_DEFAULTS.diffIndicators;
|
|
412
|
-
}
|
|
413
|
-
function normalizeTheme$1(theme) {
|
|
414
|
-
return theme && DIFF_THEMES.includes(theme) ? theme : DEFAULT_DIFFS_TOOL_DEFAULTS.theme;
|
|
396
|
+
return clampNumber(asFiniteNumber(lineSpacing) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.lineSpacing, 1, 3);
|
|
415
397
|
}
|
|
416
398
|
function normalizeFileFormat(fileFormat) {
|
|
417
399
|
return fileFormat && DIFF_OUTPUT_FORMATS.includes(fileFormat) ? fileFormat : DEFAULT_DIFFS_TOOL_DEFAULTS.fileFormat;
|
|
418
400
|
}
|
|
419
|
-
function normalizeFileQuality
|
|
401
|
+
function normalizeFileQuality(fileQuality) {
|
|
420
402
|
return fileQuality && DIFF_IMAGE_QUALITY_PRESETS.includes(fileQuality) ? fileQuality : DEFAULT_DIFFS_TOOL_DEFAULTS.fileQuality;
|
|
421
403
|
}
|
|
422
404
|
function normalizeFileScale(fileScale, fallback) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return Math.min(Math.max(rounded, 1), 4);
|
|
405
|
+
const value = asFiniteNumber(fileScale);
|
|
406
|
+
return value === void 0 ? fallback : clampNumber(Math.round(value * 100) / 100, 1, 4);
|
|
426
407
|
}
|
|
427
408
|
function normalizeFileMaxWidth(fileMaxWidth, fallback) {
|
|
428
|
-
|
|
429
|
-
return
|
|
430
|
-
}
|
|
431
|
-
function normalizeMode$1(mode) {
|
|
432
|
-
return mode && DIFF_MODES.includes(mode) ? mode : DEFAULT_DIFFS_TOOL_DEFAULTS.mode;
|
|
409
|
+
const value = asFiniteNumber(fileMaxWidth);
|
|
410
|
+
return value === void 0 ? fallback : clampNumber(Math.round(value), 640, 2400);
|
|
433
411
|
}
|
|
434
412
|
function normalizeTtlSeconds(ttlSeconds) {
|
|
435
|
-
|
|
436
|
-
return Math.min(Math.max(Math.floor(ttlSeconds), 1), 21600);
|
|
413
|
+
return clampInt(asFiniteNumber(ttlSeconds) ?? DEFAULT_DIFFS_TOOL_DEFAULTS.ttlSeconds, 1, 21600);
|
|
437
414
|
}
|
|
438
415
|
function resolveDiffImageRenderOptions(params) {
|
|
439
416
|
const format = normalizeFileFormat(params.fileFormat ?? params.defaults.fileFormat);
|
|
440
417
|
const qualityOverrideProvided = params.fileQuality !== void 0;
|
|
441
|
-
const qualityPreset = normalizeFileQuality
|
|
418
|
+
const qualityPreset = normalizeFileQuality(params.fileQuality ?? params.defaults.fileQuality);
|
|
442
419
|
const profile = DEFAULT_IMAGE_QUALITY_PROFILES[qualityPreset];
|
|
443
420
|
return {
|
|
444
421
|
format,
|
|
@@ -477,7 +454,7 @@ function createDiffsHttpHandler(params) {
|
|
|
477
454
|
maxEntries: VIEWER_LIMITER_MAX_KEYS
|
|
478
455
|
});
|
|
479
456
|
return async (req, res) => {
|
|
480
|
-
const parsed =
|
|
457
|
+
const parsed = req.url ? URL.parse(req.url, "http://127.0.0.1") : null;
|
|
481
458
|
if (!parsed) return false;
|
|
482
459
|
if (parsed.pathname.startsWith("/plugins/diffs/assets/")) return await serveAsset(req, res, parsed.pathname, params.logger);
|
|
483
460
|
if (!parsed.pathname.startsWith(VIEW_PREFIX)) return false;
|
|
@@ -537,14 +514,6 @@ function createDiffsHttpHandler(params) {
|
|
|
537
514
|
}
|
|
538
515
|
};
|
|
539
516
|
}
|
|
540
|
-
function parseRequestUrl(rawUrl) {
|
|
541
|
-
if (!rawUrl) return null;
|
|
542
|
-
try {
|
|
543
|
-
return new URL(rawUrl, "http://127.0.0.1");
|
|
544
|
-
} catch {
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
517
|
async function serveAsset(req, res, pathname, logger) {
|
|
549
518
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
550
519
|
respondText(res, 405, "Method not allowed");
|
|
@@ -1188,12 +1157,6 @@ function buildDiffTitle(input) {
|
|
|
1188
1157
|
if (input.kind === "before_after") return input.path?.trim() || "Text diff";
|
|
1189
1158
|
return "Patch diff";
|
|
1190
1159
|
}
|
|
1191
|
-
function resolveBeforeAfterFileName(params) {
|
|
1192
|
-
const { input, lang } = params;
|
|
1193
|
-
if (input.path?.trim()) return input.path.trim();
|
|
1194
|
-
if (lang && lang !== "text") return `diff.${lang.replace(/^\.+/, "")}`;
|
|
1195
|
-
return DEFAULT_FILE_NAME;
|
|
1196
|
-
}
|
|
1197
1160
|
function resolveDiffTypography(presentation) {
|
|
1198
1161
|
const fontSize = normalizeDiffFontSize(presentation.fontSize);
|
|
1199
1162
|
const lineSpacing = normalizeDiffLineSpacing(presentation.lineSpacing);
|
|
@@ -1574,43 +1537,29 @@ async function renderSection(input, { viewerOptions, imageOptions, preloadOption
|
|
|
1574
1537
|
usesLanguagePack: payload.langs.some((lang) => !isBaseDiffViewerLanguage(lang))
|
|
1575
1538
|
};
|
|
1576
1539
|
}
|
|
1577
|
-
function buildRenderedBodies(sections, leadingHtml) {
|
|
1578
|
-
const lead = leadingHtml ? [leadingHtml] : [];
|
|
1579
|
-
const viewerSections = sections.flatMap((section) => section.viewer ? [section.viewer] : []);
|
|
1580
|
-
const imageSections = sections.flatMap((section) => section.image ? [section.image] : []);
|
|
1581
|
-
return {
|
|
1582
|
-
...viewerSections.length > 0 ? { viewerBodyHtml: [...lead, ...viewerSections].join("\n") } : {},
|
|
1583
|
-
...imageSections.length > 0 ? { imageBodyHtml: [...lead, ...imageSections].join("\n") } : {}
|
|
1584
|
-
};
|
|
1585
|
-
}
|
|
1586
1540
|
async function renderBeforeAfterDiff(input, options, target) {
|
|
1587
1541
|
const languagePackAvailable = options.languagePackAvailable === true;
|
|
1588
1542
|
const lang = await normalizeSupportedLanguageHint(input.lang, { languagePackAvailable });
|
|
1589
|
-
const fileName =
|
|
1590
|
-
input,
|
|
1591
|
-
lang
|
|
1592
|
-
});
|
|
1593
|
-
const section = await renderSection({
|
|
1594
|
-
oldFile: {
|
|
1595
|
-
name: fileName,
|
|
1596
|
-
contents: input.before,
|
|
1597
|
-
...lang ? { lang } : {}
|
|
1598
|
-
},
|
|
1599
|
-
newFile: {
|
|
1600
|
-
name: fileName,
|
|
1601
|
-
contents: input.after,
|
|
1602
|
-
...lang ? { lang } : {}
|
|
1603
|
-
}
|
|
1604
|
-
}, buildRenderVariants(options, target), languagePackAvailable);
|
|
1543
|
+
const fileName = input.path?.trim() || (lang && lang !== "text" ? `diff.${lang.replace(/^\.+/, "")}` : DEFAULT_FILE_NAME);
|
|
1605
1544
|
return {
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1545
|
+
sections: [await renderSection({
|
|
1546
|
+
oldFile: {
|
|
1547
|
+
name: fileName,
|
|
1548
|
+
contents: input.before,
|
|
1549
|
+
...lang ? { lang } : {}
|
|
1550
|
+
},
|
|
1551
|
+
newFile: {
|
|
1552
|
+
name: fileName,
|
|
1553
|
+
contents: input.after,
|
|
1554
|
+
...lang ? { lang } : {}
|
|
1555
|
+
}
|
|
1556
|
+
}, buildRenderVariants(options, target), languagePackAvailable)],
|
|
1557
|
+
leadingHtml: void 0
|
|
1609
1558
|
};
|
|
1610
1559
|
}
|
|
1611
1560
|
async function renderPatchDiff(input, options, target) {
|
|
1612
1561
|
const languagePackAvailable = options.languagePackAvailable === true;
|
|
1613
|
-
const files =
|
|
1562
|
+
const files = parsePatchFiles(input.patch).flatMap((entry) => entry.files ?? []);
|
|
1614
1563
|
if (files.length === 0) throw new DiffRenderInputError("Patch input did not contain any file diffs.");
|
|
1615
1564
|
if (files.length > MAX_PATCH_FILE_COUNT) throw new DiffRenderInputError(`Patch input contains too many files (max ${MAX_PATCH_FILE_COUNT}).`);
|
|
1616
1565
|
if (files.reduce((sum, fileDiff) => {
|
|
@@ -1624,39 +1573,34 @@ async function renderPatchDiff(input, options, target) {
|
|
|
1624
1573
|
fileDiff,
|
|
1625
1574
|
stats: computeFileDiffStats(fileDiff)
|
|
1626
1575
|
}));
|
|
1627
|
-
const sections = await Promise.all(files.map((fileDiff, index) => renderSection({ fileDiff }, variants, languagePackAvailable, navEntries[index]?.anchorId)));
|
|
1628
1576
|
return {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
usesLanguagePack: sections.some((section) => section.usesLanguagePack === true)
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
async function normalizePatchFileLanguage(fileDiff, options) {
|
|
1635
|
-
const lang = await normalizeSupportedLanguageHint(fileDiff.lang, options);
|
|
1636
|
-
if (lang === fileDiff.lang) return fileDiff;
|
|
1637
|
-
return {
|
|
1638
|
-
...fileDiff,
|
|
1639
|
-
...lang ? { lang } : { lang: "text" }
|
|
1577
|
+
sections: await Promise.all(files.map((fileDiff, index) => renderSection({ fileDiff }, variants, languagePackAvailable, navEntries[index]?.anchorId))),
|
|
1578
|
+
leadingHtml: files.length > 1 ? renderFileSummaryNav(navEntries) : void 0
|
|
1640
1579
|
};
|
|
1641
1580
|
}
|
|
1642
1581
|
async function renderDiffDocument(input, options, target = "both") {
|
|
1643
1582
|
const title = buildDiffTitle(input);
|
|
1644
|
-
const
|
|
1645
|
-
const
|
|
1583
|
+
const { sections, leadingHtml } = input.kind === "before_after" ? await renderBeforeAfterDiff(input, options, target) : await renderPatchDiff(input, options, target);
|
|
1584
|
+
const lead = leadingHtml ? [leadingHtml] : [];
|
|
1585
|
+
const viewerSections = sections.flatMap((section) => section.viewer ? [section.viewer] : []);
|
|
1586
|
+
const imageSections = sections.flatMap((section) => section.image ? [section.image] : []);
|
|
1587
|
+
const viewerBodyHtml = viewerSections.length ? [...lead, ...viewerSections].join("\n") : void 0;
|
|
1588
|
+
const imageBodyHtml = imageSections.length ? [...lead, ...imageSections].join("\n") : void 0;
|
|
1589
|
+
const viewerRuntime = sections.some((section) => section.usesLanguagePack) ? "language-pack" : "base";
|
|
1646
1590
|
const imageTypography = resolveDiffTypography(buildImageRenderOptions(options).presentation);
|
|
1647
1591
|
return {
|
|
1648
|
-
...
|
|
1592
|
+
...viewerBodyHtml ? { html: buildHtmlDocument({
|
|
1649
1593
|
title,
|
|
1650
|
-
bodyHtml:
|
|
1594
|
+
bodyHtml: viewerBodyHtml,
|
|
1651
1595
|
theme: options.presentation.theme,
|
|
1652
1596
|
imageMaxWidth: options.image.maxWidth,
|
|
1653
1597
|
imageTypography,
|
|
1654
1598
|
runtimeMode: "viewer",
|
|
1655
1599
|
viewerRuntime
|
|
1656
1600
|
}) } : {},
|
|
1657
|
-
...
|
|
1601
|
+
...imageBodyHtml ? { imageHtml: buildHtmlDocument({
|
|
1658
1602
|
title,
|
|
1659
|
-
bodyHtml:
|
|
1603
|
+
bodyHtml: imageBodyHtml,
|
|
1660
1604
|
theme: options.presentation.theme,
|
|
1661
1605
|
imageMaxWidth: options.image.maxWidth,
|
|
1662
1606
|
imageTypography,
|
|
@@ -1664,20 +1608,17 @@ async function renderDiffDocument(input, options, target = "both") {
|
|
|
1664
1608
|
viewerRuntime
|
|
1665
1609
|
}) } : {},
|
|
1666
1610
|
title,
|
|
1667
|
-
fileCount:
|
|
1611
|
+
fileCount: sections.length,
|
|
1668
1612
|
inputKind: input.kind,
|
|
1669
1613
|
viewerRuntime
|
|
1670
1614
|
};
|
|
1671
1615
|
}
|
|
1672
|
-
function shouldFallbackToClientHydration(error) {
|
|
1673
|
-
return error instanceof TypeError && error.message.includes("needs an import attribute of \"type: json\"");
|
|
1674
|
-
}
|
|
1675
1616
|
async function preloadDiffHTMLWithFallback(params) {
|
|
1676
1617
|
try {
|
|
1677
1618
|
return await preloadDiffHTML(params);
|
|
1678
1619
|
} catch (error) {
|
|
1679
|
-
if (
|
|
1680
|
-
|
|
1620
|
+
if (error instanceof TypeError && error.message.includes("needs an import attribute of \"type: json\"")) return "";
|
|
1621
|
+
throw error;
|
|
1681
1622
|
}
|
|
1682
1623
|
}
|
|
1683
1624
|
//#endregion
|
|
@@ -1688,7 +1629,7 @@ const MAX_TITLE_BYTES = 1024;
|
|
|
1688
1629
|
const MAX_PATH_BYTES = 2048;
|
|
1689
1630
|
const MAX_LANG_BYTES = 128;
|
|
1690
1631
|
const MAX_DIFF_ARTIFACT_TTL_SECONDS = 21600;
|
|
1691
|
-
const loadDiffsBrowserRuntime = createLazyRuntimeModule(() => import("./.setup/browser.runtime-
|
|
1632
|
+
const loadDiffsBrowserRuntime = createLazyRuntimeModule(() => import("./.setup/browser.runtime-B8JeyUXz.mjs"));
|
|
1692
1633
|
const DiffsToolSchema = Type.Object({
|
|
1693
1634
|
before: Type.Optional(Type.String({ description: "Original text content." })),
|
|
1694
1635
|
after: Type.Optional(Type.String({ description: "Updated text content." })),
|
|
@@ -1732,7 +1673,6 @@ const DiffsToolSchema = Type.Object({
|
|
|
1732
1673
|
baseUrl: Type.Optional(Type.String({ description: "Optional gateway base URL override used when building the viewer URL. Overrides configured viewerBaseUrl, for example https://gateway.example.com." }))
|
|
1733
1674
|
}, { additionalProperties: false });
|
|
1734
1675
|
function createDiffsTool(params) {
|
|
1735
|
-
const loadScreenshotter = async (config) => params.screenshotter ?? new (await (loadDiffsBrowserRuntime())).PlaywrightDiffScreenshotter({ config });
|
|
1736
1676
|
return {
|
|
1737
1677
|
name: "diffs",
|
|
1738
1678
|
label: "Diffs",
|
|
@@ -1744,19 +1684,13 @@ function createDiffsTool(params) {
|
|
|
1744
1684
|
const rawRecord = toolParams;
|
|
1745
1685
|
const artifactContext = buildArtifactContext(params.context);
|
|
1746
1686
|
const input = normalizeDiffInput(toolParams);
|
|
1747
|
-
if (input.kind === "before_after" && input.before === input.after) return {
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
...artifactContext ? { context: artifactContext } : {}
|
|
1755
|
-
}
|
|
1756
|
-
};
|
|
1757
|
-
const mode = normalizeMode(toolParams.mode, params.defaults.mode);
|
|
1758
|
-
const theme = normalizeTheme(toolParams.theme, params.defaults.theme);
|
|
1759
|
-
const layout = normalizeLayout(toolParams.layout, params.defaults.layout);
|
|
1687
|
+
if (input.kind === "before_after" && input.before === input.after) return textResult("Before and after are identical — no changes to render.", {
|
|
1688
|
+
changed: false,
|
|
1689
|
+
...artifactContext ? { context: artifactContext } : {}
|
|
1690
|
+
});
|
|
1691
|
+
const mode = DIFF_MODES.find((value) => value === toolParams.mode) ?? params.defaults.mode;
|
|
1692
|
+
const theme = DIFF_THEMES.find((value) => value === toolParams.theme) ?? params.defaults.theme;
|
|
1693
|
+
const layout = DIFF_LAYOUTS.find((value) => value === toolParams.layout) ?? params.defaults.layout;
|
|
1760
1694
|
const expandUnchanged = toolParams.expandUnchanged === true;
|
|
1761
1695
|
const ttlSeconds = readFiniteNumberParam(rawRecord, "ttlSeconds") ?? params.defaults.ttlSeconds;
|
|
1762
1696
|
const fileScale = readFiniteNumberParam(rawRecord, "fileScale");
|
|
@@ -1764,8 +1698,8 @@ function createDiffsTool(params) {
|
|
|
1764
1698
|
const ttlMs = normalizeTtlMs(ttlSeconds);
|
|
1765
1699
|
const image = resolveDiffImageRenderOptions({
|
|
1766
1700
|
defaults: params.defaults,
|
|
1767
|
-
fileFormat:
|
|
1768
|
-
fileQuality:
|
|
1701
|
+
fileFormat: DIFF_OUTPUT_FORMATS.find((value) => value === toolParams.fileFormat),
|
|
1702
|
+
fileQuality: DIFF_IMAGE_QUALITY_PRESETS.find((value) => value === toolParams.fileQuality),
|
|
1769
1703
|
fileScale,
|
|
1770
1704
|
fileMaxWidth
|
|
1771
1705
|
});
|
|
@@ -1809,72 +1743,65 @@ function createDiffsTool(params) {
|
|
|
1809
1743
|
mode,
|
|
1810
1744
|
...artifactContext ? { context: artifactContext } : {}
|
|
1811
1745
|
} : void 0;
|
|
1812
|
-
if (mode === "view") return {
|
|
1813
|
-
content: [{
|
|
1814
|
-
type: "text",
|
|
1815
|
-
text: `Diff viewer ready.\n${viewerUrl}`
|
|
1816
|
-
}],
|
|
1817
|
-
details: viewerDetails
|
|
1818
|
-
};
|
|
1746
|
+
if (mode === "view") return textResult(`Diff viewer ready.\n${viewerUrl}`, viewerDetails);
|
|
1819
1747
|
try {
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
theme,
|
|
1825
|
-
image,
|
|
1748
|
+
const screenshotter = params.screenshotter ?? new (await (loadDiffsBrowserRuntime())).PlaywrightDiffScreenshotter({ config });
|
|
1749
|
+
const html = requireRenderedHtml(rendered.imageHtml, "image");
|
|
1750
|
+
const artifactFile = await params.store.createStandaloneFileArtifact({
|
|
1751
|
+
format: image.format,
|
|
1826
1752
|
ttlMs,
|
|
1827
1753
|
context: artifactContext
|
|
1828
1754
|
});
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
viewerUrl
|
|
1836
|
-
})
|
|
1837
|
-
}],
|
|
1838
|
-
details: buildArtifactDetails({
|
|
1839
|
-
baseDetails: viewerDetails ?? {
|
|
1840
|
-
changed: true,
|
|
1841
|
-
...artifactFile.artifactId ? { artifactId: artifactFile.artifactId } : {},
|
|
1842
|
-
...artifactFile.expiresAt ? { expiresAt: artifactFile.expiresAt } : {},
|
|
1843
|
-
title: rendered.title,
|
|
1844
|
-
inputKind: rendered.inputKind,
|
|
1845
|
-
fileCount: rendered.fileCount,
|
|
1846
|
-
mode,
|
|
1847
|
-
...artifactContext ? { context: artifactContext } : {}
|
|
1848
|
-
},
|
|
1849
|
-
artifactFile,
|
|
1755
|
+
let fileBytes;
|
|
1756
|
+
try {
|
|
1757
|
+
await screenshotter.screenshotHtml({
|
|
1758
|
+
html,
|
|
1759
|
+
outputPath: artifactFile.filePath,
|
|
1760
|
+
theme,
|
|
1850
1761
|
image
|
|
1851
|
-
})
|
|
1852
|
-
|
|
1762
|
+
});
|
|
1763
|
+
fileBytes = (await fs$1.stat(artifactFile.filePath)).size;
|
|
1764
|
+
await params.store.completeFileArtifact(artifactFile.id);
|
|
1765
|
+
} catch (error) {
|
|
1766
|
+
await params.store.deleteFileArtifact(artifactFile.id);
|
|
1767
|
+
throw error;
|
|
1768
|
+
}
|
|
1769
|
+
return textResult([
|
|
1770
|
+
...viewerUrl ? [`Diff viewer: ${viewerUrl}`] : [],
|
|
1771
|
+
`Diff ${image.format.toUpperCase()} generated at: ${artifactFile.filePath}`,
|
|
1772
|
+
"To send this file, use an available file-sending tool to send it as an attachment."
|
|
1773
|
+
].join("\n"), {
|
|
1774
|
+
...viewerDetails ?? {
|
|
1775
|
+
changed: true,
|
|
1776
|
+
artifactId: artifactFile.id,
|
|
1777
|
+
expiresAt: artifactFile.expiresAt,
|
|
1778
|
+
title: rendered.title,
|
|
1779
|
+
inputKind: rendered.inputKind,
|
|
1780
|
+
fileCount: rendered.fileCount,
|
|
1781
|
+
mode,
|
|
1782
|
+
...artifactContext ? { context: artifactContext } : {}
|
|
1783
|
+
},
|
|
1784
|
+
filePath: artifactFile.filePath,
|
|
1785
|
+
path: artifactFile.filePath,
|
|
1786
|
+
fileBytes,
|
|
1787
|
+
fileFormat: image.format,
|
|
1788
|
+
fileQuality: image.qualityPreset,
|
|
1789
|
+
fileScale: image.scale,
|
|
1790
|
+
fileMaxWidth: image.maxWidth
|
|
1791
|
+
});
|
|
1853
1792
|
} catch (error) {
|
|
1854
1793
|
if (mode === "both") {
|
|
1855
1794
|
const errorMessage = formatErrorMessage(error);
|
|
1856
|
-
return {
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
}],
|
|
1861
|
-
details: {
|
|
1862
|
-
...viewerDetails,
|
|
1863
|
-
fileError: errorMessage
|
|
1864
|
-
}
|
|
1865
|
-
};
|
|
1795
|
+
return textResult(`Diff viewer ready.\n${viewerUrl}\nFile rendering failed: ${errorMessage}`, {
|
|
1796
|
+
...viewerDetails,
|
|
1797
|
+
fileError: errorMessage
|
|
1798
|
+
});
|
|
1866
1799
|
}
|
|
1867
1800
|
throw error;
|
|
1868
1801
|
}
|
|
1869
1802
|
}
|
|
1870
1803
|
};
|
|
1871
1804
|
}
|
|
1872
|
-
function normalizeFileQuality(fileQuality) {
|
|
1873
|
-
return fileQuality && DIFF_IMAGE_QUALITY_PRESETS.includes(fileQuality) ? fileQuality : void 0;
|
|
1874
|
-
}
|
|
1875
|
-
function normalizeOutputFormat(format) {
|
|
1876
|
-
return format && DIFF_OUTPUT_FORMATS.includes(format) ? format : void 0;
|
|
1877
|
-
}
|
|
1878
1805
|
function isArtifactOnlyMode(mode) {
|
|
1879
1806
|
return mode === "image" || mode === "file";
|
|
1880
1807
|
}
|
|
@@ -1887,62 +1814,18 @@ function requireRenderedHtml(html, target) {
|
|
|
1887
1814
|
if (html !== void 0) return html;
|
|
1888
1815
|
throw new Error(`Missing ${target} render output.`);
|
|
1889
1816
|
}
|
|
1890
|
-
function buildArtifactDetails(params) {
|
|
1891
|
-
return {
|
|
1892
|
-
...params.baseDetails,
|
|
1893
|
-
filePath: params.artifactFile.path,
|
|
1894
|
-
path: params.artifactFile.path,
|
|
1895
|
-
fileBytes: params.artifactFile.bytes,
|
|
1896
|
-
fileFormat: params.image.format,
|
|
1897
|
-
fileQuality: params.image.qualityPreset,
|
|
1898
|
-
fileScale: params.image.scale,
|
|
1899
|
-
fileMaxWidth: params.image.maxWidth
|
|
1900
|
-
};
|
|
1901
|
-
}
|
|
1902
|
-
function buildFileArtifactMessage(params) {
|
|
1903
|
-
const lines = params.viewerUrl ? [`Diff viewer: ${params.viewerUrl}`] : [];
|
|
1904
|
-
lines.push(`Diff ${params.format.toUpperCase()} generated at: ${params.filePath}`);
|
|
1905
|
-
lines.push("To send this file, use an available file-sending tool to send it as an attachment.");
|
|
1906
|
-
return lines.join("\n");
|
|
1907
|
-
}
|
|
1908
|
-
async function renderDiffArtifactFile(params) {
|
|
1909
|
-
const fileArtifact = await params.store.createStandaloneFileArtifact({
|
|
1910
|
-
format: params.image.format,
|
|
1911
|
-
ttlMs: params.ttlMs,
|
|
1912
|
-
context: params.context
|
|
1913
|
-
});
|
|
1914
|
-
try {
|
|
1915
|
-
await params.screenshotter.screenshotHtml({
|
|
1916
|
-
html: params.html,
|
|
1917
|
-
outputPath: fileArtifact.filePath,
|
|
1918
|
-
theme: params.theme,
|
|
1919
|
-
image: params.image
|
|
1920
|
-
});
|
|
1921
|
-
const stats = await fs$1.stat(fileArtifact.filePath);
|
|
1922
|
-
await params.store.completeFileArtifact(fileArtifact.id);
|
|
1923
|
-
return {
|
|
1924
|
-
path: fileArtifact.filePath,
|
|
1925
|
-
bytes: stats.size,
|
|
1926
|
-
artifactId: fileArtifact.id,
|
|
1927
|
-
expiresAt: fileArtifact.expiresAt
|
|
1928
|
-
};
|
|
1929
|
-
} catch (error) {
|
|
1930
|
-
await params.store.deleteFileArtifact(fileArtifact.id);
|
|
1931
|
-
throw error;
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
1817
|
function buildArtifactContext(context) {
|
|
1935
1818
|
if (!context) return;
|
|
1936
|
-
const
|
|
1937
|
-
const
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
}
|
|
1819
|
+
const artifactContext = {};
|
|
1820
|
+
for (const key of [
|
|
1821
|
+
"agentId",
|
|
1822
|
+
"sessionId",
|
|
1823
|
+
"messageChannel",
|
|
1824
|
+
"agentAccountId"
|
|
1825
|
+
]) {
|
|
1826
|
+
const value = normalizeOptionalString(context[key]);
|
|
1827
|
+
if (value) artifactContext[key] = value;
|
|
1828
|
+
}
|
|
1946
1829
|
return Object.keys(artifactContext).length > 0 ? artifactContext : void 0;
|
|
1947
1830
|
}
|
|
1948
1831
|
function normalizeDiffInput(params) {
|
|
@@ -1991,15 +1874,6 @@ function normalizeBaseUrl(baseUrl) {
|
|
|
1991
1874
|
throw new PluginToolInputError(`Invalid baseUrl: ${normalized}`);
|
|
1992
1875
|
}
|
|
1993
1876
|
}
|
|
1994
|
-
function normalizeMode(mode, fallback) {
|
|
1995
|
-
return mode && DIFF_MODES.includes(mode) ? mode : fallback;
|
|
1996
|
-
}
|
|
1997
|
-
function normalizeTheme(theme, fallback) {
|
|
1998
|
-
return theme && DIFF_THEMES.includes(theme) ? theme : fallback;
|
|
1999
|
-
}
|
|
2000
|
-
function normalizeLayout(layout, fallback) {
|
|
2001
|
-
return layout && DIFF_LAYOUTS.includes(layout) ? layout : fallback;
|
|
2002
|
-
}
|
|
2003
1877
|
function normalizeTtlMs(ttlSeconds) {
|
|
2004
1878
|
if (!Number.isFinite(ttlSeconds) || ttlSeconds === void 0) return;
|
|
2005
1879
|
return Math.floor(Math.min(Math.max(ttlSeconds, 1), MAX_DIFF_ARTIFACT_TTL_SECONDS) * 1e3);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/diffs",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1-beta.2",
|
|
4
4
|
"description": "OpenClaw read-only diff viewer plugin and file renderer for agents.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
"minHostVersion": ">=2026.4.30"
|
|
26
26
|
},
|
|
27
27
|
"compat": {
|
|
28
|
-
"pluginApi": ">=2026.
|
|
28
|
+
"pluginApi": ">=2026.10.1-beta.2"
|
|
29
29
|
},
|
|
30
30
|
"assetScripts": {
|
|
31
31
|
"build": "node --import tsx ../../scripts/build-diffs-viewer-runtime.mts curated"
|
|
32
32
|
},
|
|
33
33
|
"build": {
|
|
34
34
|
"bundledDist": false,
|
|
35
|
-
"openclawVersion": "2026.
|
|
35
|
+
"openclawVersion": "2026.10.1-beta.2",
|
|
36
36
|
"staticAssets": [
|
|
37
37
|
{
|
|
38
38
|
"source": "./assets/viewer-runtime.js",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"skills/**"
|
|
58
58
|
],
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"openclaw": ">=2026.
|
|
60
|
+
"openclaw": ">=2026.10.1-beta.2"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"openclaw": {
|