@openclaw/diffs 2026.5.7 → 2026.5.10-beta.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/README.md +2 -0
- package/dist/index.js +89 -64
- package/openclaw.plugin.json +11 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -106,6 +106,7 @@ Set plugin-wide defaults in `~/.openclaw/openclaw.json`:
|
|
|
106
106
|
fileScale: 2,
|
|
107
107
|
fileMaxWidth: 960,
|
|
108
108
|
mode: "both",
|
|
109
|
+
ttlSeconds: 21600,
|
|
109
110
|
},
|
|
110
111
|
},
|
|
111
112
|
},
|
|
@@ -120,6 +121,7 @@ Security options:
|
|
|
120
121
|
|
|
121
122
|
- `security.allowRemoteViewer` (default `false`): allows non-loopback access to `/plugins/diffs/view/...` token URLs
|
|
122
123
|
- `viewerBaseUrl` (optional): persistent viewer-link origin/path fallback for shareable URLs
|
|
124
|
+
- `defaults.ttlSeconds` (default `1800`, max `21600`): default artifact lifetime for viewer and standalone file outputs
|
|
123
125
|
|
|
124
126
|
Example:
|
|
125
127
|
|
package/dist/index.js
CHANGED
|
@@ -10,12 +10,15 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "opencl
|
|
|
10
10
|
import crypto from "node:crypto";
|
|
11
11
|
import fs from "node:fs/promises";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { root, writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
|
|
14
|
+
import { stringEnum } from "openclaw/plugin-sdk/channel-actions";
|
|
13
15
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
14
16
|
import { Type } from "typebox";
|
|
15
17
|
import { constants } from "node:fs";
|
|
16
18
|
import { chromium } from "playwright-core";
|
|
17
19
|
import { RegisteredCustomThemes, ResolvedThemes, ResolvingThemes, parsePatchFiles, resolveLanguage } from "@pierre/diffs";
|
|
18
20
|
import { preloadFileDiff, preloadMultiFileDiff } from "@pierre/diffs/ssr";
|
|
21
|
+
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
|
19
22
|
//#region extensions/diffs/src/types.ts
|
|
20
23
|
const DIFF_LAYOUTS = ["unified", "split"];
|
|
21
24
|
const DIFF_MODES = [
|
|
@@ -104,7 +107,8 @@ const DEFAULT_DIFFS_TOOL_DEFAULTS = {
|
|
|
104
107
|
fileQuality: "standard",
|
|
105
108
|
fileScale: DEFAULT_IMAGE_QUALITY_PROFILES.standard.scale,
|
|
106
109
|
fileMaxWidth: DEFAULT_IMAGE_QUALITY_PROFILES.standard.maxWidth,
|
|
107
|
-
mode: "both"
|
|
110
|
+
mode: "both",
|
|
111
|
+
ttlSeconds: 1800
|
|
108
112
|
};
|
|
109
113
|
const DEFAULT_DIFFS_PLUGIN_SECURITY = { allowRemoteViewer: false };
|
|
110
114
|
const VIEWER_BASE_URL_JSON_SCHEMA = {
|
|
@@ -143,7 +147,8 @@ const DiffsPluginJsonSchemaSource = z.strictObject({
|
|
|
143
147
|
imageQuality: z.enum(DIFF_IMAGE_QUALITY_PRESETS).optional().describe("Deprecated alias for fileQuality."),
|
|
144
148
|
imageScale: z.number().min(1).max(4).optional().describe("Deprecated alias for fileScale."),
|
|
145
149
|
imageMaxWidth: z.number().min(640).max(2400).optional().describe("Deprecated alias for fileMaxWidth."),
|
|
146
|
-
mode: z.enum(DIFF_MODES).default(DEFAULT_DIFFS_TOOL_DEFAULTS.mode).optional()
|
|
150
|
+
mode: z.enum(DIFF_MODES).default(DEFAULT_DIFFS_TOOL_DEFAULTS.mode).optional(),
|
|
151
|
+
ttlSeconds: z.number().min(1).max(21600).default(DEFAULT_DIFFS_TOOL_DEFAULTS.ttlSeconds).optional()
|
|
147
152
|
}).optional(),
|
|
148
153
|
security: z.strictObject({ allowRemoteViewer: z.boolean().default(DEFAULT_DIFFS_PLUGIN_SECURITY.allowRemoteViewer).optional() }).optional()
|
|
149
154
|
});
|
|
@@ -222,7 +227,8 @@ function resolveDiffsPluginDefaults(config) {
|
|
|
222
227
|
fileQuality,
|
|
223
228
|
fileScale: normalizeFileScale(fileScale, profile.scale),
|
|
224
229
|
fileMaxWidth: normalizeFileMaxWidth(fileMaxWidth, profile.maxWidth),
|
|
225
|
-
mode: normalizeMode$1(defaults.mode)
|
|
230
|
+
mode: normalizeMode$1(defaults.mode),
|
|
231
|
+
ttlSeconds: normalizeTtlSeconds(defaults.ttlSeconds)
|
|
226
232
|
};
|
|
227
233
|
}
|
|
228
234
|
function resolveDiffsPluginSecurity(config) {
|
|
@@ -276,6 +282,10 @@ function normalizeFileMaxWidth(fileMaxWidth, fallback) {
|
|
|
276
282
|
function normalizeMode$1(mode) {
|
|
277
283
|
return mode && DIFF_MODES.includes(mode) ? mode : DEFAULT_DIFFS_TOOL_DEFAULTS.mode;
|
|
278
284
|
}
|
|
285
|
+
function normalizeTtlSeconds(ttlSeconds) {
|
|
286
|
+
if (ttlSeconds === void 0 || !Number.isFinite(ttlSeconds)) return DEFAULT_DIFFS_TOOL_DEFAULTS.ttlSeconds;
|
|
287
|
+
return Math.min(Math.max(Math.floor(ttlSeconds), 1), 21600);
|
|
288
|
+
}
|
|
279
289
|
function resolveDiffImageRenderOptions(params) {
|
|
280
290
|
const format = normalizeFileFormat(params.fileFormat ?? params.imageFormat ?? params.format ?? params.defaults.fileFormat);
|
|
281
291
|
const qualityOverrideProvided = params.fileQuality !== void 0 || params.imageQuality !== void 0;
|
|
@@ -597,8 +607,9 @@ var DiffArtifactStore = class {
|
|
|
597
607
|
htmlPath,
|
|
598
608
|
...params.context ? { context: params.context } : {}
|
|
599
609
|
};
|
|
600
|
-
await
|
|
601
|
-
await
|
|
610
|
+
const root = await this.artifactRoot();
|
|
611
|
+
await root.mkdir(id);
|
|
612
|
+
await root.write(path.posix.join(id, "viewer.html"), params.html);
|
|
602
613
|
await this.writeMeta(meta);
|
|
603
614
|
this.scheduleCleanup();
|
|
604
615
|
return meta;
|
|
@@ -617,7 +628,7 @@ var DiffArtifactStore = class {
|
|
|
617
628
|
const meta = await this.readMeta(id);
|
|
618
629
|
if (!meta) throw new Error(`Diff artifact not found: ${id}`);
|
|
619
630
|
const htmlPath = this.normalizeStoredPath(meta.htmlPath, "htmlPath");
|
|
620
|
-
return await
|
|
631
|
+
return await (await this.artifactRoot()).readText(this.relativeStoredPath(htmlPath));
|
|
621
632
|
}
|
|
622
633
|
async updateFilePath(id, filePath) {
|
|
623
634
|
const meta = await this.readMeta(id);
|
|
@@ -654,7 +665,7 @@ var DiffArtifactStore = class {
|
|
|
654
665
|
filePath: this.normalizeStoredPath(filePath, "filePath"),
|
|
655
666
|
...params.context ? { context: params.context } : {}
|
|
656
667
|
};
|
|
657
|
-
await
|
|
668
|
+
await (await this.artifactRoot()).mkdir(id);
|
|
658
669
|
await this.writeStandaloneMeta(meta);
|
|
659
670
|
this.scheduleCleanup();
|
|
660
671
|
return {
|
|
@@ -671,10 +682,9 @@ var DiffArtifactStore = class {
|
|
|
671
682
|
this.maybeCleanupExpired();
|
|
672
683
|
}
|
|
673
684
|
async cleanupExpired() {
|
|
674
|
-
await this.
|
|
675
|
-
const entries = await fs.readdir(this.rootDir, { withFileTypes: true }).catch(() => []);
|
|
685
|
+
const entries = await (await this.artifactRoot()).list("", { withFileTypes: true }).catch(() => []);
|
|
676
686
|
const now = Date.now();
|
|
677
|
-
await Promise.all(entries.filter((entry) => entry.isDirectory
|
|
687
|
+
await Promise.all(entries.filter((entry) => entry.isDirectory).map(async (entry) => {
|
|
678
688
|
const id = entry.name;
|
|
679
689
|
const meta = await this.readMeta(id);
|
|
680
690
|
if (meta) {
|
|
@@ -686,15 +696,16 @@ var DiffArtifactStore = class {
|
|
|
686
696
|
if (isExpired(standaloneMeta)) await this.deleteArtifact(id);
|
|
687
697
|
return;
|
|
688
698
|
}
|
|
689
|
-
|
|
690
|
-
const stat = await fs.stat(artifactPath).catch(() => null);
|
|
691
|
-
if (!stat) return;
|
|
692
|
-
if (now - stat.mtimeMs > SWEEP_FALLBACK_AGE_MS) await this.deleteArtifact(id);
|
|
699
|
+
if (now - entry.mtimeMs > SWEEP_FALLBACK_AGE_MS) await this.deleteArtifact(id);
|
|
693
700
|
}));
|
|
694
701
|
}
|
|
695
702
|
async ensureRoot() {
|
|
696
703
|
await fs.mkdir(this.rootDir, { recursive: true });
|
|
697
704
|
}
|
|
705
|
+
async artifactRoot() {
|
|
706
|
+
await this.ensureRoot();
|
|
707
|
+
return await root(this.rootDir);
|
|
708
|
+
}
|
|
698
709
|
maybeCleanupExpired() {
|
|
699
710
|
const now = Date.now();
|
|
700
711
|
if (this.cleanupInFlight || now < this.nextCleanupAt) return;
|
|
@@ -740,15 +751,12 @@ var DiffArtifactStore = class {
|
|
|
740
751
|
return null;
|
|
741
752
|
}
|
|
742
753
|
}
|
|
743
|
-
metaFilePath(id, fileName) {
|
|
744
|
-
return path.join(this.artifactDir(id), fileName);
|
|
745
|
-
}
|
|
746
754
|
async writeJsonMeta(id, fileName, data) {
|
|
747
|
-
await
|
|
755
|
+
await (await this.artifactRoot()).writeJson(path.posix.join(id, fileName), data, { space: 2 });
|
|
748
756
|
}
|
|
749
757
|
async readJsonMeta(id, fileName, context) {
|
|
750
758
|
try {
|
|
751
|
-
const raw = await
|
|
759
|
+
const raw = await (await this.artifactRoot()).readText(path.posix.join(id, fileName));
|
|
752
760
|
return JSON.parse(raw);
|
|
753
761
|
} catch (error) {
|
|
754
762
|
if (isFileNotFound(error)) return null;
|
|
@@ -772,6 +780,9 @@ var DiffArtifactStore = class {
|
|
|
772
780
|
this.assertWithinRoot(candidate, label);
|
|
773
781
|
return candidate;
|
|
774
782
|
}
|
|
783
|
+
relativeStoredPath(storedPath) {
|
|
784
|
+
return path.relative(this.rootDir, this.normalizeStoredPath(storedPath, "path")).split(path.sep).join(path.posix.sep);
|
|
785
|
+
}
|
|
775
786
|
assertWithinRoot(candidate, label = "path") {
|
|
776
787
|
const relative = path.relative(this.rootDir, candidate);
|
|
777
788
|
if (relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative)) return;
|
|
@@ -790,7 +801,8 @@ function isExpired(meta) {
|
|
|
790
801
|
return Date.now() >= expiresAt;
|
|
791
802
|
}
|
|
792
803
|
function isFileNotFound(error) {
|
|
793
|
-
|
|
804
|
+
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
805
|
+
return code === "ENOENT" || code === "not-found";
|
|
794
806
|
}
|
|
795
807
|
function normalizeArtifactContext(value) {
|
|
796
808
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
@@ -819,7 +831,6 @@ var PlaywrightDiffScreenshotter = class {
|
|
|
819
831
|
this.browserIdleMs = params.browserIdleMs ?? DEFAULT_BROWSER_IDLE_MS;
|
|
820
832
|
}
|
|
821
833
|
async screenshotHtml(params) {
|
|
822
|
-
await fs.mkdir(path.dirname(params.outputPath), { recursive: true });
|
|
823
834
|
const lease = await acquireSharedBrowser({
|
|
824
835
|
config: this.config,
|
|
825
836
|
idleMs: this.browserIdleMs
|
|
@@ -918,16 +929,22 @@ var PlaywrightDiffScreenshotter = class {
|
|
|
918
929
|
const estimatedPixels = pdfWidth * pdfHeight;
|
|
919
930
|
const estimatedPages = Math.ceil(pdfHeight / PDF_REFERENCE_PAGE_HEIGHT_PX);
|
|
920
931
|
if (estimatedPixels > params.image.maxPixels || estimatedPages > MAX_PDF_PAGES) throw new Error(IMAGE_SIZE_LIMIT_ERROR);
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
932
|
+
const pageForPdf = page;
|
|
933
|
+
await writeExternalArtifactFile({
|
|
934
|
+
outputPath: params.outputPath,
|
|
935
|
+
write: async (tempPath) => {
|
|
936
|
+
await pageForPdf.pdf({
|
|
937
|
+
path: tempPath,
|
|
938
|
+
width: `${pdfWidth}px`,
|
|
939
|
+
height: `${pdfHeight}px`,
|
|
940
|
+
printBackground: true,
|
|
941
|
+
margin: {
|
|
942
|
+
top: "0",
|
|
943
|
+
right: "0",
|
|
944
|
+
bottom: "0",
|
|
945
|
+
left: "0"
|
|
946
|
+
}
|
|
947
|
+
});
|
|
931
948
|
}
|
|
932
949
|
});
|
|
933
950
|
return params.outputPath;
|
|
@@ -956,15 +973,21 @@ var PlaywrightDiffScreenshotter = class {
|
|
|
956
973
|
}
|
|
957
974
|
throw new Error(IMAGE_SIZE_LIMIT_ERROR);
|
|
958
975
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
976
|
+
const pageForScreenshot = page;
|
|
977
|
+
await writeExternalArtifactFile({
|
|
978
|
+
outputPath: params.outputPath,
|
|
979
|
+
write: async (tempPath) => {
|
|
980
|
+
await pageForScreenshot.screenshot({
|
|
981
|
+
path: tempPath,
|
|
982
|
+
type: "png",
|
|
983
|
+
scale: "device",
|
|
984
|
+
clip: {
|
|
985
|
+
x,
|
|
986
|
+
y,
|
|
987
|
+
width: cssWidth,
|
|
988
|
+
height: cssHeight
|
|
989
|
+
}
|
|
990
|
+
});
|
|
968
991
|
}
|
|
969
992
|
});
|
|
970
993
|
return params.outputPath;
|
|
@@ -980,6 +1003,15 @@ var PlaywrightDiffScreenshotter = class {
|
|
|
980
1003
|
}
|
|
981
1004
|
}
|
|
982
1005
|
};
|
|
1006
|
+
async function writeExternalArtifactFile(params) {
|
|
1007
|
+
const rootDir = path.dirname(params.outputPath);
|
|
1008
|
+
await fs.mkdir(rootDir, { recursive: true });
|
|
1009
|
+
await writeExternalFileWithinRoot({
|
|
1010
|
+
rootDir,
|
|
1011
|
+
path: path.basename(params.outputPath),
|
|
1012
|
+
write: params.write
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
983
1015
|
function injectBaseHref(html) {
|
|
984
1016
|
if (html.includes("<base ")) return html;
|
|
985
1017
|
return html.replace("<head>", `<head><base href="${LOCAL_VIEWER_BASE_HREF}" />`);
|
|
@@ -1169,13 +1201,8 @@ async function isExecutable(candidate) {
|
|
|
1169
1201
|
//#endregion
|
|
1170
1202
|
//#region extensions/diffs/src/language-hints.ts
|
|
1171
1203
|
const PASSTHROUGH_LANGUAGE_HINTS = new Set(["ansi", "text"]);
|
|
1172
|
-
function normalizeOptionalString$1(value) {
|
|
1173
|
-
if (typeof value !== "string") return;
|
|
1174
|
-
const trimmed = value.trim();
|
|
1175
|
-
return trimmed ? trimmed : void 0;
|
|
1176
|
-
}
|
|
1177
1204
|
async function normalizeSupportedLanguageHint(value) {
|
|
1178
|
-
const normalized = normalizeOptionalString
|
|
1205
|
+
const normalized = normalizeOptionalString(value);
|
|
1179
1206
|
if (!normalized) return;
|
|
1180
1207
|
if (PASSTHROUGH_LANGUAGE_HINTS.has(normalized)) return normalized;
|
|
1181
1208
|
try {
|
|
@@ -1246,9 +1273,9 @@ function createThemeLoader(themeName, themeSpecifier) {
|
|
|
1246
1273
|
let cachedTheme;
|
|
1247
1274
|
return async () => {
|
|
1248
1275
|
if (cachedTheme) return cachedTheme;
|
|
1249
|
-
const
|
|
1276
|
+
const { value: theme } = await readJsonFileWithFallback(themeRequire.resolve(themeSpecifier), {});
|
|
1250
1277
|
cachedTheme = {
|
|
1251
|
-
...
|
|
1278
|
+
...theme,
|
|
1252
1279
|
name: themeName
|
|
1253
1280
|
};
|
|
1254
1281
|
return cachedTheme;
|
|
@@ -1677,14 +1704,6 @@ const MAX_PATCH_BYTES = 2 * 1024 * 1024;
|
|
|
1677
1704
|
const MAX_TITLE_BYTES = 1024;
|
|
1678
1705
|
const MAX_PATH_BYTES = 2048;
|
|
1679
1706
|
const MAX_LANG_BYTES = 128;
|
|
1680
|
-
function stringEnum(values, description, options = {}) {
|
|
1681
|
-
return Type.Unsafe({
|
|
1682
|
-
type: "string",
|
|
1683
|
-
enum: [...values],
|
|
1684
|
-
description,
|
|
1685
|
-
...options
|
|
1686
|
-
});
|
|
1687
|
-
}
|
|
1688
1707
|
const DiffsToolSchema = Type.Object({
|
|
1689
1708
|
before: Type.Optional(Type.String({ description: "Original text content." })),
|
|
1690
1709
|
after: Type.Optional(Type.String({ description: "Updated text content." })),
|
|
@@ -1704,11 +1723,11 @@ const DiffsToolSchema = Type.Object({
|
|
|
1704
1723
|
description: "Optional title for the rendered diff.",
|
|
1705
1724
|
maxLength: MAX_TITLE_BYTES
|
|
1706
1725
|
})),
|
|
1707
|
-
mode: Type.Optional(stringEnum(DIFF_MODES, "Output mode: view, file, image (deprecated alias for file), or both. Default: both.")),
|
|
1708
|
-
theme: Type.Optional(stringEnum(DIFF_THEMES, "Viewer theme. Default: dark.")),
|
|
1709
|
-
layout: Type.Optional(stringEnum(DIFF_LAYOUTS, "Diff layout. Default: unified.")),
|
|
1710
|
-
fileQuality: Type.Optional(stringEnum(DIFF_IMAGE_QUALITY_PRESETS, "File quality preset: standard, hq, or print.")),
|
|
1711
|
-
fileFormat: Type.Optional(stringEnum(DIFF_OUTPUT_FORMATS, "Rendered file format: png or pdf.")),
|
|
1726
|
+
mode: Type.Optional(stringEnum(DIFF_MODES, { description: "Output mode: view, file, image (deprecated alias for file), or both. Default: both." })),
|
|
1727
|
+
theme: Type.Optional(stringEnum(DIFF_THEMES, { description: "Viewer theme. Default: dark." })),
|
|
1728
|
+
layout: Type.Optional(stringEnum(DIFF_LAYOUTS, { description: "Diff layout. Default: unified." })),
|
|
1729
|
+
fileQuality: Type.Optional(stringEnum(DIFF_IMAGE_QUALITY_PRESETS, { description: "File quality preset: standard, hq, or print." })),
|
|
1730
|
+
fileFormat: Type.Optional(stringEnum(DIFF_OUTPUT_FORMATS, { description: "Rendered file format: png or pdf." })),
|
|
1712
1731
|
fileScale: Type.Optional(Type.Number({
|
|
1713
1732
|
description: "Optional rendered-file device scale factor override (1-4).",
|
|
1714
1733
|
minimum: 1,
|
|
@@ -1720,9 +1739,15 @@ const DiffsToolSchema = Type.Object({
|
|
|
1720
1739
|
maximum: 2400
|
|
1721
1740
|
})),
|
|
1722
1741
|
/** @deprecated Use fileQuality. */
|
|
1723
|
-
imageQuality: Type.Optional(stringEnum(DIFF_IMAGE_QUALITY_PRESETS,
|
|
1742
|
+
imageQuality: Type.Optional(stringEnum(DIFF_IMAGE_QUALITY_PRESETS, {
|
|
1743
|
+
description: "Deprecated alias for fileQuality.",
|
|
1744
|
+
deprecated: true
|
|
1745
|
+
})),
|
|
1724
1746
|
/** @deprecated Use fileFormat. */
|
|
1725
|
-
imageFormat: Type.Optional(stringEnum(DIFF_OUTPUT_FORMATS,
|
|
1747
|
+
imageFormat: Type.Optional(stringEnum(DIFF_OUTPUT_FORMATS, {
|
|
1748
|
+
description: "Deprecated alias for fileFormat.",
|
|
1749
|
+
deprecated: true
|
|
1750
|
+
})),
|
|
1726
1751
|
/** @deprecated Use fileScale. */
|
|
1727
1752
|
imageScale: Type.Optional(Type.Number({
|
|
1728
1753
|
description: "Deprecated alias for fileScale.",
|
|
@@ -1759,7 +1784,7 @@ function createDiffsTool(params) {
|
|
|
1759
1784
|
const theme = normalizeTheme(toolParams.theme, params.defaults.theme);
|
|
1760
1785
|
const layout = normalizeLayout(toolParams.layout, params.defaults.layout);
|
|
1761
1786
|
const expandUnchanged = toolParams.expandUnchanged === true;
|
|
1762
|
-
const ttlMs = normalizeTtlMs(toolParams.ttlSeconds);
|
|
1787
|
+
const ttlMs = normalizeTtlMs(toolParams.ttlSeconds ?? params.defaults.ttlSeconds);
|
|
1763
1788
|
const image = resolveDiffImageRenderOptions({
|
|
1764
1789
|
defaults: params.defaults,
|
|
1765
1790
|
fileFormat: normalizeOutputFormat(toolParams.fileFormat ?? toolParams.imageFormat ?? toolParams.format),
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "diffs",
|
|
3
3
|
"activation": {
|
|
4
|
-
"onStartup":
|
|
4
|
+
"onStartup": true
|
|
5
5
|
},
|
|
6
6
|
"name": "Diffs",
|
|
7
7
|
"description": "Read-only diff viewer and file renderer for agents.",
|
|
@@ -75,6 +75,10 @@
|
|
|
75
75
|
"label": "Default Output Mode",
|
|
76
76
|
"help": "Tool default when mode is omitted. Use view for canvas/gateway viewer, file for PNG/PDF, or both."
|
|
77
77
|
},
|
|
78
|
+
"defaults.ttlSeconds": {
|
|
79
|
+
"label": "Default Artifact TTL",
|
|
80
|
+
"help": "Default lifetime in seconds for diff viewer and file artifacts. Maximum: 21600."
|
|
81
|
+
},
|
|
78
82
|
"security.allowRemoteViewer": {
|
|
79
83
|
"label": "Allow Remote Viewer",
|
|
80
84
|
"help": "Allow non-loopback access to diff viewer URLs when the token path is known."
|
|
@@ -190,6 +194,12 @@
|
|
|
190
194
|
"type": "string",
|
|
191
195
|
"enum": ["view", "image", "file", "both"],
|
|
192
196
|
"default": "both"
|
|
197
|
+
},
|
|
198
|
+
"ttlSeconds": {
|
|
199
|
+
"type": "number",
|
|
200
|
+
"minimum": 1,
|
|
201
|
+
"maximum": 21600,
|
|
202
|
+
"default": 1800
|
|
193
203
|
}
|
|
194
204
|
}
|
|
195
205
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/diffs",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.10-beta.1",
|
|
4
4
|
"description": "OpenClaw diff viewer plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"build:viewer": "bun build src/viewer-client.ts --target browser --format esm --minify --outfile assets/viewer-runtime.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@pierre/diffs": "1.1.
|
|
14
|
+
"@pierre/diffs": "1.1.21",
|
|
15
15
|
"@pierre/theme": "0.0.29",
|
|
16
16
|
"playwright-core": "1.59.1",
|
|
17
|
-
"typebox": "1.1.
|
|
17
|
+
"typebox": "1.1.38"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@openclaw/plugin-sdk": "workspace:*"
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"minHostVersion": ">=2026.4.30"
|
|
31
31
|
},
|
|
32
32
|
"compat": {
|
|
33
|
-
"pluginApi": ">=2026.5.
|
|
33
|
+
"pluginApi": ">=2026.5.10-beta.1"
|
|
34
34
|
},
|
|
35
35
|
"build": {
|
|
36
|
-
"openclawVersion": "2026.5.
|
|
36
|
+
"openclawVersion": "2026.5.10-beta.1",
|
|
37
37
|
"staticAssets": [
|
|
38
38
|
{
|
|
39
39
|
"source": "./assets/viewer-runtime.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"skills/**"
|
|
57
57
|
],
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"openclaw": ">=2026.5.
|
|
59
|
+
"openclaw": ">=2026.5.10-beta.1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
62
62
|
"openclaw": {
|