@anionex/dsh-vision-toolkit 0.1.25 → 0.1.27
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.i18n.yaml +2 -2
- package/README.md +75 -1
- package/README.zh.md +73 -1
- package/lib/client.js +1 -0
- package/lib/client.js.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/paste-images.js +8 -1
- package/lib/paste-images.js.map +1 -1
- package/lib/runtime.js +223 -20
- package/lib/runtime.js.map +1 -1
- package/lib/tools.js +9 -3
- package/lib/tools.js.map +1 -1
- package/lib/types/client/paste-images.d.ts.map +1 -1
- package/lib/types/config.d.ts +2 -2
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/paste-images.d.ts +8 -1
- package/lib/types/paste-images.d.ts.map +1 -1
- package/lib/types/runtime.d.ts +7 -0
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/upstream.d.ts +21 -0
- package/lib/types/upstream.d.ts.map +1 -1
- package/lib/upstream.js +157 -0
- package/lib/upstream.js.map +1 -1
- package/package.json +1 -1
- package/src/client/paste-images.tsx +1 -0
- package/src/config.ts +2 -2
- package/src/index.ts +2 -2
- package/src/paste-images.ts +10 -2
- package/src/runtime.ts +253 -23
- package/src/tools.ts +9 -3
- package/src/upstream.ts +184 -0
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AACzF,OAAO,EACL,MAAM,EACN,iCAAiC,EACjC,aAAa,GAEd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAA;AAClG,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/paste-images.js
CHANGED
|
@@ -12,6 +12,13 @@ export const PASTE_IMAGES_ROUTE = '/_dsh/vision-toolkit/paste-images';
|
|
|
12
12
|
*/
|
|
13
13
|
export const PASTE_POLICY_ROUTE = '/_dsh/vision-toolkit/paste-policy';
|
|
14
14
|
const MAX_NAME_BYTES = 180;
|
|
15
|
+
/**
|
|
16
|
+
* Hard per-image upload ceiling for pastes. Files between the configured
|
|
17
|
+
* `maxImageBytes` (default 4 MiB) and this ceiling are stored as-is and
|
|
18
|
+
* auto-compressed by the runtime on first use; matching the browser-side
|
|
19
|
+
* safety cap keeps the two bundles in agreement.
|
|
20
|
+
*/
|
|
21
|
+
export const MAX_PASTE_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
15
22
|
function responseJson(res, status, body) {
|
|
16
23
|
const bytes = Buffer.from(JSON.stringify(body));
|
|
17
24
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
@@ -196,7 +203,7 @@ export class PastedImageBackend {
|
|
|
196
203
|
throw new TypeError('Content-Length does not match the declared size');
|
|
197
204
|
}
|
|
198
205
|
const directory = await sessionPasteRoot(this.ctx, sessionId);
|
|
199
|
-
const writtenPath = await writeImage(req, directory.writeRoot, filename, size, this.runtime.
|
|
206
|
+
const writtenPath = await writeImage(req, directory.writeRoot, filename, size, this.runtime.maxUploadBytes());
|
|
200
207
|
const absolutePath = join(directory.visibleRoot, basename(writtenPath));
|
|
201
208
|
responseJson(res, 201, { ok: true, value: { absolutePath, filename, bytes: size } });
|
|
202
209
|
}
|
package/lib/paste-images.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paste-images.js","sourceRoot":"","sources":["../src/paste-images.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AAE3E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAGvF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,yDAAyD;AACzD,MAAM,CAAC,MAAM,kBAAkB,GAAG,mCAAmC,CAAA;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,mCAAmC,CAAA;AAkCrE,MAAM,cAAc,GAAG,GAAG,CAAA;
|
|
1
|
+
{"version":3,"file":"paste-images.js","sourceRoot":"","sources":["../src/paste-images.ts"],"names":[],"mappings":"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"}
|
package/lib/runtime.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module dsh-vision-toolkit/runtime
|
|
7
7
|
*/
|
|
8
8
|
import { createHash, randomUUID } from 'node:crypto';
|
|
9
|
-
import { readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
9
|
+
import { lstat, mkdir, readFile, readdir, realpath, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
10
10
|
import { basename, extname, join } from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { SaxesParser } from 'saxes';
|
|
@@ -20,6 +20,11 @@ import { PLUGIN_VERSION } from "./version.js";
|
|
|
20
20
|
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
21
21
|
const VISION_MODEL_TEST_IMAGE = fileURLToPath(new URL('../assets/vision-model-test.png', import.meta.url));
|
|
22
22
|
const VISION_MODEL_TEST_PROMPT = 'This is an explicit service readiness test. Reply with one short sentence confirming that you received the image.';
|
|
23
|
+
/** Bump when the Pillow compression ladder changes so stale cache entries are ignored. */
|
|
24
|
+
const COMPRESSED_IMAGE_CACHE_VERSION = 'v1';
|
|
25
|
+
const COMPRESSED_IMAGE_CACHE_MAX_ENTRIES = 200;
|
|
26
|
+
const COMPRESSED_IMAGE_CACHE_MAX_BYTES = 512 * 1024 * 1024;
|
|
27
|
+
const COMPRESSED_IMAGE_CACHE_STALE_PARTIAL_MS = 60 * 60 * 1000;
|
|
23
28
|
function svgDocumentPathCount(svg) {
|
|
24
29
|
const parser = new SaxesParser({ xmlns: true });
|
|
25
30
|
let depth = 0;
|
|
@@ -413,22 +418,213 @@ export class VisionToolkitRuntime {
|
|
|
413
418
|
pathPolicy(workspace) {
|
|
414
419
|
return createPathPolicy(workspace, this.config.allowedDirs);
|
|
415
420
|
}
|
|
421
|
+
async compressedImageRoot(policy) {
|
|
422
|
+
const root = join(policy.workspace, '.dsh-vision-toolkit', 'tmp', 'compressed-images');
|
|
423
|
+
let current = policy.workspace;
|
|
424
|
+
for (const segment of ['.dsh-vision-toolkit', 'tmp', 'compressed-images']) {
|
|
425
|
+
current = join(current, segment);
|
|
426
|
+
try {
|
|
427
|
+
await mkdir(current, { mode: 0o700 });
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST'))
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
const info = await lstat(current);
|
|
434
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
435
|
+
throw new VisionToolkitError('path', `compressed-image cache path is not a real directory: ${current}`);
|
|
436
|
+
}
|
|
437
|
+
if (!isWithin(policy.workspace, current)) {
|
|
438
|
+
throw new VisionToolkitError('path', `compressed-image cache path escaped the workspace: ${current}`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const canonical = await realpath(root);
|
|
442
|
+
if (!isWithin(policy.workspace, canonical)) {
|
|
443
|
+
throw new VisionToolkitError('path', 'compressed-image cache resolved outside the workspace');
|
|
444
|
+
}
|
|
445
|
+
return canonical;
|
|
446
|
+
}
|
|
447
|
+
async readCacheCandidate(root, name, expectedOutDigest, maxBytes, maxPixels, operation) {
|
|
448
|
+
const candidate = join(root, name);
|
|
449
|
+
let info;
|
|
450
|
+
try {
|
|
451
|
+
info = await lstat(candidate);
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
if (!info.isFile() || info.size < 1 || info.size > maxBytes)
|
|
457
|
+
return undefined;
|
|
458
|
+
let real;
|
|
459
|
+
try {
|
|
460
|
+
real = await realpath(candidate);
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
if (!isWithin(root, real))
|
|
466
|
+
return undefined;
|
|
467
|
+
let bytes;
|
|
468
|
+
try {
|
|
469
|
+
bytes = await readFile(real, { signal: operation.signal });
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
return undefined;
|
|
473
|
+
}
|
|
474
|
+
if (bytes.length !== info.size || createHash('sha256').update(bytes).digest('hex') !== expectedOutDigest)
|
|
475
|
+
return undefined;
|
|
476
|
+
let probed;
|
|
477
|
+
try {
|
|
478
|
+
probed = await this.adapter.probeImageSize(real, { signal: operation.signal });
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
probed = undefined;
|
|
482
|
+
}
|
|
483
|
+
const extension = extname(real).toLowerCase();
|
|
484
|
+
if (probed === undefined
|
|
485
|
+
|| FORMAT_BY_EXTENSION.get(extension) !== probed.format
|
|
486
|
+
|| probed.width * probed.height > maxPixels) {
|
|
487
|
+
return undefined;
|
|
488
|
+
}
|
|
489
|
+
return { path: real, bytes: bytes.length, width: probed.width, height: probed.height, format: probed.format };
|
|
490
|
+
}
|
|
491
|
+
cacheEntryOutDigest(entry, prefix) {
|
|
492
|
+
const tail = entry.slice(prefix.length + 1);
|
|
493
|
+
return /^[0-9a-f]{64}-/u.test(tail) ? tail.slice(0, 64) : undefined;
|
|
494
|
+
}
|
|
495
|
+
async pruneCompressedCache(root) {
|
|
496
|
+
let entries;
|
|
497
|
+
try {
|
|
498
|
+
entries = await readdir(root);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const stalePartials = [];
|
|
504
|
+
const candidates = [];
|
|
505
|
+
for (const name of entries) {
|
|
506
|
+
if (name.startsWith('.')) {
|
|
507
|
+
if (name.endsWith('.partial')) {
|
|
508
|
+
const info = await lstat(join(root, name)).catch(() => undefined);
|
|
509
|
+
if (info !== undefined && Date.now() - info.mtimeMs > COMPRESSED_IMAGE_CACHE_STALE_PARTIAL_MS) {
|
|
510
|
+
stalePartials.push(name);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
let info;
|
|
516
|
+
try {
|
|
517
|
+
info = await lstat(join(root, name));
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
candidates.push({
|
|
523
|
+
name,
|
|
524
|
+
size: info.isFile() ? info.size : 0,
|
|
525
|
+
mtime: info.mtimeMs,
|
|
526
|
+
removable: !info.isFile() || !name.startsWith(`${COMPRESSED_IMAGE_CACHE_VERSION}-`),
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
candidates.sort((a, b) => a.mtime - b.mtime);
|
|
530
|
+
let totalBytes = 0;
|
|
531
|
+
let kept = 0;
|
|
532
|
+
const remove = [];
|
|
533
|
+
for (const candidate of candidates) {
|
|
534
|
+
if (candidate.removable
|
|
535
|
+
|| totalBytes + candidate.size > COMPRESSED_IMAGE_CACHE_MAX_BYTES
|
|
536
|
+
|| kept >= COMPRESSED_IMAGE_CACHE_MAX_ENTRIES) {
|
|
537
|
+
remove.push(candidate.name);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
totalBytes += candidate.size;
|
|
541
|
+
kept += 1;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
await Promise.all([...stalePartials, ...remove].map(name => rm(join(root, name), { force: true }).catch(() => { })));
|
|
545
|
+
}
|
|
546
|
+
async autoCompressImage(image, policy, operation) {
|
|
547
|
+
let bytes;
|
|
548
|
+
try {
|
|
549
|
+
bytes = await readFile(image.path, { signal: operation.signal });
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
throw new VisionToolkitError('input', `image changed while preparing the vision request: ${image.path}`, { cause: error });
|
|
553
|
+
}
|
|
554
|
+
if (bytes.length !== image.bytes) {
|
|
555
|
+
throw new VisionToolkitError('input', `image changed while preparing the vision request: ${image.path}`);
|
|
556
|
+
}
|
|
557
|
+
const digest = createHash('sha256').update(bytes).digest('hex');
|
|
558
|
+
const root = await this.compressedImageRoot(policy);
|
|
559
|
+
await this.pruneCompressedCache(root);
|
|
560
|
+
const prefix = `${COMPRESSED_IMAGE_CACHE_VERSION}-${digest}-b${this.config.maxImageBytes}-p${this.config.maxImagePixels}`;
|
|
561
|
+
for (const entry of await readdir(root)) {
|
|
562
|
+
if (!entry.startsWith(`${prefix}-`) || entry.startsWith('.'))
|
|
563
|
+
continue;
|
|
564
|
+
const outDigest = this.cacheEntryOutDigest(entry, prefix);
|
|
565
|
+
if (outDigest === undefined) {
|
|
566
|
+
await rm(join(root, entry), { force: true }).catch(() => { });
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
const cached = await this.readCacheCandidate(root, entry, outDigest, this.config.maxImageBytes, this.config.maxImagePixels, operation);
|
|
570
|
+
if (cached !== undefined) {
|
|
571
|
+
return { ...cached, originalPath: image.path };
|
|
572
|
+
}
|
|
573
|
+
await rm(join(root, entry), { force: true }).catch(() => { });
|
|
574
|
+
}
|
|
575
|
+
const staged = join(root, `.${prefix}-${randomUUID()}.partial`);
|
|
576
|
+
let compressed;
|
|
577
|
+
try {
|
|
578
|
+
compressed = await this.adapter.compressImage(image.path, staged, this.config.maxImageBytes, this.config.maxImagePixels, { signal: operation.signal });
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
await rm(staged, { force: true }).catch(() => { });
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
const extension = compressed.format === 'jpeg' ? 'jpg' : compressed.format;
|
|
585
|
+
const stagedBytes = await readFile(staged, { signal: operation.signal });
|
|
586
|
+
const outDigest = createHash('sha256').update(stagedBytes).digest('hex');
|
|
587
|
+
const finalName = `${prefix}-${outDigest}-${compressed.width}x${compressed.height}.${extension}`;
|
|
588
|
+
const finalPath = join(root, finalName);
|
|
589
|
+
const existing = await this.readCacheCandidate(root, finalName, outDigest, this.config.maxImageBytes, this.config.maxImagePixels, operation);
|
|
590
|
+
if (existing !== undefined) {
|
|
591
|
+
await rm(staged, { force: true }).catch(() => { });
|
|
592
|
+
return { ...existing, originalPath: image.path };
|
|
593
|
+
}
|
|
594
|
+
await rm(finalPath, { force: true }).catch(() => { });
|
|
595
|
+
try {
|
|
596
|
+
await rename(staged, finalPath);
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
await rm(staged, { force: true }).catch(() => { });
|
|
600
|
+
throw new VisionToolkitError('path', `cannot commit compressed image cache entry: ${finalPath}`, { cause: error });
|
|
601
|
+
}
|
|
602
|
+
await this.pruneCompressedCache(root);
|
|
603
|
+
return {
|
|
604
|
+
path: finalPath,
|
|
605
|
+
bytes: compressed.bytes,
|
|
606
|
+
width: compressed.width,
|
|
607
|
+
height: compressed.height,
|
|
608
|
+
format: compressed.format,
|
|
609
|
+
originalPath: image.path,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
416
612
|
async validateImage(raw, policy, operation) {
|
|
417
613
|
const image = await resolveInputFile(raw, policy);
|
|
418
|
-
if (image.bytes > this.config.maxImageBytes) {
|
|
419
|
-
throw new VisionToolkitError('capacity', `image is ${image.bytes} bytes, exceeding maxImageBytes ${this.config.maxImageBytes}`);
|
|
420
|
-
}
|
|
421
614
|
const decoded = await this.adapter.probeImageSize(image.path, { signal: operation.signal });
|
|
422
615
|
const pixels = decoded.width * decoded.height;
|
|
423
|
-
if (!Number.isSafeInteger(pixels) || pixels
|
|
424
|
-
throw new VisionToolkitError('
|
|
616
|
+
if (!Number.isSafeInteger(pixels) || pixels < 1) {
|
|
617
|
+
throw new VisionToolkitError('input', `image dimensions are invalid: ${decoded.width}x${decoded.height}`);
|
|
425
618
|
}
|
|
426
619
|
const extension = extname(image.path).toLowerCase();
|
|
427
620
|
const expected = FORMAT_BY_EXTENSION.get(extension);
|
|
428
621
|
if (expected !== decoded.format) {
|
|
429
622
|
throw new VisionToolkitError('input', `image content is ${decoded.format}, but the filename uses ${extension}`);
|
|
430
623
|
}
|
|
431
|
-
|
|
624
|
+
if (image.bytes <= this.config.maxImageBytes && pixels <= this.config.maxImagePixels) {
|
|
625
|
+
return { ...image, width: decoded.width, height: decoded.height, format: decoded.format, originalPath: image.path };
|
|
626
|
+
}
|
|
627
|
+
return this.autoCompressImage(image, policy, operation);
|
|
432
628
|
}
|
|
433
629
|
accountImage(image, operation) {
|
|
434
630
|
operation.metrics.imageCount += 1;
|
|
@@ -501,11 +697,12 @@ export class VisionToolkitRuntime {
|
|
|
501
697
|
}
|
|
502
698
|
}
|
|
503
699
|
async annotateLocations(tool, image, elements, output, policy, operation) {
|
|
504
|
-
const extension = extname(image.
|
|
505
|
-
const stem = basename(image.
|
|
700
|
+
const extension = extname(image.originalPath).toLowerCase();
|
|
701
|
+
const stem = basename(image.originalPath, extension);
|
|
506
702
|
const suffix = tool === 'vision_ground' ? 'ground' : 'detect';
|
|
507
703
|
const finalPath = resolveOutputFile(output, policy, `${stem}.${suffix}.preview.png`, ['.png']);
|
|
508
704
|
assertDistinctOutput(image.path, finalPath);
|
|
705
|
+
assertDistinctOutput(image.originalPath, finalPath);
|
|
509
706
|
const staged = createStagedOutput(policy, '.png');
|
|
510
707
|
try {
|
|
511
708
|
const started = Date.now();
|
|
@@ -668,10 +865,11 @@ export class VisionToolkitRuntime {
|
|
|
668
865
|
const policy = await this.pathPolicy(options.workspace);
|
|
669
866
|
const image = await this.validateImage(request.image, policy, operation);
|
|
670
867
|
this.accountImage(image, operation);
|
|
671
|
-
const sourceExtension = extname(image.
|
|
672
|
-
const stem = basename(image.
|
|
868
|
+
const sourceExtension = extname(image.originalPath).toLowerCase();
|
|
869
|
+
const stem = basename(image.originalPath, sourceExtension);
|
|
673
870
|
const finalPath = resolveOutputFile(request.output, policy, request.scale !== undefined && request.scale > 1 ? `${stem}.crop@${request.scale}x.png` : `${stem}.crop.png`, ['.png', '.jpg', '.jpeg']);
|
|
674
871
|
assertDistinctOutput(image.path, finalPath);
|
|
872
|
+
assertDistinctOutput(image.originalPath, finalPath);
|
|
675
873
|
const outputExtension = extname(finalPath).toLowerCase();
|
|
676
874
|
const staged = createStagedOutput(policy, outputExtension);
|
|
677
875
|
try {
|
|
@@ -729,10 +927,11 @@ export class VisionToolkitRuntime {
|
|
|
729
927
|
const policy = await this.pathPolicy(options.workspace);
|
|
730
928
|
const image = await this.validateImage(request.image, policy, operation);
|
|
731
929
|
this.accountImage(image, operation);
|
|
732
|
-
const extension = extname(image.
|
|
733
|
-
const stem = basename(image.
|
|
930
|
+
const extension = extname(image.originalPath).toLowerCase();
|
|
931
|
+
const stem = basename(image.originalPath, extension);
|
|
734
932
|
const finalPath = resolveOutputFile(request.output, policy, `${stem}.svg`, ['.svg']);
|
|
735
933
|
assertDistinctOutput(image.path, finalPath);
|
|
934
|
+
assertDistinctOutput(image.originalPath, finalPath);
|
|
736
935
|
const staged = createStagedOutput(policy, '.svg');
|
|
737
936
|
try {
|
|
738
937
|
const result = await this.runUpstream('trace', [
|
|
@@ -796,10 +995,13 @@ export class VisionToolkitRuntime {
|
|
|
796
995
|
const rebuilt = await this.validateImage(request.rebuilt, policy, operation);
|
|
797
996
|
this.accountImage(original, operation);
|
|
798
997
|
this.accountImage(rebuilt, operation);
|
|
799
|
-
const originalStem = basename(original.
|
|
800
|
-
const rebuiltStem = basename(rebuilt.
|
|
998
|
+
const originalStem = basename(original.originalPath, extname(original.originalPath));
|
|
999
|
+
const rebuiltStem = basename(rebuilt.originalPath, extname(rebuilt.originalPath));
|
|
801
1000
|
const finalDirectory = resolveOutputDirectory(request.runName, policy, `${originalStem}-vs-${rebuiltStem}.pixel-diff`);
|
|
802
|
-
if (isWithin(finalDirectory, original.path)
|
|
1001
|
+
if (isWithin(finalDirectory, original.path)
|
|
1002
|
+
|| isWithin(finalDirectory, rebuilt.path)
|
|
1003
|
+
|| isWithin(finalDirectory, original.originalPath)
|
|
1004
|
+
|| isWithin(finalDirectory, rebuilt.originalPath)) {
|
|
803
1005
|
throw new VisionToolkitError('input', 'pixel_diff artifact directory would replace an input image');
|
|
804
1006
|
}
|
|
805
1007
|
const stagedDirectory = await createStagedDirectory(policy);
|
|
@@ -922,9 +1124,9 @@ export class VisionToolkitRuntime {
|
|
|
922
1124
|
const policy = await this.pathPolicy(options.workspace);
|
|
923
1125
|
const image = await this.validateImage(request.image, policy, operation);
|
|
924
1126
|
this.accountImage(image, operation);
|
|
925
|
-
const stem = basename(image.
|
|
1127
|
+
const stem = basename(image.originalPath, extname(image.originalPath));
|
|
926
1128
|
const finalDirectory = resolveOutputDirectory(request.runName, policy, `${stem}.long-ocr`);
|
|
927
|
-
if (isWithin(finalDirectory, image.path)) {
|
|
1129
|
+
if (isWithin(finalDirectory, image.path) || isWithin(finalDirectory, image.originalPath)) {
|
|
928
1130
|
throw new VisionToolkitError('input', 'long_screenshot_ocr artifact directory would replace the input image');
|
|
929
1131
|
}
|
|
930
1132
|
const stagedDirectory = await createStagedDirectory(policy);
|
|
@@ -1074,10 +1276,11 @@ export class VisionToolkitRuntime {
|
|
|
1074
1276
|
if (excludeColor !== undefined && !HEX_COLOR_PATTERN.test(excludeColor)) {
|
|
1075
1277
|
throw new VisionToolkitError('input', 'extract_foreground.excludeColor must be #RRGGBB');
|
|
1076
1278
|
}
|
|
1077
|
-
const extension = extname(image.
|
|
1078
|
-
const stem = basename(image.
|
|
1279
|
+
const extension = extname(image.originalPath).toLowerCase();
|
|
1280
|
+
const stem = basename(image.originalPath, extension);
|
|
1079
1281
|
const finalPath = resolveOutputFile(request.output, policy, `${stem}.foreground.png`, ['.png']);
|
|
1080
1282
|
assertDistinctOutput(image.path, finalPath);
|
|
1283
|
+
assertDistinctOutput(image.originalPath, finalPath);
|
|
1081
1284
|
const staged = createStagedOutput(policy, '.png');
|
|
1082
1285
|
try {
|
|
1083
1286
|
const result = await this.runUpstream('extract_foreground', [
|