@anionex/dsh-vision-toolkit 0.1.31 → 0.1.32

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.
@@ -822,6 +822,13 @@ export class VisionToolkitPluginUpdateService {
822
822
  profile: ProfileInstall,
823
823
  pnpmPath: string,
824
824
  ): Promise<CommandResult> {
825
+ // Windows batch shims cannot be spawned directly; route them through cmd.exe.
826
+ const program = this.platform === 'win32' && /\.(?:cmd|bat)$/i.test(pnpmPath)
827
+ ? process.env.COMSPEC ?? 'cmd.exe'
828
+ : pnpmPath
829
+ const argv = program === pnpmPath
830
+ ? [pnpmPath, ...args]
831
+ : [program, '/d', '/s', '/c', pnpmPath, ...args]
825
832
  const controller = new AbortController()
826
833
  let timedOut = false
827
834
  const timeout = setTimeout(() => {
@@ -830,7 +837,7 @@ export class VisionToolkitPluginUpdateService {
830
837
  }, timeoutMs)
831
838
  try {
832
839
  const handle = this.ctx.subprocess.spawn({
833
- argv: [pnpmPath, ...args],
840
+ argv,
834
841
  cwd: profile.profileDir,
835
842
  stdio: {
836
843
  stdin: 'ignore',
package/src/runtime.ts CHANGED
@@ -56,7 +56,9 @@ const VISION_MODEL_TEST_IMAGE = fileURLToPath(new URL('../assets/vision-model-te
56
56
  const VISION_MODEL_TEST_PROMPT = 'This is an explicit service readiness test. Reply with one short sentence confirming that you received the image.'
57
57
 
58
58
  /** Bump when the Pillow compression ladder changes so stale cache entries are ignored. */
59
- const COMPRESSED_IMAGE_CACHE_VERSION = 'v1'
59
+ const COMPRESSED_IMAGE_CACHE_VERSION = 'v2'
60
+ /** Cache keys carry 64-bit digests so Windows paths stay below MAX_PATH; the full file sha256 is computed on read and compared against this prefix. */
61
+ const COMPRESSED_IMAGE_CACHE_KEY_DIGEST_LENGTH = 16
60
62
  const COMPRESSED_IMAGE_CACHE_MAX_ENTRIES = 200
61
63
  const COMPRESSED_IMAGE_CACHE_MAX_BYTES = 512 * 1024 * 1024
62
64
  const COMPRESSED_IMAGE_CACHE_STALE_PARTIAL_MS = 60 * 60 * 1000
@@ -883,7 +885,7 @@ export class VisionToolkitRuntime {
883
885
  private async readCacheCandidate(
884
886
  root: string,
885
887
  name: string,
886
- expectedOutDigest: string,
888
+ expectedOutDigestPrefix: string,
887
889
  maxBytes: number,
888
890
  maxPixels: number,
889
891
  operation: OperationContext,
@@ -909,7 +911,8 @@ export class VisionToolkitRuntime {
909
911
  } catch {
910
912
  return undefined
911
913
  }
912
- if (bytes.length !== info.size || createHash('sha256').update(bytes).digest('hex') !== expectedOutDigest) return undefined
914
+ const digest = createHash('sha256').update(bytes).digest('hex')
915
+ if (bytes.length !== info.size || !digest.startsWith(expectedOutDigestPrefix)) return undefined
913
916
  let probed: { width: number; height: number; format: string } | undefined
914
917
  try {
915
918
  probed = await this.adapter.probeImageSize(real, { signal: operation.signal })
@@ -929,7 +932,7 @@ export class VisionToolkitRuntime {
929
932
 
930
933
  private cacheEntryOutDigest(entry: string, prefix: string): string | undefined {
931
934
  const tail = entry.slice(prefix.length + 1)
932
- return /^[0-9a-f]{64}-/u.test(tail) ? tail.slice(0, 64) : undefined
935
+ return /^[0-9a-f]{16}-/u.test(tail) ? tail.slice(0, 16) : undefined
933
936
  }
934
937
 
935
938
  private async pruneCompressedCache(root: string): Promise<void> {
@@ -997,21 +1000,21 @@ export class VisionToolkitRuntime {
997
1000
  if (bytes.length !== image.bytes) {
998
1001
  throw new VisionToolkitError('input', `image changed while preparing the vision request: ${image.path}`)
999
1002
  }
1000
- const digest = createHash('sha256').update(bytes).digest('hex')
1003
+ const digest = createHash('sha256').update(bytes).digest('hex').slice(0, COMPRESSED_IMAGE_CACHE_KEY_DIGEST_LENGTH)
1001
1004
  const root = await this.compressedImageRoot(policy)
1002
1005
  await this.pruneCompressedCache(root)
1003
1006
  const prefix = `${COMPRESSED_IMAGE_CACHE_VERSION}-${digest}-b${this.config.maxImageBytes}-p${this.config.maxImagePixels}`
1004
1007
  for (const entry of await readdir(root)) {
1005
1008
  if (!entry.startsWith(`${prefix}-`) || entry.startsWith('.')) continue
1006
- const outDigest = this.cacheEntryOutDigest(entry, prefix)
1007
- if (outDigest === undefined) {
1009
+ const outDigestPrefix = this.cacheEntryOutDigest(entry, prefix)
1010
+ if (outDigestPrefix === undefined) {
1008
1011
  await rm(join(root, entry), { force: true }).catch(() => {})
1009
1012
  continue
1010
1013
  }
1011
1014
  const cached = await this.readCacheCandidate(
1012
1015
  root,
1013
1016
  entry,
1014
- outDigest,
1017
+ outDigestPrefix,
1015
1018
  this.config.maxImageBytes,
1016
1019
  this.config.maxImagePixels,
1017
1020
  operation,
@@ -1037,7 +1040,7 @@ export class VisionToolkitRuntime {
1037
1040
  }
1038
1041
  const extension = compressed.format === 'jpeg' ? 'jpg' : compressed.format
1039
1042
  const stagedBytes = await readFile(staged, { signal: operation.signal })
1040
- const outDigest = createHash('sha256').update(stagedBytes).digest('hex')
1043
+ const outDigest = createHash('sha256').update(stagedBytes).digest('hex').slice(0, COMPRESSED_IMAGE_CACHE_KEY_DIGEST_LENGTH)
1041
1044
  const finalName = `${prefix}-${outDigest}-${compressed.width}x${compressed.height}.${extension}`
1042
1045
  const finalPath = join(root, finalName)
1043
1046
  const existing = await this.readCacheCandidate(