@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,17 +1,23 @@
1
1
  import {
2
2
  createPythonPackageEnvironment,
3
- pythonDocumentPackages
4
- } from "./chunk-R4NPYXQX.js";
3
+ createPythonPackageManifestStore,
4
+ openPythonPackageFile,
5
+ pythonDocumentPackages,
6
+ stagePythonPackage
7
+ } from "./chunk-WDTWJFPK.js";
5
8
  import {
6
9
  pythonShellDispatchActive
7
10
  } from "./chunk-NHAG4DWL.js";
8
11
  import {
9
12
  PythonInvocationError,
10
13
  parsePythonInvocation
11
- } from "./chunk-5EFNQ6FY.js";
14
+ } from "./chunk-64HIOP7Q.js";
12
15
  import {
13
16
  pythonLlmProvider
14
17
  } from "./chunk-YYRVGXJH.js";
18
+ import {
19
+ createPythonExecutorPool
20
+ } from "./chunk-GT27M77E.js";
15
21
  import {
16
22
  PythonFailure,
17
23
  isPythonFailureCategory,
@@ -107,45 +113,23 @@ function encodePythonReply(value, maxBytes) {
107
113
  }
108
114
 
109
115
  // packages/safe-bash-command-python/src/installation.ts
110
- var pythonInstallationHelp = `Usage: python -m pip install [-r FILE | --requirement FILE] PACKAGE ...
111
- Installs compatible wheels and dependencies using the configured Pyodide installer.
112
- PACKAGE is a package requirement or a canonical filesystem wheel path.
113
- Supported options: -r, --requirement, -h, --help, --.
114
- All other pip options and commands are unsupported, including --upgrade,
115
- --force-reinstall, --no-index, --no-deps, --target, --user, --prefix, --root, --editable,
116
- --index-url, --extra-index-url, --find-links, --trusted-host, --require-hashes,
117
- --constraint, --dry-run, --quiet, --verbose and cache/build/configuration flags.
118
- Configure transport and integrity through the Python package SDK configuration.
116
+ var pythonInstallationHelp = `Usage: python -m pip install [-r/--requirement FILE] [OPTIONS] PACKAGE ...
117
+ python -m pip uninstall [-y/--yes] PACKAGE ...
118
+ Install options: --pre, -U/--upgrade, --force-reinstall, --no-cache-dir
119
+ Compatible wheels; configure transport with the Python package SDK.
120
+ Both accept -h/--help and --. Other pip operations are unsupported.
119
121
  `;
120
- function parsePythonInstallation(args) {
121
- let pipArgs;
122
- scan: for (let index = 0; index < args.length; index++) {
123
- const option = args[index];
124
- if (option === "--" || option === "-" || !option.startsWith("-") || option.startsWith("--")) return void 0;
125
- for (let offset = 1; offset < option.length; offset++) {
126
- const flag = option[offset];
127
- if ("h?V".includes(flag)) return void 0;
128
- if ("cmWX".includes(flag)) {
129
- const operand = option.slice(offset + 1) || args[++index];
130
- if (flag === "c") return void 0;
131
- if (flag === "m") {
132
- if (operand !== "pip") return void 0;
133
- pipArgs = args.slice(index + 1);
134
- break scan;
135
- }
136
- break;
137
- }
138
- }
139
- }
140
- if (!pipArgs) return void 0;
122
+ function parsePythonInstallation(pipArgs) {
123
+ const controls = {};
141
124
  const packages = [];
142
125
  const requirements = [];
143
- if (pipArgs.length === 0) return { packages, requirements, help: true };
144
- if (pipArgs[0] === "--help" || pipArgs[0] === "-h") {
126
+ if (pipArgs.length === 0 || pipArgs[0] === "--help" || pipArgs[0] === "-h") {
145
127
  if (pipArgs.length > 1) throw new PythonInvocationError("unsupported pip option or argument after help: " + pipArgs[1]);
146
- return { packages, requirements, help: true };
128
+ return { packages, requirements, controls, help: true };
147
129
  }
148
- if (pipArgs[0] !== "install") throw new PythonInvocationError("unsupported pip command " + pipArgs[0]);
130
+ const uninstall = pipArgs[0] === "uninstall";
131
+ let yes = false;
132
+ if (!uninstall && pipArgs[0] !== "install") throw new PythonInvocationError("unsupported pip command " + pipArgs[0]);
149
133
  let operands = false;
150
134
  let help = false;
151
135
  for (let index = 1; index < pipArgs.length; index++) {
@@ -154,6 +138,14 @@ function parsePythonInstallation(args) {
154
138
  packages.push(option);
155
139
  continue;
156
140
  }
141
+ if (!uninstall && (option === "--pre" || option === "--no-cache-dir")) {
142
+ controls[option === "--pre" ? "pre" : "noCache"] = true;
143
+ continue;
144
+ }
145
+ if (!uninstall && (option === "-U" || option === "--upgrade" || option === "--force-reinstall")) {
146
+ controls[option === "--force-reinstall" ? "forceReinstall" : "upgrade"] = true;
147
+ continue;
148
+ }
157
149
  if (option === "--") {
158
150
  operands = true;
159
151
  continue;
@@ -162,7 +154,11 @@ function parsePythonInstallation(args) {
162
154
  help = true;
163
155
  continue;
164
156
  }
165
- if (option === "-r" || option === "--requirement" || option.startsWith("--requirement=") || option.startsWith("-r")) {
157
+ if (uninstall && (option === "-y" || option === "--yes")) {
158
+ yes = true;
159
+ continue;
160
+ }
161
+ if (!uninstall && (option === "-r" || option === "--requirement" || option.startsWith("--requirement=") || option.startsWith("-r"))) {
166
162
  const path = option.startsWith("--requirement=") ? option.slice("--requirement=".length) : option !== "-r" && option.startsWith("-r") ? option.slice(2) : pipArgs[++index];
167
163
  if (!path || path.startsWith("-")) throw new PythonInvocationError(option + " requires a path");
168
164
  requirements.push(path);
@@ -170,18 +166,455 @@ function parsePythonInstallation(args) {
170
166
  }
171
167
  throw new PythonInvocationError("unsupported pip option " + option);
172
168
  }
173
- if (!help && packages.length === 0 && requirements.length === 0) throw new PythonInvocationError("pip install requires at least one package or requirements file");
174
- return { packages, requirements, help };
169
+ if (!help && packages.length === 0 && requirements.length === 0) throw new PythonInvocationError("pip " + pipArgs[0] + " requires at least one package or requirements file");
170
+ return { packages, requirements, help, controls, ...uninstall ? { uninstall, yes } : {} };
175
171
  }
176
172
 
177
173
  // packages/safe-bash-command-python/src/executor.ts
178
174
  import { readBytes, writeBytes } from "../../safe-bash-contracts/io.js";
179
175
  import { createOutputOperation } from "../../safe-bash-contracts/output.js";
180
- import { inheritYieldCheckpoint, yieldTurn } from "../../safe-bash-contracts/yield.js";
176
+ import { inheritYieldCheckpoint, yieldTurn as yieldTurn2 } from "../../safe-bash-contracts/yield.js";
181
177
 
182
- // packages/safe-bash-command-python/src/llm-functions-loader.ts
183
- import { createLlmSpool, createLlmToolRegistry } from "../../safe-bash-command-llm/index.js";
178
+ // packages/safe-bash-command-python/src/llm-package-manager.ts
184
179
  import { toByteSource } from "../../safe-bash-contracts/index.js";
180
+ function createPythonLlmPackageManager(options) {
181
+ if (!options.createExecutor || !options.environment || options.provisioning) throw new TypeError("LLM package management requires an asynchronous executor and a shared Python environment");
182
+ const planners = /* @__PURE__ */ new WeakMap();
183
+ const pool = createPythonExecutorPool({
184
+ createExecutor: options.createExecutor,
185
+ ...options.maxConcurrentWorkers === void 0 || options.maxConcurrentWorkers === Infinity ? {} : { maxConcurrentExecutors: options.maxConcurrentWorkers }
186
+ });
187
+ const configuration = { ...options, createExecutor: pool.createExecutor, createCapabilities(context) {
188
+ const capabilities = options.createCapabilities?.(context) ?? {};
189
+ if (capabilities.llm_packages) throw new Error("Python package capability is reserved");
190
+ const capability = planners.get(context.args);
191
+ return capability ? { ...capabilities, llm_packages: capability } : capabilities;
192
+ } };
193
+ const installer = createPythonExecutorCommands(configuration)[0];
194
+ const { environment: ignoredEnvironment, packages: ignoredPackages, requirements: ignoredRequirements, packageProfile: ignoredProfile, ...planning } = configuration;
195
+ const command = createPythonExecutorCommands(planning)[0];
196
+ return async ({ context, args }) => {
197
+ if (!Array.isArray(args) || !["install", "uninstall"].includes(args[0]) || args.some((value) => typeof value !== "string")) throw new TypeError("Invalid LLM package command");
198
+ context.signal.throwIfAborted();
199
+ let planned;
200
+ const { registerCleanup: ignoredCleanup, argumentValues: ignoredArguments, ...isolated } = context;
201
+ const invocation = { ...isolated, command: "python", args: ["-c", pythonLlmPackageProgram, ...args], stdin: toByteSource("") };
202
+ planners.set(invocation.args, { async call(value) {
203
+ if (planned || !Array.isArray(value) || value.some((item) => typeof item !== "string") || value[0] !== "pip" || value[1] !== args[0]) throw new Error("Invalid native LLM package request");
204
+ planned = value.slice(1);
205
+ return null;
206
+ } });
207
+ let result;
208
+ try {
209
+ result = await command.execute(invocation);
210
+ } finally {
211
+ planners.delete(invocation.args);
212
+ }
213
+ if (result.exitCode || !planned) return result;
214
+ context.signal.throwIfAborted();
215
+ return installer.execute({ ...isolated, command: "python", args: ["-m", "pip", ...planned] });
216
+ };
217
+ }
218
+ var pythonLlmPackageProgram = String.raw`
219
+ import sys
220
+ import safe_host
221
+ import llm.cli
222
+
223
+ def _safe_package_request(module, run_name):
224
+ if module != 'pip' or run_name != '__main__':
225
+ raise RuntimeError('Unexpected LLM package entrypoint')
226
+ safe_host.call('llm_packages', list(sys.argv))
227
+
228
+ llm.cli.run_module = _safe_package_request
229
+ llm.cli.cli.main(args=sys.argv[1:], prog_name='llm')
230
+ `;
231
+
232
+ // packages/safe-bash-command-python/src/llm-plugin-setup.ts
233
+ var pythonLlmPluginSetup = /* @__PURE__ */ (() => String.raw`
234
+ def load_plugins(request):
235
+ import llm.plugins as manager
236
+ manager.load_plugins()
237
+ if request['plugins']:
238
+ original = (manager.DEFAULT_PLUGINS, manager.LLM_LOAD_PLUGINS, manager._loaded)
239
+ try:
240
+ manager.DEFAULT_PLUGINS = ()
241
+ manager.LLM_LOAD_PLUGINS = ','.join(request['plugins'])
242
+ manager._loaded = False
243
+ manager.load_plugins()
244
+ finally:
245
+ manager.DEFAULT_PLUGINS, manager.LLM_LOAD_PLUGINS, manager._loaded = original
246
+ `)();
247
+
248
+ // packages/safe-bash-command-python/src/llm-json-loader.ts
249
+ import { LlmLoaderLookupError } from "../../safe-bash-command-llm/index.js";
250
+ import { toByteSource as toByteSource2 } from "../../safe-bash-contracts/index.js";
251
+ function createPythonLlmJsonLoader(options, capabilityName, program, label) {
252
+ if (!options.createExecutor) throw new TypeError(`Python ${label} loading requires an asynchronous executor`);
253
+ if (options.plugins !== void 0 && (!Array.isArray(options.plugins) || options.plugins.some((name) => typeof name !== "string" || !name || name !== name.trim() || name.includes(",")))) throw new TypeError(`Python ${label} plugins must be explicit distribution names`);
254
+ const plugins = Object.freeze([...options.plugins ?? []]), capabilities = /* @__PURE__ */ new WeakMap();
255
+ const command = createPythonExecutorCommands({ ...options, createCapabilities(context) {
256
+ const provided = options.createCapabilities?.(context) ?? {};
257
+ if (provided[capabilityName]) throw new Error(`Python ${label} capability is reserved`);
258
+ const capability = capabilities.get(context.args);
259
+ if (!capability) throw new Error(`Unknown Python ${label} invocation`);
260
+ return { ...provided, [capabilityName]: capability };
261
+ } })[0];
262
+ return async (request, context, admitBytes) => {
263
+ if (!context) throw new TypeError(`Python ${label} loading requires caller context`);
264
+ const { maxBytes, signal } = context;
265
+ if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError(`Invalid Python ${label} byte limit`);
266
+ signal.throwIfAborted();
267
+ let size = 0, done = false, failure;
268
+ const chunks = [];
269
+ const invocation = { ...context, command: "python", args: ["-c", program], signal, env: context.env ?? {}, stdin: toByteSource2(""), stdout: context.stdout ?? { async write() {
270
+ } }, stderr: context.stderr ?? { async write() {
271
+ } } };
272
+ capabilities.set(invocation.args, { async call(input) {
273
+ try {
274
+ signal.throwIfAborted();
275
+ if (!input || typeof input !== "object" || Array.isArray(input) || done) throw new TypeError(`Invalid Python ${label} message`);
276
+ const message = input;
277
+ if (message.op === "request") return { ...request, plugins };
278
+ if (message.op === "missing" || message.op === "lookup" || message.op === "error") {
279
+ failure ??= message.op === "error" ? new Error(String(message.message)) : new LlmLoaderLookupError(String(message.message));
280
+ done = true;
281
+ return null;
282
+ }
283
+ if (message.op === "done") {
284
+ done = true;
285
+ return null;
286
+ }
287
+ if (message.op !== "text" || typeof message.text !== "string" || message.text.length > 8192) throw new TypeError(`Invalid Python ${label} text window`);
288
+ const bytes = new TextEncoder().encode(message.text).length;
289
+ if (bytes > 16384) throw new TypeError(`Invalid Python ${label} text window`);
290
+ if (bytes > maxBytes - size) throw new RangeError(`Python ${label} byte limit exceeded`);
291
+ admitBytes?.(bytes);
292
+ size += bytes;
293
+ if (bytes) chunks.push(message.text);
294
+ return null;
295
+ } catch (error) {
296
+ failure ??= error;
297
+ throw error;
298
+ }
299
+ } });
300
+ try {
301
+ const result = await command.execute(invocation);
302
+ signal.throwIfAborted();
303
+ if (failure) throw failure;
304
+ if (result.exitCode) throw new Error(`Python ${label} interpreter exited with status ${result.exitCode}`);
305
+ if (!done) throw new Error(`Python ${label} interpreter returned no result`);
306
+ return JSON.parse(chunks.join(""));
307
+ } finally {
308
+ capabilities.delete(invocation.args);
309
+ }
310
+ };
311
+ }
312
+
313
+ // packages/safe-bash-command-python/src/llm-template-loader.ts
314
+ function createPythonLlmTemplateLoader(options, prefix) {
315
+ if (typeof prefix !== "string" || !prefix) throw new TypeError("Python template loader requires a prefix");
316
+ const load = createPythonLlmJsonLoader(options, "llm_templates", pythonLlmTemplateProgram, "template");
317
+ return async (value, signal, context) => {
318
+ if (!context) throw new TypeError("Python template loading requires caller context");
319
+ if (typeof value !== "string") throw new TypeError("Python template input must be a string");
320
+ const template = await load({ prefix, value }, { ...context, signal });
321
+ if (!template || typeof template !== "object" || Array.isArray(template) || typeof template.name !== "string") throw new TypeError("Invalid native Python template");
322
+ return template;
323
+ };
324
+ }
325
+ var pythonLlmTemplateProgram = /* @__PURE__ */ (() => String.raw`
326
+ import json, llm, safe_host
327
+
328
+ ${pythonLlmPluginSetup}
329
+
330
+ def send(op, **fields):
331
+ return safe_host.call('llm_templates', dict(op=op, **fields))
332
+
333
+ def main():
334
+ request = send('request')
335
+ try:
336
+ load_plugins(request)
337
+ loaders = llm.get_template_loaders()
338
+ except Exception as error:
339
+ send('lookup', message=str(error))
340
+ return
341
+ if request['prefix'] not in loaders:
342
+ send('missing', message='Unknown template prefix: ' + request['prefix'])
343
+ return
344
+ template = loaders[request['prefix']](request['value'])
345
+ encoder = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'))
346
+ for part in encoder.iterencode(template.model_dump(exclude_none=True)):
347
+ for offset in range(0, len(part), 4096): send('text', text=part[offset:offset + 4096])
348
+ send('done')
349
+ try:
350
+ main()
351
+ except Exception as error:
352
+ send('error', message=str(error))
353
+ `)();
354
+
355
+ // packages/safe-bash-command-python/src/llm-fragment-loader.ts
356
+ import { createLlmSpool, createLlmUrlSource, LlmLoaderLookupError as LlmLoaderLookupError2 } from "../../safe-bash-command-llm/index.js";
357
+ import { toByteSource as toByteSource3 } from "../../safe-bash-contracts/index.js";
358
+ function deferred() {
359
+ let resolve, reject;
360
+ const promise = new Promise((yes, no) => {
361
+ resolve = yes;
362
+ reject = no;
363
+ });
364
+ void promise.catch(() => {
365
+ });
366
+ return { promise, resolve, reject };
367
+ }
368
+ function createPythonLlmFragmentLoader(options, prefix) {
369
+ if (!options.createExecutor) throw new TypeError("Python fragment loading requires an asynchronous executor");
370
+ if (typeof prefix !== "string" || !prefix) throw new TypeError("Python fragment loader requires a prefix");
371
+ if (options.plugins !== void 0 && (!Array.isArray(options.plugins) || options.plugins.some((name) => typeof name !== "string" || !name || name !== name.trim() || name.includes(",")))) throw new TypeError("Python fragment plugins must be explicit distribution names");
372
+ const plugins = Object.freeze([...options.plugins ?? []]), capabilities = /* @__PURE__ */ new WeakMap();
373
+ const command = createPythonExecutorCommands({ ...options, createCapabilities(context) {
374
+ const provided = options.createCapabilities?.(context) ?? {};
375
+ if (provided.llm_fragments) throw new Error("Python fragment capability is reserved");
376
+ const capability = capabilities.get(context.args);
377
+ if (!capability) throw new Error("Unknown Python fragment invocation");
378
+ return { ...provided, llm_fragments: capability };
379
+ } })[0];
380
+ return async function* (value, context) {
381
+ if (typeof value !== "string") throw new TypeError("Python fragment input must be a string");
382
+ const { fs, cwd, maxBytes } = context;
383
+ if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError("Invalid Python fragment byte limit");
384
+ const controller = new AbortController(), signal = AbortSignal.any([context.signal, controller.signal]);
385
+ signal.throwIfAborted();
386
+ let ready = deferred(), resume;
387
+ let current, spool, closed = false, bytes = 0;
388
+ const sources = /* @__PURE__ */ new Set();
389
+ const admit = (size) => {
390
+ if (size > maxBytes - bytes) throw new RangeError("Python fragment byte limit exceeded");
391
+ bytes += size;
392
+ };
393
+ const fail = (error) => {
394
+ ready.reject(error);
395
+ resume?.resolve(false);
396
+ controller.abort(error);
397
+ };
398
+ const aborted = () => {
399
+ ready.reject(signal.reason);
400
+ resume?.resolve(false);
401
+ };
402
+ signal.addEventListener("abort", aborted, { once: true });
403
+ const retire = async (source) => {
404
+ try {
405
+ await source.dispose();
406
+ } finally {
407
+ sources.delete(source);
408
+ }
409
+ };
410
+ const dispatch = async (input) => {
411
+ signal.throwIfAborted();
412
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new TypeError("Invalid Python fragment message");
413
+ const message = input;
414
+ if (message.op === "request") return { prefix, value, plugins };
415
+ if (message.op === "missing" || message.op === "lookup") throw new LlmLoaderLookupError2(String(message.message));
416
+ if (message.op === "error") throw new Error(String(message.message));
417
+ if (message.op === "begin") {
418
+ if (current || resume) throw new Error("Python fragment already active");
419
+ if (message.type !== "text" && message.type !== "attachment") throw new TypeError("Invalid Python fragment type");
420
+ if (message.type === "attachment" && (typeof message.mimeType !== "string" || typeof message.id !== "string")) throw new TypeError("Invalid Python fragment metadata");
421
+ let source;
422
+ if (message.url !== void 0) {
423
+ if (message.type !== "attachment" || typeof message.url !== "string" || !context.capabilities?.fetch) throw new TypeError("Attachment URL loading is not configured");
424
+ source = createLlmUrlSource({ url: message.url, fetch: context.capabilities.fetch, signal, maxBytes: maxBytes - bytes, admitBytes: admit });
425
+ } else {
426
+ const owned = await createLlmSpool(fs, cwd, signal, "input");
427
+ spool = owned;
428
+ source = { bytes: owned.replay(), dispose: () => owned.close() };
429
+ }
430
+ sources.add(source);
431
+ current = message.type === "text" ? { type: "text", source: { ...source, normalizeNewlines: false } } : { type: "attachment", source, mimeType: message.mimeType, id: message.id };
432
+ sources.delete(source);
433
+ sources.add(current.source);
434
+ return null;
435
+ }
436
+ if (!current) throw new Error("No active Python fragment");
437
+ if (message.op === "text" || message.op === "bytes") {
438
+ if (!spool) throw new Error("Remote Python fragments cannot accept inline bytes");
439
+ let chunk;
440
+ if (message.op === "text") {
441
+ if (current.type !== "text" || typeof message.text !== "string" || message.text.length > 8192) throw new TypeError("Invalid Python fragment text window");
442
+ chunk = new TextEncoder().encode(message.text);
443
+ if (chunk.length > 16384) throw new TypeError("Invalid Python fragment text window");
444
+ } else {
445
+ if (current.type !== "attachment" || !Array.isArray(message.bytes) || message.bytes.length > 4096 || message.bytes.some((byte) => typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255)) throw new TypeError("Invalid Python fragment byte window");
446
+ chunk = Uint8Array.from(message.bytes);
447
+ }
448
+ admit(chunk.length);
449
+ await spool.write(chunk);
450
+ return null;
451
+ }
452
+ if (message.op === "end") {
453
+ const continuation = resume = deferred();
454
+ ready.resolve(current);
455
+ return continuation.promise;
456
+ }
457
+ throw new TypeError("Invalid Python fragment operation");
458
+ };
459
+ const invocation = { ...context, command: "python", args: ["-c", pythonLlmFragmentProgram], fs, cwd, signal, env: context.env ?? {}, stdin: toByteSource3(""), stdout: context.stdout ?? { async write() {
460
+ } }, stderr: context.stderr ?? { async write() {
461
+ } } };
462
+ capabilities.set(invocation.args, { async call(input) {
463
+ try {
464
+ return await dispatch(input);
465
+ } catch (error) {
466
+ fail(error);
467
+ throw error;
468
+ }
469
+ } });
470
+ const running = Promise.resolve().then(() => command.execute(invocation)).then((result) => {
471
+ if (closed) return;
472
+ if (result.exitCode) fail(new Error(`Python fragment interpreter exited with status ${result.exitCode}`));
473
+ else ready.resolve(void 0);
474
+ }, (error) => {
475
+ if (!closed) fail(error);
476
+ });
477
+ let closing;
478
+ const close = () => closing ??= (async () => {
479
+ closed = true;
480
+ resume?.resolve(false);
481
+ controller.abort(new Error("Python fragment session closed"));
482
+ await running;
483
+ capabilities.delete(invocation.args);
484
+ signal.removeEventListener("abort", aborted);
485
+ await Promise.all([...sources].map(retire));
486
+ })();
487
+ context.registerCleanup?.(close);
488
+ try {
489
+ while (true) {
490
+ const fragment = await ready.promise;
491
+ if (!fragment) break;
492
+ let advance = false;
493
+ try {
494
+ yield fragment;
495
+ advance = true;
496
+ } finally {
497
+ await retire(fragment.source);
498
+ current = void 0;
499
+ spool = void 0;
500
+ ready = deferred();
501
+ const continuation = resume;
502
+ resume = void 0;
503
+ continuation?.resolve(advance);
504
+ }
505
+ }
506
+ } finally {
507
+ await close();
508
+ }
509
+ };
510
+ }
511
+ var pythonLlmFragmentProgram = /* @__PURE__ */ (() => String.raw`
512
+ import hashlib, llm, safe_host
513
+ from llm_safe_host import _attachment_type
514
+
515
+ ${pythonLlmPluginSetup}
516
+
517
+ def send(op, **fields):
518
+ return safe_host.call('llm_fragments', dict(op=op, **fields))
519
+
520
+ def emit_attachment(value):
521
+ mime = _attachment_type(value)
522
+ if not value.content and value.path:
523
+ with open(value.path, 'rb') as source:
524
+ if value._id is None:
525
+ digest = hashlib.sha256()
526
+ for chunk in iter(lambda: source.read(4096), b''): digest.update(chunk)
527
+ value._id = digest.hexdigest()
528
+ source.seek(0)
529
+ send('begin', type='attachment', mimeType=mime, id=value.id())
530
+ for chunk in iter(lambda: source.read(4096), b''): send('bytes', bytes=list(chunk))
531
+ elif not value.content and value.url:
532
+ send('begin', type='attachment', mimeType=mime, id=value.id(), url=value.url)
533
+ else:
534
+ send('begin', type='attachment', mimeType=mime, id=value.id())
535
+ content = memoryview(value.content or b'')
536
+ for offset in range(0, len(content), 4096): send('bytes', bytes=list(content[offset:offset + 4096]))
537
+
538
+ def main():
539
+ request = send('request')
540
+ try:
541
+ load_plugins(request)
542
+ loaders = llm.get_fragment_loaders()
543
+ except Exception as error:
544
+ send('lookup', message=str(error))
545
+ return
546
+ if request['prefix'] not in loaders:
547
+ send('missing', message='Unknown fragment prefix: ' + request['prefix'])
548
+ return
549
+ result = loaders[request['prefix']](request['value'])
550
+ if not isinstance(result, list): result = [result]
551
+ for value in result:
552
+ if isinstance(value, llm.Attachment): emit_attachment(value)
553
+ elif isinstance(value, str):
554
+ send('begin', type='text')
555
+ for offset in range(0, len(value), 4096): send('text', text=value[offset:offset + 4096])
556
+ else: raise TypeError('Fragment loader must return text or attachments')
557
+ if not send('end'): return
558
+ try:
559
+ main()
560
+ except Exception as error:
561
+ send('error', message=str(error))
562
+ `)();
563
+
564
+ // packages/safe-bash-command-python/src/llm-loader-discovery.ts
565
+ function createPythonLlmLoaderProvider(options) {
566
+ const discover = createPythonLlmLoaderDiscovery(options), configured = { ...options, plugins: Object.freeze([...options.plugins ?? []]) };
567
+ return { discover, fragments: createPythonLlmFragmentLoader.bind(null, configured), templates: createPythonLlmTemplateLoader.bind(null, configured) };
568
+ }
569
+ function createPythonLlmLoaderDiscovery(options) {
570
+ const load = createPythonLlmJsonLoader(options, "llm_loaders", pythonLlmLoaderDiscoveryProgram, "loader discovery");
571
+ const configured = { ...options, plugins: Object.freeze([...options.plugins ?? []]) };
572
+ return async (context) => {
573
+ if (context.kind !== void 0 && context.kind !== "fragments" && context.kind !== "templates") throw new TypeError("Invalid native loader discovery kind");
574
+ const result = await load(context.kind === void 0 ? {} : { kind: context.kind }, context, context.admitBytes);
575
+ if (!result || typeof result !== "object" || Array.isArray(result)) throw new TypeError("Invalid native loader discovery");
576
+ function loaders(entries, create) {
577
+ if (!Array.isArray(entries)) throw new TypeError("Invalid native loader discovery");
578
+ const values = /* @__PURE__ */ new Map();
579
+ for (const entry of entries) {
580
+ if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== "string" || !entry[0] || values.has(entry[0]) || entry[1] !== null && typeof entry[1] !== "string") throw new TypeError("Invalid native loader registration");
581
+ const loader = create(configured, entry[0]);
582
+ if (entry[1] !== null) Object.defineProperty(loader, "description", { value: entry[1], enumerable: true });
583
+ values.set(entry[0], loader);
584
+ }
585
+ return values;
586
+ }
587
+ const data = result;
588
+ return { fragmentLoaders: loaders(data.fragments, createPythonLlmFragmentLoader), templateLoaders: loaders(data.templates, createPythonLlmTemplateLoader) };
589
+ };
590
+ }
591
+ var pythonLlmLoaderDiscoveryProgram = /* @__PURE__ */ (() => String.raw`
592
+ import json, llm, safe_host
593
+
594
+ ${pythonLlmPluginSetup}
595
+
596
+ def send(op, **fields):
597
+ return safe_host.call('llm_loaders', dict(op=op, **fields))
598
+
599
+ def main():
600
+ request = send('request')
601
+ load_plugins(request)
602
+ encoder = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'))
603
+ kind = request.get('kind')
604
+ result = dict(fragments=[] if kind == 'templates' else [(prefix, loader.__doc__) for prefix, loader in llm.get_fragment_loaders().items()],
605
+ templates=[] if kind == 'fragments' else [(prefix, loader.__doc__) for prefix, loader in llm.get_template_loaders().items()])
606
+ for part in encoder.iterencode(result):
607
+ for offset in range(0, len(part), 4096): send('text', text=part[offset:offset + 4096])
608
+ send('done')
609
+ try:
610
+ main()
611
+ except Exception as error:
612
+ send('error', message=str(error))
613
+ `)();
614
+
615
+ // packages/safe-bash-command-python/src/llm-functions-loader.ts
616
+ import { createLlmSpool as createLlmSpool2, createLlmToolRegistry } from "../../safe-bash-command-llm/index.js";
617
+ import { toByteSource as toByteSource4 } from "../../safe-bash-contracts/index.js";
185
618
 
186
619
  // packages/safe-bash-command-python/src/llm-functions-program.ts
187
620
  var pythonLlmFunctionsProgram = /* @__PURE__ */ (() => String.raw`
@@ -248,6 +681,20 @@ async def main():
248
681
  discovery = selection.get("discovery", False)
249
682
  tools = []
250
683
  try:
684
+ requested_plugins = selection.get("plugins", [])
685
+ if requested_plugins:
686
+ import llm.plugins as plugin_manager
687
+ plugin_manager.load_plugins()
688
+ original = (plugin_manager.DEFAULT_PLUGINS, plugin_manager.LLM_LOAD_PLUGINS, plugin_manager._loaded)
689
+ try:
690
+ # Reuse native explicit-distribution loading. The host's tooling
691
+ # registration guard and ambient-entrypoint prohibition stay active.
692
+ plugin_manager.DEFAULT_PLUGINS = ()
693
+ plugin_manager.LLM_LOAD_PLUGINS = ",".join(requested_plugins)
694
+ plugin_manager._loaded = False
695
+ plugin_manager.load_plugins()
696
+ finally:
697
+ plugin_manager.DEFAULT_PLUGINS, plugin_manager.LLM_LOAD_PLUGINS, plugin_manager._loaded = original
251
698
  plugin_query = selection.get("pluginQuery")
252
699
  if plugin_query is not None:
253
700
  plugins = get_plugins(plugin_query["all"])
@@ -356,7 +803,7 @@ asyncio.run(main())
356
803
  `)();
357
804
 
358
805
  // packages/safe-bash-command-python/src/llm-functions-loader.ts
359
- function deferred() {
806
+ function deferred2() {
360
807
  let resolve, reject;
361
808
  const promise = new Promise((yes, no) => {
362
809
  resolve = yes;
@@ -370,6 +817,8 @@ function record(value) {
370
817
  }
371
818
  function createPythonLlmToolLoader(options) {
372
819
  if (!options.createExecutor) throw new TypeError("Python tool loading requires an asynchronous executor");
820
+ if (options.plugins !== void 0 && (!Array.isArray(options.plugins) || options.plugins.some((name) => typeof name !== "string" || !name || name !== name.trim() || name.includes(",")))) throw new TypeError("Python tool plugins must be explicit distribution names");
821
+ const pluginNames = Object.freeze([...options.plugins ?? []]);
373
822
  const capabilitiesByArguments = /* @__PURE__ */ new WeakMap();
374
823
  const command = createPythonExecutorCommands({ ...options, createCapabilities: (current) => {
375
824
  const capabilities = options.createCapabilities?.(current) ?? {};
@@ -387,7 +836,7 @@ function createPythonLlmToolLoader(options) {
387
836
  const controller = new AbortController();
388
837
  const runtimeController = new AbortController();
389
838
  const signal = AbortSignal.any([context.signal, controller.signal]);
390
- const ready = deferred();
839
+ const ready = deferred2();
391
840
  const tools = [];
392
841
  const toolboxes = [];
393
842
  let plugins;
@@ -395,7 +844,7 @@ function createPythonLlmToolLoader(options) {
395
844
  const jobs = /* @__PURE__ */ new Map();
396
845
  const queue = [];
397
846
  let waiter;
398
- let serial = 0, controls = 0, loaded = false, initializationFailed = false, closed = false, closing;
847
+ let serial3 = 0, controls = 0, loaded = false, initializationFailed = false, closed = false, closing;
399
848
  const encoder = new TextEncoder();
400
849
  const fail = (error) => {
401
850
  ready.reject(error);
@@ -411,7 +860,7 @@ function createPythonLlmToolLoader(options) {
411
860
  const aborted = () => fail(signal.reason);
412
861
  signal.addEventListener("abort", aborted, { once: true });
413
862
  const spool = async () => {
414
- const value = await createLlmSpool(context.fs, context.cwd, signal);
863
+ const value = await createLlmSpool2(context.fs, context.cwd, signal);
415
864
  spools.add(value);
416
865
  if (closed || signal.aborted) {
417
866
  await value.close();
@@ -438,7 +887,7 @@ function createPythonLlmToolLoader(options) {
438
887
  try {
439
888
  output = await spool();
440
889
  callContext.signal.throwIfAborted();
441
- const result = deferred(), id = ++serial;
890
+ const result = deferred2(), id = ++serial3;
442
891
  jobs.set(id, { result, output, attachments: [], files: /* @__PURE__ */ new Map(), bytes: 0, limit: Math.min(callContext.maxBytes, maxOutputBytes) });
443
892
  const request = { id, ...fields };
444
893
  if (waiter) {
@@ -464,7 +913,7 @@ function createPythonLlmToolLoader(options) {
464
913
  const message = record(value);
465
914
  if (message.op !== "next" && message.op !== "failed") signal.throwIfAborted();
466
915
  if (message.op === "definitions") return [...definitions];
467
- if (message.op === "selection") return { names: [...toolNames], discovery, ...pluginQuery ? { pluginQuery: { all: pluginQuery.all, hooks: [...pluginQuery.hooks] } } : {} };
916
+ if (message.op === "selection") return { names: [...toolNames], discovery, ...pluginNames.length ? { plugins: [...pluginNames] } : {}, ...pluginQuery ? { pluginQuery: { all: pluginQuery.all, hooks: [...pluginQuery.hooks] } } : {} };
468
917
  if (message.op === "admit") {
469
918
  if (typeof message.size !== "number" || !Number.isSafeInteger(message.size) || message.size < 0) throw new TypeError("Invalid Python definition byte count");
470
919
  controls += message.size;
@@ -541,7 +990,7 @@ function createPythonLlmToolLoader(options) {
541
990
  if (signal.aborted) return { cancel: true };
542
991
  if (!loaded || waiter) throw new Error("Invalid Python tool request queue");
543
992
  if (queue.length) return queue.shift();
544
- waiter = deferred();
993
+ waiter = deferred2();
545
994
  return waiter.promise;
546
995
  }
547
996
  if (typeof message.id !== "number") throw new TypeError("Invalid Python tool call identity");
@@ -604,7 +1053,7 @@ function createPythonLlmToolLoader(options) {
604
1053
  }
605
1054
  } };
606
1055
  const { registerCleanup: ignoredParentCleanup, ...isolatedContext } = context;
607
- const invocation = { ...isolatedContext, signal: runtimeController.signal, command: "python", args: ["-c", pythonLlmFunctionsProgram], stdin: toByteSource("") };
1056
+ const invocation = { ...isolatedContext, signal: runtimeController.signal, command: "python", args: ["-c", pythonLlmFunctionsProgram], stdin: toByteSource4("") };
608
1057
  capabilitiesByArguments.set(invocation.args, capability);
609
1058
  const running = Promise.resolve().then(() => command.execute(invocation)).then(
610
1059
  (result) => {
@@ -644,6 +1093,352 @@ function createPythonLlmToolLoader(options) {
644
1093
  };
645
1094
  }
646
1095
 
1096
+ // packages/safe-bash-command-python/src/build-environment.ts
1097
+ function createPythonBuildEnvironment(options = {}) {
1098
+ const manifestStore = createPythonPackageManifestStore({ maxEntries: 1, ...options.maxManifestBytes === void 0 ? {} : { maxBytes: options.maxManifestBytes } });
1099
+ const isolated = { ...options, manifestStore, scope: "build" };
1100
+ delete isolated.requirements;
1101
+ delete isolated.requirementFiles;
1102
+ delete isolated.profile;
1103
+ const environment = createPythonPackageEnvironment(isolated);
1104
+ let disposing;
1105
+ return { ...environment, dispose() {
1106
+ return disposing ??= environment.dispose().finally(() => manifestStore.dispose());
1107
+ } };
1108
+ }
1109
+
1110
+ // packages/safe-bash-command-python/src/build-dependencies.ts
1111
+ import { toByteSource as toByteSource6 } from "../../safe-bash-contracts/index.js";
1112
+
1113
+ // packages/safe-bash-command-python/src/build-backend.ts
1114
+ import { toByteSource as toByteSource5 } from "../../safe-bash-contracts/index.js";
1115
+ function createPythonBuildBackend(options) {
1116
+ if (!options.createExecutor || !options.environment || options.provisioning) throw new TypeError("Python build hooks require an asynchronous executor and explicit environment");
1117
+ const capabilities = /* @__PURE__ */ new WeakMap();
1118
+ const environment = options.environment;
1119
+ const command = createPythonExecutorCommands({ ...options, environment: { ...environment, async prepare(context) {
1120
+ return { ...await environment.prepare(context), bootstrap: true };
1121
+ } }, createCapabilities(context) {
1122
+ const provided = options.createCapabilities?.(context) ?? {};
1123
+ if (provided.python_build) throw new Error("Python build capability is reserved");
1124
+ const capability = capabilities.get(context.args);
1125
+ if (!capability) throw new Error("Unknown Python build invocation");
1126
+ return { ...provided, python_build: capability };
1127
+ } })[0];
1128
+ return async function run(input, context) {
1129
+ context.signal.throwIfAborted();
1130
+ const { maxBytes } = context;
1131
+ if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError("Invalid Python build metadata limit");
1132
+ if (!input || !["read_build_system", "check_build_requirements", "get_requires_for_build_wheel", "build_wheel"].includes(input.hook) || typeof input.source !== "string" || !input.source) throw new TypeError("Invalid Python build hook request");
1133
+ if (input.hook === "check_build_requirements") {
1134
+ if ([input.requirements, input.installed].some((values) => !Array.isArray(values) || values.some((value) => typeof value !== "string"))) throw new TypeError("Invalid Python build requirements request");
1135
+ } else if (input.hook === "read_build_system") {
1136
+ if (input.name !== void 0 && typeof input.name !== "string" || input.usePep517 !== void 0 && typeof input.usePep517 !== "boolean") throw new TypeError("Invalid Python build system request");
1137
+ } else {
1138
+ if (typeof input.backend !== "string" || !input.backend || input.backendPath !== void 0 && (!Array.isArray(input.backendPath) || input.backendPath.some((path) => typeof path !== "string")) || input.hook === "build_wheel" && (typeof input.wheelDirectory !== "string" || !input.wheelDirectory || input.metadataDirectory !== void 0 && typeof input.metadataDirectory !== "string")) throw new TypeError("Invalid Python build hook request");
1139
+ if (input.configSettings !== void 0 && (!input.configSettings || typeof input.configSettings !== "object" || Array.isArray(input.configSettings) || Object.values(input.configSettings).some((value) => typeof value !== "string" && (!Array.isArray(value) || value.some((item) => typeof item !== "string"))))) throw new TypeError("Invalid Python build configuration");
1140
+ }
1141
+ const request = structuredClone(input.hook === "read_build_system" ? { ...input, maxBytes: maxBytes === Infinity ? null : maxBytes } : input);
1142
+ let done = false, size = 0, failure;
1143
+ const chunks = [];
1144
+ const invocation = { ...context, command: "python", args: ["-c", pythonBuildBackendProgram], stdin: toByteSource5("") };
1145
+ capabilities.set(invocation.args, { async call(value) {
1146
+ try {
1147
+ context.signal.throwIfAborted();
1148
+ if (!value || typeof value !== "object" || Array.isArray(value) || done) throw new TypeError("Invalid Python build message");
1149
+ const message = value;
1150
+ if (message.op === "request") return request;
1151
+ if (message.op === "error") {
1152
+ if (typeof message.type !== "string" || typeof message.message !== "string") throw new TypeError("Invalid Python build failure");
1153
+ failure ??= Object.assign(new Error(message.message), { name: message.type });
1154
+ done = true;
1155
+ return null;
1156
+ }
1157
+ if (message.op === "done") {
1158
+ done = true;
1159
+ return null;
1160
+ }
1161
+ if (message.op !== "text" || typeof message.text !== "string" || message.text.length > 8192) throw new TypeError("Invalid Python build metadata window");
1162
+ const bytes = new TextEncoder().encode(message.text).length;
1163
+ if (bytes > 16384 || bytes > maxBytes - size) throw new RangeError("Python build metadata limit exceeded");
1164
+ size += bytes;
1165
+ if (bytes) chunks.push(message.text);
1166
+ return null;
1167
+ } catch (error) {
1168
+ failure ??= error;
1169
+ throw error;
1170
+ }
1171
+ } });
1172
+ try {
1173
+ const result = await command.execute(invocation);
1174
+ context.signal.throwIfAborted();
1175
+ if (failure) throw failure;
1176
+ if (result.exitCode) throw new Error(`Python build interpreter exited with status ${result.exitCode}`);
1177
+ if (!done) throw new Error("Python build interpreter returned no result");
1178
+ const value = JSON.parse(chunks.join(""));
1179
+ if (request.hook === "check_build_requirements") {
1180
+ const result2 = value;
1181
+ if (!result2 || !Array.isArray(result2.missing) || result2.missing.some((value2) => typeof value2 !== "string") || !Array.isArray(result2.conflicting) || result2.conflicting.some((pair) => !Array.isArray(pair) || pair.length !== 2 || pair.some((value2) => typeof value2 !== "string"))) throw new TypeError("Invalid Python build requirement status");
1182
+ } else if (request.hook === "read_build_system") {
1183
+ if (value !== null) {
1184
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Invalid Python build system");
1185
+ const details = value;
1186
+ if (typeof details.backend !== "string" || !details.backend || ["requires", "check", "backendPath"].some((key) => !Array.isArray(details[key]) || details[key].some((item) => typeof item !== "string"))) throw new TypeError("Invalid Python build system");
1187
+ }
1188
+ } else if (request.hook === "build_wheel") {
1189
+ if (typeof value !== "string" || !value.endsWith(".whl") || value.includes("/") || value.includes("\\") || value.includes("\0")) throw new TypeError("Invalid Python build wheel filename");
1190
+ } else if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new TypeError("Invalid Python build requirements");
1191
+ return value;
1192
+ } finally {
1193
+ capabilities.delete(invocation.args);
1194
+ }
1195
+ };
1196
+ }
1197
+ var pythonBuildBackendProgram = /* @__PURE__ */ (() => String.raw`
1198
+ import importlib, json, os, sys, safe_host
1199
+
1200
+ def send(op, **fields):
1201
+ return safe_host.call('python_build', dict(op=op, **fields))
1202
+
1203
+ def parse_build_requirement(value):
1204
+ from urllib.parse import urlparse, urlunparse
1205
+ from micropip._vendored.packaging.src.packaging.requirements import Requirement, InvalidRequirement
1206
+ requirement = Requirement(value)
1207
+ if requirement.url:
1208
+ url = urlparse(requirement.url)
1209
+ if (url.scheme == 'file' and urlunparse(url) != requirement.url) or (url.scheme != 'file' and not (url.scheme and url.netloc)): raise InvalidRequirement(value)
1210
+ return requirement
1211
+
1212
+ def check_build_requirements(request):
1213
+ from importlib.metadata import distribution, PackageNotFoundError
1214
+ from micropip._vendored.packaging.src.packaging.utils import canonicalize_name
1215
+ from micropip._vendored.packaging.src.packaging.version import Version
1216
+ installed = {canonicalize_name(name) for name in request['installed']}
1217
+ conflicting, missing = set(), set()
1218
+ for value in request['requirements']:
1219
+ requirement = parse_build_requirement(value)
1220
+ if canonicalize_name(requirement.name) not in installed:
1221
+ missing.add(value)
1222
+ continue
1223
+ try: version = Version(distribution(requirement.name).version)
1224
+ except PackageNotFoundError:
1225
+ missing.add(value)
1226
+ continue
1227
+ if version not in requirement.specifier: conflicting.add((requirement.name + '==' + str(version), value))
1228
+ return dict(conflicting=sorted(conflicting), missing=sorted(missing))
1229
+
1230
+ def read_build_system(request):
1231
+ import tomllib
1232
+ from micropip._vendored.packaging.src.packaging.requirements import InvalidRequirement
1233
+ class InstallationError(Exception): pass
1234
+ source = request['source']
1235
+ filename = os.path.join(source, 'pyproject.toml')
1236
+ has_project, has_setup = os.path.isfile(filename), os.path.isfile(os.path.join(source, 'setup.py'))
1237
+ system = None
1238
+ if has_project:
1239
+ limit = request['maxBytes']
1240
+ with open(filename, 'rb') as stream: raw = stream.read() if limit is None else stream.read(limit + 1)
1241
+ if limit is not None and len(raw) > limit: raise ValueError('Python build configuration limit exceeded')
1242
+ system = tomllib.loads(raw.decode('utf-8')).get('build-system')
1243
+ selected = request.get('usePep517')
1244
+ if has_project and not has_setup:
1245
+ if selected is False: raise InstallationError('Disabling PEP 517 processing is invalid: project does not have a setup.py')
1246
+ selected = True
1247
+ elif system and 'build-backend' in system:
1248
+ if selected is False: raise InstallationError('Disabling PEP 517 processing is invalid: project specifies a build backend of {} in pyproject.toml'.format(system['build-backend']))
1249
+ selected = True
1250
+ elif selected is None: selected = has_project
1251
+ if not selected: return None
1252
+ defaults = ['setuptools>=40.8.0', 'wheel']
1253
+ if system is None: system = {'requires': defaults, 'build-backend': 'setuptools.build_meta:__legacy__'}
1254
+ def invalid(reason):
1255
+ raise InstallationError('{} has a pyproject.toml file that does not comply with PEP 518: {}'.format(request.get('name', source), reason))
1256
+ if 'requires' not in system: invalid("it has a 'build-system' table but not 'build-system.requires' which is mandatory in the table")
1257
+ requirements = system['requires']
1258
+ if not isinstance(requirements, list) or not all(isinstance(value, str) for value in requirements): invalid("'build-system.requires' is not a list of strings.")
1259
+ for value in requirements:
1260
+ try:
1261
+ parse_build_requirement(value)
1262
+ except InvalidRequirement: invalid("'build-system.requires' contains an invalid requirement: {!r}".format(value))
1263
+ backend = system.get('build-backend')
1264
+ return dict(requires=requirements, backend=backend if backend is not None else 'setuptools.build_meta:__legacy__', check=defaults if backend is None else [], backendPath=system.get('backend-path', []))
1265
+
1266
+ def main():
1267
+ request = send('request')
1268
+ if request['hook'] == 'read_build_system': return read_build_system(request)
1269
+ if request['hook'] == 'check_build_requirements': return check_build_requirements(request)
1270
+ source = os.path.realpath(request['source'])
1271
+ wheel_directory = os.path.abspath(request['wheelDirectory']) if request['hook'] == 'build_wheel' else None
1272
+ metadata_directory = os.path.abspath(request['metadataDirectory']) if request.get('metadataDirectory') is not None else None
1273
+ os.chdir(source)
1274
+ paths = []
1275
+ for entry in request.get('backendPath', []):
1276
+ if os.path.isabs(entry): raise ValueError('paths must be relative')
1277
+ path = os.path.realpath(os.path.join(source, entry))
1278
+ if os.path.commonpath([source, path]) != source: raise ValueError('paths must be inside source tree')
1279
+ paths.append(path)
1280
+ sys.path[:0] = paths
1281
+ module, _, attributes = request['backend'].partition(':')
1282
+ backend = importlib.import_module(module)
1283
+ if paths and not any(os.path.commonpath([os.path.realpath(backend.__file__), path]) == path for path in paths):
1284
+ raise ValueError('Backend was not loaded from backend-path')
1285
+ if attributes:
1286
+ for name in attributes.split('.'): backend = getattr(backend, name)
1287
+ settings = request.get('configSettings')
1288
+ if request['hook'] == 'get_requires_for_build_wheel':
1289
+ try: hook = backend.get_requires_for_build_wheel
1290
+ except AttributeError: result = []
1291
+ else: result = hook(settings)
1292
+ else:
1293
+ result = backend.build_wheel(wheel_directory, settings, metadata_directory)
1294
+ return result
1295
+
1296
+ try:
1297
+ result = main()
1298
+ encoder = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'))
1299
+ for part in encoder.iterencode(result):
1300
+ for offset in range(0, len(part), 4096): send('text', text=part[offset:offset + 4096])
1301
+ send('done')
1302
+ except Exception as error:
1303
+ send('error', type=type(error).__name__, message=str(error))
1304
+ `)();
1305
+
1306
+ // packages/safe-bash-command-python/src/build-dependencies.ts
1307
+ function createPythonBuildDependencies(options) {
1308
+ const isolated = { ...options };
1309
+ for (const key of ["packages", "requirements", "packageProfile", "provisioning"]) Reflect.deleteProperty(isolated, key);
1310
+ const hook = createPythonBuildBackend(isolated), command = createPythonExecutorCommands(isolated)[0];
1311
+ return async (input, caller) => {
1312
+ const context = { ...caller };
1313
+ context.signal.throwIfAborted();
1314
+ const request = structuredClone(input), { source, buildSystem, configSettings } = request;
1315
+ if (typeof source !== "string" || !source || request.name !== void 0 && typeof request.name !== "string" || !buildSystem || typeof buildSystem.backend !== "string" || !buildSystem.backend || [buildSystem.requires, buildSystem.check, buildSystem.backendPath].some((values) => !Array.isArray(values) || values.some((value) => typeof value !== "string"))) throw new TypeError("Invalid Python build dependency request");
1316
+ if (configSettings !== void 0 && (!configSettings || typeof configSettings !== "object" || Array.isArray(configSettings) || Object.values(configSettings).some((value) => typeof value !== "string" && (!Array.isArray(value) || value.some((item) => typeof item !== "string"))))) throw new TypeError("Invalid Python build configuration");
1317
+ if (context.maxBytes !== Infinity && (!Number.isSafeInteger(context.maxBytes) || context.maxBytes < 0)) throw new RangeError("Invalid Python build metadata limit");
1318
+ const install = async (requirements2) => {
1319
+ if (!requirements2.length) return;
1320
+ const result = await command.execute({ ...context, command: "python", args: ["-m", "pip", "install", "--", ...requirements2], stdin: toByteSource6("") });
1321
+ context.signal.throwIfAborted();
1322
+ if (result.exitCode) throw new Error("Python build dependency installation exited with status " + result.exitCode);
1323
+ };
1324
+ const check = async (requirements2, against) => {
1325
+ if (!requirements2.length) return [];
1326
+ const receipt = await isolated.environment.prepare(context);
1327
+ let installed;
1328
+ try {
1329
+ if (!receipt.records && receipt.restore?.length) throw new Error("Python build dependency checks require an installed metadata manifest");
1330
+ installed = receipt.records?.map((record2) => record2[0]) ?? [];
1331
+ } finally {
1332
+ await isolated.environment.finish(receipt);
1333
+ }
1334
+ const status = await hook({ hook: "check_build_requirements", source, requirements: requirements2, installed }, context);
1335
+ if (status.conflicting.length) throw Object.assign(new Error("Some build dependencies for " + (request.name ?? source) + " conflict with " + against + ": " + [...status.conflicting].sort(([a, b], [c, d]) => a < c ? -1 : a > c ? 1 : b < d ? -1 : b > d ? 1 : 0).map(([installed2, wanted]) => installed2 + " is incompatible with " + wanted).join(", ") + "."), { name: "InstallationError" });
1336
+ return status.missing;
1337
+ };
1338
+ await install(buildSystem.requires);
1339
+ const missing = await check(buildSystem.check, "PEP 517/518 supported requirements");
1340
+ if (missing.length) await context.stderr.write(new TextEncoder().encode("Missing build requirements in pyproject.toml for " + (request.name ?? source) + ".\nThe project does not specify a build backend, and pip cannot fall back to setuptools without " + missing.map((value) => "'" + value + "'").join(" and ") + ".\n"));
1341
+ const requirements = await hook({ hook: "get_requires_for_build_wheel", source, backend: buildSystem.backend, backendPath: buildSystem.backendPath, ...configSettings === void 0 ? {} : { configSettings } }, context);
1342
+ await install(await check(requirements, "the backend dependencies"));
1343
+ };
1344
+ }
1345
+
1346
+ // packages/safe-bash-command-python/src/source-snapshot.ts
1347
+ import { compareIdentity } from "@poe-platform/safe-fs/core";
1348
+ import { FsError as FsError2 } from "../../safe-bash-contracts/index.js";
1349
+ import { resolvePath } from "../../safe-bash-contracts/path.js";
1350
+ import { yieldTurn } from "../../safe-bash-contracts/yield.js";
1351
+ import { admitCopySource, admitCopyDestination, copyCheckedSource } from "../../safe-bash-io-engine/commands/copy-source.js";
1352
+ var serial = 0;
1353
+ async function createPythonSourceSnapshot(source, directory, context) {
1354
+ const { fs, signal } = context, settings = { signal };
1355
+ signal.throwIfAborted();
1356
+ if (!fs.iterateDirectory) throw new Error("Python source snapshots require lazy directory enumeration");
1357
+ const origin = await fs.realpath(resolvePath(context.cwd, source), settings);
1358
+ const parentPath = await fs.realpath(resolvePath(context.cwd, directory), settings);
1359
+ const capabilities = await fs.capabilitiesFor?.(parentPath, settings) ?? fs.capabilities;
1360
+ if (!fs.prepareDirectory || !fs.removeTreeConditional || !fs.confineExtraction || !capabilities.atomicTreeRemoval) throw new Error("Python source snapshots require confined writes, conditional directory creation and tree removal");
1361
+ const original = await fs.stat(origin, settings), parent = await fs.stat(parentPath, settings);
1362
+ if (original.type !== "directory" || parent.type !== "directory") throw new FsError2("ENOTDIR", { path: origin });
1363
+ let path = "", owned;
1364
+ for (let attempt = 0; attempt < 16; attempt++) {
1365
+ path = resolvePath(parentPath, ".python-source-" + ++serial);
1366
+ try {
1367
+ owned = await fs.prepareDirectory(path, { parent, expected: null, mode: 448, signal });
1368
+ break;
1369
+ } catch (error) {
1370
+ signal.throwIfAborted();
1371
+ if (!(error instanceof FsError2) || !["EEXIST", "EAGAIN"].includes(error.code)) throw error;
1372
+ }
1373
+ }
1374
+ if (!owned) throw new Error("Python source snapshot names exhausted");
1375
+ let disposing;
1376
+ const snapshot = { path, dispose() {
1377
+ return disposing ??= (async () => {
1378
+ const current = await fs.lstat(path);
1379
+ if (compareIdentity(owned, current) !== "same") throw new Error("Python source snapshot identity changed");
1380
+ await fs.removeTreeConditional(path, { parent, expected: current });
1381
+ })();
1382
+ } };
1383
+ try {
1384
+ const destination = await fs.confineExtraction([path], settings);
1385
+ if (compareIdentity(owned, await fs.lstat(path, settings)) !== "same") throw new Error("Python source snapshot identity changed");
1386
+ let activeCleanup;
1387
+ context.registerCleanup?.(() => activeCleanup?.());
1388
+ const copyContext = { ...context, registerCleanup(cleanup) {
1389
+ activeCleanup = cleanup;
1390
+ }, fs: new Proxy(destination, { get(target, key) {
1391
+ const owner = key === "openReadFile" ? fs : target, value = Reflect.get(owner, key);
1392
+ return typeof value === "function" ? value.bind(owner) : value;
1393
+ } }) };
1394
+ const metadata = async (from, to, expected) => {
1395
+ const current = await fs.lstat(from, settings);
1396
+ if (compareIdentity(expected, current) !== "same") throw new Error("Python source identity changed while copying");
1397
+ if (destination.chmod) await destination.chmod(to, current.mode & 4095, settings);
1398
+ if (destination.utimes) await destination.utimes(to, current.atimeMs, current.mtimeMs, settings);
1399
+ };
1400
+ const copy = async (from, to, expected) => {
1401
+ signal.throwIfAborted();
1402
+ if (expected.type === "symlink") {
1403
+ if (!destination.symlink || !fs.readlink) throw new Error("Python source snapshots require symlink support");
1404
+ const target = await fs.readlink(from, settings);
1405
+ if (compareIdentity(expected, await fs.lstat(from, settings)) !== "same") throw new Error("Python source identity changed while copying");
1406
+ await destination.symlink(target, to, settings);
1407
+ return;
1408
+ }
1409
+ if (expected.type === "file") {
1410
+ await admitCopySource(context, from);
1411
+ await admitCopyDestination(copyContext, to, true);
1412
+ try {
1413
+ await copyCheckedSource(copyContext, from, to, expected, true);
1414
+ } finally {
1415
+ activeCleanup = void 0;
1416
+ }
1417
+ } else if (expected.type === "directory") {
1418
+ if (to !== path) await destination.mkdir(to, { mode: 448, signal });
1419
+ for await (const entry of fs.iterateDirectory(from, settings)) {
1420
+ await yieldTurn(signal);
1421
+ if (!entry.name || entry.name === "." || entry.name === ".." || entry.name.includes("/") || entry.name.includes("\0")) throw new Error("Invalid Python source directory entry");
1422
+ const child = resolvePath(from, entry.name);
1423
+ if (child === path || from === origin && [".tox", ".nox"].includes(entry.name)) continue;
1424
+ await copy(child, resolvePath(to, entry.name), await fs.lstat(child, settings));
1425
+ }
1426
+ } else throw new Error("Unsupported Python source entry: " + from);
1427
+ await metadata(from, to, expected);
1428
+ };
1429
+ await copy(origin, path, original);
1430
+ signal.throwIfAborted();
1431
+ return snapshot;
1432
+ } catch (error) {
1433
+ try {
1434
+ await snapshot.dispose();
1435
+ } catch (cleanup) {
1436
+ throw new AggregateError([error, cleanup], "Python source snapshot cleanup failed");
1437
+ }
1438
+ throw error;
1439
+ }
1440
+ }
1441
+
647
1442
  // packages/safe-bash-command-python/src/llm-manifest.ts
648
1443
  var pythonLlmManifest = {
649
1444
  "llm": "0.27.1",
@@ -1008,6 +1803,151 @@ del _install_safe_llm_import
1008
1803
  }
1009
1804
  }
1010
1805
 
1806
+ // packages/safe-bash-command-python/src/build-wheel.ts
1807
+ import { FsError as FsError3 } from "../../safe-bash-contracts/index.js";
1808
+ import { resolvePath as resolvePath2, basename } from "../../safe-bash-contracts/path.js";
1809
+ async function publishPythonBuildWheel(source, directory, maxBytes, context) {
1810
+ const { fs, signal } = context, settings = { signal };
1811
+ signal.throwIfAborted();
1812
+ if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError("Invalid Python wheel size limit");
1813
+ const filename = basename(source);
1814
+ if (!filename.endsWith(".whl") || filename.includes("\\") || filename.includes("\0")) throw new TypeError("Invalid Python build wheel filename");
1815
+ const root = await fs.realpath(resolvePath2(context.cwd, directory), settings);
1816
+ const caps = await fs.capabilitiesFor?.(root, settings) ?? fs.capabilities;
1817
+ if (!fs.confineExtraction || !fs.prepareDirectory || !caps.retainedStagingWrite || !caps.retainedStagingCleanup || !caps.retainedRead || !caps.atomicFileStaging) throw new Error("Python wheel publication requires confined retained atomic staging");
1818
+ const artifact = await openPythonPackageFile(context, resolvePath2(context.cwd, source), maxBytes);
1819
+ if (!artifact) throw new Error("Python wheel publication requires retained source reads");
1820
+ let reading;
1821
+ try {
1822
+ const confined = await fs.confineExtraction([root], settings);
1823
+ const destination = new Proxy(confined, { get(target, key) {
1824
+ const owner = key === "openReadFile" ? fs : target, value = Reflect.get(owner, key);
1825
+ return typeof value === "function" ? value.bind(owner) : value;
1826
+ } });
1827
+ const parent = await destination.stat(root, settings), path = resolvePath2(root, artifact.key);
1828
+ try {
1829
+ await destination.prepareDirectory(path, { parent, expected: null, mode: 448, signal });
1830
+ } catch (error) {
1831
+ if (!(error instanceof FsError3) || !["EEXIST", "EAGAIN"].includes(error.code)) throw error;
1832
+ }
1833
+ if ((await destination.lstat(path, settings)).type !== "directory") throw new Error("Python wheel publication directory changed");
1834
+ const output = resolvePath2(path, filename);
1835
+ const body = async function* () {
1836
+ for (let offset = 0; offset < artifact.size; ) {
1837
+ const bytes = await (reading = artifact.read(offset, 65536));
1838
+ offset += bytes.length;
1839
+ yield bytes;
1840
+ }
1841
+ };
1842
+ const published = await stagePythonPackage({ ...context, fs: destination }, path, body(), maxBytes, () => {
1843
+ }, (key) => {
1844
+ if (key !== artifact.key) throw new Error("Built Python wheel integrity mismatch");
1845
+ }, () => output);
1846
+ if (!published) throw new Error("Python wheel publication requires retained atomic staging");
1847
+ try {
1848
+ return { url: "file://" + output.split("/").map(encodeURIComponent).join("/"), digest: published.key, size: published.size };
1849
+ } finally {
1850
+ await published.close();
1851
+ }
1852
+ } finally {
1853
+ try {
1854
+ await reading?.catch(() => {
1855
+ });
1856
+ } finally {
1857
+ await artifact.close();
1858
+ }
1859
+ }
1860
+ }
1861
+
1862
+ // packages/safe-bash-command-python/src/source-environment.ts
1863
+ import { compareIdentity as compareIdentity2 } from "@poe-platform/safe-fs/core";
1864
+ import { FsError as FsError4, toByteSource as toByteSource7 } from "../../safe-bash-contracts/index.js";
1865
+ import { resolvePath as resolvePath3 } from "../../safe-bash-contracts/path.js";
1866
+ var serial2 = 0;
1867
+ function createPythonSourcePackageEnvironment(options, build) {
1868
+ if (options.prepareRequirements || !build.python.createExecutor || !build.directory) throw new TypeError("Source packages require an asynchronous build executor and caller storage");
1869
+ const prepareRequirement = async (requirement, context) => {
1870
+ const { fs, signal } = context, settings = { signal };
1871
+ let source = requirement;
1872
+ if (source.startsWith("file:")) {
1873
+ const url = new URL(source);
1874
+ if (url.host && url.host !== "localhost") return requirement;
1875
+ source = decodeURIComponent(url.pathname);
1876
+ } else if (source.includes("://")) return requirement;
1877
+ source = resolvePath3(context.cwd, source);
1878
+ try {
1879
+ if ((await fs.stat(source, settings)).type !== "directory") return requirement;
1880
+ } catch (error) {
1881
+ if (error instanceof FsError4 && error.code === "ENOENT") return requirement;
1882
+ throw error;
1883
+ }
1884
+ if (!context.stdout || !context.stderr || !context.env) throw new TypeError("Source package preparation requires command output and environment context");
1885
+ if (!fs.prepareDirectory || !fs.removeTreeConditional || !fs.confineExtraction) throw new Error("Source packages require conditional caller storage");
1886
+ const root = await fs.realpath(resolvePath3(context.cwd, build.directory), settings), parent = await fs.stat(root, settings);
1887
+ const caps = await fs.capabilitiesFor?.(root, settings) ?? fs.capabilities;
1888
+ if (!caps.atomicTreeRemoval) throw new Error("Source packages require conditional tree cleanup");
1889
+ let path = "", owned;
1890
+ for (let attempt = 0; attempt < 16; attempt++) {
1891
+ path = resolvePath3(root, ".python-build-" + ++serial2);
1892
+ try {
1893
+ owned = await fs.prepareDirectory(path, { parent, expected: null, mode: 448, signal });
1894
+ break;
1895
+ } catch (error) {
1896
+ if (!(error instanceof FsError4) || !["EEXIST", "EAGAIN"].includes(error.code)) throw error;
1897
+ signal.throwIfAborted();
1898
+ }
1899
+ }
1900
+ if (!owned) throw new Error("Python build directory names exhausted");
1901
+ const environment = createPythonBuildEnvironment(options);
1902
+ let cleaning;
1903
+ const remove = async () => {
1904
+ const current = await fs.lstat(path);
1905
+ if (compareIdentity2(owned, current) !== "same") throw new Error("Python build directory identity changed");
1906
+ await fs.removeTreeConditional(path, { parent, expected: current });
1907
+ };
1908
+ const cleanup = () => cleaning ??= (async () => {
1909
+ try {
1910
+ await environment.dispose();
1911
+ } finally {
1912
+ await remove();
1913
+ }
1914
+ })();
1915
+ try {
1916
+ const confined = await fs.confineExtraction([path], settings);
1917
+ if (compareIdentity2(owned, await fs.lstat(path, settings)) !== "same") throw new Error("Python build directory identity changed");
1918
+ const staging = new Proxy(confined, { get(target, key) {
1919
+ const owner = ["stat", "lstat", "realpath", "openReadFile", "iterateDirectory", "readlink", "confineExtraction", "removeTreeConditional"].includes(String(key)) ? fs : target, value = Reflect.get(owner, key);
1920
+ return typeof value === "function" ? value.bind(owner) : value;
1921
+ } });
1922
+ const command = { ...context, env: context.env, stdout: context.stdout, stderr: context.stderr, command: "python", args: [], stdin: toByteSource7("") };
1923
+ const snapshot = await createPythonSourceSnapshot(source, path, { ...command, fs: staging });
1924
+ const wheelDirectory = resolvePath3(path, "wheels");
1925
+ await confined.mkdir(wheelDirectory, settings);
1926
+ const configuration = { ...build.python, environment }, hook = createPythonBuildBackend(configuration), hookContext = { ...command, maxBytes: options.maxMetadataBytes ?? Infinity };
1927
+ const buildSystem = await hook({ hook: "read_build_system", source: snapshot.path }, hookContext);
1928
+ if (!buildSystem) throw new Error("Legacy setup.py source installation is not yet available");
1929
+ await createPythonBuildDependencies(configuration)({ source: snapshot.path, buildSystem }, hookContext);
1930
+ const filename = await hook({ hook: "build_wheel", source: snapshot.path, backend: buildSystem.backend, backendPath: buildSystem.backendPath, wheelDirectory }, hookContext);
1931
+ const published = await publishPythonBuildWheel(resolvePath3(wheelDirectory, filename), root, options.maxDownloadBytes ?? Infinity, context);
1932
+ await cleanup();
1933
+ return published.url;
1934
+ } catch (error) {
1935
+ try {
1936
+ await cleanup();
1937
+ } catch (retirement) {
1938
+ if (retirement === error) throw error;
1939
+ throw new AggregateError([error, retirement], "Python source build cleanup failed");
1940
+ }
1941
+ throw error;
1942
+ }
1943
+ };
1944
+ return createPythonPackageEnvironment({ ...options, async prepareRequirements(requirements, context) {
1945
+ const resolved = [];
1946
+ for (const requirement of new Set(requirements)) resolved.push(await prepareRequirement(requirement, context));
1947
+ return resolved;
1948
+ } });
1949
+ }
1950
+
1011
1951
  // packages/safe-bash-command-python/src/executor.ts
1012
1952
  var PythonInputChunkError = class extends RangeError {
1013
1953
  };
@@ -1035,8 +1975,8 @@ function createPythonExecutorCommands(options) {
1035
1975
  }
1036
1976
  let installation;
1037
1977
  try {
1038
- parsePythonInvocation(context.args, context.env);
1039
- installation = parsePythonInstallation(context.args);
1978
+ const invocation = parsePythonInvocation(context.args, context.env);
1979
+ installation = invocation.module?.name === "pip" ? parsePythonInstallation(invocation.module.args) : void 0;
1040
1980
  } catch (error) {
1041
1981
  if (!(error instanceof PythonInvocationError)) throw error;
1042
1982
  await writeBytes(context.stderr, new TextEncoder().encode("python: " + error.message + "\n"), context.signal);
@@ -1099,7 +2039,7 @@ function createPythonExecutorCommands(options) {
1099
2039
  const results = await Promise.allSettled([subscription, termination, filesystemRetirement, hostBridge?.close(), stdoutOperation?.close(), stderrOperation?.close(), ...pending]);
1100
2040
  await input.return?.(void 0);
1101
2041
  fragment = void 0;
1102
- if (packages) environment.finish(packages);
2042
+ if (packages) results.push(...await Promise.allSettled([Promise.resolve().then(() => environment.finish(packages))]));
1103
2043
  if (results[1]?.status === "fulfilled" && admitted) {
1104
2044
  activeWorkers--;
1105
2045
  admitted = false;
@@ -1149,7 +2089,7 @@ function createPythonExecutorCommands(options) {
1149
2089
  if (!Number.isSafeInteger(length) || length < 1 || length > maxTransferBytes) throw new RangeError("Invalid Python input request");
1150
2090
  let pulls = 0;
1151
2091
  while (!fragment || offset === fragment.length) {
1152
- if (++pulls % 128 === 0) await yieldTurn(signal);
2092
+ if (++pulls % 128 === 0) await yieldTurn2(signal);
1153
2093
  const next = await input.next();
1154
2094
  if (next.done) return [];
1155
2095
  inputBytes += next.value.length;
@@ -1185,10 +2125,11 @@ function createPythonExecutorCommands(options) {
1185
2125
  }
1186
2126
  options.onProgress?.({ phase: "initializing", command: context.command });
1187
2127
  const preparation = environment.prepare({
1188
- fs: context.fs,
1189
- cwd: context.cwd,
2128
+ ...context,
1190
2129
  signal,
1191
- requirements: [...options.packages ?? [], ...options.packageProfile ? pythonDocumentPackages : [], ...installation?.packages ?? []],
2130
+ ...installation?.controls,
2131
+ ...installation?.uninstall ? { uninstall: { packages: installation.packages, yes: !!installation.yes } } : {},
2132
+ requirements: [...options.packages ?? [], ...options.packageProfile ? pythonDocumentPackages : [], ...installation?.uninstall ? [] : installation?.packages ?? []],
1192
2133
  requirementFiles: [...options.requirements ?? [], ...installation?.requirements ?? []]
1193
2134
  }).then((value) => {
1194
2135
  packages = value;
@@ -1458,9 +2399,20 @@ function pythonExecutorCommands(options) {
1458
2399
  }
1459
2400
 
1460
2401
  export {
2402
+ createPythonLlmPackageManager,
2403
+ createPythonLlmTemplateLoader,
2404
+ createPythonLlmFragmentLoader,
2405
+ createPythonLlmLoaderProvider,
2406
+ createPythonLlmLoaderDiscovery,
1461
2407
  createPythonLlmToolLoader,
2408
+ createPythonBuildEnvironment,
2409
+ createPythonBuildBackend,
2410
+ createPythonBuildDependencies,
2411
+ createPythonSourceSnapshot,
1462
2412
  pythonLlmPackages,
1463
2413
  installPythonLlmPackages,
2414
+ publishPythonBuildWheel,
2415
+ createPythonSourcePackageEnvironment,
1464
2416
  createPythonExecutorCommands,
1465
2417
  pythonExecutorCommands
1466
2418
  };