@poe-platform/safe-bash 0.1.821 → 0.1.823

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.
Files changed (93) hide show
  1. package/dist/safe-bash/chunks/{chunk-HNT2MR4W.js → chunk-363QOIDR.js} +2 -1
  2. package/dist/safe-bash/chunks/chunk-AGHV53VF.js +42 -0
  3. package/dist/safe-bash/chunks/{chunk-VF3Q2V3E.js → chunk-RAFD5ZAH.js} +1 -1
  4. package/dist/safe-bash/chunks/{chunk-67QE3JLU.js → chunk-TZRVK3NU.js} +1 -1
  5. package/dist/safe-bash/chunks/{chunk-IRJS54AA.js → chunk-VWLQDZFF.js} +20 -12
  6. package/dist/safe-bash/chunks/{chunk-NAETSXBW.js → chunk-ZJAXMB5M.js} +1 -1
  7. package/dist/safe-bash/command-chunks/{chunk-QX3WKBLI.js → chunk-36FV6BAG.js} +5 -3
  8. package/dist/safe-bash/command-chunks/{chunk-ZSHXZ3XG.js → chunk-3EAZRKI4.js} +7 -7
  9. package/dist/safe-bash/command-chunks/chunk-64HIOP7Q.js +993 -0
  10. package/dist/safe-bash/command-chunks/{chunk-WOT2YXBT.js → chunk-7ZQVMPSB.js} +213 -42
  11. package/dist/safe-bash/command-chunks/{chunk-GRFJKJFN.js → chunk-AVSINNZ6.js} +233 -3
  12. package/dist/safe-bash/command-chunks/{chunk-BZLLQWRK.js → chunk-E5CDHXFA.js} +2 -2
  13. package/dist/safe-bash/command-chunks/{chunk-RHXV2KG5.js → chunk-EO3RYGUZ.js} +1010 -58
  14. package/dist/safe-bash/command-chunks/{chunk-MCUDSE4H.js → chunk-EWECMOQ5.js} +1 -1
  15. package/dist/safe-bash/command-chunks/{chunk-RTUDDAAY.js → chunk-O3KFOFJM.js} +1 -1
  16. package/dist/safe-bash/command-chunks/{chunk-SLD5A7IL.js → chunk-OU5UEUZN.js} +1 -1
  17. package/dist/safe-bash/command-chunks/{chunk-RKAL5VFP.js → chunk-PLVFJ6FF.js} +255 -616
  18. package/dist/safe-bash/command-chunks/{chunk-CJIFJ45R.js → chunk-QBCEAYQT.js} +1 -1
  19. package/dist/safe-bash/command-chunks/{chunk-4Q3HP33O.js → chunk-RGKHTSUY.js} +146 -123
  20. package/dist/safe-bash/command-chunks/{chunk-I7MEH76L.js → chunk-UYNJF5QN.js} +1 -1
  21. package/dist/safe-bash/command-chunks/{chunk-IAHYG67O.js → chunk-V5ZBRM5R.js} +3 -3
  22. package/dist/safe-bash/command-chunks/{chunk-R4NPYXQX.js → chunk-WDTWJFPK.js} +320 -67
  23. package/dist/safe-bash/command-chunks/{chunk-GQV5RWMN.js → chunk-ZFXHSFTU.js} +1 -1
  24. package/dist/safe-bash/command-chunks/{prompt-tool-debug-ERJKK3XZ.js → prompt-tool-debug-3POFNRYP.js} +5 -5
  25. package/dist/safe-bash/commands/bc/index.browser.js +1 -1
  26. package/dist/safe-bash/commands/llm/index.browser.js +3 -1
  27. package/dist/safe-bash/commands/llm/index.js +1 -1
  28. package/dist/safe-bash/commands/python/executor.browser.js +12 -1
  29. package/dist/safe-bash/commands/python/executor.js +1 -1
  30. package/dist/safe-bash/commands/python/index.browser.js +23 -1
  31. package/dist/safe-bash/commands/python/index.js +1 -1
  32. package/dist/safe-bash/core.browser.js +2 -2
  33. package/dist/safe-bash/full-core.browser.js +30 -6
  34. package/dist/safe-bash/plugins/index.browser.js +3 -3
  35. package/dist/safe-bash/registry-entry.browser.js +2 -2
  36. package/dist/safe-bash/search.browser.js +1 -1
  37. package/dist/safe-bash/shell/runtime.d.ts +2 -2
  38. package/dist/safe-bash/shell/runtime.js +11 -2
  39. package/dist/safe-bash/shell/sync-extra-evaluators.js +12 -12
  40. package/dist/safe-bash/shell-entry.browser.js +2 -2
  41. package/dist/safe-bash-command-llm/collections.js +24 -65
  42. package/dist/safe-bash-command-llm/command.js +6 -6
  43. package/dist/safe-bash-command-llm/fragment-loaders.d.ts +3 -2
  44. package/dist/safe-bash-command-llm/index.d.ts +3 -2
  45. package/dist/safe-bash-command-llm/index.js +10 -8
  46. package/dist/safe-bash-command-llm/loader-provider.d.ts +25 -0
  47. package/dist/safe-bash-command-llm/openai-sse.js +2 -2
  48. package/dist/safe-bash-command-llm/openai.js +4 -4
  49. package/dist/safe-bash-command-llm/providers/index.js +4 -4
  50. package/dist/safe-bash-command-llm/providers/openai.js +4 -4
  51. package/dist/safe-bash-command-llm/python-unicode.d.ts +2 -0
  52. package/dist/safe-bash-command-llm/templates.d.ts +7 -2
  53. package/dist/safe-bash-command-llm/types.d.ts +7 -1
  54. package/dist/safe-bash-command-mcp/index.browser.js +3 -0
  55. package/dist/safe-bash-command-mcp/index.js +3 -0
  56. package/dist/safe-bash-command-patch/index.js +2 -2
  57. package/dist/safe-bash-command-patch/patch-publication.js +1 -1
  58. package/dist/safe-bash-command-patch/patch.js +2 -2
  59. package/dist/safe-bash-command-python/build-backend.d.ts +51 -0
  60. package/dist/safe-bash-command-python/build-dependencies.d.ts +12 -0
  61. package/dist/safe-bash-command-python/build-environment.d.ts +3 -0
  62. package/dist/safe-bash-command-python/build-wheel.d.ts +8 -0
  63. package/dist/safe-bash-command-python/executor.d.ts +12 -2
  64. package/dist/safe-bash-command-python/executor.js +26 -4
  65. package/dist/safe-bash-command-python/index.js +26 -4
  66. package/dist/safe-bash-command-python/jspi.js +2 -2
  67. package/dist/safe-bash-command-python/llm-assets-node.js +1 -1
  68. package/dist/safe-bash-command-python/llm-fragment-loader.d.ts +6 -0
  69. package/dist/safe-bash-command-python/llm-functions-loader.d.ts +5 -1
  70. package/dist/safe-bash-command-python/llm-loader-discovery.d.ts +13 -0
  71. package/dist/safe-bash-command-python/llm-package-manager.d.ts +7 -0
  72. package/dist/safe-bash-command-python/llm-template-loader.d.ts +6 -0
  73. package/dist/safe-bash-command-python/manifest.d.ts +13 -0
  74. package/dist/safe-bash-command-python/provisioning.d.ts +33 -5
  75. package/dist/safe-bash-command-python/provisioning.js +1 -1
  76. package/dist/safe-bash-command-python/source-environment.d.ts +9 -0
  77. package/dist/safe-bash-command-python/source-snapshot.d.ts +8 -0
  78. package/dist/safe-bash-command-python/worker.js +3 -185
  79. package/dist/safe-bash-csv-engine/index.js +1 -1
  80. package/dist/safe-bash-csv-engine/python-iso2022.d.ts +21 -0
  81. package/dist/safe-bash-csv-engine/python-utf7.d.ts +18 -0
  82. package/dist/safe-bash-csv-engine/text-decoder.d.ts +2 -0
  83. package/dist/safe-bash-csv-engine/text-decoder.js +7 -3
  84. package/dist/safe-bash-zip-engine/index.js +1 -1
  85. package/dist/safe-bash-zip-engine/zip/repair.js +1 -1
  86. package/dist/safe-bash-zip-engine/zip/sfx.js +1 -1
  87. package/dist/safe-bash-zip-engine/zip-format.d.ts +3 -1
  88. package/dist/safe-bash-zip-engine/zip-format.js +1 -1
  89. package/dist/safe-playwright-cloudflare/shell-browser-resource.d.ts +1 -1
  90. package/dist/safe-playwright-cloudflare/shell-browser-resource.js +35 -20
  91. package/package.json +3 -3
  92. package/dist/safe-bash/chunks/chunk-PSD2RSOS.js +0 -31
  93. package/dist/safe-bash/command-chunks/chunk-5EFNQ6FY.js +0 -458
