command-stream 0.8.2 → 0.9.0
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/js/examples/01-basic-streaming.mjs +14 -0
- package/js/examples/02-async-iterator.mjs +9 -0
- package/js/examples/03-file-and-console.mjs +16 -0
- package/js/examples/04-claude-jq-pipe.mjs +16 -0
- package/js/examples/CI-DEBUG-README.md +138 -0
- package/js/examples/README-examples.mjs +111 -0
- package/js/examples/README.md +345 -0
- package/js/examples/STREAMING_INTERFACES_SUMMARY.md +116 -0
- package/js/examples/add-test-timeouts.js +107 -0
- package/js/examples/ansi-default-preserved.mjs +11 -0
- package/js/examples/ansi-global-config.mjs +12 -0
- package/js/examples/ansi-reset-default.mjs +12 -0
- package/js/examples/ansi-strip-utils.mjs +12 -0
- package/js/examples/baseline-child-process.mjs +23 -0
- package/js/examples/baseline-claude-test.mjs +47 -0
- package/js/examples/baseline-working.mjs +34 -0
- package/js/examples/capture-mirror-comparison.mjs +23 -0
- package/js/examples/capture-mirror-default.mjs +11 -0
- package/js/examples/capture-mirror-performance.mjs +16 -0
- package/js/examples/capture-mirror-show-only.mjs +16 -0
- package/js/examples/capture-mirror-silent-processing.mjs +16 -0
- package/js/examples/ci-debug-baseline-vs-library.mjs +265 -0
- package/js/examples/ci-debug-es-module-loading.mjs +184 -0
- package/js/examples/ci-debug-signal-handling.mjs +225 -0
- package/js/examples/ci-debug-stdout-buffering.mjs +94 -0
- package/js/examples/ci-debug-test-timeouts.mjs +259 -0
- package/js/examples/claude-exact-file-output.mjs +20 -0
- package/js/examples/claude-exact-jq.mjs +13 -0
- package/js/examples/claude-exact-streaming.mjs +13 -0
- package/js/examples/claude-jq-pipeline.mjs +13 -0
- package/js/examples/claude-json-stream.mjs +39 -0
- package/js/examples/claude-streaming-basic.mjs +99 -0
- package/js/examples/claude-streaming-demo.mjs +126 -0
- package/js/examples/claude-streaming-final.mjs +20 -0
- package/js/examples/cleanup-verification-test.mjs +115 -0
- package/js/examples/colors-buffer-processing.mjs +14 -0
- package/js/examples/colors-default-preserved.mjs +15 -0
- package/js/examples/colors-per-command-config.mjs +15 -0
- package/js/examples/colors-strip-ansi.mjs +13 -0
- package/js/examples/commandstream-jq.mjs +29 -0
- package/js/examples/commandstream-working.mjs +23 -0
- package/js/examples/comprehensive-streams-demo.mjs +121 -0
- package/js/examples/ctrl-c-concurrent-processes.mjs +20 -0
- package/js/examples/ctrl-c-long-running-command.mjs +20 -0
- package/js/examples/ctrl-c-real-system-command.mjs +17 -0
- package/js/examples/ctrl-c-sleep-command.mjs +17 -0
- package/js/examples/ctrl-c-stdin-forwarding.mjs +20 -0
- package/js/examples/ctrl-c-virtual-command.mjs +17 -0
- package/js/examples/debug-already-started.mjs +20 -0
- package/js/examples/debug-ansi-processing.mjs +42 -0
- package/js/examples/debug-basic-streaming.mjs +44 -0
- package/js/examples/debug-buildshellcommand.mjs +82 -0
- package/js/examples/debug-child-process.mjs +43 -0
- package/js/examples/debug-child-state.mjs +55 -0
- package/js/examples/debug-chunking.mjs +38 -0
- package/js/examples/debug-command-parsing.mjs +61 -0
- package/js/examples/debug-complete-consolidation.mjs +97 -0
- package/js/examples/debug-echo-args.mjs +22 -0
- package/js/examples/debug-emit-timing.mjs +28 -0
- package/js/examples/debug-end-event.mjs +56 -0
- package/js/examples/debug-errexit.mjs +16 -0
- package/js/examples/debug-event-emission.mjs +40 -0
- package/js/examples/debug-event-timing.mjs +67 -0
- package/js/examples/debug-event-vs-result.mjs +30 -0
- package/js/examples/debug-exact-command.mjs +28 -0
- package/js/examples/debug-exact-test-scenario.mjs +44 -0
- package/js/examples/debug-execution-path.mjs +27 -0
- package/js/examples/debug-exit-command.mjs +38 -0
- package/js/examples/debug-exit-virtual.mjs +54 -0
- package/js/examples/debug-finish-consolidation.mjs +44 -0
- package/js/examples/debug-force-cleanup.mjs +47 -0
- package/js/examples/debug-getter-basic.mjs +13 -0
- package/js/examples/debug-getter-direct.mjs +23 -0
- package/js/examples/debug-getter-internals.mjs +61 -0
- package/js/examples/debug-handler-detection.mjs +65 -0
- package/js/examples/debug-idempotent-finish.mjs +54 -0
- package/js/examples/debug-idempotent-kill.mjs +58 -0
- package/js/examples/debug-interpolation-individual.mjs +88 -0
- package/js/examples/debug-interpolation-issue.mjs +101 -0
- package/js/examples/debug-jq-streaming.mjs +57 -0
- package/js/examples/debug-jq-tty-colors.mjs +168 -0
- package/js/examples/debug-kill-cleanup.mjs +56 -0
- package/js/examples/debug-kill-method.mjs +33 -0
- package/js/examples/debug-listener-interference.mjs +38 -0
- package/js/examples/debug-listener-lifecycle.mjs +50 -0
- package/js/examples/debug-listener-timing.mjs +62 -0
- package/js/examples/debug-listeners-property.mjs +34 -0
- package/js/examples/debug-map-methods.mjs +39 -0
- package/js/examples/debug-not-awaited-cleanup.mjs +106 -0
- package/js/examples/debug-off-method.mjs +28 -0
- package/js/examples/debug-option-merging.mjs +17 -0
- package/js/examples/debug-options.mjs +36 -0
- package/js/examples/debug-output.mjs +25 -0
- package/js/examples/debug-pattern-matching.mjs +69 -0
- package/js/examples/debug-pipeline-cat.mjs +28 -0
- package/js/examples/debug-pipeline-cleanup.mjs +71 -0
- package/js/examples/debug-pipeline-error-detailed.mjs +78 -0
- package/js/examples/debug-pipeline-error.mjs +65 -0
- package/js/examples/debug-pipeline-issue.mjs +26 -0
- package/js/examples/debug-pipeline-method.mjs +22 -0
- package/js/examples/debug-pipeline-stream.mjs +66 -0
- package/js/examples/debug-pipeline.mjs +14 -0
- package/js/examples/debug-process-exit-trace.mjs +41 -0
- package/js/examples/debug-process-path.mjs +38 -0
- package/js/examples/debug-property-check.mjs +29 -0
- package/js/examples/debug-resource-cleanup.mjs +83 -0
- package/js/examples/debug-shell-args.mjs +103 -0
- package/js/examples/debug-sigint-child-handler.mjs +44 -0
- package/js/examples/debug-sigint-forwarding.mjs +61 -0
- package/js/examples/debug-sigint-handler-install.mjs +79 -0
- package/js/examples/debug-sigint-handler-order.mjs +85 -0
- package/js/examples/debug-sigint-listeners.mjs +48 -0
- package/js/examples/debug-sigint-start-pattern.mjs +62 -0
- package/js/examples/debug-sigint-timer.mjs +40 -0
- package/js/examples/debug-simple-command.mjs +49 -0
- package/js/examples/debug-simple-getter.mjs +30 -0
- package/js/examples/debug-simple.mjs +39 -0
- package/js/examples/debug-simplified-finished.mjs +102 -0
- package/js/examples/debug-stack-overflow.mjs +25 -0
- package/js/examples/debug-stdin-simple.mjs +28 -0
- package/js/examples/debug-stdin.mjs +28 -0
- package/js/examples/debug-stream-emitter-isolated.mjs +45 -0
- package/js/examples/debug-stream-emitter.mjs +25 -0
- package/js/examples/debug-stream-events.mjs +70 -0
- package/js/examples/debug-stream-generator.mjs +23 -0
- package/js/examples/debug-stream-getter-issue.mjs +37 -0
- package/js/examples/debug-stream-getter.mjs +19 -0
- package/js/examples/debug-stream-internals.mjs +64 -0
- package/js/examples/debug-stream-method.mjs +46 -0
- package/js/examples/debug-stream-object.mjs +58 -0
- package/js/examples/debug-stream-properties.mjs +37 -0
- package/js/examples/debug-stream-timing.mjs +28 -0
- package/js/examples/debug-streaming.mjs +24 -0
- package/js/examples/debug-test-isolation.mjs +54 -0
- package/js/examples/debug-test-state.mjs +27 -0
- package/js/examples/debug-user-sigint.mjs +19 -0
- package/js/examples/debug-virtual-disable.mjs +21 -0
- package/js/examples/debug-virtual-vs-real.mjs +68 -0
- package/js/examples/debug-with-trace.mjs +23 -0
- package/js/examples/debug_parent_stream.mjs +22 -0
- package/js/examples/emulate-claude-stream.mjs +30 -0
- package/js/examples/emulated-streaming-direct.mjs +22 -0
- package/js/examples/emulated-streaming-jq-pipe.mjs +22 -0
- package/js/examples/emulated-streaming-sh-pipe.mjs +23 -0
- package/js/examples/events-build-process.mjs +73 -0
- package/js/examples/events-concurrent-streams.mjs +51 -0
- package/js/examples/events-error-handling.mjs +59 -0
- package/js/examples/events-file-monitoring.mjs +66 -0
- package/js/examples/events-interactive-simulation.mjs +69 -0
- package/js/examples/events-log-processing.mjs +72 -0
- package/js/examples/events-network-monitoring.mjs +68 -0
- package/js/examples/events-ping-basic.mjs +37 -0
- package/js/examples/events-progress-tracking.mjs +55 -0
- package/js/examples/events-stdin-input.mjs +34 -0
- package/js/examples/example-ansi-ls.mjs +39 -0
- package/js/examples/example-top.mjs +27 -0
- package/js/examples/final-ping-stdin-proof.mjs +88 -0
- package/js/examples/final-test-shell-operators.mjs +123 -0
- package/js/examples/final-working-examples.mjs +162 -0
- package/js/examples/gh-auth-test.mjs +103 -0
- package/js/examples/gh-delete-hang-test.mjs +79 -0
- package/js/examples/gh-gist-creation-test.mjs +276 -0
- package/js/examples/gh-gist-minimal-test.mjs +89 -0
- package/js/examples/gh-hang-exact-original.mjs +83 -0
- package/js/examples/gh-hang-reproduction.mjs +151 -0
- package/js/examples/gh-hang-test-with-redirect.mjs +45 -0
- package/js/examples/gh-hang-test-without-redirect.mjs +43 -0
- package/js/examples/gh-minimal-hang-check.mjs +33 -0
- package/js/examples/gh-operations-with-cd.mjs +187 -0
- package/js/examples/gh-output-test.mjs +102 -0
- package/js/examples/gh-reproduce-hang.mjs +41 -0
- package/js/examples/git-operations-with-cd.mjs +186 -0
- package/js/examples/interactive-math-calc.mjs +45 -0
- package/js/examples/interactive-top-fixed.mjs +25 -0
- package/js/examples/interactive-top-improved.mjs +72 -0
- package/js/examples/interactive-top-pty-logging.mjs +69 -0
- package/js/examples/interactive-top-pty.mjs +27 -0
- package/js/examples/interactive-top-with-logging.mjs +56 -0
- package/js/examples/interactive-top.mjs +29 -0
- package/js/examples/jq-color-demo.mjs +53 -0
- package/js/examples/jq-colors-streaming.mjs +42 -0
- package/js/examples/manual-ctrl-c-test.mjs +50 -0
- package/js/examples/methods-multiple-options.mjs +25 -0
- package/js/examples/methods-run-basic.mjs +10 -0
- package/js/examples/methods-start-basic.mjs +10 -0
- package/js/examples/node-compat-data-events.mjs +36 -0
- package/js/examples/node-compat-readable-event.mjs +29 -0
- package/js/examples/node-compat-small-buffer.mjs +33 -0
- package/js/examples/options-capture-false.mjs +12 -0
- package/js/examples/options-combined-settings.mjs +13 -0
- package/js/examples/options-custom-input.mjs +16 -0
- package/js/examples/options-default-behavior.mjs +10 -0
- package/js/examples/options-maximum-performance.mjs +15 -0
- package/js/examples/options-mirror-false.mjs +10 -0
- package/js/examples/options-performance-mode.mjs +13 -0
- package/js/examples/options-performance-optimization.mjs +14 -0
- package/js/examples/options-run-alias-demo.mjs +15 -0
- package/js/examples/options-run-alias.mjs +10 -0
- package/js/examples/options-silent-execution.mjs +14 -0
- package/js/examples/options-streaming-capture.mjs +24 -0
- package/js/examples/options-streaming-multiple.mjs +35 -0
- package/js/examples/options-streaming-silent.mjs +21 -0
- package/js/examples/options-streaming-stdin.mjs +21 -0
- package/js/examples/ping-streaming-filtered.mjs +22 -0
- package/js/examples/ping-streaming-interruptible.mjs +47 -0
- package/js/examples/ping-streaming-silent.mjs +24 -0
- package/js/examples/ping-streaming-simple.mjs +13 -0
- package/js/examples/ping-streaming-statistics.mjs +49 -0
- package/js/examples/ping-streaming-timestamps.mjs +22 -0
- package/js/examples/ping-streaming.mjs +48 -0
- package/js/examples/prove-ping-stdin-limitation.mjs +94 -0
- package/js/examples/readme-example.mjs +39 -0
- package/js/examples/realtime-json-stream.mjs +143 -0
- package/js/examples/reliable-stdin-commands.mjs +135 -0
- package/js/examples/reproduce-issue-135-v2.mjs +15 -0
- package/js/examples/reproduce-issue-135.mjs +17 -0
- package/js/examples/shell-cd-behavior.mjs +88 -0
- package/js/examples/sigint-forwarding-test.mjs +60 -0
- package/js/examples/sigint-handler-test.mjs +72 -0
- package/js/examples/simple-async-test.mjs +49 -0
- package/js/examples/simple-claude-test.mjs +17 -0
- package/js/examples/simple-event-test.mjs +33 -0
- package/js/examples/simple-jq-streaming.mjs +48 -0
- package/js/examples/simple-stream-demo.mjs +35 -0
- package/js/examples/simple-test-sleep.js +30 -0
- package/js/examples/simple-working-stdin.mjs +30 -0
- package/js/examples/streaming-behavior-test.mjs +116 -0
- package/js/examples/streaming-direct-command.mjs +21 -0
- package/js/examples/streaming-filtered-output.mjs +33 -0
- package/js/examples/streaming-grep-pipeline.mjs +21 -0
- package/js/examples/streaming-interactive-stdin.mjs +24 -0
- package/js/examples/streaming-jq-pipeline.mjs +23 -0
- package/js/examples/streaming-multistage-pipeline.mjs +23 -0
- package/js/examples/streaming-pipes-event-pattern.mjs +27 -0
- package/js/examples/streaming-pipes-multistage.mjs +22 -0
- package/js/examples/streaming-pipes-realtime-jq.mjs +23 -0
- package/js/examples/streaming-progress-tracking.mjs +34 -0
- package/js/examples/streaming-reusable-configs.mjs +52 -0
- package/js/examples/streaming-silent-capture.mjs +20 -0
- package/js/examples/streaming-test-simple.mjs +70 -0
- package/js/examples/streaming-virtual-pipeline.mjs +18 -0
- package/js/examples/syntax-basic-comparison.mjs +31 -0
- package/js/examples/syntax-basic-options.mjs +12 -0
- package/js/examples/syntax-combined-options.mjs +19 -0
- package/js/examples/syntax-command-chaining.mjs +12 -0
- package/js/examples/syntax-custom-directory.mjs +10 -0
- package/js/examples/syntax-custom-environment.mjs +13 -0
- package/js/examples/syntax-custom-stdin.mjs +10 -0
- package/js/examples/syntax-mixed-regular.mjs +11 -0
- package/js/examples/syntax-mixed-usage.mjs +15 -0
- package/js/examples/syntax-multiple-listeners.mjs +87 -0
- package/js/examples/syntax-piping-comparison.mjs +32 -0
- package/js/examples/syntax-reusable-config.mjs +16 -0
- package/js/examples/syntax-reusable-configs.mjs +21 -0
- package/js/examples/syntax-silent-operations.mjs +10 -0
- package/js/examples/syntax-stdin-option.mjs +12 -0
- package/js/examples/temp-sigint-test.mjs +21 -0
- package/js/examples/test-actual-buildshell.mjs +44 -0
- package/js/examples/test-async-streams-working.mjs +102 -0
- package/js/examples/test-async-streams.mjs +90 -0
- package/js/examples/test-auth-parse.mjs +74 -0
- package/js/examples/test-auto-quoting.mjs +57 -0
- package/js/examples/test-auto-start-fix.mjs +95 -0
- package/js/examples/test-baseline-sigint.mjs +38 -0
- package/js/examples/test-buffer-behavior.mjs +39 -0
- package/js/examples/test-buffers-simple.mjs +35 -0
- package/js/examples/test-bun-specific-issue.mjs +106 -0
- package/js/examples/test-bun-streaming.mjs +81 -0
- package/js/examples/test-cat-direct.mjs +41 -0
- package/js/examples/test-cat-pipe.mjs +34 -0
- package/js/examples/test-cd-behavior.mjs +42 -0
- package/js/examples/test-child-process-timing.mjs +53 -0
- package/js/examples/test-child-sigint-handler.mjs +62 -0
- package/js/examples/test-cleanup-simple.mjs +21 -0
- package/js/examples/test-comprehensive-tracing.mjs +58 -0
- package/js/examples/test-correct-space-handling.mjs +46 -0
- package/js/examples/test-ctrl-c-debug.mjs +44 -0
- package/js/examples/test-ctrl-c-inherit.mjs +30 -0
- package/js/examples/test-ctrl-c-sleep.mjs +31 -0
- package/js/examples/test-ctrl-c.mjs +17 -0
- package/js/examples/test-debug-new-options.mjs +55 -0
- package/js/examples/test-debug-pty.mjs +49 -0
- package/js/examples/test-debug-tee.mjs +38 -0
- package/js/examples/test-debug.mjs +25 -0
- package/js/examples/test-direct-jq.mjs +47 -0
- package/js/examples/test-direct-pipe-reading.mjs +119 -0
- package/js/examples/test-direct-pipe.sh +28 -0
- package/js/examples/test-double-quoting-prevention.mjs +138 -0
- package/js/examples/test-edge-cases-quoting.mjs +89 -0
- package/js/examples/test-events.mjs +37 -0
- package/js/examples/test-explicit-stdio.mjs +51 -0
- package/js/examples/test-final-streaming.mjs +71 -0
- package/js/examples/test-fix.mjs +71 -0
- package/js/examples/test-incremental-streaming.mjs +46 -0
- package/js/examples/test-individual-spawn.mjs +35 -0
- package/js/examples/test-inherit-stdout-not-stdin.mjs +133 -0
- package/js/examples/test-injection-protection.mjs +77 -0
- package/js/examples/test-interactive-streaming.mjs +140 -0
- package/js/examples/test-interactive-top.md +24 -0
- package/js/examples/test-interactive.mjs +17 -0
- package/js/examples/test-interpolation.mjs +14 -0
- package/js/examples/test-interrupt.mjs +40 -0
- package/js/examples/test-issue-135-comprehensive.mjs +41 -0
- package/js/examples/test-issue12-detailed.mjs +89 -0
- package/js/examples/test-issue12-exact.mjs +33 -0
- package/js/examples/test-jq-color.mjs +57 -0
- package/js/examples/test-jq-colors.mjs +41 -0
- package/js/examples/test-jq-compact.mjs +33 -0
- package/js/examples/test-jq-native.sh +10 -0
- package/js/examples/test-jq-pipeline-behavior.mjs +80 -0
- package/js/examples/test-jq-realtime.mjs +40 -0
- package/js/examples/test-manual-start.mjs +54 -0
- package/js/examples/test-mixed-quoting.mjs +88 -0
- package/js/examples/test-multi-stream.mjs +50 -0
- package/js/examples/test-multistage-debug.mjs +44 -0
- package/js/examples/test-native-spawn-vs-command-stream.mjs +154 -0
- package/js/examples/test-no-parse-pipeline.mjs +33 -0
- package/js/examples/test-non-virtual.mjs +52 -0
- package/js/examples/test-operators.mjs +53 -0
- package/js/examples/test-parent-continues.mjs +44 -0
- package/js/examples/test-path-interpolation.mjs +86 -0
- package/js/examples/test-ping-kill-and-stdin.mjs +98 -0
- package/js/examples/test-ping.mjs +12 -0
- package/js/examples/test-pty-spawn.mjs +101 -0
- package/js/examples/test-pty.mjs +38 -0
- package/js/examples/test-quote-behavior-summary.mjs +110 -0
- package/js/examples/test-quote-edge-cases.mjs +69 -0
- package/js/examples/test-quote-parsing.mjs +23 -0
- package/js/examples/test-raw-function.mjs +153 -0
- package/js/examples/test-raw-streaming.mjs +47 -0
- package/js/examples/test-readme-examples.mjs +142 -0
- package/js/examples/test-real-cat.mjs +28 -0
- package/js/examples/test-real-commands.mjs +21 -0
- package/js/examples/test-real-shell.mjs +31 -0
- package/js/examples/test-real-stdin-commands.mjs +160 -0
- package/js/examples/test-runner-batched.mjs +98 -0
- package/js/examples/test-runner-simple.mjs +80 -0
- package/js/examples/test-runner.mjs +67 -0
- package/js/examples/test-scope-parse.mjs +31 -0
- package/js/examples/test-sh-pipeline.mjs +24 -0
- package/js/examples/test-shell-detection.mjs +71 -0
- package/js/examples/test-shell-parser.mjs +37 -0
- package/js/examples/test-sigint-behavior.mjs +241 -0
- package/js/examples/test-sigint-handling.sh +14 -0
- package/js/examples/test-simple-pipe.mjs +12 -0
- package/js/examples/test-simple-streaming.mjs +32 -0
- package/js/examples/test-sleep-stdin.js +27 -0
- package/js/examples/test-sleep.mjs +56 -0
- package/js/examples/test-smart-quoting.mjs +180 -0
- package/js/examples/test-spaces-in-path.mjs +48 -0
- package/js/examples/test-special-chars-quoting.mjs +54 -0
- package/js/examples/test-stdin-after-start.mjs +39 -0
- package/js/examples/test-stdin-simple.mjs +67 -0
- package/js/examples/test-stdin-timing.mjs +74 -0
- package/js/examples/test-stdio-combinations.mjs +124 -0
- package/js/examples/test-stream-access.mjs +84 -0
- package/js/examples/test-stream-cleanup.mjs +27 -0
- package/js/examples/test-stream-readers.mjs +152 -0
- package/js/examples/test-streaming-final.mjs +57 -0
- package/js/examples/test-streaming-interfaces.mjs +141 -0
- package/js/examples/test-streaming-timing.mjs +27 -0
- package/js/examples/test-streaming.mjs +32 -0
- package/js/examples/test-streams-stdin-comprehensive.mjs +134 -0
- package/js/examples/test-streams-stdin-ctrl-c.mjs +96 -0
- package/js/examples/test-template-literal.mjs +26 -0
- package/js/examples/test-template-vs-interpolation.mjs +49 -0
- package/js/examples/test-timing.mjs +41 -0
- package/js/examples/test-top-inherit-stdout-stdin-control.mjs +123 -0
- package/js/examples/test-top-quit-stdin.mjs +118 -0
- package/js/examples/test-trace-option.mjs +21 -0
- package/js/examples/test-user-double-quotes.mjs +36 -0
- package/js/examples/test-user-single-quotes.mjs +36 -0
- package/js/examples/test-verbose.mjs +18 -0
- package/js/examples/test-verbose2.mjs +32 -0
- package/js/examples/test-virtual-streaming.mjs +125 -0
- package/js/examples/test-waiting-command.mjs +52 -0
- package/js/examples/test-waiting-commands.mjs +83 -0
- package/js/examples/test-watch-mode.mjs +104 -0
- package/js/examples/test-yes-cancellation.mjs +26 -0
- package/js/examples/test-yes-detailed.mjs +58 -0
- package/js/examples/test-yes-trace.mjs +28 -0
- package/js/examples/trace-abort-controller.mjs +30 -0
- package/js/examples/trace-error-handling.mjs +22 -0
- package/js/examples/trace-pipeline-command.mjs +22 -0
- package/js/examples/trace-signal-handling.mjs +35 -0
- package/js/examples/trace-simple-command.mjs +18 -0
- package/js/examples/trace-stderr-output.mjs +22 -0
- package/js/examples/verify-fix-both-runtimes.mjs +73 -0
- package/js/examples/verify-issue12-fixed.mjs +78 -0
- package/js/examples/which-command-common-commands.mjs +19 -0
- package/js/examples/which-command-gh-test.mjs +23 -0
- package/js/examples/which-command-nonexistent.mjs +20 -0
- package/js/examples/which-command-system-comparison.mjs +28 -0
- package/js/examples/working-example.mjs +13 -0
- package/js/examples/working-stdin-examples.mjs +138 -0
- package/js/examples/working-streaming-demo.mjs +49 -0
- package/{src → js/src}/$.mjs +20 -4
- package/{src → js/src}/$.utils.mjs +14 -2
- package/js/tests/$.features.test.mjs +283 -0
- package/js/tests/$.test.mjs +935 -0
- package/js/tests/builtin-commands.test.mjs +387 -0
- package/js/tests/bun-shell-path-fix.test.mjs +115 -0
- package/js/tests/bun.features.test.mjs +189 -0
- package/js/tests/cd-virtual-command.test.mjs +622 -0
- package/js/tests/cleanup-verification.test.mjs +127 -0
- package/js/tests/ctrl-c-baseline.test.mjs +207 -0
- package/js/tests/ctrl-c-basic.test.mjs +220 -0
- package/js/tests/ctrl-c-library.test.mjs +197 -0
- package/js/tests/ctrl-c-signal.test.mjs +915 -0
- package/js/tests/examples.test.mjs +252 -0
- package/js/tests/execa.features.test.mjs +198 -0
- package/js/tests/gh-commands.test.mjs +164 -0
- package/js/tests/gh-gist-operations.test.mjs +221 -0
- package/js/tests/git-gh-cd.test.mjs +466 -0
- package/js/tests/interactive-option.test.mjs +114 -0
- package/js/tests/interactive-streaming.test.mjs +307 -0
- package/js/tests/issue-135-final.test.mjs +58 -0
- package/js/tests/jq-color-behavior.test.mjs +140 -0
- package/js/tests/jq.test.mjs +318 -0
- package/js/tests/options-examples.test.mjs +106 -0
- package/js/tests/options-syntax.test.mjs +112 -0
- package/js/tests/path-interpolation.test.mjs +412 -0
- package/js/tests/pipe.test.mjs +291 -0
- package/js/tests/raw-function.test.mjs +266 -0
- package/js/tests/readme-examples.test.mjs +427 -0
- package/js/tests/resource-cleanup-internals.test.mjs +669 -0
- package/js/tests/shell-settings.test.mjs +279 -0
- package/js/tests/sigint-cleanup-isolated.test.mjs +151 -0
- package/js/tests/sigint-cleanup.test.mjs +118 -0
- package/js/tests/start-run-edge-cases.test.mjs +152 -0
- package/js/tests/start-run-options.test.mjs +181 -0
- package/js/tests/stderr-output-handling.test.mjs +279 -0
- package/js/tests/streaming-interfaces.test.mjs +194 -0
- package/js/tests/sync.test.mjs +297 -0
- package/js/tests/system-pipe.test.mjs +226 -0
- package/js/tests/test-cleanup.mjs +200 -0
- package/js/tests/test-helper-fixed.mjs +148 -0
- package/js/tests/test-helper-v2.mjs +118 -0
- package/js/tests/test-helper.mjs +171 -0
- package/js/tests/test-sigint-child.js +15 -0
- package/js/tests/text-method.test.mjs +225 -0
- package/js/tests/virtual.test.mjs +364 -0
- package/js/tests/yes-command-cleanup.test.mjs +208 -0
- package/js/tests/zx.features.test.mjs +233 -0
- package/package.json +13 -12
- package/rust/Cargo.lock +947 -0
- package/rust/Cargo.toml +47 -0
- package/rust/src/commands/basename.rs +69 -0
- package/rust/src/commands/cat.rs +123 -0
- package/rust/src/commands/cd.rs +67 -0
- package/rust/src/commands/cp.rs +187 -0
- package/rust/src/commands/dirname.rs +57 -0
- package/rust/src/commands/echo.rs +73 -0
- package/rust/src/commands/env.rs +33 -0
- package/rust/src/commands/exit.rs +36 -0
- package/rust/src/commands/false.rs +24 -0
- package/rust/src/commands/ls.rs +182 -0
- package/rust/src/commands/mkdir.rs +98 -0
- package/rust/src/commands/mod.rs +200 -0
- package/rust/src/commands/mv.rs +180 -0
- package/rust/src/commands/pwd.rs +28 -0
- package/rust/src/commands/rm.rs +150 -0
- package/rust/src/commands/seq.rs +179 -0
- package/rust/src/commands/sleep.rs +97 -0
- package/rust/src/commands/test.rs +204 -0
- package/rust/src/commands/touch.rs +99 -0
- package/rust/src/commands/true.rs +24 -0
- package/rust/src/commands/which.rs +87 -0
- package/rust/src/commands/yes.rs +99 -0
- package/rust/src/lib.rs +492 -0
- package/rust/src/main.rs +37 -0
- package/rust/src/shell_parser.rs +565 -0
- package/rust/src/utils.rs +335 -0
- package/rust/tests/builtin_commands.rs +549 -0
- package/rust/tests/process_runner.rs +286 -0
- package/rust/tests/shell_parser.rs +296 -0
- package/rust/tests/utils.rs +282 -0
- package/rust/tests/virtual_commands.rs +199 -0
- /package/{src → js/src}/commands/$.basename.mjs +0 -0
- /package/{src → js/src}/commands/$.cat.mjs +0 -0
- /package/{src → js/src}/commands/$.cd.mjs +0 -0
- /package/{src → js/src}/commands/$.cp.mjs +0 -0
- /package/{src → js/src}/commands/$.dirname.mjs +0 -0
- /package/{src → js/src}/commands/$.echo.mjs +0 -0
- /package/{src → js/src}/commands/$.env.mjs +0 -0
- /package/{src → js/src}/commands/$.exit.mjs +0 -0
- /package/{src → js/src}/commands/$.false.mjs +0 -0
- /package/{src → js/src}/commands/$.ls.mjs +0 -0
- /package/{src → js/src}/commands/$.mkdir.mjs +0 -0
- /package/{src → js/src}/commands/$.mv.mjs +0 -0
- /package/{src → js/src}/commands/$.pwd.mjs +0 -0
- /package/{src → js/src}/commands/$.rm.mjs +0 -0
- /package/{src → js/src}/commands/$.seq.mjs +0 -0
- /package/{src → js/src}/commands/$.sleep.mjs +0 -0
- /package/{src → js/src}/commands/$.test.mjs +0 -0
- /package/{src → js/src}/commands/$.touch.mjs +0 -0
- /package/{src → js/src}/commands/$.true.mjs +0 -0
- /package/{src → js/src}/commands/$.which.mjs +0 -0
- /package/{src → js/src}/commands/$.yes.mjs +0 -0
- /package/{src → js/src}/shell-parser.mjs +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//! Virtual `false` command implementation
|
|
2
|
+
|
|
3
|
+
use crate::commands::CommandContext;
|
|
4
|
+
use crate::utils::CommandResult;
|
|
5
|
+
|
|
6
|
+
/// Execute the false command
|
|
7
|
+
///
|
|
8
|
+
/// Always returns failure (exit code 1).
|
|
9
|
+
pub async fn r#false(_ctx: CommandContext) -> CommandResult {
|
|
10
|
+
CommandResult::error_with_code("", 1)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[cfg(test)]
|
|
14
|
+
mod tests {
|
|
15
|
+
use super::*;
|
|
16
|
+
|
|
17
|
+
#[tokio::test]
|
|
18
|
+
async fn test_false() {
|
|
19
|
+
let ctx = CommandContext::new(vec![]);
|
|
20
|
+
let result = r#false(ctx).await;
|
|
21
|
+
assert!(!result.is_success());
|
|
22
|
+
assert_eq!(result.code, 1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
//! Virtual `ls` command implementation
|
|
2
|
+
|
|
3
|
+
use crate::commands::CommandContext;
|
|
4
|
+
use crate::utils::{trace_lazy, CommandResult};
|
|
5
|
+
use std::fs;
|
|
6
|
+
use std::path::Path;
|
|
7
|
+
|
|
8
|
+
/// Execute the ls command
|
|
9
|
+
///
|
|
10
|
+
/// Lists directory contents.
|
|
11
|
+
pub async fn ls(ctx: CommandContext) -> CommandResult {
|
|
12
|
+
// Parse flags
|
|
13
|
+
let mut show_all = false;
|
|
14
|
+
let mut long_format = false;
|
|
15
|
+
let mut paths = Vec::new();
|
|
16
|
+
|
|
17
|
+
for arg in &ctx.args {
|
|
18
|
+
if arg == "-a" || arg == "--all" {
|
|
19
|
+
show_all = true;
|
|
20
|
+
} else if arg == "-l" {
|
|
21
|
+
long_format = true;
|
|
22
|
+
} else if arg == "-la" || arg == "-al" {
|
|
23
|
+
show_all = true;
|
|
24
|
+
long_format = true;
|
|
25
|
+
} else if arg.starts_with('-') {
|
|
26
|
+
if arg.contains('a') {
|
|
27
|
+
show_all = true;
|
|
28
|
+
}
|
|
29
|
+
if arg.contains('l') {
|
|
30
|
+
long_format = true;
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
paths.push(arg.clone());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Default to current directory
|
|
38
|
+
if paths.is_empty() {
|
|
39
|
+
paths.push(".".to_string());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let cwd = ctx.get_cwd();
|
|
43
|
+
let mut outputs = Vec::new();
|
|
44
|
+
|
|
45
|
+
for path_str in paths {
|
|
46
|
+
let resolved_path = if Path::new(&path_str).is_absolute() {
|
|
47
|
+
Path::new(&path_str).to_path_buf()
|
|
48
|
+
} else {
|
|
49
|
+
cwd.join(&path_str)
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
trace_lazy("VirtualCommand", || {
|
|
53
|
+
format!("ls: listing {:?}", resolved_path)
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if !resolved_path.exists() {
|
|
57
|
+
return CommandResult::error(format!(
|
|
58
|
+
"ls: cannot access '{}': No such file or directory\n",
|
|
59
|
+
path_str
|
|
60
|
+
));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if resolved_path.is_file() {
|
|
64
|
+
outputs.push(format_entry(&resolved_path, long_format));
|
|
65
|
+
} else {
|
|
66
|
+
match fs::read_dir(&resolved_path) {
|
|
67
|
+
Ok(entries) => {
|
|
68
|
+
let mut entry_strs = Vec::new();
|
|
69
|
+
|
|
70
|
+
for entry in entries {
|
|
71
|
+
if let Ok(entry) = entry {
|
|
72
|
+
let name = entry.file_name().to_string_lossy().to_string();
|
|
73
|
+
|
|
74
|
+
// Skip hidden files unless -a is specified
|
|
75
|
+
if !show_all && name.starts_with('.') {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if long_format {
|
|
80
|
+
entry_strs.push(format_entry(&entry.path(), true));
|
|
81
|
+
} else {
|
|
82
|
+
entry_strs.push(name);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
entry_strs.sort();
|
|
88
|
+
outputs.push(entry_strs.join("\n"));
|
|
89
|
+
}
|
|
90
|
+
Err(e) => {
|
|
91
|
+
return CommandResult::error(format!("ls: cannot open '{}': {}\n", path_str, e));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let output = outputs.join("\n");
|
|
98
|
+
if output.is_empty() {
|
|
99
|
+
CommandResult::success_empty()
|
|
100
|
+
} else {
|
|
101
|
+
CommandResult::success(format!("{}\n", output))
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn format_entry(path: &Path, long_format: bool) -> String {
|
|
106
|
+
let name = path.file_name()
|
|
107
|
+
.map(|n| n.to_string_lossy().to_string())
|
|
108
|
+
.unwrap_or_else(|| path.display().to_string());
|
|
109
|
+
|
|
110
|
+
if !long_format {
|
|
111
|
+
return name;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Long format: permissions, links, owner, group, size, date, name
|
|
115
|
+
let metadata = match fs::metadata(path) {
|
|
116
|
+
Ok(m) => m,
|
|
117
|
+
Err(_) => return name,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let file_type = if metadata.is_dir() { "d" } else { "-" };
|
|
121
|
+
let size = metadata.len();
|
|
122
|
+
|
|
123
|
+
// Simplified permissions
|
|
124
|
+
let perms = if metadata.is_dir() {
|
|
125
|
+
"drwxr-xr-x"
|
|
126
|
+
} else {
|
|
127
|
+
"-rw-r--r--"
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
format!("{} {:>8} {}", perms, size, name)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#[cfg(test)]
|
|
134
|
+
mod tests {
|
|
135
|
+
use super::*;
|
|
136
|
+
use tempfile::tempdir;
|
|
137
|
+
|
|
138
|
+
#[tokio::test]
|
|
139
|
+
async fn test_ls_current_dir() {
|
|
140
|
+
let ctx = CommandContext::new(vec![]);
|
|
141
|
+
let result = ls(ctx).await;
|
|
142
|
+
assert!(result.is_success());
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#[tokio::test]
|
|
146
|
+
async fn test_ls_with_path() {
|
|
147
|
+
let temp = tempdir().unwrap();
|
|
148
|
+
fs::write(temp.path().join("file.txt"), "test").unwrap();
|
|
149
|
+
|
|
150
|
+
let ctx = CommandContext::new(vec![
|
|
151
|
+
temp.path().to_string_lossy().to_string()
|
|
152
|
+
]);
|
|
153
|
+
let result = ls(ctx).await;
|
|
154
|
+
|
|
155
|
+
assert!(result.is_success());
|
|
156
|
+
assert!(result.stdout.contains("file.txt"));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#[tokio::test]
|
|
160
|
+
async fn test_ls_hidden_files() {
|
|
161
|
+
let temp = tempdir().unwrap();
|
|
162
|
+
fs::write(temp.path().join(".hidden"), "test").unwrap();
|
|
163
|
+
fs::write(temp.path().join("visible"), "test").unwrap();
|
|
164
|
+
|
|
165
|
+
// Without -a
|
|
166
|
+
let ctx = CommandContext::new(vec![
|
|
167
|
+
temp.path().to_string_lossy().to_string()
|
|
168
|
+
]);
|
|
169
|
+
let result = ls(ctx).await;
|
|
170
|
+
assert!(!result.stdout.contains(".hidden"));
|
|
171
|
+
assert!(result.stdout.contains("visible"));
|
|
172
|
+
|
|
173
|
+
// With -a
|
|
174
|
+
let ctx = CommandContext::new(vec![
|
|
175
|
+
"-a".to_string(),
|
|
176
|
+
temp.path().to_string_lossy().to_string(),
|
|
177
|
+
]);
|
|
178
|
+
let result = ls(ctx).await;
|
|
179
|
+
assert!(result.stdout.contains(".hidden"));
|
|
180
|
+
assert!(result.stdout.contains("visible"));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//! Virtual `mkdir` command implementation
|
|
2
|
+
|
|
3
|
+
use crate::commands::CommandContext;
|
|
4
|
+
use crate::utils::{trace_lazy, CommandResult, VirtualUtils};
|
|
5
|
+
use std::fs;
|
|
6
|
+
|
|
7
|
+
/// Execute the mkdir command
|
|
8
|
+
///
|
|
9
|
+
/// Creates directories.
|
|
10
|
+
pub async fn mkdir(ctx: CommandContext) -> CommandResult {
|
|
11
|
+
if ctx.args.is_empty() {
|
|
12
|
+
return VirtualUtils::missing_operand_error("mkdir");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check for -p flag
|
|
16
|
+
let mut create_parents = false;
|
|
17
|
+
let mut dirs = Vec::new();
|
|
18
|
+
|
|
19
|
+
for arg in &ctx.args {
|
|
20
|
+
if arg == "-p" {
|
|
21
|
+
create_parents = true;
|
|
22
|
+
} else if arg.starts_with('-') {
|
|
23
|
+
// Skip other flags
|
|
24
|
+
} else {
|
|
25
|
+
dirs.push(arg.clone());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if dirs.is_empty() {
|
|
30
|
+
return VirtualUtils::missing_operand_error("mkdir");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let cwd = ctx.get_cwd();
|
|
34
|
+
|
|
35
|
+
for dir in dirs {
|
|
36
|
+
let resolved_path = VirtualUtils::resolve_path(&dir, Some(&cwd));
|
|
37
|
+
|
|
38
|
+
trace_lazy("VirtualCommand", || {
|
|
39
|
+
format!("mkdir: creating directory {:?}, parents: {}", resolved_path, create_parents)
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
let result = if create_parents {
|
|
43
|
+
fs::create_dir_all(&resolved_path)
|
|
44
|
+
} else {
|
|
45
|
+
fs::create_dir(&resolved_path)
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if let Err(e) = result {
|
|
49
|
+
return CommandResult::error(format!("mkdir: cannot create directory '{}': {}\n", dir, e));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
CommandResult::success_empty()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[cfg(test)]
|
|
57
|
+
mod tests {
|
|
58
|
+
use super::*;
|
|
59
|
+
use tempfile::tempdir;
|
|
60
|
+
|
|
61
|
+
#[tokio::test]
|
|
62
|
+
async fn test_mkdir_simple() {
|
|
63
|
+
let temp = tempdir().unwrap();
|
|
64
|
+
let new_dir = temp.path().join("new_directory");
|
|
65
|
+
|
|
66
|
+
let ctx = CommandContext::new(vec![
|
|
67
|
+
new_dir.to_string_lossy().to_string()
|
|
68
|
+
]);
|
|
69
|
+
let result = mkdir(ctx).await;
|
|
70
|
+
|
|
71
|
+
assert!(result.is_success());
|
|
72
|
+
assert!(new_dir.exists());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[tokio::test]
|
|
76
|
+
async fn test_mkdir_with_parents() {
|
|
77
|
+
let temp = tempdir().unwrap();
|
|
78
|
+
let nested_dir = temp.path().join("a/b/c/d");
|
|
79
|
+
|
|
80
|
+
let ctx = CommandContext::new(vec![
|
|
81
|
+
"-p".to_string(),
|
|
82
|
+
nested_dir.to_string_lossy().to_string(),
|
|
83
|
+
]);
|
|
84
|
+
let result = mkdir(ctx).await;
|
|
85
|
+
|
|
86
|
+
assert!(result.is_success());
|
|
87
|
+
assert!(nested_dir.exists());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[tokio::test]
|
|
91
|
+
async fn test_mkdir_missing_operand() {
|
|
92
|
+
let ctx = CommandContext::new(vec![]);
|
|
93
|
+
let result = mkdir(ctx).await;
|
|
94
|
+
|
|
95
|
+
assert!(!result.is_success());
|
|
96
|
+
assert!(result.stderr.contains("missing operand"));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
//! Virtual command implementations
|
|
2
|
+
//!
|
|
3
|
+
//! This module contains implementations of shell commands that run in-process
|
|
4
|
+
//! without spawning external processes. These provide faster execution and
|
|
5
|
+
//! consistent behavior across platforms.
|
|
6
|
+
|
|
7
|
+
mod cat;
|
|
8
|
+
mod cd;
|
|
9
|
+
mod echo;
|
|
10
|
+
mod pwd;
|
|
11
|
+
mod sleep;
|
|
12
|
+
mod r#true;
|
|
13
|
+
mod r#false;
|
|
14
|
+
mod mkdir;
|
|
15
|
+
mod rm;
|
|
16
|
+
mod touch;
|
|
17
|
+
mod ls;
|
|
18
|
+
mod cp;
|
|
19
|
+
mod mv;
|
|
20
|
+
mod basename;
|
|
21
|
+
mod dirname;
|
|
22
|
+
mod env;
|
|
23
|
+
mod exit;
|
|
24
|
+
mod which;
|
|
25
|
+
mod yes;
|
|
26
|
+
mod seq;
|
|
27
|
+
mod test;
|
|
28
|
+
|
|
29
|
+
pub use cat::cat;
|
|
30
|
+
pub use cd::cd;
|
|
31
|
+
pub use echo::echo;
|
|
32
|
+
pub use pwd::pwd;
|
|
33
|
+
pub use sleep::sleep;
|
|
34
|
+
pub use r#true::r#true;
|
|
35
|
+
pub use r#false::r#false;
|
|
36
|
+
pub use mkdir::mkdir;
|
|
37
|
+
pub use rm::rm;
|
|
38
|
+
pub use touch::touch;
|
|
39
|
+
pub use ls::ls;
|
|
40
|
+
pub use cp::cp;
|
|
41
|
+
pub use mv::mv;
|
|
42
|
+
pub use basename::basename;
|
|
43
|
+
pub use dirname::dirname;
|
|
44
|
+
pub use env::env;
|
|
45
|
+
pub use exit::exit;
|
|
46
|
+
pub use which::which;
|
|
47
|
+
pub use yes::yes;
|
|
48
|
+
pub use seq::seq;
|
|
49
|
+
pub use test::test;
|
|
50
|
+
|
|
51
|
+
use crate::utils::CommandResult;
|
|
52
|
+
use std::collections::HashMap;
|
|
53
|
+
use std::path::Path;
|
|
54
|
+
use tokio::sync::mpsc;
|
|
55
|
+
|
|
56
|
+
/// Context for virtual command execution
|
|
57
|
+
pub struct CommandContext {
|
|
58
|
+
/// Command arguments (excluding the command name)
|
|
59
|
+
pub args: Vec<String>,
|
|
60
|
+
/// Standard input content
|
|
61
|
+
pub stdin: Option<String>,
|
|
62
|
+
/// Current working directory
|
|
63
|
+
pub cwd: Option<std::path::PathBuf>,
|
|
64
|
+
/// Environment variables
|
|
65
|
+
pub env: Option<HashMap<String, String>>,
|
|
66
|
+
/// Channel to send streaming output
|
|
67
|
+
pub output_tx: Option<mpsc::Sender<StreamChunk>>,
|
|
68
|
+
/// Cancellation check function
|
|
69
|
+
pub is_cancelled: Option<Box<dyn Fn() -> bool + Send + Sync>>,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl std::fmt::Debug for CommandContext {
|
|
73
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
74
|
+
f.debug_struct("CommandContext")
|
|
75
|
+
.field("args", &self.args)
|
|
76
|
+
.field("stdin", &self.stdin)
|
|
77
|
+
.field("cwd", &self.cwd)
|
|
78
|
+
.field("env", &self.env)
|
|
79
|
+
.field("output_tx", &self.output_tx.is_some())
|
|
80
|
+
.field("is_cancelled", &self.is_cancelled.is_some())
|
|
81
|
+
.finish()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// A chunk of streaming output
|
|
86
|
+
#[derive(Debug, Clone)]
|
|
87
|
+
pub enum StreamChunk {
|
|
88
|
+
Stdout(String),
|
|
89
|
+
Stderr(String),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
impl CommandContext {
|
|
93
|
+
/// Create a new command context with arguments
|
|
94
|
+
pub fn new(args: Vec<String>) -> Self {
|
|
95
|
+
CommandContext {
|
|
96
|
+
args,
|
|
97
|
+
stdin: None,
|
|
98
|
+
cwd: None,
|
|
99
|
+
env: None,
|
|
100
|
+
output_tx: None,
|
|
101
|
+
is_cancelled: None,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Check if the command has been cancelled
|
|
106
|
+
pub fn is_cancelled(&self) -> bool {
|
|
107
|
+
self.is_cancelled
|
|
108
|
+
.as_ref()
|
|
109
|
+
.map(|f| f())
|
|
110
|
+
.unwrap_or(false)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Get the current working directory
|
|
114
|
+
pub fn get_cwd(&self) -> std::path::PathBuf {
|
|
115
|
+
self.cwd.clone().unwrap_or_else(|| {
|
|
116
|
+
std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("/"))
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Type for virtual command handler functions
|
|
122
|
+
pub type VirtualCommandHandler = fn(CommandContext) -> std::pin::Pin<Box<dyn std::future::Future<Output = CommandResult> + Send>>;
|
|
123
|
+
|
|
124
|
+
/// Registry of virtual commands
|
|
125
|
+
pub struct VirtualCommandRegistry {
|
|
126
|
+
commands: HashMap<String, VirtualCommandHandler>,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
impl Default for VirtualCommandRegistry {
|
|
130
|
+
fn default() -> Self {
|
|
131
|
+
Self::new()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
impl VirtualCommandRegistry {
|
|
136
|
+
/// Create a new empty registry
|
|
137
|
+
pub fn new() -> Self {
|
|
138
|
+
VirtualCommandRegistry {
|
|
139
|
+
commands: HashMap::new(),
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Create a registry with all built-in commands registered
|
|
144
|
+
pub fn with_builtins() -> Self {
|
|
145
|
+
let mut registry = Self::new();
|
|
146
|
+
registry.register_builtins();
|
|
147
|
+
registry
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Register a virtual command
|
|
151
|
+
pub fn register(&mut self, name: &str, handler: VirtualCommandHandler) {
|
|
152
|
+
self.commands.insert(name.to_string(), handler);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Unregister a virtual command
|
|
156
|
+
pub fn unregister(&mut self, name: &str) -> bool {
|
|
157
|
+
self.commands.remove(name).is_some()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Get a virtual command handler
|
|
161
|
+
pub fn get(&self, name: &str) -> Option<&VirtualCommandHandler> {
|
|
162
|
+
self.commands.get(name)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Check if a command is registered
|
|
166
|
+
pub fn contains(&self, name: &str) -> bool {
|
|
167
|
+
self.commands.contains_key(name)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// List all registered command names
|
|
171
|
+
pub fn list(&self) -> Vec<&str> {
|
|
172
|
+
self.commands.keys().map(|s| s.as_str()).collect()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/// Register all built-in commands
|
|
176
|
+
pub fn register_builtins(&mut self) {
|
|
177
|
+
// Note: These are placeholder registrations - actual async handlers
|
|
178
|
+
// would need proper wrapper functions
|
|
179
|
+
// The actual commands are available as standalone functions
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Global virtual commands enabled flag
|
|
184
|
+
static VIRTUAL_COMMANDS_ENABLED: std::sync::atomic::AtomicBool =
|
|
185
|
+
std::sync::atomic::AtomicBool::new(true);
|
|
186
|
+
|
|
187
|
+
/// Enable virtual commands
|
|
188
|
+
pub fn enable_virtual_commands() {
|
|
189
|
+
VIRTUAL_COMMANDS_ENABLED.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Disable virtual commands
|
|
193
|
+
pub fn disable_virtual_commands() {
|
|
194
|
+
VIRTUAL_COMMANDS_ENABLED.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// Check if virtual commands are enabled
|
|
198
|
+
pub fn are_virtual_commands_enabled() -> bool {
|
|
199
|
+
VIRTUAL_COMMANDS_ENABLED.load(std::sync::atomic::Ordering::SeqCst)
|
|
200
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
//! Virtual `mv` command implementation
|
|
2
|
+
|
|
3
|
+
use crate::commands::CommandContext;
|
|
4
|
+
use crate::utils::{trace_lazy, CommandResult, VirtualUtils};
|
|
5
|
+
use std::fs;
|
|
6
|
+
|
|
7
|
+
/// Execute the mv command
|
|
8
|
+
///
|
|
9
|
+
/// Moves (renames) files and directories.
|
|
10
|
+
pub async fn mv(ctx: CommandContext) -> CommandResult {
|
|
11
|
+
if ctx.args.len() < 2 {
|
|
12
|
+
return VirtualUtils::invalid_argument_error("mv", "missing file operand");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Parse flags (currently just skip them)
|
|
16
|
+
let mut paths = Vec::new();
|
|
17
|
+
|
|
18
|
+
for arg in &ctx.args {
|
|
19
|
+
if !arg.starts_with('-') {
|
|
20
|
+
paths.push(arg.clone());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if paths.len() < 2 {
|
|
25
|
+
return VirtualUtils::invalid_argument_error("mv", "missing destination file operand");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let cwd = ctx.get_cwd();
|
|
29
|
+
let dest = paths.pop().unwrap();
|
|
30
|
+
let dest_path = VirtualUtils::resolve_path(&dest, Some(&cwd));
|
|
31
|
+
|
|
32
|
+
// If multiple sources or dest is a directory, move into the directory
|
|
33
|
+
let dest_is_dir = dest_path.is_dir();
|
|
34
|
+
let multiple_sources = paths.len() > 1;
|
|
35
|
+
|
|
36
|
+
if multiple_sources && !dest_is_dir {
|
|
37
|
+
return CommandResult::error(format!(
|
|
38
|
+
"mv: target '{}' is not a directory\n",
|
|
39
|
+
dest
|
|
40
|
+
));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for source in paths {
|
|
44
|
+
let source_path = VirtualUtils::resolve_path(&source, Some(&cwd));
|
|
45
|
+
|
|
46
|
+
trace_lazy("VirtualCommand", || {
|
|
47
|
+
format!("mv: moving {:?} to {:?}", source_path, dest_path)
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if !source_path.exists() {
|
|
51
|
+
return CommandResult::error(format!(
|
|
52
|
+
"mv: cannot stat '{}': No such file or directory\n",
|
|
53
|
+
source
|
|
54
|
+
));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let final_dest = if dest_is_dir {
|
|
58
|
+
dest_path.join(source_path.file_name().unwrap_or_default())
|
|
59
|
+
} else {
|
|
60
|
+
dest_path.clone()
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Try rename first (fastest if on same filesystem)
|
|
64
|
+
match fs::rename(&source_path, &final_dest) {
|
|
65
|
+
Ok(()) => continue,
|
|
66
|
+
Err(e) => {
|
|
67
|
+
// If rename fails (e.g., cross-filesystem), try copy + delete
|
|
68
|
+
if e.kind() == std::io::ErrorKind::CrossesDevices
|
|
69
|
+
|| e.kind() == std::io::ErrorKind::Other
|
|
70
|
+
{
|
|
71
|
+
if source_path.is_dir() {
|
|
72
|
+
if let Err(e) = copy_and_remove_dir(&source_path, &final_dest) {
|
|
73
|
+
return CommandResult::error(format!(
|
|
74
|
+
"mv: cannot move '{}': {}\n",
|
|
75
|
+
source, e
|
|
76
|
+
));
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
if let Err(e) = fs::copy(&source_path, &final_dest) {
|
|
80
|
+
return CommandResult::error(format!(
|
|
81
|
+
"mv: cannot move '{}': {}\n",
|
|
82
|
+
source, e
|
|
83
|
+
));
|
|
84
|
+
}
|
|
85
|
+
if let Err(e) = fs::remove_file(&source_path) {
|
|
86
|
+
return CommandResult::error(format!(
|
|
87
|
+
"mv: cannot remove '{}': {}\n",
|
|
88
|
+
source, e
|
|
89
|
+
));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
return CommandResult::error(format!(
|
|
94
|
+
"mv: cannot move '{}': {}\n",
|
|
95
|
+
source, e
|
|
96
|
+
));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
CommandResult::success_empty()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn copy_and_remove_dir(src: &std::path::Path, dst: &std::path::Path) -> std::io::Result<()> {
|
|
106
|
+
fs::create_dir_all(dst)?;
|
|
107
|
+
|
|
108
|
+
for entry in fs::read_dir(src)? {
|
|
109
|
+
let entry = entry?;
|
|
110
|
+
let entry_path = entry.path();
|
|
111
|
+
let dest_path = dst.join(entry.file_name());
|
|
112
|
+
|
|
113
|
+
if entry_path.is_dir() {
|
|
114
|
+
copy_and_remove_dir(&entry_path, &dest_path)?;
|
|
115
|
+
} else {
|
|
116
|
+
fs::copy(&entry_path, &dest_path)?;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fs::remove_dir_all(src)?;
|
|
121
|
+
Ok(())
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#[cfg(test)]
|
|
125
|
+
mod tests {
|
|
126
|
+
use super::*;
|
|
127
|
+
use tempfile::tempdir;
|
|
128
|
+
|
|
129
|
+
#[tokio::test]
|
|
130
|
+
async fn test_mv_file() {
|
|
131
|
+
let temp = tempdir().unwrap();
|
|
132
|
+
let src = temp.path().join("source.txt");
|
|
133
|
+
let dst = temp.path().join("dest.txt");
|
|
134
|
+
fs::write(&src, "test content").unwrap();
|
|
135
|
+
|
|
136
|
+
let ctx = CommandContext::new(vec![
|
|
137
|
+
src.to_string_lossy().to_string(),
|
|
138
|
+
dst.to_string_lossy().to_string(),
|
|
139
|
+
]);
|
|
140
|
+
let result = mv(ctx).await;
|
|
141
|
+
|
|
142
|
+
assert!(result.is_success());
|
|
143
|
+
assert!(!src.exists());
|
|
144
|
+
assert!(dst.exists());
|
|
145
|
+
assert_eq!(fs::read_to_string(&dst).unwrap(), "test content");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#[tokio::test]
|
|
149
|
+
async fn test_mv_directory() {
|
|
150
|
+
let temp = tempdir().unwrap();
|
|
151
|
+
let src_dir = temp.path().join("src_dir");
|
|
152
|
+
let dst_dir = temp.path().join("dst_dir");
|
|
153
|
+
|
|
154
|
+
fs::create_dir(&src_dir).unwrap();
|
|
155
|
+
fs::write(src_dir.join("file.txt"), "test").unwrap();
|
|
156
|
+
|
|
157
|
+
let ctx = CommandContext::new(vec![
|
|
158
|
+
src_dir.to_string_lossy().to_string(),
|
|
159
|
+
dst_dir.to_string_lossy().to_string(),
|
|
160
|
+
]);
|
|
161
|
+
let result = mv(ctx).await;
|
|
162
|
+
|
|
163
|
+
assert!(result.is_success());
|
|
164
|
+
assert!(!src_dir.exists());
|
|
165
|
+
assert!(dst_dir.join("file.txt").exists());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#[tokio::test]
|
|
169
|
+
async fn test_mv_nonexistent() {
|
|
170
|
+
let temp = tempdir().unwrap();
|
|
171
|
+
|
|
172
|
+
let ctx = CommandContext::new(vec![
|
|
173
|
+
"/nonexistent/file".to_string(),
|
|
174
|
+
temp.path().join("dest").to_string_lossy().to_string(),
|
|
175
|
+
]);
|
|
176
|
+
let result = mv(ctx).await;
|
|
177
|
+
|
|
178
|
+
assert!(!result.is_success());
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//! Virtual `pwd` command implementation
|
|
2
|
+
|
|
3
|
+
use crate::commands::CommandContext;
|
|
4
|
+
use crate::utils::CommandResult;
|
|
5
|
+
use std::env;
|
|
6
|
+
|
|
7
|
+
/// Execute the pwd command
|
|
8
|
+
///
|
|
9
|
+
/// Prints the current working directory.
|
|
10
|
+
pub async fn pwd(_ctx: CommandContext) -> CommandResult {
|
|
11
|
+
match env::current_dir() {
|
|
12
|
+
Ok(path) => CommandResult::success(format!("{}\n", path.display())),
|
|
13
|
+
Err(e) => CommandResult::error(format!("pwd: {}\n", e)),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[cfg(test)]
|
|
18
|
+
mod tests {
|
|
19
|
+
use super::*;
|
|
20
|
+
|
|
21
|
+
#[tokio::test]
|
|
22
|
+
async fn test_pwd() {
|
|
23
|
+
let ctx = CommandContext::new(vec![]);
|
|
24
|
+
let result = pwd(ctx).await;
|
|
25
|
+
assert!(result.is_success());
|
|
26
|
+
assert!(!result.stdout.is_empty());
|
|
27
|
+
}
|
|
28
|
+
}
|