@gakr-gakr/diffs 0.1.0 → 0.1.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/dist/api.js +3 -0
- package/dist/index.js +2104 -0
- package/dist/runtime-api.js +2 -0
- package/package.json +18 -1
- package/api.ts +0 -10
- package/index.ts +0 -11
- package/runtime-api.ts +0 -1
- package/src/browser.ts +0 -564
- package/src/config.ts +0 -443
- package/src/http.ts +0 -324
- package/src/language-hints.ts +0 -117
- package/src/pierre-themes.ts +0 -59
- package/src/plugin.ts +0 -73
- package/src/prompt-guidance.ts +0 -7
- package/src/render.ts +0 -557
- package/src/store.ts +0 -387
- package/src/tool.ts +0 -547
- package/src/types.ts +0 -127
- package/src/url.ts +0 -60
- package/src/viewer-assets.ts +0 -103
- package/src/viewer-client.ts +0 -353
- package/src/viewer-payload.ts +0 -94
- package/tsconfig.json +0 -16
- /package/{assets → dist/assets}/viewer-runtime.js +0 -0
package/src/tool.ts
DELETED
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import { stringEnum } from "autobot/plugin-sdk/channel-actions";
|
|
3
|
-
import { formatErrorMessage } from "autobot/plugin-sdk/error-runtime";
|
|
4
|
-
import { normalizeOptionalString } from "autobot/plugin-sdk/string-coerce-runtime";
|
|
5
|
-
import { Static, Type } from "typebox";
|
|
6
|
-
import type { AnyAgentTool, AutoBotPluginApi, AutoBotPluginToolContext } from "../api.js";
|
|
7
|
-
import { PlaywrightDiffScreenshotter, type DiffScreenshotter } from "./browser.js";
|
|
8
|
-
import { resolveDiffImageRenderOptions } from "./config.js";
|
|
9
|
-
import { renderDiffDocument } from "./render.js";
|
|
10
|
-
import type { DiffArtifactStore } from "./store.js";
|
|
11
|
-
import type {
|
|
12
|
-
DiffArtifactContext,
|
|
13
|
-
DiffRenderOptions,
|
|
14
|
-
DiffRenderTarget,
|
|
15
|
-
DiffToolDefaults,
|
|
16
|
-
} from "./types.js";
|
|
17
|
-
import {
|
|
18
|
-
DIFF_IMAGE_QUALITY_PRESETS,
|
|
19
|
-
DIFF_LAYOUTS,
|
|
20
|
-
DIFF_MODES,
|
|
21
|
-
DIFF_OUTPUT_FORMATS,
|
|
22
|
-
DIFF_THEMES,
|
|
23
|
-
type DiffInput,
|
|
24
|
-
type DiffImageQualityPreset,
|
|
25
|
-
type DiffLayout,
|
|
26
|
-
type DiffMode,
|
|
27
|
-
type DiffOutputFormat,
|
|
28
|
-
type DiffTheme,
|
|
29
|
-
} from "./types.js";
|
|
30
|
-
import { buildViewerUrl, normalizeViewerBaseUrl } from "./url.js";
|
|
31
|
-
|
|
32
|
-
const MAX_BEFORE_AFTER_BYTES = 512 * 1024;
|
|
33
|
-
const MAX_PATCH_BYTES = 2 * 1024 * 1024;
|
|
34
|
-
const MAX_TITLE_BYTES = 1_024;
|
|
35
|
-
const MAX_PATH_BYTES = 2_048;
|
|
36
|
-
const MAX_LANG_BYTES = 128;
|
|
37
|
-
|
|
38
|
-
const DiffsToolSchema = Type.Object(
|
|
39
|
-
{
|
|
40
|
-
before: Type.Optional(Type.String({ description: "Original text content." })),
|
|
41
|
-
after: Type.Optional(Type.String({ description: "Updated text content." })),
|
|
42
|
-
patch: Type.Optional(
|
|
43
|
-
Type.String({
|
|
44
|
-
description: "Unified diff or patch text.",
|
|
45
|
-
maxLength: MAX_PATCH_BYTES,
|
|
46
|
-
}),
|
|
47
|
-
),
|
|
48
|
-
path: Type.Optional(
|
|
49
|
-
Type.String({
|
|
50
|
-
description: "Display path for before/after input.",
|
|
51
|
-
maxLength: MAX_PATH_BYTES,
|
|
52
|
-
}),
|
|
53
|
-
),
|
|
54
|
-
lang: Type.Optional(
|
|
55
|
-
Type.String({
|
|
56
|
-
description: "Optional language override for before/after input.",
|
|
57
|
-
maxLength: MAX_LANG_BYTES,
|
|
58
|
-
}),
|
|
59
|
-
),
|
|
60
|
-
title: Type.Optional(
|
|
61
|
-
Type.String({
|
|
62
|
-
description: "Optional title for the rendered diff.",
|
|
63
|
-
maxLength: MAX_TITLE_BYTES,
|
|
64
|
-
}),
|
|
65
|
-
),
|
|
66
|
-
mode: Type.Optional(
|
|
67
|
-
stringEnum(DIFF_MODES, {
|
|
68
|
-
description:
|
|
69
|
-
"Output mode: view, file, image (deprecated alias for file), or both. Default: both.",
|
|
70
|
-
}),
|
|
71
|
-
),
|
|
72
|
-
theme: Type.Optional(stringEnum(DIFF_THEMES, { description: "Viewer theme. Default: dark." })),
|
|
73
|
-
layout: Type.Optional(
|
|
74
|
-
stringEnum(DIFF_LAYOUTS, { description: "Diff layout. Default: unified." }),
|
|
75
|
-
),
|
|
76
|
-
fileQuality: Type.Optional(
|
|
77
|
-
stringEnum(DIFF_IMAGE_QUALITY_PRESETS, {
|
|
78
|
-
description: "File quality preset: standard, hq, or print.",
|
|
79
|
-
}),
|
|
80
|
-
),
|
|
81
|
-
fileFormat: Type.Optional(
|
|
82
|
-
stringEnum(DIFF_OUTPUT_FORMATS, { description: "Rendered file format: png or pdf." }),
|
|
83
|
-
),
|
|
84
|
-
fileScale: Type.Optional(
|
|
85
|
-
Type.Number({
|
|
86
|
-
description: "Optional rendered-file device scale factor override (1-4).",
|
|
87
|
-
minimum: 1,
|
|
88
|
-
maximum: 4,
|
|
89
|
-
}),
|
|
90
|
-
),
|
|
91
|
-
fileMaxWidth: Type.Optional(
|
|
92
|
-
Type.Number({
|
|
93
|
-
description: "Optional rendered-file max width in CSS pixels (640-2400).",
|
|
94
|
-
minimum: 640,
|
|
95
|
-
maximum: 2400,
|
|
96
|
-
}),
|
|
97
|
-
),
|
|
98
|
-
/** @deprecated Use fileQuality. */
|
|
99
|
-
imageQuality: Type.Optional(
|
|
100
|
-
stringEnum(DIFF_IMAGE_QUALITY_PRESETS, {
|
|
101
|
-
description: "Deprecated alias for fileQuality.",
|
|
102
|
-
deprecated: true,
|
|
103
|
-
}),
|
|
104
|
-
),
|
|
105
|
-
/** @deprecated Use fileFormat. */
|
|
106
|
-
imageFormat: Type.Optional(
|
|
107
|
-
stringEnum(DIFF_OUTPUT_FORMATS, {
|
|
108
|
-
description: "Deprecated alias for fileFormat.",
|
|
109
|
-
deprecated: true,
|
|
110
|
-
}),
|
|
111
|
-
),
|
|
112
|
-
/** @deprecated Use fileScale. */
|
|
113
|
-
imageScale: Type.Optional(
|
|
114
|
-
Type.Number({
|
|
115
|
-
description: "Deprecated alias for fileScale.",
|
|
116
|
-
deprecated: true,
|
|
117
|
-
minimum: 1,
|
|
118
|
-
maximum: 4,
|
|
119
|
-
}),
|
|
120
|
-
),
|
|
121
|
-
/** @deprecated Use fileMaxWidth. */
|
|
122
|
-
imageMaxWidth: Type.Optional(
|
|
123
|
-
Type.Number({
|
|
124
|
-
description: "Deprecated alias for fileMaxWidth.",
|
|
125
|
-
deprecated: true,
|
|
126
|
-
minimum: 640,
|
|
127
|
-
maximum: 2400,
|
|
128
|
-
}),
|
|
129
|
-
),
|
|
130
|
-
expandUnchanged: Type.Optional(
|
|
131
|
-
Type.Boolean({ description: "Expand unchanged sections instead of collapsing them." }),
|
|
132
|
-
),
|
|
133
|
-
ttlSeconds: Type.Optional(
|
|
134
|
-
Type.Number({
|
|
135
|
-
description: "Artifact lifetime in seconds. Default: 1800. Maximum: 21600.",
|
|
136
|
-
minimum: 1,
|
|
137
|
-
maximum: 21_600,
|
|
138
|
-
}),
|
|
139
|
-
),
|
|
140
|
-
baseUrl: Type.Optional(
|
|
141
|
-
Type.String({
|
|
142
|
-
description:
|
|
143
|
-
"Optional gateway base URL override used when building the viewer URL. Overrides configured viewerBaseUrl, for example https://gateway.example.com.",
|
|
144
|
-
}),
|
|
145
|
-
),
|
|
146
|
-
},
|
|
147
|
-
{ additionalProperties: false },
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
type DiffsToolParams = Static<typeof DiffsToolSchema>;
|
|
151
|
-
type DiffsToolRawParams = DiffsToolParams & {
|
|
152
|
-
/** @deprecated Use fileFormat. */
|
|
153
|
-
format?: DiffOutputFormat;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
export function createDiffsTool(params: {
|
|
157
|
-
api: AutoBotPluginApi;
|
|
158
|
-
store: DiffArtifactStore;
|
|
159
|
-
defaults: DiffToolDefaults;
|
|
160
|
-
viewerBaseUrl?: string;
|
|
161
|
-
screenshotter?: DiffScreenshotter;
|
|
162
|
-
context?: AutoBotPluginToolContext;
|
|
163
|
-
}): AnyAgentTool {
|
|
164
|
-
return {
|
|
165
|
-
name: "diffs",
|
|
166
|
-
label: "Diffs",
|
|
167
|
-
description:
|
|
168
|
-
"Create a read-only diff viewer from before/after text or a unified patch. Returns a gateway viewer URL for canvas use and can also render the same diff to a PNG or PDF.",
|
|
169
|
-
parameters: DiffsToolSchema,
|
|
170
|
-
execute: async (_toolCallId, rawParams) => {
|
|
171
|
-
const toolParams = rawParams as DiffsToolRawParams;
|
|
172
|
-
const artifactContext = buildArtifactContext(params.context);
|
|
173
|
-
const input = normalizeDiffInput(toolParams);
|
|
174
|
-
const mode = normalizeMode(toolParams.mode, params.defaults.mode);
|
|
175
|
-
const theme = normalizeTheme(toolParams.theme, params.defaults.theme);
|
|
176
|
-
const layout = normalizeLayout(toolParams.layout, params.defaults.layout);
|
|
177
|
-
const expandUnchanged = toolParams.expandUnchanged === true;
|
|
178
|
-
const ttlMs = normalizeTtlMs(toolParams.ttlSeconds ?? params.defaults.ttlSeconds);
|
|
179
|
-
const image = resolveDiffImageRenderOptions({
|
|
180
|
-
defaults: params.defaults,
|
|
181
|
-
fileFormat: normalizeOutputFormat(
|
|
182
|
-
toolParams.fileFormat ?? toolParams.imageFormat ?? toolParams.format,
|
|
183
|
-
),
|
|
184
|
-
fileQuality: normalizeFileQuality(toolParams.fileQuality ?? toolParams.imageQuality),
|
|
185
|
-
fileScale: toolParams.fileScale ?? toolParams.imageScale,
|
|
186
|
-
fileMaxWidth: toolParams.fileMaxWidth ?? toolParams.imageMaxWidth,
|
|
187
|
-
});
|
|
188
|
-
const renderTarget = resolveRenderTarget(mode);
|
|
189
|
-
|
|
190
|
-
const rendered = await renderDiffDocument(
|
|
191
|
-
input,
|
|
192
|
-
{
|
|
193
|
-
presentation: {
|
|
194
|
-
...params.defaults,
|
|
195
|
-
layout,
|
|
196
|
-
theme,
|
|
197
|
-
},
|
|
198
|
-
image,
|
|
199
|
-
expandUnchanged,
|
|
200
|
-
},
|
|
201
|
-
renderTarget,
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
const screenshotter =
|
|
205
|
-
params.screenshotter ?? new PlaywrightDiffScreenshotter({ config: params.api.config });
|
|
206
|
-
|
|
207
|
-
if (isArtifactOnlyMode(mode)) {
|
|
208
|
-
const artifactFile = await renderDiffArtifactFile({
|
|
209
|
-
screenshotter,
|
|
210
|
-
store: params.store,
|
|
211
|
-
html: requireRenderedHtml(rendered.imageHtml, "image"),
|
|
212
|
-
theme,
|
|
213
|
-
image,
|
|
214
|
-
ttlMs,
|
|
215
|
-
context: artifactContext,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
content: [
|
|
220
|
-
{
|
|
221
|
-
type: "text",
|
|
222
|
-
text: buildFileArtifactMessage({
|
|
223
|
-
format: image.format,
|
|
224
|
-
filePath: artifactFile.path,
|
|
225
|
-
}),
|
|
226
|
-
},
|
|
227
|
-
],
|
|
228
|
-
details: buildArtifactDetails({
|
|
229
|
-
baseDetails: {
|
|
230
|
-
...(artifactFile.artifactId ? { artifactId: artifactFile.artifactId } : {}),
|
|
231
|
-
...(artifactFile.expiresAt ? { expiresAt: artifactFile.expiresAt } : {}),
|
|
232
|
-
title: rendered.title,
|
|
233
|
-
inputKind: rendered.inputKind,
|
|
234
|
-
fileCount: rendered.fileCount,
|
|
235
|
-
mode,
|
|
236
|
-
...(artifactContext ? { context: artifactContext } : {}),
|
|
237
|
-
},
|
|
238
|
-
artifactFile,
|
|
239
|
-
image,
|
|
240
|
-
}),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const artifact = await params.store.createArtifact({
|
|
245
|
-
html: requireRenderedHtml(rendered.html, "viewer"),
|
|
246
|
-
title: rendered.title,
|
|
247
|
-
inputKind: rendered.inputKind,
|
|
248
|
-
fileCount: rendered.fileCount,
|
|
249
|
-
ttlMs,
|
|
250
|
-
context: artifactContext,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const viewerUrl = buildViewerUrl({
|
|
254
|
-
config: params.api.config,
|
|
255
|
-
viewerPath: artifact.viewerPath,
|
|
256
|
-
baseUrl: normalizeBaseUrl(toolParams.baseUrl) ?? params.viewerBaseUrl,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const baseDetails = {
|
|
260
|
-
artifactId: artifact.id,
|
|
261
|
-
viewerUrl,
|
|
262
|
-
viewerPath: artifact.viewerPath,
|
|
263
|
-
title: artifact.title,
|
|
264
|
-
expiresAt: artifact.expiresAt,
|
|
265
|
-
inputKind: artifact.inputKind,
|
|
266
|
-
fileCount: artifact.fileCount,
|
|
267
|
-
mode,
|
|
268
|
-
...(artifactContext ? { context: artifactContext } : {}),
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
if (mode === "view") {
|
|
272
|
-
return {
|
|
273
|
-
content: [
|
|
274
|
-
{
|
|
275
|
-
type: "text",
|
|
276
|
-
text: `Diff viewer ready.\n${viewerUrl}`,
|
|
277
|
-
},
|
|
278
|
-
],
|
|
279
|
-
details: baseDetails,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
const artifactFile = await renderDiffArtifactFile({
|
|
285
|
-
screenshotter,
|
|
286
|
-
store: params.store,
|
|
287
|
-
artifactId: artifact.id,
|
|
288
|
-
html: requireRenderedHtml(rendered.imageHtml, "image"),
|
|
289
|
-
theme,
|
|
290
|
-
image,
|
|
291
|
-
});
|
|
292
|
-
await params.store.updateFilePath(artifact.id, artifactFile.path);
|
|
293
|
-
|
|
294
|
-
return {
|
|
295
|
-
content: [
|
|
296
|
-
{
|
|
297
|
-
type: "text",
|
|
298
|
-
text: buildFileArtifactMessage({
|
|
299
|
-
format: image.format,
|
|
300
|
-
filePath: artifactFile.path,
|
|
301
|
-
viewerUrl,
|
|
302
|
-
}),
|
|
303
|
-
},
|
|
304
|
-
],
|
|
305
|
-
details: buildArtifactDetails({
|
|
306
|
-
baseDetails,
|
|
307
|
-
artifactFile,
|
|
308
|
-
image,
|
|
309
|
-
}),
|
|
310
|
-
};
|
|
311
|
-
} catch (error) {
|
|
312
|
-
if (mode === "both") {
|
|
313
|
-
const errorMessage = formatErrorMessage(error);
|
|
314
|
-
return {
|
|
315
|
-
content: [
|
|
316
|
-
{
|
|
317
|
-
type: "text",
|
|
318
|
-
text: `Diff viewer ready.\n${viewerUrl}\nFile rendering failed: ${errorMessage}`,
|
|
319
|
-
},
|
|
320
|
-
],
|
|
321
|
-
details: {
|
|
322
|
-
...baseDetails,
|
|
323
|
-
fileError: errorMessage,
|
|
324
|
-
imageError: errorMessage,
|
|
325
|
-
},
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
throw error;
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function normalizeFileQuality(
|
|
335
|
-
fileQuality: DiffImageQualityPreset | undefined,
|
|
336
|
-
): DiffImageQualityPreset | undefined {
|
|
337
|
-
return fileQuality && DIFF_IMAGE_QUALITY_PRESETS.includes(fileQuality) ? fileQuality : undefined;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function normalizeOutputFormat(format: DiffOutputFormat | undefined): DiffOutputFormat | undefined {
|
|
341
|
-
return format && DIFF_OUTPUT_FORMATS.includes(format) ? format : undefined;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function isArtifactOnlyMode(mode: DiffMode): mode is "image" | "file" {
|
|
345
|
-
return mode === "image" || mode === "file";
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function resolveRenderTarget(mode: DiffMode): DiffRenderTarget {
|
|
349
|
-
if (mode === "view") {
|
|
350
|
-
return "viewer";
|
|
351
|
-
}
|
|
352
|
-
if (isArtifactOnlyMode(mode)) {
|
|
353
|
-
return "image";
|
|
354
|
-
}
|
|
355
|
-
return "both";
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function requireRenderedHtml(html: string | undefined, target: DiffRenderTarget): string {
|
|
359
|
-
if (html !== undefined) {
|
|
360
|
-
return html;
|
|
361
|
-
}
|
|
362
|
-
throw new Error(`Missing ${target} render output.`);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function buildArtifactDetails(params: {
|
|
366
|
-
baseDetails: Record<string, unknown>;
|
|
367
|
-
artifactFile: { path: string; bytes: number };
|
|
368
|
-
image: DiffRenderOptions["image"];
|
|
369
|
-
}) {
|
|
370
|
-
return {
|
|
371
|
-
...params.baseDetails,
|
|
372
|
-
filePath: params.artifactFile.path,
|
|
373
|
-
imagePath: params.artifactFile.path,
|
|
374
|
-
path: params.artifactFile.path,
|
|
375
|
-
fileBytes: params.artifactFile.bytes,
|
|
376
|
-
imageBytes: params.artifactFile.bytes,
|
|
377
|
-
format: params.image.format,
|
|
378
|
-
fileFormat: params.image.format,
|
|
379
|
-
fileQuality: params.image.qualityPreset,
|
|
380
|
-
imageQuality: params.image.qualityPreset,
|
|
381
|
-
fileScale: params.image.scale,
|
|
382
|
-
imageScale: params.image.scale,
|
|
383
|
-
fileMaxWidth: params.image.maxWidth,
|
|
384
|
-
imageMaxWidth: params.image.maxWidth,
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function buildFileArtifactMessage(params: {
|
|
389
|
-
format: DiffOutputFormat;
|
|
390
|
-
filePath: string;
|
|
391
|
-
viewerUrl?: string;
|
|
392
|
-
}): string {
|
|
393
|
-
const lines = params.viewerUrl ? [`Diff viewer: ${params.viewerUrl}`] : [];
|
|
394
|
-
lines.push(`Diff ${params.format.toUpperCase()} generated at: ${params.filePath}`);
|
|
395
|
-
lines.push("Use the `message` tool with `path` or `filePath` to send this file.");
|
|
396
|
-
return lines.join("\n");
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function renderDiffArtifactFile(params: {
|
|
400
|
-
screenshotter: DiffScreenshotter;
|
|
401
|
-
store: DiffArtifactStore;
|
|
402
|
-
artifactId?: string;
|
|
403
|
-
html: string;
|
|
404
|
-
theme: DiffTheme;
|
|
405
|
-
image: DiffRenderOptions["image"];
|
|
406
|
-
ttlMs?: number;
|
|
407
|
-
context?: DiffArtifactContext;
|
|
408
|
-
}): Promise<{ path: string; bytes: number; artifactId?: string; expiresAt?: string }> {
|
|
409
|
-
const standaloneArtifact = params.artifactId
|
|
410
|
-
? undefined
|
|
411
|
-
: await params.store.createStandaloneFileArtifact({
|
|
412
|
-
format: params.image.format,
|
|
413
|
-
ttlMs: params.ttlMs,
|
|
414
|
-
context: params.context,
|
|
415
|
-
});
|
|
416
|
-
const outputPath = params.artifactId
|
|
417
|
-
? params.store.allocateFilePath(params.artifactId, params.image.format)
|
|
418
|
-
: standaloneArtifact!.filePath;
|
|
419
|
-
|
|
420
|
-
await params.screenshotter.screenshotHtml({
|
|
421
|
-
html: params.html,
|
|
422
|
-
outputPath,
|
|
423
|
-
theme: params.theme,
|
|
424
|
-
image: params.image,
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
const stats = await fs.stat(outputPath);
|
|
428
|
-
return {
|
|
429
|
-
path: outputPath,
|
|
430
|
-
bytes: stats.size,
|
|
431
|
-
...(standaloneArtifact?.id ? { artifactId: standaloneArtifact.id } : {}),
|
|
432
|
-
...(standaloneArtifact?.expiresAt ? { expiresAt: standaloneArtifact.expiresAt } : {}),
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function buildArtifactContext(
|
|
437
|
-
context: AutoBotPluginToolContext | undefined,
|
|
438
|
-
): DiffArtifactContext | undefined {
|
|
439
|
-
if (!context) {
|
|
440
|
-
return undefined;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const artifactContext = {
|
|
444
|
-
agentId: normalizeOptionalString(context.agentId),
|
|
445
|
-
sessionId: normalizeOptionalString(context.sessionId),
|
|
446
|
-
messageChannel: normalizeOptionalString(context.messageChannel),
|
|
447
|
-
agentAccountId: normalizeOptionalString(context.agentAccountId),
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
return Object.values(artifactContext).some((value) => value !== undefined)
|
|
451
|
-
? artifactContext
|
|
452
|
-
: undefined;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
function normalizeDiffInput(params: DiffsToolParams): DiffInput {
|
|
456
|
-
const patch = params.patch?.trim();
|
|
457
|
-
const before = params.before;
|
|
458
|
-
const after = params.after;
|
|
459
|
-
|
|
460
|
-
if (patch) {
|
|
461
|
-
assertMaxBytes(patch, "patch", MAX_PATCH_BYTES);
|
|
462
|
-
if (before !== undefined || after !== undefined) {
|
|
463
|
-
throw new PluginToolInputError("Provide either patch or before/after input, not both.");
|
|
464
|
-
}
|
|
465
|
-
const title = params.title?.trim();
|
|
466
|
-
if (title) {
|
|
467
|
-
assertMaxBytes(title, "title", MAX_TITLE_BYTES);
|
|
468
|
-
}
|
|
469
|
-
return {
|
|
470
|
-
kind: "patch",
|
|
471
|
-
patch,
|
|
472
|
-
title,
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (before === undefined || after === undefined) {
|
|
477
|
-
throw new PluginToolInputError("Provide patch or both before and after text.");
|
|
478
|
-
}
|
|
479
|
-
assertMaxBytes(before, "before", MAX_BEFORE_AFTER_BYTES);
|
|
480
|
-
assertMaxBytes(after, "after", MAX_BEFORE_AFTER_BYTES);
|
|
481
|
-
const path = normalizeOptionalString(params.path);
|
|
482
|
-
const lang = normalizeOptionalString(params.lang);
|
|
483
|
-
const title = normalizeOptionalString(params.title);
|
|
484
|
-
if (path) {
|
|
485
|
-
assertMaxBytes(path, "path", MAX_PATH_BYTES);
|
|
486
|
-
}
|
|
487
|
-
if (lang) {
|
|
488
|
-
assertMaxBytes(lang, "lang", MAX_LANG_BYTES);
|
|
489
|
-
}
|
|
490
|
-
if (title) {
|
|
491
|
-
assertMaxBytes(title, "title", MAX_TITLE_BYTES);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return {
|
|
495
|
-
kind: "before_after",
|
|
496
|
-
before,
|
|
497
|
-
after,
|
|
498
|
-
path,
|
|
499
|
-
lang,
|
|
500
|
-
title,
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function assertMaxBytes(value: string, label: string, maxBytes: number): void {
|
|
505
|
-
if (Buffer.byteLength(value, "utf8") <= maxBytes) {
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
throw new PluginToolInputError(`${label} exceeds maximum size (${maxBytes} bytes).`);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
function normalizeBaseUrl(baseUrl?: string): string | undefined {
|
|
512
|
-
const normalized = baseUrl?.trim();
|
|
513
|
-
if (!normalized) {
|
|
514
|
-
return undefined;
|
|
515
|
-
}
|
|
516
|
-
try {
|
|
517
|
-
return normalizeViewerBaseUrl(normalized);
|
|
518
|
-
} catch {
|
|
519
|
-
throw new PluginToolInputError(`Invalid baseUrl: ${normalized}`);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function normalizeMode(mode: DiffMode | undefined, fallback: DiffMode): DiffMode {
|
|
524
|
-
return mode && DIFF_MODES.includes(mode) ? mode : fallback;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function normalizeTheme(theme: DiffTheme | undefined, fallback: DiffTheme): DiffTheme {
|
|
528
|
-
return theme && DIFF_THEMES.includes(theme) ? theme : fallback;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function normalizeLayout(layout: DiffLayout | undefined, fallback: DiffLayout): DiffLayout {
|
|
532
|
-
return layout && DIFF_LAYOUTS.includes(layout) ? layout : fallback;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function normalizeTtlMs(ttlSeconds?: number): number | undefined {
|
|
536
|
-
if (!Number.isFinite(ttlSeconds) || ttlSeconds === undefined) {
|
|
537
|
-
return undefined;
|
|
538
|
-
}
|
|
539
|
-
return Math.floor(ttlSeconds * 1000);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
class PluginToolInputError extends Error {
|
|
543
|
-
constructor(message: string) {
|
|
544
|
-
super(message);
|
|
545
|
-
this.name = "ToolInputError";
|
|
546
|
-
}
|
|
547
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import type { FileContents, FileDiffMetadata, SupportedLanguages } from "@pierre/diffs";
|
|
2
|
-
|
|
3
|
-
export const DIFF_LAYOUTS = ["unified", "split"] as const;
|
|
4
|
-
export const DIFF_MODES = ["view", "image", "file", "both"] as const;
|
|
5
|
-
export const DIFF_THEMES = ["light", "dark"] as const;
|
|
6
|
-
export const DIFF_INDICATORS = ["bars", "classic", "none"] as const;
|
|
7
|
-
export const DIFF_IMAGE_QUALITY_PRESETS = ["standard", "hq", "print"] as const;
|
|
8
|
-
export const DIFF_OUTPUT_FORMATS = ["png", "pdf"] as const;
|
|
9
|
-
|
|
10
|
-
export type DiffLayout = (typeof DIFF_LAYOUTS)[number];
|
|
11
|
-
export type DiffMode = (typeof DIFF_MODES)[number];
|
|
12
|
-
export type DiffTheme = (typeof DIFF_THEMES)[number];
|
|
13
|
-
export type DiffIndicators = (typeof DIFF_INDICATORS)[number];
|
|
14
|
-
export type DiffImageQualityPreset = (typeof DIFF_IMAGE_QUALITY_PRESETS)[number];
|
|
15
|
-
export type DiffOutputFormat = (typeof DIFF_OUTPUT_FORMATS)[number];
|
|
16
|
-
export type DiffRenderTarget = "viewer" | "image" | "both";
|
|
17
|
-
|
|
18
|
-
type DiffPresentationDefaults = {
|
|
19
|
-
fontFamily: string;
|
|
20
|
-
fontSize: number;
|
|
21
|
-
lineSpacing: number;
|
|
22
|
-
layout: DiffLayout;
|
|
23
|
-
showLineNumbers: boolean;
|
|
24
|
-
diffIndicators: DiffIndicators;
|
|
25
|
-
wordWrap: boolean;
|
|
26
|
-
background: boolean;
|
|
27
|
-
theme: DiffTheme;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type DiffFileDefaults = {
|
|
31
|
-
fileFormat: DiffOutputFormat;
|
|
32
|
-
fileQuality: DiffImageQualityPreset;
|
|
33
|
-
fileScale: number;
|
|
34
|
-
fileMaxWidth: number;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type DiffToolDefaults = DiffPresentationDefaults &
|
|
38
|
-
DiffFileDefaults & {
|
|
39
|
-
mode: DiffMode;
|
|
40
|
-
ttlSeconds: number;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
type BeforeAfterDiffInput = {
|
|
44
|
-
kind: "before_after";
|
|
45
|
-
before: string;
|
|
46
|
-
after: string;
|
|
47
|
-
path?: string;
|
|
48
|
-
lang?: string;
|
|
49
|
-
title?: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type PatchDiffInput = {
|
|
53
|
-
kind: "patch";
|
|
54
|
-
patch: string;
|
|
55
|
-
title?: string;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type DiffInput = BeforeAfterDiffInput | PatchDiffInput;
|
|
59
|
-
|
|
60
|
-
export type DiffRenderOptions = {
|
|
61
|
-
presentation: DiffPresentationDefaults;
|
|
62
|
-
image: {
|
|
63
|
-
format: DiffOutputFormat;
|
|
64
|
-
qualityPreset: DiffImageQualityPreset;
|
|
65
|
-
scale: number;
|
|
66
|
-
maxWidth: number;
|
|
67
|
-
maxPixels: number;
|
|
68
|
-
};
|
|
69
|
-
expandUnchanged: boolean;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type DiffViewerOptions = {
|
|
73
|
-
theme: {
|
|
74
|
-
light: "pierre-light";
|
|
75
|
-
dark: "pierre-dark";
|
|
76
|
-
};
|
|
77
|
-
diffStyle: DiffLayout;
|
|
78
|
-
diffIndicators: DiffIndicators;
|
|
79
|
-
disableLineNumbers: boolean;
|
|
80
|
-
expandUnchanged: boolean;
|
|
81
|
-
themeType: DiffTheme;
|
|
82
|
-
backgroundEnabled: boolean;
|
|
83
|
-
overflow: "scroll" | "wrap";
|
|
84
|
-
unsafeCSS: string;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export type DiffViewerPayload = {
|
|
88
|
-
prerenderedHTML: string;
|
|
89
|
-
options: DiffViewerOptions;
|
|
90
|
-
langs: SupportedLanguages[];
|
|
91
|
-
oldFile?: FileContents;
|
|
92
|
-
newFile?: FileContents;
|
|
93
|
-
fileDiff?: FileDiffMetadata;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export type RenderedDiffDocument = {
|
|
97
|
-
html?: string;
|
|
98
|
-
imageHtml?: string;
|
|
99
|
-
title: string;
|
|
100
|
-
fileCount: number;
|
|
101
|
-
inputKind: DiffInput["kind"];
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export type DiffArtifactContext = {
|
|
105
|
-
agentId?: string;
|
|
106
|
-
sessionId?: string;
|
|
107
|
-
messageChannel?: string;
|
|
108
|
-
agentAccountId?: string;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
export type DiffArtifactMeta = {
|
|
112
|
-
id: string;
|
|
113
|
-
token: string;
|
|
114
|
-
createdAt: string;
|
|
115
|
-
expiresAt: string;
|
|
116
|
-
title: string;
|
|
117
|
-
inputKind: DiffInput["kind"];
|
|
118
|
-
fileCount: number;
|
|
119
|
-
viewerPath: string;
|
|
120
|
-
htmlPath: string;
|
|
121
|
-
context?: DiffArtifactContext;
|
|
122
|
-
filePath?: string;
|
|
123
|
-
imagePath?: string;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
export const DIFF_ARTIFACT_ID_PATTERN = /^[0-9a-f]{20}$/;
|
|
127
|
-
export const DIFF_ARTIFACT_TOKEN_PATTERN = /^[0-9a-f]{48}$/;
|