@anionex/dsh-vision-toolkit 0.1.28 → 0.1.29

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.md CHANGED
@@ -143,7 +143,7 @@ dsh plugin --profile headless add @anionex/dsh-vision-toolkit
143
143
 
144
144
  Restart a running Web Profile, then open **Settings → Vision Toolkit**. The free provider is already configured; run **Test vision model** to confirm it is reachable.
145
145
 
146
- The first start prepares an isolated runtime, so it needs access to the Python package cache or the network. A normal installation does not require an `agent-vision-toolkit` source checkout or a local path setting.
146
+ The first start prepares an isolated runtime: the plugin prefers a system Python 3.11+; when none is found, it downloads a hash-verified standalone Python (about 35 MB) from a pinned release source on first use. A normal installation does not require an `agent-vision-toolkit` source checkout or a local path setting.
147
147
 
148
148
  ### 3. Paste an image and describe the outcome you want
149
149
 
@@ -274,15 +274,17 @@ For a trusted internal endpoint that uses a self-signed certificate or MITM prox
274
274
 
275
275
  - A DeepSeek Harness Web or Headless Profile.
276
276
  - Node.js `^22.19.0` or `>=24.0.0`.
277
- - Python 3.11+; the plugin prepares an isolated environment by default.
277
+ - Python 3.11+ is usually not needed in advance: the plugin prefers a system Python and otherwise downloads a pinned standalone Python 3.13 automatically, preparing its own isolated environment. Only that first automatic download needs network access.
278
278
  - Only `vision_html_screenshot` requires Chrome, Chromium, or Edge.
279
279
  - Inputs must be PNG, JPEG, GIF, or WebP files in the session workspace, the platform temporary directory, or an explicitly allowed directory.
280
280
 
281
281
  ### Configure the Python runtime
282
282
 
283
+ By default the plugin picks a system Python 3.11+, or downloads a standalone Python when none is found; most users never need to configure this section. The rest is for advanced setups where automatic discovery fails, a specific interpreter is required, or an external runtime is used.
284
+
283
285
  The packaged `managed` runtime creates its own isolated virtual environment. `runtime.python` selects the Python executable used to bootstrap or refresh that environment; it does not replace the managed environment with the interpreter's global site-packages. Set it when automatic discovery fails or when several Python installations exist. The override is also used by `runtime.mode: external`.
284
286
 
285
- Python 3.11 or newer is required. Without an override, the plugin tries `python3` then `python` on macOS/Linux, and `python`, `py -3`, then `python3` on Windows. A configured value is passed as one executable name or path, not as a shell command with arguments, so use `py` (not `py -3`) for the Windows launcher; use an absolute path when you need a specific version.
287
+ Python 3.11 or newer is required; the automatically downloaded standalone Python is 3.13.15 and, like a system interpreter, is only used to bootstrap the isolated environment. Without an override, the plugin tries `python3` then `python` on macOS/Linux, and `python`, `py -3`, then `python3` on Windows, before falling back to the standalone download. A configured value is passed as one executable name or path, not as a shell command with arguments, so use `py` (not `py -3`) for the Windows launcher; use an absolute path when you need a specific version.
286
288
 
287
289
  Configure it in the Profile patch:
288
290
 
@@ -385,7 +387,7 @@ The updater revalidates the Profile before mutation, snapshots the original mani
385
387
  | The free service returns 429 | Wait for the `Retry-After` interval, or switch to your own endpoint when you need stable higher volume |
386
388
  | The image exceeds a size or pixel limit | Crop or resize it first; the error identifies whether bytes or decoded pixels caused the rejection |
387
389
  | A custom Credential is missing | Enter the API key in **Settings → Vision Toolkit** and confirm the Credential name matches the provider configuration |
388
- | First-time runtime setup fails | Check Python 3.11+, network or package-cache access, and disk permissions, then retry the model test in Settings |
390
+ | First-time runtime setup fails | The standalone-Python download needs network and disk access. Check connectivity or package-cache access, or install Python 3.11+ / configure `runtime.python` in Settings, then retry the model test |
389
391
  | Chrome is not found | Install Chrome, Chromium, or Edge. Only HTML screenshot rendering is unavailable; the other tools still work |
390
392
  | An artifact cannot be previewed | Use **Open file** or the workspace path in the result. Preview URLs exist only while the Web route is available |
391
393
 
