@poe-platform/safe-bash 0.1.630 → 0.1.631
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/office-package/compression.d.ts +19 -0
- package/dist/office-package/compression.js +186 -0
- package/dist/office-package/runtime.d.ts +11 -0
- package/dist/office-package/runtime.js +28 -0
- package/dist/safe-bash/chunks/{bz2-B2RXT4EX.js → bz2-ACU4EOVE.js} +2 -2
- package/dist/safe-bash/chunks/chunk-2E4KBFO6.js +239 -0
- package/dist/safe-bash/chunks/chunk-2E4KBFO6.js.map +7 -0
- package/dist/safe-bash/chunks/chunk-3E5M2WEL.js +33 -0
- package/dist/safe-bash/chunks/chunk-3E5M2WEL.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-KQ6J7JHQ.js → chunk-3LD67SER.js} +12 -10
- package/dist/safe-bash/chunks/{chunk-KQ6J7JHQ.js.map → chunk-3LD67SER.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-6LSSEOG7.js → chunk-6HNK7Q66.js} +11 -7
- package/dist/safe-bash/chunks/{chunk-6LSSEOG7.js.map → chunk-6HNK7Q66.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-AD7UAP5B.js → chunk-7EEWZY5S.js} +10 -8
- package/dist/safe-bash/chunks/{chunk-AD7UAP5B.js.map → chunk-7EEWZY5S.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-RNKTWFEM.js → chunk-CDBQXRWL.js} +3 -3
- package/dist/safe-bash/chunks/{chunk-UYX72YHY.js → chunk-CHHAB5SS.js} +10 -74
- package/dist/safe-bash/chunks/{chunk-UYX72YHY.js.map → chunk-CHHAB5SS.js.map} +3 -3
- package/dist/safe-bash/chunks/{chunk-6YSBXTTJ.js → chunk-EI4X5ETX.js} +12 -10
- package/dist/safe-bash/chunks/{chunk-6YSBXTTJ.js.map → chunk-EI4X5ETX.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-KZ7QCYNT.js → chunk-GIBLUW2U.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-KZ7QCYNT.js.map → chunk-GIBLUW2U.js.map} +1 -1
- package/dist/safe-bash/chunks/chunk-GPV7ZFYS.js +1528 -0
- package/dist/safe-bash/chunks/chunk-GPV7ZFYS.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-N5CYTGBN.js → chunk-H3E67KTZ.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-N5CYTGBN.js.map → chunk-H3E67KTZ.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-AFH57PPL.js → chunk-IEKESMO4.js} +3 -3
- package/dist/safe-bash/chunks/{chunk-DPJWEGIX.js → chunk-JLBIUYT3.js} +2 -2
- package/dist/safe-bash/chunks/chunk-JZDO5MC3.js +77 -0
- package/dist/safe-bash/chunks/chunk-JZDO5MC3.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-GFZKQIPL.js → chunk-LA6BX562.js} +8 -6
- package/dist/safe-bash/chunks/{chunk-GFZKQIPL.js.map → chunk-LA6BX562.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-PVJJFCGA.js → chunk-M3GPCYSP.js} +2 -2
- package/dist/safe-bash/chunks/{chunk-FWDMJTQO.js → chunk-NDCZ3UBP.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-FWDMJTQO.js.map → chunk-NDCZ3UBP.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-2KL6W6NB.js → chunk-O2EVBRKF.js} +1 -2
- package/dist/safe-bash/chunks/chunk-OFT77XIB.js +389 -0
- package/dist/safe-bash/chunks/chunk-OFT77XIB.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-PCLOK32J.js → chunk-PC6BEIAF.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-PCLOK32J.js.map → chunk-PC6BEIAF.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-3M5F7LD5.js → chunk-QWKP7ZU7.js} +13 -9
- package/dist/safe-bash/chunks/{chunk-3M5F7LD5.js.map → chunk-QWKP7ZU7.js.map} +1 -1
- package/dist/safe-bash/chunks/chunk-THXJ6CLF.js +53 -0
- package/dist/safe-bash/chunks/chunk-THXJ6CLF.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-K7W6Q6FN.js → chunk-V73A742N.js} +58 -16
- package/dist/safe-bash/chunks/chunk-V73A742N.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-4BYUV5PA.js → chunk-VN3ZGME3.js} +12 -8
- package/dist/safe-bash/chunks/{chunk-4BYUV5PA.js.map → chunk-VN3ZGME3.js.map} +1 -1
- package/dist/safe-bash/chunks/{chunk-3BZ5NQ73.js → chunk-WECHUK3Y.js} +369 -36
- package/dist/safe-bash/chunks/chunk-WECHUK3Y.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-X55AMTNM.js → chunk-WIOEUR7D.js} +14 -10
- package/dist/safe-bash/chunks/{chunk-X55AMTNM.js.map → chunk-WIOEUR7D.js.map} +1 -1
- package/dist/safe-bash/chunks/chunk-WPIOO4NX.js +443 -0
- package/dist/safe-bash/chunks/chunk-WPIOO4NX.js.map +7 -0
- package/dist/safe-bash/chunks/{chunk-C7QNJ3D4.js → chunk-WYTI64IU.js} +2 -2
- package/dist/safe-bash/chunks/{xz-AVXFDXW4.js → xz-U4QDTPZL.js} +2 -2
- package/dist/safe-bash/chunks/{zstd-R6RPHJYO.js → zstd-OG5RPYXO.js} +2 -2
- package/dist/safe-bash/commands/archive/zip/crc.d.ts +2 -0
- package/dist/safe-bash/commands/archive/zip/crc.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crc.js +7 -0
- package/dist/safe-bash/commands/archive/zip/crc.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts +4 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crypto.js +34 -0
- package/dist/safe-bash/commands/archive/zip/crypto.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/help.d.ts +3 -0
- package/dist/safe-bash/commands/archive/zip/help.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/help.js +87 -0
- package/dist/safe-bash/commands/archive/zip/help.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/move.d.ts +12 -0
- package/dist/safe-bash/commands/archive/zip/move.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/move.js +60 -0
- package/dist/safe-bash/commands/archive/zip/move.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/options.js +5 -5
- package/dist/safe-bash/commands/archive/zip/options.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.d.ts +1 -1
- package/dist/safe-bash/commands/archive/zip-format.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.js +14 -17
- package/dist/safe-bash/commands/archive/zip-format.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip.js +45 -15
- package/dist/safe-bash/commands/archive/zip.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts +2 -0
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.js +4 -0
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec.d.ts +4 -24
- package/dist/safe-bash/commands/bytes/compression/codec.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec.js +12 -168
- package/dist/safe-bash/commands/bytes/compression/codec.js.map +1 -1
- package/dist/safe-bash/commands/cmp.js +1 -1
- package/dist/safe-bash/commands/cmp.js.map +1 -1
- package/dist/safe-bash/commands/csplit/index.browser.js +10 -7
- package/dist/safe-bash/commands/docx/index.browser.js +54 -0
- package/dist/safe-bash/commands/docx/index.browser.js.map +7 -0
- package/dist/safe-bash/commands/docx/index.d.ts +25 -0
- package/dist/safe-bash/commands/docx/index.d.ts.map +1 -0
- package/dist/safe-bash/commands/docx/index.js +42 -0
- package/dist/safe-bash/commands/docx/index.js.map +1 -0
- package/dist/safe-bash/commands/docx/io.d.ts +15 -0
- package/dist/safe-bash/commands/docx/io.d.ts.map +1 -0
- package/dist/safe-bash/commands/docx/io.js +16 -0
- package/dist/safe-bash/commands/docx/io.js.map +1 -0
- package/dist/safe-bash/commands/factor/index.browser.js +7 -5
- package/dist/safe-bash/commands/filesystem.d.ts.map +1 -1
- package/dist/safe-bash/commands/filesystem.js +9 -1
- package/dist/safe-bash/commands/filesystem.js.map +1 -1
- package/dist/safe-bash/commands/getopt/index.browser.js +7 -5
- package/dist/safe-bash/commands/hexdump/index.browser.js +8 -6
- package/dist/safe-bash/commands/iconv/index.browser.js +8 -6
- package/dist/safe-bash/commands/line-endings/index.browser.js +10 -7
- package/dist/safe-bash/commands/llm/index.browser.js +10 -8
- package/dist/safe-bash/commands/llm/providers/index.browser.js +3 -3
- package/dist/safe-bash/commands/network/index.browser.js +11 -8
- package/dist/safe-bash/commands/node/index.browser.js +8 -6
- package/dist/safe-bash/commands/op/index.browser.js +3 -3
- package/dist/safe-bash/commands/op/index.js +0 -1
- package/dist/safe-bash/commands/op/index.js.map +2 -2
- package/dist/safe-bash/commands/pptx/index.d.ts +29 -0
- package/dist/safe-bash/commands/pptx/index.d.ts.map +1 -0
- package/dist/safe-bash/commands/pptx/index.js +153 -0
- package/dist/safe-bash/commands/pptx/index.js.map +1 -0
- package/dist/safe-bash/commands/pr/index.browser.js +8 -6
- package/dist/safe-bash/commands/predicates.js +1 -1
- package/dist/safe-bash/commands/python/execution.d.ts +3 -0
- package/dist/safe-bash/commands/python/execution.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/execution.js +193 -0
- package/dist/safe-bash/commands/python/execution.js.map +1 -0
- package/dist/safe-bash/commands/python/index.browser.js +21 -0
- package/dist/safe-bash/commands/python/index.browser.js.map +7 -0
- package/dist/safe-bash/commands/python/index.d.ts +49 -0
- package/dist/safe-bash/commands/python/index.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/index.js +317 -0
- package/dist/safe-bash/commands/python/index.js.map +1 -0
- package/dist/safe-bash/commands/python/installation.d.ts +9 -0
- package/dist/safe-bash/commands/python/installation.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/installation.js +80 -0
- package/dist/safe-bash/commands/python/installation.js.map +1 -0
- package/dist/safe-bash/commands/python/invocation.d.ts +10 -0
- package/dist/safe-bash/commands/python/invocation.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/invocation.js +81 -0
- package/dist/safe-bash/commands/python/invocation.js.map +1 -0
- package/dist/safe-bash/commands/python/node.d.ts +12 -0
- package/dist/safe-bash/commands/python/node.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/node.js +33 -0
- package/dist/safe-bash/commands/python/node.js.map +1 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.d.ts +5 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.js +179 -0
- package/dist/safe-bash/commands/python/provisioning-runtime.js.map +1 -0
- package/dist/safe-bash/commands/python/provisioning.d.ts +49 -0
- package/dist/safe-bash/commands/python/provisioning.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/provisioning.js +320 -0
- package/dist/safe-bash/commands/python/provisioning.js.map +1 -0
- package/dist/safe-bash/commands/python/reply.d.ts +3 -0
- package/dist/safe-bash/commands/python/reply.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/reply.js +103 -0
- package/dist/safe-bash/commands/python/reply.js.map +1 -0
- package/dist/safe-bash/commands/python/worker.browser.js +736 -0
- package/dist/safe-bash/commands/python/worker.browser.js.map +7 -0
- package/dist/safe-bash/commands/python/worker.d.ts +47 -0
- package/dist/safe-bash/commands/python/worker.d.ts.map +1 -0
- package/dist/safe-bash/commands/python/worker.js +373 -0
- package/dist/safe-bash/commands/python/worker.js.map +1 -0
- package/dist/safe-bash/commands/tsort/index.browser.js +8 -6
- package/dist/safe-bash/commands/xml/index.browser.js +9 -7
- package/dist/safe-bash/commands/yq/index.browser.js +10 -8
- package/dist/safe-bash/commands/yq/index.d.ts.map +1 -1
- package/dist/safe-bash/commands/yq/index.js +32 -4
- package/dist/safe-bash/commands/yq/index.js.map +1 -1
- package/dist/safe-bash/commands/yq/parser.d.ts.map +1 -1
- package/dist/safe-bash/commands/yq/parser.js +21 -5
- package/dist/safe-bash/commands/yq/parser.js.map +1 -1
- package/dist/safe-bash/contracts/command.d.ts +2 -0
- package/dist/safe-bash/contracts/command.d.ts.map +1 -1
- package/dist/safe-bash/contracts/command.js +6 -2
- package/dist/safe-bash/contracts/command.js.map +1 -1
- package/dist/safe-bash/contracts/filesystem-descriptor.d.ts +13 -0
- package/dist/safe-bash/contracts/filesystem-descriptor.d.ts.map +1 -0
- package/dist/safe-bash/contracts/filesystem-descriptor.js +189 -0
- package/dist/safe-bash/contracts/filesystem-descriptor.js.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.d.ts +15 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.d.ts.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.js +69 -0
- package/dist/safe-bash/contracts/filesystem-output-budget.js.map +1 -0
- package/dist/safe-bash/contracts/filesystem-output.d.ts +11 -5
- package/dist/safe-bash/contracts/filesystem-output.d.ts.map +1 -1
- package/dist/safe-bash/contracts/filesystem-output.js +229 -20
- package/dist/safe-bash/contracts/filesystem-output.js.map +1 -1
- package/dist/safe-bash/contracts/io.d.ts +29 -0
- package/dist/safe-bash/contracts/io.d.ts.map +1 -1
- package/dist/safe-bash/contracts/io.js +317 -37
- package/dist/safe-bash/contracts/io.js.map +1 -1
- package/dist/safe-bash/core.browser.js +5502 -2210
- package/dist/safe-bash/core.browser.js.map +4 -4
- package/dist/safe-bash/core.d.ts +1 -0
- package/dist/safe-bash/core.d.ts.map +1 -1
- package/dist/safe-bash/core.js +1 -0
- package/dist/safe-bash/core.js.map +1 -1
- package/dist/safe-bash/opt-in/commands/cmp/compare.js +95 -0
- package/dist/safe-bash/opt-in/commands/cmp/index.d.ts +6 -0
- package/dist/safe-bash/opt-in/commands/cmp/index.js +99 -0
- package/dist/safe-bash/opt-in/commands/cmp/io.js +218 -0
- package/dist/safe-bash/opt-in/commands/cmp/options.d.ts +24 -0
- package/dist/safe-bash/opt-in/commands/cmp/options.js +195 -0
- package/dist/safe-bash/opt-in/commands/dd/conversions.js +152 -0
- package/dist/safe-bash/opt-in/commands/dd/index.d.ts +18 -0
- package/dist/safe-bash/opt-in/commands/dd/index.js +561 -0
- package/dist/safe-bash/opt-in/commands/dd/io.d.ts +31 -0
- package/dist/safe-bash/opt-in/commands/dd/io.js +244 -0
- package/dist/safe-bash/opt-in/commands/dd/options.js +184 -0
- package/dist/safe-bash/opt-in/commands/dd/report.js +69 -0
- package/dist/safe-bash/opt-in/commands/install/arguments.js +224 -0
- package/dist/safe-bash/opt-in/commands/install/index.d.ts +6 -0
- package/dist/safe-bash/opt-in/commands/install/index.js +678 -0
- package/dist/safe-bash/opt-in/commands/install/mode.js +71 -0
- package/dist/safe-bash/opt-in/commands/install/options.d.ts +36 -0
- package/dist/safe-bash/opt-in/commands/install/options.js +27 -0
- package/dist/safe-bash/opt-in/commands/shuf/args.js +179 -0
- package/dist/safe-bash/opt-in/commands/shuf/index.d.ts +6 -0
- package/dist/safe-bash/opt-in/commands/shuf/index.js +20 -0
- package/dist/safe-bash/opt-in/commands/shuf/input.js +93 -0
- package/dist/safe-bash/opt-in/commands/shuf/options.d.ts +9 -0
- package/dist/safe-bash/opt-in/commands/shuf/options.js +8 -0
- package/dist/safe-bash/opt-in/commands/shuf/random.js +114 -0
- package/dist/safe-bash/opt-in/commands/shuf/shuf.d.ts +3 -0
- package/dist/safe-bash/opt-in/commands/shuf/shuf.js +230 -0
- package/dist/safe-bash/opt-in/commands/shuf/usage.js +24 -0
- package/dist/safe-bash/opt-in/commands/truncate/arguments.js +186 -0
- package/dist/safe-bash/opt-in/commands/truncate/index.d.ts +9 -0
- package/dist/safe-bash/opt-in/commands/truncate/index.js +244 -0
- package/dist/safe-bash/opt-in/commands/yes/index.d.ts +11 -0
- package/dist/safe-bash/opt-in/commands/yes/index.js +167 -0
- package/dist/safe-bash/opt-in/commands/yq/arguments.js +177 -0
- package/dist/safe-bash/opt-in/commands/yq/evaluate.js +564 -0
- package/dist/safe-bash/opt-in/commands/yq/expression.js +183 -0
- package/dist/safe-bash/opt-in/commands/yq/inplace.js +65 -0
- package/dist/safe-bash/opt-in/commands/yq/mike.d.ts +9 -0
- package/dist/safe-bash/opt-in/commands/yq/mike.js +192 -0
- package/dist/safe-bash/opt-in/commands/yq/native-encoder.js +165 -0
- package/dist/safe-bash/opt-in/commands/yq/native-work.d.ts +44 -0
- package/dist/safe-bash/opt-in/commands/yq/native-work.js +188 -0
- package/dist/safe-bash/opt-in/commands/yq/nodes.js +621 -0
- package/dist/safe-bash/opt-in/entrypoints/arrays.d.ts +1 -0
- package/dist/safe-bash/opt-in/entrypoints/arrays.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/cmp.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/cmp.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/dd.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/dd.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/devices.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/devices.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/install.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/install.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/jobs.d.ts +1 -0
- package/dist/safe-bash/opt-in/entrypoints/jobs.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/mapfile.d.ts +1 -0
- package/dist/safe-bash/opt-in/entrypoints/mapfile.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/read.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/read.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/shuf.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/shuf.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/trap.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/trap.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/truncate.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/truncate.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/yes.d.ts +2 -0
- package/dist/safe-bash/opt-in/entrypoints/yes.js +1 -0
- package/dist/safe-bash/opt-in/entrypoints/yq.d.ts +3 -0
- package/dist/safe-bash/opt-in/entrypoints/yq.js +1 -0
- package/dist/safe-bash/opt-in/fs/devices/index.d.ts +8 -0
- package/dist/safe-bash/opt-in/fs/devices/index.js +292 -0
- package/dist/safe-bash/opt-in/optional.d.ts +28 -0
- package/dist/safe-bash/opt-in/optional.js +14 -0
- package/dist/safe-bash/opt-in/shell/extensions/arrays/index.d.ts +2 -0
- package/dist/safe-bash/opt-in/shell/extensions/arrays/index.js +4 -0
- package/dist/safe-bash/opt-in/shell/extensions/jobs/index.d.ts +2 -0
- package/dist/safe-bash/opt-in/shell/extensions/jobs/index.js +291 -0
- package/dist/safe-bash/opt-in/shell/extensions/jobs/state.js +374 -0
- package/dist/safe-bash/opt-in/shell/extensions/mapfile/index.d.ts +24 -0
- package/dist/safe-bash/opt-in/shell/extensions/mapfile/index.js +338 -0
- package/dist/safe-bash/opt-in/shell/extensions/read/index.d.ts +5 -0
- package/dist/safe-bash/opt-in/shell/extensions/read/index.js +351 -0
- package/dist/safe-bash/opt-in/shell/extensions/trap/index.d.ts +9 -0
- package/dist/safe-bash/opt-in/shell/extensions/trap/index.js +210 -0
- package/dist/safe-bash/optional-host.d.ts +5 -0
- package/dist/safe-bash/optional-host.d.ts.map +1 -0
- package/dist/safe-bash/optional-host.js +3 -0
- package/dist/safe-bash/optional-host.js.map +1 -0
- package/dist/safe-bash/shell/arrays/bindings.d.ts +15 -5
- package/dist/safe-bash/shell/arrays/bindings.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/bindings.js +131 -23
- package/dist/safe-bash/shell/arrays/bindings.js.map +1 -1
- package/dist/safe-bash/shell/arrays/ledger.d.ts +1 -0
- package/dist/safe-bash/shell/arrays/ledger.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/ledger.js +19 -0
- package/dist/safe-bash/shell/arrays/ledger.js.map +1 -1
- package/dist/safe-bash/shell/arrays/state.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/state.js +3 -1
- package/dist/safe-bash/shell/arrays/state.js.map +1 -1
- package/dist/safe-bash/shell/arrays/syntax.d.ts +4 -1
- package/dist/safe-bash/shell/arrays/syntax.d.ts.map +1 -1
- package/dist/safe-bash/shell/arrays/syntax.js +3 -3
- package/dist/safe-bash/shell/arrays/syntax.js.map +1 -1
- package/dist/safe-bash/shell/conditional.js +1 -1
- package/dist/safe-bash/shell/descriptors.d.ts +31 -0
- package/dist/safe-bash/shell/descriptors.d.ts.map +1 -0
- package/dist/safe-bash/shell/descriptors.js +234 -0
- package/dist/safe-bash/shell/descriptors.js.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.d.ts +2 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.d.ts.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.js +717 -0
- package/dist/safe-bash/shell/diagnostic-name-ranges.js.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name.d.ts +3 -0
- package/dist/safe-bash/shell/diagnostic-name.d.ts.map +1 -0
- package/dist/safe-bash/shell/diagnostic-name.js +89 -0
- package/dist/safe-bash/shell/diagnostic-name.js.map +1 -0
- package/dist/safe-bash/shell/display.js +1 -1
- package/dist/safe-bash/shell/display.js.map +1 -1
- package/dist/safe-bash/shell/extensions.d.ts +188 -0
- package/dist/safe-bash/shell/extensions.d.ts.map +1 -0
- package/dist/safe-bash/shell/extensions.js +141 -0
- package/dist/safe-bash/shell/extensions.js.map +1 -0
- package/dist/safe-bash/shell/input.d.ts +66 -14
- package/dist/safe-bash/shell/input.d.ts.map +1 -1
- package/dist/safe-bash/shell/input.js +947 -173
- package/dist/safe-bash/shell/input.js.map +1 -1
- package/dist/safe-bash/shell/parser.d.ts +38 -6
- package/dist/safe-bash/shell/parser.d.ts.map +1 -1
- package/dist/safe-bash/shell/parser.js +339 -52
- package/dist/safe-bash/shell/parser.js.map +1 -1
- package/dist/safe-bash/shell/runtime.d.ts +92 -20
- package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
- package/dist/safe-bash/shell/runtime.js +2547 -689
- package/dist/safe-bash/shell/runtime.js.map +1 -1
- package/dist/safe-bash/shell/shell.d.ts.map +1 -1
- package/dist/safe-bash/shell/shell.js +52 -21
- package/dist/safe-bash/shell/shell.js.map +1 -1
- package/dist/safe-bash/shell/types.d.ts +4 -0
- package/dist/safe-bash/shell/types.d.ts.map +1 -1
- package/dist/safe-bash/shell/types.js.map +1 -1
- package/package.json +92 -4
- package/dist/safe-bash/chunks/chunk-3BZ5NQ73.js.map +0 -7
- package/dist/safe-bash/chunks/chunk-4DBWBEU7.js +0 -194
- package/dist/safe-bash/chunks/chunk-4DBWBEU7.js.map +0 -7
- package/dist/safe-bash/chunks/chunk-CXMIIXJA.js +0 -178
- package/dist/safe-bash/chunks/chunk-CXMIIXJA.js.map +0 -7
- package/dist/safe-bash/chunks/chunk-K7W6Q6FN.js.map +0 -7
- /package/dist/safe-bash/chunks/{bz2-B2RXT4EX.js.map → bz2-ACU4EOVE.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-RNKTWFEM.js.map → chunk-CDBQXRWL.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-AFH57PPL.js.map → chunk-IEKESMO4.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-DPJWEGIX.js.map → chunk-JLBIUYT3.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-PVJJFCGA.js.map → chunk-M3GPCYSP.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-2KL6W6NB.js.map → chunk-O2EVBRKF.js.map} +0 -0
- /package/dist/safe-bash/chunks/{chunk-C7QNJ3D4.js.map → chunk-WYTI64IU.js.map} +0 -0
- /package/dist/safe-bash/chunks/{xz-AVXFDXW4.js.map → xz-U4QDTPZL.js.map} +0 -0
- /package/dist/safe-bash/chunks/{zstd-R6RPHJYO.js.map → zstd-OG5RPYXO.js.map} +0 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { FsError, basename, dirname, readBytes, writeBytes } from "@poe-platform/safe-bash/contracts/index";
|
|
2
|
+
import { commandRuntimeIdentity } from "@poe-platform/safe-bash/contracts/command";
|
|
3
|
+
import { retainFileSystemCleanup } from "@poe-platform/safe-fs/core";
|
|
4
|
+
import { assertCountedFileOutput, openFileOutput, writeFileOutputCounted } from "@poe-platform/safe-bash/contracts/filesystem-output";
|
|
5
|
+
import { yieldTurn } from "@poe-platform/safe-bash/contracts/yield";
|
|
6
|
+
import { codeOf, output, pathOf } from "@poe-platform/safe-bash/optional-host";
|
|
7
|
+
import { compareCopyIdentity, compareObservedEntries } from "@poe-platform/safe-bash/optional-host";
|
|
8
|
+
import { helpText, parseArguments } from "./arguments.js";
|
|
9
|
+
import { parseMode } from "./mode.js";
|
|
10
|
+
import { InstallError, quote } from "./options.js";
|
|
11
|
+
const errorMessages = {
|
|
12
|
+
ENOENT: "No such file or directory", EACCES: "Permission denied", EPERM: "Operation not permitted", EEXIST: "File exists",
|
|
13
|
+
EISDIR: "Is a directory", ENOTDIR: "Not a directory", EROFS: "Read-only file system", ENOSPC: "No space left on device",
|
|
14
|
+
ENOTSUP: "Operation not supported", EOPNOTSUPP: "Operation not supported", ELOOP: "Too many levels of symbolic links",
|
|
15
|
+
EFBIG: "File too large", EIO: "Input/output error", EINVAL: "Invalid argument", ENOTEMPTY: "Directory not empty",
|
|
16
|
+
};
|
|
17
|
+
function failure(message, error) {
|
|
18
|
+
const code = codeOf(error);
|
|
19
|
+
return new InstallError(`${message}: ${code && errorMessages[code] || (error instanceof Error ? error.message : String(error))}`);
|
|
20
|
+
}
|
|
21
|
+
async function maybeStat(context, path, follow = false) {
|
|
22
|
+
try {
|
|
23
|
+
const stat = await context.fs[follow ? "stat" : "lstat"](path, { signal: context.signal });
|
|
24
|
+
context.signal.throwIfAborted();
|
|
25
|
+
return stat;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
context.signal.throwIfAborted();
|
|
29
|
+
if (codeOf(error) === "ENOENT")
|
|
30
|
+
return undefined;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function admit(context, path, capabilities) {
|
|
35
|
+
const check = (available) => {
|
|
36
|
+
if (available.readOnly)
|
|
37
|
+
throw new FsError("EROFS");
|
|
38
|
+
if (capabilities.some(capability => available[capability] === false))
|
|
39
|
+
throw new FsError("ENOTSUP");
|
|
40
|
+
};
|
|
41
|
+
const globalCapabilities = context.fs.capabilities;
|
|
42
|
+
check(globalCapabilities);
|
|
43
|
+
if (!context.fs.capabilitiesFor)
|
|
44
|
+
return globalCapabilities;
|
|
45
|
+
let candidate = path;
|
|
46
|
+
while (true) {
|
|
47
|
+
context.signal.throwIfAborted();
|
|
48
|
+
try {
|
|
49
|
+
const available = await context.fs.capabilitiesFor(candidate, { signal: context.signal });
|
|
50
|
+
check(available);
|
|
51
|
+
return available;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (codeOf(error) !== "ENOENT" || candidate === "/")
|
|
55
|
+
throw error;
|
|
56
|
+
candidate = dirname(candidate);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function account(context, text, kind, settings) {
|
|
61
|
+
if (text === undefined)
|
|
62
|
+
return undefined;
|
|
63
|
+
const resolver = kind === "user" ? settings.resolveUser : settings.resolveGroup;
|
|
64
|
+
let id = await resolver?.(text, context);
|
|
65
|
+
context.signal.throwIfAborted();
|
|
66
|
+
if (id === undefined) {
|
|
67
|
+
let digits = text, base = 10;
|
|
68
|
+
while (digits && " \t\n\r\v\f".includes(digits[0]))
|
|
69
|
+
digits = digits.slice(1);
|
|
70
|
+
if (digits.startsWith("+"))
|
|
71
|
+
digits = digits.slice(1);
|
|
72
|
+
if (digits.startsWith("0x") || digits.startsWith("0X")) {
|
|
73
|
+
base = 16;
|
|
74
|
+
digits = digits.slice(2);
|
|
75
|
+
}
|
|
76
|
+
else if (digits.startsWith("0") && digits.length > 1) {
|
|
77
|
+
base = 8;
|
|
78
|
+
digits = digits.slice(1);
|
|
79
|
+
}
|
|
80
|
+
if (!digits || !Array.from(digits).every(character => "0123456789abcdef".indexOf(character.toLowerCase()) >= 0 && "0123456789abcdef".indexOf(character.toLowerCase()) < base)) {
|
|
81
|
+
if (!resolver && text && !"+-0123456789".includes(text[0]))
|
|
82
|
+
throw new InstallError(`cannot resolve ${kind} ${quote(text)}: Operation not supported (account database unavailable)`);
|
|
83
|
+
throw new InstallError(`invalid ${kind} ${quote(text)}`);
|
|
84
|
+
}
|
|
85
|
+
id = Number.parseInt(digits, base);
|
|
86
|
+
}
|
|
87
|
+
if (!Number.isSafeInteger(id) || id < 0 || id > 4294967295)
|
|
88
|
+
throw new InstallError(`invalid ${kind} ${quote(text)}`);
|
|
89
|
+
return id === 4294967295 ? undefined : id;
|
|
90
|
+
}
|
|
91
|
+
async function setPermissions(operation, path, display, mode, kind, change) {
|
|
92
|
+
const { context, settings } = operation, fsOptions = { signal: context.signal };
|
|
93
|
+
let directorySearchable = false;
|
|
94
|
+
if (kind !== "file" && (mode & 0o2000) !== 0 && !settings.setMode) {
|
|
95
|
+
try {
|
|
96
|
+
await context.fs.access(path, 1, fsOptions);
|
|
97
|
+
directorySearchable = true;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
context.signal.throwIfAborted();
|
|
101
|
+
if (!["EACCES", "EPERM", "ENOTSUP", "EOPNOTSUPP"].includes(codeOf(error) ?? ""))
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
if (change) {
|
|
107
|
+
if (settings.setMode)
|
|
108
|
+
await settings.setMode({ path, mode, kind }, context);
|
|
109
|
+
else
|
|
110
|
+
await context.fs.chmod(path, mode, fsOptions);
|
|
111
|
+
}
|
|
112
|
+
context.signal.throwIfAborted();
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
throw failure(`cannot change permissions of ${quote(display)}`, error);
|
|
116
|
+
}
|
|
117
|
+
const actual = await context.fs.stat(path, fsOptions);
|
|
118
|
+
const actualMode = actual.mode & 0o7777;
|
|
119
|
+
const mayClearSetgid = kind !== "file" && (directorySearchable || settings.setMode !== undefined);
|
|
120
|
+
if (actualMode !== mode && !(mayClearSetgid && actualMode === (mode & ~0o2000)))
|
|
121
|
+
throw new InstallError(`cannot change permissions of ${quote(display)}: Operation not supported (backend did not retain requested mode)`);
|
|
122
|
+
}
|
|
123
|
+
async function attributes(operation, path, display, mode, source, directoryStat, createdDirectory = false) {
|
|
124
|
+
const { context, args, settings, uid, gid } = operation;
|
|
125
|
+
const ownershipChanges = uid !== undefined && uid !== directoryStat?.uid || gid !== undefined && gid !== directoryStat?.gid;
|
|
126
|
+
if (ownershipChanges) {
|
|
127
|
+
try {
|
|
128
|
+
await settings.chown(path, uid, gid, context);
|
|
129
|
+
context.signal.throwIfAborted();
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
throw failure(`cannot change ownership of ${quote(display)}`, error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const change = !directoryStat || (directoryStat.mode & 0o7777) !== mode || ownershipChanges && (directoryStat.mode & 0o6111) !== 0;
|
|
136
|
+
await setPermissions(operation, path, display, mode, directoryStat ? createdDirectory ? "new-directory" : "existing-directory" : "file", change);
|
|
137
|
+
if (settings.securityContext?.enabled && (args.contextMode !== "preserve" || source !== undefined)) {
|
|
138
|
+
if (!settings.securityContext.apply)
|
|
139
|
+
throw new InstallError(`cannot set context of ${quote(display)}: Operation not supported`);
|
|
140
|
+
await settings.securityContext.apply({ path, mode: args.contextMode ?? "default", ...(source === undefined ? {} : { source }), ...(args.contextLabel === undefined ? {} : { label: args.contextLabel }) }, context);
|
|
141
|
+
context.signal.throwIfAborted();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function directories(operation, display, final) {
|
|
145
|
+
const { context, args, modes, uid, gid } = operation;
|
|
146
|
+
if (!display)
|
|
147
|
+
throw failure(`cannot create directory ${quote(display)}`, new FsError("ENOENT"));
|
|
148
|
+
const parts = display.split("/");
|
|
149
|
+
let current = display.startsWith("/") ? "/" : "";
|
|
150
|
+
const components = parts.filter(Boolean);
|
|
151
|
+
if (!components.length)
|
|
152
|
+
components.push(".");
|
|
153
|
+
for (let index = 0; index < components.length; index++) {
|
|
154
|
+
current = current && current !== "/" ? `${current}/${components[index]}` : `${current}${components[index]}`;
|
|
155
|
+
const path = pathOf(context, current), last = index === components.length - 1;
|
|
156
|
+
try {
|
|
157
|
+
await admit(context, path, ["stat", "mkdir", "permissions"]);
|
|
158
|
+
const before = await maybeStat(context, path, true);
|
|
159
|
+
if (before && before.type !== "directory")
|
|
160
|
+
throw new FsError("ENOTDIR");
|
|
161
|
+
if (!before) {
|
|
162
|
+
let creationMode = final && last ? modes.directory : 0o755;
|
|
163
|
+
if (final && last) {
|
|
164
|
+
if (uid !== undefined || gid !== undefined)
|
|
165
|
+
creationMode &= ~0o077;
|
|
166
|
+
else if ((modes.directoryMask & 0o6000) !== 0 || (modes.directory & 0o1000) !== 0)
|
|
167
|
+
creationMode &= ~0o022;
|
|
168
|
+
}
|
|
169
|
+
await context.fs.mkdir(path, { mode: creationMode, signal: context.signal });
|
|
170
|
+
if (args.verbose)
|
|
171
|
+
await output(context, `install: creating directory ${quote(current)}\n`);
|
|
172
|
+
if (!(final && last)) {
|
|
173
|
+
const created = await context.fs.stat(path, { signal: context.signal });
|
|
174
|
+
await setPermissions(operation, path, current, 0o755, "new-directory", (created.mode & 0o7777) !== 0o755);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (final && last) {
|
|
178
|
+
const currentStat = before ?? await context.fs.stat(path, { signal: context.signal });
|
|
179
|
+
await attributes(operation, path, display, currentStat.mode & 0o7777 & ~modes.directoryMask | modes.directory, undefined, currentStat, !before);
|
|
180
|
+
}
|
|
181
|
+
context.signal.throwIfAborted();
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
if (error instanceof InstallError)
|
|
185
|
+
throw error;
|
|
186
|
+
throw failure(`cannot create directory ${quote(current)}`, error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function backup(operation, destination, display, original, source, sourceStat, sourceDisplay) {
|
|
191
|
+
const { context, args, settings } = operation;
|
|
192
|
+
let numbered = args.backup === "numbered", highest = 0n;
|
|
193
|
+
if (args.backup === "numbered" || args.backup === "existing") {
|
|
194
|
+
const prefix = `${basename(destination)}.~`;
|
|
195
|
+
for (const entry of await context.fs.readdir(dirname(destination), { signal: context.signal })) {
|
|
196
|
+
if (!entry.name.startsWith(prefix) || !entry.name.endsWith("~"))
|
|
197
|
+
continue;
|
|
198
|
+
const digits = entry.name.slice(prefix.length, -1);
|
|
199
|
+
if (!digits || digits[0] === "0" || !Array.from(digits).every(character => character >= "0" && character <= "9"))
|
|
200
|
+
continue;
|
|
201
|
+
const version = BigInt(digits);
|
|
202
|
+
if (version > highest)
|
|
203
|
+
highest = version;
|
|
204
|
+
}
|
|
205
|
+
numbered ||= highest > 0n;
|
|
206
|
+
}
|
|
207
|
+
const suffix = numbered ? `.~${highest + 1n}~` : args.suffix;
|
|
208
|
+
const target = `${destination}${suffix}`, targetDisplay = `${display}${suffix}`;
|
|
209
|
+
const sourceConflict = () => new InstallError(`backing up ${quote(display)} might destroy source; ${quote(sourceDisplay)} not copied`);
|
|
210
|
+
if (target === destination)
|
|
211
|
+
throw sourceConflict();
|
|
212
|
+
const previous = await maybeStat(context, target);
|
|
213
|
+
if (previous) {
|
|
214
|
+
const identity = await compareObservedEntries(context.fs, source, sourceStat, context.fs, target, previous, { signal: context.signal });
|
|
215
|
+
if (identity === "unknown")
|
|
216
|
+
throw new InstallError(`cannot safely replace backup ${quote(targetDisplay)}: Operation not supported (unknown identity)`);
|
|
217
|
+
if (identity === "same" && await context.fs.realpath(source, { signal: context.signal }) === await context.fs.realpath(target, { signal: context.signal }))
|
|
218
|
+
throw sourceConflict();
|
|
219
|
+
}
|
|
220
|
+
await admit(context, destination, ["rename", "remove"]);
|
|
221
|
+
try {
|
|
222
|
+
if (!numbered)
|
|
223
|
+
await context.fs.rename(destination, target, { signal: context.signal });
|
|
224
|
+
else if (settings.renameExclusive)
|
|
225
|
+
await settings.renameExclusive(destination, target, context);
|
|
226
|
+
else if (original.type === "file" && context.fs.link && context.fs.capabilities.hardlinks !== false) {
|
|
227
|
+
await context.fs.link(destination, target, { signal: context.signal });
|
|
228
|
+
const current = await maybeStat(context, destination), saved = await maybeStat(context, target);
|
|
229
|
+
if (!current || !saved || await compareObservedEntries(context.fs, destination, original, context.fs, destination, current, { signal: context.signal }) !== "same"
|
|
230
|
+
|| await compareObservedEntries(context.fs, destination, original, context.fs, target, saved, { signal: context.signal }) !== "same")
|
|
231
|
+
throw new FsError("ENOTSUP", { message: "backup identity changed during publication" });
|
|
232
|
+
await context.fs.rm(destination, { signal: context.signal });
|
|
233
|
+
}
|
|
234
|
+
else
|
|
235
|
+
throw new FsError("ENOTSUP", { message: "exclusive backup movement unavailable" });
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
throw failure(`cannot backup ${quote(display)}`, error);
|
|
239
|
+
}
|
|
240
|
+
return targetDisplay;
|
|
241
|
+
}
|
|
242
|
+
async function sameContent(operation, source, sourceStat, destination, destinationStat) {
|
|
243
|
+
const { context, settings, modes, uid, gid, args, maxFileBytes } = operation;
|
|
244
|
+
if (sourceStat.type !== "file" || destinationStat.type !== "file" || ((sourceStat.mode | destinationStat.mode | modes.file) & 0o7000)
|
|
245
|
+
|| sourceStat.size !== destinationStat.size || (destinationStat.mode & 0o7777) !== modes.file)
|
|
246
|
+
return false;
|
|
247
|
+
const owner = uid ?? settings.identity?.uid, group = gid ?? settings.identity?.gid;
|
|
248
|
+
if (owner === undefined || group === undefined || destinationStat.uid === undefined || destinationStat.gid === undefined)
|
|
249
|
+
throw new InstallError("cannot compare ownership: Operation not supported (caller or file identity unavailable)");
|
|
250
|
+
if (owner !== destinationStat.uid || group !== destinationStat.gid)
|
|
251
|
+
return false;
|
|
252
|
+
if (settings.securityContext?.enabled && args.contextMode === "preserve") {
|
|
253
|
+
if (!settings.securityContext.matches)
|
|
254
|
+
throw new InstallError("cannot compare security contexts: Operation not supported");
|
|
255
|
+
if (!await settings.securityContext.matches(source, destination, context))
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const left = await context.fs.readFile(source, { signal: context.signal, maxBytes: maxFileBytes });
|
|
260
|
+
const right = await context.fs.readFile(destination, { signal: context.signal, maxBytes: maxFileBytes });
|
|
261
|
+
context.signal.throwIfAborted();
|
|
262
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
context.signal.throwIfAborted();
|
|
266
|
+
if (codeOf(error) === "EFBIG")
|
|
267
|
+
throw error;
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function installFile(operation, sourceDisplay, destinationDisplay) {
|
|
272
|
+
const { context, args, modes, settings, maxFileBytes } = operation;
|
|
273
|
+
const fsOptions = { signal: context.signal };
|
|
274
|
+
if (args.parents)
|
|
275
|
+
await directories(operation, dirname(destinationDisplay), false);
|
|
276
|
+
let source, destination;
|
|
277
|
+
let sourceStat;
|
|
278
|
+
try {
|
|
279
|
+
source = pathOf(context, sourceDisplay);
|
|
280
|
+
sourceStat = await context.fs.stat(source, fsOptions);
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
throw failure(`cannot stat ${quote(sourceDisplay)}`, error);
|
|
284
|
+
}
|
|
285
|
+
if (sourceStat.type === "directory")
|
|
286
|
+
throw new InstallError(`omitting directory ${quote(sourceDisplay)}`);
|
|
287
|
+
if (!destinationDisplay)
|
|
288
|
+
throw new InstallError(`cannot overwrite directory ${quote(destinationDisplay)} with non-directory ${quote(sourceDisplay)}`);
|
|
289
|
+
try {
|
|
290
|
+
destination = pathOf(context, destinationDisplay);
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
throw failure(`cannot create regular file ${quote(destinationDisplay)}`, error);
|
|
294
|
+
}
|
|
295
|
+
const target = await maybeStat(context, destination);
|
|
296
|
+
if (target?.type === "directory")
|
|
297
|
+
throw new InstallError(`cannot overwrite directory ${quote(destinationDisplay)} with non-directory ${quote(sourceDisplay)}`);
|
|
298
|
+
if (target?.type === "file") {
|
|
299
|
+
const identity = await compareObservedEntries(context.fs, source, sourceStat, context.fs, destination, target, fsOptions);
|
|
300
|
+
if (identity === "unknown")
|
|
301
|
+
throw new InstallError(`cannot establish source/destination identity: Operation not supported`);
|
|
302
|
+
if (identity === "same" && await context.fs.realpath(source, fsOptions) === await context.fs.realpath(destination, fsOptions))
|
|
303
|
+
throw new InstallError(`${quote(sourceDisplay)} and ${quote(destinationDisplay)} are the same file`);
|
|
304
|
+
}
|
|
305
|
+
if (args.compare && target && await sameContent(operation, source, sourceStat, destination, target))
|
|
306
|
+
return;
|
|
307
|
+
const destinationCapabilities = await admit(context, destination, ["write", "exclusiveCreate", "permissions", ...(target ? ["remove"] : [])]);
|
|
308
|
+
const streamingOutput = !!context.fs.writeStream && destinationCapabilities.streamingWrite !== false;
|
|
309
|
+
if (!streamingOutput)
|
|
310
|
+
assertCountedFileOutput(context);
|
|
311
|
+
const removeAfterStripFailure = args.strip ? retainFileSystemCleanup(context.fs, cleanup => cleanup.rm(destination), { maxOperations: 1 }) : undefined;
|
|
312
|
+
if (sourceStat.type === "file" && sourceStat.size > maxFileBytes)
|
|
313
|
+
throw failure(`cannot copy ${quote(sourceDisplay)}`, new FsError("EFBIG"));
|
|
314
|
+
let backupDisplay;
|
|
315
|
+
if (target) {
|
|
316
|
+
if (args.backup !== "none")
|
|
317
|
+
backupDisplay = await backup(operation, destination, destinationDisplay, target, source, sourceStat, sourceDisplay);
|
|
318
|
+
else {
|
|
319
|
+
try {
|
|
320
|
+
await context.fs.rm(destination, fsOptions);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
throw failure(`cannot remove ${quote(destinationDisplay)}`, error);
|
|
324
|
+
}
|
|
325
|
+
if (args.verbose)
|
|
326
|
+
await output(context, `removed ${quote(destinationDisplay)}\n`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (args.verbose)
|
|
330
|
+
await output(context, `${quote(sourceDisplay)} -> ${quote(destinationDisplay)}${backupDisplay === undefined ? "" : ` (backup: ${quote(backupDisplay)})`}\n`);
|
|
331
|
+
let closed = false, completion, cleanupFailure;
|
|
332
|
+
let bufferedWrite;
|
|
333
|
+
let opening, descriptorClose, timestampStat = sourceStat;
|
|
334
|
+
let iterator, returned;
|
|
335
|
+
const closeDescriptor = () => descriptorClose ??= (async () => {
|
|
336
|
+
let descriptor;
|
|
337
|
+
try {
|
|
338
|
+
descriptor = await opening;
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
await descriptor?.close();
|
|
344
|
+
})();
|
|
345
|
+
const returnSource = () => returned ??= Promise.resolve().then(() => iterator?.return ? iterator.return() : { done: true, value: undefined });
|
|
346
|
+
const sourceBytes = (async function* () {
|
|
347
|
+
if (closed)
|
|
348
|
+
throw new FsError("EBADF");
|
|
349
|
+
let chunks;
|
|
350
|
+
if (context.fs.open && context.fs.capabilities.open !== false) {
|
|
351
|
+
opening = context.fs.open(source, { ...fsOptions, access: "read", creation: "never" });
|
|
352
|
+
const descriptor = await opening;
|
|
353
|
+
context.signal.throwIfAborted();
|
|
354
|
+
if (closed)
|
|
355
|
+
throw new FsError("EBADF");
|
|
356
|
+
let openedStat;
|
|
357
|
+
try {
|
|
358
|
+
openedStat = await descriptor.stat(fsOptions);
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
throw failure(`cannot fstat ${quote(sourceDisplay)}`, error);
|
|
362
|
+
}
|
|
363
|
+
const identity = compareCopyIdentity(sourceStat, openedStat);
|
|
364
|
+
if (identity === "distinct")
|
|
365
|
+
throw new InstallError(`skipping file ${quote(sourceDisplay)}, as it was replaced while being copied`);
|
|
366
|
+
if (identity === "unknown")
|
|
367
|
+
throw new InstallError("cannot establish opened source identity: Operation not supported");
|
|
368
|
+
if (args.preserve && !args.strip && sourceStat.type === "file")
|
|
369
|
+
timestampStat = openedStat;
|
|
370
|
+
chunks = (async function* () {
|
|
371
|
+
const buffer = new Uint8Array(65536);
|
|
372
|
+
while (true) {
|
|
373
|
+
context.signal.throwIfAborted();
|
|
374
|
+
if (closed)
|
|
375
|
+
throw new FsError("EBADF");
|
|
376
|
+
const count = await descriptor.read(buffer, null, fsOptions);
|
|
377
|
+
if (!Number.isSafeInteger(count) || count < 0 || count > buffer.length)
|
|
378
|
+
throw new FsError("EIO");
|
|
379
|
+
if (!count)
|
|
380
|
+
return;
|
|
381
|
+
yield buffer.subarray(0, count);
|
|
382
|
+
}
|
|
383
|
+
})();
|
|
384
|
+
}
|
|
385
|
+
else
|
|
386
|
+
chunks = context.fs.readStream && context.fs.capabilities.streamingRead !== false ? context.fs.readStream(source, fsOptions)
|
|
387
|
+
: (async function* () { yield await context.fs.readFile(source, { ...fsOptions, maxBytes: maxFileBytes }); })();
|
|
388
|
+
iterator = chunks[Symbol.asyncIterator]();
|
|
389
|
+
let size = 0, untilYield = 65536;
|
|
390
|
+
const guarded = { [Symbol.asyncIterator]: () => ({ next: () => iterator.next(), return: returnSource }) };
|
|
391
|
+
for await (const bytes of readBytes(guarded, context.signal)) {
|
|
392
|
+
context.signal.throwIfAborted();
|
|
393
|
+
if (closed)
|
|
394
|
+
throw new FsError("EBADF");
|
|
395
|
+
size += bytes.byteLength;
|
|
396
|
+
if (size > maxFileBytes)
|
|
397
|
+
throw new FsError("EFBIG");
|
|
398
|
+
const owned = new Uint8Array(bytes);
|
|
399
|
+
untilYield -= Math.max(64, owned.byteLength);
|
|
400
|
+
if (untilYield <= 0) {
|
|
401
|
+
await yieldTurn(context.signal);
|
|
402
|
+
untilYield = 65536;
|
|
403
|
+
}
|
|
404
|
+
yield owned;
|
|
405
|
+
}
|
|
406
|
+
})();
|
|
407
|
+
const close = () => {
|
|
408
|
+
closed = true;
|
|
409
|
+
return completion ??= (async () => {
|
|
410
|
+
let failure;
|
|
411
|
+
try {
|
|
412
|
+
await closeDescriptor();
|
|
413
|
+
}
|
|
414
|
+
catch (reason) {
|
|
415
|
+
failure = { reason };
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
if (iterator)
|
|
419
|
+
await returnSource();
|
|
420
|
+
}
|
|
421
|
+
catch (reason) {
|
|
422
|
+
failure ??= { reason };
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
await sourceBytes.return(undefined);
|
|
426
|
+
}
|
|
427
|
+
catch (reason) {
|
|
428
|
+
failure ??= { reason };
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
await bufferedWrite;
|
|
432
|
+
}
|
|
433
|
+
catch (reason) {
|
|
434
|
+
failure ??= { reason };
|
|
435
|
+
}
|
|
436
|
+
if (failure)
|
|
437
|
+
throw failure.reason;
|
|
438
|
+
})();
|
|
439
|
+
};
|
|
440
|
+
context.registerCleanup?.(close);
|
|
441
|
+
try {
|
|
442
|
+
if (closed)
|
|
443
|
+
throw new FsError("EBADF");
|
|
444
|
+
let first;
|
|
445
|
+
try {
|
|
446
|
+
first = await sourceBytes.next();
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
if (error instanceof InstallError)
|
|
450
|
+
throw error;
|
|
451
|
+
throw failure(`cannot open ${quote(sourceDisplay)} for reading`, error);
|
|
452
|
+
}
|
|
453
|
+
if (closed)
|
|
454
|
+
throw new FsError("EBADF");
|
|
455
|
+
const contentStream = (async function* () {
|
|
456
|
+
if (!first.done)
|
|
457
|
+
yield first.value;
|
|
458
|
+
yield* sourceBytes;
|
|
459
|
+
})();
|
|
460
|
+
if (streamingOutput) {
|
|
461
|
+
const targetOutput = await openFileOutput(context, destination, { flag: "wx", mode: 0o600 });
|
|
462
|
+
try {
|
|
463
|
+
for await (const bytes of contentStream)
|
|
464
|
+
await targetOutput.sink.write(bytes);
|
|
465
|
+
await targetOutput.finish();
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
try {
|
|
469
|
+
await targetOutput.abort(error);
|
|
470
|
+
}
|
|
471
|
+
catch { }
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
const chunks = [];
|
|
477
|
+
let size = 0;
|
|
478
|
+
for await (const bytes of contentStream) {
|
|
479
|
+
chunks.push(bytes);
|
|
480
|
+
size += bytes.byteLength;
|
|
481
|
+
}
|
|
482
|
+
const content = new Uint8Array(size);
|
|
483
|
+
let offset = 0;
|
|
484
|
+
for (const bytes of chunks) {
|
|
485
|
+
content.set(bytes, offset);
|
|
486
|
+
offset += bytes.byteLength;
|
|
487
|
+
}
|
|
488
|
+
bufferedWrite = writeFileOutputCounted(context, content, async () => {
|
|
489
|
+
if (closed)
|
|
490
|
+
throw new FsError("EBADF");
|
|
491
|
+
await context.fs.writeFile(destination, content, { ...fsOptions, flag: "wx", mode: 0o600 });
|
|
492
|
+
return content.byteLength;
|
|
493
|
+
});
|
|
494
|
+
await bufferedWrite;
|
|
495
|
+
}
|
|
496
|
+
context.signal.throwIfAborted();
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
context.signal.throwIfAborted();
|
|
500
|
+
if (backupDisplay !== undefined && !await maybeStat(context, destination)) {
|
|
501
|
+
const backupPath = pathOf(context, backupDisplay);
|
|
502
|
+
if (settings.renameExclusive)
|
|
503
|
+
await settings.renameExclusive(backupPath, destination, context);
|
|
504
|
+
else if (target?.type === "file" && context.fs.link && context.fs.capabilities.hardlinks !== false) {
|
|
505
|
+
await context.fs.link(backupPath, destination, fsOptions);
|
|
506
|
+
await context.fs.rm(backupPath, fsOptions);
|
|
507
|
+
}
|
|
508
|
+
else
|
|
509
|
+
throw failure(`cannot restore backup ${quote(backupDisplay)}`, new FsError("ENOTSUP"));
|
|
510
|
+
if (args.verbose)
|
|
511
|
+
await output(context, `${quote(backupDisplay)} -> ${quote(destinationDisplay)} (unbackup)\n`);
|
|
512
|
+
}
|
|
513
|
+
if (error instanceof InstallError)
|
|
514
|
+
throw error;
|
|
515
|
+
throw failure(["ENOSPC", "EFBIG"].includes(codeOf(error) ?? "") ? `error writing ${quote(destinationDisplay)}` : `cannot create regular file ${quote(destinationDisplay)}`, error);
|
|
516
|
+
}
|
|
517
|
+
finally {
|
|
518
|
+
try {
|
|
519
|
+
await close();
|
|
520
|
+
}
|
|
521
|
+
catch (reason) {
|
|
522
|
+
cleanupFailure = { reason };
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
context.signal.throwIfAborted();
|
|
526
|
+
if (cleanupFailure)
|
|
527
|
+
throw cleanupFailure.reason;
|
|
528
|
+
if (args.debug)
|
|
529
|
+
await output(context, "copy offload: unsupported, reflink: unsupported, sparse detection: no\n");
|
|
530
|
+
if (args.strip) {
|
|
531
|
+
let status;
|
|
532
|
+
try {
|
|
533
|
+
status = await settings.strip(destination, args.stripProgram, context);
|
|
534
|
+
context.signal.throwIfAborted();
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
await removeAfterStripFailure();
|
|
538
|
+
throw failure(`cannot run ${quote(args.stripProgram)}`, error);
|
|
539
|
+
}
|
|
540
|
+
context.signal.throwIfAborted();
|
|
541
|
+
if (status !== 0) {
|
|
542
|
+
await removeAfterStripFailure();
|
|
543
|
+
throw new InstallError("strip process terminated abnormally");
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (args.preserve) {
|
|
547
|
+
try {
|
|
548
|
+
await context.fs.utimes(destination, timestampStat.atimeMs, timestampStat.mtimeMs, fsOptions);
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
throw failure(`preserving times for ${quote(destinationDisplay)}`, error);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
await attributes(operation, destination, destinationDisplay, modes.file, source);
|
|
555
|
+
}
|
|
556
|
+
export function createInstallCommand(options = {}) {
|
|
557
|
+
const settings = { ...options }, maxFileBytes = settings.maxFileBytes ?? 32 * 1024 * 1024;
|
|
558
|
+
if (!Number.isSafeInteger(maxFileBytes) || maxFileBytes < 0)
|
|
559
|
+
throw new TypeError("maxFileBytes must be a nonnegative safe integer");
|
|
560
|
+
return { name: "install", runtimeIdentity: commandRuntimeIdentity, filesystemRequirements: [
|
|
561
|
+
{ id: "file", description: "Install file contents with modes", capabilities: ["read", "stat", "write", "exclusiveCreate", "permissions"], mutates: true },
|
|
562
|
+
{ id: "directory", description: "Create installation directories", capabilities: ["stat", "mkdir", "permissions"], mutates: true },
|
|
563
|
+
{ id: "timestamps", description: "Preserve installation timestamps", capabilities: ["timestamps"], mutates: true },
|
|
564
|
+
], async execute(context) {
|
|
565
|
+
context.signal.throwIfAborted();
|
|
566
|
+
const report = async (error) => {
|
|
567
|
+
context.signal.throwIfAborted();
|
|
568
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
569
|
+
await writeBytes(context.stderr, new TextEncoder().encode(`install: ${message}\n${error instanceof InstallError && error.usage ? "Try 'install --help' for more information.\n" : ""}`), context.signal);
|
|
570
|
+
};
|
|
571
|
+
try {
|
|
572
|
+
const args = await parseArguments(context, settings, report);
|
|
573
|
+
if (args.display) {
|
|
574
|
+
await output(context, args.display === "help" ? helpText : "install (safe-bash; GNU coreutils 9.7 target)\n");
|
|
575
|
+
return { exitCode: 0 };
|
|
576
|
+
}
|
|
577
|
+
let targetDirectory = args.target;
|
|
578
|
+
if (targetDirectory !== undefined) {
|
|
579
|
+
try {
|
|
580
|
+
const target = await maybeStat(context, pathOf(context, targetDirectory), true);
|
|
581
|
+
if (target?.type !== "directory" && !(target === undefined && args.parents))
|
|
582
|
+
throw new FsError(target ? "ENOTDIR" : "ENOENT");
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
throw failure(`failed to access ${quote(targetDirectory)}`, error);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else if (!args.directory && !args.noTarget) {
|
|
589
|
+
const last = args.files.at(-1);
|
|
590
|
+
const target = last ? await maybeStat(context, pathOf(context, last), true) : undefined;
|
|
591
|
+
if (target?.type === "directory") {
|
|
592
|
+
targetDirectory = last;
|
|
593
|
+
args.files.pop();
|
|
594
|
+
}
|
|
595
|
+
else if (args.files.length > 2)
|
|
596
|
+
throw failure(`target ${quote(last)}`, new FsError(target ? "ENOTDIR" : "ENOENT"));
|
|
597
|
+
}
|
|
598
|
+
const modes = parseMode(args.mode, args.modeBytes);
|
|
599
|
+
if (args.stripProgramSpecified && !args.strip)
|
|
600
|
+
await report("WARNING: ignoring --strip-program option as -s option was not specified");
|
|
601
|
+
if (args.compare && args.preserve)
|
|
602
|
+
throw new InstallError("options --compare (-C) and --preserve-timestamps are mutually exclusive", true);
|
|
603
|
+
if (args.compare && args.strip)
|
|
604
|
+
throw new InstallError("options --compare (-C) and --strip are mutually exclusive", true);
|
|
605
|
+
if (args.compare && (modes.file & 0o7000))
|
|
606
|
+
await report("the --compare (-C) option is ignored when you specify a mode with non-permission bits");
|
|
607
|
+
const uid = await account(context, args.owner, "user", settings), gid = await account(context, args.group, "group", settings);
|
|
608
|
+
if ((!context.fs.chmod && !settings.setMode) || context.fs.capabilities.permissions === false)
|
|
609
|
+
throw new InstallError("cannot set installation modes: Operation not supported");
|
|
610
|
+
if ((uid !== undefined || gid !== undefined) && !settings.chown)
|
|
611
|
+
throw new InstallError("cannot set ownership: Operation not supported (chown unavailable)");
|
|
612
|
+
if (args.strip && !settings.strip)
|
|
613
|
+
throw new InstallError("cannot strip files: Operation not supported (trusted strip hook unavailable)");
|
|
614
|
+
if (args.preserve && !args.directory && (!context.fs.utimes || context.fs.capabilities.timestamps === false))
|
|
615
|
+
throw new InstallError("cannot preserve timestamps: Operation not supported");
|
|
616
|
+
if (args.contextMode && !settings.securityContext)
|
|
617
|
+
throw new InstallError("cannot apply security context: Operation not supported (host profile unavailable)");
|
|
618
|
+
if (settings.securityContext?.enabled && !settings.securityContext.apply)
|
|
619
|
+
throw new InstallError("cannot apply security context: Operation not supported (context capability unavailable)");
|
|
620
|
+
const operation = { context, args, settings, uid, gid, modes, maxFileBytes };
|
|
621
|
+
let exitCode = 0;
|
|
622
|
+
if (args.directory) {
|
|
623
|
+
for (const directory of args.files) {
|
|
624
|
+
try {
|
|
625
|
+
await directories(operation, directory, true);
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
await report(error);
|
|
629
|
+
exitCode = 1;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
if (args.parents && targetDirectory !== undefined)
|
|
635
|
+
await directories(operation, targetDirectory, false);
|
|
636
|
+
const sources = targetDirectory === undefined ? args.files.slice(0, 1) : args.files;
|
|
637
|
+
const installed = new Map();
|
|
638
|
+
for (const source of sources) {
|
|
639
|
+
const destination = targetDirectory === undefined ? args.files[1] : `${targetDirectory.endsWith("/") ? targetDirectory : `${targetDirectory}/`}${basename(source)}`;
|
|
640
|
+
try {
|
|
641
|
+
const path = destination ? pathOf(context, destination) : "", previous = installed.get(path);
|
|
642
|
+
if (previous && args.backup !== "numbered") {
|
|
643
|
+
const current = await maybeStat(context, path);
|
|
644
|
+
if (current && await compareObservedEntries(context.fs, path, previous, context.fs, path, current, { signal: context.signal }) !== "distinct")
|
|
645
|
+
throw new InstallError(`will not overwrite just-created ${quote(destination)} with ${quote(source)}`);
|
|
646
|
+
}
|
|
647
|
+
await installFile(operation, source, destination);
|
|
648
|
+
const stat = await maybeStat(context, path);
|
|
649
|
+
if (stat)
|
|
650
|
+
installed.set(path, stat);
|
|
651
|
+
}
|
|
652
|
+
catch (error) {
|
|
653
|
+
await report(error);
|
|
654
|
+
exitCode = 1;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
context.signal.throwIfAborted();
|
|
659
|
+
return { exitCode };
|
|
660
|
+
}
|
|
661
|
+
catch (error) {
|
|
662
|
+
await report(error);
|
|
663
|
+
return { exitCode: 1 };
|
|
664
|
+
}
|
|
665
|
+
} };
|
|
666
|
+
}
|
|
667
|
+
export function createInstallCommands(options = {}) { return [createInstallCommand(options)]; }
|
|
668
|
+
export function installCommands(options = {}) {
|
|
669
|
+
const commands = createInstallCommands(options), replace = options.replace ?? false;
|
|
670
|
+
return { name: "install-commands", setup(host) {
|
|
671
|
+
if (!replace)
|
|
672
|
+
for (const command of commands)
|
|
673
|
+
if (host.commands.has(command.name))
|
|
674
|
+
throw new Error(`Command already registered: ${command.name}`);
|
|
675
|
+
for (const command of commands)
|
|
676
|
+
host.commands.register(command, { replace });
|
|
677
|
+
} };
|
|
678
|
+
}
|