@poe-platform/safe-bash 0.1.822 → 0.1.824
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/safe-bash/chunks/{chunk-HNT2MR4W.js → chunk-363QOIDR.js} +2 -1
- package/dist/safe-bash/chunks/chunk-AGHV53VF.js +42 -0
- package/dist/safe-bash/chunks/{chunk-VF3Q2V3E.js → chunk-RAFD5ZAH.js} +1 -1
- package/dist/safe-bash/chunks/{chunk-67QE3JLU.js → chunk-TZRVK3NU.js} +1 -1
- package/dist/safe-bash/chunks/{chunk-IRJS54AA.js → chunk-VWLQDZFF.js} +20 -12
- package/dist/safe-bash/chunks/{chunk-NAETSXBW.js → chunk-ZJAXMB5M.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-LDR3PPM3.js → chunk-22QEL76N.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-QX3WKBLI.js → chunk-36FV6BAG.js} +5 -3
- package/dist/safe-bash/command-chunks/{chunk-BSLXS4PE.js → chunk-3DH5QBZC.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-ZSHXZ3XG.js → chunk-3EAZRKI4.js} +7 -7
- package/dist/safe-bash/command-chunks/{chunk-5EFNQ6FY.js → chunk-3EBQOZET.js} +216 -1
- package/dist/safe-bash/command-chunks/{chunk-KQE2D7PX.js → chunk-3HUCLYKJ.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-J4I7KCQQ.js → chunk-4JABZEDO.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-PVKSBXHB.js → chunk-4M6D4IUB.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-UVM6QG2H.js → chunk-4MVQE6CT.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-QWQHLMDX.js → chunk-4VTB4DGE.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-JQPDVCBU.js → chunk-565AAQTV.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-JSW6YH6T.js → chunk-5PRXCH54.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-42OQIFPW.js → chunk-5RLK2QSY.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-PYYAX5FB.js → chunk-63WEGFLL.js} +16 -16
- package/dist/safe-bash/command-chunks/{chunk-5QR4LTAK.js → chunk-6KZXFPOT.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-LJLB7TBN.js → chunk-6YTXU3NF.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-NNYOPI7K.js → chunk-7NF44M2V.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-ZRHKXN6W.js → chunk-7RQGRFHJ.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-QGSY4ZAU.js → chunk-7UYBNOOM.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-WOT2YXBT.js → chunk-7ZQVMPSB.js} +213 -42
- package/dist/safe-bash/command-chunks/{chunk-6REH56FL.js → chunk-AA254KE5.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-2VSNIHIW.js → chunk-APUXNXXQ.js} +11 -11
- package/dist/safe-bash/command-chunks/{chunk-4VHCA4KK.js → chunk-AVSINNZ6.js} +80 -3
- package/dist/safe-bash/command-chunks/{chunk-XE75H5ZZ.js → chunk-AVXSR5MO.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-22D6Q6SW.js → chunk-BKCVCMUX.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-UDRJKA3P.js → chunk-C23L4HZB.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-PAEAEFK3.js → chunk-C3EEMMQL.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-NDJPULBE.js → chunk-CEKBIZGU.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-75OUKYCM.js → chunk-CH5JDE3C.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-XNVL6AOV.js → chunk-CIYGC7WU.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-RUXY7GN5.js → chunk-CMJBRGU2.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-DGFMOT4O.js → chunk-CO4HYNPS.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-TMJG745A.js → chunk-CQLZM75U.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-T2LZWXWF.js → chunk-DBCTHLB5.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-6RE4MZYM.js → chunk-DBEJMI7J.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-XXUMECE4.js → chunk-DEEZYCOQ.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-YJMS5Q4Y.js → chunk-DHTTI7CE.js} +8 -8
- package/dist/safe-bash/command-chunks/{chunk-D3WMD32B.js → chunk-DLLGOC4Q.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-HVLF3Y4B.js → chunk-DYCYAIET.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-BZLLQWRK.js → chunk-E5CDHXFA.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-5NC2IQMQ.js → chunk-EV7A5FNN.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-MCUDSE4H.js → chunk-EWECMOQ5.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-2YC6MWQB.js → chunk-EY3UE26F.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-ZQ4NQ24M.js → chunk-FJLVISSD.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-WOO3YO2X.js → chunk-HTMGN3KL.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-2E53ZL5D.js → chunk-HU72CZBN.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-AH3ZEPAN.js → chunk-IGHFIRXK.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-R4NPYXQX.js → chunk-IKBTII7C.js} +285 -70
- package/dist/safe-bash/command-chunks/{chunk-J3ISXTYB.js → chunk-JTEAYB72.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-NR7POLKP.js → chunk-KCS25XUJ.js} +2 -2
- package/dist/safe-bash/command-chunks/chunk-LKAFLJ5L.js +123 -0
- package/dist/safe-bash/command-chunks/{chunk-EU5SR3LS.js → chunk-LSH6UKCT.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-WV3C6BRD.js → chunk-MHZ7UTRX.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-K6J3KFQ2.js → chunk-MSA5BQFK.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-OMD45J52.js → chunk-MW3L6JB7.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-AR65HNNX.js → chunk-N2TN7IBE.js} +1 -1
- package/dist/safe-bash/command-chunks/chunk-NNGWTDVJ.js +2819 -0
- package/dist/safe-bash/command-chunks/{chunk-NWA7ZJDV.js → chunk-NU64GWGN.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-4Q3HP33O.js → chunk-NW3S6T6R.js} +146 -123
- package/dist/safe-bash/command-chunks/chunk-NXCJNPL3.js +47 -0
- package/dist/safe-bash/command-chunks/{chunk-GAYTDZPP.js → chunk-ORWYNKKL.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-FCGMSOF6.js → chunk-P6YTNXRV.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-3RDEX4AF.js → chunk-PAS4YSNN.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-RKAL5VFP.js → chunk-PLVFJ6FF.js} +255 -616
- package/dist/safe-bash/command-chunks/{chunk-TTPT3OME.js → chunk-PSNTCRL5.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-CJIFJ45R.js → chunk-QBCEAYQT.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-FGK6HLSY.js → chunk-QC2RWSYM.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-HCZITM4C.js → chunk-QPY3NCMC.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-3TXPZLFI.js → chunk-R6RVR6ZZ.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-HQFX5XKV.js → chunk-RB7XAYO2.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-6PDXNY3T.js → chunk-RF5VEKFU.js} +9 -9
- package/dist/safe-bash/command-chunks/{chunk-VAMBWXDV.js → chunk-RUAGHZNF.js} +18 -18
- package/dist/safe-bash/command-chunks/{chunk-3MFYCMQU.js → chunk-S2T7DDGY.js} +16 -16
- package/dist/safe-bash/command-chunks/{chunk-LZHFVNEV.js → chunk-S3QYSI5E.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-ZLMCWAAU.js → chunk-STTXZKR6.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-AXUHORS3.js → chunk-SUYSZJI3.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-C5SLY3RB.js → chunk-U2C6A3QO.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-HCJWEB6P.js → chunk-U6IEK2CI.js} +7 -7
- package/dist/safe-bash/command-chunks/{chunk-NN6OWOJG.js → chunk-UC6DUY3K.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-64L57DEI.js → chunk-UQTU7H6T.js} +9 -9
- package/dist/safe-bash/command-chunks/{chunk-I7MEH76L.js → chunk-UYNJF5QN.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-ARA57PMC.js → chunk-V5PVNBOS.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-IAHYG67O.js → chunk-V5ZBRM5R.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-ZZ6EIACJ.js → chunk-VGHRMAI4.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-DZTT5SLG.js → chunk-VP7R7HKY.js} +5 -5
- package/dist/safe-bash/command-chunks/{chunk-TP5MZHCB.js → chunk-W5Y7Y7PS.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-ZTW2E5OT.js → chunk-WAE3OCOU.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-DBYY7IDI.js → chunk-WCM47S5A.js} +10 -10
- package/dist/safe-bash/command-chunks/{chunk-U6VSRO6E.js → chunk-WNVBS4GV.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-CXOAMPOV.js → chunk-WUIS2INH.js} +13 -13
- package/dist/safe-bash/command-chunks/{chunk-4HJEZGYN.js → chunk-YODHIRPW.js} +7 -7
- package/dist/safe-bash/command-chunks/{chunk-C3FERGHU.js → chunk-YRMTOLG5.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-GQV5RWMN.js → chunk-ZFXHSFTU.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-YXIJOYT6.js → chunk-ZUOCXDYW.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-LBBJ5S3W.js → chunk-ZZKJPLIH.js} +1 -1
- package/dist/safe-bash/command-chunks/{engine-UPLVIV72-IGXUDNO5.js → engine-UPLVIV72-MSSFJLK4.js} +1 -1
- package/dist/safe-bash/command-chunks/{prompt-tool-debug-ERJKK3XZ.js → prompt-tool-debug-3POFNRYP.js} +5 -5
- package/dist/safe-bash/commands/bc/index.browser.js +1 -1
- package/dist/safe-bash/commands/llm/index.browser.js +3 -1
- package/dist/safe-bash/commands/llm/index.js +1 -1
- package/dist/safe-bash/commands/python/executor.browser.js +12 -1
- package/dist/safe-bash/commands/python/executor.js +1 -1
- package/dist/safe-bash/commands/python/index.browser.js +23 -1
- package/dist/safe-bash/commands/python/index.js +1 -1
- package/dist/safe-bash/commands/python/source-archive.d.ts +2 -0
- package/dist/safe-bash/commands/python/source-archive.js +2 -0
- package/dist/safe-bash/commands/python/source-zip.d.ts +2 -0
- package/dist/safe-bash/commands/python/source-zip.js +2 -0
- package/dist/safe-bash/core.browser.js +2 -2
- package/dist/safe-bash/full-core.browser.js +30 -6
- package/dist/safe-bash/plugins/index.browser.js +3 -3
- package/dist/safe-bash/registry-entry.browser.js +2 -2
- package/dist/safe-bash/search.browser.js +1 -1
- package/dist/safe-bash/shell/runtime.d.ts +2 -2
- package/dist/safe-bash/shell/runtime.js +11 -2
- package/dist/safe-bash/shell/sync-extra-evaluators.js +12 -12
- package/dist/safe-bash/shell-entry.browser.js +2 -2
- package/dist/safe-bash-command-exiftool/index.js +41 -0
- package/dist/safe-bash-command-git/git_rust.wasm +0 -0
- package/dist/safe-bash-command-llm/collections.js +24 -65
- package/dist/safe-bash-command-llm/command.js +6 -6
- package/dist/safe-bash-command-llm/fragment-loaders.d.ts +3 -2
- package/dist/safe-bash-command-llm/index.d.ts +3 -2
- package/dist/safe-bash-command-llm/index.js +10 -8
- package/dist/safe-bash-command-llm/loader-provider.d.ts +25 -0
- package/dist/safe-bash-command-llm/openai-sse.js +2 -2
- package/dist/safe-bash-command-llm/openai.js +4 -4
- package/dist/safe-bash-command-llm/providers/index.js +4 -4
- package/dist/safe-bash-command-llm/providers/openai.js +4 -4
- package/dist/safe-bash-command-llm/python-unicode.d.ts +2 -0
- package/dist/safe-bash-command-llm/templates.d.ts +7 -2
- package/dist/safe-bash-command-llm/types.d.ts +7 -1
- package/dist/safe-bash-command-mcp/index.browser.js +3 -0
- package/dist/safe-bash-command-mcp/index.js +3 -0
- package/dist/safe-bash-command-playwright-cli/index.js +36 -36
- package/dist/safe-bash-command-playwright-cli/playwright/abilities.js +7 -7
- package/dist/safe-bash-command-playwright-cli/playwright/adapter.js +3 -3
- package/dist/safe-bash-command-playwright-cli/playwright/capability-events.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/capability-result.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/controller.js +15 -15
- package/dist/safe-bash-command-playwright-cli/playwright/download-capabilities.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/drop-capability.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/index.js +35 -35
- package/dist/safe-bash-command-playwright-cli/playwright/modal-capabilities.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/native-evaluation.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/native-json-snapshot.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/native-snapshot.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/native-storage-replacement.js +2 -2
- package/dist/safe-bash-command-playwright-cli/playwright/profile.js +3 -3
- package/dist/safe-bash-command-playwright-cli/playwright/recording-capabilities.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/route-capabilities.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/snapshot.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/standard-capabilities.js +6 -6
- package/dist/safe-bash-command-playwright-cli/playwright/storage-capabilities.js +5 -5
- package/dist/safe-bash-command-playwright-cli/playwright/tracing-capabilities.js +1 -1
- package/dist/safe-bash-command-playwright-cli/playwright/webmcp-capabilities.js +1 -1
- package/dist/safe-bash-command-python/build-backend.d.ts +93 -0
- package/dist/safe-bash-command-python/build-dependencies.d.ts +13 -0
- package/dist/safe-bash-command-python/build-environment.d.ts +3 -0
- package/dist/safe-bash-command-python/build-wheel.d.ts +8 -0
- package/dist/safe-bash-command-python/executor.d.ts +12 -2
- package/dist/safe-bash-command-python/executor.js +27 -4
- package/dist/safe-bash-command-python/index.js +27 -4
- package/dist/safe-bash-command-python/jspi.js +2 -2
- package/dist/safe-bash-command-python/llm-assets-node.js +1 -1
- package/dist/safe-bash-command-python/llm-fragment-loader.d.ts +6 -0
- package/dist/safe-bash-command-python/llm-functions-loader.d.ts +5 -1
- package/dist/safe-bash-command-python/llm-loader-discovery.d.ts +13 -0
- package/dist/safe-bash-command-python/llm-package-manager.d.ts +7 -0
- package/dist/safe-bash-command-python/llm-template-loader.d.ts +6 -0
- package/dist/safe-bash-command-python/manifest.d.ts +17 -0
- package/dist/safe-bash-command-python/provisioning.d.ts +37 -5
- package/dist/safe-bash-command-python/provisioning.js +2 -1
- package/dist/safe-bash-command-python/source-archive.d.ts +4 -0
- package/dist/safe-bash-command-python/source-archive.js +136 -0
- package/dist/safe-bash-command-python/source-environment.d.ts +12 -0
- package/dist/safe-bash-command-python/source-snapshot.d.ts +8 -0
- package/dist/safe-bash-command-python/source-zip.d.ts +8 -0
- package/dist/safe-bash-command-python/source-zip.js +8 -0
- package/dist/safe-bash-command-python/worker.js +3 -185
- package/dist/safe-bash-command-safejs/index.js +5 -5
- package/dist/safe-bash-command-safejs/runtime.js +3 -3
- package/dist/safe-bash-command-ssconvert/index.js +8 -8
- package/dist/safe-bash-compression-engine/bounded-codec.js +2 -2
- package/dist/safe-bash-compression-engine/codec-loader.js +1 -1
- package/dist/safe-bash-compression-engine/files.js +5 -5
- package/dist/safe-bash-compression-engine/index.js +4 -4
- package/dist/safe-bash-compression-engine/operand.js +5 -5
- package/dist/safe-bash-compression-engine/stream.js +4 -4
- package/dist/safe-bash-compression-engine/zstd-decode.js +3 -3
- package/dist/safe-bash-csv-engine/index.js +1 -1
- package/dist/safe-bash-csv-engine/python-utf7.d.ts +18 -0
- package/dist/safe-bash-csv-engine/text-decoder.d.ts +1 -0
- package/dist/safe-bash-csv-engine/text-decoder.js +5 -3
- package/dist/safe-bash-presentation-engine/accessibility.js +15 -15
- package/dist/safe-bash-presentation-engine/animation-editing.js +5 -5
- package/dist/safe-bash-presentation-engine/animations.js +5 -5
- package/dist/safe-bash-presentation-engine/backgrounds.js +6 -6
- package/dist/safe-bash-presentation-engine/chart-editing.js +18 -18
- package/dist/safe-bash-presentation-engine/chart-object-operations.js +12 -12
- package/dist/safe-bash-presentation-engine/charts.js +15 -15
- package/dist/safe-bash-presentation-engine/command-placeholder.js +39 -39
- package/dist/safe-bash-presentation-engine/comments.js +6 -6
- package/dist/safe-bash-presentation-engine/connector-operations.js +16 -16
- package/dist/safe-bash-presentation-engine/connectors.js +13 -13
- package/dist/safe-bash-presentation-engine/diff.js +21 -21
- package/dist/safe-bash-presentation-engine/drawing-format.js +7 -7
- package/dist/safe-bash-presentation-engine/drawing-operations.js +15 -15
- package/dist/safe-bash-presentation-engine/drawing-schema.js +8 -8
- package/dist/safe-bash-presentation-engine/equations.js +6 -6
- package/dist/safe-bash-presentation-engine/fields.js +6 -6
- package/dist/safe-bash-presentation-engine/image-extraction.js +7 -7
- package/dist/safe-bash-presentation-engine/image-formatting.js +16 -16
- package/dist/safe-bash-presentation-engine/image-insertion.js +6 -6
- package/dist/safe-bash-presentation-engine/image-replacement.js +7 -7
- package/dist/safe-bash-presentation-engine/images.js +6 -6
- package/dist/safe-bash-presentation-engine/index.js +104 -104
- package/dist/safe-bash-presentation-engine/layouts.js +5 -5
- package/dist/safe-bash-presentation-engine/links.js +5 -5
- package/dist/safe-bash-presentation-engine/masters.js +4 -4
- package/dist/safe-bash-presentation-engine/media-editing.js +13 -13
- package/dist/safe-bash-presentation-engine/media-extraction.js +7 -7
- package/dist/safe-bash-presentation-engine/media.js +6 -6
- package/dist/safe-bash-presentation-engine/memberships.js +5 -5
- package/dist/safe-bash-presentation-engine/notes.js +6 -6
- package/dist/safe-bash-presentation-engine/ole-insertion.js +6 -6
- package/dist/safe-bash-presentation-engine/opaque-objects.js +2 -2
- package/dist/safe-bash-presentation-engine/package-tools.js +6 -6
- package/dist/safe-bash-presentation-engine/presentation-model.js +38 -38
- package/dist/safe-bash-presentation-engine/presentation-settings.js +5 -5
- package/dist/safe-bash-presentation-engine/properties.js +6 -6
- package/dist/safe-bash-presentation-engine/retained-inspection.js +3 -3
- package/dist/safe-bash-presentation-engine/retained-text.js +16 -16
- package/dist/safe-bash-presentation-engine/sanitize.js +7 -7
- package/dist/safe-bash-presentation-engine/selectors.js +2 -2
- package/dist/safe-bash-presentation-engine/shape-groups.js +15 -15
- package/dist/safe-bash-presentation-engine/shape-operations.js +14 -14
- package/dist/safe-bash-presentation-engine/shape-path-operations.js +16 -16
- package/dist/safe-bash-presentation-engine/shape-paths.js +5 -5
- package/dist/safe-bash-presentation-engine/shape-selection.js +15 -15
- package/dist/safe-bash-presentation-engine/shape-transforms.js +5 -5
- package/dist/safe-bash-presentation-engine/shapes.js +11 -11
- package/dist/safe-bash-presentation-engine/slide-copy.js +7 -7
- package/dist/safe-bash-presentation-engine/slide-import.js +6 -6
- package/dist/safe-bash-presentation-engine/slide-merge-split.js +8 -8
- package/dist/safe-bash-presentation-engine/slide-model.js +25 -25
- package/dist/safe-bash-presentation-engine/slide-removal.js +7 -7
- package/dist/safe-bash-presentation-engine/slide-transfer-budget.js +2 -2
- package/dist/safe-bash-presentation-engine/slides.js +5 -5
- package/dist/safe-bash-presentation-engine/table-operations.js +17 -17
- package/dist/safe-bash-presentation-engine/table-spans.js +16 -16
- package/dist/safe-bash-presentation-engine/tables.js +15 -15
- package/dist/safe-bash-presentation-engine/tags.js +6 -6
- package/dist/safe-bash-presentation-engine/template-bindings.js +16 -16
- package/dist/safe-bash-presentation-engine/template-repeat.js +26 -26
- package/dist/safe-bash-presentation-engine/text-fitting.js +11 -11
- package/dist/safe-bash-presentation-engine/text-frames.js +11 -11
- package/dist/safe-bash-presentation-engine/text-paragraphs.js +11 -11
- package/dist/safe-bash-presentation-engine/text-reading.js +3 -3
- package/dist/safe-bash-presentation-engine/text-replacement.js +6 -6
- package/dist/safe-bash-presentation-engine/text-runs.js +8 -8
- package/dist/safe-bash-presentation-engine/themes.js +5 -5
- package/dist/safe-bash-presentation-engine/transitions.js +6 -6
- package/dist/safe-bash-presentation-engine/validation.js +2 -2
- package/dist/safe-bash-presentation-engine/xml-parts.js +5 -5
- package/dist/safe-bash-zip-engine/index.js +1 -1
- package/dist/safe-bash-zip-engine/zip/repair.js +1 -1
- package/dist/safe-bash-zip-engine/zip/sfx.js +1 -1
- package/dist/safe-bash-zip-engine/zip-format.d.ts +3 -1
- package/dist/safe-bash-zip-engine/zip-format.js +1 -1
- package/dist/safe-playwright-cloudflare/shell-browser-resource.d.ts +1 -1
- package/dist/safe-playwright-cloudflare/shell-browser-resource.js +35 -20
- package/package.json +13 -3
- package/dist/safe-bash/chunks/chunk-PSD2RSOS.js +0 -31
- package/dist/safe-bash/command-chunks/chunk-RHXV2KG5.js +0 -1466
- package/dist/safe-bash/command-chunks/{biff-AYJZR2YV-YFF3BK43.js → biff-AYJZR2YV-2BK47I2W.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-SKDJCMGI.js → chunk-ATUZ7J3B.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-4Q4R3VZU.js → chunk-ETFJ4L7F.js} +0 -0
- package/dist/safe-bash/command-chunks/{chunk-QWHBQBPB.js → chunk-EXDMIGZY.js} +6 -6
- package/dist/safe-bash/command-chunks/{chunk-SKF6PGA5.js → chunk-I2OKE2NI.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-2H6RJPCQ.js → chunk-JDDZRKC4.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-3TP4SSPS.js → chunk-SRXARACJ.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-D54L2IOZ.js → chunk-Y7UXJ77P.js} +3 -3
- package/dist/safe-bash/command-chunks/{dif-BXKPQOCO-FVJYYGYB.js → dif-BXKPQOCO-4KB3RC6G.js} +4 -4
- package/dist/safe-bash/command-chunks/{sylk-25D7TL2Y-NJKBZZTF.js → sylk-25D7TL2Y-RU6ANKGT.js} +4 -4
|
@@ -0,0 +1,2819 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPythonPackageEnvironment,
|
|
3
|
+
createPythonPackageManifestStore,
|
|
4
|
+
pythonDocumentPackages,
|
|
5
|
+
stagePythonPackage
|
|
6
|
+
} from "./chunk-IKBTII7C.js";
|
|
7
|
+
import {
|
|
8
|
+
pythonShellDispatchActive
|
|
9
|
+
} from "./chunk-NHAG4DWL.js";
|
|
10
|
+
import {
|
|
11
|
+
openPythonPackageFile
|
|
12
|
+
} from "./chunk-NXCJNPL3.js";
|
|
13
|
+
import {
|
|
14
|
+
PythonInvocationError,
|
|
15
|
+
parsePythonInvocation,
|
|
16
|
+
pythonPackageUrlHash
|
|
17
|
+
} from "./chunk-3EBQOZET.js";
|
|
18
|
+
import {
|
|
19
|
+
pythonLlmProvider
|
|
20
|
+
} from "./chunk-YYRVGXJH.js";
|
|
21
|
+
import {
|
|
22
|
+
createPythonExecutorPool
|
|
23
|
+
} from "./chunk-GT27M77E.js";
|
|
24
|
+
import {
|
|
25
|
+
PythonFailure,
|
|
26
|
+
isPythonFailureCategory,
|
|
27
|
+
reportPythonFailure
|
|
28
|
+
} from "./chunk-TO6UNFEF.js";
|
|
29
|
+
import {
|
|
30
|
+
createPythonHostBridge
|
|
31
|
+
} from "./chunk-EPYJSPOY.js";
|
|
32
|
+
|
|
33
|
+
// packages/safe-bash-command-python/src/executor.ts
|
|
34
|
+
import { PythonFileSystem, PythonStatTranslator } from "@poe-platform/safe-fs/core";
|
|
35
|
+
import { validateExitCode } from "../../safe-bash-contracts/command.js";
|
|
36
|
+
import { openCommandFile } from "../../safe-bash-contracts/filesystem-descriptor.js";
|
|
37
|
+
|
|
38
|
+
// packages/safe-bash-command-python/src/reply.ts
|
|
39
|
+
import { FsError } from "../../safe-bash-contracts/errors.js";
|
|
40
|
+
function encodePythonReply(value, maxBytes) {
|
|
41
|
+
let used = 0;
|
|
42
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
43
|
+
const reserve = (bytes) => {
|
|
44
|
+
used += bytes;
|
|
45
|
+
if (used > maxBytes) throw new FsError("EFBIG", { syscall: "python reply" });
|
|
46
|
+
};
|
|
47
|
+
const string = (text) => {
|
|
48
|
+
if (text.length + 2 > maxBytes - used) throw new FsError("EFBIG", { syscall: "python reply" });
|
|
49
|
+
reserve(2);
|
|
50
|
+
for (let index = 0; index < text.length; index++) {
|
|
51
|
+
const code = text.charCodeAt(index);
|
|
52
|
+
if (code < 32) reserve([8, 9, 10, 12, 13].includes(code) ? 2 : 6);
|
|
53
|
+
else if (code === 34 || code === 92) reserve(2);
|
|
54
|
+
else if (code < 128) reserve(1);
|
|
55
|
+
else if (code < 2048) reserve(2);
|
|
56
|
+
else if (code >= 55296 && code <= 56319 && text.charCodeAt(index + 1) >= 56320 && text.charCodeAt(index + 1) <= 57343) {
|
|
57
|
+
reserve(4);
|
|
58
|
+
index++;
|
|
59
|
+
} else reserve(code >= 55296 && code <= 57343 ? 6 : 3);
|
|
60
|
+
}
|
|
61
|
+
return text;
|
|
62
|
+
};
|
|
63
|
+
const own = (input, depth) => {
|
|
64
|
+
if (depth > 64) throw new FsError("EFBIG", { syscall: "python reply" });
|
|
65
|
+
if (input === null || input === void 0) {
|
|
66
|
+
reserve(4);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
if (typeof input === "string") return string(input);
|
|
70
|
+
if (typeof input === "number") {
|
|
71
|
+
reserve(Number.isFinite(input) ? String(input).length : 4);
|
|
72
|
+
return input;
|
|
73
|
+
}
|
|
74
|
+
if (typeof input === "boolean") {
|
|
75
|
+
reserve(input ? 4 : 5);
|
|
76
|
+
return input;
|
|
77
|
+
}
|
|
78
|
+
if (typeof input !== "object") throw new TypeError("Invalid Python reply value");
|
|
79
|
+
if (ancestors.has(input)) throw new TypeError("Cyclic Python reply value");
|
|
80
|
+
ancestors.add(input);
|
|
81
|
+
try {
|
|
82
|
+
if (input instanceof Uint8Array || Array.isArray(input)) {
|
|
83
|
+
if (input.length > maxBytes - used) throw new FsError("EFBIG", { syscall: "python reply" });
|
|
84
|
+
reserve(2);
|
|
85
|
+
const output2 = [];
|
|
86
|
+
for (let index = 0; index < input.length; index++) {
|
|
87
|
+
if (index) reserve(1);
|
|
88
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, String(index));
|
|
89
|
+
if (descriptor && !("value" in descriptor)) throw new TypeError("Accessor Python reply");
|
|
90
|
+
output2.push(own(descriptor?.value, depth + 1));
|
|
91
|
+
}
|
|
92
|
+
return output2;
|
|
93
|
+
}
|
|
94
|
+
reserve(2);
|
|
95
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
96
|
+
let count = 0;
|
|
97
|
+
let visited = 0;
|
|
98
|
+
for (const key in input) {
|
|
99
|
+
if (!Object.hasOwn(input, key)) continue;
|
|
100
|
+
if (++visited > maxBytes) throw new FsError("EFBIG", { syscall: "python reply" });
|
|
101
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
102
|
+
if (!("value" in descriptor)) throw new TypeError("Accessor Python reply");
|
|
103
|
+
if (descriptor.value === void 0 || typeof descriptor.value === "function") continue;
|
|
104
|
+
if (count++) reserve(1);
|
|
105
|
+
string(key);
|
|
106
|
+
reserve(1);
|
|
107
|
+
output[key] = own(descriptor.value, depth + 1);
|
|
108
|
+
}
|
|
109
|
+
return output;
|
|
110
|
+
} finally {
|
|
111
|
+
ancestors.delete(input);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const owned = own(value, 0);
|
|
115
|
+
return new TextEncoder().encode(JSON.stringify(owned));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// packages/safe-bash-command-python/src/installation.ts
|
|
119
|
+
var PythonInstallationError = class extends Error {
|
|
120
|
+
};
|
|
121
|
+
var pythonInstallationHelp = `Usage: python -m pip install [OPTIONS] PACKAGE ...
|
|
122
|
+
python -m pip uninstall [-y/--yes] PACKAGE ...
|
|
123
|
+
Install: -r/--requirement FILE, -e/--editable PATH, --pre, -U/--upgrade,
|
|
124
|
+
--force-reinstall, --no-cache-dir
|
|
125
|
+
Both accept -h/--help and --.
|
|
126
|
+
`;
|
|
127
|
+
function parsePythonInstallation(pipArgs) {
|
|
128
|
+
const controls = {};
|
|
129
|
+
const packages = [];
|
|
130
|
+
const requirements = [];
|
|
131
|
+
if (pipArgs.length === 0 || pipArgs[0] === "--help" || pipArgs[0] === "-h") {
|
|
132
|
+
if (pipArgs.length > 1) throw new PythonInvocationError("unsupported pip option or argument after help: " + pipArgs[1]);
|
|
133
|
+
return { packages, requirements, controls, help: true };
|
|
134
|
+
}
|
|
135
|
+
const uninstall = pipArgs[0] === "uninstall";
|
|
136
|
+
let yes = false;
|
|
137
|
+
if (!uninstall && pipArgs[0] !== "install") throw new PythonInvocationError("unsupported pip command " + pipArgs[0]);
|
|
138
|
+
let operands = false;
|
|
139
|
+
let help = false;
|
|
140
|
+
for (let index = 1; index < pipArgs.length; index++) {
|
|
141
|
+
const option = pipArgs[index];
|
|
142
|
+
if (operands || !option.startsWith("-")) {
|
|
143
|
+
packages.push(option);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const flag = { "--pre": "pre", "--no-cache-dir": "noCache", "-U": "upgrade", "--upgrade": "upgrade", "--force-reinstall": "forceReinstall" }[option];
|
|
147
|
+
if (!uninstall && flag) {
|
|
148
|
+
controls[flag] = true;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (option === "--") {
|
|
152
|
+
operands = true;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (option === "--help" || option === "-h") {
|
|
156
|
+
help = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (uninstall && (option === "-y" || option === "--yes")) {
|
|
160
|
+
yes = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (!uninstall) {
|
|
164
|
+
const kind = ["requirement", "editable"].find((name) => option.startsWith("-" + name[0]) || option.split("=", 1)[0] === "--" + name);
|
|
165
|
+
if (kind) {
|
|
166
|
+
const prefix = "--" + kind;
|
|
167
|
+
const path = option.startsWith(prefix + "=") ? option.slice(prefix.length + 1) : option === prefix || option.length === 2 ? pipArgs[++index] : option.slice(2);
|
|
168
|
+
if (!path || path.startsWith("-")) throw new PythonInvocationError(option + " requires a path");
|
|
169
|
+
(kind === "editable" ? controls.editable ??= [] : requirements).push(path);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw new PythonInvocationError("unsupported pip option " + option);
|
|
174
|
+
}
|
|
175
|
+
if (!help && packages.length === 0 && requirements.length === 0 && !controls.editable?.length) throw new PythonInvocationError("pip " + pipArgs[0] + " requires at least one package or requirements file");
|
|
176
|
+
return { packages, requirements, help, controls, ...uninstall ? { uninstall, yes } : {} };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// packages/safe-bash-command-python/src/executor.ts
|
|
180
|
+
import { readBytes, writeBytes } from "../../safe-bash-contracts/io.js";
|
|
181
|
+
import { createOutputOperation } from "../../safe-bash-contracts/output.js";
|
|
182
|
+
import { inheritYieldCheckpoint, yieldTurn as yieldTurn2 } from "../../safe-bash-contracts/yield.js";
|
|
183
|
+
|
|
184
|
+
// packages/safe-bash-command-python/src/llm-package-manager.ts
|
|
185
|
+
import { toByteSource } from "../../safe-bash-contracts/index.js";
|
|
186
|
+
function createPythonLlmPackageManager(options) {
|
|
187
|
+
if (!options.createExecutor || !options.environment || options.provisioning) throw new TypeError("LLM package management requires an asynchronous executor and a shared Python environment");
|
|
188
|
+
const planners = /* @__PURE__ */ new WeakMap();
|
|
189
|
+
const pool = createPythonExecutorPool({
|
|
190
|
+
createExecutor: options.createExecutor,
|
|
191
|
+
...options.maxConcurrentWorkers === void 0 || options.maxConcurrentWorkers === Infinity ? {} : { maxConcurrentExecutors: options.maxConcurrentWorkers }
|
|
192
|
+
});
|
|
193
|
+
const configuration = { ...options, createExecutor: pool.createExecutor, createCapabilities(context) {
|
|
194
|
+
const capabilities = options.createCapabilities?.(context) ?? {};
|
|
195
|
+
if (capabilities.llm_packages) throw new Error("Python package capability is reserved");
|
|
196
|
+
const capability = planners.get(context.args);
|
|
197
|
+
return capability ? { ...capabilities, llm_packages: capability } : capabilities;
|
|
198
|
+
} };
|
|
199
|
+
const installer = createPythonExecutorCommands(configuration)[0];
|
|
200
|
+
const { environment: ignoredEnvironment, packages: ignoredPackages, requirements: ignoredRequirements, packageProfile: ignoredProfile, ...planning } = configuration;
|
|
201
|
+
const command = createPythonExecutorCommands(planning)[0];
|
|
202
|
+
return async ({ context, args }) => {
|
|
203
|
+
if (!Array.isArray(args) || !["install", "uninstall"].includes(args[0]) || args.some((value) => typeof value !== "string")) throw new TypeError("Invalid LLM package command");
|
|
204
|
+
context.signal.throwIfAborted();
|
|
205
|
+
let planned;
|
|
206
|
+
const { registerCleanup: ignoredCleanup, argumentValues: ignoredArguments, ...isolated } = context;
|
|
207
|
+
const invocation = { ...isolated, command: "python", args: ["-c", pythonLlmPackageProgram, ...args], stdin: toByteSource("") };
|
|
208
|
+
planners.set(invocation.args, { async call(value) {
|
|
209
|
+
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");
|
|
210
|
+
planned = value.slice(1);
|
|
211
|
+
return null;
|
|
212
|
+
} });
|
|
213
|
+
let result;
|
|
214
|
+
try {
|
|
215
|
+
result = await command.execute(invocation);
|
|
216
|
+
} finally {
|
|
217
|
+
planners.delete(invocation.args);
|
|
218
|
+
}
|
|
219
|
+
if (result.exitCode || !planned) return result;
|
|
220
|
+
context.signal.throwIfAborted();
|
|
221
|
+
return installer.execute({ ...isolated, command: "python", args: ["-m", "pip", ...planned] });
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
var pythonLlmPackageProgram = String.raw`
|
|
225
|
+
import sys
|
|
226
|
+
import safe_host
|
|
227
|
+
import llm.cli
|
|
228
|
+
|
|
229
|
+
def _safe_package_request(module, run_name):
|
|
230
|
+
if module != 'pip' or run_name != '__main__':
|
|
231
|
+
raise RuntimeError('Unexpected LLM package entrypoint')
|
|
232
|
+
safe_host.call('llm_packages', list(sys.argv))
|
|
233
|
+
|
|
234
|
+
llm.cli.run_module = _safe_package_request
|
|
235
|
+
llm.cli.cli.main(args=sys.argv[1:], prog_name='llm')
|
|
236
|
+
`;
|
|
237
|
+
|
|
238
|
+
// packages/safe-bash-command-python/src/llm-plugin-setup.ts
|
|
239
|
+
var pythonLlmPluginSetup = /* @__PURE__ */ (() => String.raw`
|
|
240
|
+
def load_plugins(request):
|
|
241
|
+
import llm.plugins as manager
|
|
242
|
+
manager.load_plugins()
|
|
243
|
+
if request['plugins']:
|
|
244
|
+
original = (manager.DEFAULT_PLUGINS, manager.LLM_LOAD_PLUGINS, manager._loaded)
|
|
245
|
+
try:
|
|
246
|
+
manager.DEFAULT_PLUGINS = ()
|
|
247
|
+
manager.LLM_LOAD_PLUGINS = ','.join(request['plugins'])
|
|
248
|
+
manager._loaded = False
|
|
249
|
+
manager.load_plugins()
|
|
250
|
+
finally:
|
|
251
|
+
manager.DEFAULT_PLUGINS, manager.LLM_LOAD_PLUGINS, manager._loaded = original
|
|
252
|
+
`)();
|
|
253
|
+
|
|
254
|
+
// packages/safe-bash-command-python/src/llm-json-loader.ts
|
|
255
|
+
import { LlmLoaderLookupError } from "../../safe-bash-command-llm/index.js";
|
|
256
|
+
import { toByteSource as toByteSource2 } from "../../safe-bash-contracts/index.js";
|
|
257
|
+
function createPythonLlmJsonLoader(options, capabilityName, program, label) {
|
|
258
|
+
if (!options.createExecutor) throw new TypeError(`Python ${label} loading requires an asynchronous executor`);
|
|
259
|
+
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`);
|
|
260
|
+
const plugins = Object.freeze([...options.plugins ?? []]), capabilities = /* @__PURE__ */ new WeakMap();
|
|
261
|
+
const command = createPythonExecutorCommands({ ...options, createCapabilities(context) {
|
|
262
|
+
const provided = options.createCapabilities?.(context) ?? {};
|
|
263
|
+
if (provided[capabilityName]) throw new Error(`Python ${label} capability is reserved`);
|
|
264
|
+
const capability = capabilities.get(context.args);
|
|
265
|
+
if (!capability) throw new Error(`Unknown Python ${label} invocation`);
|
|
266
|
+
return { ...provided, [capabilityName]: capability };
|
|
267
|
+
} })[0];
|
|
268
|
+
return async (request, context, admitBytes) => {
|
|
269
|
+
if (!context) throw new TypeError(`Python ${label} loading requires caller context`);
|
|
270
|
+
const { maxBytes, signal } = context;
|
|
271
|
+
if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError(`Invalid Python ${label} byte limit`);
|
|
272
|
+
signal.throwIfAborted();
|
|
273
|
+
let size = 0, done = false, failure;
|
|
274
|
+
const chunks = [];
|
|
275
|
+
const invocation = { ...context, command: "python", args: ["-c", program], signal, env: context.env ?? {}, stdin: toByteSource2(""), stdout: context.stdout ?? { async write() {
|
|
276
|
+
} }, stderr: context.stderr ?? { async write() {
|
|
277
|
+
} } };
|
|
278
|
+
capabilities.set(invocation.args, { async call(input) {
|
|
279
|
+
try {
|
|
280
|
+
signal.throwIfAborted();
|
|
281
|
+
if (!input || typeof input !== "object" || Array.isArray(input) || done) throw new TypeError(`Invalid Python ${label} message`);
|
|
282
|
+
const message = input;
|
|
283
|
+
if (message.op === "request") return { ...request, plugins };
|
|
284
|
+
if (message.op === "missing" || message.op === "lookup" || message.op === "error") {
|
|
285
|
+
failure ??= message.op === "error" ? new Error(String(message.message)) : new LlmLoaderLookupError(String(message.message));
|
|
286
|
+
done = true;
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
if (message.op === "done") {
|
|
290
|
+
done = true;
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (message.op !== "text" || typeof message.text !== "string" || message.text.length > 8192) throw new TypeError(`Invalid Python ${label} text window`);
|
|
294
|
+
const bytes = new TextEncoder().encode(message.text).length;
|
|
295
|
+
if (bytes > 16384) throw new TypeError(`Invalid Python ${label} text window`);
|
|
296
|
+
if (bytes > maxBytes - size) throw new RangeError(`Python ${label} byte limit exceeded`);
|
|
297
|
+
admitBytes?.(bytes);
|
|
298
|
+
size += bytes;
|
|
299
|
+
if (bytes) chunks.push(message.text);
|
|
300
|
+
return null;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
failure ??= error;
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
} });
|
|
306
|
+
try {
|
|
307
|
+
const result = await command.execute(invocation);
|
|
308
|
+
signal.throwIfAborted();
|
|
309
|
+
if (failure) throw failure;
|
|
310
|
+
if (result.exitCode) throw new Error(`Python ${label} interpreter exited with status ${result.exitCode}`);
|
|
311
|
+
if (!done) throw new Error(`Python ${label} interpreter returned no result`);
|
|
312
|
+
return JSON.parse(chunks.join(""));
|
|
313
|
+
} finally {
|
|
314
|
+
capabilities.delete(invocation.args);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// packages/safe-bash-command-python/src/llm-template-loader.ts
|
|
320
|
+
function createPythonLlmTemplateLoader(options, prefix) {
|
|
321
|
+
if (typeof prefix !== "string" || !prefix) throw new TypeError("Python template loader requires a prefix");
|
|
322
|
+
const load = createPythonLlmJsonLoader(options, "llm_templates", pythonLlmTemplateProgram, "template");
|
|
323
|
+
return async (value, signal, context) => {
|
|
324
|
+
if (!context) throw new TypeError("Python template loading requires caller context");
|
|
325
|
+
if (typeof value !== "string") throw new TypeError("Python template input must be a string");
|
|
326
|
+
const template = await load({ prefix, value }, { ...context, signal });
|
|
327
|
+
if (!template || typeof template !== "object" || Array.isArray(template) || typeof template.name !== "string") throw new TypeError("Invalid native Python template");
|
|
328
|
+
return template;
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
var pythonLlmTemplateProgram = /* @__PURE__ */ (() => String.raw`
|
|
332
|
+
import json, llm, safe_host
|
|
333
|
+
|
|
334
|
+
${pythonLlmPluginSetup}
|
|
335
|
+
|
|
336
|
+
def send(op, **fields):
|
|
337
|
+
return safe_host.call('llm_templates', dict(op=op, **fields))
|
|
338
|
+
|
|
339
|
+
def main():
|
|
340
|
+
request = send('request')
|
|
341
|
+
try:
|
|
342
|
+
load_plugins(request)
|
|
343
|
+
loaders = llm.get_template_loaders()
|
|
344
|
+
except Exception as error:
|
|
345
|
+
send('lookup', message=str(error))
|
|
346
|
+
return
|
|
347
|
+
if request['prefix'] not in loaders:
|
|
348
|
+
send('missing', message='Unknown template prefix: ' + request['prefix'])
|
|
349
|
+
return
|
|
350
|
+
template = loaders[request['prefix']](request['value'])
|
|
351
|
+
encoder = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'))
|
|
352
|
+
for part in encoder.iterencode(template.model_dump(exclude_none=True)):
|
|
353
|
+
for offset in range(0, len(part), 4096): send('text', text=part[offset:offset + 4096])
|
|
354
|
+
send('done')
|
|
355
|
+
try:
|
|
356
|
+
main()
|
|
357
|
+
except Exception as error:
|
|
358
|
+
send('error', message=str(error))
|
|
359
|
+
`)();
|
|
360
|
+
|
|
361
|
+
// packages/safe-bash-command-python/src/llm-fragment-loader.ts
|
|
362
|
+
import { createLlmSpool, createLlmUrlSource, LlmLoaderLookupError as LlmLoaderLookupError2 } from "../../safe-bash-command-llm/index.js";
|
|
363
|
+
import { toByteSource as toByteSource3 } from "../../safe-bash-contracts/index.js";
|
|
364
|
+
function deferred() {
|
|
365
|
+
let resolve, reject;
|
|
366
|
+
const promise = new Promise((yes, no) => {
|
|
367
|
+
resolve = yes;
|
|
368
|
+
reject = no;
|
|
369
|
+
});
|
|
370
|
+
void promise.catch(() => {
|
|
371
|
+
});
|
|
372
|
+
return { promise, resolve, reject };
|
|
373
|
+
}
|
|
374
|
+
function createPythonLlmFragmentLoader(options, prefix) {
|
|
375
|
+
if (!options.createExecutor) throw new TypeError("Python fragment loading requires an asynchronous executor");
|
|
376
|
+
if (typeof prefix !== "string" || !prefix) throw new TypeError("Python fragment loader requires a prefix");
|
|
377
|
+
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");
|
|
378
|
+
const plugins = Object.freeze([...options.plugins ?? []]), capabilities = /* @__PURE__ */ new WeakMap();
|
|
379
|
+
const command = createPythonExecutorCommands({ ...options, createCapabilities(context) {
|
|
380
|
+
const provided = options.createCapabilities?.(context) ?? {};
|
|
381
|
+
if (provided.llm_fragments) throw new Error("Python fragment capability is reserved");
|
|
382
|
+
const capability = capabilities.get(context.args);
|
|
383
|
+
if (!capability) throw new Error("Unknown Python fragment invocation");
|
|
384
|
+
return { ...provided, llm_fragments: capability };
|
|
385
|
+
} })[0];
|
|
386
|
+
return async function* (value, context) {
|
|
387
|
+
if (typeof value !== "string") throw new TypeError("Python fragment input must be a string");
|
|
388
|
+
const { fs, cwd, maxBytes } = context;
|
|
389
|
+
if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError("Invalid Python fragment byte limit");
|
|
390
|
+
const controller = new AbortController(), signal = AbortSignal.any([context.signal, controller.signal]);
|
|
391
|
+
signal.throwIfAborted();
|
|
392
|
+
let ready = deferred(), resume;
|
|
393
|
+
let current, spool, closed = false, bytes = 0;
|
|
394
|
+
const sources = /* @__PURE__ */ new Set();
|
|
395
|
+
const admit = (size) => {
|
|
396
|
+
if (size > maxBytes - bytes) throw new RangeError("Python fragment byte limit exceeded");
|
|
397
|
+
bytes += size;
|
|
398
|
+
};
|
|
399
|
+
const fail = (error) => {
|
|
400
|
+
ready.reject(error);
|
|
401
|
+
resume?.resolve(false);
|
|
402
|
+
controller.abort(error);
|
|
403
|
+
};
|
|
404
|
+
const aborted = () => {
|
|
405
|
+
ready.reject(signal.reason);
|
|
406
|
+
resume?.resolve(false);
|
|
407
|
+
};
|
|
408
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
409
|
+
const retire = async (source) => {
|
|
410
|
+
try {
|
|
411
|
+
await source.dispose();
|
|
412
|
+
} finally {
|
|
413
|
+
sources.delete(source);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const dispatch = async (input) => {
|
|
417
|
+
signal.throwIfAborted();
|
|
418
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) throw new TypeError("Invalid Python fragment message");
|
|
419
|
+
const message = input;
|
|
420
|
+
if (message.op === "request") return { prefix, value, plugins };
|
|
421
|
+
if (message.op === "missing" || message.op === "lookup") throw new LlmLoaderLookupError2(String(message.message));
|
|
422
|
+
if (message.op === "error") throw new Error(String(message.message));
|
|
423
|
+
if (message.op === "begin") {
|
|
424
|
+
if (current || resume) throw new Error("Python fragment already active");
|
|
425
|
+
if (message.type !== "text" && message.type !== "attachment") throw new TypeError("Invalid Python fragment type");
|
|
426
|
+
if (message.type === "attachment" && (typeof message.mimeType !== "string" || typeof message.id !== "string")) throw new TypeError("Invalid Python fragment metadata");
|
|
427
|
+
let source;
|
|
428
|
+
if (message.url !== void 0) {
|
|
429
|
+
if (message.type !== "attachment" || typeof message.url !== "string" || !context.capabilities?.fetch) throw new TypeError("Attachment URL loading is not configured");
|
|
430
|
+
source = createLlmUrlSource({ url: message.url, fetch: context.capabilities.fetch, signal, maxBytes: maxBytes - bytes, admitBytes: admit });
|
|
431
|
+
} else {
|
|
432
|
+
const owned = await createLlmSpool(fs, cwd, signal, "input");
|
|
433
|
+
spool = owned;
|
|
434
|
+
source = { bytes: owned.replay(), dispose: () => owned.close() };
|
|
435
|
+
}
|
|
436
|
+
sources.add(source);
|
|
437
|
+
current = message.type === "text" ? { type: "text", source: { ...source, normalizeNewlines: false } } : { type: "attachment", source, mimeType: message.mimeType, id: message.id };
|
|
438
|
+
sources.delete(source);
|
|
439
|
+
sources.add(current.source);
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
if (!current) throw new Error("No active Python fragment");
|
|
443
|
+
if (message.op === "text" || message.op === "bytes") {
|
|
444
|
+
if (!spool) throw new Error("Remote Python fragments cannot accept inline bytes");
|
|
445
|
+
let chunk;
|
|
446
|
+
if (message.op === "text") {
|
|
447
|
+
if (current.type !== "text" || typeof message.text !== "string" || message.text.length > 8192) throw new TypeError("Invalid Python fragment text window");
|
|
448
|
+
chunk = new TextEncoder().encode(message.text);
|
|
449
|
+
if (chunk.length > 16384) throw new TypeError("Invalid Python fragment text window");
|
|
450
|
+
} else {
|
|
451
|
+
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");
|
|
452
|
+
chunk = Uint8Array.from(message.bytes);
|
|
453
|
+
}
|
|
454
|
+
admit(chunk.length);
|
|
455
|
+
await spool.write(chunk);
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
if (message.op === "end") {
|
|
459
|
+
const continuation = resume = deferred();
|
|
460
|
+
ready.resolve(current);
|
|
461
|
+
return continuation.promise;
|
|
462
|
+
}
|
|
463
|
+
throw new TypeError("Invalid Python fragment operation");
|
|
464
|
+
};
|
|
465
|
+
const invocation = { ...context, command: "python", args: ["-c", pythonLlmFragmentProgram], fs, cwd, signal, env: context.env ?? {}, stdin: toByteSource3(""), stdout: context.stdout ?? { async write() {
|
|
466
|
+
} }, stderr: context.stderr ?? { async write() {
|
|
467
|
+
} } };
|
|
468
|
+
capabilities.set(invocation.args, { async call(input) {
|
|
469
|
+
try {
|
|
470
|
+
return await dispatch(input);
|
|
471
|
+
} catch (error) {
|
|
472
|
+
fail(error);
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
} });
|
|
476
|
+
const running = Promise.resolve().then(() => command.execute(invocation)).then((result) => {
|
|
477
|
+
if (closed) return;
|
|
478
|
+
if (result.exitCode) fail(new Error(`Python fragment interpreter exited with status ${result.exitCode}`));
|
|
479
|
+
else ready.resolve(void 0);
|
|
480
|
+
}, (error) => {
|
|
481
|
+
if (!closed) fail(error);
|
|
482
|
+
});
|
|
483
|
+
let closing;
|
|
484
|
+
const close = () => closing ??= (async () => {
|
|
485
|
+
closed = true;
|
|
486
|
+
resume?.resolve(false);
|
|
487
|
+
controller.abort(new Error("Python fragment session closed"));
|
|
488
|
+
await running;
|
|
489
|
+
capabilities.delete(invocation.args);
|
|
490
|
+
signal.removeEventListener("abort", aborted);
|
|
491
|
+
await Promise.all([...sources].map(retire));
|
|
492
|
+
})();
|
|
493
|
+
context.registerCleanup?.(close);
|
|
494
|
+
try {
|
|
495
|
+
while (true) {
|
|
496
|
+
const fragment = await ready.promise;
|
|
497
|
+
if (!fragment) break;
|
|
498
|
+
let advance = false;
|
|
499
|
+
try {
|
|
500
|
+
yield fragment;
|
|
501
|
+
advance = true;
|
|
502
|
+
} finally {
|
|
503
|
+
await retire(fragment.source);
|
|
504
|
+
current = void 0;
|
|
505
|
+
spool = void 0;
|
|
506
|
+
ready = deferred();
|
|
507
|
+
const continuation = resume;
|
|
508
|
+
resume = void 0;
|
|
509
|
+
continuation?.resolve(advance);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} finally {
|
|
513
|
+
await close();
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
var pythonLlmFragmentProgram = /* @__PURE__ */ (() => String.raw`
|
|
518
|
+
import hashlib, llm, safe_host
|
|
519
|
+
from llm_safe_host import _attachment_type
|
|
520
|
+
|
|
521
|
+
${pythonLlmPluginSetup}
|
|
522
|
+
|
|
523
|
+
def send(op, **fields):
|
|
524
|
+
return safe_host.call('llm_fragments', dict(op=op, **fields))
|
|
525
|
+
|
|
526
|
+
def emit_attachment(value):
|
|
527
|
+
mime = _attachment_type(value)
|
|
528
|
+
if not value.content and value.path:
|
|
529
|
+
with open(value.path, 'rb') as source:
|
|
530
|
+
if value._id is None:
|
|
531
|
+
digest = hashlib.sha256()
|
|
532
|
+
for chunk in iter(lambda: source.read(4096), b''): digest.update(chunk)
|
|
533
|
+
value._id = digest.hexdigest()
|
|
534
|
+
source.seek(0)
|
|
535
|
+
send('begin', type='attachment', mimeType=mime, id=value.id())
|
|
536
|
+
for chunk in iter(lambda: source.read(4096), b''): send('bytes', bytes=list(chunk))
|
|
537
|
+
elif not value.content and value.url:
|
|
538
|
+
send('begin', type='attachment', mimeType=mime, id=value.id(), url=value.url)
|
|
539
|
+
else:
|
|
540
|
+
send('begin', type='attachment', mimeType=mime, id=value.id())
|
|
541
|
+
content = memoryview(value.content or b'')
|
|
542
|
+
for offset in range(0, len(content), 4096): send('bytes', bytes=list(content[offset:offset + 4096]))
|
|
543
|
+
|
|
544
|
+
def main():
|
|
545
|
+
request = send('request')
|
|
546
|
+
try:
|
|
547
|
+
load_plugins(request)
|
|
548
|
+
loaders = llm.get_fragment_loaders()
|
|
549
|
+
except Exception as error:
|
|
550
|
+
send('lookup', message=str(error))
|
|
551
|
+
return
|
|
552
|
+
if request['prefix'] not in loaders:
|
|
553
|
+
send('missing', message='Unknown fragment prefix: ' + request['prefix'])
|
|
554
|
+
return
|
|
555
|
+
result = loaders[request['prefix']](request['value'])
|
|
556
|
+
if not isinstance(result, list): result = [result]
|
|
557
|
+
for value in result:
|
|
558
|
+
if isinstance(value, llm.Attachment): emit_attachment(value)
|
|
559
|
+
elif isinstance(value, str):
|
|
560
|
+
send('begin', type='text')
|
|
561
|
+
for offset in range(0, len(value), 4096): send('text', text=value[offset:offset + 4096])
|
|
562
|
+
else: raise TypeError('Fragment loader must return text or attachments')
|
|
563
|
+
if not send('end'): return
|
|
564
|
+
try:
|
|
565
|
+
main()
|
|
566
|
+
except Exception as error:
|
|
567
|
+
send('error', message=str(error))
|
|
568
|
+
`)();
|
|
569
|
+
|
|
570
|
+
// packages/safe-bash-command-python/src/llm-loader-discovery.ts
|
|
571
|
+
function createPythonLlmLoaderProvider(options) {
|
|
572
|
+
const discover = createPythonLlmLoaderDiscovery(options), configured = { ...options, plugins: Object.freeze([...options.plugins ?? []]) };
|
|
573
|
+
return { discover, fragments: createPythonLlmFragmentLoader.bind(null, configured), templates: createPythonLlmTemplateLoader.bind(null, configured) };
|
|
574
|
+
}
|
|
575
|
+
function createPythonLlmLoaderDiscovery(options) {
|
|
576
|
+
const load = createPythonLlmJsonLoader(options, "llm_loaders", pythonLlmLoaderDiscoveryProgram, "loader discovery");
|
|
577
|
+
const configured = { ...options, plugins: Object.freeze([...options.plugins ?? []]) };
|
|
578
|
+
return async (context) => {
|
|
579
|
+
if (context.kind !== void 0 && context.kind !== "fragments" && context.kind !== "templates") throw new TypeError("Invalid native loader discovery kind");
|
|
580
|
+
const result = await load(context.kind === void 0 ? {} : { kind: context.kind }, context, context.admitBytes);
|
|
581
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) throw new TypeError("Invalid native loader discovery");
|
|
582
|
+
function loaders(entries, create) {
|
|
583
|
+
if (!Array.isArray(entries)) throw new TypeError("Invalid native loader discovery");
|
|
584
|
+
const values = /* @__PURE__ */ new Map();
|
|
585
|
+
for (const entry of entries) {
|
|
586
|
+
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");
|
|
587
|
+
const loader = create(configured, entry[0]);
|
|
588
|
+
if (entry[1] !== null) Object.defineProperty(loader, "description", { value: entry[1], enumerable: true });
|
|
589
|
+
values.set(entry[0], loader);
|
|
590
|
+
}
|
|
591
|
+
return values;
|
|
592
|
+
}
|
|
593
|
+
const data = result;
|
|
594
|
+
return { fragmentLoaders: loaders(data.fragments, createPythonLlmFragmentLoader), templateLoaders: loaders(data.templates, createPythonLlmTemplateLoader) };
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
var pythonLlmLoaderDiscoveryProgram = /* @__PURE__ */ (() => String.raw`
|
|
598
|
+
import json, llm, safe_host
|
|
599
|
+
|
|
600
|
+
${pythonLlmPluginSetup}
|
|
601
|
+
|
|
602
|
+
def send(op, **fields):
|
|
603
|
+
return safe_host.call('llm_loaders', dict(op=op, **fields))
|
|
604
|
+
|
|
605
|
+
def main():
|
|
606
|
+
request = send('request')
|
|
607
|
+
load_plugins(request)
|
|
608
|
+
encoder = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'))
|
|
609
|
+
kind = request.get('kind')
|
|
610
|
+
result = dict(fragments=[] if kind == 'templates' else [(prefix, loader.__doc__) for prefix, loader in llm.get_fragment_loaders().items()],
|
|
611
|
+
templates=[] if kind == 'fragments' else [(prefix, loader.__doc__) for prefix, loader in llm.get_template_loaders().items()])
|
|
612
|
+
for part in encoder.iterencode(result):
|
|
613
|
+
for offset in range(0, len(part), 4096): send('text', text=part[offset:offset + 4096])
|
|
614
|
+
send('done')
|
|
615
|
+
try:
|
|
616
|
+
main()
|
|
617
|
+
except Exception as error:
|
|
618
|
+
send('error', message=str(error))
|
|
619
|
+
`)();
|
|
620
|
+
|
|
621
|
+
// packages/safe-bash-command-python/src/llm-functions-loader.ts
|
|
622
|
+
import { createLlmSpool as createLlmSpool2, createLlmToolRegistry } from "../../safe-bash-command-llm/index.js";
|
|
623
|
+
import { toByteSource as toByteSource4 } from "../../safe-bash-contracts/index.js";
|
|
624
|
+
|
|
625
|
+
// packages/safe-bash-command-python/src/llm-functions-program.ts
|
|
626
|
+
var pythonLlmFunctionsProgram = /* @__PURE__ */ (() => String.raw`
|
|
627
|
+
import asyncio, codecs, inspect, json
|
|
628
|
+
import safe_host
|
|
629
|
+
from _poe_llm_capability import bridge
|
|
630
|
+
from llm.cli import _tools_from_code, _gather_tools
|
|
631
|
+
from llm import ToolOutput, Tool, Toolbox, get_tools, get_plugins
|
|
632
|
+
from llm.models import _wrap_tools, _get_instance
|
|
633
|
+
from llm_safe_host import _attachment_type
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def send(op, **fields):
|
|
637
|
+
return safe_host.call("llm_tools", dict(op=op, **fields))
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def load_definition(definition):
|
|
641
|
+
if "\n" in definition or not definition.endswith(".py"):
|
|
642
|
+
send("admit", size=len(definition.encode("utf-8")))
|
|
643
|
+
return definition
|
|
644
|
+
decoder = codecs.getincrementaldecoder("utf-8")()
|
|
645
|
+
chunks = []
|
|
646
|
+
try:
|
|
647
|
+
with open(definition, "rb") as source:
|
|
648
|
+
for chunk in iter(lambda: source.read(4096), b""):
|
|
649
|
+
send("admit", size=len(chunk))
|
|
650
|
+
chunks.append(decoder.decode(chunk))
|
|
651
|
+
chunks.append(decoder.decode(b"", final=True))
|
|
652
|
+
except FileNotFoundError:
|
|
653
|
+
from click import ClickException
|
|
654
|
+
raise ClickException("File not found: " + definition)
|
|
655
|
+
code = "".join(chunks).replace("\r\n", "\n").replace("\r", "\n")
|
|
656
|
+
return code + "\n" if "\n" not in code and code.endswith(".py") else code
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def write_text(identifier, value):
|
|
660
|
+
for offset in range(0, len(value), 4096):
|
|
661
|
+
send("text", id=identifier, text=value[offset:offset + 4096])
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def write_attachment(identifier, attachment):
|
|
665
|
+
mime = _attachment_type(attachment)
|
|
666
|
+
if attachment.content:
|
|
667
|
+
descriptor = dict(mimeType=mime, id=attachment.id())
|
|
668
|
+
handle = send("attachment", id=identifier, descriptor=descriptor)
|
|
669
|
+
content = memoryview(attachment.content)
|
|
670
|
+
for offset in range(0, len(content), 4096):
|
|
671
|
+
send("bytes", id=identifier, attachment=handle, bytes=list(content[offset:offset + 4096]))
|
|
672
|
+
elif attachment.path:
|
|
673
|
+
handle = send("attachment", id=identifier, descriptor=dict(mimeType=mime, id=attachment.id()))
|
|
674
|
+
with open(attachment.path, "rb") as source:
|
|
675
|
+
for chunk in iter(lambda: source.read(4096), b""):
|
|
676
|
+
send("bytes", id=identifier, attachment=handle, bytes=list(chunk))
|
|
677
|
+
elif attachment.url:
|
|
678
|
+
send("attachment", id=identifier, descriptor=dict(mimeType=mime, id=attachment.id(), url=attachment.url))
|
|
679
|
+
else:
|
|
680
|
+
send("attachment", id=identifier, descriptor=dict(mimeType=mime, id=attachment.id()))
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
async def main():
|
|
684
|
+
definitions = send("definitions")
|
|
685
|
+
selection = send("selection")
|
|
686
|
+
names = selection.get("names", [])
|
|
687
|
+
discovery = selection.get("discovery", False)
|
|
688
|
+
tools = []
|
|
689
|
+
try:
|
|
690
|
+
requested_plugins = selection.get("plugins", [])
|
|
691
|
+
if requested_plugins:
|
|
692
|
+
import llm.plugins as plugin_manager
|
|
693
|
+
plugin_manager.load_plugins()
|
|
694
|
+
original = (plugin_manager.DEFAULT_PLUGINS, plugin_manager.LLM_LOAD_PLUGINS, plugin_manager._loaded)
|
|
695
|
+
try:
|
|
696
|
+
# Reuse native explicit-distribution loading. The host's tooling
|
|
697
|
+
# registration guard and ambient-entrypoint prohibition stay active.
|
|
698
|
+
plugin_manager.DEFAULT_PLUGINS = ()
|
|
699
|
+
plugin_manager.LLM_LOAD_PLUGINS = ",".join(requested_plugins)
|
|
700
|
+
plugin_manager._loaded = False
|
|
701
|
+
plugin_manager.load_plugins()
|
|
702
|
+
finally:
|
|
703
|
+
plugin_manager.DEFAULT_PLUGINS, plugin_manager.LLM_LOAD_PLUGINS, plugin_manager._loaded = original
|
|
704
|
+
plugin_query = selection.get("pluginQuery")
|
|
705
|
+
if plugin_query is not None:
|
|
706
|
+
plugins = get_plugins(plugin_query["all"])
|
|
707
|
+
hooks = set(plugin_query["hooks"])
|
|
708
|
+
if hooks:
|
|
709
|
+
plugins = [plugin for plugin in plugins if hooks.intersection(plugin["hooks"])]
|
|
710
|
+
send("plugins", plugins=plugins)
|
|
711
|
+
registered = get_tools() if discovery and not names else {}
|
|
712
|
+
functions = []
|
|
713
|
+
for definition in definitions:
|
|
714
|
+
functions.extend(_tools_from_code(load_definition(definition)))
|
|
715
|
+
if discovery:
|
|
716
|
+
if names:
|
|
717
|
+
registered = {tool.name: tool for tool in functions + _gather_tools(names, [])}
|
|
718
|
+
else:
|
|
719
|
+
for tool in functions:
|
|
720
|
+
registered[tool.name] = tool
|
|
721
|
+
entries = [(key, tool, None) for key, tool in registered.items()]
|
|
722
|
+
else:
|
|
723
|
+
entries = [(tool.name, tool, None) for tool in functions]
|
|
724
|
+
for selection_index, selected in enumerate(_gather_tools(names, [])):
|
|
725
|
+
entries.extend((tool.name, tool, selection_index) for tool in _wrap_tools([selected]))
|
|
726
|
+
for key, tool, selection_index in entries:
|
|
727
|
+
if not isinstance(tool, Tool):
|
|
728
|
+
methods = []
|
|
729
|
+
for method in tool.method_tools():
|
|
730
|
+
methods.append(dict(name=method.name, description=method.description,
|
|
731
|
+
inputSchema=method.input_schema,
|
|
732
|
+
signature=str(inspect.signature(method.implementation)).replace("(self, ", "(").replace("(self)", "()")))
|
|
733
|
+
send("toolbox", name=key, tools=methods)
|
|
734
|
+
continue
|
|
735
|
+
index = len(tools)
|
|
736
|
+
tools.append(tool)
|
|
737
|
+
send("register", index=index, name=tool.name, registryKey=key, description=tool.description,
|
|
738
|
+
plugin=tool.plugin, selectionIndex=selection_index, inputSchema=tool.input_schema,
|
|
739
|
+
signature=str(inspect.signature(tool.implementation)),
|
|
740
|
+
asynchronous=inspect.iscoroutinefunction(tool.implementation))
|
|
741
|
+
except Exception as error:
|
|
742
|
+
send("failed", message=getattr(error, "message", str(error)))
|
|
743
|
+
return
|
|
744
|
+
send("ready", prepare=any(isinstance(_get_instance(tool.implementation), Toolbox) for tool in tools))
|
|
745
|
+
pending = set()
|
|
746
|
+
cancelled = False
|
|
747
|
+
|
|
748
|
+
async def execute(command):
|
|
749
|
+
identifier = command["id"]
|
|
750
|
+
try:
|
|
751
|
+
if command.get("prepare"):
|
|
752
|
+
asynchronous = command.get("asynchronous", False)
|
|
753
|
+
flag = "_async_prepared" if asynchronous else "_prepared"
|
|
754
|
+
instances = []
|
|
755
|
+
for tool in {tool.name: tool for tool in tools}.values():
|
|
756
|
+
instance = _get_instance(tool.implementation)
|
|
757
|
+
if isinstance(instance, Toolbox) and not getattr(instance, flag, False):
|
|
758
|
+
instances.append(instance)
|
|
759
|
+
for instance in instances:
|
|
760
|
+
if asynchronous:
|
|
761
|
+
await instance.prepare_async()
|
|
762
|
+
else:
|
|
763
|
+
instance.prepare()
|
|
764
|
+
setattr(instance, flag, True)
|
|
765
|
+
send("done", id=identifier)
|
|
766
|
+
return
|
|
767
|
+
output = tools[command["tool"]].implementation(**command["arguments"])
|
|
768
|
+
if inspect.isawaitable(output):
|
|
769
|
+
output = await output
|
|
770
|
+
attachments = []
|
|
771
|
+
if isinstance(output, ToolOutput):
|
|
772
|
+
attachments, output = output.attachments, output.output
|
|
773
|
+
if not isinstance(output, str):
|
|
774
|
+
output = json.dumps(output, default=repr)
|
|
775
|
+
write_text(identifier, output)
|
|
776
|
+
for attachment in attachments:
|
|
777
|
+
write_attachment(identifier, attachment)
|
|
778
|
+
send("done", id=identifier)
|
|
779
|
+
except Exception as error:
|
|
780
|
+
send("error", id=identifier, message=str(error))
|
|
781
|
+
|
|
782
|
+
def completed(task):
|
|
783
|
+
pending.discard(task)
|
|
784
|
+
# Host transport failures already reject all outstanding calls. Retrieve
|
|
785
|
+
# the exception so a completed task cannot leak an unobserved failure.
|
|
786
|
+
if not task.cancelled():
|
|
787
|
+
task.exception()
|
|
788
|
+
|
|
789
|
+
try:
|
|
790
|
+
while True:
|
|
791
|
+
command = await bridge.wait("begin", capability="llm_tools", value=dict(op="next"))
|
|
792
|
+
if command is None:
|
|
793
|
+
break
|
|
794
|
+
if command.get("cancel"):
|
|
795
|
+
cancelled = True
|
|
796
|
+
break
|
|
797
|
+
task = asyncio.create_task(execute(command))
|
|
798
|
+
pending.add(task)
|
|
799
|
+
task.add_done_callback(completed)
|
|
800
|
+
if pending and not cancelled:
|
|
801
|
+
await asyncio.gather(*pending)
|
|
802
|
+
finally:
|
|
803
|
+
for task in pending:
|
|
804
|
+
task.cancel()
|
|
805
|
+
if pending:
|
|
806
|
+
await asyncio.gather(*pending, return_exceptions=True)
|
|
807
|
+
|
|
808
|
+
asyncio.run(main())
|
|
809
|
+
`)();
|
|
810
|
+
|
|
811
|
+
// packages/safe-bash-command-python/src/llm-functions-loader.ts
|
|
812
|
+
function deferred2() {
|
|
813
|
+
let resolve, reject;
|
|
814
|
+
const promise = new Promise((yes, no) => {
|
|
815
|
+
resolve = yes;
|
|
816
|
+
reject = no;
|
|
817
|
+
});
|
|
818
|
+
return { promise, resolve, reject };
|
|
819
|
+
}
|
|
820
|
+
function record(value) {
|
|
821
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Invalid Python tool protocol");
|
|
822
|
+
return value;
|
|
823
|
+
}
|
|
824
|
+
function createPythonLlmToolLoader(options) {
|
|
825
|
+
if (!options.createExecutor) throw new TypeError("Python tool loading requires an asynchronous executor");
|
|
826
|
+
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");
|
|
827
|
+
const pluginNames = Object.freeze([...options.plugins ?? []]);
|
|
828
|
+
const capabilitiesByArguments = /* @__PURE__ */ new WeakMap();
|
|
829
|
+
const command = createPythonExecutorCommands({ ...options, createCapabilities: (current) => {
|
|
830
|
+
const capabilities = options.createCapabilities?.(current) ?? {};
|
|
831
|
+
if (capabilities.llm_tools) throw new Error("Python tool capability is reserved");
|
|
832
|
+
const capability = capabilitiesByArguments.get(current.args);
|
|
833
|
+
if (!capability) throw new Error("Unknown Python tool invocation");
|
|
834
|
+
return { ...capabilities, llm_tools: capability };
|
|
835
|
+
} })[0];
|
|
836
|
+
return async ({ context, definitions, toolNames = [], discovery = false, pluginQuery, maxInputBytes, maxOutputBytes }) => {
|
|
837
|
+
if (!Array.isArray(definitions) || definitions.some((value) => typeof value !== "string")) throw new TypeError("Python tool definitions must be strings");
|
|
838
|
+
if (!Array.isArray(toolNames) || toolNames.some((value) => typeof value !== "string") || typeof discovery !== "boolean") throw new TypeError("Invalid Python tool selection");
|
|
839
|
+
if (pluginQuery && (typeof pluginQuery.all !== "boolean" || !Array.isArray(pluginQuery.hooks) || pluginQuery.hooks.some((value) => typeof value !== "string"))) throw new TypeError("Invalid Python plugin query");
|
|
840
|
+
for (const limit of [maxInputBytes, maxOutputBytes]) if (limit !== Infinity && (!Number.isSafeInteger(limit) || limit < 0)) throw new RangeError("Invalid Python tool byte limit");
|
|
841
|
+
context.signal.throwIfAborted();
|
|
842
|
+
const controller = new AbortController();
|
|
843
|
+
const runtimeController = new AbortController();
|
|
844
|
+
const signal = AbortSignal.any([context.signal, controller.signal]);
|
|
845
|
+
const ready = deferred2();
|
|
846
|
+
const tools = [];
|
|
847
|
+
const toolboxes = [];
|
|
848
|
+
let plugins;
|
|
849
|
+
const spools = /* @__PURE__ */ new Set();
|
|
850
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
851
|
+
const queue = [];
|
|
852
|
+
let waiter;
|
|
853
|
+
let serial3 = 0, controls = 0, loaded = false, initializationFailed = false, closed = false, closing;
|
|
854
|
+
const encoder2 = new TextEncoder();
|
|
855
|
+
const fail = (error) => {
|
|
856
|
+
ready.reject(error);
|
|
857
|
+
queue.length = 0;
|
|
858
|
+
if (waiter) {
|
|
859
|
+
waiter.resolve({ cancel: true });
|
|
860
|
+
waiter = void 0;
|
|
861
|
+
} else queue.push({ cancel: true });
|
|
862
|
+
for (const job of jobs.values()) job.result.reject(error);
|
|
863
|
+
jobs.clear();
|
|
864
|
+
controller.abort(error);
|
|
865
|
+
};
|
|
866
|
+
const aborted = () => fail(signal.reason);
|
|
867
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
868
|
+
const spool = async () => {
|
|
869
|
+
const value = await createLlmSpool2(context.fs, context.cwd, signal);
|
|
870
|
+
spools.add(value);
|
|
871
|
+
if (closed || signal.aborted) {
|
|
872
|
+
await value.close();
|
|
873
|
+
spools.delete(value);
|
|
874
|
+
signal.throwIfAborted();
|
|
875
|
+
throw new Error("Python tool session is closed");
|
|
876
|
+
}
|
|
877
|
+
return value;
|
|
878
|
+
};
|
|
879
|
+
const source = (value) => ({ bytes: value.replay(), async dispose() {
|
|
880
|
+
try {
|
|
881
|
+
await value.close();
|
|
882
|
+
} finally {
|
|
883
|
+
spools.delete(value);
|
|
884
|
+
}
|
|
885
|
+
} });
|
|
886
|
+
const invoke = async (fields, callContext) => {
|
|
887
|
+
signal.throwIfAborted();
|
|
888
|
+
callContext.signal.throwIfAborted();
|
|
889
|
+
if (closed) throw new Error("Python tool session is closed");
|
|
890
|
+
const stop = () => fail(callContext.signal.reason);
|
|
891
|
+
callContext.signal.addEventListener("abort", stop, { once: true });
|
|
892
|
+
let output;
|
|
893
|
+
try {
|
|
894
|
+
output = await spool();
|
|
895
|
+
callContext.signal.throwIfAborted();
|
|
896
|
+
const result = deferred2(), id = ++serial3;
|
|
897
|
+
jobs.set(id, { result, output, attachments: [], files: /* @__PURE__ */ new Map(), bytes: 0, limit: Math.min(callContext.maxBytes, maxOutputBytes) });
|
|
898
|
+
const request = { id, ...fields };
|
|
899
|
+
if (waiter) {
|
|
900
|
+
waiter.resolve(request);
|
|
901
|
+
waiter = void 0;
|
|
902
|
+
} else queue.push(request);
|
|
903
|
+
return await result.promise;
|
|
904
|
+
} catch (error) {
|
|
905
|
+
if (output) {
|
|
906
|
+
await output.close();
|
|
907
|
+
spools.delete(output);
|
|
908
|
+
}
|
|
909
|
+
throw error;
|
|
910
|
+
} finally {
|
|
911
|
+
callContext.signal.removeEventListener("abort", stop);
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const prepare = async (callContext, mode) => {
|
|
915
|
+
const result = await invoke({ prepare: true, asynchronous: mode.async }, callContext);
|
|
916
|
+
await result.source?.dispose();
|
|
917
|
+
};
|
|
918
|
+
const dispatch = async (value) => {
|
|
919
|
+
const message = record(value);
|
|
920
|
+
if (message.op !== "next" && message.op !== "failed") signal.throwIfAborted();
|
|
921
|
+
if (message.op === "definitions") return [...definitions];
|
|
922
|
+
if (message.op === "selection") return { names: [...toolNames], discovery, ...pluginNames.length ? { plugins: [...pluginNames] } : {}, ...pluginQuery ? { pluginQuery: { all: pluginQuery.all, hooks: [...pluginQuery.hooks] } } : {} };
|
|
923
|
+
if (message.op === "admit") {
|
|
924
|
+
if (typeof message.size !== "number" || !Number.isSafeInteger(message.size) || message.size < 0) throw new TypeError("Invalid Python definition byte count");
|
|
925
|
+
controls += message.size;
|
|
926
|
+
if (controls > maxInputBytes) {
|
|
927
|
+
const error = new RangeError("Python tool definition byte limit exceeded");
|
|
928
|
+
fail(error);
|
|
929
|
+
throw error;
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
if (message.op === "failed") {
|
|
934
|
+
initializationFailed = true;
|
|
935
|
+
fail(new Error(String(message.message)));
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
if (message.op === "register" || message.op === "toolbox" || message.op === "plugins") {
|
|
939
|
+
controls += encoder2.encode(JSON.stringify(message)).length;
|
|
940
|
+
if (controls > maxInputBytes) throw new RangeError("Python tool definition byte limit exceeded");
|
|
941
|
+
}
|
|
942
|
+
if (message.op === "plugins") {
|
|
943
|
+
if (loaded || plugins || !pluginQuery || !Array.isArray(message.plugins)) throw new TypeError("Invalid Python plugin discovery");
|
|
944
|
+
plugins = Object.freeze(message.plugins.map((value2) => {
|
|
945
|
+
const plugin = record(value2);
|
|
946
|
+
if (typeof plugin.name !== "string" || !Array.isArray(plugin.hooks) || plugin.hooks.some((hook) => typeof hook !== "string") || plugin.version !== void 0 && typeof plugin.version !== "string") throw new TypeError("Invalid Python plugin metadata");
|
|
947
|
+
return Object.freeze({ name: plugin.name, hooks: Object.freeze(plugin.hooks), ...typeof plugin.version === "string" ? { version: plugin.version } : {} });
|
|
948
|
+
}));
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
if (message.op === "toolbox") {
|
|
952
|
+
if (loaded || typeof message.name !== "string" || !Array.isArray(message.tools)) throw new TypeError("Invalid Python toolbox registration");
|
|
953
|
+
const methods = message.tools.map((value2) => {
|
|
954
|
+
const method = record(value2);
|
|
955
|
+
const tool = {
|
|
956
|
+
name: String(method.name),
|
|
957
|
+
inputSchema: record(method.inputSchema),
|
|
958
|
+
...typeof method.description === "string" ? { description: method.description } : {},
|
|
959
|
+
...typeof method.signature === "string" ? { signature: method.signature } : {}
|
|
960
|
+
};
|
|
961
|
+
if (typeof method.name !== "string") throw new TypeError("Invalid Python toolbox method");
|
|
962
|
+
createLlmToolRegistry([tool]);
|
|
963
|
+
return tool;
|
|
964
|
+
});
|
|
965
|
+
toolboxes.push({ name: message.name, tools: methods });
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
if (message.op === "register") {
|
|
969
|
+
if (loaded || message.index !== tools.length || typeof message.name !== "string" || typeof message.signature !== "string" || typeof message.asynchronous !== "boolean") throw new TypeError("Invalid Python tool registration");
|
|
970
|
+
const index = tools.length;
|
|
971
|
+
const tool = {
|
|
972
|
+
name: message.name,
|
|
973
|
+
inputSchema: record(message.inputSchema),
|
|
974
|
+
signature: message.signature,
|
|
975
|
+
...typeof message.description === "string" ? { description: message.description } : {},
|
|
976
|
+
...typeof message.plugin === "string" ? { plugin: message.plugin } : {},
|
|
977
|
+
...typeof message.registryKey === "string" ? { registryKey: message.registryKey } : {},
|
|
978
|
+
...typeof message.selectionIndex === "number" ? { selectionIndex: message.selectionIndex } : {},
|
|
979
|
+
async: message.asynchronous,
|
|
980
|
+
async implementation(args, callContext) {
|
|
981
|
+
return invoke({ tool: index, arguments: args }, callContext);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
createLlmToolRegistry([tool]);
|
|
985
|
+
tools.push(tool);
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
if (message.op === "ready") {
|
|
989
|
+
if (loaded) throw new Error("Python tools already loaded");
|
|
990
|
+
if (message.prepare === true) for (let index = 0; index < tools.length; index++) tools[index] = { ...tools[index], prepare };
|
|
991
|
+
loaded = true;
|
|
992
|
+
ready.resolve();
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
if (message.op === "next") {
|
|
996
|
+
if (signal.aborted) return { cancel: true };
|
|
997
|
+
if (!loaded || waiter) throw new Error("Invalid Python tool request queue");
|
|
998
|
+
if (queue.length) return queue.shift();
|
|
999
|
+
waiter = deferred2();
|
|
1000
|
+
return waiter.promise;
|
|
1001
|
+
}
|
|
1002
|
+
if (typeof message.id !== "number") throw new TypeError("Invalid Python tool call identity");
|
|
1003
|
+
const job = jobs.get(message.id);
|
|
1004
|
+
if (!job) throw new Error("Unknown Python tool call");
|
|
1005
|
+
if (message.op === "text" || message.op === "bytes") {
|
|
1006
|
+
let bytes, target = job.output;
|
|
1007
|
+
if (message.op === "text") {
|
|
1008
|
+
if (typeof message.text !== "string" || message.text.length > 8192) throw new TypeError("Invalid Python tool output window");
|
|
1009
|
+
bytes = encoder2.encode(message.text);
|
|
1010
|
+
if (bytes.length > 16384) throw new TypeError("Invalid Python tool output window");
|
|
1011
|
+
} else {
|
|
1012
|
+
if (!Array.isArray(message.bytes) || message.bytes.length > 4096 || message.bytes.some((value2) => typeof value2 !== "number" || !Number.isInteger(value2) || value2 < 0 || value2 > 255)) throw new TypeError("Invalid Python attachment window");
|
|
1013
|
+
if (typeof message.attachment !== "number" || !job.files.has(message.attachment)) throw new Error("Unknown Python attachment");
|
|
1014
|
+
target = job.files.get(message.attachment);
|
|
1015
|
+
bytes = Uint8Array.from(message.bytes);
|
|
1016
|
+
}
|
|
1017
|
+
if (bytes.length > job.limit - job.bytes) throw new RangeError("Python tool output byte limit exceeded");
|
|
1018
|
+
job.bytes += bytes.length;
|
|
1019
|
+
await target.write(bytes);
|
|
1020
|
+
return null;
|
|
1021
|
+
}
|
|
1022
|
+
if (message.op === "attachment") {
|
|
1023
|
+
const metadata = record(message.descriptor);
|
|
1024
|
+
if (typeof metadata.mimeType !== "string" || typeof metadata.id !== "string" || metadata.url !== void 0 && typeof metadata.url !== "string") throw new TypeError("Invalid Python tool attachment");
|
|
1025
|
+
const size = encoder2.encode(JSON.stringify(metadata)).length;
|
|
1026
|
+
if (size > job.limit - job.bytes) throw new RangeError("Python tool attachment metadata limit exceeded");
|
|
1027
|
+
job.bytes += size;
|
|
1028
|
+
const index = job.attachments.length;
|
|
1029
|
+
if (metadata.url !== void 0) job.attachments.push({ mimeType: metadata.mimeType, id: metadata.id, url: metadata.url });
|
|
1030
|
+
else {
|
|
1031
|
+
const file = await spool();
|
|
1032
|
+
job.files.set(index, file);
|
|
1033
|
+
job.attachments.push({ mimeType: metadata.mimeType, id: metadata.id, source: source(file) });
|
|
1034
|
+
}
|
|
1035
|
+
return index;
|
|
1036
|
+
}
|
|
1037
|
+
if (message.op === "error") {
|
|
1038
|
+
jobs.delete(message.id);
|
|
1039
|
+
await Promise.all([job.output, ...job.files.values()].map(async (value2) => {
|
|
1040
|
+
await value2.close();
|
|
1041
|
+
spools.delete(value2);
|
|
1042
|
+
}));
|
|
1043
|
+
job.result.reject(new Error(String(message.message)));
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
if (message.op === "done") {
|
|
1047
|
+
jobs.delete(message.id);
|
|
1048
|
+
job.result.resolve({ source: source(job.output), attachments: job.attachments });
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
1051
|
+
throw new TypeError("Invalid Python tool operation");
|
|
1052
|
+
};
|
|
1053
|
+
const capability = { async call(value) {
|
|
1054
|
+
try {
|
|
1055
|
+
return await dispatch(value);
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
fail(error);
|
|
1058
|
+
throw error;
|
|
1059
|
+
}
|
|
1060
|
+
} };
|
|
1061
|
+
const { registerCleanup: ignoredParentCleanup, ...isolatedContext } = context;
|
|
1062
|
+
const invocation = { ...isolatedContext, signal: runtimeController.signal, command: "python", args: ["-c", pythonLlmFunctionsProgram], stdin: toByteSource4("") };
|
|
1063
|
+
capabilitiesByArguments.set(invocation.args, capability);
|
|
1064
|
+
const running = Promise.resolve().then(() => command.execute(invocation)).then(
|
|
1065
|
+
(result) => {
|
|
1066
|
+
if (!closed) fail(new Error(`Python tool interpreter exited with status ${result.exitCode}`));
|
|
1067
|
+
},
|
|
1068
|
+
(error) => {
|
|
1069
|
+
if (!closed) fail(error);
|
|
1070
|
+
}
|
|
1071
|
+
);
|
|
1072
|
+
const close = () => closing ??= (async () => {
|
|
1073
|
+
closed = true;
|
|
1074
|
+
if (loaded && !signal.aborted && !jobs.size) {
|
|
1075
|
+
if (waiter) {
|
|
1076
|
+
waiter.resolve(null);
|
|
1077
|
+
waiter = void 0;
|
|
1078
|
+
} else queue.push(null);
|
|
1079
|
+
} else {
|
|
1080
|
+
controller.abort(new Error("Python tool session closed"));
|
|
1081
|
+
if (!loaded && !initializationFailed) runtimeController.abort(signal.reason);
|
|
1082
|
+
}
|
|
1083
|
+
await running;
|
|
1084
|
+
capabilitiesByArguments.delete(invocation.args);
|
|
1085
|
+
controller.abort(new Error("Python tool session closed"));
|
|
1086
|
+
signal.removeEventListener("abort", aborted);
|
|
1087
|
+
await Promise.all([...spools].map((value) => value.close()));
|
|
1088
|
+
spools.clear();
|
|
1089
|
+
})();
|
|
1090
|
+
context.registerCleanup?.(close);
|
|
1091
|
+
try {
|
|
1092
|
+
signal.throwIfAborted();
|
|
1093
|
+
await ready.promise;
|
|
1094
|
+
return { tools: Object.freeze(tools), toolboxes: Object.freeze(toolboxes), ...plugins ? { plugins } : {}, close };
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
await close();
|
|
1097
|
+
throw error;
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// packages/safe-bash-command-python/src/build-environment.ts
|
|
1103
|
+
function createPythonBuildEnvironment(options = {}) {
|
|
1104
|
+
const manifestStore = createPythonPackageManifestStore({ maxEntries: 1, ...options.maxManifestBytes === void 0 ? {} : { maxBytes: options.maxManifestBytes } });
|
|
1105
|
+
const isolated = { ...options, manifestStore, scope: "build" };
|
|
1106
|
+
delete isolated.editable;
|
|
1107
|
+
delete isolated.requirements;
|
|
1108
|
+
delete isolated.requirementFiles;
|
|
1109
|
+
delete isolated.profile;
|
|
1110
|
+
const environment = createPythonPackageEnvironment(isolated);
|
|
1111
|
+
let disposing;
|
|
1112
|
+
return { ...environment, dispose() {
|
|
1113
|
+
return disposing ??= environment.dispose().finally(() => manifestStore.dispose());
|
|
1114
|
+
} };
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// packages/safe-bash-command-python/src/build-dependencies.ts
|
|
1118
|
+
import { toByteSource as toByteSource6 } from "../../safe-bash-contracts/index.js";
|
|
1119
|
+
|
|
1120
|
+
// packages/safe-bash-command-python/src/source-origin-program.generated.ts
|
|
1121
|
+
var pythonSourceOriginProgram = "def read_source_origin(request):\n import re, json\n from urllib.parse import urlsplit, urlunsplit\n source = request['source']\n if source.startswith('/'):\n from pathlib import Path\n source = Path(source).as_uri()\n url = urlsplit(source)\n netloc = url.netloc\n if '@' in netloc:\n credentials, host = netloc.split('@', 1)\n if not re.match(r'^\\$\\{[A-Za-z0-9-_]+\\}(:\\$\\{[A-Za-z0-9-_]+\\})?$', credentials): netloc = host\n result = {'url': urlunsplit((url.scheme, netloc, url.path, url.query, ''))}\n subdirectory = re.search(r'[#&]subdirectory=([^&]*)', source)\n if subdirectory: result['subdirectory'] = subdirectory.group(1)\n info = {}\n if not request['directory']:\n hashed = re.search(r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)', source)\n if hashed: info['hash'] = hashed.group(1) + '=' + hashed.group(2)\n result['dir_info' if request['directory'] else 'archive_info'] = info\n return json.dumps(result, sort_keys=True)\n";
|
|
1122
|
+
|
|
1123
|
+
// packages/safe-bash-command-python/src/source-filename-program.ts
|
|
1124
|
+
var pythonDownloadFilenameProgram = /* @__PURE__ */ (() => String.raw`
|
|
1125
|
+
MIME_PREFERENCES = {"application/andrew-inset":".ez","application/applixware":".aw","application/atom+xml":".atom","application/atomcat+xml":".atomcat","application/atomsvc+xml":".atomsvc","application/ccxml+xml":".ccxml","application/cdmi-capability":".cdmia","application/cdmi-container":".cdmic","application/cdmi-domain":".cdmid","application/cdmi-object":".cdmio","application/cdmi-queue":".cdmiq","application/cu-seeme":".cu","application/davmount+xml":".davmount","application/docbook+xml":".dbk","application/dssc+der":".dssc","application/dssc+xml":".xdssc","application/ecmascript":".ecma","application/emma+xml":".emma","application/epub+zip":".epub","application/exi":".exi","application/font-tdpfr":".pfr","application/gml+xml":".gml","application/gpx+xml":".gpx","application/gxf":".gxf","application/hyperstudio":".stk","application/inkml+xml":".ink","application/ipfix":".ipfix","application/java-archive":".jar","application/java-serialized-object":".ser","application/java-vm":".class","application/json":".json","application/jsonml+json":".jsonml","application/lost+xml":".lostxml","application/mac-binhex40":".hqx","application/mac-compactpro":".cpt","application/mads+xml":".mads","application/manifest+json":".webmanifest","application/marc":".mrc","application/marcxml+xml":".mrcx","application/mathematica":".ma","application/mathml+xml":".mathml","application/mbox":".mbox","application/mediaservercontrol+xml":".mscml","application/metalink+xml":".metalink","application/metalink4+xml":".meta4","application/mets+xml":".mets","application/mods+xml":".mods","application/mp21":".m21","application/mp4":".mp4s","application/msword":".doc","application/mxf":".mxf","application/octet-stream":".bin","application/oda":".oda","application/oebps-package+xml":".opf","application/ogg":".ogx","application/omdoc+xml":".omdoc","application/onenote":".onetoc","application/oxps":".oxps","application/patch-ops-error+xml":".xer","application/pdf":".pdf","application/pgp-encrypted":".pgp","application/pgp-signature":".asc","application/pics-rules":".prf","application/pkcs10":".p10","application/pkcs7-mime":".p7c","application/pkcs7-signature":".p7s","application/pkcs8":".p8","application/pkix-attr-cert":".ac","application/pkix-cert":".cer","application/pkix-crl":".crl","application/pkix-pkipath":".pkipath","application/pkixcmp":".pki","application/pls+xml":".pls","application/postscript":".ps","application/prs.cww":".cww","application/pskc+xml":".pskcxml","application/rdf+xml":".rdf","application/reginfo+xml":".rif","application/relax-ng-compact-syntax":".rnc","application/resource-lists+xml":".rl","application/resource-lists-diff+xml":".rld","application/rls-services+xml":".rs","application/rpki-ghostbusters":".gbr","application/rpki-manifest":".mft","application/rpki-roa":".roa","application/rsd+xml":".rsd","application/rss+xml":".rss","application/rtf":".rtf","application/sbml+xml":".sbml","application/scvp-cv-request":".scq","application/scvp-cv-response":".scs","application/scvp-vp-request":".spq","application/scvp-vp-response":".spp","application/sdp":".sdp","application/set-payment-initiation":".setpay","application/set-registration-initiation":".setreg","application/shf+xml":".shf","application/smil+xml":".smi","application/sparql-query":".rq","application/sparql-results+xml":".srx","application/srgs":".gram","application/srgs+xml":".grxml","application/sru+xml":".sru","application/ssdl+xml":".ssdl","application/ssml+xml":".ssml","application/tei+xml":".tei","application/thraud+xml":".tfi","application/timestamped-data":".tsd","application/vnd.3gpp.pic-bw-large":".plb","application/vnd.3gpp.pic-bw-small":".psb","application/vnd.3gpp.pic-bw-var":".pvb","application/vnd.3gpp2.tcap":".tcap","application/vnd.3m.post-it-notes":".pwn","application/vnd.accpac.simply.aso":".aso","application/vnd.accpac.simply.imp":".imp","application/vnd.acucobol":".acu","application/vnd.acucorp":".atc","application/vnd.adobe.air-application-installer-package+zip":".air","application/vnd.adobe.formscentral.fcdt":".fcdt","application/vnd.adobe.fxp":".fxp","application/vnd.adobe.xdp+xml":".xdp","application/vnd.adobe.xfdf":".xfdf","application/vnd.ahead.space":".ahead","application/vnd.airzip.filesecure.azf":".azf","application/vnd.airzip.filesecure.azs":".azs","application/vnd.amazon.ebook":".azw","application/vnd.americandynamics.acc":".acc","application/vnd.amiga.ami":".ami","application/vnd.android.package-archive":".apk","application/vnd.anser-web-certificate-issue-initiation":".cii","application/vnd.anser-web-funds-transfer-initiation":".fti","application/vnd.antix.game-component":".atx","application/vnd.apple.installer+xml":".mpkg","application/vnd.aristanetworks.swi":".swi","application/vnd.astraea-software.iota":".iota","application/vnd.audiograph":".aep","application/vnd.blueice.multipass":".mpm","application/vnd.bmi":".bmi","application/vnd.businessobjects":".rep","application/vnd.chemdraw+xml":".cdxml","application/vnd.chipnuts.karaoke-mmd":".mmd","application/vnd.cinderella":".cdy","application/vnd.claymore":".cla","application/vnd.cloanto.rp9":".rp9","application/vnd.clonk.c4group":".c4g","application/vnd.cluetrust.cartomobile-config":".c11amc","application/vnd.cluetrust.cartomobile-config-pkg":".c11amz","application/vnd.commonspace":".csp","application/vnd.contact.cmsg":".cdbcmsg","application/vnd.cosmocaller":".cmc","application/vnd.crick.clicker":".clkx","application/vnd.crick.clicker.keyboard":".clkk","application/vnd.crick.clicker.palette":".clkp","application/vnd.crick.clicker.template":".clkt","application/vnd.crick.clicker.wordbank":".clkw","application/vnd.criticaltools.wbs+xml":".wbs","application/vnd.ctc-posml":".pml","application/vnd.cups-ppd":".ppd","application/vnd.curl.car":".car","application/vnd.curl.pcurl":".pcurl","application/vnd.dart":".dart","application/vnd.data-vision.rdz":".rdz","application/vnd.dece.data":".uvf","application/vnd.dece.ttml+xml":".uvt","application/vnd.dece.unspecified":".uvx","application/vnd.dece.zip":".uvz","application/vnd.denovo.fcselayout-link":".fe_launch","application/vnd.dna":".dna","application/vnd.dolby.mlp":".mlp","application/vnd.dpgraph":".dpg","application/vnd.dreamfactory":".dfac","application/vnd.ds-keypoint":".kpxx","application/vnd.dvb.ait":".ait","application/vnd.dvb.service":".svc","application/vnd.dynageo":".geo","application/vnd.ecowin.chart":".mag","application/vnd.enliven":".nml","application/vnd.epson.esf":".esf","application/vnd.epson.msf":".msf","application/vnd.epson.quickanime":".qam","application/vnd.epson.salt":".slt","application/vnd.epson.ssf":".ssf","application/vnd.eszigno3+xml":".es3","application/vnd.ezpix-album":".ez2","application/vnd.ezpix-package":".ez3","application/vnd.fdf":".fdf","application/vnd.fdsn.mseed":".mseed","application/vnd.fdsn.seed":".seed","application/vnd.flographit":".gph","application/vnd.fluxtime.clip":".ftc","application/vnd.framemaker":".fm","application/vnd.frogans.fnc":".fnc","application/vnd.frogans.ltf":".ltf","application/vnd.fsc.weblaunch":".fsc","application/vnd.fujitsu.oasys":".oas","application/vnd.fujitsu.oasys2":".oa2","application/vnd.fujitsu.oasys3":".oa3","application/vnd.fujitsu.oasysgp":".fg5","application/vnd.fujitsu.oasysprs":".bh2","application/vnd.fujixerox.ddd":".ddd","application/vnd.fujixerox.docuworks":".xdw","application/vnd.fujixerox.docuworks.binder":".xbd","application/vnd.fuzzysheet":".fzs","application/vnd.genomatix.tuxedo":".txd","application/vnd.geogebra.file":".ggb","application/vnd.geogebra.slides":".ggs","application/vnd.geogebra.tool":".ggt","application/vnd.geometry-explorer":".gex","application/vnd.geonext":".gxt","application/vnd.geoplan":".g2w","application/vnd.geospace":".g3w","application/vnd.gmx":".gmx","application/vnd.google-earth.kml+xml":".kml","application/vnd.google-earth.kmz":".kmz","application/vnd.grafeq":".gqf","application/vnd.groove-account":".gac","application/vnd.groove-help":".ghf","application/vnd.groove-identity-message":".gim","application/vnd.groove-injector":".grv","application/vnd.groove-tool-message":".gtm","application/vnd.groove-tool-template":".tpl","application/vnd.groove-vcard":".vcg","application/vnd.hal+xml":".hal","application/vnd.handheld-entertainment+xml":".zmm","application/vnd.hbci":".hbci","application/vnd.hhe.lesson-player":".les","application/vnd.hp-hpgl":".hpgl","application/vnd.hp-hpid":".hpid","application/vnd.hp-hps":".hps","application/vnd.hp-jlyt":".jlt","application/vnd.hp-pcl":".pcl","application/vnd.hp-pclxl":".pclxl","application/vnd.hydrostatix.sof-data":".sfd-hdstx","application/vnd.ibm.minipay":".mpy","application/vnd.ibm.modcap":".afp","application/vnd.ibm.rights-management":".irm","application/vnd.ibm.secure-container":".sc","application/vnd.iccprofile":".icc","application/vnd.igloader":".igl","application/vnd.immervision-ivp":".ivp","application/vnd.immervision-ivu":".ivu","application/vnd.insors.igm":".igm","application/vnd.intercon.formnet":".xpw","application/vnd.intergeo":".i2g","application/vnd.intu.qbo":".qbo","application/vnd.intu.qfx":".qfx","application/vnd.ipunplugged.rcprofile":".rcprofile","application/vnd.irepository.package+xml":".irp","application/vnd.is-xpr":".xpr","application/vnd.isac.fcs":".fcs","application/vnd.jam":".jam","application/vnd.jcp.javame.midlet-rms":".rms","application/vnd.jisp":".jisp","application/vnd.joost.joda-archive":".joda","application/vnd.kahootz":".ktz","application/vnd.kde.karbon":".karbon","application/vnd.kde.kchart":".chrt","application/vnd.kde.kformula":".kfo","application/vnd.kde.kivio":".flw","application/vnd.kde.kontour":".kon","application/vnd.kde.kpresenter":".kpr","application/vnd.kde.kspread":".ksp","application/vnd.kde.kword":".kwd","application/vnd.kenameaapp":".htke","application/vnd.kidspiration":".kia","application/vnd.kinar":".kne","application/vnd.koan":".skp","application/vnd.kodak-descriptor":".sse","application/vnd.las.las+xml":".lasxml","application/vnd.llamagraphics.life-balance.desktop":".lbd","application/vnd.llamagraphics.life-balance.exchange+xml":".lbe","application/vnd.lotus-1-2-3":".123","application/vnd.lotus-approach":".apr","application/vnd.lotus-freelance":".pre","application/vnd.lotus-notes":".nsf","application/vnd.lotus-organizer":".org","application/vnd.lotus-screencam":".scm","application/vnd.lotus-wordpro":".lwp","application/vnd.macports.portpkg":".portpkg","application/vnd.mcd":".mcd","application/vnd.medcalcdata":".mc1","application/vnd.mediastation.cdkey":".cdkey","application/vnd.mfer":".mwf","application/vnd.mfmp":".mfm","application/vnd.micrografx.flo":".flo","application/vnd.micrografx.igx":".igx","application/vnd.mif":".mif","application/vnd.mobius.daf":".daf","application/vnd.mobius.dis":".dis","application/vnd.mobius.mbk":".mbk","application/vnd.mobius.mqy":".mqy","application/vnd.mobius.msl":".msl","application/vnd.mobius.plc":".plc","application/vnd.mobius.txf":".txf","application/vnd.mophun.application":".mpn","application/vnd.mophun.certificate":".mpc","application/vnd.mozilla.xul+xml":".xul","application/vnd.ms-artgalry":".cil","application/vnd.ms-cab-compressed":".cab","application/vnd.ms-excel":".xls","application/vnd.ms-excel.addin.macroenabled.12":".xlam","application/vnd.ms-excel.sheet.binary.macroenabled.12":".xlsb","application/vnd.ms-excel.sheet.macroenabled.12":".xlsm","application/vnd.ms-excel.template.macroenabled.12":".xltm","application/vnd.ms-fontobject":".eot","application/vnd.ms-htmlhelp":".chm","application/vnd.ms-ims":".ims","application/vnd.ms-lrm":".lrm","application/vnd.ms-officetheme":".thmx","application/vnd.ms-pki.seccat":".cat","application/vnd.ms-pki.stl":".stl","application/vnd.ms-powerpoint":".ppt","application/vnd.ms-powerpoint.addin.macroenabled.12":".ppam","application/vnd.ms-powerpoint.presentation.macroenabled.12":".pptm","application/vnd.ms-powerpoint.slide.macroenabled.12":".sldm","application/vnd.ms-powerpoint.slideshow.macroenabled.12":".ppsm","application/vnd.ms-powerpoint.template.macroenabled.12":".potm","application/vnd.ms-project":".mpp","application/vnd.ms-word.document.macroenabled.12":".docm","application/vnd.ms-word.template.macroenabled.12":".dotm","application/vnd.ms-works":".wps","application/vnd.ms-wpl":".wpl","application/vnd.ms-xpsdocument":".xps","application/vnd.mseq":".mseq","application/vnd.musician":".mus","application/vnd.muvee.style":".msty","application/vnd.mynfc":".taglet","application/vnd.neurolanguage.nlu":".nlu","application/vnd.nitf":".ntf","application/vnd.noblenet-directory":".nnd","application/vnd.noblenet-sealer":".nns","application/vnd.noblenet-web":".nnw","application/vnd.nokia.n-gage.data":".ngdat","application/vnd.nokia.n-gage.symbian.install":".n-gage","application/vnd.nokia.radio-preset":".rpst","application/vnd.nokia.radio-presets":".rpss","application/vnd.novadigm.edm":".edm","application/vnd.novadigm.edx":".edx","application/vnd.novadigm.ext":".ext","application/vnd.oasis.opendocument.chart":".odc","application/vnd.oasis.opendocument.chart-template":".otc","application/vnd.oasis.opendocument.database":".odb","application/vnd.oasis.opendocument.formula":".odf","application/vnd.oasis.opendocument.formula-template":".odft","application/vnd.oasis.opendocument.graphics":".odg","application/vnd.oasis.opendocument.graphics-template":".otg","application/vnd.oasis.opendocument.image":".odi","application/vnd.oasis.opendocument.image-template":".oti","application/vnd.oasis.opendocument.presentation":".odp","application/vnd.oasis.opendocument.presentation-template":".otp","application/vnd.oasis.opendocument.spreadsheet":".ods","application/vnd.oasis.opendocument.spreadsheet-template":".ots","application/vnd.oasis.opendocument.text":".odt","application/vnd.oasis.opendocument.text-master":".odm","application/vnd.oasis.opendocument.text-template":".ott","application/vnd.oasis.opendocument.text-web":".oth","application/vnd.olpc-sugar":".xo","application/vnd.oma.dd2+xml":".dd2","application/vnd.openofficeorg.extension":".oxt","application/vnd.openxmlformats-officedocument.presentationml.presentation":".pptx","application/vnd.openxmlformats-officedocument.presentationml.slide":".sldx","application/vnd.openxmlformats-officedocument.presentationml.slideshow":".ppsx","application/vnd.openxmlformats-officedocument.presentationml.template":".potx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":".xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.template":".xltx","application/vnd.openxmlformats-officedocument.wordprocessingml.document":".docx","application/vnd.openxmlformats-officedocument.wordprocessingml.template":".dotx","application/vnd.osgeo.mapguide.package":".mgp","application/vnd.osgi.dp":".dp","application/vnd.osgi.subsystem":".esa","application/vnd.palm":".pdb","application/vnd.pawaafile":".paw","application/vnd.pg.format":".str","application/vnd.pg.osasli":".ei6","application/vnd.picsel":".efif","application/vnd.pmi.widget":".wg","application/vnd.pocketlearn":".plf","application/vnd.powerbuilder6":".pbd","application/vnd.previewsystems.box":".box","application/vnd.proteus.magazine":".mgz","application/vnd.publishare-delta-tree":".qps","application/vnd.pvi.ptid1":".ptid","application/vnd.quark.quarkxpress":".qxd","application/vnd.realvnc.bed":".bed","application/vnd.recordare.musicxml":".mxl","application/vnd.recordare.musicxml+xml":".musicxml","application/vnd.rig.cryptonote":".cryptonote","application/vnd.rim.cod":".cod","application/vnd.rn-realmedia":".rm","application/vnd.rn-realmedia-vbr":".rmvb","application/vnd.route66.link66+xml":".link66","application/vnd.sailingtracker.track":".st","application/vnd.seemail":".see","application/vnd.sema":".sema","application/vnd.semd":".semd","application/vnd.semf":".semf","application/vnd.shana.informed.formdata":".ifm","application/vnd.shana.informed.formtemplate":".itp","application/vnd.shana.informed.interchange":".iif","application/vnd.shana.informed.package":".ipk","application/vnd.simtech-mindmapper":".twd","application/vnd.smaf":".mmf","application/vnd.smart.teacher":".teacher","application/vnd.solent.sdkm+xml":".sdkm","application/vnd.spotfire.dxp":".dxp","application/vnd.spotfire.sfs":".sfs","application/vnd.stardivision.calc":".sdc","application/vnd.stardivision.draw":".sda","application/vnd.stardivision.impress":".sdd","application/vnd.stardivision.math":".smf","application/vnd.stardivision.writer":".sdw","application/vnd.stardivision.writer-global":".sgl","application/vnd.stepmania.package":".smzip","application/vnd.stepmania.stepchart":".sm","application/vnd.sun.xml.calc":".sxc","application/vnd.sun.xml.calc.template":".stc","application/vnd.sun.xml.draw":".sxd","application/vnd.sun.xml.draw.template":".std","application/vnd.sun.xml.impress":".sxi","application/vnd.sun.xml.impress.template":".sti","application/vnd.sun.xml.math":".sxm","application/vnd.sun.xml.writer":".sxw","application/vnd.sun.xml.writer.global":".sxg","application/vnd.sun.xml.writer.template":".stw","application/vnd.sus-calendar":".sus","application/vnd.svd":".svd","application/vnd.symbian.install":".sis","application/vnd.syncml+xml":".xsm","application/vnd.syncml.dm+wbxml":".bdm","application/vnd.syncml.dm+xml":".xdm","application/vnd.tao.intent-module-archive":".tao","application/vnd.tcpdump.pcap":".pcap","application/vnd.tmobile-livetv":".tmo","application/vnd.trid.tpt":".tpt","application/vnd.triscape.mxs":".mxs","application/vnd.trueapp":".tra","application/vnd.ufdl":".ufd","application/vnd.uiq.theme":".utz","application/vnd.umajin":".umj","application/vnd.unity":".unityweb","application/vnd.uoml+xml":".uoml","application/vnd.vcx":".vcx","application/vnd.visio":".vsd","application/vnd.visionary":".vis","application/vnd.vsf":".vsf","application/vnd.wap.wbxml":".wbxml","application/vnd.wap.wmlc":".wmlc","application/vnd.wap.wmlscriptc":".wmlsc","application/vnd.webturbo":".wtb","application/vnd.wolfram.player":".nbp","application/vnd.wordperfect":".wpd","application/vnd.wqd":".wqd","application/vnd.wt.stf":".stf","application/vnd.xara":".xar","application/vnd.xfdl":".xfdl","application/vnd.yamaha.hv-dic":".hvd","application/vnd.yamaha.hv-script":".hvs","application/vnd.yamaha.hv-voice":".hvp","application/vnd.yamaha.openscoreformat":".osf","application/vnd.yamaha.openscoreformat.osfpvg+xml":".osfpvg","application/vnd.yamaha.smaf-audio":".saf","application/vnd.yamaha.smaf-phrase":".spf","application/vnd.yellowriver-custom-menu":".cmp","application/vnd.zul":".zir","application/vnd.zzazz.deck+xml":".zaz","application/voicexml+xml":".vxml","application/wasm":".wasm","application/widget":".wgt","application/winhlp":".hlp","application/wsdl+xml":".wsdl","application/wspolicy+xml":".wspolicy","application/x-7z-compressed":".7z","application/x-abiword":".abw","application/x-ace-compressed":".ace","application/x-apple-diskimage":".dmg","application/x-authorware-bin":".aab","application/x-authorware-map":".aam","application/x-authorware-seg":".aas","application/x-bcpio":".bcpio","application/x-bittorrent":".torrent","application/x-blorb":".blb","application/x-bzip":".bz","application/x-bzip2":".bz2","application/x-cbr":".cbr","application/x-cdlink":".vcd","application/x-cfs-compressed":".cfs","application/x-chat":".chat","application/x-chess-pgn":".pgn","application/x-conference":".nsc","application/x-cpio":".cpio","application/x-csh":".csh","application/x-debian-package":".deb","application/x-dgc-compressed":".dgc","application/x-director":".dir","application/x-doom":".wad","application/x-dtbncx+xml":".ncx","application/x-dtbook+xml":".dtb","application/x-dtbresource+xml":".res","application/x-dvi":".dvi","application/x-envoy":".evy","application/x-eva":".eva","application/x-font-bdf":".bdf","application/x-font-ghostscript":".gsf","application/x-font-linux-psf":".psf","application/x-font-pcf":".pcf","application/x-font-snf":".snf","application/x-font-type1":".pfa","application/x-freearc":".arc","application/x-futuresplash":".spl","application/x-gca-compressed":".gca","application/x-glulx":".ulx","application/x-gnumeric":".gnumeric","application/x-gramps-xml":".gramps","application/x-gtar":".gtar","application/x-hdf":".hdf","application/x-install-instructions":".install","application/x-iso9660-image":".iso","application/x-java-jnlp-file":".jnlp","application/x-latex":".latex","application/x-lzh-compressed":".lzh","application/x-mie":".mie","application/x-mobipocket-ebook":".prc","application/x-mpegurl":".m3u8","application/x-ms-application":".application","application/x-ms-shortcut":".lnk","application/x-ms-wmd":".wmd","application/x-ms-xbap":".xbap","application/x-msaccess":".mdb","application/x-msbinder":".obd","application/x-mscardfile":".crd","application/x-msclip":".clp","application/x-msdownload":".exe","application/x-msmediaview":".mvb","application/x-msmetafile":".wmf","application/x-msmoney":".mny","application/x-mspublisher":".pub","application/x-msschedule":".scd","application/x-msterminal":".trm","application/x-mswrite":".wri","application/x-netcdf":".cdf","application/x-nzb":".nzb","application/x-pkcs12":".p12","application/x-pkcs7-certificates":".p7b","application/x-pkcs7-certreqresp":".p7r","application/x-python-code":".pyc","application/x-rar-compressed":".rar","application/x-research-info-systems":".ris","application/x-sh":".sh","application/x-shar":".shar","application/x-shockwave-flash":".swf","application/x-silverlight-app":".xap","application/x-sql":".sql","application/x-stuffit":".sit","application/x-stuffitx":".sitx","application/x-subrip":".srt","application/x-sv4cpio":".sv4cpio","application/x-sv4crc":".sv4crc","application/x-t3vm-image":".t3","application/x-tads":".gam","application/x-tar":".tar","application/x-tcl":".tcl","application/x-tex":".tex","application/x-tex-tfm":".tfm","application/x-texinfo":".texi","application/x-tgif":".obj","application/x-ustar":".ustar","application/x-wais-source":".src","application/x-x509-ca-cert":".der","application/x-xfig":".fig","application/x-xliff+xml":".xlf","application/x-xpinstall":".xpi","application/x-xz":".xz","application/x-zmachine":".z1","application/xaml+xml":".xaml","application/xcap-diff+xml":".xdf","application/xenc+xml":".xenc","application/xhtml+xml":".xhtml","application/xml":".xsl","application/xml-dtd":".dtd","application/xop+xml":".xop","application/xproc+xml":".xpl","application/xslt+xml":".xslt","application/xspf+xml":".xspf","application/xv+xml":".mxml","application/yang":".yang","application/yin+xml":".yin","application/zip":".zip","audio/adpcm":".adp","audio/basic":".au","audio/midi":".mid","audio/mp4":".m4a","audio/mp4a-latm":".m4a","audio/mpeg":".mp3","audio/ogg":".oga","audio/s3m":".s3m","audio/silk":".sil","audio/vnd.dece.audio":".uva","audio/vnd.digital-winds":".eol","audio/vnd.dra":".dra","audio/vnd.dts":".dts","audio/vnd.dts.hd":".dtshd","audio/vnd.lucent.voice":".lvp","audio/vnd.ms-playready.media.pya":".pya","audio/vnd.nuera.ecelp4800":".ecelp4800","audio/vnd.nuera.ecelp7470":".ecelp7470","audio/vnd.nuera.ecelp9600":".ecelp9600","audio/vnd.rip":".rip","audio/webm":".weba","audio/x-aac":".aac","audio/x-aiff":".aif","audio/x-caf":".caf","audio/x-flac":".flac","audio/x-matroska":".mka","audio/x-mpegurl":".m3u","audio/x-ms-wax":".wax","audio/x-ms-wma":".wma","audio/x-pn-realaudio":".ra","audio/x-pn-realaudio-plugin":".rmp","audio/x-wav":".wav","audio/xm":".xm","chemical/x-cdx":".cdx","chemical/x-cif":".cif","chemical/x-cmdf":".cmdf","chemical/x-cml":".cml","chemical/x-csml":".csml","chemical/x-xyz":".xyz","font/collection":".ttc","font/otf":".otf","font/ttf":".ttf","font/woff":".woff","font/woff2":".woff2","image/avif":".avif","image/bmp":".bmp","image/cgm":".cgm","image/g3fax":".g3","image/gif":".gif","image/ief":".ief","image/jp2":".jp2","image/jpeg":".jpg","image/jxl":".jxl","image/ktx":".ktx","image/pict":".pict","image/png":".png","image/prs.btif":".btif","image/sgi":".sgi","image/svg+xml":".svg","image/tiff":".tiff","image/vnd.adobe.photoshop":".psd","image/vnd.dece.graphic":".uvi","image/vnd.djvu":".djvu","image/vnd.dwg":".dwg","image/vnd.dxf":".dxf","image/vnd.fastbidsheet":".fbs","image/vnd.fpx":".fpx","image/vnd.fst":".fst","image/vnd.fujixerox.edmics-mmr":".mmr","image/vnd.fujixerox.edmics-rlc":".rlc","image/vnd.ms-modi":".mdi","image/vnd.ms-photo":".wdp","image/vnd.net-fpx":".npx","image/vnd.wap.wbmp":".wbmp","image/vnd.xiff":".xif","image/webp":".webp","image/x-3ds":".3ds","image/x-cmu-raster":".ras","image/x-cmx":".cmx","image/x-freehand":".fh","image/x-icon":".ico","image/x-macpaint":".pntg","image/x-mrsid-image":".sid","image/x-pcx":".pcx","image/x-pict":".pic","image/x-portable-anymap":".pnm","image/x-portable-bitmap":".pbm","image/x-portable-graymap":".pgm","image/x-portable-pixmap":".ppm","image/x-quicktime":".qtif","image/x-rgb":".rgb","image/x-tga":".tga","image/x-xbitmap":".xbm","image/x-xpixmap":".xpm","image/x-xwindowdump":".xwd","message/rfc822":".eml","model/iges":".igs","model/mesh":".msh","model/vnd.collada+xml":".dae","model/vnd.dwf":".dwf","model/vnd.gdl":".gdl","model/vnd.gtw":".gtw","model/vnd.vtu":".vtu","model/vrml":".wrl","model/x3d+binary":".x3db","model/x3d+vrml":".x3dv","model/x3d+xml":".x3d","text/cache-manifest":".manifest","text/calendar":".ics","text/css":".css","text/csv":".csv","text/html":".html","text/javascript":".js","text/n3":".n3","text/plain":".txt","text/prs.lines.tag":".dsc","text/richtext":".rtx","text/sgml":".sgml","text/tab-separated-values":".tsv","text/troff":".t","text/turtle":".ttl","text/uri-list":".uri","text/vcard":".vcard","text/vnd.curl":".curl","text/vnd.curl.dcurl":".dcurl","text/vnd.curl.mcurl":".mcurl","text/vnd.curl.scurl":".scurl","text/vnd.dvb.subtitle":".sub","text/vnd.fly":".fly","text/vnd.fmi.flexstor":".flx","text/vnd.graphviz":".gv","text/vnd.in3d.3dml":".3dml","text/vnd.in3d.spot":".spot","text/vnd.sun.j2me.app-descriptor":".jad","text/vnd.wap.wml":".wml","text/vnd.wap.wmlscript":".wmls","text/x-asm":".s","text/x-c":".c","text/x-fortran":".f","text/x-java-source":".java","text/x-nfo":".nfo","text/x-opml":".opml","text/x-pascal":".p","text/x-python":".py","text/x-setext":".etx","text/x-sfv":".sfv","text/x-uuencode":".uu","text/x-vcalendar":".vcs","text/x-vcard":".vcf","video/3gpp":".3gp","video/3gpp2":".3g2","video/h261":".h261","video/h263":".h263","video/h264":".h264","video/jpeg":".jpgv","video/jpm":".jpm","video/mj2":".mj2","video/mp2t":".ts","video/mp4":".mp4","video/mpeg":".mpeg","video/ogg":".ogv","video/quicktime":".mov","video/vnd.dece.hd":".uvh","video/vnd.dece.mobile":".uvm","video/vnd.dece.pd":".uvp","video/vnd.dece.sd":".uvs","video/vnd.dece.video":".uvv","video/vnd.dvb.file":".dvb","video/vnd.fvt":".fvt","video/vnd.mpegurl":".mxu","video/vnd.ms-playready.media.pyv":".pyv","video/vnd.uvvu.mp4":".uvu","video/vnd.vivo":".viv","video/webm":".webm","video/x-dv":".dv","video/x-f4v":".f4v","video/x-fli":".fli","video/x-flv":".flv","video/x-m4v":".m4v","video/x-matroska":".mkv","video/x-mng":".mng","video/x-ms-asf":".asf","video/x-ms-vob":".vob","video/x-ms-wm":".wm","video/x-ms-wmv":".wmv","video/x-ms-wmx":".wmx","video/x-ms-wvx":".wvx","video/x-msvideo":".avi","video/x-sgi-movie":".movie","video/x-smv":".smv","x-conference/x-cooltalk":".ice"}
|
|
1126
|
+
|
|
1127
|
+
def read_download_filename(request):
|
|
1128
|
+
from urllib.parse import urlsplit, unquote
|
|
1129
|
+
import posixpath
|
|
1130
|
+
url = urlsplit(request['source'])
|
|
1131
|
+
filename = unquote(posixpath.basename(url.path.rstrip('/'))) or url.netloc.rsplit('@', 1)[-1]
|
|
1132
|
+
headers = {key.lower(): value for key, value in request['headers']}
|
|
1133
|
+
# CPython 3.9 cgi.parse_header rules, including last duplicate wins.
|
|
1134
|
+
remaining = ';' + headers.get('content-disposition', '')
|
|
1135
|
+
parameters = []
|
|
1136
|
+
while remaining[:1] == ';':
|
|
1137
|
+
remaining = remaining[1:]
|
|
1138
|
+
end = remaining.find(';')
|
|
1139
|
+
while end > 0 and (remaining.count('"', 0, end) - remaining.count('\\"', 0, end)) % 2:
|
|
1140
|
+
end = remaining.find(';', end + 1)
|
|
1141
|
+
if end < 0: end = len(remaining)
|
|
1142
|
+
parameters.append(remaining[:end].strip())
|
|
1143
|
+
remaining = remaining[end:]
|
|
1144
|
+
selected = None
|
|
1145
|
+
for parameter in parameters[1:]:
|
|
1146
|
+
if '=' not in parameter: continue
|
|
1147
|
+
key, value = parameter.split('=', 1)
|
|
1148
|
+
if key.strip().lower() != 'filename': continue
|
|
1149
|
+
value = value.strip()
|
|
1150
|
+
if len(value) >= 2 and value[0] == value[-1] == '"':
|
|
1151
|
+
value = value[1:-1].replace('\\\\', '\\').replace('\\"', '"')
|
|
1152
|
+
selected = posixpath.basename(value) if value else ''
|
|
1153
|
+
filename = selected or filename
|
|
1154
|
+
ext = posixpath.splitext(filename)[1]
|
|
1155
|
+
if not ext:
|
|
1156
|
+
ext = MIME_PREFERENCES.get(headers.get('content-type', '').lower())
|
|
1157
|
+
if ext: filename += ext
|
|
1158
|
+
if not ext and request['source'] != request['responseUrl']:
|
|
1159
|
+
ext = posixpath.splitext(request['responseUrl'])[1]
|
|
1160
|
+
if ext: filename += ext
|
|
1161
|
+
return filename
|
|
1162
|
+
`)();
|
|
1163
|
+
|
|
1164
|
+
// packages/safe-bash-command-python/src/build-backend.ts
|
|
1165
|
+
import { toByteSource as toByteSource5 } from "../../safe-bash-contracts/index.js";
|
|
1166
|
+
function createPythonBuildBackend(options) {
|
|
1167
|
+
if (!options.createExecutor || !options.environment || options.provisioning) throw new TypeError("Python build hooks require an asynchronous executor and explicit environment");
|
|
1168
|
+
const capabilities = /* @__PURE__ */ new WeakMap();
|
|
1169
|
+
const environment = options.environment, legacy = /* @__PURE__ */ new WeakSet();
|
|
1170
|
+
const command = createPythonExecutorCommands({ ...options, environment: { ...environment, async prepare(context) {
|
|
1171
|
+
return { ...await environment.prepare(context), bootstrap: true, ...context.args && legacy.has(context.args) ? { bootstrapPackages: ["setuptools"] } : {} };
|
|
1172
|
+
} }, createCapabilities(context) {
|
|
1173
|
+
const provided = options.createCapabilities?.(context) ?? {};
|
|
1174
|
+
if (provided.python_build) throw new Error("Python build capability is reserved");
|
|
1175
|
+
const capability = capabilities.get(context.args);
|
|
1176
|
+
if (!capability) throw new Error("Unknown Python build invocation");
|
|
1177
|
+
return { ...provided, python_build: capability };
|
|
1178
|
+
} })[0];
|
|
1179
|
+
return async function run(input, context) {
|
|
1180
|
+
context.signal.throwIfAborted();
|
|
1181
|
+
const { maxBytes } = context;
|
|
1182
|
+
if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError("Invalid Python build metadata limit");
|
|
1183
|
+
if (!input || !["read_source_origin", "read_download_filename", "read_editable_requirement", "get_requires_for_legacy_wheel", "read_source_requirement", "read_build_system", "check_build_requirements", "get_requires_for_build_wheel", "build_wheel", "build_legacy_wheel"].includes(input.hook) || typeof input.source !== "string" || !input.source) throw new TypeError("Invalid Python build hook request");
|
|
1184
|
+
if (input.hook === "read_editable_requirement" && input.requirementLine !== void 0 && typeof input.requirementLine !== "boolean") throw new TypeError("Invalid editable requirement line");
|
|
1185
|
+
if (input.hook === "read_source_origin") {
|
|
1186
|
+
if (typeof input.directory !== "boolean") throw new TypeError("Invalid Python source origin");
|
|
1187
|
+
} else if (input.hook === "read_download_filename") {
|
|
1188
|
+
if (typeof input.responseUrl !== "string" || !Array.isArray(input.headers) || input.headers.some((pair) => !Array.isArray(pair) || pair.length !== 2 || pair.some((value) => typeof value !== "string"))) throw new TypeError("Invalid Python download metadata");
|
|
1189
|
+
} else if (input.hook === "build_legacy_wheel") {
|
|
1190
|
+
if (typeof input.wheelDirectory !== "string" || !input.wheelDirectory || input.editable !== void 0 && typeof input.editable !== "boolean") throw new TypeError("Invalid Python legacy wheel directory");
|
|
1191
|
+
} else if (input.hook === "check_build_requirements") {
|
|
1192
|
+
if ([input.requirements, input.installed].some((values) => !Array.isArray(values) || values.some((value) => typeof value !== "string"))) throw new TypeError("Invalid Python build requirements request");
|
|
1193
|
+
} else if (input.hook === "read_build_system") {
|
|
1194
|
+
if (input.installation !== void 0 && !["directory", "archive"].includes(input.installation)) throw new TypeError("Invalid Python source installation");
|
|
1195
|
+
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");
|
|
1196
|
+
} else if (input.hook !== "read_source_requirement" && input.hook !== "read_editable_requirement" && input.hook !== "get_requires_for_legacy_wheel") {
|
|
1197
|
+
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");
|
|
1198
|
+
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");
|
|
1199
|
+
}
|
|
1200
|
+
const request = structuredClone(input.hook === "read_build_system" ? { ...input, maxBytes: maxBytes === Infinity ? null : maxBytes } : input);
|
|
1201
|
+
let done = false, size = 0, failure;
|
|
1202
|
+
const chunks = [];
|
|
1203
|
+
const invocation = { ...context, command: "python", args: ["-c", pythonBuildBackendProgram], stdin: toByteSource5("") };
|
|
1204
|
+
if (request.hook === "build_legacy_wheel" || request.hook === "get_requires_for_legacy_wheel") legacy.add(invocation.args);
|
|
1205
|
+
capabilities.set(invocation.args, { async call(value) {
|
|
1206
|
+
try {
|
|
1207
|
+
context.signal.throwIfAborted();
|
|
1208
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || done) throw new TypeError("Invalid Python build message");
|
|
1209
|
+
const message = value;
|
|
1210
|
+
if (message.op === "request") return request;
|
|
1211
|
+
if (message.op === "error") {
|
|
1212
|
+
if (typeof message.type !== "string" || typeof message.message !== "string") throw new TypeError("Invalid Python build failure");
|
|
1213
|
+
failure ??= Object.assign(message.type === "InstallationError" ? new PythonInstallationError(message.message) : new Error(message.message), { name: message.type });
|
|
1214
|
+
done = true;
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
if (message.op === "done") {
|
|
1218
|
+
done = true;
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1221
|
+
if (message.op !== "text" || typeof message.text !== "string" || message.text.length > 8192) throw new TypeError("Invalid Python build metadata window");
|
|
1222
|
+
const bytes = new TextEncoder().encode(message.text).length;
|
|
1223
|
+
if (bytes > 16384 || bytes > maxBytes - size) throw new RangeError("Python build metadata limit exceeded");
|
|
1224
|
+
size += bytes;
|
|
1225
|
+
if (bytes) chunks.push(message.text);
|
|
1226
|
+
return null;
|
|
1227
|
+
} catch (error) {
|
|
1228
|
+
failure ??= error;
|
|
1229
|
+
throw error;
|
|
1230
|
+
}
|
|
1231
|
+
} });
|
|
1232
|
+
try {
|
|
1233
|
+
const result = await command.execute(invocation);
|
|
1234
|
+
context.signal.throwIfAborted();
|
|
1235
|
+
if (failure) throw failure;
|
|
1236
|
+
if (result.exitCode) throw new Error(`Python build interpreter exited with status ${result.exitCode}`);
|
|
1237
|
+
if (!done) throw new Error("Python build interpreter returned no result");
|
|
1238
|
+
const value = JSON.parse(chunks.join(""));
|
|
1239
|
+
if (request.hook === "read_source_origin" || request.hook === "read_download_filename") {
|
|
1240
|
+
if (typeof value !== "string") throw new TypeError("Invalid Python download filename");
|
|
1241
|
+
} else if (request.hook === "read_source_requirement" || request.hook === "read_editable_requirement") {
|
|
1242
|
+
const result2 = value;
|
|
1243
|
+
if (result2 !== null && (!result2 || typeof result2.name !== "string" || typeof result2.url !== "string" || typeof result2.active !== "boolean" || result2.marker !== null && typeof result2.marker !== "string" || !Array.isArray(result2.extras) || result2.extras.some((extra) => typeof extra !== "string"))) throw new TypeError("Invalid Python source requirement");
|
|
1244
|
+
} else if (request.hook === "check_build_requirements") {
|
|
1245
|
+
const result2 = value;
|
|
1246
|
+
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");
|
|
1247
|
+
} else if (request.hook === "read_build_system") {
|
|
1248
|
+
if (value !== null) {
|
|
1249
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Invalid Python build system");
|
|
1250
|
+
const details = value;
|
|
1251
|
+
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");
|
|
1252
|
+
}
|
|
1253
|
+
} else if (request.hook === "build_wheel" || request.hook === "build_legacy_wheel") {
|
|
1254
|
+
if (typeof value !== "string" || !value.endsWith(".whl") || value.includes("/") || value.includes("\\") || value.includes("\0")) throw new TypeError("Invalid Python build wheel filename");
|
|
1255
|
+
} else if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new TypeError("Invalid Python build requirements");
|
|
1256
|
+
return value;
|
|
1257
|
+
} finally {
|
|
1258
|
+
capabilities.delete(invocation.args);
|
|
1259
|
+
legacy.delete(invocation.args);
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
var pythonBuildBackendProgram = /* @__PURE__ */ (() => String.raw`
|
|
1264
|
+
import importlib, json, os, sys, safe_host
|
|
1265
|
+
${pythonDownloadFilenameProgram}
|
|
1266
|
+
|
|
1267
|
+
def send(op, **fields):
|
|
1268
|
+
return safe_host.call('python_build', dict(op=op, **fields))
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
${pythonSourceOriginProgram}
|
|
1272
|
+
|
|
1273
|
+
def parse_build_requirement(value):
|
|
1274
|
+
from urllib.parse import urlparse, urlunparse
|
|
1275
|
+
from micropip._vendored.packaging.src.packaging.requirements import Requirement, InvalidRequirement
|
|
1276
|
+
requirement = Requirement(value)
|
|
1277
|
+
if requirement.url:
|
|
1278
|
+
url = urlparse(requirement.url)
|
|
1279
|
+
if (url.scheme == 'file' and urlunparse(url) != requirement.url) or (url.scheme != 'file' and not (url.scheme and url.netloc)): raise InvalidRequirement(value)
|
|
1280
|
+
return requirement
|
|
1281
|
+
|
|
1282
|
+
def check_build_requirements(request):
|
|
1283
|
+
from importlib.metadata import distribution, PackageNotFoundError
|
|
1284
|
+
from micropip._vendored.packaging.src.packaging.utils import canonicalize_name
|
|
1285
|
+
from micropip._vendored.packaging.src.packaging.version import Version
|
|
1286
|
+
installed = {canonicalize_name(name) for name in request['installed']}
|
|
1287
|
+
conflicting, missing = set(), set()
|
|
1288
|
+
for value in request['requirements']:
|
|
1289
|
+
requirement = parse_build_requirement(value)
|
|
1290
|
+
if canonicalize_name(requirement.name) not in installed:
|
|
1291
|
+
missing.add(value)
|
|
1292
|
+
continue
|
|
1293
|
+
try: version = Version(distribution(requirement.name).version)
|
|
1294
|
+
except PackageNotFoundError:
|
|
1295
|
+
missing.add(value)
|
|
1296
|
+
continue
|
|
1297
|
+
if version not in requirement.specifier: conflicting.add((requirement.name + '==' + str(version), value))
|
|
1298
|
+
return dict(conflicting=sorted(conflicting), missing=sorted(missing))
|
|
1299
|
+
|
|
1300
|
+
def read_build_system(request):
|
|
1301
|
+
import tomllib
|
|
1302
|
+
from micropip._vendored.packaging.src.packaging.requirements import InvalidRequirement
|
|
1303
|
+
class InstallationError(Exception): pass
|
|
1304
|
+
source = request['source']
|
|
1305
|
+
filename = os.path.join(source, 'pyproject.toml')
|
|
1306
|
+
has_project, has_setup = os.path.isfile(filename), os.path.isfile(os.path.join(source, 'setup.py'))
|
|
1307
|
+
if request.get('installation') == 'directory' and not has_project and not has_setup:
|
|
1308
|
+
raise InstallationError("Directory {!r} is not installable. Neither 'setup.py' nor 'pyproject.toml' found.".format(request.get('name', source)))
|
|
1309
|
+
system = None
|
|
1310
|
+
if has_project:
|
|
1311
|
+
limit = request['maxBytes']
|
|
1312
|
+
with open(filename, 'rb') as stream: raw = stream.read() if limit is None else stream.read(limit + 1)
|
|
1313
|
+
if limit is not None and len(raw) > limit: raise ValueError('Python build configuration limit exceeded')
|
|
1314
|
+
system = tomllib.loads(raw.decode('utf-8')).get('build-system')
|
|
1315
|
+
selected = request.get('usePep517')
|
|
1316
|
+
if has_project and not has_setup:
|
|
1317
|
+
if selected is False: raise InstallationError('Disabling PEP 517 processing is invalid: project does not have a setup.py')
|
|
1318
|
+
selected = True
|
|
1319
|
+
elif system and 'build-backend' in system:
|
|
1320
|
+
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']))
|
|
1321
|
+
selected = True
|
|
1322
|
+
elif selected is None: selected = has_project
|
|
1323
|
+
if not selected:
|
|
1324
|
+
if request.get('installation') and not os.path.exists(os.path.join(source, 'setup.py')):
|
|
1325
|
+
raise InstallationError('File "setup.py" not found for legacy project {}.'.format(request.get('name', source)))
|
|
1326
|
+
return None
|
|
1327
|
+
defaults = ['setuptools>=40.8.0', 'wheel']
|
|
1328
|
+
if system is None: system = {'requires': defaults, 'build-backend': 'setuptools.build_meta:__legacy__'}
|
|
1329
|
+
def invalid(reason):
|
|
1330
|
+
raise InstallationError('{} has a pyproject.toml file that does not comply with PEP 518: {}'.format(request.get('name', source), reason))
|
|
1331
|
+
if 'requires' not in system: invalid("it has a 'build-system' table but not 'build-system.requires' which is mandatory in the table")
|
|
1332
|
+
requirements = system['requires']
|
|
1333
|
+
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.")
|
|
1334
|
+
for value in requirements:
|
|
1335
|
+
try:
|
|
1336
|
+
parse_build_requirement(value)
|
|
1337
|
+
except InvalidRequirement: invalid("'build-system.requires' contains an invalid requirement: {!r}".format(value))
|
|
1338
|
+
backend = system.get('build-backend')
|
|
1339
|
+
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', []))
|
|
1340
|
+
|
|
1341
|
+
def build_legacy_wheel(request):
|
|
1342
|
+
source = os.path.realpath(request['source'])
|
|
1343
|
+
directory = os.path.abspath(request['wheelDirectory'])
|
|
1344
|
+
filename = os.path.join(source, 'setup.py')
|
|
1345
|
+
os.chdir(source)
|
|
1346
|
+
sys.path.insert(0, '')
|
|
1347
|
+
sys.argv = [filename, 'bdist_wheel', '-d', directory]
|
|
1348
|
+
namespace = dict(__name__='__main__', __file__=filename)
|
|
1349
|
+
try:
|
|
1350
|
+
exec("import io, os, sys, setuptools, tokenize\nsys.argv[0] = __file__\nf = getattr(tokenize, 'open', open)(__file__) if os.path.exists(__file__) else io.StringIO('from setuptools import setup; setup()')\ncode = f.read().replace('\\r\\n', '\\n')\nf.close()\nexec(compile(code, __file__, 'exec'))", namespace)
|
|
1351
|
+
except SystemExit as error:
|
|
1352
|
+
if error.code not in (None, 0): raise
|
|
1353
|
+
first = None
|
|
1354
|
+
with os.scandir(directory) as entries:
|
|
1355
|
+
for entry in entries:
|
|
1356
|
+
if first is None or entry.name < first: first = entry.name
|
|
1357
|
+
if first is None: raise RuntimeError('Legacy wheel build created no files')
|
|
1358
|
+
return first
|
|
1359
|
+
|
|
1360
|
+
def read_source_requirement(request):
|
|
1361
|
+
from micropip._vendored.packaging.src.packaging.requirements import InvalidRequirement
|
|
1362
|
+
try: requirement = parse_build_requirement(request['source'])
|
|
1363
|
+
except InvalidRequirement: return None
|
|
1364
|
+
if not requirement.url: return None
|
|
1365
|
+
marker = requirement.marker
|
|
1366
|
+
return dict(name=requirement.name, extras=sorted({'_'.join(part for part in extra.lower().split('_') if part) for extra in requirement.extras}), url=requirement.url, marker=str(marker) if marker else None, active=not marker or marker.evaluate({'extra': ''}))
|
|
1367
|
+
|
|
1368
|
+
def read_editable_requirement(request):
|
|
1369
|
+
from pathlib import Path
|
|
1370
|
+
from micropip._vendored.packaging.src.packaging.requirements import Requirement
|
|
1371
|
+
class InstallationError(Exception): pass
|
|
1372
|
+
source, extras = request['source'], None
|
|
1373
|
+
if request.get('requirementLine'):
|
|
1374
|
+
import shlex, optparse
|
|
1375
|
+
class OptionParsingError(Exception): pass
|
|
1376
|
+
class Parser(optparse.OptionParser):
|
|
1377
|
+
def exit(self, status=0, message=None): raise OptionParsingError(message)
|
|
1378
|
+
parser = Parser(add_help_option=False)
|
|
1379
|
+
parser.add_option('-e', '--editable', action='append', dest='editables')
|
|
1380
|
+
options, _ = parser.parse_args(shlex.split(source))
|
|
1381
|
+
if not options.editables: raise InstallationError('Editable requirement line requires -e')
|
|
1382
|
+
source = options.editables[0]
|
|
1383
|
+
start = source.rfind('[')
|
|
1384
|
+
if start > 0 and source.endswith(']') and start < len(source) - 2 and ']' not in source[start + 1:-1]:
|
|
1385
|
+
source, extras = source[:start], source[start:]
|
|
1386
|
+
if os.path.isdir(source):
|
|
1387
|
+
if not any(os.path.exists(os.path.join(source, name)) for name in ('setup.py', 'setup.cfg')):
|
|
1388
|
+
message = 'File "setup.py" or "setup.cfg" not found. Directory cannot be installed in editable mode: {}'.format(os.path.abspath(source))
|
|
1389
|
+
if os.path.isfile(os.path.join(source, 'pyproject.toml')): message += '\n(A "pyproject.toml" file was found, but editable mode currently requires a setuptools-based build.)'
|
|
1390
|
+
raise InstallationError(message)
|
|
1391
|
+
source = Path(os.path.abspath(source)).as_uri()
|
|
1392
|
+
if not source.lower().startswith('file:'): raise InstallationError('Editable source requires a local directory')
|
|
1393
|
+
name = ''
|
|
1394
|
+
for index, character in enumerate(source):
|
|
1395
|
+
if character in '#&' and source.startswith('egg=', index + 1):
|
|
1396
|
+
name = source[index + 5:].split('&', 1)[0]
|
|
1397
|
+
break
|
|
1398
|
+
return dict(name=name, url=source, extras=sorted(Requirement('placeholder' + extras.lower()).extras) if extras else [], marker=None, active=True)
|
|
1399
|
+
|
|
1400
|
+
def main():
|
|
1401
|
+
request = send('request')
|
|
1402
|
+
if request['hook'] == 'get_requires_for_legacy_wheel': request.update(hook='get_requires_for_build_wheel', backend='setuptools.build_meta:__legacy__')
|
|
1403
|
+
if request['hook'] == 'read_source_origin': return read_source_origin(request)
|
|
1404
|
+
if request['hook'] == 'read_download_filename': return read_download_filename(request)
|
|
1405
|
+
if request['hook'] == 'read_source_requirement': return read_source_requirement(request)
|
|
1406
|
+
if request['hook'] == 'read_editable_requirement': return read_editable_requirement(request)
|
|
1407
|
+
if request['hook'] == 'build_legacy_wheel':
|
|
1408
|
+
if not request.get('editable'): return build_legacy_wheel(request)
|
|
1409
|
+
request.update(hook='build_wheel', backend='setuptools.build_meta:__legacy__')
|
|
1410
|
+
if request['hook'] == 'read_build_system': return read_build_system(request)
|
|
1411
|
+
if request['hook'] == 'check_build_requirements': return check_build_requirements(request)
|
|
1412
|
+
source = os.path.realpath(request['source'])
|
|
1413
|
+
wheel_directory = os.path.abspath(request['wheelDirectory']) if request['hook'] == 'build_wheel' else None
|
|
1414
|
+
metadata_directory = os.path.abspath(request['metadataDirectory']) if request.get('metadataDirectory') is not None else None
|
|
1415
|
+
os.chdir(source)
|
|
1416
|
+
paths = []
|
|
1417
|
+
for entry in request.get('backendPath', []):
|
|
1418
|
+
if os.path.isabs(entry): raise ValueError('paths must be relative')
|
|
1419
|
+
path = os.path.realpath(os.path.join(source, entry))
|
|
1420
|
+
if os.path.commonpath([source, path]) != source: raise ValueError('paths must be inside source tree')
|
|
1421
|
+
paths.append(path)
|
|
1422
|
+
sys.path[:0] = paths
|
|
1423
|
+
module, _, attributes = request['backend'].partition(':')
|
|
1424
|
+
backend = importlib.import_module(module)
|
|
1425
|
+
if paths and not any(os.path.commonpath([os.path.realpath(backend.__file__), path]) == path for path in paths):
|
|
1426
|
+
raise ValueError('Backend was not loaded from backend-path')
|
|
1427
|
+
if attributes:
|
|
1428
|
+
for name in attributes.split('.'): backend = getattr(backend, name)
|
|
1429
|
+
settings = request.get('configSettings')
|
|
1430
|
+
if request['hook'] == 'get_requires_for_build_wheel':
|
|
1431
|
+
try: hook = backend.get_requires_for_build_wheel
|
|
1432
|
+
except AttributeError: result = []
|
|
1433
|
+
else: result = hook(settings)
|
|
1434
|
+
else:
|
|
1435
|
+
if request.get('editable'): result = backend.build_editable(wheel_directory, {'editable_mode': 'compat'}, metadata_directory)
|
|
1436
|
+
else: result = backend.build_wheel(wheel_directory, settings, metadata_directory)
|
|
1437
|
+
return result
|
|
1438
|
+
|
|
1439
|
+
try:
|
|
1440
|
+
result = main()
|
|
1441
|
+
encoder = json.JSONEncoder(ensure_ascii=False, separators=(',', ':'))
|
|
1442
|
+
for part in encoder.iterencode(result):
|
|
1443
|
+
for offset in range(0, len(part), 4096): send('text', text=part[offset:offset + 4096])
|
|
1444
|
+
send('done')
|
|
1445
|
+
except Exception as error:
|
|
1446
|
+
send('error', type=type(error).__name__, message=str(error))
|
|
1447
|
+
`)();
|
|
1448
|
+
|
|
1449
|
+
// packages/safe-bash-command-python/src/build-dependencies.ts
|
|
1450
|
+
function createPythonBuildDependencies(options) {
|
|
1451
|
+
const isolated = { ...options };
|
|
1452
|
+
for (const key of ["packages", "requirements", "packageProfile", "provisioning"]) Reflect.deleteProperty(isolated, key);
|
|
1453
|
+
const hook = createPythonBuildBackend(isolated), command = createPythonExecutorCommands(isolated)[0];
|
|
1454
|
+
return async (input, caller) => {
|
|
1455
|
+
const context = { ...caller };
|
|
1456
|
+
context.signal.throwIfAborted();
|
|
1457
|
+
const request = structuredClone(input), { source, buildSystem, configSettings } = request;
|
|
1458
|
+
if (typeof source !== "string" || !source || request.name !== void 0 && typeof request.name !== "string" || buildSystem !== null && (!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");
|
|
1459
|
+
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");
|
|
1460
|
+
if (context.maxBytes !== Infinity && (!Number.isSafeInteger(context.maxBytes) || context.maxBytes < 0)) throw new RangeError("Invalid Python build metadata limit");
|
|
1461
|
+
const install = async (requirements2) => {
|
|
1462
|
+
if (!requirements2.length) return;
|
|
1463
|
+
const result = await command.execute({ ...context, command: "python", args: ["-m", "pip", "install", "--", ...requirements2], stdin: toByteSource6("") });
|
|
1464
|
+
context.signal.throwIfAborted();
|
|
1465
|
+
if (result.exitCode) throw new Error("Python build dependency installation exited with status " + result.exitCode);
|
|
1466
|
+
};
|
|
1467
|
+
const check = async (requirements2, against) => {
|
|
1468
|
+
if (!requirements2.length) return [];
|
|
1469
|
+
const receipt = await isolated.environment.prepare(context);
|
|
1470
|
+
let installed;
|
|
1471
|
+
try {
|
|
1472
|
+
if (!receipt.records && receipt.restore?.length) throw new Error("Python build dependency checks require an installed metadata manifest");
|
|
1473
|
+
installed = receipt.records?.map((record2) => record2[0]) ?? [];
|
|
1474
|
+
} finally {
|
|
1475
|
+
await isolated.environment.finish(receipt);
|
|
1476
|
+
}
|
|
1477
|
+
const status = await hook({ hook: "check_build_requirements", source, requirements: requirements2, installed }, context);
|
|
1478
|
+
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" });
|
|
1479
|
+
return status.missing;
|
|
1480
|
+
};
|
|
1481
|
+
if (buildSystem) {
|
|
1482
|
+
await install(buildSystem.requires);
|
|
1483
|
+
const missing = await check(buildSystem.check, "PEP 517/518 supported requirements");
|
|
1484
|
+
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"));
|
|
1485
|
+
}
|
|
1486
|
+
const requirements = await hook(buildSystem ? { hook: "get_requires_for_build_wheel", source, backend: buildSystem.backend, backendPath: buildSystem.backendPath, ...configSettings === void 0 ? {} : { configSettings } } : { hook: "get_requires_for_legacy_wheel", source }, context);
|
|
1487
|
+
await install(await check(requirements, "the backend dependencies"));
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// packages/safe-bash-command-python/src/source-snapshot.ts
|
|
1492
|
+
import { compareIdentity } from "@poe-platform/safe-fs/core";
|
|
1493
|
+
import { FsError as FsError2 } from "../../safe-bash-contracts/index.js";
|
|
1494
|
+
import { resolvePath } from "../../safe-bash-contracts/path.js";
|
|
1495
|
+
import { yieldTurn } from "../../safe-bash-contracts/yield.js";
|
|
1496
|
+
import { admitCopySource, admitCopyDestination, copyCheckedSource } from "../../safe-bash-io-engine/commands/copy-source.js";
|
|
1497
|
+
var serial = 0;
|
|
1498
|
+
async function createPythonSourceSnapshot(source, directory, context) {
|
|
1499
|
+
const { fs, signal } = context, settings = { signal };
|
|
1500
|
+
signal.throwIfAborted();
|
|
1501
|
+
if (!fs.iterateDirectory) throw new Error("Python source snapshots require lazy directory enumeration");
|
|
1502
|
+
const origin = await fs.realpath(resolvePath(context.cwd, source), settings);
|
|
1503
|
+
const parentPath = await fs.realpath(resolvePath(context.cwd, directory), settings);
|
|
1504
|
+
const capabilities = await fs.capabilitiesFor?.(parentPath, settings) ?? fs.capabilities;
|
|
1505
|
+
if (!fs.prepareDirectory || !fs.removeTreeConditional || !fs.confineExtraction || !capabilities.atomicTreeRemoval) throw new Error("Python source snapshots require confined writes, conditional directory creation and tree removal");
|
|
1506
|
+
const original = await fs.stat(origin, settings), parent = await fs.stat(parentPath, settings);
|
|
1507
|
+
if (original.type !== "directory" || parent.type !== "directory") throw new FsError2("ENOTDIR", { path: origin });
|
|
1508
|
+
let path = "", owned;
|
|
1509
|
+
for (let attempt = 0; attempt < 16; attempt++) {
|
|
1510
|
+
path = resolvePath(parentPath, ".python-source-" + ++serial);
|
|
1511
|
+
try {
|
|
1512
|
+
owned = await fs.prepareDirectory(path, { parent, expected: null, mode: 448, signal });
|
|
1513
|
+
break;
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
signal.throwIfAborted();
|
|
1516
|
+
if (!(error instanceof FsError2) || !["EEXIST", "EAGAIN"].includes(error.code)) throw error;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (!owned) throw new Error("Python source snapshot names exhausted");
|
|
1520
|
+
let disposing;
|
|
1521
|
+
const snapshot = { path, dispose() {
|
|
1522
|
+
return disposing ??= (async () => {
|
|
1523
|
+
const current = await fs.lstat(path);
|
|
1524
|
+
if (compareIdentity(owned, current) !== "same") throw new Error("Python source snapshot identity changed");
|
|
1525
|
+
await fs.removeTreeConditional(path, { parent, expected: current });
|
|
1526
|
+
})();
|
|
1527
|
+
} };
|
|
1528
|
+
try {
|
|
1529
|
+
const destination = await fs.confineExtraction([path], settings);
|
|
1530
|
+
if (compareIdentity(owned, await fs.lstat(path, settings)) !== "same") throw new Error("Python source snapshot identity changed");
|
|
1531
|
+
let activeCleanup;
|
|
1532
|
+
context.registerCleanup?.(() => activeCleanup?.());
|
|
1533
|
+
const copyContext = { ...context, registerCleanup(cleanup) {
|
|
1534
|
+
activeCleanup = cleanup;
|
|
1535
|
+
}, fs: new Proxy(destination, { get(target, key) {
|
|
1536
|
+
const owner = key === "openReadFile" ? fs : target, value = Reflect.get(owner, key);
|
|
1537
|
+
return typeof value === "function" ? value.bind(owner) : value;
|
|
1538
|
+
} }) };
|
|
1539
|
+
const metadata = async (from, to, expected) => {
|
|
1540
|
+
const current = await fs.lstat(from, settings);
|
|
1541
|
+
if (compareIdentity(expected, current) !== "same") throw new Error("Python source identity changed while copying");
|
|
1542
|
+
if (destination.chmod) await destination.chmod(to, current.mode & 4095, settings);
|
|
1543
|
+
if (destination.utimes) await destination.utimes(to, current.atimeMs, current.mtimeMs, settings);
|
|
1544
|
+
};
|
|
1545
|
+
const copy = async (from, to, expected) => {
|
|
1546
|
+
signal.throwIfAborted();
|
|
1547
|
+
if (expected.type === "symlink") {
|
|
1548
|
+
if (!destination.symlink || !fs.readlink) throw new Error("Python source snapshots require symlink support");
|
|
1549
|
+
const target = await fs.readlink(from, settings);
|
|
1550
|
+
if (compareIdentity(expected, await fs.lstat(from, settings)) !== "same") throw new Error("Python source identity changed while copying");
|
|
1551
|
+
await destination.symlink(target, to, settings);
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
if (expected.type === "file") {
|
|
1555
|
+
await admitCopySource(context, from);
|
|
1556
|
+
await admitCopyDestination(copyContext, to, true);
|
|
1557
|
+
try {
|
|
1558
|
+
await copyCheckedSource(copyContext, from, to, expected, true);
|
|
1559
|
+
} finally {
|
|
1560
|
+
activeCleanup = void 0;
|
|
1561
|
+
}
|
|
1562
|
+
} else if (expected.type === "directory") {
|
|
1563
|
+
if (to !== path) await destination.mkdir(to, { mode: 448, signal });
|
|
1564
|
+
for await (const entry of fs.iterateDirectory(from, settings)) {
|
|
1565
|
+
await yieldTurn(signal);
|
|
1566
|
+
if (!entry.name || entry.name === "." || entry.name === ".." || entry.name.includes("/") || entry.name.includes("\0")) throw new Error("Invalid Python source directory entry");
|
|
1567
|
+
const child = resolvePath(from, entry.name);
|
|
1568
|
+
if (child === path || from === origin && [".tox", ".nox"].includes(entry.name)) continue;
|
|
1569
|
+
await copy(child, resolvePath(to, entry.name), await fs.lstat(child, settings));
|
|
1570
|
+
}
|
|
1571
|
+
} else throw new Error("Unsupported Python source entry: " + from);
|
|
1572
|
+
await metadata(from, to, expected);
|
|
1573
|
+
};
|
|
1574
|
+
await copy(origin, path, original);
|
|
1575
|
+
signal.throwIfAborted();
|
|
1576
|
+
return snapshot;
|
|
1577
|
+
} catch (error) {
|
|
1578
|
+
try {
|
|
1579
|
+
await snapshot.dispose();
|
|
1580
|
+
} catch (cleanup) {
|
|
1581
|
+
throw new AggregateError([error, cleanup], "Python source snapshot cleanup failed");
|
|
1582
|
+
}
|
|
1583
|
+
throw error;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// packages/safe-bash-command-python/src/llm-manifest.ts
|
|
1588
|
+
var pythonLlmManifest = {
|
|
1589
|
+
"llm": "0.27.1",
|
|
1590
|
+
"pyodide": "314.0.6",
|
|
1591
|
+
"distributions": [
|
|
1592
|
+
{
|
|
1593
|
+
"name": "llm",
|
|
1594
|
+
"version": "0.27.1",
|
|
1595
|
+
"url": "https://files.pythonhosted.org/packages/6c/67/47585c961ff2299749e519891681025c2685b75801e3f784ee232853c5b0/llm-0.27.1-py3-none-any.whl",
|
|
1596
|
+
"sha256": "a884a575062fbea8c2b129708a80e146fa9682bd1c444d8d7b028196107de727",
|
|
1597
|
+
"bytes": 82500,
|
|
1598
|
+
"expandedBytes": 348753,
|
|
1599
|
+
"nativeModules": [],
|
|
1600
|
+
"file": "llm-0.27.1-py3-none-any.whl"
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
"name": "pydantic",
|
|
1604
|
+
"version": "2.12.5",
|
|
1605
|
+
"url": "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl",
|
|
1606
|
+
"sha256": "e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d",
|
|
1607
|
+
"bytes": 463580,
|
|
1608
|
+
"expandedBytes": 1851456,
|
|
1609
|
+
"nativeModules": [],
|
|
1610
|
+
"file": "pydantic-2.12.5-py3-none-any.whl"
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
"name": "pydantic-core",
|
|
1614
|
+
"version": "2.41.5",
|
|
1615
|
+
"url": "https://cdn.jsdelivr.net/pyodide/v314.0.6/full/pydantic_core-2.41.5-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
|
|
1616
|
+
"sha256": "9802bd7a5e6679ec4c13be778f19f021e6147459c89cc916b913f7970f86dddb",
|
|
1617
|
+
"bytes": 1310163,
|
|
1618
|
+
"expandedBytes": 4592549,
|
|
1619
|
+
"nativeModules": [
|
|
1620
|
+
{
|
|
1621
|
+
"path": "pydantic_core/_pydantic_core.cpython-314-wasm32-emscripten.so",
|
|
1622
|
+
"bytes": 4275289,
|
|
1623
|
+
"sha256": "94477475eb48c67b6b25fd47a7955c1d01479f7971522aff90215bb517be8246"
|
|
1624
|
+
}
|
|
1625
|
+
],
|
|
1626
|
+
"file": "pydantic_core-2.41.5-cp314-cp314-pyemscripten_2026_0_wasm32.whl"
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
"name": "httpx",
|
|
1630
|
+
"version": "0.28.1",
|
|
1631
|
+
"url": "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl",
|
|
1632
|
+
"sha256": "d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad",
|
|
1633
|
+
"bytes": 73517,
|
|
1634
|
+
"expandedBytes": 295303,
|
|
1635
|
+
"nativeModules": [],
|
|
1636
|
+
"file": "httpx-0.28.1-py3-none-any.whl"
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
"name": "click",
|
|
1640
|
+
"version": "8.1.8",
|
|
1641
|
+
"url": "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl",
|
|
1642
|
+
"sha256": "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2",
|
|
1643
|
+
"bytes": 98188,
|
|
1644
|
+
"expandedBytes": 355272,
|
|
1645
|
+
"nativeModules": [],
|
|
1646
|
+
"file": "click-8.1.8-py3-none-any.whl"
|
|
1647
|
+
},
|
|
1648
|
+
{
|
|
1649
|
+
"name": "sqlite-utils",
|
|
1650
|
+
"version": "3.38",
|
|
1651
|
+
"url": "https://files.pythonhosted.org/packages/4d/eb/f8e8e827805f810838efff3311cccd2601238c5fa3fc35c1f878709e161b/sqlite_utils-3.38-py3-none-any.whl",
|
|
1652
|
+
"sha256": "8a27441015c3b2ef475f555861f7a2592f73bc60d247af9803a11b65fc605bf9",
|
|
1653
|
+
"bytes": 68183,
|
|
1654
|
+
"expandedBytes": 278821,
|
|
1655
|
+
"nativeModules": [],
|
|
1656
|
+
"file": "sqlite_utils-3.38-py3-none-any.whl"
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
"name": "sqlite-migrate",
|
|
1660
|
+
"version": "0.1b0",
|
|
1661
|
+
"url": "https://files.pythonhosted.org/packages/df/92/994545b912e6d6feb40323047f02ca039321e690aa2c27afcd5c4105e37b/sqlite_migrate-0.1b0-py3-none-any.whl",
|
|
1662
|
+
"sha256": "a4125e35e1de3dc56b6b6ec60e9833ce0ce20192b929ddcb2d4246c5098859c6",
|
|
1663
|
+
"bytes": 9986,
|
|
1664
|
+
"expandedBytes": 25349,
|
|
1665
|
+
"nativeModules": [],
|
|
1666
|
+
"file": "sqlite_migrate-0.1b0-py3-none-any.whl"
|
|
1667
|
+
},
|
|
1668
|
+
{
|
|
1669
|
+
"name": "python-ulid",
|
|
1670
|
+
"version": "3.0.0",
|
|
1671
|
+
"url": "https://files.pythonhosted.org/packages/63/4e/cc2ba2c0df2589f35a4db8473b8c2ba9bbfc4acdec4a94f1c78934d2350f/python_ulid-3.0.0-py3-none-any.whl",
|
|
1672
|
+
"sha256": "e4c4942ff50dbd79167ad01ac725ec58f924b4018025ce22c858bfcff99a5e31",
|
|
1673
|
+
"bytes": 11194,
|
|
1674
|
+
"expandedBytes": 30439,
|
|
1675
|
+
"nativeModules": [],
|
|
1676
|
+
"file": "python_ulid-3.0.0-py3-none-any.whl"
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
"name": "condense-json",
|
|
1680
|
+
"version": "0.1.3",
|
|
1681
|
+
"url": "https://files.pythonhosted.org/packages/28/5f/63badd4924358fad1efa6defd66eef700ccf8783c0e44098987f867e8b1f/condense_json-0.1.3-py3-none-any.whl",
|
|
1682
|
+
"sha256": "e0a3d42db4f44a89e74af8737d8e517e97420be0f7e5437087f4decfd38c3366",
|
|
1683
|
+
"bytes": 8432,
|
|
1684
|
+
"expandedBytes": 21056,
|
|
1685
|
+
"nativeModules": [],
|
|
1686
|
+
"file": "condense_json-0.1.3-py3-none-any.whl"
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
"name": "puremagic",
|
|
1690
|
+
"version": "1.30",
|
|
1691
|
+
"url": "https://files.pythonhosted.org/packages/91/ed/1e347d85d05b37a8b9a039ca832e5747e1e5248d0bd66042783ef48b4a37/puremagic-1.30-py3-none-any.whl",
|
|
1692
|
+
"sha256": "5eeeb2dd86f335b9cfe8e205346612197af3500c6872dffebf26929f56e9d3c1",
|
|
1693
|
+
"bytes": 43304,
|
|
1694
|
+
"expandedBytes": 185153,
|
|
1695
|
+
"nativeModules": [],
|
|
1696
|
+
"file": "puremagic-1.30-py3-none-any.whl"
|
|
1697
|
+
},
|
|
1698
|
+
{
|
|
1699
|
+
"name": "anyio",
|
|
1700
|
+
"version": "4.15.1",
|
|
1701
|
+
"url": "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl",
|
|
1702
|
+
"sha256": "6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101",
|
|
1703
|
+
"bytes": 132079,
|
|
1704
|
+
"expandedBytes": 539217,
|
|
1705
|
+
"nativeModules": [],
|
|
1706
|
+
"file": "anyio-4.15.1-py3-none-any.whl"
|
|
1707
|
+
},
|
|
1708
|
+
{
|
|
1709
|
+
"name": "httpcore",
|
|
1710
|
+
"version": "1.0.9",
|
|
1711
|
+
"url": "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl",
|
|
1712
|
+
"sha256": "2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55",
|
|
1713
|
+
"bytes": 78784,
|
|
1714
|
+
"expandedBytes": 288016,
|
|
1715
|
+
"nativeModules": [],
|
|
1716
|
+
"file": "httpcore-1.0.9-py3-none-any.whl"
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
"name": "typing-extensions",
|
|
1720
|
+
"version": "4.16.0",
|
|
1721
|
+
"url": "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl",
|
|
1722
|
+
"sha256": "481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8",
|
|
1723
|
+
"bytes": 45571,
|
|
1724
|
+
"expandedBytes": 182767,
|
|
1725
|
+
"nativeModules": [],
|
|
1726
|
+
"file": "typing_extensions-4.16.0-py3-none-any.whl"
|
|
1727
|
+
},
|
|
1728
|
+
{
|
|
1729
|
+
"name": "annotated-types",
|
|
1730
|
+
"version": "0.8.0",
|
|
1731
|
+
"url": "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl",
|
|
1732
|
+
"sha256": "f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0",
|
|
1733
|
+
"bytes": 13427,
|
|
1734
|
+
"expandedBytes": 36347,
|
|
1735
|
+
"nativeModules": [],
|
|
1736
|
+
"file": "annotated_types-0.8.0-py3-none-any.whl"
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
"name": "click-default-group",
|
|
1740
|
+
"version": "1.2.4",
|
|
1741
|
+
"url": "https://files.pythonhosted.org/packages/2c/1a/aff8bb287a4b1400f69e09a53bd65de96aa5cee5691925b38731c67fc695/click_default_group-1.2.4-py2.py3-none-any.whl",
|
|
1742
|
+
"sha256": "9b60486923720e7fc61731bdb32b617039aba820e22e1c88766b1125592eaa5f",
|
|
1743
|
+
"bytes": 4123,
|
|
1744
|
+
"expandedBytes": 8671,
|
|
1745
|
+
"nativeModules": [],
|
|
1746
|
+
"file": "click_default_group-1.2.4-py2.py3-none-any.whl"
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
"name": "h11",
|
|
1750
|
+
"version": "0.16.0",
|
|
1751
|
+
"url": "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl",
|
|
1752
|
+
"sha256": "63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86",
|
|
1753
|
+
"bytes": 37515,
|
|
1754
|
+
"expandedBytes": 103935,
|
|
1755
|
+
"nativeModules": [],
|
|
1756
|
+
"file": "h11-0.16.0-py3-none-any.whl"
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
"name": "idna",
|
|
1760
|
+
"version": "3.20",
|
|
1761
|
+
"url": "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl",
|
|
1762
|
+
"sha256": "ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c",
|
|
1763
|
+
"bytes": 69583,
|
|
1764
|
+
"expandedBytes": 342091,
|
|
1765
|
+
"nativeModules": [],
|
|
1766
|
+
"file": "idna-3.20-py3-none-any.whl"
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
"name": "typing-inspection",
|
|
1770
|
+
"version": "0.4.4",
|
|
1771
|
+
"url": "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl",
|
|
1772
|
+
"sha256": "65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147",
|
|
1773
|
+
"bytes": 14750,
|
|
1774
|
+
"expandedBytes": 54538,
|
|
1775
|
+
"nativeModules": [],
|
|
1776
|
+
"file": "typing_inspection-0.4.4-py3-none-any.whl"
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
"name": "certifi",
|
|
1780
|
+
"version": "2026.7.22",
|
|
1781
|
+
"url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl",
|
|
1782
|
+
"sha256": "62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775",
|
|
1783
|
+
"bytes": 136983,
|
|
1784
|
+
"expandedBytes": 248921,
|
|
1785
|
+
"nativeModules": [],
|
|
1786
|
+
"file": "certifi-2026.7.22-py3-none-any.whl"
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
"name": "pluggy",
|
|
1790
|
+
"version": "1.6.0",
|
|
1791
|
+
"url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl",
|
|
1792
|
+
"sha256": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746",
|
|
1793
|
+
"bytes": 20538,
|
|
1794
|
+
"expandedBytes": 65823,
|
|
1795
|
+
"nativeModules": [],
|
|
1796
|
+
"file": "pluggy-1.6.0-py3-none-any.whl"
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
"name": "python-dateutil",
|
|
1800
|
+
"version": "2.9.0.post0",
|
|
1801
|
+
"url": "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl",
|
|
1802
|
+
"sha256": "a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427",
|
|
1803
|
+
"bytes": 229892,
|
|
1804
|
+
"expandedBytes": 441579,
|
|
1805
|
+
"nativeModules": [],
|
|
1806
|
+
"file": "python_dateutil-2.9.0.post0-py2.py3-none-any.whl"
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
"name": "six",
|
|
1810
|
+
"version": "1.17.0",
|
|
1811
|
+
"url": "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl",
|
|
1812
|
+
"sha256": "4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
|
|
1813
|
+
"bytes": 11050,
|
|
1814
|
+
"expandedBytes": 37975,
|
|
1815
|
+
"nativeModules": [],
|
|
1816
|
+
"file": "six-1.17.0-py2.py3-none-any.whl"
|
|
1817
|
+
},
|
|
1818
|
+
{
|
|
1819
|
+
"name": "pyyaml",
|
|
1820
|
+
"version": "6.0.3",
|
|
1821
|
+
"url": "https://cdn.jsdelivr.net/pyodide/v314.0.6/full/pyyaml-6.0.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
|
|
1822
|
+
"sha256": "b1447216501f0d3aef290558fe33691dd51b0839e70f9a970defb12b36d82df8",
|
|
1823
|
+
"bytes": 111318,
|
|
1824
|
+
"expandedBytes": 439274,
|
|
1825
|
+
"nativeModules": [
|
|
1826
|
+
{
|
|
1827
|
+
"path": "yaml/_yaml.cpython-314-wasm32-emscripten.so",
|
|
1828
|
+
"bytes": 214986,
|
|
1829
|
+
"sha256": "a3b18ccfd3c043f10f4ae4044bea14079f14a30707bf5c6ddf1f6dc61abaffc1"
|
|
1830
|
+
}
|
|
1831
|
+
],
|
|
1832
|
+
"file": "pyyaml-6.0.3-cp314-cp314-pyemscripten_2026_0_wasm32.whl"
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
"name": "sniffio",
|
|
1836
|
+
"version": "1.3.1",
|
|
1837
|
+
"url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl",
|
|
1838
|
+
"sha256": "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
|
|
1839
|
+
"bytes": 10235,
|
|
1840
|
+
"expandedBytes": 22921,
|
|
1841
|
+
"nativeModules": [],
|
|
1842
|
+
"file": "sniffio-1.3.1-py3-none-any.whl"
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
"name": "sqlite-fts4",
|
|
1846
|
+
"version": "1.0.3",
|
|
1847
|
+
"url": "https://files.pythonhosted.org/packages/51/29/0096e8b1811aaa78cfb296996f621f41120c21c2f5cd448ae1d54979d9fc/sqlite_fts4-1.0.3-py3-none-any.whl",
|
|
1848
|
+
"sha256": "0359edd8dea6fd73c848989e1e2b1f31a50fe5f9d7272299ff0e8dbaa62d035f",
|
|
1849
|
+
"bytes": 9972,
|
|
1850
|
+
"expandedBytes": 28735,
|
|
1851
|
+
"nativeModules": [],
|
|
1852
|
+
"file": "sqlite_fts4-1.0.3-py3-none-any.whl"
|
|
1853
|
+
},
|
|
1854
|
+
{
|
|
1855
|
+
"name": "tabulate",
|
|
1856
|
+
"version": "0.10.0",
|
|
1857
|
+
"url": "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl",
|
|
1858
|
+
"sha256": "f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3",
|
|
1859
|
+
"bytes": 39814,
|
|
1860
|
+
"expandedBytes": 150857,
|
|
1861
|
+
"nativeModules": [],
|
|
1862
|
+
"file": "tabulate-0.10.0-py3-none-any.whl"
|
|
1863
|
+
}
|
|
1864
|
+
]
|
|
1865
|
+
};
|
|
1866
|
+
|
|
1867
|
+
// packages/safe-bash-command-python/src/llm-packages.ts
|
|
1868
|
+
var pythonLlmPackages = pythonLlmManifest;
|
|
1869
|
+
function installPythonLlmPackages(runtime, assets) {
|
|
1870
|
+
if (runtime.version !== pythonLlmManifest.pyodide) throw new Error("LLM packages require the pinned Python runtime");
|
|
1871
|
+
if (assets.length !== pythonLlmManifest.distributions.length) throw new Error("Incomplete Python LLM package assets");
|
|
1872
|
+
const root = "/lib/python3.14/site-packages";
|
|
1873
|
+
const wheelRoot = root + "/llm-wheels";
|
|
1874
|
+
runtime.FS.mkdirTree(wheelRoot);
|
|
1875
|
+
const paths = [];
|
|
1876
|
+
for (const distribution of pythonLlmManifest.distributions) {
|
|
1877
|
+
const asset = assets.find((asset2) => asset2.file === distribution.file);
|
|
1878
|
+
if (!asset || asset.bytes.byteLength !== distribution.bytes) throw new Error("Invalid Python LLM package asset: " + distribution.file);
|
|
1879
|
+
if (distribution.nativeModules.length || ["llm", "puremagic"].includes(distribution.name)) {
|
|
1880
|
+
runtime.unpackArchive(asset.bytes, "zip", { extractDir: root });
|
|
1881
|
+
} else {
|
|
1882
|
+
const path = wheelRoot + "/" + distribution.file;
|
|
1883
|
+
runtime.FS.writeFile(path, asset.bytes);
|
|
1884
|
+
paths.push(path);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
runtime.FS.writeFile(root + "/readline.py", new TextEncoder().encode(
|
|
1888
|
+
'"""Readline key bindings for the non-terminal Worker input stream."""\ndef parse_and_bind(binding):\n import sys\n if sys.stdin.isatty():\n raise OSError("Terminal line editing is unavailable in this runtime")\n'
|
|
1889
|
+
));
|
|
1890
|
+
runtime.FS.writeFile(root + "/llm_safe_host.py", new TextEncoder().encode(pythonLlmProvider));
|
|
1891
|
+
const metadata = root + "/llm_safe_host-0.1.dist-info";
|
|
1892
|
+
runtime.FS.mkdirTree(metadata);
|
|
1893
|
+
runtime.FS.writeFile(metadata + "/METADATA", new TextEncoder().encode("Metadata-Version: 2.1\nName: llm-safe-host\nVersion: 0.1\n"));
|
|
1894
|
+
runtime.FS.writeFile(metadata + "/entry_points.txt", new TextEncoder().encode("[llm]\nsafe_host = llm_safe_host\n"));
|
|
1895
|
+
runtime.globals.set("_safe_llm_wheel_paths", JSON.stringify(paths));
|
|
1896
|
+
try {
|
|
1897
|
+
runtime.runPython(String.raw`
|
|
1898
|
+
import json, sys
|
|
1899
|
+
sys.path.extend(json.loads(_safe_llm_wheel_paths))
|
|
1900
|
+
def _install_safe_llm_import():
|
|
1901
|
+
if any(getattr(finder, "_safe_llm_provider", False) for finder in sys.meta_path):
|
|
1902
|
+
return
|
|
1903
|
+
def configure_plugins(module):
|
|
1904
|
+
module.DEFAULT_PLUGINS = ("llm.default_plugins.default_tools",)
|
|
1905
|
+
module.LLM_LOAD_PLUGINS = "llm-safe-host"
|
|
1906
|
+
|
|
1907
|
+
def configure_provider(module):
|
|
1908
|
+
configure_plugins(module.plugins)
|
|
1909
|
+
module.plugins.load_plugins()
|
|
1910
|
+
from llm_safe_host import restrict_providers
|
|
1911
|
+
restrict_providers()
|
|
1912
|
+
|
|
1913
|
+
class HostLlmFinder:
|
|
1914
|
+
_safe_llm_provider = True
|
|
1915
|
+
def find_spec(self, fullname, path=None, target=None):
|
|
1916
|
+
if fullname not in ("llm", "llm.plugins"):
|
|
1917
|
+
return None
|
|
1918
|
+
for finder in tuple(sys.meta_path):
|
|
1919
|
+
if finder is self:
|
|
1920
|
+
continue
|
|
1921
|
+
spec = finder.find_spec(fullname, path, target)
|
|
1922
|
+
if spec is None:
|
|
1923
|
+
continue
|
|
1924
|
+
original = spec.loader
|
|
1925
|
+
class HostLlmLoader:
|
|
1926
|
+
def create_module(self, spec):
|
|
1927
|
+
return original.create_module(spec)
|
|
1928
|
+
def exec_module(self, module):
|
|
1929
|
+
original.exec_module(module)
|
|
1930
|
+
if fullname == "llm.plugins":
|
|
1931
|
+
configure_plugins(module)
|
|
1932
|
+
else:
|
|
1933
|
+
configure_provider(module)
|
|
1934
|
+
def __getattr__(self, name):
|
|
1935
|
+
return getattr(original, name)
|
|
1936
|
+
spec.loader = HostLlmLoader()
|
|
1937
|
+
return spec
|
|
1938
|
+
return None
|
|
1939
|
+
|
|
1940
|
+
sys.meta_path.insert(0, HostLlmFinder())
|
|
1941
|
+
if "llm" in sys.modules:
|
|
1942
|
+
configure_provider(sys.modules["llm"])
|
|
1943
|
+
_install_safe_llm_import()
|
|
1944
|
+
del _install_safe_llm_import
|
|
1945
|
+
`);
|
|
1946
|
+
} finally {
|
|
1947
|
+
runtime.globals.delete("_safe_llm_wheel_paths");
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// packages/safe-bash-command-python/src/build-wheel.ts
|
|
1952
|
+
import { FsError as FsError3 } from "../../safe-bash-contracts/index.js";
|
|
1953
|
+
import { resolvePath as resolvePath2, basename } from "../../safe-bash-contracts/path.js";
|
|
1954
|
+
async function publishPythonBuildWheel(source, directory, maxBytes, context) {
|
|
1955
|
+
const { fs, signal } = context, settings = { signal };
|
|
1956
|
+
signal.throwIfAborted();
|
|
1957
|
+
if (maxBytes !== Infinity && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) throw new RangeError("Invalid Python wheel size limit");
|
|
1958
|
+
const filename = basename(source);
|
|
1959
|
+
if (!filename.endsWith(".whl") || filename.includes("\\") || filename.includes("\0")) throw new TypeError("Invalid Python build wheel filename");
|
|
1960
|
+
const root = await fs.realpath(resolvePath2(context.cwd, directory), settings);
|
|
1961
|
+
const caps = await fs.capabilitiesFor?.(root, settings) ?? fs.capabilities;
|
|
1962
|
+
if (!fs.confineExtraction || !fs.prepareDirectory || !caps.retainedStagingWrite || !caps.retainedStagingCleanup || !caps.retainedRead || !caps.atomicFileStaging) throw new Error("Python wheel publication requires confined retained atomic staging");
|
|
1963
|
+
const artifact = await openPythonPackageFile(context, resolvePath2(context.cwd, source), maxBytes);
|
|
1964
|
+
if (!artifact) throw new Error("Python wheel publication requires retained source reads");
|
|
1965
|
+
let reading;
|
|
1966
|
+
try {
|
|
1967
|
+
const confined = await fs.confineExtraction([root], settings);
|
|
1968
|
+
const destination = new Proxy(confined, { get(target, key) {
|
|
1969
|
+
const owner = key === "openReadFile" ? fs : target, value = Reflect.get(owner, key);
|
|
1970
|
+
return typeof value === "function" ? value.bind(owner) : value;
|
|
1971
|
+
} });
|
|
1972
|
+
const parent = await destination.stat(root, settings), path = resolvePath2(root, artifact.key);
|
|
1973
|
+
try {
|
|
1974
|
+
await destination.prepareDirectory(path, { parent, expected: null, mode: 448, signal });
|
|
1975
|
+
} catch (error) {
|
|
1976
|
+
if (!(error instanceof FsError3) || !["EEXIST", "EAGAIN"].includes(error.code)) throw error;
|
|
1977
|
+
}
|
|
1978
|
+
if ((await destination.lstat(path, settings)).type !== "directory") throw new Error("Python wheel publication directory changed");
|
|
1979
|
+
const output = resolvePath2(path, filename);
|
|
1980
|
+
const body = async function* () {
|
|
1981
|
+
for (let offset = 0; offset < artifact.size; ) {
|
|
1982
|
+
const bytes = await (reading = artifact.read(offset, 65536));
|
|
1983
|
+
offset += bytes.length;
|
|
1984
|
+
yield bytes;
|
|
1985
|
+
}
|
|
1986
|
+
};
|
|
1987
|
+
const published = await stagePythonPackage({ ...context, fs: destination }, path, body(), maxBytes, () => {
|
|
1988
|
+
}, (key) => {
|
|
1989
|
+
if (key !== artifact.key) throw new Error("Built Python wheel integrity mismatch");
|
|
1990
|
+
}, () => output);
|
|
1991
|
+
if (!published) throw new Error("Python wheel publication requires retained atomic staging");
|
|
1992
|
+
try {
|
|
1993
|
+
return { url: "file://" + output.split("/").map(encodeURIComponent).join("/"), digest: published.key, size: published.size };
|
|
1994
|
+
} finally {
|
|
1995
|
+
await published.close();
|
|
1996
|
+
}
|
|
1997
|
+
} finally {
|
|
1998
|
+
try {
|
|
1999
|
+
await reading?.catch(() => {
|
|
2000
|
+
});
|
|
2001
|
+
} finally {
|
|
2002
|
+
await artifact.close();
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// packages/safe-bash-command-python/src/source-origin.ts
|
|
2008
|
+
import { FsError as FsError4 } from "../../safe-bash-contracts/index.js";
|
|
2009
|
+
import { resolvePath as resolvePath3, dirname, basename as basename2 } from "../../safe-bash-contracts/path.js";
|
|
2010
|
+
function withPythonSourceOrigins(environment, directory, maxBytes) {
|
|
2011
|
+
const origins = /* @__PURE__ */ new Map(), encoder2 = new TextEncoder();
|
|
2012
|
+
return {
|
|
2013
|
+
...environment,
|
|
2014
|
+
async dispatch(operation, args, context) {
|
|
2015
|
+
const result = await environment.dispatch(operation, args, context), session = String(args[0]);
|
|
2016
|
+
if (operation === "package-open") {
|
|
2017
|
+
origins.delete(session);
|
|
2018
|
+
const url = new URL(String(args[1]));
|
|
2019
|
+
if (url.protocol === "file:" && url.hash.startsWith("#python-source=")) {
|
|
2020
|
+
const parent = dirname(decodeURIComponent(url.pathname));
|
|
2021
|
+
if (basename2(parent) !== result.key) return result;
|
|
2022
|
+
let root;
|
|
2023
|
+
try {
|
|
2024
|
+
root = await context.fs.realpath(resolvePath3(context.cwd, directory), { signal: context.signal });
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
if (error instanceof FsError4 && error.code === "ENOENT") return result;
|
|
2027
|
+
throw error;
|
|
2028
|
+
}
|
|
2029
|
+
if (dirname(parent) !== root) return result;
|
|
2030
|
+
const origin = decodeURIComponent(url.hash.slice("#python-source=".length));
|
|
2031
|
+
if (encoder2.encode(origin).length > maxBytes) throw new RangeError("Python source origin exceeds maxMetadataBytes");
|
|
2032
|
+
origins.set(session, { key: result.key, origin });
|
|
2033
|
+
}
|
|
2034
|
+
} else if (operation === "package-retain") {
|
|
2035
|
+
const origin = origins.get(session);
|
|
2036
|
+
origins.delete(session);
|
|
2037
|
+
if (origin !== void 0) return { ...result, metadata: origin.origin === "null" ? {} : { "direct_url.json": origin.origin } };
|
|
2038
|
+
} else if (operation === "package-close" && origins.get(session)?.key === String(args[1])) origins.delete(session);
|
|
2039
|
+
return result;
|
|
2040
|
+
},
|
|
2041
|
+
finish(start) {
|
|
2042
|
+
origins.delete(start.session);
|
|
2043
|
+
return environment.finish(start);
|
|
2044
|
+
},
|
|
2045
|
+
async dispose() {
|
|
2046
|
+
try {
|
|
2047
|
+
await environment.dispose();
|
|
2048
|
+
} finally {
|
|
2049
|
+
origins.clear();
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// packages/safe-bash-command-python/src/source-environment.ts
|
|
2056
|
+
import { compareIdentity as compareIdentity2 } from "@poe-platform/safe-fs/core";
|
|
2057
|
+
import { FsError as FsError5, toByteSource as toByteSource7 } from "../../safe-bash-contracts/index.js";
|
|
2058
|
+
import { resolvePath as resolvePath5 } from "../../safe-bash-contracts/path.js";
|
|
2059
|
+
|
|
2060
|
+
// packages/safe-bash-command-python/src/source-download.ts
|
|
2061
|
+
import { dirname as dirname2, resolvePath as resolvePath4 } from "../../safe-bash-contracts/path.js";
|
|
2062
|
+
import { sha224, sha256, sha384, sha512 } from "@noble/hashes/sha2.js";
|
|
2063
|
+
import { sha1, md5 } from "@noble/hashes/legacy.js";
|
|
2064
|
+
async function* verifiedBytes(source, expected, maxBytes, signal) {
|
|
2065
|
+
const checksum = expected ? { sha1, sha224, sha384, sha256, sha512, md5 }[expected[0]].create() : void 0;
|
|
2066
|
+
let count = 0;
|
|
2067
|
+
try {
|
|
2068
|
+
for await (const bytes of source) {
|
|
2069
|
+
signal.throwIfAborted();
|
|
2070
|
+
if (!(bytes instanceof Uint8Array)) throw new TypeError("Python package body must yield bytes");
|
|
2071
|
+
if (bytes.length > maxBytes - count) throw new Error("Package download exceeds maxDownloadBytes");
|
|
2072
|
+
count += bytes.length;
|
|
2073
|
+
for (let offset = 0; offset < bytes.length; offset += 65536) {
|
|
2074
|
+
signal.throwIfAborted();
|
|
2075
|
+
const chunk = bytes.subarray(offset, offset + 65536);
|
|
2076
|
+
checksum?.update(chunk);
|
|
2077
|
+
yield chunk;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (checksum && Array.from(checksum.digest(), (byte) => byte.toString(16).padStart(2, "0")).join("") !== expected[1]) throw new Error("Python source archive integrity mismatch: " + expected[0]);
|
|
2081
|
+
} finally {
|
|
2082
|
+
checksum?.destroy();
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
async function downloadPythonSourceArchive(url, path, options, context) {
|
|
2086
|
+
const { fs, signal } = context, cacheDirectory = resolvePath4(context.cwd, options.cacheDirectory ?? dirname2(path));
|
|
2087
|
+
const caps = await fs.capabilitiesFor?.(cacheDirectory, { signal }) ?? fs.capabilities;
|
|
2088
|
+
if (!caps.retainedStagingWrite || !caps.retainedStagingCleanup || !caps.retainedRead || !caps.atomicFileStaging || !fs.createStagedFile || !fs.openReadFile || !fs.publishStagedFile || !fs.confineExtraction) throw new Error("Remote Python sources require retained caller storage");
|
|
2089
|
+
const output = await fs.confineExtraction([dirname2(path)], { signal });
|
|
2090
|
+
if (!output.writeStream) throw new Error("Remote Python sources require streaming writes");
|
|
2091
|
+
const expected = pythonPackageUrlHash(url.href), supplied = options.transport, maxBytes = options.maxDownloadBytes ?? Infinity;
|
|
2092
|
+
const transport = supplied && expected ? Object.assign(async (request) => {
|
|
2093
|
+
const response = await supplied(request);
|
|
2094
|
+
return response.status >= 200 && response.status < 300 ? {
|
|
2095
|
+
status: response.status,
|
|
2096
|
+
statusText: response.statusText,
|
|
2097
|
+
headers: response.headers,
|
|
2098
|
+
...response.httpVersion === void 0 ? {} : { httpVersion: response.httpVersion },
|
|
2099
|
+
...response.contentDecoded === void 0 ? {} : { contentDecoded: response.contentDecoded },
|
|
2100
|
+
body: verifiedBytes(response.body, expected, maxBytes, signal),
|
|
2101
|
+
dispose: response.dispose.bind(response)
|
|
2102
|
+
} : response;
|
|
2103
|
+
}, supplied.supportsPrivateNetworkDeny ? { supportsPrivateNetworkDeny: true } : {}) : supplied;
|
|
2104
|
+
const environment = createPythonBuildEnvironment({ ...options, ...transport ? { transport } : {}, cacheDirectory });
|
|
2105
|
+
try {
|
|
2106
|
+
const start = await environment.prepare({ ...context, requirements: [], requirementFiles: [] });
|
|
2107
|
+
try {
|
|
2108
|
+
const address = new URL(url);
|
|
2109
|
+
address.hash = "";
|
|
2110
|
+
const receipt = await environment.dispatch("package-open", [start.session, address.href], context);
|
|
2111
|
+
let consumed = false;
|
|
2112
|
+
const bytes = verifiedBytes((async function* () {
|
|
2113
|
+
for (let offset = 0; offset < receipt.size; ) {
|
|
2114
|
+
const chunk = await environment.dispatch("package-read", [start.session, receipt.key, offset, Math.min(65536, receipt.size - offset)], context);
|
|
2115
|
+
if (!chunk.length) throw new Error("Remote Python source ended early");
|
|
2116
|
+
offset += chunk.length;
|
|
2117
|
+
yield Uint8Array.from(chunk);
|
|
2118
|
+
}
|
|
2119
|
+
consumed = true;
|
|
2120
|
+
})(), expected, maxBytes, signal);
|
|
2121
|
+
try {
|
|
2122
|
+
await output.writeStream(path, bytes, { flag: "wx", mode: 384, signal });
|
|
2123
|
+
if (!consumed) throw new Error("Remote Python source write ended early");
|
|
2124
|
+
} finally {
|
|
2125
|
+
await bytes.return(void 0);
|
|
2126
|
+
}
|
|
2127
|
+
return { url: receipt.url, headers: receipt.headers };
|
|
2128
|
+
} finally {
|
|
2129
|
+
await environment.finish(start);
|
|
2130
|
+
}
|
|
2131
|
+
} finally {
|
|
2132
|
+
await environment.dispose();
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
async function snapshotPythonSourceArchive(source, path, url, maxBytes, context) {
|
|
2136
|
+
const { fs, signal } = context;
|
|
2137
|
+
if (!fs.writeStream) throw new Error("Local Python sources require streaming writes");
|
|
2138
|
+
const file = await openPythonPackageFile(context, source, maxBytes);
|
|
2139
|
+
if (!file) throw new Error("Local Python sources require retained reads");
|
|
2140
|
+
const bytes = verifiedBytes((async function* () {
|
|
2141
|
+
for (let offset = 0; offset < file.size; ) {
|
|
2142
|
+
const chunk = await file.read(offset, Math.min(65536, file.size - offset));
|
|
2143
|
+
offset += chunk.length;
|
|
2144
|
+
yield chunk;
|
|
2145
|
+
}
|
|
2146
|
+
})(), pythonPackageUrlHash(url), maxBytes, signal);
|
|
2147
|
+
let complete = false;
|
|
2148
|
+
try {
|
|
2149
|
+
await fs.writeStream(path, (async function* () {
|
|
2150
|
+
yield* bytes;
|
|
2151
|
+
complete = true;
|
|
2152
|
+
})(), { flag: "wx", mode: 384, signal });
|
|
2153
|
+
if (!complete) throw new Error("Local Python source write ended early");
|
|
2154
|
+
} finally {
|
|
2155
|
+
try {
|
|
2156
|
+
await bytes.return(void 0);
|
|
2157
|
+
} finally {
|
|
2158
|
+
await file.close();
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
// packages/safe-bash-command-python/src/source-environment.ts
|
|
2164
|
+
var serial2 = 0;
|
|
2165
|
+
var archiveSuffix = (path) => [".zip", ".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tbz", ".tar.xz", ".txz"].find((suffix) => path.toLowerCase().endsWith(suffix));
|
|
2166
|
+
var wheelLink = (url) => {
|
|
2167
|
+
let path = url.pathname;
|
|
2168
|
+
while (path.endsWith("/")) path = path.slice(0, -1);
|
|
2169
|
+
const parts = (path.slice(path.lastIndexOf("/") + 1) || url.host).split("%");
|
|
2170
|
+
let name = parts.shift();
|
|
2171
|
+
for (const part of parts) {
|
|
2172
|
+
const hex = part.slice(0, 2).toLowerCase();
|
|
2173
|
+
name += hex.length === 2 && [...hex].every((value) => "0123456789abcdef".includes(value)) ? String.fromCharCode(Number.parseInt(hex, 16)) + part.slice(2) : "%" + part;
|
|
2174
|
+
}
|
|
2175
|
+
name = name.slice(name.lastIndexOf("/") + 1);
|
|
2176
|
+
return name.endsWith(".whl") && name.slice(0, -4).split(".").some(Boolean);
|
|
2177
|
+
};
|
|
2178
|
+
function createPythonSourcePackageEnvironment(options, build) {
|
|
2179
|
+
if (options.prepareRequirements || !build.python.createExecutor || !build.directory) throw new TypeError("Source packages require an asynchronous build executor and caller storage");
|
|
2180
|
+
const prepareRequirement = async (requirement, context, editable = false) => {
|
|
2181
|
+
const { fs, signal } = context, settings = { signal }, requirementLine = requirement.startsWith("-");
|
|
2182
|
+
editable ||= requirementLine;
|
|
2183
|
+
let source = requirement, named = null;
|
|
2184
|
+
let remote;
|
|
2185
|
+
const at = requirement.indexOf("@");
|
|
2186
|
+
let namedSource = false;
|
|
2187
|
+
if (at >= 0 && !requirement.startsWith("file:")) {
|
|
2188
|
+
const tail = requirement.slice(at + 1).trimStart();
|
|
2189
|
+
let end = 0;
|
|
2190
|
+
while (end < tail.length && ![" ", " ", "\r", "\n"].includes(tail[end])) end++;
|
|
2191
|
+
try {
|
|
2192
|
+
const url = new URL(tail.slice(0, end));
|
|
2193
|
+
namedSource = url.protocol === "file:" || ["http:", "https:"].includes(url.protocol) && !wheelLink(url);
|
|
2194
|
+
} catch {
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
const editableExtras = editable && requirement.endsWith("]") && requirement.lastIndexOf("[") > 0;
|
|
2198
|
+
if (namedSource || editableExtras || requirementLine) {
|
|
2199
|
+
if (!context.stdout || !context.stderr || !context.env) throw new TypeError("Source requirement parsing requires command output and environment context");
|
|
2200
|
+
const environment2 = createPythonBuildEnvironment(options);
|
|
2201
|
+
const parsing = { ...context, env: context.env, stdout: context.stdout, stderr: context.stderr, maxBytes: options.maxMetadataBytes ?? Infinity };
|
|
2202
|
+
Reflect.deleteProperty(parsing, "editable");
|
|
2203
|
+
try {
|
|
2204
|
+
named = await createPythonBuildBackend({ ...build.python, environment: environment2 })({ hook: editableExtras || requirementLine ? "read_editable_requirement" : "read_source_requirement", source: requirement, ...requirementLine ? { requirementLine } : {} }, parsing);
|
|
2205
|
+
} finally {
|
|
2206
|
+
await environment2.dispose();
|
|
2207
|
+
}
|
|
2208
|
+
if (named) {
|
|
2209
|
+
if (!named.active) return requirement;
|
|
2210
|
+
source = named.url;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
const originalLink = source.slice(0, 5).toLowerCase() === "file:" || source.startsWith("https://") || source.startsWith("http://") ? source : void 0;
|
|
2214
|
+
let subdirectory;
|
|
2215
|
+
if (source.startsWith("file:") || source.startsWith("https://") || source.startsWith("http://")) {
|
|
2216
|
+
for (let index = 0; index < source.length; index++) if ((source[index] === "#" || source[index] === "&") && source.startsWith("subdirectory=", index + 1)) {
|
|
2217
|
+
const start = index + 1 + "subdirectory=".length, end = source.indexOf("&", start);
|
|
2218
|
+
subdirectory = source.slice(start, end < 0 ? void 0 : end);
|
|
2219
|
+
break;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
if (source.slice(0, 5).toLowerCase() === "file:") {
|
|
2223
|
+
const url = new URL(source);
|
|
2224
|
+
if (url.host && url.host !== "localhost") {
|
|
2225
|
+
if (editable) throw new Error("Editable source requires a local directory");
|
|
2226
|
+
return requirement;
|
|
2227
|
+
}
|
|
2228
|
+
source = decodeURIComponent(url.pathname);
|
|
2229
|
+
} else if (source.startsWith("https://") || source.startsWith("http://")) remote = new URL(source);
|
|
2230
|
+
else if (source.includes("://")) {
|
|
2231
|
+
if (editable) throw new Error("Editable source requires a local directory");
|
|
2232
|
+
return requirement;
|
|
2233
|
+
}
|
|
2234
|
+
if (editable && remote) throw new Error("Editable source requires a local directory");
|
|
2235
|
+
const suffix = archiveSuffix(remote?.pathname ?? source);
|
|
2236
|
+
let archived = !!remote && !wheelLink(remote);
|
|
2237
|
+
if (remote) {
|
|
2238
|
+
if (!archived) return requirement;
|
|
2239
|
+
} else {
|
|
2240
|
+
source = resolvePath5(context.cwd, source);
|
|
2241
|
+
try {
|
|
2242
|
+
const stat = await fs.stat(source, settings);
|
|
2243
|
+
archived = stat.type === "file" && !!suffix;
|
|
2244
|
+
if (editable && stat.type !== "directory") throw new Error("Editable source requires a local directory");
|
|
2245
|
+
if (stat.type !== "directory" && !archived) return requirement;
|
|
2246
|
+
} catch (error) {
|
|
2247
|
+
if (error instanceof FsError5 && error.code === "ENOENT") {
|
|
2248
|
+
if (editable) throw new Error("Editable source requires an existing local directory");
|
|
2249
|
+
return requirement;
|
|
2250
|
+
}
|
|
2251
|
+
throw error;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
const originSource = originalLink ?? source;
|
|
2255
|
+
if (!context.stdout || !context.stderr || !context.env) throw new TypeError("Source package preparation requires command output and environment context");
|
|
2256
|
+
if (!fs.prepareDirectory || !fs.removeTreeConditional || !fs.confineExtraction) throw new Error("Source packages require conditional caller storage");
|
|
2257
|
+
const root = await fs.realpath(resolvePath5(context.cwd, build.directory), settings), parent = await fs.stat(root, settings);
|
|
2258
|
+
const caps = await fs.capabilitiesFor?.(root, settings) ?? fs.capabilities;
|
|
2259
|
+
if (!caps.atomicTreeRemoval) throw new Error("Source packages require conditional tree cleanup");
|
|
2260
|
+
let path = "", owned;
|
|
2261
|
+
for (let attempt = 0; attempt < 16; attempt++) {
|
|
2262
|
+
path = resolvePath5(root, ".python-build-" + ++serial2);
|
|
2263
|
+
try {
|
|
2264
|
+
owned = await fs.prepareDirectory(path, { parent, expected: null, mode: 448, signal });
|
|
2265
|
+
break;
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
if (!(error instanceof FsError5) || !["EEXIST", "EAGAIN"].includes(error.code)) throw error;
|
|
2268
|
+
signal.throwIfAborted();
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
if (!owned) throw new Error("Python build directory names exhausted");
|
|
2272
|
+
const environment = createPythonBuildEnvironment(options);
|
|
2273
|
+
let cleaning;
|
|
2274
|
+
const remove = async () => {
|
|
2275
|
+
const current = await fs.lstat(path);
|
|
2276
|
+
if (compareIdentity2(owned, current) !== "same") throw new Error("Python build directory identity changed");
|
|
2277
|
+
await fs.removeTreeConditional(path, { parent, expected: current });
|
|
2278
|
+
};
|
|
2279
|
+
const cleanup = () => cleaning ??= (async () => {
|
|
2280
|
+
try {
|
|
2281
|
+
await environment.dispose();
|
|
2282
|
+
} finally {
|
|
2283
|
+
await remove();
|
|
2284
|
+
}
|
|
2285
|
+
})();
|
|
2286
|
+
try {
|
|
2287
|
+
const confined = await fs.confineExtraction([path], settings);
|
|
2288
|
+
if (compareIdentity2(owned, await fs.lstat(path, settings)) !== "same") throw new Error("Python build directory identity changed");
|
|
2289
|
+
const staging = new Proxy(confined, { get(target, key) {
|
|
2290
|
+
const owner = ["stat", "lstat", "realpath", "openReadFile", "iterateDirectory", "readlink", "confineExtraction", "removeTreeConditional"].includes(String(key)) ? fs : target, value = Reflect.get(owner, key);
|
|
2291
|
+
return typeof value === "function" ? value.bind(owner) : value;
|
|
2292
|
+
} });
|
|
2293
|
+
const command = { ...context, env: context.env, stdout: context.stdout, stderr: context.stderr, command: "python", args: [], stdin: toByteSource7("") };
|
|
2294
|
+
Reflect.deleteProperty(command, "editable");
|
|
2295
|
+
const configuration = { ...build.python, environment }, hook = createPythonBuildBackend(configuration), hookContext = { ...command, maxBytes: options.maxMetadataBytes ?? Infinity };
|
|
2296
|
+
let prepared;
|
|
2297
|
+
if (archived) {
|
|
2298
|
+
prepared = resolvePath5(path, "source");
|
|
2299
|
+
await confined.mkdir(prepared, settings);
|
|
2300
|
+
if (!build.extractArchive) throw new Error("Source archives require an extraction capability");
|
|
2301
|
+
let metadata;
|
|
2302
|
+
if (remote) {
|
|
2303
|
+
source = resolvePath5(path, "archive");
|
|
2304
|
+
const response = await downloadPythonSourceArchive(remote, source, options, command);
|
|
2305
|
+
const filename2 = await hook({ hook: "read_download_filename", source: remote.href, responseUrl: response.url, headers: response.headers }, hookContext);
|
|
2306
|
+
const headers = new Map(response.headers.map(([key, value]) => [key.toLowerCase(), value]));
|
|
2307
|
+
metadata = { filename: filename2, ...headers.has("content-type") ? { contentType: headers.get("content-type") } : {} };
|
|
2308
|
+
}
|
|
2309
|
+
if (!remote && originalLink && pythonPackageUrlHash(originalLink)) {
|
|
2310
|
+
const snapshot = resolvePath5(path, "archive" + suffix);
|
|
2311
|
+
await snapshotPythonSourceArchive(source, snapshot, originalLink, options.maxDownloadBytes ?? Infinity, { ...command, fs: staging });
|
|
2312
|
+
source = snapshot;
|
|
2313
|
+
}
|
|
2314
|
+
await build.extractArchive(source, prepared, options.maxDownloadBytes ?? Infinity, { ...command, fs: staging }, metadata);
|
|
2315
|
+
} else prepared = editable ? await fs.realpath(source, settings) : (await createPythonSourceSnapshot(source, path, { ...command, fs: staging })).path;
|
|
2316
|
+
if (subdirectory) {
|
|
2317
|
+
const selected = resolvePath5(prepared, subdirectory);
|
|
2318
|
+
if (selected !== prepared && !selected.startsWith(prepared + "/")) throw new Error("Source subdirectory escapes the build tree");
|
|
2319
|
+
const root2 = await fs.realpath(prepared, settings), actual = await fs.realpath(selected, settings);
|
|
2320
|
+
if (actual !== root2 && !actual.startsWith(root2 + "/")) throw new Error("Source subdirectory escapes the build tree");
|
|
2321
|
+
prepared = actual;
|
|
2322
|
+
}
|
|
2323
|
+
const wheelDirectory = resolvePath5(path, "wheels");
|
|
2324
|
+
await confined.mkdir(wheelDirectory, settings);
|
|
2325
|
+
const buildSystem = await hook({ hook: "read_build_system", source: prepared, name: requirement, installation: archived ? "archive" : "directory" }, hookContext);
|
|
2326
|
+
await createPythonBuildDependencies(configuration)({ source: prepared, buildSystem }, hookContext);
|
|
2327
|
+
const filename = await hook(editable ? { hook: "build_legacy_wheel", source: prepared, wheelDirectory, editable: true } : buildSystem ? { hook: "build_wheel", source: prepared, backend: buildSystem.backend, backendPath: buildSystem.backendPath, wheelDirectory } : { hook: "build_legacy_wheel", source: prepared, wheelDirectory }, hookContext);
|
|
2328
|
+
const published = await publishPythonBuildWheel(resolvePath5(wheelDirectory, filename), root, options.maxDownloadBytes ?? Infinity, context);
|
|
2329
|
+
const origin = buildSystem && !editable ? await hook({ hook: "read_source_origin", source: originSource, directory: !archived }, hookContext) : void 0;
|
|
2330
|
+
const url = published.url + "#python-source=" + encodeURIComponent(origin ?? "null");
|
|
2331
|
+
await cleanup();
|
|
2332
|
+
return named ? (named.name || filename.slice(0, filename.indexOf("-"))) + (named.extras.length ? "[" + named.extras.join(",") + "]" : "") + " @ " + url + (named.marker ? " ; " + named.marker : "") : url;
|
|
2333
|
+
} catch (error) {
|
|
2334
|
+
try {
|
|
2335
|
+
await cleanup();
|
|
2336
|
+
} catch (retirement) {
|
|
2337
|
+
if (retirement === error) throw error;
|
|
2338
|
+
throw new AggregateError([error, retirement], "Python source build cleanup failed");
|
|
2339
|
+
}
|
|
2340
|
+
throw error;
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
return withPythonSourceOrigins(createPythonPackageEnvironment({ ...options, async prepareRequirements(requirements, context) {
|
|
2344
|
+
const resolved = [];
|
|
2345
|
+
for (const requirement of /* @__PURE__ */ new Set([...options.editable ?? [], ...context.editable ?? []])) resolved.push(await prepareRequirement(requirement, context, true));
|
|
2346
|
+
for (const requirement of new Set(requirements)) resolved.push(await prepareRequirement(requirement, context));
|
|
2347
|
+
return resolved;
|
|
2348
|
+
} }), build.directory, options.maxMetadataBytes ?? Infinity);
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
// packages/safe-bash-command-python/src/executor.ts
|
|
2352
|
+
var encoder = new TextEncoder();
|
|
2353
|
+
var PythonInputChunkError = class extends RangeError {
|
|
2354
|
+
};
|
|
2355
|
+
function createPythonExecutorCommands(options) {
|
|
2356
|
+
if (!options || typeof options.createWorker === "function" === (typeof options.createExecutor === "function") || options.createWorker !== void 0 && typeof options.createWorker !== "function" || options.createExecutor !== void 0 && typeof options.createExecutor !== "function") throw new PythonFailure("executor-unavailable");
|
|
2357
|
+
if (options.createCapabilities && !options.createExecutor) throw new TypeError("Python host capabilities require an asynchronous executor");
|
|
2358
|
+
if (options.onDiagnostic !== void 0 && typeof options.onDiagnostic !== "function") throw new TypeError("Python onDiagnostic must be a function");
|
|
2359
|
+
if (options.environment && options.provisioning) throw new TypeError("A borrowed Python environment cannot be combined with provisioning options");
|
|
2360
|
+
const maxTransferBytes = options.maxTransferBytes ?? Infinity;
|
|
2361
|
+
const transferChunkBytes = Math.min(maxTransferBytes, 65536);
|
|
2362
|
+
const maxOpenFiles = options.maxOpenFiles;
|
|
2363
|
+
const maxConcurrentWorkers = options.maxConcurrentWorkers ?? Infinity;
|
|
2364
|
+
const maxInputChunkBytes = options.maxInputChunkBytes ?? Infinity;
|
|
2365
|
+
if (maxConcurrentWorkers !== Infinity && (!Number.isSafeInteger(maxConcurrentWorkers) || maxConcurrentWorkers < 1)) throw new RangeError("Invalid Python worker concurrency limit");
|
|
2366
|
+
if (maxInputChunkBytes !== Infinity && (!Number.isSafeInteger(maxInputChunkBytes) || maxInputChunkBytes < 1)) throw new RangeError("Invalid Python input chunk limit");
|
|
2367
|
+
let activeWorkers = 0;
|
|
2368
|
+
for (const size of [maxTransferBytes, maxOpenFiles, options.maxDirectoryEntries]) if (size !== void 0 && size !== Infinity && (!Number.isSafeInteger(size) || size < 1)) throw new RangeError("Invalid Python resource limit");
|
|
2369
|
+
const runtimeMount = options.runtimeMount ?? "/.pyodide-runtime";
|
|
2370
|
+
if (!runtimeMount.startsWith("/") || runtimeMount === "/" || runtimeMount.slice(1).includes("/") || runtimeMount.includes("\0") || runtimeMount.split("/").some((part) => part === ".." || part === ".")) throw new TypeError("Python runtime mount must be an absolute top-level canonical path");
|
|
2371
|
+
const environment = options.environment ?? createPythonPackageEnvironment(options.provisioning);
|
|
2372
|
+
const execute = async (context) => {
|
|
2373
|
+
if (pythonShellDispatchActive(context.executionScope)) {
|
|
2374
|
+
await writeBytes(context.stderr, encoder.encode("python: nested Python execution is unavailable while the parent interpreter is suspended\n"), context.signal);
|
|
2375
|
+
return { exitCode: 1 };
|
|
2376
|
+
}
|
|
2377
|
+
let installation;
|
|
2378
|
+
try {
|
|
2379
|
+
const invocation = parsePythonInvocation(context.args, context.env);
|
|
2380
|
+
installation = invocation.module?.name === "pip" ? parsePythonInstallation(invocation.module.args) : void 0;
|
|
2381
|
+
} catch (error) {
|
|
2382
|
+
if (!(error instanceof PythonInvocationError)) throw error;
|
|
2383
|
+
await writeBytes(context.stderr, encoder.encode("python: " + error.message + "\n"), context.signal);
|
|
2384
|
+
return { exitCode: 2 };
|
|
2385
|
+
}
|
|
2386
|
+
if (installation?.help) {
|
|
2387
|
+
await writeBytes(context.stdout, encoder.encode(pythonInstallationHelp), context.signal);
|
|
2388
|
+
return { exitCode: 0 };
|
|
2389
|
+
}
|
|
2390
|
+
context.signal.throwIfAborted();
|
|
2391
|
+
if (activeWorkers >= maxConcurrentWorkers) {
|
|
2392
|
+
const failure = reportPythonFailure("capacity", void 0, options.onDiagnostic);
|
|
2393
|
+
await writeBytes(context.stderr, encoder.encode("python: " + failure.message + "\n"), context.signal);
|
|
2394
|
+
return { exitCode: 1 };
|
|
2395
|
+
}
|
|
2396
|
+
const controller = new AbortController();
|
|
2397
|
+
const signal = AbortSignal.any([context.signal, controller.signal]);
|
|
2398
|
+
inheritYieldCheckpoint(context.signal, signal);
|
|
2399
|
+
const fileContext = { ...context, descriptorCleanup: "caller" };
|
|
2400
|
+
const metadata = new PythonStatTranslator();
|
|
2401
|
+
const service = new PythonFileSystem(context.fs, {
|
|
2402
|
+
cwd: context.cwd,
|
|
2403
|
+
signal,
|
|
2404
|
+
maxTransferBytes,
|
|
2405
|
+
...maxOpenFiles === void 0 ? {} : { maxOpenFiles },
|
|
2406
|
+
...options.maxDirectoryEntries === void 0 ? {} : { maxDirectoryEntries: options.maxDirectoryEntries },
|
|
2407
|
+
open: (path, settings) => openCommandFile(fileContext, path, settings)
|
|
2408
|
+
});
|
|
2409
|
+
let hostBridge;
|
|
2410
|
+
let stdoutOperation;
|
|
2411
|
+
let stderrOperation;
|
|
2412
|
+
let endpoint;
|
|
2413
|
+
let executor;
|
|
2414
|
+
let packages;
|
|
2415
|
+
let unsubscribe;
|
|
2416
|
+
let admitted = false;
|
|
2417
|
+
let closed = false;
|
|
2418
|
+
let closing;
|
|
2419
|
+
const pending = /* @__PURE__ */ new Set();
|
|
2420
|
+
const input = readBytes(context.stdin, signal)[Symbol.asyncIterator]();
|
|
2421
|
+
let fragment;
|
|
2422
|
+
let offset = 0;
|
|
2423
|
+
let inputBytes = 0;
|
|
2424
|
+
let rejectRun;
|
|
2425
|
+
const close = () => {
|
|
2426
|
+
if (closing) return closing;
|
|
2427
|
+
let finish;
|
|
2428
|
+
let fail;
|
|
2429
|
+
closing = new Promise((resolve, reject) => {
|
|
2430
|
+
finish = resolve;
|
|
2431
|
+
fail = reject;
|
|
2432
|
+
});
|
|
2433
|
+
closed = true;
|
|
2434
|
+
controller.abort(new Error("Python invocation retired"));
|
|
2435
|
+
void (async () => {
|
|
2436
|
+
signal.removeEventListener("abort", aborted);
|
|
2437
|
+
const subscription = Promise.resolve().then(() => unsubscribe?.());
|
|
2438
|
+
const termination = Promise.resolve().then(() => executor ? executor.terminate() : endpoint?.terminate());
|
|
2439
|
+
const filesystemRetirement = termination.finally(() => service.close());
|
|
2440
|
+
const results = await Promise.allSettled([subscription, termination, filesystemRetirement, hostBridge?.close(), stdoutOperation?.close(), stderrOperation?.close(), ...pending]);
|
|
2441
|
+
await input.return?.(void 0);
|
|
2442
|
+
fragment = void 0;
|
|
2443
|
+
if (packages) results.push(...await Promise.allSettled([Promise.resolve().then(() => environment.finish(packages))]));
|
|
2444
|
+
if (results[1]?.status === "fulfilled" && admitted) {
|
|
2445
|
+
activeWorkers--;
|
|
2446
|
+
admitted = false;
|
|
2447
|
+
}
|
|
2448
|
+
for (const result2 of results) if (result2.status === "rejected") throw result2.reason;
|
|
2449
|
+
})().then(() => {
|
|
2450
|
+
if (admitted) {
|
|
2451
|
+
activeWorkers--;
|
|
2452
|
+
admitted = false;
|
|
2453
|
+
}
|
|
2454
|
+
finish();
|
|
2455
|
+
}, (reason) => {
|
|
2456
|
+
fail(reportPythonFailure("cleanup", reason, options.onDiagnostic));
|
|
2457
|
+
});
|
|
2458
|
+
return closing;
|
|
2459
|
+
};
|
|
2460
|
+
const aborted = () => {
|
|
2461
|
+
rejectRun?.(signal.reason);
|
|
2462
|
+
};
|
|
2463
|
+
context.registerCleanup?.(close);
|
|
2464
|
+
let primary;
|
|
2465
|
+
let ready = false;
|
|
2466
|
+
const filesystemDiagnostics = /* @__PURE__ */ new Set();
|
|
2467
|
+
let result = 1;
|
|
2468
|
+
try {
|
|
2469
|
+
signal.throwIfAborted();
|
|
2470
|
+
activeWorkers++;
|
|
2471
|
+
admitted = true;
|
|
2472
|
+
const outputContext = { signal, ...context.registerCleanup ? { registerCleanup: context.registerCleanup } : {} };
|
|
2473
|
+
stdoutOperation = createOutputOperation(outputContext, context.stdout);
|
|
2474
|
+
stderrOperation = createOutputOperation(outputContext, context.stderr);
|
|
2475
|
+
const diagnoseFilesystemFailure = async (operation, error) => {
|
|
2476
|
+
if (typeof error !== "object" || error === null || !("code" in error) || error.code !== "ENOTSUP") return;
|
|
2477
|
+
const category = operation === "open" ? "filesystem-open" : operation === "read" ? "filesystem-read" : operation === "write" || operation === "ftruncate" ? "filesystem-write" : operation === "readdir" ? "filesystem-directory" : "filesystem-operation";
|
|
2478
|
+
if (!filesystemDiagnostics.has(category)) {
|
|
2479
|
+
filesystemDiagnostics.add(category);
|
|
2480
|
+
const failure = reportPythonFailure(category, error, options.onDiagnostic);
|
|
2481
|
+
await writeBytes(stderrOperation.output, encoder.encode("python: " + failure.message + "\n"), signal);
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
const dispatch = async (request) => {
|
|
2485
|
+
if (request.op === "close") return service.dispatch(request);
|
|
2486
|
+
signal.throwIfAborted();
|
|
2487
|
+
if (request.op.startsWith("package-")) return environment.dispatch(request.op, request.args, { fs: context.fs, cwd: context.cwd, signal });
|
|
2488
|
+
if (request.op === "stdin") {
|
|
2489
|
+
const length = request.args[0];
|
|
2490
|
+
if (!Number.isSafeInteger(length) || length < 1 || length > maxTransferBytes) throw new RangeError("Invalid Python input request");
|
|
2491
|
+
let pulls = 0;
|
|
2492
|
+
while (!fragment || offset === fragment.length) {
|
|
2493
|
+
if (++pulls % 128 === 0) await yieldTurn2(signal);
|
|
2494
|
+
const next = await input.next();
|
|
2495
|
+
if (next.done) return [];
|
|
2496
|
+
inputBytes += next.value.length;
|
|
2497
|
+
context.inputBudget?.check(inputBytes);
|
|
2498
|
+
if (next.value.length > maxInputChunkBytes) throw new PythonInputChunkError("Python input chunk exceeds maxInputChunkBytes");
|
|
2499
|
+
fragment = Uint8Array.from(next.value);
|
|
2500
|
+
offset = 0;
|
|
2501
|
+
}
|
|
2502
|
+
const end = Math.min(fragment.length, offset + length);
|
|
2503
|
+
const bytes = Array.from(fragment.subarray(offset, end));
|
|
2504
|
+
offset = end;
|
|
2505
|
+
return bytes;
|
|
2506
|
+
}
|
|
2507
|
+
if (request.op === "stdout" || request.op === "stderr") {
|
|
2508
|
+
const bytes = request.args[0];
|
|
2509
|
+
if (!Array.isArray(bytes) || bytes.length > maxTransferBytes || bytes.some((value2) => !Number.isInteger(value2) || value2 < 0 || value2 > 255)) throw new TypeError("Invalid Python output bytes");
|
|
2510
|
+
const operation = request.op === "stdout" ? stdoutOperation : stderrOperation;
|
|
2511
|
+
await writeBytes(operation.output, Uint8Array.from(bytes), signal);
|
|
2512
|
+
return bytes.length;
|
|
2513
|
+
}
|
|
2514
|
+
const value = await service.dispatch(request);
|
|
2515
|
+
return request.op === "stat" || request.op === "lstat" || request.op === "fstat" ? metadata.translate(value) : value;
|
|
2516
|
+
};
|
|
2517
|
+
if (options.createCapabilities) {
|
|
2518
|
+
const capabilityInput = { async *[Symbol.asyncIterator]() {
|
|
2519
|
+
while (true) {
|
|
2520
|
+
const bytes = await dispatch({ op: "stdin", args: [transferChunkBytes] });
|
|
2521
|
+
if (!bytes.length) break;
|
|
2522
|
+
yield Uint8Array.from(bytes);
|
|
2523
|
+
}
|
|
2524
|
+
} };
|
|
2525
|
+
hostBridge = createPythonHostBridge(options.createCapabilities({ ...context, stdin: capabilityInput, signal }), { ...options.capabilityLimits, signal });
|
|
2526
|
+
}
|
|
2527
|
+
options.onProgress?.({ phase: "initializing", command: context.command });
|
|
2528
|
+
const preparation = environment.prepare({
|
|
2529
|
+
...context,
|
|
2530
|
+
signal,
|
|
2531
|
+
...installation?.controls,
|
|
2532
|
+
...installation?.uninstall ? { uninstall: { packages: installation.packages, yes: !!installation.yes } } : {},
|
|
2533
|
+
requirements: [...options.packages ?? [], ...options.packageProfile ? pythonDocumentPackages : [], ...installation?.uninstall ? [] : installation?.packages ?? []],
|
|
2534
|
+
requirementFiles: [...options.requirements ?? [], ...installation?.requirements ?? []]
|
|
2535
|
+
}).then((value) => {
|
|
2536
|
+
packages = value;
|
|
2537
|
+
});
|
|
2538
|
+
pending.add(preparation);
|
|
2539
|
+
try {
|
|
2540
|
+
await preparation;
|
|
2541
|
+
} finally {
|
|
2542
|
+
pending.delete(preparation);
|
|
2543
|
+
}
|
|
2544
|
+
signal.throwIfAborted();
|
|
2545
|
+
if (options.createExecutor) {
|
|
2546
|
+
try {
|
|
2547
|
+
executor = options.createExecutor();
|
|
2548
|
+
} catch (reason) {
|
|
2549
|
+
throw reportPythonFailure(reason instanceof PythonFailure ? reason.category : "startup", reason, options.onDiagnostic);
|
|
2550
|
+
}
|
|
2551
|
+
if (!executor || typeof executor.run !== "function" || typeof executor.terminate !== "function") throw reportPythonFailure("executor-unavailable", void 0, options.onDiagnostic);
|
|
2552
|
+
let requesting = false;
|
|
2553
|
+
let running = true;
|
|
2554
|
+
const start = {
|
|
2555
|
+
invocation: { command: context.command, args: [...context.args], cwd: context.cwd, env: { ...context.env } },
|
|
2556
|
+
...hostBridge ? { host: hostBridge } : {},
|
|
2557
|
+
runtimeMount,
|
|
2558
|
+
maxTransferBytes,
|
|
2559
|
+
signal,
|
|
2560
|
+
...packages ? { packages } : {},
|
|
2561
|
+
installOnly: !!installation,
|
|
2562
|
+
onReady() {
|
|
2563
|
+
if (closed || !running || signal.aborted || ready) return;
|
|
2564
|
+
ready = true;
|
|
2565
|
+
try {
|
|
2566
|
+
options.onProgress?.({ phase: "ready", command: context.command });
|
|
2567
|
+
} catch (reason) {
|
|
2568
|
+
running = false;
|
|
2569
|
+
rejectRun?.(reason);
|
|
2570
|
+
throw reason;
|
|
2571
|
+
}
|
|
2572
|
+
},
|
|
2573
|
+
async dispatch(request) {
|
|
2574
|
+
if (!request || typeof request.op !== "string" || !Array.isArray(request.args)) throw reportPythonFailure("transport-unavailable", void 0, options.onDiagnostic);
|
|
2575
|
+
const releasing = request.op === "close";
|
|
2576
|
+
if (!releasing) signal.throwIfAborted();
|
|
2577
|
+
if (closed && !releasing || !running || requesting) throw reportPythonFailure("transport-unavailable", void 0, options.onDiagnostic);
|
|
2578
|
+
requesting = true;
|
|
2579
|
+
const operation = Promise.resolve().then(() => dispatch(request)).catch(async (error) => {
|
|
2580
|
+
if (releasing) throw error;
|
|
2581
|
+
signal.throwIfAborted();
|
|
2582
|
+
if (error instanceof PythonInputChunkError) rejectRun?.(error);
|
|
2583
|
+
if (request.op.startsWith("package-")) throw Object.assign(reportPythonFailure("runtime-assets", error, options.onDiagnostic), { code: "EPACKAGE" });
|
|
2584
|
+
await diagnoseFilesystemFailure(request.op, error);
|
|
2585
|
+
throw error;
|
|
2586
|
+
});
|
|
2587
|
+
const tracked2 = operation.then(() => {
|
|
2588
|
+
}, () => {
|
|
2589
|
+
});
|
|
2590
|
+
pending.add(tracked2);
|
|
2591
|
+
try {
|
|
2592
|
+
return await operation;
|
|
2593
|
+
} finally {
|
|
2594
|
+
requesting = false;
|
|
2595
|
+
pending.delete(tracked2);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
};
|
|
2599
|
+
const execution = Promise.resolve().then(() => {
|
|
2600
|
+
signal.throwIfAborted();
|
|
2601
|
+
return executor.run(start);
|
|
2602
|
+
}).catch((reason) => {
|
|
2603
|
+
running = false;
|
|
2604
|
+
if (signal.aborted) throw signal.reason;
|
|
2605
|
+
throw reportPythonFailure(reason instanceof PythonFailure ? reason.category : ready ? "runtime" : "startup", reason, options.onDiagnostic);
|
|
2606
|
+
}).then((status) => {
|
|
2607
|
+
running = false;
|
|
2608
|
+
if (requesting) throw reportPythonFailure("transport-unavailable", void 0, options.onDiagnostic);
|
|
2609
|
+
try {
|
|
2610
|
+
return validateExitCode(status);
|
|
2611
|
+
} catch (reason) {
|
|
2612
|
+
throw reportPythonFailure("transport-unavailable", reason, options.onDiagnostic);
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
const tracked = execution.then(() => {
|
|
2616
|
+
}, () => {
|
|
2617
|
+
});
|
|
2618
|
+
pending.add(tracked);
|
|
2619
|
+
void tracked.then(() => pending.delete(tracked));
|
|
2620
|
+
result = await new Promise((resolve, reject) => {
|
|
2621
|
+
rejectRun = reject;
|
|
2622
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
2623
|
+
if (signal.aborted) aborted();
|
|
2624
|
+
execution.then(resolve, reject);
|
|
2625
|
+
});
|
|
2626
|
+
} else {
|
|
2627
|
+
let shared;
|
|
2628
|
+
try {
|
|
2629
|
+
shared = new SharedArrayBuffer(8 + transferChunkBytes * 6 + 65536);
|
|
2630
|
+
} catch (reason) {
|
|
2631
|
+
throw reportPythonFailure("transport-unavailable", reason, options.onDiagnostic);
|
|
2632
|
+
}
|
|
2633
|
+
const control = new Int32Array(shared, 0, 2);
|
|
2634
|
+
const payload = new Uint8Array(shared, 8);
|
|
2635
|
+
const reply = (value, status) => {
|
|
2636
|
+
if (closed) return;
|
|
2637
|
+
let bytes;
|
|
2638
|
+
try {
|
|
2639
|
+
bytes = encodePythonReply(value, payload.length);
|
|
2640
|
+
} catch (error) {
|
|
2641
|
+
if (typeof error !== "object" || error === null || !("code" in error) || error.code !== "EFBIG") throw error;
|
|
2642
|
+
bytes = encodePythonReply({ code: "EFBIG" }, payload.length);
|
|
2643
|
+
status = 2;
|
|
2644
|
+
}
|
|
2645
|
+
payload.set(bytes);
|
|
2646
|
+
try {
|
|
2647
|
+
Atomics.store(control, 1, bytes.length);
|
|
2648
|
+
Atomics.store(control, 0, status);
|
|
2649
|
+
Atomics.notify(control, 0);
|
|
2650
|
+
} catch (reason) {
|
|
2651
|
+
throw reportPythonFailure("transport-unavailable", reason, options.onDiagnostic);
|
|
2652
|
+
}
|
|
2653
|
+
};
|
|
2654
|
+
try {
|
|
2655
|
+
endpoint = options.createWorker();
|
|
2656
|
+
} catch (reason) {
|
|
2657
|
+
throw reportPythonFailure(reason instanceof PythonFailure ? reason.category : "startup", reason, options.onDiagnostic);
|
|
2658
|
+
}
|
|
2659
|
+
signal.throwIfAborted();
|
|
2660
|
+
result = await new Promise((resolve, reject) => {
|
|
2661
|
+
let settled = false;
|
|
2662
|
+
const fail = (reason) => {
|
|
2663
|
+
settled = true;
|
|
2664
|
+
reject(reason);
|
|
2665
|
+
};
|
|
2666
|
+
rejectRun = fail;
|
|
2667
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
2668
|
+
const transportFailure = (reason) => {
|
|
2669
|
+
fail(reportPythonFailure("transport-unavailable", reason, options.onDiagnostic));
|
|
2670
|
+
};
|
|
2671
|
+
try {
|
|
2672
|
+
unsubscribe = endpoint.subscribe((value) => {
|
|
2673
|
+
if (closed || settled) return;
|
|
2674
|
+
if (typeof value !== "object" || value === null) {
|
|
2675
|
+
transportFailure(new TypeError("Invalid Python worker message"));
|
|
2676
|
+
return;
|
|
2677
|
+
}
|
|
2678
|
+
const message = value;
|
|
2679
|
+
if (message.type === "ready") {
|
|
2680
|
+
ready = true;
|
|
2681
|
+
try {
|
|
2682
|
+
options.onProgress?.({ phase: "ready", command: context.command });
|
|
2683
|
+
} catch (error) {
|
|
2684
|
+
fail(error);
|
|
2685
|
+
}
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
if (message.type === "exit") {
|
|
2689
|
+
if (pending.size) {
|
|
2690
|
+
transportFailure(new TypeError("Python worker exited with an outstanding request"));
|
|
2691
|
+
return;
|
|
2692
|
+
}
|
|
2693
|
+
try {
|
|
2694
|
+
const status = validateExitCode(message.exitCode);
|
|
2695
|
+
settled = true;
|
|
2696
|
+
resolve(status);
|
|
2697
|
+
} catch (error) {
|
|
2698
|
+
transportFailure(error);
|
|
2699
|
+
}
|
|
2700
|
+
return;
|
|
2701
|
+
}
|
|
2702
|
+
if (message.type === "error") {
|
|
2703
|
+
fail(reportPythonFailure(isPythonFailureCategory(message.category) ? message.category : ready ? "runtime" : "startup", message.message, options.onDiagnostic));
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2706
|
+
if (typeof message.op !== "string" || !Array.isArray(message.args) || pending.size) {
|
|
2707
|
+
transportFailure(new TypeError("Invalid concurrent Python worker request"));
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
const work = dispatch({ op: message.op, args: message.args }).then(
|
|
2711
|
+
(value2) => {
|
|
2712
|
+
pending.delete(work);
|
|
2713
|
+
reply(value2, 1);
|
|
2714
|
+
},
|
|
2715
|
+
async (error) => {
|
|
2716
|
+
if (signal.aborted) {
|
|
2717
|
+
pending.delete(work);
|
|
2718
|
+
fail(signal.reason);
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
if (typeof message.op === "string" && message.op.startsWith("package-")) {
|
|
2722
|
+
const failure = reportPythonFailure("runtime-assets", error, options.onDiagnostic);
|
|
2723
|
+
pending.delete(work);
|
|
2724
|
+
reply({ code: "EPACKAGE", message: failure.message }, 2);
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
if (typeof error !== "object" || error === null || !("code" in error) || typeof error.code !== "string") {
|
|
2728
|
+
pending.delete(work);
|
|
2729
|
+
fail(error);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
await diagnoseFilesystemFailure(message.op, error);
|
|
2733
|
+
pending.delete(work);
|
|
2734
|
+
reply({ code: error.code }, 2);
|
|
2735
|
+
}
|
|
2736
|
+
);
|
|
2737
|
+
pending.add(work);
|
|
2738
|
+
void work.catch((reason) => {
|
|
2739
|
+
pending.delete(work);
|
|
2740
|
+
fail(reason);
|
|
2741
|
+
});
|
|
2742
|
+
}, transportFailure);
|
|
2743
|
+
} catch (reason) {
|
|
2744
|
+
transportFailure(reason);
|
|
2745
|
+
}
|
|
2746
|
+
if (settled) return;
|
|
2747
|
+
const start = { type: "start", shared, invocation: { command: context.command, args: [...context.args], cwd: context.cwd, env: { ...context.env } }, runtimeMount, maxTransferBytes: transferChunkBytes, ...packages ? { packages } : {}, installOnly: !!installation };
|
|
2748
|
+
signal.throwIfAborted();
|
|
2749
|
+
try {
|
|
2750
|
+
endpoint.postMessage(start);
|
|
2751
|
+
} catch (reason) {
|
|
2752
|
+
transportFailure(reason);
|
|
2753
|
+
}
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
} catch (reason) {
|
|
2757
|
+
if (!signal.aborted && (reason instanceof PythonInputChunkError || reason instanceof PythonInstallationError)) {
|
|
2758
|
+
try {
|
|
2759
|
+
await writeBytes(stderrOperation.output, encoder.encode("python: " + reason.message + "\n"), signal);
|
|
2760
|
+
} catch (error) {
|
|
2761
|
+
primary = { reason: error };
|
|
2762
|
+
}
|
|
2763
|
+
} else if (!signal.aborted && typeof reason === "object" && reason !== null && "code" in reason && reason.code === "EPACKAGE") {
|
|
2764
|
+
primary = { reason: reportPythonFailure("runtime-assets", reason, options.onDiagnostic) };
|
|
2765
|
+
} else primary = { reason };
|
|
2766
|
+
}
|
|
2767
|
+
try {
|
|
2768
|
+
await close();
|
|
2769
|
+
} catch (reason) {
|
|
2770
|
+
primary ??= { reason };
|
|
2771
|
+
}
|
|
2772
|
+
try {
|
|
2773
|
+
options.onProgress?.({ phase: "finished", command: context.command });
|
|
2774
|
+
} catch (reason) {
|
|
2775
|
+
primary ??= { reason };
|
|
2776
|
+
}
|
|
2777
|
+
context.signal.throwIfAborted();
|
|
2778
|
+
if (primary) {
|
|
2779
|
+
if (!(primary.reason instanceof PythonFailure)) throw primary.reason;
|
|
2780
|
+
const failure = primary.reason;
|
|
2781
|
+
await writeBytes(context.stderr, encoder.encode("python: " + failure.message + "\n"), context.signal);
|
|
2782
|
+
return { exitCode: 1 };
|
|
2783
|
+
}
|
|
2784
|
+
return { exitCode: result };
|
|
2785
|
+
};
|
|
2786
|
+
return ["python", "python3"].map((name) => ({ name, description: "Python with an explicit interpreter executor and canonical filesystem", execute }));
|
|
2787
|
+
}
|
|
2788
|
+
function pythonExecutorCommands(options) {
|
|
2789
|
+
if (!options || typeof options.createWorker === "function" === (typeof options.createExecutor === "function")) throw new PythonFailure("executor-unavailable");
|
|
2790
|
+
if (options?.environment && options.provisioning) throw new TypeError("A borrowed Python environment cannot be combined with provisioning options");
|
|
2791
|
+
const { provisioning, ...configuration } = options;
|
|
2792
|
+
const environment = options.environment ?? createPythonPackageEnvironment(provisioning);
|
|
2793
|
+
const commands = createPythonExecutorCommands({ ...configuration, environment });
|
|
2794
|
+
return { name: "python-commands", ...options.environment ? {} : { dispose: environment.dispose }, setup(host) {
|
|
2795
|
+
if (!options.replace) {
|
|
2796
|
+
for (const command of commands) if (host.commands.has(command.name)) throw new Error(`Command already registered: ${command.name}`);
|
|
2797
|
+
}
|
|
2798
|
+
for (const command of commands) host.commands.register(command, { replace: options.replace ?? false });
|
|
2799
|
+
} };
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
export {
|
|
2803
|
+
createPythonLlmPackageManager,
|
|
2804
|
+
createPythonLlmTemplateLoader,
|
|
2805
|
+
createPythonLlmFragmentLoader,
|
|
2806
|
+
createPythonLlmLoaderProvider,
|
|
2807
|
+
createPythonLlmLoaderDiscovery,
|
|
2808
|
+
createPythonLlmToolLoader,
|
|
2809
|
+
createPythonBuildEnvironment,
|
|
2810
|
+
createPythonBuildBackend,
|
|
2811
|
+
createPythonBuildDependencies,
|
|
2812
|
+
createPythonSourceSnapshot,
|
|
2813
|
+
pythonLlmPackages,
|
|
2814
|
+
installPythonLlmPackages,
|
|
2815
|
+
publishPythonBuildWheel,
|
|
2816
|
+
createPythonSourcePackageEnvironment,
|
|
2817
|
+
createPythonExecutorCommands,
|
|
2818
|
+
pythonExecutorCommands
|
|
2819
|
+
};
|