package/README.zh.md CHANGED
@@ -146,7 +146,7 @@ dsh plugin --profile headless add @anionex/dsh-vision-toolkit
146
146
 
147
147
  重启正在运行的 Web Profile,打开 **设置 → 视觉工具**。默认免费服务已经配置好;你可以直接运行**测试视觉模型**确认连接。
148
148
 
149
- 首次启动会自动准备隔离运行环境,因此需要能访问 Python 包缓存或网络。普通安装不需要下载 `agent-vision-toolkit` 源码,也不需要设置本地路径。
149
+ 首次启动会自动准备隔离运行环境:插件优先使用系统已有的 Python 3.11+;如果系统没有,会自动从固定发布源下载一个带完整性校验的托管 Python(约 35MB,仅首次需要网络)。普通安装不需要下载 `agent-vision-toolkit` 源码,也不需要设置本地路径。
150
150
 
151
151
  ### 3. 粘贴图片,直接说你要做什么
152
152
 
@@ -275,15 +275,17 @@ API Key: https://agent-vision.anionex.me(自动填写)
275
275
 
276
276
  - DeepSeek Harness Web 或 Headless Profile。
277
277
  - Node.js `^22.19.0` 或 `>=24.0.0`。
278
- - Python 3.11+;插件默认自动创建隔离环境。
278
+ - Python 3.11+,通常无需预装:插件优先使用系统 Python;找不到时会自动下载固定版本托管 Python(3.13)并创建隔离环境,仅首次自动下载需要网络。
279
279
  - 只有 `vision_html_screenshot` 需要 Chrome、Chromium 或 Edge。
280
280
  - 图片需为 PNG、JPEG、GIF 或 WebP,并位于会话工作区、平台临时目录或明确允许的目录中。
281
281
 
282
282
  ### 配置 Python 运行时
283
283
 
284
+ 默认情况下插件会自动选择系统 Python 3.11+,找不到时下载托管 Python,普通用户无需配置本节。以下内容供自动发现失败、需要固定版本或使用外部运行时的高级场景参考。
285
+
284
286
  打包的 `managed` 运行时会创建自己的隔离虚拟环境。`runtime.python` 指定的是引导或刷新该环境时使用的 Python 解释器,并不会把 managed 运行时替换成系统解释器的全局 site-packages。当自动发现失败或机器上有多个 Python 时,应设置这个选项;`runtime.mode: external` 也使用该覆盖值。
285
287
 
286
- 要求 Python 3.11 或更高版本。不设置覆盖值时,插件在 macOS/Linux 上依次尝试 `python3`、`python`,在 Windows 上依次尝试 `python`、`py -3`、`python3`。手动配置的值会作为一个可执行文件名或路径传入,而不是作为带参数的 Shell 命令,因此 Windows 启动器应填写 `py`(不要填写 `py -3`);需要固定版本时,请填写绝对路径。
288
+ 要求 Python 3.11 或更高版本;自动下载的托管 Python 为 3.13.15,与系统 Python 一样只用于引导隔离环境。不设置覆盖值时,插件在 macOS/Linux 上依次尝试 `python3`、`python`,在 Windows 上依次尝试 `python`、`py -3`、`python3`,全部不可用时会自动下载托管 Python。手动配置的值会作为一个可执行文件名或路径传入,而不是作为带参数的 Shell 命令,因此 Windows 启动器应填写 `py`(不要填写 `py -3`);需要固定版本时,请填写绝对路径。
287
289
 
288
290
  在 Profile patch 中配置:
289
291
 
@@ -386,7 +388,7 @@ dsh plugin --profile web remove @anionex/dsh-vision-toolkit
386
388
  | 免费服务提示 429 | 按错误中的 `Retry-After` 等待后重试;如果需要稳定高额度,切换到自己的视觉端点 |
387
389
  | 图片过大或像素超限 | 先裁剪或缩放图片;错误会明确显示是字节还是像素限制 |
388
390
  | 自定义 Credential 缺失 | 在 **设置 → 视觉工具** 填写 API Key,并确认 Credential 名称与配置一致 |
389
- | 首次运行时准备失败 | 检查 Python 3.11+、网络或包缓存、磁盘权限,然后在 Settings 中重新测试 |
391
+ | 首次运行时准备失败 | 自动下载托管 Python 需要网络和磁盘权限;失败时检查网络或包缓存,也可以安装 Python 3.11+ 或在 Settings 中配置 `runtime.python`,然后重新测试 |
390
392
  | 找不到 Chrome | 安装 Chrome、Chromium 或 Edge;只有 HTML 截图不可用,其他工具不受影响 |
