@anionex/dsh-vision-toolkit 0.1.5
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +383 -0
- package/README.zh.md +383 -0
- package/assets/dsh-conversation-artifact.png +0 -0
- package/assets/dsh-conversation-image-qa-top.png +0 -0
- package/assets/dsh-conversation-image-qa.png +0 -0
- package/assets/dsh-conversation-pixel-diff.png +0 -0
- package/assets/dsh-conversation-screenshot-debugging-top.png +0 -0
- package/assets/dsh-conversation-screenshot-debugging.png +0 -0
- package/assets/dsh-conversation-tool-call.png +0 -0
- package/assets/dsh-conversation-vision-trace.png +0 -0
- package/assets/hero.png +0 -0
- package/assets/social-preview.png +0 -0
- package/assets/upstream/README.md +16 -0
- package/assets/upstream/image-qa.webp +0 -0
- package/assets/upstream/infographic-reference.webp +0 -0
- package/assets/upstream/infographic-result.webp +0 -0
- package/assets/upstream/screenshot-debugging.webp +0 -0
- package/assets/upstream/ui-result.webp +0 -0
- package/assets/upstream/ui-sketch.webp +0 -0
- package/assets/vision-settings.png +0 -0
- package/cordis.patch.yml +6 -0
- package/docs/assets/vision-settings.png +0 -0
- package/docs/requirements-traceability/README.i18n.yaml +6 -0
- package/docs/requirements-traceability/README.md +75 -0
- package/docs/requirements-traceability/README.zh.md +75 -0
- package/examples/ui-restoration/README.i18n.yaml +6 -0
- package/examples/ui-restoration/README.md +70 -0
- package/examples/ui-restoration/README.zh.md +70 -0
- package/examples/ui-restoration/assets/final-heatmap.png +0 -0
- package/examples/ui-restoration/assets/final-report.json +83 -0
- package/examples/ui-restoration/assets/implementation.png +0 -0
- package/examples/ui-restoration/assets/initial-heatmap.png +0 -0
- package/examples/ui-restoration/assets/initial-report.json +83 -0
- package/examples/ui-restoration/assets/initial.png +0 -0
- package/examples/ui-restoration/assets/metrics.json +12 -0
- package/examples/ui-restoration/assets/reference.png +0 -0
- package/examples/ui-restoration/implementation.html +94 -0
- package/examples/ui-restoration/initial.html +57 -0
- package/lib/artifact-access.js +369 -0
- package/lib/artifact-access.js.map +1 -0
- package/lib/artifacts.js +56 -0
- package/lib/artifacts.js.map +1 -0
- package/lib/client.js +952 -0
- package/lib/client.js.map +1 -0
- package/lib/config.js +117 -0
- package/lib/config.js.map +1 -0
- package/lib/errors.js +56 -0
- package/lib/errors.js.map +1 -0
- package/lib/exposure.js +213 -0
- package/lib/exposure.js.map +1 -0
- package/lib/index.js +97 -0
- package/lib/index.js.map +1 -0
- package/lib/paste-images.js +199 -0
- package/lib/paste-images.js.map +1 -0
- package/lib/paths.js +325 -0
- package/lib/paths.js.map +1 -0
- package/lib/runtime-install.js +601 -0
- package/lib/runtime-install.js.map +1 -0
- package/lib/runtime-manager.js +126 -0
- package/lib/runtime-manager.js.map +1 -0
- package/lib/runtime.js +1344 -0
- package/lib/runtime.js.map +1 -0
- package/lib/skill.js +139 -0
- package/lib/skill.js.map +1 -0
- package/lib/tools.js +528 -0
- package/lib/tools.js.map +1 -0
- package/lib/types/artifact-access.d.ts +61 -0
- package/lib/types/artifact-access.d.ts.map +1 -0
- package/lib/types/artifacts.d.ts +42 -0
- package/lib/types/artifacts.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +179 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/paste-images.d.ts +57 -0
- package/lib/types/client/paste-images.d.ts.map +1 -0
- package/lib/types/config.d.ts +73 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/errors.d.ts +35 -0
- package/lib/types/errors.d.ts.map +1 -0
- package/lib/types/exposure.d.ts +40 -0
- package/lib/types/exposure.d.ts.map +1 -0
- package/lib/types/index.d.ts +18 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/paste-images.d.ts +21 -0
- package/lib/types/paste-images.d.ts.map +1 -0
- package/lib/types/paths.d.ts +107 -0
- package/lib/types/paths.d.ts.map +1 -0
- package/lib/types/runtime-install.d.ts +49 -0
- package/lib/types/runtime-install.d.ts.map +1 -0
- package/lib/types/runtime-manager.d.ts +60 -0
- package/lib/types/runtime-manager.d.ts.map +1 -0
- package/lib/types/runtime.d.ts +389 -0
- package/lib/types/runtime.d.ts.map +1 -0
- package/lib/types/skill.d.ts +15 -0
- package/lib/types/skill.d.ts.map +1 -0
- package/lib/types/tools.d.ts +22 -0
- package/lib/types/tools.d.ts.map +1 -0
- package/lib/types/upstream.d.ts +207 -0
- package/lib/types/upstream.d.ts.map +1 -0
- package/lib/types/version.d.ts +15 -0
- package/lib/types/version.d.ts.map +1 -0
- package/lib/types/web-request.d.ts +4 -0
- package/lib/types/web-request.d.ts.map +1 -0
- package/lib/types/web.d.ts +74 -0
- package/lib/types/web.d.ts.map +1 -0
- package/lib/upstream.js +675 -0
- package/lib/upstream.js.map +1 -0
- package/lib/version.js +18 -0
- package/lib/version.js.map +1 -0
- package/lib/web-request.js +20 -0
- package/lib/web-request.js.map +1 -0
- package/lib/web.js +244 -0
- package/lib/web.js.map +1 -0
- package/package.json +139 -0
- package/runtime/requirements.lock +3 -0
- package/src/artifact-access.ts +386 -0
- package/src/artifacts.ts +85 -0
- package/src/client/index.tsx +866 -0
- package/src/client/paste-images.tsx +426 -0
- package/src/config.ts +177 -0
- package/src/errors.ts +62 -0
- package/src/exposure.ts +227 -0
- package/src/index.ts +122 -0
- package/src/paste-images.ts +234 -0
- package/src/paths.ts +348 -0
- package/src/runtime-install.ts +723 -0
- package/src/runtime-manager.ts +166 -0
- package/src/runtime.ts +1783 -0
- package/src/skill.ts +143 -0
- package/src/tools.ts +668 -0
- package/src/upstream.ts +861 -0
- package/src/version.ts +37 -0
- package/src/web-request.ts +17 -0
- package/src/web.ts +329 -0
- package/vendor/agent-vision-toolkit/CHANGELOG.md +16 -0
- package/vendor/agent-vision-toolkit/LICENSE +21 -0
- package/vendor/agent-vision-toolkit/README.md +399 -0
- package/vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json +89 -0
- package/vendor/agent-vision-toolkit/bin/crop +90 -0
- package/vendor/agent-vision-toolkit/bin/detect +13 -0
- package/vendor/agent-vision-toolkit/bin/glance +93 -0
- package/vendor/agent-vision-toolkit/bin/ground +13 -0
- package/vendor/agent-vision-toolkit/bin/trace +129 -0
- package/vendor/agent-vision-toolkit/detect.py +56 -0
- package/vendor/agent-vision-toolkit/ground.py +216 -0
- package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/dominant_colors.py +224 -0
- package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/extract_fg.py +278 -0
- package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/html_shot.py +108 -0
- package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/long_screenshot_ocr.py +1245 -0
- package/vendor/agent-vision-toolkit/skills/vision-tools/scripts/pixel_diff.py +88 -0
- package/vendor/agent-vision-toolkit/vision_client.py +156 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability-gated HTTP delivery for managed Vision Toolkit artifacts.
|
|
3
|
+
* Signed tokens are durable across process restarts, expose no secret, and
|
|
4
|
+
* are accepted only for the exact artifact facts projected into a tool result.
|
|
5
|
+
* @module dsh-vision-toolkit/artifact-access
|
|
6
|
+
*/
|
|
7
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
8
|
+
import { constants as fsConstants } from 'node:fs';
|
|
9
|
+
import { chmod, lstat, mkdir, open, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { basename, dirname, extname, isAbsolute, join, relative, sep } from 'node:path';
|
|
11
|
+
import { isWithin } from "./paths.js";
|
|
12
|
+
import { visionToolkitStateRoot } from "./runtime-install.js";
|
|
13
|
+
/** Prefix owned by the plugin's artifact capability route. */
|
|
14
|
+
export const ARTIFACT_ROUTE_PREFIX = '/_dsh/vision-toolkit/artifacts';
|
|
15
|
+
/** Presentation metadata key reserved by the browser half of this package. */
|
|
16
|
+
export const PRESENTATION_META_KEY = '$dshVisionToolkit';
|
|
17
|
+
const KEY_BYTES = 32;
|
|
18
|
+
const TOKEN_PATTERN = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
19
|
+
const MAX_TOKEN_LENGTH = 16 * 1024;
|
|
20
|
+
const MIME_BY_EXTENSION = new Map([
|
|
21
|
+
['.png', { mimeType: 'image/png', kind: 'image' }],
|
|
22
|
+
['.jpg', { mimeType: 'image/jpeg', kind: 'image' }],
|
|
23
|
+
['.jpeg', { mimeType: 'image/jpeg', kind: 'image' }],
|
|
24
|
+
['.gif', { mimeType: 'image/gif', kind: 'image' }],
|
|
25
|
+
['.webp', { mimeType: 'image/webp', kind: 'image' }],
|
|
26
|
+
['.svg', { mimeType: 'image/svg+xml', kind: 'svg' }],
|
|
27
|
+
['.md', { mimeType: 'text/markdown', kind: 'markdown' }],
|
|
28
|
+
['.json', { mimeType: 'application/json', kind: 'json' }],
|
|
29
|
+
]);
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function isArtifactKind(value) {
|
|
34
|
+
return value === 'image' || value === 'svg' || value === 'markdown' || value === 'json';
|
|
35
|
+
}
|
|
36
|
+
function artifactFrom(value) {
|
|
37
|
+
if (!isRecord(value))
|
|
38
|
+
return undefined;
|
|
39
|
+
if (typeof value.path !== 'string'
|
|
40
|
+
|| typeof value.filename !== 'string'
|
|
41
|
+
|| typeof value.mimeType !== 'string'
|
|
42
|
+
|| !isArtifactKind(value.kind)
|
|
43
|
+
|| typeof value.description !== 'string'
|
|
44
|
+
|| typeof value.sourceTool !== 'string'
|
|
45
|
+
|| (value.previewIntent !== 'image' && value.previewIntent !== 'svg' && value.previewIntent !== 'text' && value.previewIntent !== 'download')
|
|
46
|
+
|| typeof value.bytes !== 'number'
|
|
47
|
+
|| !Number.isSafeInteger(value.bytes)
|
|
48
|
+
|| value.bytes < 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
function collectArtifacts(value, found, depth = 0) {
|
|
53
|
+
if (depth > 16)
|
|
54
|
+
return;
|
|
55
|
+
const artifact = artifactFrom(value);
|
|
56
|
+
if (artifact !== undefined) {
|
|
57
|
+
found.set(artifact.path, artifact);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
for (const entry of value)
|
|
62
|
+
collectArtifacts(entry, found, depth + 1);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!isRecord(value))
|
|
66
|
+
return;
|
|
67
|
+
for (const entry of Object.values(value))
|
|
68
|
+
collectArtifacts(entry, found, depth + 1);
|
|
69
|
+
}
|
|
70
|
+
function parsePayload(value) {
|
|
71
|
+
if (!isRecord(value))
|
|
72
|
+
return undefined;
|
|
73
|
+
if (value.v !== 1
|
|
74
|
+
|| typeof value.path !== 'string'
|
|
75
|
+
|| !isAbsolute(value.path)
|
|
76
|
+
|| typeof value.filename !== 'string'
|
|
77
|
+
|| basename(value.path) !== value.filename
|
|
78
|
+
|| typeof value.mimeType !== 'string'
|
|
79
|
+
|| !isArtifactKind(value.kind)
|
|
80
|
+
|| typeof value.bytes !== 'number'
|
|
81
|
+
|| !Number.isSafeInteger(value.bytes)
|
|
82
|
+
|| value.bytes < 0)
|
|
83
|
+
return undefined;
|
|
84
|
+
const expected = MIME_BY_EXTENSION.get(extname(value.path).toLowerCase());
|
|
85
|
+
if (expected === undefined || expected.mimeType !== value.mimeType || expected.kind !== value.kind)
|
|
86
|
+
return undefined;
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
function mac(key, payload) {
|
|
90
|
+
return createHmac('sha256', key).update(payload).digest();
|
|
91
|
+
}
|
|
92
|
+
function safeEqual(left, right) {
|
|
93
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
94
|
+
}
|
|
95
|
+
async function readKey(path) {
|
|
96
|
+
const info = await lstat(path);
|
|
97
|
+
if (info.isSymbolicLink() || !info.isFile())
|
|
98
|
+
throw new Error('artifact access key is not a regular file');
|
|
99
|
+
const key = await readFile(path);
|
|
100
|
+
if (key.length !== KEY_BYTES)
|
|
101
|
+
throw new Error('artifact access key has an invalid length');
|
|
102
|
+
await chmod(path, 0o600);
|
|
103
|
+
return key;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Load or atomically create the per-DSH-home signing key.
|
|
107
|
+
* @param root - state root override used by tests; defaults to the plugin cache.
|
|
108
|
+
* @returns the 32-byte signing key.
|
|
109
|
+
*/
|
|
110
|
+
export async function prepareArtifactAccessKey(root = visionToolkitStateRoot()) {
|
|
111
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
112
|
+
const path = join(root, 'artifact-access.key');
|
|
113
|
+
try {
|
|
114
|
+
return await readKey(path);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error.code !== 'ENOENT')
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
const candidate = randomBytes(KEY_BYTES);
|
|
121
|
+
try {
|
|
122
|
+
await writeFile(path, candidate, { flag: 'wx', mode: 0o600 });
|
|
123
|
+
return candidate;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (error.code !== 'EEXIST')
|
|
127
|
+
throw error;
|
|
128
|
+
return readKey(path);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function artifactRoot(path) {
|
|
132
|
+
let current = dirname(path);
|
|
133
|
+
while (true) {
|
|
134
|
+
if (basename(current) === 'artifacts' && basename(dirname(current)) === '.dsh-vision-toolkit')
|
|
135
|
+
return current;
|
|
136
|
+
const parent = dirname(current);
|
|
137
|
+
if (parent === current)
|
|
138
|
+
return undefined;
|
|
139
|
+
current = parent;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function assertNoSymlinkPath(root, path) {
|
|
143
|
+
const rootInfo = await lstat(root);
|
|
144
|
+
if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
|
|
145
|
+
throw new Error('artifact root is not a real directory');
|
|
146
|
+
const rel = relative(root, path);
|
|
147
|
+
if (rel === '' || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
148
|
+
throw new Error('artifact path escaped its managed root');
|
|
149
|
+
}
|
|
150
|
+
let current = root;
|
|
151
|
+
const parts = rel.split(sep);
|
|
152
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
153
|
+
const part = parts[index];
|
|
154
|
+
if (part === undefined || part.length === 0 || part === '.' || part === '..')
|
|
155
|
+
throw new Error('artifact path is malformed');
|
|
156
|
+
current = join(current, part);
|
|
157
|
+
const info = await lstat(current);
|
|
158
|
+
if (info.isSymbolicLink())
|
|
159
|
+
throw new Error('artifact path contains a symbolic link');
|
|
160
|
+
const final = index === parts.length - 1;
|
|
161
|
+
if (final ? !info.isFile() : !info.isDirectory())
|
|
162
|
+
throw new Error('artifact path contains an unexpected entry type');
|
|
163
|
+
}
|
|
164
|
+
const [realRoot, realFile] = await Promise.all([realpath(root), realpath(path)]);
|
|
165
|
+
if (!isWithin(realRoot, realFile))
|
|
166
|
+
throw new Error('artifact path escaped its managed root');
|
|
167
|
+
}
|
|
168
|
+
function sameFile(opened, current) {
|
|
169
|
+
if (opened.dev === 0 || current.dev === 0 || opened.ino === 0 || current.ino === 0)
|
|
170
|
+
return true;
|
|
171
|
+
return opened.dev === current.dev && opened.ino === current.ino;
|
|
172
|
+
}
|
|
173
|
+
async function openVerifiedArtifact(payload) {
|
|
174
|
+
const root = artifactRoot(payload.path);
|
|
175
|
+
if (root === undefined)
|
|
176
|
+
throw new Error('artifact path is outside the managed delivery tree');
|
|
177
|
+
await assertNoSymlinkPath(root, payload.path);
|
|
178
|
+
const noFollow = typeof fsConstants.O_NOFOLLOW === 'number' ? fsConstants.O_NOFOLLOW : 0;
|
|
179
|
+
const handle = await open(payload.path, fsConstants.O_RDONLY | noFollow);
|
|
180
|
+
try {
|
|
181
|
+
const info = await handle.stat();
|
|
182
|
+
if (!info.isFile() || info.size !== payload.bytes)
|
|
183
|
+
throw new Error('artifact no longer matches its delivered descriptor');
|
|
184
|
+
const current = await lstat(payload.path);
|
|
185
|
+
if (current.isSymbolicLink() || !current.isFile() || !sameFile(info, current)) {
|
|
186
|
+
throw new Error('artifact changed while it was being opened');
|
|
187
|
+
}
|
|
188
|
+
await assertNoSymlinkPath(root, payload.path);
|
|
189
|
+
return { handle, info };
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
await handle.close().catch(() => { });
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function asciiFilename(filename) {
|
|
197
|
+
const fallback = filename.replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 160);
|
|
198
|
+
return fallback.length === 0 ? 'artifact' : fallback;
|
|
199
|
+
}
|
|
200
|
+
function contentDisposition(filename, download) {
|
|
201
|
+
const mode = download ? 'attachment' : 'inline';
|
|
202
|
+
return `${mode}; filename="${asciiFilename(filename)}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
203
|
+
}
|
|
204
|
+
function securityHeaders(res, payload, download) {
|
|
205
|
+
res.setHeader('Content-Type', payload.mimeType);
|
|
206
|
+
res.setHeader('Content-Length', String(payload.bytes));
|
|
207
|
+
res.setHeader('Content-Disposition', contentDisposition(payload.filename, download));
|
|
208
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
209
|
+
res.setHeader('Cache-Control', 'private, no-store, max-age=0');
|
|
210
|
+
res.setHeader('Referrer-Policy', 'no-referrer');
|
|
211
|
+
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
|
|
212
|
+
res.setHeader('Content-Security-Policy', payload.kind === 'svg'
|
|
213
|
+
? "sandbox; default-src 'none'; img-src data:; style-src 'unsafe-inline'"
|
|
214
|
+
: "sandbox; default-src 'none'");
|
|
215
|
+
}
|
|
216
|
+
/** Signed-capability encoder and safe Artifact route handler. */
|
|
217
|
+
export class ArtifactAccessController {
|
|
218
|
+
key;
|
|
219
|
+
routeCount = 0;
|
|
220
|
+
constructor(key) {
|
|
221
|
+
this.key = key;
|
|
222
|
+
if (key.length !== KEY_BYTES)
|
|
223
|
+
throw new TypeError(`artifact access key must be ${KEY_BYTES} bytes`);
|
|
224
|
+
}
|
|
225
|
+
/** Whether at least one HTTP carrier currently owns the route. */
|
|
226
|
+
get routeAvailable() {
|
|
227
|
+
return this.routeCount > 0;
|
|
228
|
+
}
|
|
229
|
+
/** Mark one route attachment; the returned disposer removes that attachment. */
|
|
230
|
+
attachRoute() {
|
|
231
|
+
this.routeCount += 1;
|
|
232
|
+
let active = true;
|
|
233
|
+
return () => {
|
|
234
|
+
if (!active)
|
|
235
|
+
return;
|
|
236
|
+
active = false;
|
|
237
|
+
this.routeCount -= 1;
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Purely enrich a canonical tool-result value with browser access grants.
|
|
242
|
+
* @param value - schema-validated tool result.
|
|
243
|
+
* @returns the unchanged value when no route/artifact exists, otherwise a detached metadata envelope.
|
|
244
|
+
*/
|
|
245
|
+
presentationMeta(value) {
|
|
246
|
+
if (!this.routeAvailable || !isRecord(value))
|
|
247
|
+
return value;
|
|
248
|
+
const artifacts = new Map();
|
|
249
|
+
collectArtifacts(value, artifacts);
|
|
250
|
+
if (artifacts.size === 0)
|
|
251
|
+
return value;
|
|
252
|
+
const grants = [...artifacts.values()].map((artifact) => {
|
|
253
|
+
const token = this.sign(artifact);
|
|
254
|
+
const previewUrl = `${ARTIFACT_ROUTE_PREFIX}/${token}`;
|
|
255
|
+
return { path: artifact.path, previewUrl, downloadUrl: `${previewUrl}?download=1` };
|
|
256
|
+
});
|
|
257
|
+
const envelope = { schemaVersion: 1, artifacts: grants };
|
|
258
|
+
return { ...value, [PRESENTATION_META_KEY]: envelope };
|
|
259
|
+
}
|
|
260
|
+
/** Mint a deterministic, tamper-evident capability for one descriptor. */
|
|
261
|
+
sign(artifact) {
|
|
262
|
+
const payload = {
|
|
263
|
+
v: 1,
|
|
264
|
+
path: artifact.path,
|
|
265
|
+
filename: artifact.filename,
|
|
266
|
+
mimeType: artifact.mimeType,
|
|
267
|
+
kind: artifact.kind,
|
|
268
|
+
bytes: artifact.bytes,
|
|
269
|
+
};
|
|
270
|
+
const encoded = Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
271
|
+
return `${encoded}.${mac(this.key, encoded).toString('base64url')}`;
|
|
272
|
+
}
|
|
273
|
+
/** Verify and decode one capability without touching the filesystem. */
|
|
274
|
+
verify(token) {
|
|
275
|
+
if (token.length === 0 || token.length > MAX_TOKEN_LENGTH || !TOKEN_PATTERN.test(token))
|
|
276
|
+
return undefined;
|
|
277
|
+
const [encoded, signature] = token.split('.');
|
|
278
|
+
if (encoded === undefined || signature === undefined)
|
|
279
|
+
return undefined;
|
|
280
|
+
let supplied;
|
|
281
|
+
try {
|
|
282
|
+
supplied = Buffer.from(signature, 'base64url');
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
if (!safeEqual(mac(this.key, encoded), supplied))
|
|
288
|
+
return undefined;
|
|
289
|
+
try {
|
|
290
|
+
return parsePayload(JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8')));
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Serve one GET/HEAD capability request with MIME, CSP, and symlink checks.
|
|
298
|
+
* @param req - Node HTTP request matched under {@link ARTIFACT_ROUTE_PREFIX}.
|
|
299
|
+
* @param res - response owned by this handler.
|
|
300
|
+
*/
|
|
301
|
+
async handle(req, res) {
|
|
302
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
303
|
+
res.setHeader('Allow', 'GET, HEAD');
|
|
304
|
+
res.writeHead(405);
|
|
305
|
+
res.end();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
let url;
|
|
309
|
+
try {
|
|
310
|
+
url = new URL(req.url ?? '/', 'http://dsh.local');
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
res.writeHead(400);
|
|
314
|
+
res.end();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const prefix = `${ARTIFACT_ROUTE_PREFIX}/`;
|
|
318
|
+
if (!url.pathname.startsWith(prefix) || url.pathname.slice(prefix.length).includes('/')) {
|
|
319
|
+
res.writeHead(404);
|
|
320
|
+
res.end();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
let token;
|
|
324
|
+
try {
|
|
325
|
+
token = decodeURIComponent(url.pathname.slice(prefix.length));
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
res.writeHead(404);
|
|
329
|
+
res.end();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const payload = this.verify(token);
|
|
333
|
+
if (payload === undefined) {
|
|
334
|
+
res.writeHead(404);
|
|
335
|
+
res.end();
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const downloadValue = url.searchParams.get('download');
|
|
339
|
+
if ([...url.searchParams.keys()].some(key => key !== 'download') || (downloadValue !== null && downloadValue !== '1')) {
|
|
340
|
+
res.writeHead(400);
|
|
341
|
+
res.end();
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
let opened;
|
|
345
|
+
try {
|
|
346
|
+
opened = await openVerifiedArtifact(payload);
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
res.writeHead(404);
|
|
350
|
+
res.end();
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
securityHeaders(res, payload, downloadValue === '1');
|
|
354
|
+
res.writeHead(200);
|
|
355
|
+
if (req.method === 'HEAD') {
|
|
356
|
+
await opened.handle.close().catch(() => { });
|
|
357
|
+
res.end();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const stream = opened.handle.createReadStream({ autoClose: true });
|
|
361
|
+
stream.on('error', () => {
|
|
362
|
+
if (!res.headersSent)
|
|
363
|
+
res.writeHead(500);
|
|
364
|
+
res.destroy();
|
|
365
|
+
});
|
|
366
|
+
stream.pipe(res);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
//# sourceMappingURL=artifact-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-access.js","sourceRoot":"","sources":["../src/artifact-access.ts"],"names":[],"mappings":"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"}
|
package/lib/artifacts.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable file-delivery descriptors owned by the plugin. DSH Core currently
|
|
3
|
+
* has no reusable Artifact service, so every file-producing tool returns this
|
|
4
|
+
* lossless contract and keeps the file inside the plugin-managed artifact
|
|
5
|
+
* directory for Web, Headless, and later tool calls alike.
|
|
6
|
+
* @module dsh-vision-toolkit/artifacts
|
|
7
|
+
*/
|
|
8
|
+
import { lstat, realpath, stat } from 'node:fs/promises';
|
|
9
|
+
import { basename } from 'node:path';
|
|
10
|
+
import { VisionToolkitError } from "./errors.js";
|
|
11
|
+
import { isWithin } from "./paths.js";
|
|
12
|
+
/**
|
|
13
|
+
* Validate and describe one committed regular file under the active artifact
|
|
14
|
+
* root. Symbolic links are rejected even when their targets remain in-bounds,
|
|
15
|
+
* so a later preview/download can never be redirected after delivery.
|
|
16
|
+
* @param path - final managed artifact path.
|
|
17
|
+
* @param policy - active workspace path policy.
|
|
18
|
+
* @param description - stable type and presentation facts.
|
|
19
|
+
* @returns a complete descriptor with the committed byte size.
|
|
20
|
+
*/
|
|
21
|
+
export async function describeArtifact(path, policy, description) {
|
|
22
|
+
let linkInfo;
|
|
23
|
+
try {
|
|
24
|
+
linkInfo = await lstat(path);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new VisionToolkitError('output', `artifact was not created: ${basename(path)}`, { cause: error });
|
|
28
|
+
}
|
|
29
|
+
if (linkInfo.isSymbolicLink()) {
|
|
30
|
+
throw new VisionToolkitError('path', `artifact must not be a symbolic link: ${basename(path)}`);
|
|
31
|
+
}
|
|
32
|
+
let real;
|
|
33
|
+
try {
|
|
34
|
+
real = await realpath(path);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new VisionToolkitError('output', `artifact is not accessible: ${basename(path)}`, { cause: error });
|
|
38
|
+
}
|
|
39
|
+
if (!isWithin(policy.outputDir, real)) {
|
|
40
|
+
throw new VisionToolkitError('path', `artifact escaped the managed output directory: ${basename(path)}`);
|
|
41
|
+
}
|
|
42
|
+
const info = await stat(real);
|
|
43
|
+
if (!info.isFile())
|
|
44
|
+
throw new VisionToolkitError('output', `artifact is not a regular file: ${basename(path)}`);
|
|
45
|
+
return {
|
|
46
|
+
path,
|
|
47
|
+
filename: basename(path),
|
|
48
|
+
mimeType: description.mimeType,
|
|
49
|
+
kind: description.kind,
|
|
50
|
+
description: description.description,
|
|
51
|
+
sourceTool: description.sourceTool,
|
|
52
|
+
previewIntent: description.previewIntent,
|
|
53
|
+
bytes: info.size,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAmB,MAAM,YAAY,CAAA;AA6BtD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,MAAkB,EAClB,WAAgC;IAEhC,IAAI,QAAQ,CAAA;IACZ,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,kBAAkB,CAAC,QAAQ,EAAE,6BAA6B,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IACzG,CAAC;IACD,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,CAAC,MAAM,EAAE,yCAAyC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACjG,CAAC;IACD,IAAI,IAAY,CAAA;IAChB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,kBAAkB,CAAC,QAAQ,EAAE,+BAA+B,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IAC3G,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,kBAAkB,CAAC,MAAM,EAAE,kDAAkD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC1G,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,kBAAkB,CAAC,QAAQ,EAAE,mCAAmC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC/G,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC;QACxB,QAAQ,EAAE,WAAW,CAAC,QAAQ;QAC9B,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,WAAW,EAAE,WAAW,CAAC,WAAW;QACpC,UAAU,EAAE,WAAW,CAAC,UAAU;QAClC,aAAa,EAAE,WAAW,CAAC,aAAa;QACxC,KAAK,EAAE,IAAI,CAAC,IAAI;KACjB,CAAA;AACH,CAAC"}
|