@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
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** Workspace-local storage for images pasted into the DSH Web composer. */
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { lstat, mkdir, open, realpath, rename, rm } from 'node:fs/promises';
|
|
4
|
+
import { basename, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { sameOriginPost } from "./web-request.js";
|
|
6
|
+
/** Exact route used by the browser paste integration. */
|
|
7
|
+
export const PASTE_IMAGES_ROUTE = '/_dsh/vision-toolkit/paste-images';
|
|
8
|
+
const MAX_NAME_BYTES = 180;
|
|
9
|
+
function responseJson(res, status, body) {
|
|
10
|
+
const bytes = Buffer.from(JSON.stringify(body));
|
|
11
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
12
|
+
res.setHeader('Content-Length', String(bytes.length));
|
|
13
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
14
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
15
|
+
res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
|
|
16
|
+
res.writeHead(status);
|
|
17
|
+
res.end(bytes);
|
|
18
|
+
}
|
|
19
|
+
function requestError(res, status, code, message) {
|
|
20
|
+
responseJson(res, status, { ok: false, error: { code, message } });
|
|
21
|
+
}
|
|
22
|
+
function message(error) {
|
|
23
|
+
return error instanceof Error ? error.message : String(error);
|
|
24
|
+
}
|
|
25
|
+
function singleQuery(url, key) {
|
|
26
|
+
const values = url.searchParams.getAll(key);
|
|
27
|
+
if (values.length !== 1 || values[0] === undefined || values[0] === '') {
|
|
28
|
+
throw new TypeError(`${key} is required exactly once`);
|
|
29
|
+
}
|
|
30
|
+
return values[0];
|
|
31
|
+
}
|
|
32
|
+
function declaredSize(url) {
|
|
33
|
+
const value = Number(singleQuery(url, 'size'));
|
|
34
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
35
|
+
throw new TypeError('size must be a positive safe integer');
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
function imageMediaType(req) {
|
|
39
|
+
const value = req.headers['content-type']?.split(';', 1)[0]?.trim().toLowerCase();
|
|
40
|
+
if (value === undefined || !value.startsWith('image/'))
|
|
41
|
+
throw new TypeError('Content-Type must be image/*');
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function extensionFor(mediaType) {
|
|
45
|
+
switch (mediaType) {
|
|
46
|
+
case 'image/jpeg': return '.jpg';
|
|
47
|
+
case 'image/png': return '.png';
|
|
48
|
+
case 'image/gif': return '.gif';
|
|
49
|
+
case 'image/webp': return '.webp';
|
|
50
|
+
case 'image/bmp': return '.bmp';
|
|
51
|
+
case 'image/tiff': return '.tiff';
|
|
52
|
+
case 'image/avif': return '.avif';
|
|
53
|
+
case 'image/heic': return '.heic';
|
|
54
|
+
case 'image/heif': return '.heif';
|
|
55
|
+
case 'image/svg+xml': return '.svg';
|
|
56
|
+
default: return '.img';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Convert an untrusted browser label into one portable leaf filename. */
|
|
60
|
+
export function safePastedImageName(raw, mediaType) {
|
|
61
|
+
const leaf = basename(raw.replaceAll('\\', '/')).normalize('NFC');
|
|
62
|
+
let cleaned = leaf
|
|
63
|
+
.replace(/[<>:"|?*\u0000-\u001f/\\]/gu, '_')
|
|
64
|
+
.replace(/\s+/gu, ' ')
|
|
65
|
+
.replace(/^\.+/u, '')
|
|
66
|
+
.trim()
|
|
67
|
+
.replace(/[. ]+$/u, '');
|
|
68
|
+
if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(cleaned))
|
|
69
|
+
cleaned = `_${cleaned}`;
|
|
70
|
+
const fallback = `clipboard-image${extensionFor(mediaType)}`;
|
|
71
|
+
const candidate = cleaned === '' || cleaned === '.' || cleaned === '..' ? fallback : cleaned;
|
|
72
|
+
if (Buffer.byteLength(candidate) <= MAX_NAME_BYTES)
|
|
73
|
+
return candidate;
|
|
74
|
+
const extension = extname(candidate).slice(0, 20);
|
|
75
|
+
const budget = Math.max(1, MAX_NAME_BYTES - Buffer.byteLength(extension));
|
|
76
|
+
let stem = candidate.slice(0, Math.max(1, candidate.length - extension.length));
|
|
77
|
+
while (Buffer.byteLength(stem) > budget)
|
|
78
|
+
stem = stem.slice(0, -1);
|
|
79
|
+
return `${stem}${extension}`;
|
|
80
|
+
}
|
|
81
|
+
/** Reject a resolved path that is not rooted below the expected directory. */
|
|
82
|
+
export function ensurePathInside(root, target) {
|
|
83
|
+
const rel = relative(root, target);
|
|
84
|
+
if (rel !== '' && (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel))) {
|
|
85
|
+
throw new Error(`resolved pasted-image path escapes its workspace root: ${target}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function ensureManagedDirectory(workspace, path) {
|
|
89
|
+
try {
|
|
90
|
+
await mkdir(path, { mode: 0o700 });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST'))
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
const entry = await lstat(path);
|
|
97
|
+
if (entry.isSymbolicLink()) {
|
|
98
|
+
throw new Error(`resolved pasted-image path escapes its workspace root: symbolic link ${path}`);
|
|
99
|
+
}
|
|
100
|
+
if (!entry.isDirectory())
|
|
101
|
+
throw new Error(`pasted-image path is not a directory: ${path}`);
|
|
102
|
+
const canonical = await realpath(path);
|
|
103
|
+
ensurePathInside(workspace, canonical);
|
|
104
|
+
return canonical;
|
|
105
|
+
}
|
|
106
|
+
async function sessionPasteRoot(ctx, sessionId) {
|
|
107
|
+
const session = ctx.sessions.get(sessionId);
|
|
108
|
+
if (session === undefined)
|
|
109
|
+
throw new Error(`live Session not found: ${sessionId}`);
|
|
110
|
+
const cwd = session.header.cwd;
|
|
111
|
+
if (cwd === undefined || !isAbsolute(cwd))
|
|
112
|
+
throw new Error(`Session has no absolute workspace: ${sessionId}`);
|
|
113
|
+
const visibleWorkspace = resolve(cwd);
|
|
114
|
+
const workspace = await realpath(visibleWorkspace);
|
|
115
|
+
const pluginRoot = join(visibleWorkspace, '.dsh-vision-toolkit');
|
|
116
|
+
await ensureManagedDirectory(workspace, pluginRoot);
|
|
117
|
+
const temporaryRoot = join(pluginRoot, 'tmp');
|
|
118
|
+
await ensureManagedDirectory(workspace, temporaryRoot);
|
|
119
|
+
const requestedRoot = join(temporaryRoot, 'pasted-images');
|
|
120
|
+
const root = await ensureManagedDirectory(workspace, requestedRoot);
|
|
121
|
+
const sessionKey = createHash('sha256').update(sessionId).digest('hex').slice(0, 20);
|
|
122
|
+
const requestedSessionRoot = join(requestedRoot, sessionKey);
|
|
123
|
+
const sessionRoot = await ensureManagedDirectory(root, requestedSessionRoot);
|
|
124
|
+
ensurePathInside(root, sessionRoot);
|
|
125
|
+
return { writeRoot: sessionRoot, visibleRoot: requestedSessionRoot };
|
|
126
|
+
}
|
|
127
|
+
async function writeImage(req, directory, filename, expectedBytes, maxBytes) {
|
|
128
|
+
if (expectedBytes > maxBytes)
|
|
129
|
+
throw new RangeError(`image exceeds the ${maxBytes}-byte paste limit`);
|
|
130
|
+
const id = randomUUID();
|
|
131
|
+
const finalPath = join(directory, `${id}-${filename}`);
|
|
132
|
+
const stagingPath = join(directory, `.${id}.partial`);
|
|
133
|
+
ensurePathInside(directory, finalPath);
|
|
134
|
+
ensurePathInside(directory, stagingPath);
|
|
135
|
+
const handle = await open(stagingPath, 'wx', 0o600);
|
|
136
|
+
let received = 0;
|
|
137
|
+
try {
|
|
138
|
+
for await (const chunk of req) {
|
|
139
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
140
|
+
received += bytes.length;
|
|
141
|
+
if (received > expectedBytes || received > maxBytes)
|
|
142
|
+
throw new RangeError('pasted image body exceeds its declared size');
|
|
143
|
+
await handle.write(bytes);
|
|
144
|
+
}
|
|
145
|
+
if (received !== expectedBytes) {
|
|
146
|
+
throw new Error(`pasted image body size mismatch: expected ${expectedBytes}, received ${received}`);
|
|
147
|
+
}
|
|
148
|
+
await handle.sync();
|
|
149
|
+
await handle.close();
|
|
150
|
+
await rename(stagingPath, finalPath);
|
|
151
|
+
return finalPath;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
await handle.close().catch(() => { });
|
|
155
|
+
await rm(stagingPath, { force: true }).catch(() => { });
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Same-origin, live-Session-bound image upload endpoint. */
|
|
160
|
+
export class PastedImageBackend {
|
|
161
|
+
ctx;
|
|
162
|
+
runtime;
|
|
163
|
+
constructor(ctx, runtime) {
|
|
164
|
+
this.ctx = ctx;
|
|
165
|
+
this.runtime = runtime;
|
|
166
|
+
}
|
|
167
|
+
async handle(req, res) {
|
|
168
|
+
if (req.method !== 'POST') {
|
|
169
|
+
res.setHeader('Allow', 'POST');
|
|
170
|
+
requestError(res, 405, 'method-not-allowed', 'Use POST');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!sameOriginPost(req)) {
|
|
174
|
+
requestError(res, 403, 'origin-rejected', 'The request must originate from this DSH Web application');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const url = new URL(req.url ?? PASTE_IMAGES_ROUTE, 'http://dsh.internal');
|
|
179
|
+
const sessionId = singleQuery(url, 'sessionId');
|
|
180
|
+
const size = declaredSize(url);
|
|
181
|
+
const mediaType = imageMediaType(req);
|
|
182
|
+
const filename = safePastedImageName(singleQuery(url, 'name'), mediaType);
|
|
183
|
+
const contentLength = req.headers['content-length'];
|
|
184
|
+
if (contentLength !== undefined && Number(contentLength) !== size) {
|
|
185
|
+
throw new TypeError('Content-Length does not match the declared size');
|
|
186
|
+
}
|
|
187
|
+
const directory = await sessionPasteRoot(this.ctx, sessionId);
|
|
188
|
+
const writtenPath = await writeImage(req, directory.writeRoot, filename, size, this.runtime.maxImageBytes());
|
|
189
|
+
const absolutePath = join(directory.visibleRoot, basename(writtenPath));
|
|
190
|
+
responseJson(res, 201, { ok: true, value: { absolutePath, filename, bytes: size } });
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const status = error instanceof RangeError ? 413 : 400;
|
|
194
|
+
this.ctx.logger.warn('dsh-vision-toolkit pasted image rejected: %s', message(error));
|
|
195
|
+
requestError(res, status, 'paste-image-rejected', message(error));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=paste-images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste-images.js","sourceRoot":"","sources":["../src/paste-images.ts"],"names":[],"mappings":"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"}
|
package/lib/paths.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path fence shared by every tool: inputs must live in the workspace or an
|
|
3
|
+
* explicitly authorized directory, outputs stay inside the plugin-managed
|
|
4
|
+
* output directory, and a symbolic link is allowed only when its real target
|
|
5
|
+
* stays inside the fence.
|
|
6
|
+
* @module dsh-vision-toolkit/paths
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { cp, link, lstat, mkdir, readdir, realpath, rename, rm, stat } from 'node:fs/promises';
|
|
10
|
+
import { extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { VisionToolkitError } from "./errors.js";
|
|
13
|
+
/** Supported input image extensions (the upstream client's allowlist). */
|
|
14
|
+
export const SUPPORTED_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp'];
|
|
15
|
+
/** Whether `child` equals or lies under `parent` on the same path root. */
|
|
16
|
+
export function isWithin(parent, child) {
|
|
17
|
+
const rel = relative(parent, child);
|
|
18
|
+
return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
19
|
+
}
|
|
20
|
+
function expandUserHome(raw) {
|
|
21
|
+
if (raw === '~')
|
|
22
|
+
return homedir();
|
|
23
|
+
if (raw.startsWith('~/') || raw.startsWith(`~${sep}`))
|
|
24
|
+
return join(homedir(), raw.slice(2));
|
|
25
|
+
return raw;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the per-invocation path policy: realpath the workspace, resolve and
|
|
29
|
+
* realpath allowed directories, and create the output directory inside the
|
|
30
|
+
* fence.
|
|
31
|
+
* @param workspaceRaw - session workspace (or process cwd fallback).
|
|
32
|
+
* @param allowedDirs - configured extra allowed roots.
|
|
33
|
+
* @param outputDirRaw - configured output directory (default `.dsh-vision-toolkit/artifacts`).
|
|
34
|
+
* @returns the resolved policy.
|
|
35
|
+
*/
|
|
36
|
+
export async function createPathPolicy(workspaceRaw, allowedDirs, outputDirRaw) {
|
|
37
|
+
let workspace;
|
|
38
|
+
try {
|
|
39
|
+
workspace = await realpath(expandUserHome(workspaceRaw));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new VisionToolkitError('path', `workspace is not accessible: ${workspaceRaw}`, { cause: error });
|
|
43
|
+
}
|
|
44
|
+
const roots = [workspace];
|
|
45
|
+
for (const raw of allowedDirs) {
|
|
46
|
+
const candidate = expandUserHome(raw);
|
|
47
|
+
const target = isAbsolute(candidate) ? candidate : resolve(workspace, candidate);
|
|
48
|
+
try {
|
|
49
|
+
roots.push(await realpath(target));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw new VisionToolkitError('path', `allowedDirs entry is not accessible: ${raw}`, { cause: error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const outputRaw = outputDirRaw === undefined || outputDirRaw.trim().length === 0
|
|
56
|
+
? join(workspace, '.dsh-vision-toolkit', 'artifacts')
|
|
57
|
+
: resolve(workspace, expandUserHome(outputDirRaw));
|
|
58
|
+
if (!roots.some(root => isWithin(root, outputRaw))) {
|
|
59
|
+
throw new VisionToolkitError('path', 'output directory must stay inside the workspace or an allowedDirs entry');
|
|
60
|
+
}
|
|
61
|
+
let outputDir;
|
|
62
|
+
try {
|
|
63
|
+
await mkdir(outputRaw, { recursive: true });
|
|
64
|
+
outputDir = await realpath(outputRaw);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new VisionToolkitError('path', `output directory is not writable: ${outputRaw}`, { cause: error });
|
|
68
|
+
}
|
|
69
|
+
return { workspace, allowedDirs: roots, outputDir };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate one input image path and return its fence-checked absolute path
|
|
73
|
+
* and byte size.
|
|
74
|
+
* @param raw - image path, resolved against the workspace.
|
|
75
|
+
* @param policy - active path fence.
|
|
76
|
+
* @returns absolute path and file size.
|
|
77
|
+
*/
|
|
78
|
+
export async function resolveInputFile(raw, policy) {
|
|
79
|
+
return resolveAuthorizedFile(raw, policy, SUPPORTED_IMAGE_EXTENSIONS, 'image');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Validate one authorized regular file against an explicit extension set.
|
|
83
|
+
* Realpath fencing makes local HTML and future non-image inputs follow the
|
|
84
|
+
* same symlink-safe policy as images.
|
|
85
|
+
* @param raw - path resolved against the workspace.
|
|
86
|
+
* @param policy - active path fence.
|
|
87
|
+
* @param extensions - accepted lowercase extensions including the leading dot.
|
|
88
|
+
* @param kind - user-facing noun used in stable errors.
|
|
89
|
+
* @returns absolute real path and file size.
|
|
90
|
+
*/
|
|
91
|
+
export async function resolveAuthorizedFile(raw, policy, extensions, kind) {
|
|
92
|
+
const candidate = expandUserHome(raw);
|
|
93
|
+
const target = isAbsolute(candidate) ? candidate : resolve(policy.workspace, candidate);
|
|
94
|
+
let real;
|
|
95
|
+
try {
|
|
96
|
+
real = await realpath(target);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw new VisionToolkitError('input', `${kind} not found: ${raw}`, { cause: error });
|
|
100
|
+
}
|
|
101
|
+
if (!policy.allowedDirs.some(root => isWithin(root, real))) {
|
|
102
|
+
throw new VisionToolkitError('path', `${kind} escapes the allowed directories: ${raw}`);
|
|
103
|
+
}
|
|
104
|
+
let info;
|
|
105
|
+
try {
|
|
106
|
+
info = await stat(real);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
throw new VisionToolkitError('input', `${kind} is not readable: ${raw}`, { cause: error });
|
|
110
|
+
}
|
|
111
|
+
if (!info.isFile())
|
|
112
|
+
throw new VisionToolkitError('input', `${kind} is not a regular file: ${raw}`);
|
|
113
|
+
const extension = real.slice(real.lastIndexOf('.')).toLowerCase();
|
|
114
|
+
if (!extensions.includes(extension)) {
|
|
115
|
+
throw new VisionToolkitError('input', `unsupported ${kind} format "${extension || '(none)'}"; supported: ${extensions.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
return { path: real, bytes: info.size };
|
|
118
|
+
}
|
|
119
|
+
/** Validate a local HTML document; URL and data-URI inputs never reach Chrome. */
|
|
120
|
+
export function resolveHtmlFile(raw, policy) {
|
|
121
|
+
return resolveAuthorizedFile(raw, policy, ['.html', '.htm'], 'HTML source');
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Resolve an optional user-supplied output filename inside the plugin output
|
|
125
|
+
* directory. Absolute paths, `..` segments, and wrong extensions are rejected.
|
|
126
|
+
* @param raw - output filename (workspace/outputDir-relative).
|
|
127
|
+
* @param policy - active path fence.
|
|
128
|
+
* @param defaultName - generated default filename.
|
|
129
|
+
* @param extensions - allowed extensions for this output kind.
|
|
130
|
+
* @returns absolute output path (not yet created).
|
|
131
|
+
*/
|
|
132
|
+
export function resolveOutputFile(raw, policy, defaultName, extensions) {
|
|
133
|
+
const name = raw === undefined || raw.trim().length === 0 ? defaultName : raw.trim();
|
|
134
|
+
const expanded = expandUserHome(name);
|
|
135
|
+
if (isAbsolute(expanded))
|
|
136
|
+
throw new VisionToolkitError('path', 'output must be a filename, not an absolute path');
|
|
137
|
+
const segments = expanded.split(/[\\/]/);
|
|
138
|
+
if (segments.length !== 1 || segments[0] === '' || segments[0] === '.' || segments[0] === '..') {
|
|
139
|
+
throw new VisionToolkitError('path', 'output must be one filename inside the output directory');
|
|
140
|
+
}
|
|
141
|
+
const extension = expanded.slice(expanded.lastIndexOf('.')).toLowerCase();
|
|
142
|
+
if (!extensions.includes(extension)) {
|
|
143
|
+
throw new VisionToolkitError('output', `output must use one of: ${extensions.join(', ')}`);
|
|
144
|
+
}
|
|
145
|
+
const target = resolve(policy.outputDir, expanded);
|
|
146
|
+
if (!isWithin(policy.outputDir, target)) {
|
|
147
|
+
throw new VisionToolkitError('path', 'output must stay inside the output directory');
|
|
148
|
+
}
|
|
149
|
+
return target;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Reserve a random, non-user-controlled staging path inside the real output
|
|
153
|
+
* directory. Upstream writes here so an existing destination symlink can
|
|
154
|
+
* never redirect the write outside the fence.
|
|
155
|
+
* @param policy - active path fence.
|
|
156
|
+
* @param extension - output extension including the leading dot.
|
|
157
|
+
* @returns absent staging path inside {@link PathPolicy.outputDir}.
|
|
158
|
+
*/
|
|
159
|
+
export function createStagedOutput(policy, extension) {
|
|
160
|
+
if (extension !== extname(`file${extension}`) || !/^\.[a-z0-9]+$/i.test(extension)) {
|
|
161
|
+
throw new VisionToolkitError('output', `invalid staging extension: ${extension}`);
|
|
162
|
+
}
|
|
163
|
+
return join(policy.outputDir, `.vision-toolkit-${randomUUID()}${extension}`);
|
|
164
|
+
}
|
|
165
|
+
/** Resolve one direct child directory of the managed artifact root. */
|
|
166
|
+
export function resolveOutputDirectory(raw, policy, defaultName) {
|
|
167
|
+
const name = raw === undefined || raw.trim().length === 0 ? defaultName : raw.trim();
|
|
168
|
+
const expanded = expandUserHome(name);
|
|
169
|
+
if (isAbsolute(expanded))
|
|
170
|
+
throw new VisionToolkitError('path', 'artifact directory must not be an absolute path');
|
|
171
|
+
const segments = expanded.split(/[\\/]/);
|
|
172
|
+
if (segments.length !== 1
|
|
173
|
+
|| segments[0] === ''
|
|
174
|
+
|| segments[0] === '.'
|
|
175
|
+
|| segments[0] === '..'
|
|
176
|
+
|| expanded.startsWith('.vision-toolkit-')) {
|
|
177
|
+
throw new VisionToolkitError('path', 'artifact directory must be one visible directory name inside the output directory');
|
|
178
|
+
}
|
|
179
|
+
const target = resolve(policy.outputDir, expanded);
|
|
180
|
+
if (!isWithin(policy.outputDir, target)) {
|
|
181
|
+
throw new VisionToolkitError('path', 'artifact directory must stay inside the output directory');
|
|
182
|
+
}
|
|
183
|
+
return target;
|
|
184
|
+
}
|
|
185
|
+
/** Create a random staging directory that no upstream command can choose. */
|
|
186
|
+
export async function createStagedDirectory(policy) {
|
|
187
|
+
const path = join(policy.outputDir, `.vision-toolkit-${randomUUID()}`);
|
|
188
|
+
await mkdir(path);
|
|
189
|
+
return path;
|
|
190
|
+
}
|
|
191
|
+
async function assertSafeDirectoryTree(root, current = root) {
|
|
192
|
+
for (const entry of await readdir(current, { withFileTypes: true })) {
|
|
193
|
+
const path = join(current, entry.name);
|
|
194
|
+
const info = await lstat(path);
|
|
195
|
+
if (info.isSymbolicLink()) {
|
|
196
|
+
throw new VisionToolkitError('path', `managed artifact directory contains a symbolic link: ${entry.name}`);
|
|
197
|
+
}
|
|
198
|
+
if (info.isDirectory()) {
|
|
199
|
+
await assertSafeDirectoryTree(root, path);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (!info.isFile()) {
|
|
203
|
+
throw new VisionToolkitError('path', `managed artifact directory contains a non-regular entry: ${entry.name}`);
|
|
204
|
+
}
|
|
205
|
+
const real = await realpath(path);
|
|
206
|
+
if (!isWithin(root, real)) {
|
|
207
|
+
throw new VisionToolkitError('path', `managed artifact entry escaped its directory: ${entry.name}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Copy an existing managed run into staging for an explicit resume operation.
|
|
213
|
+
* A missing destination is a normal first run; non-directory or symlink state
|
|
214
|
+
* fails closed instead of giving the upstream script an ambiguous workspace.
|
|
215
|
+
*/
|
|
216
|
+
export async function seedStagedDirectory(finalPath, staged, policy) {
|
|
217
|
+
let info;
|
|
218
|
+
try {
|
|
219
|
+
info = await lstat(finalPath);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (error.code === 'ENOENT')
|
|
223
|
+
return false;
|
|
224
|
+
throw new VisionToolkitError('path', 'existing artifact directory is not accessible', { cause: error });
|
|
225
|
+
}
|
|
226
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
227
|
+
throw new VisionToolkitError('path', 'resume target must be a real managed artifact directory');
|
|
228
|
+
}
|
|
229
|
+
const real = await realpath(finalPath);
|
|
230
|
+
if (!isWithin(policy.outputDir, real)) {
|
|
231
|
+
throw new VisionToolkitError('path', 'resume target escaped the managed output directory');
|
|
232
|
+
}
|
|
233
|
+
await assertSafeDirectoryTree(real);
|
|
234
|
+
await cp(real, staged, { recursive: true, force: true });
|
|
235
|
+
await assertSafeDirectoryTree(staged);
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Atomically replace one managed artifact directory, restoring the previous
|
|
240
|
+
* complete run if the final rename fails. The upstream only ever writes the
|
|
241
|
+
* random staging path.
|
|
242
|
+
*/
|
|
243
|
+
export async function commitStagedDirectory(staged, finalPath, policy) {
|
|
244
|
+
const stagedReal = await realpath(staged).catch((error) => {
|
|
245
|
+
throw new VisionToolkitError('output', 'upstream did not create the expected artifact directory', { cause: error });
|
|
246
|
+
});
|
|
247
|
+
if (!isWithin(policy.outputDir, stagedReal)) {
|
|
248
|
+
throw new VisionToolkitError('path', 'staged artifact directory escaped the managed output directory');
|
|
249
|
+
}
|
|
250
|
+
const stagedInfo = await lstat(stagedReal);
|
|
251
|
+
if (stagedInfo.isSymbolicLink() || !stagedInfo.isDirectory()) {
|
|
252
|
+
throw new VisionToolkitError('output', 'staged artifact output is not a real directory');
|
|
253
|
+
}
|
|
254
|
+
await assertSafeDirectoryTree(stagedReal);
|
|
255
|
+
const backup = join(policy.outputDir, `.vision-toolkit-backup-${randomUUID()}`);
|
|
256
|
+
let movedPrevious = false;
|
|
257
|
+
try {
|
|
258
|
+
try {
|
|
259
|
+
await rename(finalPath, backup);
|
|
260
|
+
movedPrevious = true;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
if (error.code !== 'ENOENT')
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
await rename(stagedReal, finalPath);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (movedPrevious)
|
|
271
|
+
await rename(backup, finalPath).catch(() => { });
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
if (movedPrevious)
|
|
275
|
+
await rm(backup, { recursive: true, force: true });
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
throw new VisionToolkitError('output', 'could not commit the managed artifact directory', { cause: error });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Validate a staged regular file and atomically place it at the resolved final
|
|
283
|
+
* filename. Replacing an existing symlink replaces the link itself; upstream
|
|
284
|
+
* never opens the user-selected destination.
|
|
285
|
+
* @param staged - random staging path returned by {@link createStagedOutput}.
|
|
286
|
+
* @param finalPath - final path returned by {@link resolveOutputFile}.
|
|
287
|
+
* @param policy - active path fence.
|
|
288
|
+
*/
|
|
289
|
+
export async function commitStagedOutput(staged, finalPath, policy) {
|
|
290
|
+
const real = await realpath(staged).catch((error) => {
|
|
291
|
+
throw new VisionToolkitError('output', 'upstream did not create the expected output file', { cause: error });
|
|
292
|
+
});
|
|
293
|
+
if (!isWithin(policy.outputDir, real)) {
|
|
294
|
+
throw new VisionToolkitError('path', 'staged output escaped the managed output directory');
|
|
295
|
+
}
|
|
296
|
+
const info = await stat(real);
|
|
297
|
+
if (!info.isFile())
|
|
298
|
+
throw new VisionToolkitError('output', 'upstream output is not a regular file');
|
|
299
|
+
try {
|
|
300
|
+
await rename(real, finalPath);
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
const code = error.code;
|
|
304
|
+
if (code !== 'EEXIST' && code !== 'EPERM')
|
|
305
|
+
throw error;
|
|
306
|
+
await rm(finalPath, { force: true });
|
|
307
|
+
try {
|
|
308
|
+
await link(real, finalPath);
|
|
309
|
+
}
|
|
310
|
+
catch (linkError) {
|
|
311
|
+
if (linkError.code === 'EEXIST') {
|
|
312
|
+
throw new VisionToolkitError('path', 'output destination changed while the staged file was being committed', { cause: linkError });
|
|
313
|
+
}
|
|
314
|
+
throw linkError;
|
|
315
|
+
}
|
|
316
|
+
await rm(real, { force: true });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/** Reject an output that would overwrite its own input file. */
|
|
320
|
+
export function assertDistinctOutput(input, output) {
|
|
321
|
+
if (input === output) {
|
|
322
|
+
throw new VisionToolkitError('input', 'output would overwrite the input image');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=paths.js.map
|
package/lib/paths.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|