@@ -1,9 +1,165 @@
1
- // packages/safe-bash-command-python/src/provisioning.ts
1
+ // packages/safe-bash-command-python/src/package-download.ts
2
+ import { FsError } from "../../safe-bash-contracts/index.js";
3
+ import { resolvePath } from "../../safe-bash-contracts/path.js";
4
+ import { interruptible } from "../../safe-bash-contracts/runtime-control.js";
5
+ import { yieldTurn as yieldTurn2 } from "../../safe-bash-contracts/yield.js";
6
+
7
+ // packages/safe-bash-command-python/src/package-file.ts
8
+ import { yieldTurn } from "../../safe-bash-contracts/yield.js";
2
9
  import { sha256 } from "@noble/hashes/sha2.js";
10
+ import { compareIdentity, compareFileVersion } from "@poe-platform/safe-fs/core";
11
+ async function openPythonPackageFile({ fs, signal }, path, maxBytes, expected) {
12
+ const settings = { signal };
13
+ const capabilities = await fs.capabilitiesFor?.(path, settings) ?? fs.capabilities;
14
+ if (!capabilities.retainedRead || !fs.openReadFile) return void 0;
15
+ const file = await fs.openReadFile(path, settings);
16
+ try {
17
+ signal.throwIfAborted();
18
+ const state = { ...await file.stat(settings) };
19
+ if (expected && (compareIdentity(expected, state) !== "same" || !compareFileVersion(expected, state))) throw new Error("Canonical Python wheel changed");
20
+ if (state.type !== "file" || !Number.isSafeInteger(state.size) || state.size < 0) throw new Error("Invalid canonical Python wheel");
21
+ if (state.size > maxBytes) throw new Error("Canonical package exceeds maxDownloadBytes");
22
+ const check = async () => {
23
+ signal.throwIfAborted();
24
+ const current = await file.stat(settings);
25
+ if (compareIdentity(state, current) !== "same" || !compareFileVersion(state, current)) throw new Error("Canonical Python wheel changed");
26
+ };
27
+ const read = async (offset, length) => {
28
+ await check();
29
+ const count = Math.min(length, Math.max(0, state.size - offset));
30
+ if (!count) return new Uint8Array(0);
31
+ const bytes = Uint8Array.from(await file.read(offset, count, settings));
32
+ if (!bytes.length || bytes.length > count) throw new Error("Invalid canonical package read");
33
+ await check();
34
+ return bytes;
35
+ };
36
+ const hash = sha256.create();
37
+ for (let offset = 0, pulls = 0; offset < state.size; ) {
38
+ if (++pulls % 16 === 0) await yieldTurn(signal);
39
+ const bytes = await read(offset, 65536);
40
+ hash.update(bytes);
41
+ offset += bytes.length;
42
+ }
43
+ await check();
44
+ return { key: Array.from(hash.digest(), (byte) => byte.toString(16).padStart(2, "0")).join(""), size: state.size, read, close: () => file.close() };
45
+ } catch (error) {
46
+ await file.close();
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ // packages/safe-bash-command-python/src/package-download.ts
52
+ var serial = 0;
53
+ async function stagePythonPackage(context, directory, body, maxBytes, progress, verify, publish) {
54
+ const { fs, signal } = context, settings = { signal };
55
+ const caps = await fs.capabilitiesFor?.(directory, settings) ?? fs.capabilities;
56
+ if (!caps.retainedStagingWrite || !caps.retainedStagingCleanup || !caps.retainedRead || !fs.createStagedFile || !fs.openReadFile || publish && (!caps.atomicFileStaging || !fs.publishStagedFile)) return void 0;
57
+ const parent = await fs.stat(directory, settings);
58
+ let stage;
59
+ for (let attempt = 0; attempt < 16; attempt++) {
60
+ try {
61
+ stage = await fs.createStagedFile(resolvePath(directory, ".python-package-" + ++serial), "wheel", { type: "file", data: new Uint8Array(0) }, { parent, mode: 384, retainCleanup: true, signal });
62
+ break;
63
+ } catch (error) {
64
+ signal.throwIfAborted();
65
+ if (!(error instanceof FsError) || error.code !== "EEXIST") throw error;
66
+ }
67
+ }
68
+ if (!stage) throw new Error("Python package staging names exhausted");
69
+ const owner = stage;
70
+ let artifact;
71
+ let closing;
72
+ const close = () => closing ??= (async () => {
73
+ try {
74
+ await artifact?.close();
75
+ } finally {
76
+ try {
77
+ await owner.cleanup?.remove();
78
+ } finally {
79
+ await owner.cleanup?.close();
80
+ }
81
+ }
82
+ })();
83
+ try {
84
+ if (!owner.cleanup || !owner.writer) throw new Error("Python package staging handles missing");
85
+ let count = 0;
86
+ const iterator = body[Symbol.asyncIterator]();
87
+ let ended = false;
88
+ try {
89
+ while (true) {
90
+ const next = await interruptible(Promise.resolve().then(() => {
91
+ signal.throwIfAborted();
92
+ return iterator.next();
93
+ }), signal);
94
+ signal.throwIfAborted();
95
+ if (next.done) {
96
+ ended = true;
97
+ break;
98
+ }
99
+ const bytes = next.value;
100
+ if (!(bytes instanceof Uint8Array)) throw new TypeError("Python package body must yield bytes");
101
+ if (count + bytes.length > maxBytes) throw new Error("Package download exceeds maxDownloadBytes");
102
+ for (let offset = 0; offset < bytes.length; offset += 65536) {
103
+ const chunk = Uint8Array.from(bytes.subarray(offset, offset + 65536));
104
+ await owner.writer.write(chunk, settings);
105
+ count += chunk.length;
106
+ await yieldTurn2(signal);
107
+ }
108
+ if (bytes.length) progress(count);
109
+ }
110
+ } finally {
111
+ if (!ended) {
112
+ const returned = Promise.resolve().then(() => iterator.return?.());
113
+ if (signal.aborted) void returned.catch(() => {
114
+ });
115
+ else await interruptible(returned, signal);
116
+ }
117
+ }
118
+ const stat = await owner.writer.finish(settings);
119
+ if (stat.size !== count) throw new Error("Python package staging size mismatch");
120
+ artifact = await openPythonPackageFile(context, owner.file.path, maxBytes, stat);
121
+ if (!artifact) throw new Error("Python package retained reads unavailable");
122
+ verify(artifact.key);
123
+ if (publish) {
124
+ const key = artifact.key, path = publish(key);
125
+ await artifact.close();
126
+ artifact = void 0;
127
+ let destination = null;
128
+ try {
129
+ destination = await fs.lstat(path, settings);
130
+ } catch (error) {
131
+ if (!(error instanceof FsError) || error.code !== "ENOENT") throw error;
132
+ }
133
+ await fs.publishStagedFile({ ...owner, file: { ...owner.file, stat } }, path, { parent, destination, signal });
134
+ artifact = await openPythonPackageFile(context, path, maxBytes);
135
+ if (!artifact || artifact.key !== key) throw new Error("Published Python package integrity mismatch");
136
+ }
137
+ return { ...artifact, close };
138
+ } catch (error) {
139
+ try {
140
+ await close();
141
+ } catch (cleanup) {
142
+ throw new AggregateError([error, cleanup], error instanceof Error ? error.message : "Python package download failed");
143
+ }
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ // packages/safe-bash-command-python/src/provisioning.ts
149
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
3
150
  import { resolvePath as resolve, dirname } from "../../safe-bash-contracts/path.js";
4
151
  import { inheritYieldCheckpoint } from "../../safe-bash-contracts/yield.js";
5
152
 
6
153
  // packages/safe-bash-command-python/src/manifest.ts
154
+ function readPackageManifest(value) {
155
+ const strings = (values) => Array.isArray(values) && values.every((item) => typeof item === "string");
156
+ if (Array.isArray(value)) return strings(value) ? value : void 0;
157
+ if (!value || typeof value !== "object") return;
158
+ const record = value;
159
+ if (![1, 2].includes(record.version) || Object.keys(value).length !== record.version + 1) return;
160
+ if (record.version === 2 && (!Array.isArray(record.records) || !record.records.every((row) => Array.isArray(row) && row.length === 5 && row.every((part, index) => index < 3 ? typeof part === "string" : strings(part))))) return;
161
+ return strings(record.installed) ? record.installed : void 0;
162
+ }
7
163
  var PythonPackageConflictError = class extends Error {
8
164
  code = "EPACKAGECONFLICT";
9
165
  retryable = true;
@@ -115,7 +271,12 @@ var pythonDocumentPackages = Object.freeze([
115
271
  var encoder = new TextEncoder();
116
272
  var decoder = new TextDecoder();
117
273
  function digest(value) {
118
- return Array.from(sha256(value), (byte) => byte.toString(16).padStart(2, "0")).join("");
274
+ return Array.from(sha2562(value), (byte) => byte.toString(16).padStart(2, "0")).join("");
275
+ }
276
+ function validDigest(value) {
277
+ if (typeof value !== "string" || value.length !== 64) return false;
278
+ for (const char of value) if (!"0123456789abcdef".includes(char)) return false;
279
+ return true;
119
280
  }
120
281
  function failure(message, cause) {
121
282
  return Object.assign(new Error(message, { cause }), { code: "EPACKAGE" });
@@ -140,35 +301,49 @@ function createPythonPackageEnvironment(options = {}) {
140
301
  const manifestKey = options.manifestStore ? pythonPackageRuntimeKey + "-environment-" + digest(encoder.encode(JSON.stringify(options.scope))) : pythonPackageRuntimeKey + "-environment";
141
302
  if (options.profile !== void 0 && options.profile !== "documents") throw new TypeError("Unknown Python package profile");
142
303
  const maxBytes = options.maxDownloadBytes ?? Infinity;
143
- if (options.maxDownloadBytes !== void 0 && (!Number.isSafeInteger(maxBytes) || maxBytes < 1)) throw new RangeError("maxDownloadBytes must be a positive integer");
144
304
  const maxManifestBytes = options.maxManifestBytes ?? Infinity;
145
305
  const maxMetadataBytes = options.maxMetadataBytes ?? Infinity;
146
- for (const name of ["maxManifestBytes", "maxMetadataBytes", "maxRequirementBytes"]) {
306
+ for (const name of ["maxDownloadBytes", "maxManifestBytes", "maxMetadataBytes", "maxRequirementBytes", "maxCacheBytes"]) {
147
307
  const value = options[name];
148
308
  if (value !== void 0 && (!Number.isSafeInteger(value) || value < 1)) throw new RangeError(`${name} must be a positive integer`);
149
309
  }
310
+ const cacheRecord = (key, headers) => {
311
+ const bytes = encoder.encode(JSON.stringify({ digest: key, headers }));
312
+ if (bytes.length > maxMetadataBytes) throw failure("Python package cache metadata exceeds maxMetadataBytes");
313
+ return bytes;
314
+ };
150
315
  const maxCacheBytes = options.maxCacheBytes;
151
- if (maxCacheBytes !== void 0 && (!Number.isSafeInteger(maxCacheBytes) || maxCacheBytes < 1)) throw new RangeError("maxCacheBytes must be a positive integer");
152
316
  const defaultCache = createPythonPackageCache(maxCacheBytes === void 0 ? {} : { maxBytes: maxCacheBytes });
153
317
  const sessions = /* @__PURE__ */ new Map();
154
318
  const controller = new AbortController();
155
319
  const pending = /* @__PURE__ */ new Set();
156
- let disposed = false;
157
- let disposing;
158
- const admit = (operation) => (...args) => {
159
- if (disposed) return Promise.reject(failure("Python package environment is disposed"));
160
- const work = Promise.resolve().then(() => {
161
- if (disposed) throw failure("Python package environment is disposed");
162
- return operation(...args);
163
- });
320
+ let cleanupFailure;
321
+ const track = (work, cleanup = false) => {
164
322
  pending.add(work);
165
- void work.then(() => {
166
- pending.delete(work);
167
- }, () => {
323
+ void work.then(() => pending.delete(work), (error) => {
324
+ if (cleanup) cleanupFailure ??= { error };
168
325
  pending.delete(work);
169
326
  });
170
327
  return work;
171
328
  };
329
+ function release(session, all = false) {
330
+ const artifacts = all ? [...session.retained.values()] : [];
331
+ if (all) session.retained.clear();
332
+ if (session.opened) {
333
+ artifacts.push(session.opened);
334
+ session.opened = void 0;
335
+ }
336
+ if (!artifacts.length) return session.retiring ?? Promise.resolve();
337
+ return track(session.retiring = Promise.allSettled([session.retiring, ...artifacts.map((artifact) => Promise.resolve().then(() => artifact.close?.()))]).then((results) => {
338
+ for (const result of results) if (result.status === "rejected") throw result.reason;
339
+ }), true);
340
+ }
341
+ let disposed = false;
342
+ let disposing;
343
+ const admit = (operation) => (...args) => track(Promise.resolve().then(() => {
344
+ if (disposed) throw failure("Python package environment is disposed");
345
+ return operation(...args);
346
+ }));
172
347
  let counter = 0;
173
348
  let committing = Promise.resolve();
174
349
  async function prepare(input) {
@@ -176,20 +351,20 @@ function createPythonPackageEnvironment(options = {}) {
176
351
  const signal = AbortSignal.any([input.signal, controller.signal, invocation.signal]);
177
352
  inheritYieldCheckpoint(input.signal, signal);
178
353
  const context = { ...input, signal };
179
- context.signal.throwIfAborted();
354
+ signal.throwIfAborted();
180
355
  const directory = options.cacheDirectory === void 0 ? void 0 : resolve(context.cwd, options.cacheDirectory, pythonPackageRuntimeKey);
181
356
  const cache = options.cache ?? (directory === void 0 ? defaultCache : {
182
357
  async get(key) {
183
358
  try {
184
- return await context.fs.readFile(resolve(directory, key), { signal: context.signal });
359
+ return await context.fs.readFile(resolve(directory, key), { signal });
185
360
  } catch (error) {
186
361
  if (missing(error)) return void 0;
187
362
  throw error;
188
363
  }
189
364
  },
190
365
  async set(key, bytes) {
191
- await context.fs.mkdir(directory, { recursive: true, signal: context.signal });
192
- await context.fs.writeFile(resolve(directory, key), bytes, { signal: context.signal });
366
+ await context.fs.mkdir(directory, { recursive: true, signal });
367
+ await context.fs.writeFile(resolve(directory, key), bytes, { signal });
193
368
  }
194
369
  });
195
370
  const manifestCache = options.cacheDirectory === void 0 ? defaultCache : cache;
@@ -198,15 +373,15 @@ function createPythonPackageEnvironment(options = {}) {
198
373
  try {
199
374
  snapshot = await options.manifestStore.get(manifestKey, context);
200
375
  } catch (error) {
201
- context.signal.throwIfAborted();
376
+ signal.throwIfAborted();
202
377
  throw failure("Cannot read Python package environment manifest", error);
203
378
  }
204
379
  }
205
- context.signal.throwIfAborted();
380
+ signal.throwIfAborted();
206
381
  if (snapshot !== void 0 && (typeof snapshot !== "object" || snapshot === null || typeof snapshot.revision !== "string" || !snapshot.revision || snapshot.revision.length > 1024 || !(snapshot.bytes instanceof Uint8Array))) throw failure("Invalid Python package manifest snapshot");
207
382
  const manifestRevision = snapshot?.revision;
208
383
  const stored = options.manifestStore ? snapshot?.bytes : await manifestCache.get(manifestKey);
209
- context.signal.throwIfAborted();
384
+ signal.throwIfAborted();
210
385
  if (stored && stored.length > maxManifestBytes) throw failure("Python package manifest exceeds maxManifestBytes");
211
386
  const manifest = stored === void 0 ? "" : decoder.decode(stored);
212
387
  let previous;
@@ -215,15 +390,18 @@ function createPythonPackageEnvironment(options = {}) {
215
390
  } catch {
216
391
  throw failure("Invalid Python package environment manifest");
217
392
  }
218
- if (!Array.isArray(previous) || previous.some((value) => typeof value !== "string")) throw failure("Invalid Python package environment manifest");
219
- const requirements = [...previous, ...options.profile === "documents" ? pythonDocumentPackages : [], ...options.requirements ?? [], ...context.requirements ?? []].map((value) => normalizeRequirement(value, context.cwd));
393
+ const saved = readPackageManifest(previous);
394
+ if (!saved) throw failure("Invalid Python package environment manifest");
395
+ const legacy = Array.isArray(previous);
396
+ const restore = saved.map((value) => normalizeRequirement(value, context.cwd));
397
+ const requirements = [...options.profile === "documents" ? pythonDocumentPackages : [], ...options.requirements ?? [], ...context.requirements ?? []].map((value) => normalizeRequirement(value, context.cwd));
220
398
  for (const file of [...options.requirementFiles ?? [], ...context.requirementFiles ?? []]) {
221
399
  const path = resolve(context.cwd, file);
222
400
  let source;
223
401
  try {
224
- source = decoder.decode(await context.fs.readFile(path, { signal: context.signal, ...options.maxRequirementBytes === void 0 ? {} : { maxBytes: options.maxRequirementBytes } }));
402
+ source = decoder.decode(await context.fs.readFile(path, { signal, ...options.maxRequirementBytes === void 0 ? {} : { maxBytes: options.maxRequirementBytes } }));
225
403
  } catch (error) {
226
- context.signal.throwIfAborted();
404
+ signal.throwIfAborted();
227
405
  throw failure(`Cannot read Python requirements ${path}: ${error instanceof Error ? error.message : String(error)}`);
228
406
  }
229
407
  for (const line of source.split("\n")) {
@@ -234,20 +412,23 @@ function createPythonPackageEnvironment(options = {}) {
234
412
  requirements.push(normalizeRequirement(text, dirname(path)));
235
413
  }
236
414
  }
237
- context.signal.throwIfAborted();
415
+ const requested = [...new Set(await options.prepareRequirements?.(requirements, context) ?? requirements)];
416
+ signal.throwIfAborted();
238
417
  const session = String(++counter);
239
- const unique = [...new Set(requirements)];
418
+ const unique = [.../* @__PURE__ */ new Set([...restore, ...requested])];
240
419
  const aborted = () => {
241
420
  const current = sessions.get(session);
242
421
  if (current) {
243
422
  current.closed = true;
244
- current.opened.clear();
423
+ void release(current, true);
245
424
  }
246
- sessions.delete(session);
247
425
  };
248
- sessions.set(session, { ...context, cache, manifestCache, manifestRevision, controller: invocation, offline: context.offline ?? options.offline ?? false, requirements: unique, opened: /* @__PURE__ */ new Map(), opening: false, closed: false, manifest, aborted });
249
- context.signal.addEventListener("abort", aborted, { once: true });
250
- return { session, requirements: unique, offline: context.offline ?? options.offline ?? false };
426
+ const offline = context.offline ?? options.offline ?? false;
427
+ sessions.set(session, { ...context, cacheDirectory: directory, noCache: context.noCache ?? options.noCache ?? false, cache, manifestCache, manifestRevision, controller: invocation, offline, requirements: unique, opening: false, retained: /* @__PURE__ */ new Map(), closed: false, manifest, aborted });
428
+ signal.addEventListener("abort", aborted, { once: true });
429
+ const controls = {};
430
+ for (const key of ["pre", "upgrade", "forceReinstall"]) if (context[key] ?? options[key]) controls[key] = true;
431
+ return { session, requirements: unique, restore, requested, legacy, records: previous.records, ...controls, ...input.uninstall ? { uninstall: input.uninstall } : {}, offline };
251
432
  }
252
433
  async function dispatch(op, args, _context) {
253
434
  _context.signal.throwIfAborted();
@@ -255,10 +436,13 @@ function createPythonPackageEnvironment(options = {}) {
255
436
  if (!session) throw failure("Python package session is closed");
256
437
  checkSession(session);
257
438
  if (op === "package-commit") {
439
+ await release(session, true);
440
+ checkSession(session);
258
441
  const pinned = args[1];
259
- if (!Array.isArray(pinned) || pinned.some((value) => typeof value !== "string")) throw failure("Invalid installed package manifest");
260
- const merged = [.../* @__PURE__ */ new Set([...session.requirements, ...pinned])];
261
- const manifestBytes = encoder.encode(JSON.stringify(merged));
442
+ const saved = readPackageManifest(pinned);
443
+ if (!saved) throw failure("Invalid installed package manifest");
444
+ const state = Array.isArray(pinned) ? [.../* @__PURE__ */ new Set([...session.requirements, ...saved])] : { ...pinned, installed: [...new Set(saved)] };
445
+ const manifestBytes = encoder.encode(JSON.stringify(state));
262
446
  if (manifestBytes.length > maxManifestBytes) throw failure("Python package manifest exceeds maxManifestBytes");
263
447
  const commit = committing.then(async () => {
264
448
  checkSession(session);
@@ -282,46 +466,86 @@ function createPythonPackageEnvironment(options = {}) {
282
466
  options.onProgress?.({ phase: "installed" });
283
467
  return null;
284
468
  }
285
- if (op === "package-read") {
286
- const bytes = session.opened.get(String(args[1]));
287
- const offset = args[2];
288
- const length = args[3];
289
- if (!bytes || !Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 1 || length > 65536) throw failure("Invalid package chunk request");
290
- return Array.from(bytes.subarray(offset, offset + length));
469
+ if (op === "package-read" || op === "package-read-retained") {
470
+ const artifact = op === "package-read" ? session.opened : session.retained.get(String(args[1])), offset = args[2], length = args[3];
471
+ if (!artifact || op === "package-read" && artifact.key !== String(args[1]) || !Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 1 || length > 65536) throw failure("Invalid package chunk request");
472
+ const bytes = await artifact.read(offset, length);
473
+ checkSession(session);
474
+ return Array.from(bytes);
475
+ }
476
+ if (op === "package-retain") {
477
+ const artifact = session.opened;
478
+ if (!artifact || artifact.key !== String(args[1])) throw failure("Invalid package retention request");
479
+ const token = String(++counter);
480
+ session.retained.set(token, artifact);
481
+ session.opened = void 0;
482
+ return { token, key: artifact.key, size: artifact.size };
291
483
  }
292
484
  if (op === "package-close") {
293
- session.opened.delete(String(args[1]));
485
+ if (session.opened?.key === String(args[1])) await release(session);
294
486
  return null;
295
487
  }
296
488
  if (op !== "package-open" || typeof args[1] !== "string") throw failure("Invalid package operation");
297
489
  if (session.opening) throw failure("Package download already in progress");
298
490
  session.opening = true;
299
- session.opened.clear();
300
491
  try {
492
+ await release(session);
493
+ checkSession(session);
301
494
  const url = args[1];
302
495
  const expected = args[2];
303
- if (expected !== void 0 && expected !== null && (typeof expected !== "string" || expected.length !== 64 || Array.from(expected).some((c) => !"0123456789abcdef".includes(c)))) throw failure("Invalid SHA-256 package integrity value");
496
+ const verifyIntegrity = (key2) => {
497
+ if (expected && key2 !== expected) throw failure(`Package integrity mismatch: ${url}`);
498
+ };
499
+ if (expected !== void 0 && expected !== null && !validDigest(expected)) throw failure("Invalid SHA-256 package integrity value");
304
500
  const address = pythonPackageRuntimeKey + "-url-" + digest(encoder.encode(url));
305
501
  const canonicalWheel = url.startsWith("file:") || url.startsWith("emfs:");
306
- const metadata = canonicalWheel ? void 0 : await session.cache.get(address);
502
+ const metadata = canonicalWheel || session.noCache || args[3] === "metadata" && !session.offline ? void 0 : await session.cache.get(address);
307
503
  checkSession(session);
308
- let bytes;
504
+ let key = "", bytes;
309
505
  let headers = [];
506
+ const adopt = async (artifact, ready) => {
507
+ session.opened = artifact;
508
+ try {
509
+ checkSession(session);
510
+ await ready();
511
+ checkSession(session);
512
+ return { key: artifact.key, size: artifact.size, headers };
513
+ } catch (error) {
514
+ await release(session);
515
+ throw error;
516
+ }
517
+ };
310
518
  if (metadata) {
311
519
  if (metadata.length > maxMetadataBytes) throw failure("Python package cache metadata exceeds maxMetadataBytes");
312
520
  let record;
313
521
  try {
314
522
  record = JSON.parse(decoder.decode(metadata));
315
523
  } catch {
316
- throw failure(`Invalid package cache metadata: ${url}`);
317
524
  }
318
- if (typeof record !== "object" || record === null || typeof record.digest !== "string" || record.digest.length !== 64 || Array.from(record.digest).some((char) => !"0123456789abcdef".includes(char)) || !Array.isArray(record.headers) || record.headers.some((pair) => !Array.isArray(pair) || pair.length !== 2 || pair.some((value) => typeof value !== "string"))) throw failure(`Invalid package cache metadata: ${url}`);
319
- bytes = await session.cache.get(pythonPackageRuntimeKey + "-sha256-" + record.digest);
525
+ if (typeof record !== "object" || record === null || !validDigest(record.digest) || !Array.isArray(record.headers) || record.headers.some((pair) => !Array.isArray(pair) || pair.length !== 2 || pair.some((value2) => typeof value2 !== "string"))) throw failure(`Invalid package cache metadata: ${url}`);
320
526
  headers = record.headers;
527
+ let absent = false;
528
+ if (session.cacheDirectory) {
529
+ let artifact;
530
+ try {
531
+ artifact = await openPythonPackageFile(session, resolve(session.cacheDirectory, pythonPackageRuntimeKey + "-sha256-" + record.digest), maxBytes);
532
+ } catch (error) {
533
+ if (!missing(error)) throw error;
534
+ absent = true;
535
+ }
536
+ if (artifact) {
537
+ return await adopt(artifact, () => {
538
+ if (artifact.key !== record.digest) throw failure(`Package cache integrity mismatch: ${url}`);
539
+ verifyIntegrity(artifact.key);
540
+ options.onProgress?.({ phase: "cached", url, bytes: artifact.size });
541
+ });
542
+ }
543
+ }
544
+ bytes = absent ? void 0 : await session.cache.get(pythonPackageRuntimeKey + "-sha256-" + record.digest);
321
545
  checkSession(session);
322
546
  if (bytes && bytes.length > maxBytes) throw failure("Cached package exceeds maxDownloadBytes");
323
547
  if (bytes) bytes = Uint8Array.from(bytes);
324
- if (bytes && digest(bytes) !== record.digest) throw failure(`Package cache integrity mismatch: ${url}`);
548
+ if (bytes && (key = digest(bytes)) !== record.digest) throw failure(`Package cache integrity mismatch: ${url}`);
325
549
  if (bytes) options.onProgress?.({ phase: "cached", url, bytes: bytes.length });
326
550
  }
327
551
  if (!bytes) {
@@ -329,7 +553,11 @@ function createPythonPackageEnvironment(options = {}) {
329
553
  const path = new URL(url);
330
554
  if (path.host && path.host !== "localhost") throw failure("Local wheels must use the canonical filesystem");
331
555
  try {
332
- bytes = await session.fs.readFile(decodeURIComponent(path.pathname), { signal: session.signal, ...Number.isFinite(maxBytes) ? { maxBytes } : {} });
556
+ const artifact = await openPythonPackageFile(session, decodeURIComponent(path.pathname), maxBytes);
557
+ if (artifact) {
558
+ return await adopt(artifact, () => verifyIntegrity(artifact.key));
559
+ }
560
+ bytes = Uint8Array.from(await session.fs.readFile(decodeURIComponent(path.pathname), { signal: session.signal, ...Number.isFinite(maxBytes) ? { maxBytes } : {} }));
333
561
  } catch (error) {
334
562
  checkSession(session);
335
563
  throw failure(`Cannot read canonical Python wheel ${path.pathname}: ${error instanceof Error ? error.message : String(error)}`);
@@ -367,6 +595,29 @@ function createPythonPackageEnvironment(options = {}) {
367
595
  const length = headers.find(([key2]) => key2.toLowerCase() === "content-length")?.[1];
368
596
  const total = length === void 0 || encoding !== void 0 && encoding !== "identity" ? void 0 : Number(length);
369
597
  if (total !== void 0 && Number.isFinite(total) && total > maxBytes) throw failure("Package download exceeds maxDownloadBytes");
598
+ const progress = (count2) => options.onProgress?.({ phase: "download", url, bytes: count2, ...typeof total === "number" && Number.isSafeInteger(total) ? { totalBytes: total } : {} });
599
+ if (session.cacheDirectory || session.noCache) {
600
+ const directory = session.cacheDirectory ?? session.cwd;
601
+ if (session.cacheDirectory) await session.fs.mkdir(directory, { recursive: true, signal: session.signal });
602
+ let metadataBytes;
603
+ const artifact = await stagePythonPackage(
604
+ session,
605
+ directory,
606
+ response.body,
607
+ maxBytes,
608
+ progress,
609
+ (key2) => {
610
+ verifyIntegrity(key2);
611
+ if (!session.noCache) metadataBytes = cacheRecord(key2, headers);
612
+ },
613
+ session.noCache ? void 0 : (key2) => resolve(directory, pythonPackageRuntimeKey + "-sha256-" + key2)
614
+ );
615
+ if (artifact) {
616
+ return await adopt(artifact, async () => {
617
+ if (!session.noCache) await session.cache.set(address, metadataBytes);
618
+ });
619
+ }
620
+ }
370
621
  const chunks = [];
371
622
  let count = 0;
372
623
  for await (const chunk of response.body) {
@@ -375,7 +626,7 @@ function createPythonPackageEnvironment(options = {}) {
375
626
  count += chunk.length;
376
627
  if (count > maxBytes) throw failure("Package download exceeds maxDownloadBytes");
377
628
  chunks.push(Uint8Array.from(chunk));
378
- options.onProgress?.({ phase: "download", url, bytes: count, ...typeof total === "number" && Number.isSafeInteger(total) ? { totalBytes: total } : {} });
629
+ progress(count);
379
630
  }
380
631
  bytes = new Uint8Array(count);
381
632
  let offset = 0;
@@ -390,19 +641,18 @@ function createPythonPackageEnvironment(options = {}) {
390
641
  }
391
642
  }
392
643
  checkSession(session);
393
- if (expected && digest(bytes) !== expected) throw failure(`Package integrity mismatch: ${url}`);
394
- const hash = digest(bytes);
395
- const metadataBytes = encoder.encode(JSON.stringify({ digest: hash, headers }));
396
- if (metadataBytes.length > maxMetadataBytes) throw failure("Python package cache metadata exceeds maxMetadataBytes");
397
- await session.cache.set(pythonPackageRuntimeKey + "-sha256-" + hash, Uint8Array.from(bytes));
398
- checkSession(session);
399
- if (!canonicalWheel) await session.cache.set(address, metadataBytes);
400
- checkSession(session);
644
+ verifyIntegrity(key = digest(bytes));
645
+ if (!session.noCache) {
646
+ const metadataBytes = cacheRecord(key, headers);
647
+ await session.cache.set(pythonPackageRuntimeKey + "-sha256-" + key, Uint8Array.from(bytes));
648
+ checkSession(session);
649
+ if (!canonicalWheel) await session.cache.set(address, metadataBytes);
650
+ }
401
651
  }
402
652
  checkSession(session);
403
- if (expected && digest(bytes) !== expected) throw failure(`Package integrity mismatch: ${url}`);
404
- const key = digest(bytes);
405
- session.opened.set(key, bytes);
653
+ verifyIntegrity(key);
654
+ const value = bytes;
655
+ session.opened = { key, size: bytes.length, read: (offset, length) => value.subarray(offset, offset + length) };
406
656
  return { key, size: bytes.length, headers };
407
657
  } finally {
408
658
  session.opening = false;
@@ -415,11 +665,11 @@ function createPythonPackageEnvironment(options = {}) {
415
665
  const session = sessions.get(start.session);
416
666
  if (session) {
417
667
  session.closed = true;
418
- session.opened.clear();
419
668
  session.signal.removeEventListener("abort", session.aborted);
420
669
  session.controller.abort(failure("Python package session is closed"));
670
+ sessions.delete(start.session);
671
+ return release(session, true);
421
672
  }
422
- sessions.delete(start.session);
423
673
  },
424
674
  dispose() {
425
675
  if (!disposing) {
@@ -428,6 +678,7 @@ function createPythonPackageEnvironment(options = {}) {
428
678
  disposing = Promise.allSettled([...pending]).then(() => {
429
679
  sessions.clear();
430
680
  defaultCache.dispose();
681
+ if (cleanupFailure) throw cleanupFailure.error;
431
682
  });
432
683
  }
433
684
  return disposing;
@@ -436,6 +687,8 @@ function createPythonPackageEnvironment(options = {}) {
436
687
  }
437
688
 
438
689
  export {
690
+ openPythonPackageFile,
691
+ stagePythonPackage,
439
692
  PythonPackageConflictError,
440
693
  createPythonPackageManifestStore,
441
694
  createPythonPackageCache,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sourceBytes
3
- } from "./chunk-QX3WKBLI.js";
3
+ } from "./chunk-36FV6BAG.js";
4
4
 
5
5
  // packages/safe-bash-command-llm/src/base64-stream.ts
6
6
  async function* base64Stream(source, signal) {
@@ -2,22 +2,22 @@ import {
2
2
  EmptyJsonDocumentError,
3
3
  indentLlmText,
4
4
  withEmbeddingJsonDocument
5
- } from "./chunk-I7MEH76L.js";
5
+ } from "./chunk-UYNJF5QN.js";
6
6
  import {
7
7
  pythonRepr
8
- } from "./chunk-MCUDSE4H.js";
8
+ } from "./chunk-EWECMOQ5.js";
9
9
  import {
10
10
  createLlmSpool,
11
11
  floatText
12
- } from "./chunk-BZLLQWRK.js";
12
+ } from "./chunk-E5CDHXFA.js";
13
13
  import "./chunk-3PO534WJ.js";
14
14
  import {
15
15
  attachmentDigestHex,
16
16
  getLlmAttachmentUrlId
17
17
  } from "./chunk-B74LVFAN.js";
18
- import "./chunk-GQV5RWMN.js";
18
+ import "./chunk-ZFXHSFTU.js";
19
19
  import "./chunk-B6NAEWHC.js";
20
- import "./chunk-QX3WKBLI.js";
20
+ import "./chunk-36FV6BAG.js";
21
21
  import "./chunk-65DS4IOB.js";
22
22
 
23
23
  // packages/safe-bash-command-llm/src/prompt-tool-debug.ts
@@ -4,7 +4,7 @@ import {
4
4
  createBcCommands,
5
5
  settings
6
6
  } from "../../chunks/chunk-Q6XBTX6G.js";
7
- import "../../chunks/chunk-IRJS54AA.js";
7
+ import "../../chunks/chunk-VWLQDZFF.js";
8
8
  import "../../chunks/chunk-N4CQ5E3G.js";
9
9
  import "../../chunks/chunk-EDJBO2HZ.js";
10
10
  import "../../chunks/chunk-IMOFLK26.js";
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  LlmCancelToolCall,
3
+ LlmLoaderLookupError,
3
4
  createElevenLabsProvider,
4
5
  createLlmCommand,
5
6
  createLlmCommands,
@@ -39,9 +40,10 @@ import {
39
40
  templateYaml,
40
41
  validateLlmTemplateParameters,
41
42
  validateModelOptions
42
- } from "../../chunks/chunk-HNT2MR4W.js";
43
+ } from "../../chunks/chunk-363QOIDR.js";
43
44
  export {
44
45
  LlmCancelToolCall,
46
+ LlmLoaderLookupError,
45
47
  createElevenLabsProvider,
46
48
  createLlmCommand,
47
49
  createLlmCommands,
@@ -1,2 +1,2 @@
1
- export { "LlmCancelToolCall", "createElevenLabsProvider", "createLlmCommand", "createLlmCommands", "createLlmConfiguration", "createLlmFragmentLoaders", "createLlmFragmentSource", "createLlmInputBudget", "createLlmService", "createLlmSpool", "createLlmTemplateStore", "createLlmToolRegistry", "createLlmUrlFragmentSource", "createLlmUrlSource", "createOpenAiProvider", "encodeLlmBase64", "evaluateLlmTemplate", "executeLlmToolCalls", "findExtractedRange", "getLlmAttachmentUrlId", "getLlmFragmentPrefix", "getLlmModelAliases", "llmCommands", "llmReferenceVersion", "llmTemplateUsesInput", "loadLlmPluginFragments", "openAiChatOptions", "parseLlmSchemaDsl", "resolveLlmSchemaInput", "resolveLlmUrlAttachment", "selectLlmModelByQuery", "selectLlmTools", "serializeLlmJsonString", "serializeLlmJsonValue", "serializeLlmTokenUsage", "serializeOpenAiChatRequest", "streamLlmToolChain", "templateYaml", "validateLlmTemplateParameters", "validateModelOptions" } from "../../../safe-bash-command-llm/index.js";
1
+ export { "LlmCancelToolCall", "LlmLoaderLookupError", "createElevenLabsProvider", "createLlmCommand", "createLlmCommands", "createLlmConfiguration", "createLlmFragmentLoaders", "createLlmFragmentSource", "createLlmInputBudget", "createLlmService", "createLlmSpool", "createLlmTemplateStore", "createLlmToolRegistry", "createLlmUrlFragmentSource", "createLlmUrlSource", "createOpenAiProvider", "encodeLlmBase64", "evaluateLlmTemplate", "executeLlmToolCalls", "findExtractedRange", "getLlmAttachmentUrlId", "getLlmFragmentPrefix", "getLlmModelAliases", "llmCommands", "llmReferenceVersion", "llmTemplateUsesInput", "loadLlmPluginFragments", "openAiChatOptions", "parseLlmSchemaDsl", "resolveLlmSchemaInput", "resolveLlmUrlAttachment", "selectLlmModelByQuery", "selectLlmTools", "serializeLlmJsonString", "serializeLlmJsonValue", "serializeLlmTokenUsage", "serializeOpenAiChatRequest", "streamLlmToolChain", "templateYaml", "validateLlmTemplateParameters", "validateModelOptions" } from "../../../safe-bash-command-llm/index.js";
2
2
  //# sourceMappingURL=index.js.map