391
393
  | 产物无法预览 | 使用“打开文件”或结果中的工作区路径;预览 URL 只在 Web 路由可用时存在 |
392
394
 
@@ -0,0 +1,47 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "pythonVersion": "3.13.15",
4
+ "buildTag": "20260814",
5
+ "artifacts": {
6
+ "darwin-arm64": {
7
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-aarch64-apple-darwin-install_only_stripped.tar.gz",
8
+ "sha256": "6d472fc49a4d95e58214a992c4c92aa73fe2a935837a01a9a36bab0bec6d72f3",
9
+ "size": 25153453
10
+ },
11
+ "darwin-x64": {
12
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-x86_64-apple-darwin-install_only_stripped.tar.gz",
13
+ "sha256": "bf87354efcd9ae517da606fcda4e3a3f0d73a6f05ca7cba3c6d3c5270074bfc8",
14
+ "size": 24928435
15
+ },
16
+ "linux-x64": {
17
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
18
+ "sha256": "aaca2af2ab4d7b68a712660d1334c0cfd5ec13c0312ccd30c29122d8d0342320",
19
+ "size": 34818695
20
+ },
21
+ "linux-arm64": {
22
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz",
23
+ "sha256": "985efd78c1c6521b379f7c64c2a25e6a1130f07441d1af8be441aa05260886aa",
24
+ "size": 29229985
25
+ },
26
+ "linux-x64-musl": {
27
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-x86_64-unknown-linux-musl-install_only_stripped.tar.gz",
28
+ "sha256": "cec161969cbd60f306c366066701a7ab2d40b78718e7d372281cabe5743423e6",
29
+ "size": 27741839
30
+ },
31
+ "linux-arm64-musl": {
32
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-aarch64-unknown-linux-musl-install_only_stripped.tar.gz",
33
+ "sha256": "6e0de96340a1096cac81c9b95226c410c71cdf81e3d4419c536ccad9b57ae1f7",
34
+ "size": 28018801
35
+ },
36
+ "win32-x64": {
37
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-x86_64-pc-windows-msvc-install_only_stripped.tar.gz",
38
+ "sha256": "07c977bbe4abad07e3bbc314608633e6c74eab482a7bae81f4361cda970b45e6",
39
+ "size": 21936392
40
+ },
41
+ "win32-arm64": {
42
+ "url": "https://github.com/astral-sh/python-build-standalone/releases/download/20260814/cpython-3.13.15+20260814-aarch64-pc-windows-msvc-install_only_stripped.tar.gz",
43
+ "sha256": "f6bf0fa39ad3668dc1996052ec6eff58c54aa126bf203cf3728a47a830af2b5d",
44
+ "size": 20657919
45
+ }
46
+ }
47
+ }
package/lib/exposure.js CHANGED
@@ -127,6 +127,10 @@ export class VisionToolExposure {
127
127
  const listeners = [
128
128
  this.ctx.on('agent/created', ({ agent }) => { this.attach(agent); }),
129
129
  this.ctx.on('agent/disposed', ({ agent }) => { this.detach(agent); }),
130
+ this.ctx.on('session/event', (session, event) => {
131
+ if (event.type === 'step/end')
132
+ this.applyHideActivationForSession(session);
133
+ }),
130
134
  this.ctx.on('tools/result', (exec, result) => {
131
135
  if (result.isError === false
132
136
  && exec.name === 'skill'
@@ -175,24 +179,42 @@ export class VisionToolExposure {
175
179
  return { activated: false, tools: [...state.toolNames] };
176
180
  const definitions = this.createTools();
177
181
  const toolDisposers = [];
178
- let hideActivation;
179
182
  try {
180
183
  for (const definition of definitions)
181
184
  toolDisposers.push(agent.ctx.tools.register(definition));
182
- hideActivation = agent.ctx.tools.restrict({ deny: [VISION_TOOLKIT_ACTIVATE] });
185
+ // A Skill call and the bootstrap can be issued in the same model step.
186
+ // Restricting immediately would turn the still-in-flight bootstrap call
187
+ // into an UNKNOWN_TOOL error, so live sessions hide at step/end.
188
+ if (!this.isLiveSession(agent.session))
189
+ this.applyHideActivation(agent);
183
190
  }
184
191
  catch (error) {
185
- hideActivation?.();
186
192
  for (const dispose of toolDisposers.reverse())
187
193
  dispose();
188
194
  throw error;
189
195
  }
190
196
  state.active = true;
191
- state.hideActivation = hideActivation;
192
197
  state.toolDisposers = toolDisposers;
193
198
  state.toolNames = definitions.map(definition => definition.name);
194
199
  return { activated: true, tools: [...state.toolNames] };
195
200
  }
201
+ /** Whether the session is attached to the live SessionStore (production). */
202
+ isLiveSession(session) {
203
+ return this.ctx.sessions.get(session.id) === session;
204
+ }
205
+ applyHideActivationForSession(session) {
206
+ for (const agent of this.ctx.agents.list()) {
207
+ if (agent.session === session && this.states.get(agent)?.active === true) {
208
+ this.applyHideActivation(agent);
209
+ }
210
+ }
211
+ }
212
+ applyHideActivation(agent) {
213
+ const state = this.states.get(agent);
214
+ if (state === undefined || state.liftRestriction !== undefined)
215
+ return;
216
+ state.liftRestriction = agent.ctx.tools.restrict({ deny: [VISION_TOOLKIT_ACTIVATE] });
217
+ }
196
218
  detach(agent) {
197
219
  const state = this.states.get(agent);
198
220
  if (state === undefined)
@@ -206,7 +228,7 @@ export class VisionToolExposure {
206
228
  this.states.clear();
207
229
  }
208
230
  disposeState(state) {
209
- state.hideActivation?.();
231
+ state.liftRestriction?.();
210
232
  for (const dispose of state.toolDisposers.reverse())
211
233
  dispose();
212
234
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exposure.js","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"exposure.js","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"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"}
@@ -6,10 +6,16 @@
6
6
  * @module dsh-vision-toolkit/runtime-install
7
7
  */
8
8
  import { createHash, randomUUID } from 'node:crypto';
9
- import { lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, utimes, writeFile, } from 'node:fs/promises';
9
+ import { access, chmod, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, utimes, writeFile, } from 'node:fs/promises';
10
+ import { createWriteStream } from 'node:fs';
10
11
  import { homedir } from 'node:os';
11
12
  import { dirname, join, resolve } from 'node:path';
13
+ import { Transform } from 'node:stream';
14
+ import { pipeline } from 'node:stream/promises';
12
15
  import { fileURLToPath } from 'node:url';
16
+ import { x as extractTar } from 'tar';
17
+ import { EnvHttpProxyAgent } from 'undici';
18
+ import { request as undiciRequest } from 'undici';
13
19
  import { VisionToolkitError } from "./errors.js";
14
20
  import { UPSTREAM_COMMIT, UPSTREAM_REPOSITORY, UPSTREAM_VERSION } from "./version.js";
15
21
  const PACKAGE_ROOT = dirname(fileURLToPath(new URL('../package.json', import.meta.url)));
@@ -180,7 +186,271 @@ async function pythonMetadata(ctx, command, cwd) {
180
186
  return undefined;
181
187
  }
182
188
  }
183
- async function resolveBootstrapPython(ctx, configured, cwd) {
189
+ const PYTHON_BOOTSTRAP_MANIFEST_PATH = join(PACKAGE_ROOT, 'assets', 'python-bootstrap.json');
190
+ const PYTHON_DOWNLOAD_TIMEOUT_MS = 10 * 60 * 1000;
191
+ const PYTHON_DOWNLOAD_ATTEMPTS = 3;
192
+ async function readPythonBootstrapManifest() {
193
+ let parsed;
194
+ try {
195
+ parsed = JSON.parse(await readFile(PYTHON_BOOTSTRAP_MANIFEST_PATH, 'utf8'));
196
+ }
197
+ catch (error) {
198
+ throw new VisionToolkitError('runtime', `python bootstrap manifest is unreadable: ${PYTHON_BOOTSTRAP_MANIFEST_PATH}`, { cause: error });
199
+ }
200
+ if (typeof parsed !== 'object' || parsed === null) {
201
+ throw new VisionToolkitError('runtime', 'python bootstrap manifest is not an object');
202
+ }
203
+ const manifest = parsed;
204
+ if (manifest.schemaVersion !== 1
205
+ || typeof manifest.pythonVersion !== 'string'
206
+ || !/^3\.\d+\.\d+$/u.test(manifest.pythonVersion)
207
+ || typeof manifest.buildTag !== 'string'
208
+ || !/^\d{8}$/u.test(manifest.buildTag)
209
+ || typeof manifest.artifacts !== 'object'
210
+ || manifest.artifacts === null) {
211
+ throw new VisionToolkitError('runtime', 'python bootstrap manifest is invalid');
212
+ }
213
+ for (const [target, artifact] of Object.entries(manifest.artifacts)) {
214
+ if (typeof artifact !== 'object'
215
+ || artifact === null
216
+ || typeof artifact.url !== 'string'
217
+ || !artifact.url.startsWith('https://github.com/astral-sh/python-build-standalone/releases/download/')
218
+ || typeof artifact.sha256 !== 'string'
219
+ || !/^[a-f0-9]{64}$/u.test(artifact.sha256)
220
+ || !Number.isInteger(artifact.size)
221
+ || artifact.size <= 0) {
222
+ throw new VisionToolkitError('runtime', `python bootstrap manifest has an invalid artifact: ${target}`);
223
+ }
224
+ }
225
+ return manifest;
226
+ }
227
+ /** Map Node platform/arch to the pinned artifact name, including musl Linux. */
228
+ export function pythonBootstrapTarget(platform, arch, musl) {
229
+ return platform === 'linux' && musl ? `${platform}-${arch}-musl` : `${platform}-${arch}`;
230
+ }
231
+ async function runningMusl() {
232
+ if (process.platform !== 'linux')
233
+ return false;
234
+ try {
235
+ await access(join('/', 'etc', 'alpine-release'));
236
+ return true;
237
+ }
238
+ catch {
239
+ // Fall through to the loader-name probe.
240
+ }
241
+ const loader = process.arch === 'arm64' ? 'aarch64' : 'x86_64';
242
+ try {
243
+ await access(join('/', 'lib', `ld-musl-${loader}.so.1`));
244
+ return true;
245
+ }
246
+ catch {
247
+ return false;
248
+ }
249
+ }
250
+ const DOWNLOAD_HOSTS = new Set([
251
+ 'github.com',
252
+ 'objects.githubusercontent.com',
253
+ 'release-assets.githubusercontent.com',
254
+ ]);
255
+ async function defaultDownloadRequest(url, signal) {
256
+ const dispatcher = new EnvHttpProxyAgent();
257
+ let current = url;
258
+ try {
259
+ for (let redirects = 0;; redirects++) {
260
+ const host = new URL(current).hostname;
261
+ if (!DOWNLOAD_HOSTS.has(host))
262
+ throw new Error(`download redirected outside GitHub: ${host}`);
263
+ const response = await undiciRequest(current, {
264
+ dispatcher,
265
+ signal,
266
+ headers: { 'user-agent': 'dsh-vision-toolkit' },
267
+ });
268
+ if (response.statusCode >= 300 && response.statusCode < 400) {
269
+ const location = response.headers.location;
270
+ await response.body.dump().catch(() => { });
271
+ if (typeof location !== 'string' || location.length === 0 || redirects >= 5) {
272
+ throw new Error('download redirected too many times or without a Location header');
273
+ }
274
+ current = new URL(location, current).toString();
275
+ continue;
276
+ }
277
+ return {
278
+ statusCode: response.statusCode,
279
+ headers: response.headers,
280
+ body: response.body,
281
+ close: () => dispatcher.close().catch(() => { }),
282
+ };
283
+ }
284
+ }
285
+ catch (error) {
286
+ await dispatcher.close().catch(() => { });
287
+ throw error;
288
+ }
289
+ }
290
+ async function downloadBundledPythonOnce(artifact, destination, requestImpl = defaultDownloadRequest) {
291
+ const signal = AbortSignal.timeout(PYTHON_DOWNLOAD_TIMEOUT_MS);
292
+ let response;
293
+ try {
294
+ response = await requestImpl(artifact.url, signal);
295
+ }
296
+ catch (error) {
297
+ throw new Error(`download request failed: ${error instanceof Error ? error.message : String(error)}`);
298
+ }
299
+ try {
300
+ if (response.statusCode !== 200)
301
+ throw new Error(`download returned HTTP ${response.statusCode}`);
302
+ const hash = createHash('sha256');
303
+ let bytes = 0;
304
+ const hasher = new Transform({
305
+ transform(chunk, _encoding, callback) {
306
+ hash.update(chunk);
307
+ bytes += chunk.length;
308
+ callback(null, chunk);
309
+ },
310
+ });
311
+ await pipeline(response.body, hasher, createWriteStream(destination));
312
+ if (bytes !== artifact.size)
313
+ throw new Error(`size mismatch: expected ${artifact.size}, received ${bytes}`);
314
+ if (hash.digest('hex') !== artifact.sha256)
315
+ throw new Error('sha256 mismatch');
316
+ }
317
+ finally {
318
+ await response.close();
319
+ }
320
+ }
321
+ async function downloadBundledPython(artifact, destination, requestImpl) {
322
+ let lastError;
323
+ for (let attempt = 0; attempt < PYTHON_DOWNLOAD_ATTEMPTS; attempt++) {
324
+ try {
325
+ await downloadBundledPythonOnce(artifact, destination, requestImpl);
326
+ return;
327
+ }
328
+ catch (error) {
329
+ lastError = error;
330
+ if (attempt + 1 < PYTHON_DOWNLOAD_ATTEMPTS) {
331
+ await new Promise(resolveWait => setTimeout(resolveWait, 500 * 2 ** attempt));
332
+ }
333
+ }
334
+ }
335
+ throw lastError;
336
+ }
337
+ /**
338
+ * Cross-process directory lock with a stale-lock timeout and heartbeat,
339
+ * matching the managed-runtime lock semantics.
340
+ */
341
+ async function withDirectoryLock(lockPath, fn) {
342
+ const owner = randomUUID();
343
+ let acquired = false;
344
+ await mkdir(dirname(lockPath), { recursive: true });
345
+ try {
346
+ await mkdir(lockPath, { recursive: false });
347
+ acquired = true;
348
+ await writeFile(join(lockPath, 'owner'), `${owner}\n`, { flag: 'wx' });
349
+ }
350
+ catch (error) {
351
+ if (acquired) {
352
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
353
+ throw error;
354
+ }
355
+ if (error.code !== 'EEXIST')
356
+ throw error;
357
+ const started = Date.now();
358
+ while (Date.now() - started < PREPARE_TIMEOUT_MS) {
359
+ try {
360
+ const info = await stat(lockPath);
361
+ if (Date.now() - info.mtimeMs > LOCK_STALE_MS) {
362
+ await rm(lockPath, { recursive: true, force: true });
363
+ return withDirectoryLock(lockPath, fn);
364
+ }
365
+ }
366
+ catch {
367
+ return withDirectoryLock(lockPath, fn);
368
+ }
369
+ await new Promise(resolveWait => setTimeout(resolveWait, 250));
370
+ }
371
+ throw new VisionToolkitError('runtime', 'timed out waiting for another process to prepare the bundled Python');
372
+ }
373
+ const heartbeat = setInterval(() => {
374
+ const now = new Date();
375
+ void utimes(lockPath, now, now).catch(() => { });
376
+ }, LOCK_HEARTBEAT_MS);
377
+ heartbeat.unref();
378
+ try {
379
+ return await fn();
380
+ }
381
+ finally {
382
+ clearInterval(heartbeat);
383
+ try {
384
+ if ((await readFile(join(lockPath, 'owner'), 'utf8')).trim() === owner) {
385
+ await rm(lockPath, { recursive: true, force: true });
386
+ }
387
+ }
388
+ catch {
389
+ // The lock was already removed or replaced.
390
+ }
391
+ }
392
+ }
393
+ export async function acquireBundledPython(ctx, stateRoot, cwd, manifestOverride, requestImpl) {
394
+ const manifest = manifestOverride ?? await readPythonBootstrapManifest();
395
+ const target = pythonBootstrapTarget(process.platform, process.arch, await runningMusl());
396
+ const artifact = manifest.artifacts[target];
397
+ if (artifact === undefined) {
398
+ throw new VisionToolkitError('runtime', `no bundled Python ${manifest.pythonVersion} artifact for ${target}; install Python 3.11+ or configure runtime.python`);
399
+ }
400
+ const root = join(stateRoot, 'python-bootstrap', `${manifest.pythonVersion}-${target}`);
401
+ const interpreter = process.platform === 'win32' ? join(root, 'python.exe') : join(root, 'bin', 'python3');
402
+ const command = { program: interpreter, prefix: [], display: interpreter };
403
+ const cached = await pythonMetadata(ctx, command, cwd);
404
+ if (cached !== undefined)
405
+ return { command, version: cached.version };
406
+ await withDirectoryLock(`${root}.lock`, async () => {
407
+ const ready = await pythonMetadata(ctx, command, cwd);
408
+ if (ready !== undefined)
409
+ return;
410
+ const parent = dirname(root);
411
+ await mkdir(parent, { recursive: true });
412
+ await rm(root, { recursive: true, force: true });
413
+ const work = await mkdtemp(join(parent, '.python-bootstrap-'));
414
+ try {
415
+ const archive = join(work, 'python.tar.gz');
416
+ const extractDir = join(work, 'extract');
417
+ await mkdir(extractDir, { recursive: true });
418
+ try {
419
+ await downloadBundledPython(artifact, archive, requestImpl ?? defaultDownloadRequest);
420
+ }
421
+ catch (error) {
422
+ throw new VisionToolkitError('runtime', `bundled Python ${manifest.pythonVersion} could not be downloaded for ${target} (${error instanceof Error ? error.message : String(error)}); install Python 3.11+ or configure runtime.python`, { cause: error });
423
+ }
424
+ try {
425
+ await extractTar({ file: archive, cwd: extractDir, strip: 1 });
426
+ }
427
+ catch (error) {
428
+ throw new VisionToolkitError('runtime', `bundled Python ${manifest.pythonVersion} could not be extracted for ${target}`, { cause: error });
429
+ }
430
+ const extractedInterpreter = process.platform === 'win32'
431
+ ? join(extractDir, 'python.exe')
432
+ : join(extractDir, 'bin', 'python3');
433
+ try {
434
+ await access(extractedInterpreter);
435
+ }
436
+ catch (error) {
437
+ throw new VisionToolkitError('runtime', `bundled Python ${manifest.pythonVersion} for ${target} is missing its interpreter`, { cause: error });
438
+ }
439
+ if (process.platform !== 'win32')
440
+ await chmod(extractedInterpreter, 0o755);
441
+ await rename(extractDir, root);
442
+ }
443
+ finally {
444
+ await rm(work, { recursive: true, force: true });
445
+ }
446
+ });
447
+ const metadata = await pythonMetadata(ctx, command, cwd);
448
+ if (metadata === undefined) {
449
+ throw new VisionToolkitError('runtime', 'bundled Python did not start after extraction');
450
+ }
451
+ return { command, version: metadata.version };
452
+ }
453
+ export async function resolveBootstrapPython(ctx, configured, cwd, manifestOverride, requestImpl) {
184
454
  const candidates = configured === undefined
185
455
  ? process.platform === 'win32'
186
456
  ? [
@@ -199,9 +469,25 @@ async function resolveBootstrapPython(ctx, configured, cwd) {
199
469
  return { command, ...metadata };
200
470
  }
201
471
  }
202
- throw new VisionToolkitError('runtime', configured === undefined
203
- ? 'vision-toolkit requires Python 3.11 or newer; tried python3, python, and the Windows py launcher'
204
- : `vision-toolkit requires Python 3.11 or newer: ${configured}`);
472
+ if (configured !== undefined) {
473
+ throw new VisionToolkitError('runtime', `vision-toolkit requires Python 3.11 or newer: ${configured}`);
474
+ }
475
+ try {
476
+ const stateRoot = visionToolkitStateRoot();
477
+ await mkdir(stateRoot, { recursive: true });
478
+ const bundled = await acquireBundledPython(ctx, stateRoot, cwd, manifestOverride, requestImpl);
479
+ return {
480
+ command: bundled.command,
481
+ version: bundled.version,
482
+ major: Number.parseInt(bundled.version.split('.')[0] ?? '', 10),
483
+ minor: Number.parseInt(bundled.version.split('.')[1] ?? '', 10),
484
+ };
485
+ }
486
+ catch (error) {
487
+ if (error instanceof VisionToolkitError)
488
+ throw error;
489
+ throw new VisionToolkitError('runtime', 'vision-toolkit requires Python 3.11 or newer; tried python3, python, and the Windows py launcher, and automatic bundled-Python preparation failed', { cause: error });
490
+ }
205
491
  }
206
492
  /** Persistent per-DSH-home cache root shared by runtime and Web support files. */
207
493
  export function visionToolkitStateRoot() {