dotdo 0.0.1 → 0.0.2
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/LICENSE +1 -1
- package/README.md +446 -315
- package/dist/ai/index.js +19 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/template-literals.js +852 -0
- package/dist/ai/template-literals.js.map +1 -0
- package/dist/api/analytics/router.js +601 -0
- package/dist/api/analytics/router.js.map +1 -0
- package/dist/api/index.js +158 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middleware/auth-federation.js +573 -0
- package/dist/api/middleware/auth-federation.js.map +1 -0
- package/dist/api/middleware/auth.js +544 -0
- package/dist/api/middleware/auth.js.map +1 -0
- package/dist/api/middleware/error-handling.js +176 -0
- package/dist/api/middleware/error-handling.js.map +1 -0
- package/dist/api/middleware/request-id.js +21 -0
- package/dist/api/middleware/request-id.js.map +1 -0
- package/dist/api/pages.js +1180 -0
- package/dist/api/pages.js.map +1 -0
- package/dist/api/routes/api.js +612 -0
- package/dist/api/routes/api.js.map +1 -0
- package/dist/api/routes/browsers.js +471 -0
- package/dist/api/routes/browsers.js.map +1 -0
- package/dist/api/routes/do.js +188 -0
- package/dist/api/routes/do.js.map +1 -0
- package/dist/api/routes/mcp.js +459 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/obs.js +445 -0
- package/dist/api/routes/obs.js.map +1 -0
- package/dist/api/routes/openapi.js +794 -0
- package/dist/api/routes/openapi.js.map +1 -0
- package/dist/api/routes/rpc.js +1103 -0
- package/dist/api/routes/rpc.js.map +1 -0
- package/dist/api/routes/sandboxes.js +389 -0
- package/dist/api/routes/sandboxes.js.map +1 -0
- package/dist/api/test-do.js +38 -0
- package/dist/api/test-do.js.map +1 -0
- package/dist/api/types.js +11 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli/bin.js +2 -0
- package/dist/cli/main.js +52342 -0
- package/dist/db/actions.js +212 -0
- package/dist/db/actions.js.map +1 -0
- package/dist/db/auth.js +506 -0
- package/dist/db/auth.js.map +1 -0
- package/dist/db/branches.js +65 -0
- package/dist/db/branches.js.map +1 -0
- package/dist/db/clickhouse.js +1074 -0
- package/dist/db/clickhouse.js.map +1 -0
- package/dist/db/dlq.js +39 -0
- package/dist/db/dlq.js.map +1 -0
- package/dist/db/events.js +28 -0
- package/dist/db/events.js.map +1 -0
- package/dist/db/exec.js +64 -0
- package/dist/db/exec.js.map +1 -0
- package/dist/db/files.js +85 -0
- package/dist/db/files.js.map +1 -0
- package/dist/db/flags.js +24 -0
- package/dist/db/flags.js.map +1 -0
- package/dist/db/git.js +116 -0
- package/dist/db/git.js.map +1 -0
- package/dist/db/iceberg/inverted-index.js +862 -0
- package/dist/db/iceberg/inverted-index.js.map +1 -0
- package/dist/db/iceberg/puffin.js +878 -0
- package/dist/db/iceberg/puffin.js.map +1 -0
- package/dist/db/iceberg/search-manifest.js +422 -0
- package/dist/db/iceberg/search-manifest.js.map +1 -0
- package/dist/db/iceberg/types.js +8 -0
- package/dist/db/iceberg/types.js.map +1 -0
- package/dist/db/index.js +121 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/integrations.js +368 -0
- package/dist/db/integrations.js.map +1 -0
- package/dist/db/json-indexes.js +332 -0
- package/dist/db/json-indexes.js.map +1 -0
- package/dist/db/linked-accounts.js +287 -0
- package/dist/db/linked-accounts.js.map +1 -0
- package/dist/db/nouns.js +183 -0
- package/dist/db/nouns.js.map +1 -0
- package/dist/db/objects.js +170 -0
- package/dist/db/objects.js.map +1 -0
- package/dist/db/primitives/dag-scheduler/index.js +869 -0
- package/dist/db/primitives/dag-scheduler/index.js.map +1 -0
- package/dist/db/primitives/exactly-once-context.js +237 -0
- package/dist/db/primitives/exactly-once-context.js.map +1 -0
- package/dist/db/primitives/index.js +62 -0
- package/dist/db/primitives/index.js.map +1 -0
- package/dist/db/primitives/keyed-router.js +145 -0
- package/dist/db/primitives/keyed-router.js.map +1 -0
- package/dist/db/primitives/observability.js +162 -0
- package/dist/db/primitives/observability.js.map +1 -0
- package/dist/db/primitives/schema-evolution.js +643 -0
- package/dist/db/primitives/schema-evolution.js.map +1 -0
- package/dist/db/primitives/stateful-operator/index.js +770 -0
- package/dist/db/primitives/stateful-operator/index.js.map +1 -0
- package/dist/db/primitives/temporal-store.js +306 -0
- package/dist/db/primitives/temporal-store.js.map +1 -0
- package/dist/db/primitives/typed-column-store.js +1229 -0
- package/dist/db/primitives/typed-column-store.js.map +1 -0
- package/dist/db/primitives/utils/duration.js +162 -0
- package/dist/db/primitives/utils/duration.js.map +1 -0
- package/dist/db/primitives/utils/murmur3.js +118 -0
- package/dist/db/primitives/utils/murmur3.js.map +1 -0
- package/dist/db/primitives/watermark-service.js +136 -0
- package/dist/db/primitives/watermark-service.js.map +1 -0
- package/dist/db/primitives/window-manager.js +764 -0
- package/dist/db/primitives/window-manager.js.map +1 -0
- package/dist/db/relationships.js +66 -0
- package/dist/db/relationships.js.map +1 -0
- package/dist/db/schema-minimal.js +61 -0
- package/dist/db/schema-minimal.js.map +1 -0
- package/dist/db/search.js +28 -0
- package/dist/db/search.js.map +1 -0
- package/dist/db/stores.js +1665 -0
- package/dist/db/stores.js.map +1 -0
- package/dist/db/things.js +297 -0
- package/dist/db/things.js.map +1 -0
- package/dist/db/vault.js +171 -0
- package/dist/db/vault.js.map +1 -0
- package/dist/db/verbs.js +102 -0
- package/dist/db/verbs.js.map +1 -0
- package/dist/do/base.js +48 -0
- package/dist/do/base.js.map +1 -0
- package/dist/do/bash.js +35 -0
- package/dist/do/bash.js.map +1 -0
- package/dist/do/fs.js +25 -0
- package/dist/do/fs.js.map +1 -0
- package/dist/do/full.js +61 -0
- package/dist/do/full.js.map +1 -0
- package/dist/do/git.js +28 -0
- package/dist/do/git.js.map +1 -0
- package/dist/do/index.js +52 -0
- package/dist/do/index.js.map +1 -0
- package/dist/do/tiny.js +31 -0
- package/dist/do/tiny.js.map +1 -0
- package/dist/lib/DOAuth.js +261 -0
- package/dist/lib/DOAuth.js.map +1 -0
- package/dist/lib/DODispatcher.js +72 -0
- package/dist/lib/DODispatcher.js.map +1 -0
- package/dist/lib/Modifier.js +189 -0
- package/dist/lib/Modifier.js.map +1 -0
- package/dist/lib/StateStorage.js +403 -0
- package/dist/lib/StateStorage.js.map +1 -0
- package/dist/lib/TypeRegistry.js +122 -0
- package/dist/lib/TypeRegistry.js.map +1 -0
- package/dist/lib/agent/tools/bash.js +336 -0
- package/dist/lib/agent/tools/bash.js.map +1 -0
- package/dist/lib/agent/tools/edit.js +157 -0
- package/dist/lib/agent/tools/edit.js.map +1 -0
- package/dist/lib/agent/tools/glob.js +137 -0
- package/dist/lib/agent/tools/glob.js.map +1 -0
- package/dist/lib/agent/tools/grep.js +315 -0
- package/dist/lib/agent/tools/grep.js.map +1 -0
- package/dist/lib/agent/tools/index.js +71 -0
- package/dist/lib/agent/tools/index.js.map +1 -0
- package/dist/lib/agent/tools/read.js +212 -0
- package/dist/lib/agent/tools/read.js.map +1 -0
- package/dist/lib/agent/tools/types.js +197 -0
- package/dist/lib/agent/tools/types.js.map +1 -0
- package/dist/lib/agent/tools/write.js +159 -0
- package/dist/lib/agent/tools/write.js.map +1 -0
- package/dist/lib/ai/gateway.js +247 -0
- package/dist/lib/ai/gateway.js.map +1 -0
- package/dist/lib/ai/tool-loop-agent.js +591 -0
- package/dist/lib/ai/tool-loop-agent.js.map +1 -0
- package/dist/lib/auto-wiring.js +439 -0
- package/dist/lib/auto-wiring.js.map +1 -0
- package/dist/lib/browse/browserbase.js +163 -0
- package/dist/lib/browse/browserbase.js.map +1 -0
- package/dist/lib/browse/cloudflare.js +144 -0
- package/dist/lib/browse/cloudflare.js.map +1 -0
- package/dist/lib/browse/index.js +62 -0
- package/dist/lib/browse/index.js.map +1 -0
- package/dist/lib/browse/types.js +13 -0
- package/dist/lib/browse/types.js.map +1 -0
- package/dist/lib/cache/index.js +37 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/visibility.js +638 -0
- package/dist/lib/cache/visibility.js.map +1 -0
- package/dist/lib/capabilities.js +268 -0
- package/dist/lib/capabilities.js.map +1 -0
- package/dist/lib/channels/base.js +106 -0
- package/dist/lib/channels/base.js.map +1 -0
- package/dist/lib/channels/discord.js +94 -0
- package/dist/lib/channels/discord.js.map +1 -0
- package/dist/lib/channels/email.js +204 -0
- package/dist/lib/channels/email.js.map +1 -0
- package/dist/lib/channels/index.js +90 -0
- package/dist/lib/channels/index.js.map +1 -0
- package/dist/lib/channels/mdxui-chat.js +95 -0
- package/dist/lib/channels/mdxui-chat.js.map +1 -0
- package/dist/lib/channels/slack-blockkit.js +121 -0
- package/dist/lib/channels/slack-blockkit.js.map +1 -0
- package/dist/lib/channels/types.js +7 -0
- package/dist/lib/channels/types.js.map +1 -0
- package/dist/lib/cloudflare/ai.js +654 -0
- package/dist/lib/cloudflare/ai.js.map +1 -0
- package/dist/lib/cloudflare/index.js +88 -0
- package/dist/lib/cloudflare/index.js.map +1 -0
- package/dist/lib/cloudflare/kv.js +342 -0
- package/dist/lib/cloudflare/kv.js.map +1 -0
- package/dist/lib/cloudflare/queues.js +434 -0
- package/dist/lib/cloudflare/queues.js.map +1 -0
- package/dist/lib/cloudflare/r2.js +604 -0
- package/dist/lib/cloudflare/r2.js.map +1 -0
- package/dist/lib/cloudflare/vectorize.js +494 -0
- package/dist/lib/cloudflare/vectorize.js.map +1 -0
- package/dist/lib/cloudflare/workflows.js +569 -0
- package/dist/lib/cloudflare/workflows.js.map +1 -0
- package/dist/lib/colo/caching.js +196 -0
- package/dist/lib/colo/caching.js.map +1 -0
- package/dist/lib/colo/detection.js +194 -0
- package/dist/lib/colo/detection.js.map +1 -0
- package/dist/lib/colo/external-data.js +219 -0
- package/dist/lib/colo/external-data.js.map +1 -0
- package/dist/lib/colo/globe-data.js +179 -0
- package/dist/lib/colo/globe-data.js.map +1 -0
- package/dist/lib/colo/index.js +16 -0
- package/dist/lib/colo/index.js.map +1 -0
- package/dist/lib/decorators.js +37 -0
- package/dist/lib/decorators.js.map +1 -0
- package/dist/lib/discovery.js +81 -0
- package/dist/lib/discovery.js.map +1 -0
- package/dist/lib/executors/AgenticFunctionExecutor.js +619 -0
- package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/BaseFunctionExecutor.js +328 -0
- package/dist/lib/executors/BaseFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/CascadeExecutor.js +418 -0
- package/dist/lib/executors/CascadeExecutor.js.map +1 -0
- package/dist/lib/executors/CodeFunctionExecutor.js +904 -0
- package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/GenerativeFunctionExecutor.js +904 -0
- package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/HumanFunctionExecutor.js +884 -0
- package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/ParallelStepExecutor.js +308 -0
- package/dist/lib/executors/ParallelStepExecutor.js.map +1 -0
- package/dist/lib/executors/types.js +12 -0
- package/dist/lib/executors/types.js.map +1 -0
- package/dist/lib/experiments.js +89 -0
- package/dist/lib/experiments.js.map +1 -0
- package/dist/lib/flags/store.js +262 -0
- package/dist/lib/flags/store.js.map +1 -0
- package/dist/lib/functions/FunctionComposition.js +467 -0
- package/dist/lib/functions/FunctionComposition.js.map +1 -0
- package/dist/lib/functions/FunctionMiddleware.js +457 -0
- package/dist/lib/functions/FunctionMiddleware.js.map +1 -0
- package/dist/lib/functions/FunctionRegistry.js +426 -0
- package/dist/lib/functions/FunctionRegistry.js.map +1 -0
- package/dist/lib/functions/createFunction.js +1048 -0
- package/dist/lib/functions/createFunction.js.map +1 -0
- package/dist/lib/humans/index.js +68 -0
- package/dist/lib/humans/index.js.map +1 -0
- package/dist/lib/humans/templates.js +117 -0
- package/dist/lib/humans/templates.js.map +1 -0
- package/dist/lib/identity.js +98 -0
- package/dist/lib/identity.js.map +1 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logging/error-logger.js +163 -0
- package/dist/lib/logging/error-logger.js.map +1 -0
- package/dist/lib/logging/index.js +160 -0
- package/dist/lib/logging/index.js.map +1 -0
- package/dist/lib/mixins/bash.js +825 -0
- package/dist/lib/mixins/bash.js.map +1 -0
- package/dist/lib/mixins/fs.js +648 -0
- package/dist/lib/mixins/fs.js.map +1 -0
- package/dist/lib/mixins/git.js +1011 -0
- package/dist/lib/mixins/git.js.map +1 -0
- package/dist/lib/mixins/index.js +29 -0
- package/dist/lib/mixins/index.js.map +1 -0
- package/dist/lib/mixins/npm.js +662 -0
- package/dist/lib/mixins/npm.js.map +1 -0
- package/dist/lib/noun-id.js +278 -0
- package/dist/lib/noun-id.js.map +1 -0
- package/dist/lib/rate-limit/sliding-window.js +148 -0
- package/dist/lib/rate-limit/sliding-window.js.map +1 -0
- package/dist/lib/rate-limit.js +110 -0
- package/dist/lib/rate-limit.js.map +1 -0
- package/dist/lib/rpc/bindings.js +548 -0
- package/dist/lib/rpc/bindings.js.map +1 -0
- package/dist/lib/rpc/index.js +64 -0
- package/dist/lib/rpc/index.js.map +1 -0
- package/dist/lib/safe-stringify.js +223 -0
- package/dist/lib/safe-stringify.js.map +1 -0
- package/dist/lib/sandbox/miniflare-sandbox.js +1007 -0
- package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -0
- package/dist/lib/sqids.js +110 -0
- package/dist/lib/sqids.js.map +1 -0
- package/dist/lib/sql/adapters/index.js +10 -0
- package/dist/lib/sql/adapters/index.js.map +1 -0
- package/dist/lib/sql/adapters/node-sql-parser.js +552 -0
- package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -0
- package/dist/lib/sql/adapters/pgsql-parser.js +1189 -0
- package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -0
- package/dist/lib/sql/index.js +277 -0
- package/dist/lib/sql/index.js.map +1 -0
- package/dist/lib/sql/types.js +56 -0
- package/dist/lib/sql/types.js.map +1 -0
- package/dist/lib/type-classifier.js +126 -0
- package/dist/lib/type-classifier.js.map +1 -0
- package/dist/lib/utils/html.js +47 -0
- package/dist/lib/utils/html.js.map +1 -0
- package/dist/lib/validation.js +48 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/vault/store.js +411 -0
- package/dist/lib/vault/store.js.map +1 -0
- package/dist/metrics/hunch.js +739 -0
- package/dist/metrics/hunch.js.map +1 -0
- package/dist/objects/API.js +302 -0
- package/dist/objects/API.js.map +1 -0
- package/dist/objects/Agent.js +179 -0
- package/dist/objects/Agent.js.map +1 -0
- package/dist/objects/AgenticFunctionExecutor.js +8 -0
- package/dist/objects/AgenticFunctionExecutor.js.map +1 -0
- package/dist/objects/App.js +83 -0
- package/dist/objects/App.js.map +1 -0
- package/dist/objects/Browser.js +884 -0
- package/dist/objects/Browser.js.map +1 -0
- package/dist/objects/Business.js +107 -0
- package/dist/objects/Business.js.map +1 -0
- package/dist/objects/CLI.js +221 -0
- package/dist/objects/CLI.js.map +1 -0
- package/dist/objects/CodeFunctionExecutor.js +8 -0
- package/dist/objects/CodeFunctionExecutor.js.map +1 -0
- package/dist/objects/Collection.js +161 -0
- package/dist/objects/Collection.js.map +1 -0
- package/dist/objects/DO.js +41 -0
- package/dist/objects/DO.js.map +1 -0
- package/dist/objects/DOBase.js +2309 -0
- package/dist/objects/DOBase.js.map +1 -0
- package/dist/objects/DOFull.js +1676 -0
- package/dist/objects/DOFull.js.map +1 -0
- package/dist/objects/DOTiny.js +207 -0
- package/dist/objects/DOTiny.js.map +1 -0
- package/dist/objects/Directory.js +199 -0
- package/dist/objects/Directory.js.map +1 -0
- package/dist/objects/Entity.js +413 -0
- package/dist/objects/Entity.js.map +1 -0
- package/dist/objects/Function.js +116 -0
- package/dist/objects/Function.js.map +1 -0
- package/dist/objects/Human.js +231 -0
- package/dist/objects/Human.js.map +1 -0
- package/dist/objects/HumanFunctionExecutor.js +8 -0
- package/dist/objects/HumanFunctionExecutor.js.map +1 -0
- package/dist/objects/IcebergMetadataDO.js +938 -0
- package/dist/objects/IcebergMetadataDO.js.map +1 -0
- package/dist/objects/IntegrationsDO.js +1174 -0
- package/dist/objects/IntegrationsDO.js.map +1 -0
- package/dist/objects/ObservabilityBroadcaster.js +149 -0
- package/dist/objects/ObservabilityBroadcaster.js.map +1 -0
- package/dist/objects/Package.js +154 -0
- package/dist/objects/Package.js.map +1 -0
- package/dist/objects/Product.js +193 -0
- package/dist/objects/Product.js.map +1 -0
- package/dist/objects/SDK.js +152 -0
- package/dist/objects/SDK.js.map +1 -0
- package/dist/objects/SaaS.js +235 -0
- package/dist/objects/SaaS.js.map +1 -0
- package/dist/objects/SandboxDO.js +759 -0
- package/dist/objects/SandboxDO.js.map +1 -0
- package/dist/objects/Service.js +337 -0
- package/dist/objects/Service.js.map +1 -0
- package/dist/objects/Site.js +80 -0
- package/dist/objects/Site.js.map +1 -0
- package/dist/objects/Startup.js +479 -0
- package/dist/objects/Startup.js.map +1 -0
- package/dist/objects/ThingsDO.js +170 -0
- package/dist/objects/ThingsDO.js.map +1 -0
- package/dist/objects/VectorShardDO.js +648 -0
- package/dist/objects/VectorShardDO.js.map +1 -0
- package/dist/objects/Worker.js +144 -0
- package/dist/objects/Worker.js.map +1 -0
- package/dist/objects/Workflow.js +196 -0
- package/dist/objects/Workflow.js.map +1 -0
- package/dist/objects/WorkflowFactory.js +313 -0
- package/dist/objects/WorkflowFactory.js.map +1 -0
- package/dist/objects/WorkflowRuntime.js +863 -0
- package/dist/objects/WorkflowRuntime.js.map +1 -0
- package/dist/objects/circuit-breaker-bulkhead.js +178 -0
- package/dist/objects/circuit-breaker-bulkhead.js.map +1 -0
- package/dist/objects/createFunction.js +934 -0
- package/dist/objects/createFunction.js.map +1 -0
- package/dist/objects/index.js +80 -0
- package/dist/objects/index.js.map +1 -0
- package/dist/objects/lifecycle/Branch.js +275 -0
- package/dist/objects/lifecycle/Branch.js.map +1 -0
- package/dist/objects/lifecycle/Clone.js +1499 -0
- package/dist/objects/lifecycle/Clone.js.map +1 -0
- package/dist/objects/lifecycle/Compact.js +237 -0
- package/dist/objects/lifecycle/Compact.js.map +1 -0
- package/dist/objects/lifecycle/Promote.js +476 -0
- package/dist/objects/lifecycle/Promote.js.map +1 -0
- package/dist/objects/lifecycle/Shard.js +560 -0
- package/dist/objects/lifecycle/Shard.js.map +1 -0
- package/dist/objects/lifecycle/index.js +15 -0
- package/dist/objects/lifecycle/index.js.map +1 -0
- package/dist/objects/lifecycle/types.js +33 -0
- package/dist/objects/lifecycle/types.js.map +1 -0
- package/dist/objects/mixins/infrastructure.js +171 -0
- package/dist/objects/mixins/infrastructure.js.map +1 -0
- package/dist/objects/modules/StoresModule.js +153 -0
- package/dist/objects/modules/StoresModule.js.map +1 -0
- package/dist/objects/persistence/checkpoint-manager.js +606 -0
- package/dist/objects/persistence/checkpoint-manager.js.map +1 -0
- package/dist/objects/persistence/index.js +72 -0
- package/dist/objects/persistence/index.js.map +1 -0
- package/dist/objects/persistence/migration-runner.js +562 -0
- package/dist/objects/persistence/migration-runner.js.map +1 -0
- package/dist/objects/persistence/replication-manager.js +501 -0
- package/dist/objects/persistence/replication-manager.js.map +1 -0
- package/dist/objects/persistence/tiered-storage-manager.js +595 -0
- package/dist/objects/persistence/tiered-storage-manager.js.map +1 -0
- package/dist/objects/persistence/types.js +14 -0
- package/dist/objects/persistence/types.js.map +1 -0
- package/dist/objects/persistence/wal-manager.js +653 -0
- package/dist/objects/persistence/wal-manager.js.map +1 -0
- package/dist/objects/presets/index.js +20 -0
- package/dist/objects/presets/index.js.map +1 -0
- package/dist/objects/presets/primitives.js +188 -0
- package/dist/objects/presets/primitives.js.map +1 -0
- package/dist/objects/primitives/alarm-adapter.js +141 -0
- package/dist/objects/primitives/alarm-adapter.js.map +1 -0
- package/dist/objects/primitives/index.js +337 -0
- package/dist/objects/primitives/index.js.map +1 -0
- package/dist/objects/primitives/storage-adapter.js +182 -0
- package/dist/objects/primitives/storage-adapter.js.map +1 -0
- package/dist/objects/primitives/with-primitives.js +102 -0
- package/dist/objects/primitives/with-primitives.js.map +1 -0
- package/dist/objects/services/StoreManager.js +227 -0
- package/dist/objects/services/StoreManager.js.map +1 -0
- package/dist/objects/services/index.js +13 -0
- package/dist/objects/services/index.js.map +1 -0
- package/dist/objects/transport/auth-layer.js +1451 -0
- package/dist/objects/transport/auth-layer.js.map +1 -0
- package/dist/objects/transport/capnweb-target.js +355 -0
- package/dist/objects/transport/capnweb-target.js.map +1 -0
- package/dist/objects/transport/chain.js +441 -0
- package/dist/objects/transport/chain.js.map +1 -0
- package/dist/objects/transport/handler.js +58 -0
- package/dist/objects/transport/handler.js.map +1 -0
- package/dist/objects/transport/index.js +53 -0
- package/dist/objects/transport/index.js.map +1 -0
- package/dist/objects/transport/mcp-server.js +690 -0
- package/dist/objects/transport/mcp-server.js.map +1 -0
- package/dist/objects/transport/rest-autowire.js +1507 -0
- package/dist/objects/transport/rest-autowire.js.map +1 -0
- package/dist/objects/transport/rest-router.js +440 -0
- package/dist/objects/transport/rest-router.js.map +1 -0
- package/dist/objects/transport/rpc-server.js +1536 -0
- package/dist/objects/transport/rpc-server.js.map +1 -0
- package/dist/objects/transport/shared.js +575 -0
- package/dist/objects/transport/shared.js.map +1 -0
- package/dist/objects/transport/sync-engine.js +291 -0
- package/dist/objects/transport/sync-engine.js.map +1 -0
- package/dist/objects/transport/types.js +8 -0
- package/dist/objects/transport/types.js.map +1 -0
- package/dist/primitives/bashx/src/ast/analyze.js +1472 -0
- package/dist/primitives/bashx/src/ast/analyze.js.map +1 -0
- package/dist/primitives/bashx/src/ast/parser.js +1488 -0
- package/dist/primitives/bashx/src/ast/parser.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/crypto.js +1954 -0
- package/dist/primitives/bashx/src/do/commands/crypto.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/data-processing.js +1812 -0
- package/dist/primitives/bashx/src/do/commands/data-processing.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/extended-utils.js +804 -0
- package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/math-control.js +1122 -0
- package/dist/primitives/bashx/src/do/commands/math-control.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/posix-utils.js +1015 -0
- package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/system-utils.js +687 -0
- package/dist/primitives/bashx/src/do/commands/system-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/test-command.js +523 -0
- package/dist/primitives/bashx/src/do/commands/test-command.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/text-processing.js +1550 -0
- package/dist/primitives/bashx/src/do/commands/text-processing.js.map +1 -0
- package/dist/primitives/bashx/src/do/container-executor.js +429 -0
- package/dist/primitives/bashx/src/do/container-executor.js.map +1 -0
- package/dist/primitives/bashx/src/do/index.js +668 -0
- package/dist/primitives/bashx/src/do/index.js.map +1 -0
- package/dist/primitives/bashx/src/do/tiered-executor.js +2647 -0
- package/dist/primitives/bashx/src/do/tiered-executor.js.map +1 -0
- package/dist/primitives/bashx/src/do/worker.js +352 -0
- package/dist/primitives/bashx/src/do/worker.js.map +1 -0
- package/dist/primitives/bashx/src/types.js +10 -0
- package/dist/primitives/bashx/src/types.js.map +1 -0
- package/dist/primitives/fsx/core/backend.js +480 -0
- package/dist/primitives/fsx/core/backend.js.map +1 -0
- package/dist/primitives/fsx/core/constants.js +140 -0
- package/dist/primitives/fsx/core/constants.js.map +1 -0
- package/dist/primitives/fsx/core/fsx.js +1184 -0
- package/dist/primitives/fsx/core/fsx.js.map +1 -0
- package/dist/primitives/fsx/core/glob/glob.js +438 -0
- package/dist/primitives/fsx/core/glob/glob.js.map +1 -0
- package/dist/primitives/fsx/core/glob/index.js +8 -0
- package/dist/primitives/fsx/core/glob/index.js.map +1 -0
- package/dist/primitives/fsx/core/glob/match.js +392 -0
- package/dist/primitives/fsx/core/glob/match.js.map +1 -0
- package/dist/primitives/fsx/core/types.js +307 -0
- package/dist/primitives/fsx/core/types.js.map +1 -0
- package/dist/sandbox/index.js +258 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sdk/capnweb-compat.js +42 -0
- package/dist/sdk/capnweb-compat.js.map +1 -0
- package/dist/sdk/client.js +20 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/index.js +17 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/snippets/artifacts-config.js +241 -0
- package/dist/snippets/artifacts-config.js.map +1 -0
- package/dist/snippets/artifacts-ingest.js +832 -0
- package/dist/snippets/artifacts-ingest.js.map +1 -0
- package/dist/snippets/artifacts-serve.js +1035 -0
- package/dist/snippets/artifacts-serve.js.map +1 -0
- package/dist/snippets/artifacts-types.js +161 -0
- package/dist/snippets/artifacts-types.js.map +1 -0
- package/dist/snippets/cache-probe.js +376 -0
- package/dist/snippets/cache-probe.js.map +1 -0
- package/dist/snippets/cache.js +10 -0
- package/dist/snippets/cache.js.map +1 -0
- package/dist/snippets/events.js +469 -0
- package/dist/snippets/events.js.map +1 -0
- package/dist/snippets/index.js +7 -0
- package/dist/snippets/index.js.map +1 -0
- package/dist/snippets/proxy.js +495 -0
- package/dist/snippets/proxy.js.map +1 -0
- package/dist/snippets/search.js +1759 -0
- package/dist/snippets/search.js.map +1 -0
- package/dist/streams/index.js +30 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/observability.js +68 -0
- package/dist/streams/observability.js.map +1 -0
- package/dist/types/AI.js +92 -0
- package/dist/types/AI.js.map +1 -0
- package/dist/types/AIFunction.js +171 -0
- package/dist/types/AIFunction.js.map +1 -0
- package/dist/types/BrowseVerb.js +89 -0
- package/dist/types/BrowseVerb.js.map +1 -0
- package/dist/types/Browser.js +31 -0
- package/dist/types/Browser.js.map +1 -0
- package/dist/types/Chaos.js +15 -0
- package/dist/types/Chaos.js.map +1 -0
- package/dist/types/CloudflareBindings.js +109 -0
- package/dist/types/CloudflareBindings.js.map +1 -0
- package/dist/types/Collection.js +50 -0
- package/dist/types/Collection.js.map +1 -0
- package/dist/types/DO.js +2 -0
- package/dist/types/DO.js.map +1 -0
- package/dist/types/DOLocation.js +63 -0
- package/dist/types/DOLocation.js.map +1 -0
- package/dist/types/EventHandler.js +57 -0
- package/dist/types/EventHandler.js.map +1 -0
- package/dist/types/Experiment.js +33 -0
- package/dist/types/Experiment.js.map +1 -0
- package/dist/types/Flag.js +57 -0
- package/dist/types/Flag.js.map +1 -0
- package/dist/types/Lifecycle.js +13 -0
- package/dist/types/Lifecycle.js.map +1 -0
- package/dist/types/Location.js +169 -0
- package/dist/types/Location.js.map +1 -0
- package/dist/types/Noun.js +66 -0
- package/dist/types/Noun.js.map +1 -0
- package/dist/types/SessionEvent.js +194 -0
- package/dist/types/SessionEvent.js.map +1 -0
- package/dist/types/Thing.js +55 -0
- package/dist/types/Thing.js.map +1 -0
- package/dist/types/ThingDO.js +153 -0
- package/dist/types/ThingDO.js.map +1 -0
- package/dist/types/Things.js +2 -0
- package/dist/types/Things.js.map +1 -0
- package/dist/types/Verb.js +119 -0
- package/dist/types/Verb.js.map +1 -0
- package/dist/types/WorkflowContext.js +70 -0
- package/dist/types/WorkflowContext.js.map +1 -0
- package/dist/types/analytics-api.js +13 -0
- package/dist/types/analytics-api.js.map +1 -0
- package/dist/types/capabilities.js +135 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/drizzle.js +12 -0
- package/dist/types/drizzle.js.map +1 -0
- package/dist/types/event.js +201 -0
- package/dist/types/event.js.map +1 -0
- package/dist/types/fn.js +12 -0
- package/dist/types/fn.js.map +1 -0
- package/dist/types/iceberg.js +48 -0
- package/dist/types/iceberg.js.map +1 -0
- package/dist/types/ids.js +170 -0
- package/dist/types/ids.js.map +1 -0
- package/dist/types/index.js +41 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/introspect.js +54 -0
- package/dist/types/introspect.js.map +1 -0
- package/dist/types/observability.js +124 -0
- package/dist/types/observability.js.map +1 -0
- package/dist/types/sync-protocol.js +175 -0
- package/dist/types/sync-protocol.js.map +1 -0
- package/dist/types/vector.js +13 -0
- package/dist/types/vector.js.map +1 -0
- package/dist/workflows/ScheduleManager.js +473 -0
- package/dist/workflows/ScheduleManager.js.map +1 -0
- package/dist/workflows/StepDOBridge.js +149 -0
- package/dist/workflows/StepDOBridge.js.map +1 -0
- package/dist/workflows/StepResultStorage.js +232 -0
- package/dist/workflows/StepResultStorage.js.map +1 -0
- package/dist/workflows/WaitForEventManager.js +461 -0
- package/dist/workflows/WaitForEventManager.js.map +1 -0
- package/dist/workflows/analyzer.js +332 -0
- package/dist/workflows/analyzer.js.map +1 -0
- package/dist/workflows/compat/activity-router.js +484 -0
- package/dist/workflows/compat/activity-router.js.map +1 -0
- package/dist/workflows/compat/backends/cloudflare-workflows.js +431 -0
- package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -0
- package/dist/workflows/compat/backends/index.js +14 -0
- package/dist/workflows/compat/backends/index.js.map +1 -0
- package/dist/workflows/compat/errors/index.js +375 -0
- package/dist/workflows/compat/errors/index.js.map +1 -0
- package/dist/workflows/compat/index.js +79 -0
- package/dist/workflows/compat/index.js.map +1 -0
- package/dist/workflows/compat/inngest/index.js +989 -0
- package/dist/workflows/compat/inngest/index.js.map +1 -0
- package/dist/workflows/compat/qstash/index.js +1263 -0
- package/dist/workflows/compat/qstash/index.js.map +1 -0
- package/dist/workflows/compat/temporal/activities.js +739 -0
- package/dist/workflows/compat/temporal/activities.js.map +1 -0
- package/dist/workflows/compat/temporal/child-workflows.js +154 -0
- package/dist/workflows/compat/temporal/child-workflows.js.map +1 -0
- package/dist/workflows/compat/temporal/client.js +381 -0
- package/dist/workflows/compat/temporal/client.js.map +1 -0
- package/dist/workflows/compat/temporal/context.js +309 -0
- package/dist/workflows/compat/temporal/context.js.map +1 -0
- package/dist/workflows/compat/temporal/determinism.js +216 -0
- package/dist/workflows/compat/temporal/determinism.js.map +1 -0
- package/dist/workflows/compat/temporal/errors.js +128 -0
- package/dist/workflows/compat/temporal/errors.js.map +1 -0
- package/dist/workflows/compat/temporal/index.js +2464 -0
- package/dist/workflows/compat/temporal/index.js.map +1 -0
- package/dist/workflows/compat/temporal/saga.js +504 -0
- package/dist/workflows/compat/temporal/saga.js.map +1 -0
- package/dist/workflows/compat/temporal/signals.js +364 -0
- package/dist/workflows/compat/temporal/signals.js.map +1 -0
- package/dist/workflows/compat/temporal/storage.js +271 -0
- package/dist/workflows/compat/temporal/storage.js.map +1 -0
- package/dist/workflows/compat/temporal/timers.js +347 -0
- package/dist/workflows/compat/temporal/timers.js.map +1 -0
- package/dist/workflows/compat/temporal/types.js +7 -0
- package/dist/workflows/compat/temporal/types.js.map +1 -0
- package/dist/workflows/compat/temporal/unified-primitives.js +339 -0
- package/dist/workflows/compat/temporal/unified-primitives.js.map +1 -0
- package/dist/workflows/compat/trigger/index.js +468 -0
- package/dist/workflows/compat/trigger/index.js.map +1 -0
- package/dist/workflows/compat/utils/index.js +69 -0
- package/dist/workflows/compat/utils/index.js.map +1 -0
- package/dist/workflows/context/correlation-capability.js +266 -0
- package/dist/workflows/context/correlation-capability.js.map +1 -0
- package/dist/workflows/context/correlation.js +484 -0
- package/dist/workflows/context/correlation.js.map +1 -0
- package/dist/workflows/context/experiment.js +289 -0
- package/dist/workflows/context/experiment.js.map +1 -0
- package/dist/workflows/context/flag.js +244 -0
- package/dist/workflows/context/flag.js.map +1 -0
- package/dist/workflows/context/foundation.js +648 -0
- package/dist/workflows/context/foundation.js.map +1 -0
- package/dist/workflows/context/human-base.js +106 -0
- package/dist/workflows/context/human-base.js.map +1 -0
- package/dist/workflows/context/human.js +368 -0
- package/dist/workflows/context/human.js.map +1 -0
- package/dist/workflows/context/measure.js +354 -0
- package/dist/workflows/context/measure.js.map +1 -0
- package/dist/workflows/context/rate-limit.js +358 -0
- package/dist/workflows/context/rate-limit.js.map +1 -0
- package/dist/workflows/context/user.js +117 -0
- package/dist/workflows/context/user.js.map +1 -0
- package/dist/workflows/context/vault.js +360 -0
- package/dist/workflows/context/vault.js.map +1 -0
- package/dist/workflows/data/entity-events/entity-events.js +489 -0
- package/dist/workflows/data/entity-events/entity-events.js.map +1 -0
- package/dist/workflows/data/experiment/index.js +599 -0
- package/dist/workflows/data/experiment/index.js.map +1 -0
- package/dist/workflows/data/goal/context.js +558 -0
- package/dist/workflows/data/goal/context.js.map +1 -0
- package/dist/workflows/data/goal/index.js +32 -0
- package/dist/workflows/data/goal/index.js.map +1 -0
- package/dist/workflows/data/measure/index.js +840 -0
- package/dist/workflows/data/measure/index.js.map +1 -0
- package/dist/workflows/data/stream/index.js +1215 -0
- package/dist/workflows/data/stream/index.js.map +1 -0
- package/dist/workflows/data/track/context.js +883 -0
- package/dist/workflows/data/track/context.js.map +1 -0
- package/dist/workflows/data/track/index.js +15 -0
- package/dist/workflows/data/track/index.js.map +1 -0
- package/dist/workflows/data/view/context.js +864 -0
- package/dist/workflows/data/view/context.js.map +1 -0
- package/dist/workflows/domain.js +93 -0
- package/dist/workflows/domain.js.map +1 -0
- package/dist/workflows/flag.js +176 -0
- package/dist/workflows/flag.js.map +1 -0
- package/dist/workflows/flags.js +217 -0
- package/dist/workflows/flags.js.map +1 -0
- package/dist/workflows/hash.js +209 -0
- package/dist/workflows/hash.js.map +1 -0
- package/dist/workflows/index.js +50 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/on.js +378 -0
- package/dist/workflows/on.js.map +1 -0
- package/dist/workflows/pipeline-promise.js +481 -0
- package/dist/workflows/pipeline-promise.js.map +1 -0
- package/dist/workflows/pipeline-types.js +20 -0
- package/dist/workflows/pipeline-types.js.map +1 -0
- package/dist/workflows/proxy.js +76 -0
- package/dist/workflows/proxy.js.map +1 -0
- package/dist/workflows/runtime.js +310 -0
- package/dist/workflows/runtime.js.map +1 -0
- package/dist/workflows/schedule-builder.js +327 -0
- package/dist/workflows/schedule-builder.js.map +1 -0
- package/dist/workflows/visibility/index.js +148 -0
- package/dist/workflows/visibility/index.js.map +1 -0
- package/dist/workflows/visibility/query-parser.js +150 -0
- package/dist/workflows/visibility/query-parser.js.map +1 -0
- package/dist/workflows/visibility/store.js +223 -0
- package/dist/workflows/visibility/store.js.map +1 -0
- package/dist/workflows/visibility/types.js +30 -0
- package/dist/workflows/visibility/types.js.map +1 -0
- package/dist/workflows/workflow.js +53 -0
- package/dist/workflows/workflow.js.map +1 -0
- package/package.json +279 -46
|
@@ -0,0 +1,1550 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Processing Commands Implementation
|
|
3
|
+
*
|
|
4
|
+
* Implements sed, awk, diff, patch, tee, and xargs commands
|
|
5
|
+
* for native Tier 1 execution in bashx.
|
|
6
|
+
*
|
|
7
|
+
* @module bashx/do/commands/text-processing
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// SHARED UTILITIES
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* LRU cache for compiled regular expressions.
|
|
14
|
+
* Improves performance when the same pattern is used multiple times.
|
|
15
|
+
*/
|
|
16
|
+
class RegexCache {
|
|
17
|
+
cache = new Map();
|
|
18
|
+
maxSize;
|
|
19
|
+
constructor(maxSize = 100) {
|
|
20
|
+
this.maxSize = maxSize;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get or compile a regex pattern with caching
|
|
24
|
+
* @param pattern - The regex pattern string
|
|
25
|
+
* @param flags - Optional regex flags
|
|
26
|
+
* @returns Compiled RegExp object
|
|
27
|
+
*/
|
|
28
|
+
get(pattern, flags = '') {
|
|
29
|
+
const key = `${pattern}:${flags}`;
|
|
30
|
+
let regex = this.cache.get(key);
|
|
31
|
+
if (!regex) {
|
|
32
|
+
regex = new RegExp(pattern, flags);
|
|
33
|
+
// Evict oldest entry if cache is full
|
|
34
|
+
if (this.cache.size >= this.maxSize) {
|
|
35
|
+
const firstKey = this.cache.keys().next().value;
|
|
36
|
+
if (firstKey)
|
|
37
|
+
this.cache.delete(firstKey);
|
|
38
|
+
}
|
|
39
|
+
this.cache.set(key, regex);
|
|
40
|
+
}
|
|
41
|
+
return regex;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Clear the cache
|
|
45
|
+
*/
|
|
46
|
+
clear() {
|
|
47
|
+
this.cache.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Global regex cache instance
|
|
51
|
+
const regexCache = new RegexCache();
|
|
52
|
+
/**
|
|
53
|
+
* Split input text into lines, handling trailing newlines consistently.
|
|
54
|
+
* @param text - Input text to split
|
|
55
|
+
* @returns Array of lines (without trailing empty line from split)
|
|
56
|
+
*/
|
|
57
|
+
function splitLines(text) {
|
|
58
|
+
const lines = text.split('\n');
|
|
59
|
+
const hasTrailingNewline = text.endsWith('\n');
|
|
60
|
+
if (hasTrailingNewline && lines[lines.length - 1] === '') {
|
|
61
|
+
lines.pop();
|
|
62
|
+
}
|
|
63
|
+
return lines;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Escape special regex characters in a string.
|
|
67
|
+
* @param str - String to escape
|
|
68
|
+
* @returns Escaped string safe for use in regex
|
|
69
|
+
*/
|
|
70
|
+
function escapeRegex(str) {
|
|
71
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Evaluate simple arithmetic expressions safely (Workers-compatible).
|
|
75
|
+
* @param left - Left operand
|
|
76
|
+
* @param op - Operator (+, -, *, /)
|
|
77
|
+
* @param right - Right operand
|
|
78
|
+
* @returns Result of the operation
|
|
79
|
+
*/
|
|
80
|
+
function evalArithmetic(left, op, right) {
|
|
81
|
+
switch (op) {
|
|
82
|
+
case '+': return left + right;
|
|
83
|
+
case '-': return left - right;
|
|
84
|
+
case '*': return left * right;
|
|
85
|
+
case '/': return right !== 0 ? left / right : 0;
|
|
86
|
+
default: return left;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Parse sed command arguments
|
|
91
|
+
*/
|
|
92
|
+
function parseSedArgs(args) {
|
|
93
|
+
const options = {
|
|
94
|
+
expressions: [],
|
|
95
|
+
};
|
|
96
|
+
const files = [];
|
|
97
|
+
let script = '';
|
|
98
|
+
let i = 0;
|
|
99
|
+
while (i < args.length) {
|
|
100
|
+
const arg = args[i];
|
|
101
|
+
if (arg === '-n') {
|
|
102
|
+
options.quiet = true;
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
else if (arg === '-E' || arg === '-r') {
|
|
106
|
+
options.extended = true;
|
|
107
|
+
i++;
|
|
108
|
+
}
|
|
109
|
+
else if (arg === '-i') {
|
|
110
|
+
options.inPlace = true;
|
|
111
|
+
i++;
|
|
112
|
+
}
|
|
113
|
+
else if (arg.startsWith('-i')) {
|
|
114
|
+
options.inPlace = true;
|
|
115
|
+
options.inPlaceSuffix = arg.slice(2);
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
else if (arg === '-e') {
|
|
119
|
+
i++;
|
|
120
|
+
if (i < args.length) {
|
|
121
|
+
options.expressions.push(args[i]);
|
|
122
|
+
i++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else if (arg.startsWith('-')) {
|
|
126
|
+
// Skip unknown options
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
else if (!script && options.expressions.length === 0) {
|
|
130
|
+
script = arg;
|
|
131
|
+
i++;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
files.push(arg);
|
|
135
|
+
i++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { options, script, files };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parse a sed substitution command (s/pattern/replacement/flags).
|
|
142
|
+
* Supports various delimiters and converts sed regex syntax to JavaScript.
|
|
143
|
+
*
|
|
144
|
+
* @param script - The sed substitution command (e.g., 's/foo/bar/g')
|
|
145
|
+
* @returns Parsed substitution object or null if not a valid substitution
|
|
146
|
+
*/
|
|
147
|
+
function parseSedSubstitution(script) {
|
|
148
|
+
// Match s/pattern/replacement/flags with various delimiters
|
|
149
|
+
const delimMatch = script.match(/^s(.)/);
|
|
150
|
+
if (!delimMatch)
|
|
151
|
+
return null;
|
|
152
|
+
const delim = delimMatch[1];
|
|
153
|
+
const escapedDelim = escapeRegex(delim);
|
|
154
|
+
const regex = regexCache.get(`^s${escapedDelim}((?:[^${escapedDelim}\\\\]|\\\\.)*)${escapedDelim}((?:[^${escapedDelim}\\\\]|\\\\.)*)${escapedDelim}([gip]*)$`);
|
|
155
|
+
const match = script.match(regex);
|
|
156
|
+
if (!match)
|
|
157
|
+
return null;
|
|
158
|
+
const [, pattern, replacement, flags] = match;
|
|
159
|
+
const global = flags.includes('g');
|
|
160
|
+
const ignoreCase = flags.includes('i');
|
|
161
|
+
// Convert sed regex to JavaScript regex
|
|
162
|
+
// Replace sed escape sequences with JS equivalents
|
|
163
|
+
// \\( -> ( (grouping in sed basic regex)
|
|
164
|
+
// \\) -> ) (grouping in sed basic regex)
|
|
165
|
+
// Note: In patterns, \1 stays as \1 for backreferences (JS regex uses \1 in pattern)
|
|
166
|
+
// Only in replacements, \1 becomes $1 (JS uses $1 in replacement strings)
|
|
167
|
+
let jsPattern = pattern
|
|
168
|
+
.replace(/\\\(/g, '(')
|
|
169
|
+
.replace(/\\\)/g, ')');
|
|
170
|
+
let jsReplacement = replacement
|
|
171
|
+
.replace(/\\1/g, '$1')
|
|
172
|
+
.replace(/\\2/g, '$2')
|
|
173
|
+
.replace(/\\3/g, '$3')
|
|
174
|
+
.replace(/\\4/g, '$4')
|
|
175
|
+
.replace(/\\5/g, '$5')
|
|
176
|
+
.replace(/\\6/g, '$6')
|
|
177
|
+
.replace(/\\7/g, '$7')
|
|
178
|
+
.replace(/\\8/g, '$8')
|
|
179
|
+
.replace(/\\9/g, '$9');
|
|
180
|
+
const regexFlags = ignoreCase ? 'i' : '';
|
|
181
|
+
return {
|
|
182
|
+
pattern: new RegExp(jsPattern, regexFlags),
|
|
183
|
+
replacement: jsReplacement,
|
|
184
|
+
global,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Parse sed print command (Np or N,Mp where N,M are line numbers)
|
|
189
|
+
*/
|
|
190
|
+
function parseSedPrint(script) {
|
|
191
|
+
// Match patterns like '5p', '2,4p', '$p', '1,$p'
|
|
192
|
+
const match = script.match(/^(\d+|\$)(?:,(\d+|\$))?p$/);
|
|
193
|
+
if (!match)
|
|
194
|
+
return null;
|
|
195
|
+
const start = match[1] === '$' ? '$' : parseInt(match[1], 10);
|
|
196
|
+
const end = match[2] ? (match[2] === '$' ? '$' : parseInt(match[2], 10)) : undefined;
|
|
197
|
+
return { start, end };
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Parse sed delete command (Nd or N,Md or /pattern/d)
|
|
201
|
+
*/
|
|
202
|
+
function parseSedDelete(script) {
|
|
203
|
+
// Match pattern delete like /^a/d
|
|
204
|
+
const patternMatch = script.match(/^\/(.+)\/d$/);
|
|
205
|
+
if (patternMatch) {
|
|
206
|
+
return { pattern: new RegExp(patternMatch[1]) };
|
|
207
|
+
}
|
|
208
|
+
// Match line range delete like '2d' or '1,10d'
|
|
209
|
+
const rangeMatch = script.match(/^(\d+)(?:,(\d+))?d$/);
|
|
210
|
+
if (rangeMatch) {
|
|
211
|
+
return {
|
|
212
|
+
start: parseInt(rangeMatch[1], 10),
|
|
213
|
+
end: rangeMatch[2] ? parseInt(rangeMatch[2], 10) : undefined,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Execute sed (stream editor) command.
|
|
220
|
+
*
|
|
221
|
+
* Supports substitution (s/pattern/replacement/flags), line printing with -n and p,
|
|
222
|
+
* line deletion with d, and multiple -e expressions.
|
|
223
|
+
*
|
|
224
|
+
* @param args - Command arguments (e.g., ['-e', 's/foo/bar/', '/path/to/file'])
|
|
225
|
+
* @param input - Input text to process (used when no file is specified)
|
|
226
|
+
* @param fs - Optional filesystem capability for in-place editing
|
|
227
|
+
* @returns Object with stdout, stderr, and exitCode
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* // Basic substitution
|
|
231
|
+
* executeSed(['s/hello/world/g'], 'hello hello')
|
|
232
|
+
* // => { stdout: 'world world\n', stderr: '', exitCode: 0 }
|
|
233
|
+
*/
|
|
234
|
+
export function executeSed(args, input, _fs) {
|
|
235
|
+
const { options, script, files: _files } = parseSedArgs(args);
|
|
236
|
+
void _files; // Reserved for future file input support
|
|
237
|
+
const scripts = options.expressions.length > 0 ? options.expressions : [script];
|
|
238
|
+
// If files provided, we'd need to read from fs - for now just use input
|
|
239
|
+
let content = input;
|
|
240
|
+
const processLine = (line, lineNum, totalLines) => {
|
|
241
|
+
let result = line;
|
|
242
|
+
for (const s of scripts) {
|
|
243
|
+
if (result === null)
|
|
244
|
+
break;
|
|
245
|
+
// Try substitution
|
|
246
|
+
const sub = parseSedSubstitution(s);
|
|
247
|
+
if (sub) {
|
|
248
|
+
if (sub.global) {
|
|
249
|
+
result = result.replace(new RegExp(sub.pattern.source, sub.pattern.flags + 'g'), sub.replacement);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
result = result.replace(sub.pattern, sub.replacement);
|
|
253
|
+
}
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
// Try print (only relevant when -n is used)
|
|
257
|
+
const print = parseSedPrint(s);
|
|
258
|
+
if (print && options.quiet) {
|
|
259
|
+
const startLine = print.start === '$' ? totalLines : print.start;
|
|
260
|
+
const endLine = print.end === undefined ? startLine : (print.end === '$' ? totalLines : print.end);
|
|
261
|
+
if (lineNum < startLine || lineNum > endLine) {
|
|
262
|
+
result = null;
|
|
263
|
+
}
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
// Try delete
|
|
267
|
+
const del = parseSedDelete(s);
|
|
268
|
+
if (del) {
|
|
269
|
+
if (del.pattern) {
|
|
270
|
+
if (del.pattern.test(result)) {
|
|
271
|
+
result = null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else if (del.start !== undefined) {
|
|
275
|
+
const endLine = del.end ?? del.start;
|
|
276
|
+
if (lineNum >= del.start && lineNum <= endLine) {
|
|
277
|
+
result = null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
};
|
|
285
|
+
const lines = splitLines(content);
|
|
286
|
+
const totalLines = lines.length;
|
|
287
|
+
const outputLines = [];
|
|
288
|
+
for (let i = 0; i < lines.length; i++) {
|
|
289
|
+
const result = processLine(lines[i], i + 1, totalLines);
|
|
290
|
+
if (result !== null) {
|
|
291
|
+
outputLines.push(result);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
let stdout = outputLines.join('\n');
|
|
295
|
+
if (outputLines.length > 0) {
|
|
296
|
+
stdout += '\n';
|
|
297
|
+
}
|
|
298
|
+
return { stdout, stderr: '', exitCode: 0 };
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Parse awk command arguments
|
|
302
|
+
*/
|
|
303
|
+
function parseAwkArgs(args) {
|
|
304
|
+
const options = {
|
|
305
|
+
fieldSeparator: /\s+/.source, // Default: whitespace
|
|
306
|
+
outputFieldSeparator: ' ',
|
|
307
|
+
outputRecordSeparator: '\n',
|
|
308
|
+
};
|
|
309
|
+
const files = [];
|
|
310
|
+
let program = '';
|
|
311
|
+
let i = 0;
|
|
312
|
+
while (i < args.length) {
|
|
313
|
+
const arg = args[i];
|
|
314
|
+
if (arg === '-F') {
|
|
315
|
+
i++;
|
|
316
|
+
if (i < args.length) {
|
|
317
|
+
let sep = args[i];
|
|
318
|
+
// Remove quotes if present
|
|
319
|
+
if ((sep.startsWith("'") && sep.endsWith("'")) || (sep.startsWith('"') && sep.endsWith('"'))) {
|
|
320
|
+
sep = sep.slice(1, -1);
|
|
321
|
+
}
|
|
322
|
+
options.fieldSeparator = sep.replace(/\\t/g, '\t');
|
|
323
|
+
i++;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (arg.startsWith('-F')) {
|
|
327
|
+
let sep = arg.slice(2);
|
|
328
|
+
// Remove quotes if present
|
|
329
|
+
if ((sep.startsWith("'") && sep.endsWith("'")) || (sep.startsWith('"') && sep.endsWith('"'))) {
|
|
330
|
+
sep = sep.slice(1, -1);
|
|
331
|
+
}
|
|
332
|
+
options.fieldSeparator = sep.replace(/\\t/g, '\t');
|
|
333
|
+
i++;
|
|
334
|
+
}
|
|
335
|
+
else if (arg.startsWith('-')) {
|
|
336
|
+
// Skip unknown options
|
|
337
|
+
i++;
|
|
338
|
+
}
|
|
339
|
+
else if (!program) {
|
|
340
|
+
program = arg;
|
|
341
|
+
i++;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
files.push(arg);
|
|
345
|
+
i++;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return { options, program, files };
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Parse an awk program into its components
|
|
352
|
+
*/
|
|
353
|
+
function parseAwkProgram(program) {
|
|
354
|
+
const result = {};
|
|
355
|
+
// Extract BEGIN block
|
|
356
|
+
const beginMatch = program.match(/BEGIN\s*\{([^}]*)\}/i);
|
|
357
|
+
if (beginMatch) {
|
|
358
|
+
result.begin = beginMatch[1].trim();
|
|
359
|
+
program = program.replace(beginMatch[0], '');
|
|
360
|
+
}
|
|
361
|
+
// Extract END block
|
|
362
|
+
const endMatch = program.match(/END\s*\{([^}]*)\}/i);
|
|
363
|
+
if (endMatch) {
|
|
364
|
+
result.end = endMatch[1].trim();
|
|
365
|
+
program = program.replace(endMatch[0], '');
|
|
366
|
+
}
|
|
367
|
+
// Parse main pattern/action
|
|
368
|
+
program = program.trim();
|
|
369
|
+
if (program) {
|
|
370
|
+
// Match pattern { action } or just { action } or just condition
|
|
371
|
+
const patternActionMatch = program.match(/^(\/[^/]+\/|[^{]+)?\s*\{([^}]*)\}$/);
|
|
372
|
+
if (patternActionMatch) {
|
|
373
|
+
result.main = {
|
|
374
|
+
pattern: patternActionMatch[1]?.trim(),
|
|
375
|
+
action: patternActionMatch[2].trim(),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
else if (program.startsWith('{') && program.endsWith('}')) {
|
|
379
|
+
result.main = {
|
|
380
|
+
action: program.slice(1, -1).trim(),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
// Just a condition like NR==5 - implicit print
|
|
385
|
+
result.main = {
|
|
386
|
+
pattern: program,
|
|
387
|
+
action: 'print',
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Evaluate an awk expression
|
|
395
|
+
*/
|
|
396
|
+
function evaluateAwkExpression(expr, fields, variables, options) {
|
|
397
|
+
// Replace field references $1, $2, $NF, $0
|
|
398
|
+
let result = expr;
|
|
399
|
+
// Replace $NF with last field
|
|
400
|
+
result = result.replace(/\$NF/g, fields[fields.length - 1] || '');
|
|
401
|
+
// Replace $0 with full line
|
|
402
|
+
result = result.replace(/\$0/g, fields.join(options.outputFieldSeparator));
|
|
403
|
+
// Replace $n with nth field (1-indexed)
|
|
404
|
+
result = result.replace(/\$(\d+)/g, (_, n) => fields[parseInt(n, 10) - 1] || '');
|
|
405
|
+
// Replace NR, NF, FS, OFS
|
|
406
|
+
result = result.replace(/\bNR\b/g, String(variables.NR));
|
|
407
|
+
result = result.replace(/\bNF\b/g, String(fields.length));
|
|
408
|
+
result = result.replace(/\bFS\b/g, String(variables.FS));
|
|
409
|
+
result = result.replace(/\bOFS\b/g, String(variables.OFS));
|
|
410
|
+
// Replace user-defined variables (after built-in ones)
|
|
411
|
+
// Use word boundary to match whole variable names
|
|
412
|
+
for (const [varName, varValue] of Object.entries(variables)) {
|
|
413
|
+
if (!['NR', 'NF', 'FS', 'OFS'].includes(varName)) {
|
|
414
|
+
// Word boundary - matches before/after word characters
|
|
415
|
+
// /sum -> matches sum, sum/count -> matches both sum and count
|
|
416
|
+
result = result.replace(new RegExp(`(?<![a-zA-Z_])${varName}(?![a-zA-Z0-9_])`, 'g'), String(varValue));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Handle arithmetic expressions like sum/count or 100/4
|
|
420
|
+
// Use the shared evalArithmetic helper (Workers-compatible, no eval/Function)
|
|
421
|
+
result = result.trim();
|
|
422
|
+
const arithMatch = result.match(/^([\d.]+)\s*([+\-*/])\s*([\d.]+)$/);
|
|
423
|
+
if (arithMatch) {
|
|
424
|
+
const [, leftStr, op, rightStr] = arithMatch;
|
|
425
|
+
const left = parseFloat(leftStr);
|
|
426
|
+
const right = parseFloat(rightStr);
|
|
427
|
+
if (!isNaN(left) && !isNaN(right)) {
|
|
428
|
+
result = String(evalArithmetic(left, op, right));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Execute an awk action
|
|
435
|
+
*/
|
|
436
|
+
function executeAwkAction(action, fields, variables, options) {
|
|
437
|
+
const outputParts = [];
|
|
438
|
+
let hasPrintf = false;
|
|
439
|
+
// Split multiple statements by semicolon
|
|
440
|
+
const statements = action.split(/;/).map(s => s.trim()).filter(Boolean);
|
|
441
|
+
for (const stmt of statements) {
|
|
442
|
+
// Handle print statement
|
|
443
|
+
if (stmt.startsWith('print')) {
|
|
444
|
+
const printArgs = stmt.slice(5).trim();
|
|
445
|
+
if (!printArgs) {
|
|
446
|
+
// print with no args prints $0
|
|
447
|
+
outputParts.push(fields.join(options.outputFieldSeparator));
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
// Parse print arguments
|
|
451
|
+
const parts = [];
|
|
452
|
+
// Handle printf-style
|
|
453
|
+
if (stmt.startsWith('printf')) {
|
|
454
|
+
hasPrintf = true;
|
|
455
|
+
const printfMatch = stmt.match(/printf\s+"([^"]*)"(?:\s*,\s*(.+))?/);
|
|
456
|
+
if (printfMatch) {
|
|
457
|
+
let format = printfMatch[1].replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
|
458
|
+
const argsStr = printfMatch[2];
|
|
459
|
+
if (argsStr) {
|
|
460
|
+
const args = argsStr.split(/\s*,\s*/).map(a => evaluateAwkExpression(a.trim(), fields, variables, options));
|
|
461
|
+
let argIndex = 0;
|
|
462
|
+
format = format.replace(/%(-?\d*\.?\d*)?([sdxef])/g, (_match, _width, type) => {
|
|
463
|
+
const val = args[argIndex++] || '';
|
|
464
|
+
if (type === 'd') {
|
|
465
|
+
return String(parseInt(val, 10) || 0);
|
|
466
|
+
}
|
|
467
|
+
return val;
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
outputParts.push(format);
|
|
471
|
+
}
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
// Regular print with comma-separated values
|
|
475
|
+
// Handle expressions like $1, $3 or $1 $3 (space = OFS, comma = OFS)
|
|
476
|
+
const argParts = printArgs.split(/\s*,\s*/);
|
|
477
|
+
for (const part of argParts) {
|
|
478
|
+
const evaluated = evaluateAwkExpression(part, fields, variables, options);
|
|
479
|
+
parts.push(evaluated);
|
|
480
|
+
}
|
|
481
|
+
outputParts.push(parts.join(options.outputFieldSeparator));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// Handle if statements (must come before general assignment check)
|
|
485
|
+
else if (stmt.startsWith('if')) {
|
|
486
|
+
const ifMatch = stmt.match(/if\s*\(([^)]+)\)\s*(\w+)\s*=\s*(.+)/);
|
|
487
|
+
if (ifMatch) {
|
|
488
|
+
const [, condition, varName, value] = ifMatch;
|
|
489
|
+
const condResult = evaluateAwkCondition(condition, fields, variables, options);
|
|
490
|
+
if (condResult) {
|
|
491
|
+
const evaluated = evaluateAwkExpression(value, fields, variables, options);
|
|
492
|
+
variables[varName] = parseFloat(evaluated) || evaluated;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// Handle variable increment (count++)
|
|
497
|
+
else if (stmt.match(/(\w+)\+\+/)) {
|
|
498
|
+
const match = stmt.match(/(\w+)\+\+/);
|
|
499
|
+
if (match) {
|
|
500
|
+
variables[match[1]] = (Number(variables[match[1]]) || 0) + 1;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// Handle variable assignments (after if statement check)
|
|
504
|
+
else if (stmt.includes('=') && !stmt.includes('==')) {
|
|
505
|
+
const assignMatch = stmt.match(/(\w+)\s*([+\-*/]?=)\s*(.+)/);
|
|
506
|
+
if (assignMatch) {
|
|
507
|
+
const [, varName, op, valueExpr] = assignMatch;
|
|
508
|
+
const evaluated = evaluateAwkExpression(valueExpr, fields, variables, options);
|
|
509
|
+
const numValue = parseFloat(evaluated) || 0;
|
|
510
|
+
if (op === '=') {
|
|
511
|
+
variables[varName] = numValue;
|
|
512
|
+
}
|
|
513
|
+
else if (op === '+=') {
|
|
514
|
+
variables[varName] = (Number(variables[varName]) || 0) + numValue;
|
|
515
|
+
}
|
|
516
|
+
else if (op === '-=') {
|
|
517
|
+
variables[varName] = (Number(variables[varName]) || 0) - numValue;
|
|
518
|
+
}
|
|
519
|
+
else if (op === '*=') {
|
|
520
|
+
variables[varName] = (Number(variables[varName]) || 0) * numValue;
|
|
521
|
+
}
|
|
522
|
+
else if (op === '/=') {
|
|
523
|
+
variables[varName] = (Number(variables[varName]) || 0) / numValue;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// For printf, join without additional separators since printf controls its own newlines
|
|
529
|
+
// For regular print, use ORS as separator
|
|
530
|
+
const output = hasPrintf
|
|
531
|
+
? outputParts.join('')
|
|
532
|
+
: outputParts.join(options.outputRecordSeparator);
|
|
533
|
+
return { output, isPrintf: hasPrintf };
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Evaluate an awk condition
|
|
537
|
+
*/
|
|
538
|
+
function evaluateAwkCondition(condition, fields, variables, options) {
|
|
539
|
+
condition = condition.trim();
|
|
540
|
+
// Handle compound conditions with && first (lower precedence than comparison operators)
|
|
541
|
+
if (condition.includes('&&')) {
|
|
542
|
+
const parts = condition.split(/\s*&&\s*/);
|
|
543
|
+
return parts.every(part => evaluateAwkCondition(part.trim(), fields, variables, options));
|
|
544
|
+
}
|
|
545
|
+
// Handle compound conditions with ||
|
|
546
|
+
if (condition.includes('||')) {
|
|
547
|
+
const parts = condition.split(/\s*\|\|\s*/);
|
|
548
|
+
return parts.some(part => evaluateAwkCondition(part.trim(), fields, variables, options));
|
|
549
|
+
}
|
|
550
|
+
// Handle regex pattern /pattern/
|
|
551
|
+
if (condition.startsWith('/') && condition.endsWith('/')) {
|
|
552
|
+
const pattern = condition.slice(1, -1);
|
|
553
|
+
const line = fields.join(options.outputFieldSeparator);
|
|
554
|
+
return new RegExp(pattern).test(line);
|
|
555
|
+
}
|
|
556
|
+
// Handle negated regex !/pattern/
|
|
557
|
+
if (condition.startsWith('!/') && condition.endsWith('/')) {
|
|
558
|
+
const pattern = condition.slice(2, -1);
|
|
559
|
+
const line = fields.join(options.outputFieldSeparator);
|
|
560
|
+
return !new RegExp(pattern).test(line);
|
|
561
|
+
}
|
|
562
|
+
// Handle comparisons like NR==5, $1>10, etc.
|
|
563
|
+
const compMatch = condition.match(/(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)/);
|
|
564
|
+
if (compMatch) {
|
|
565
|
+
const [, left, op, right] = compMatch;
|
|
566
|
+
const leftVal = evaluateAwkExpression(left.trim(), fields, variables, options);
|
|
567
|
+
const rightVal = evaluateAwkExpression(right.trim(), fields, variables, options);
|
|
568
|
+
const leftNum = parseFloat(leftVal);
|
|
569
|
+
const rightNum = parseFloat(rightVal);
|
|
570
|
+
const useNumeric = !isNaN(leftNum) && !isNaN(rightNum);
|
|
571
|
+
switch (op) {
|
|
572
|
+
case '==': return useNumeric ? leftNum === rightNum : leftVal === rightVal;
|
|
573
|
+
case '!=': return useNumeric ? leftNum !== rightNum : leftVal !== rightVal;
|
|
574
|
+
case '>=': return useNumeric ? leftNum >= rightNum : leftVal >= rightVal;
|
|
575
|
+
case '<=': return useNumeric ? leftNum <= rightNum : leftVal <= rightVal;
|
|
576
|
+
case '>': return useNumeric ? leftNum > rightNum : leftVal > rightVal;
|
|
577
|
+
case '<': return useNumeric ? leftNum < rightNum : leftVal < rightVal;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// Default: truthy check
|
|
581
|
+
const evaluated = evaluateAwkExpression(condition, fields, variables, options);
|
|
582
|
+
return Boolean(evaluated);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Execute awk (pattern scanning and processing) command.
|
|
586
|
+
*
|
|
587
|
+
* Supports field extraction ($1, $2, $NF), pattern matching, BEGIN/END blocks,
|
|
588
|
+
* custom field separators (-F), variables, arithmetic, and control flow.
|
|
589
|
+
*
|
|
590
|
+
* @param args - Command arguments (e.g., ['-F:', '{print $1}', '/path/to/file'])
|
|
591
|
+
* @param input - Input text to process
|
|
592
|
+
* @returns Object with stdout, stderr, and exitCode
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* // Print first field
|
|
596
|
+
* executeAwk(['{print $1}'], 'hello world')
|
|
597
|
+
* // => { stdout: 'hello\n', stderr: '', exitCode: 0 }
|
|
598
|
+
*
|
|
599
|
+
* @example
|
|
600
|
+
* // Sum a column
|
|
601
|
+
* executeAwk(['{sum+=$1} END {print sum}'], '10\n20\n30')
|
|
602
|
+
* // => { stdout: '60\n', stderr: '', exitCode: 0 }
|
|
603
|
+
*/
|
|
604
|
+
export function executeAwk(args, input) {
|
|
605
|
+
const { options, program } = parseAwkArgs(args);
|
|
606
|
+
const parsed = parseAwkProgram(program);
|
|
607
|
+
const variables = {
|
|
608
|
+
NR: 0,
|
|
609
|
+
NF: 0,
|
|
610
|
+
FS: options.fieldSeparator,
|
|
611
|
+
OFS: options.outputFieldSeparator,
|
|
612
|
+
};
|
|
613
|
+
const output = [];
|
|
614
|
+
// Execute BEGIN block
|
|
615
|
+
if (parsed.begin) {
|
|
616
|
+
// Parse OFS/ORS assignments in BEGIN
|
|
617
|
+
const ofsMatch = parsed.begin.match(/OFS\s*=\s*"([^"]*)"/);
|
|
618
|
+
if (ofsMatch) {
|
|
619
|
+
options.outputFieldSeparator = ofsMatch[1];
|
|
620
|
+
variables.OFS = ofsMatch[1];
|
|
621
|
+
}
|
|
622
|
+
const orsMatch = parsed.begin.match(/ORS\s*=\s*"([^"]*)"/);
|
|
623
|
+
if (orsMatch) {
|
|
624
|
+
options.outputRecordSeparator = orsMatch[1];
|
|
625
|
+
}
|
|
626
|
+
// Initialize variables
|
|
627
|
+
const varMatches = parsed.begin.matchAll(/(\w+)\s*=\s*(\d+)/g);
|
|
628
|
+
for (const match of varMatches) {
|
|
629
|
+
variables[match[1]] = parseInt(match[2], 10);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// Process input lines
|
|
633
|
+
const lines = splitLines(input);
|
|
634
|
+
for (let i = 0; i < lines.length; i++) {
|
|
635
|
+
const line = lines[i];
|
|
636
|
+
variables.NR = i + 1;
|
|
637
|
+
// Split into fields using cached regex
|
|
638
|
+
const fieldSep = options.fieldSeparator === /\s+/.source
|
|
639
|
+
? /\s+/
|
|
640
|
+
: regexCache.get(escapeRegex(options.fieldSeparator));
|
|
641
|
+
const fields = ['', ...line.split(fieldSep)]; // $0 is handled specially, but fields[1] = $1
|
|
642
|
+
fields[0] = line; // $0 is the whole line
|
|
643
|
+
variables.NF = fields.length - 1;
|
|
644
|
+
if (parsed.main) {
|
|
645
|
+
// Check pattern if present
|
|
646
|
+
let shouldExecute = true;
|
|
647
|
+
if (parsed.main.pattern) {
|
|
648
|
+
shouldExecute = evaluateAwkCondition(parsed.main.pattern, fields.slice(1), variables, options);
|
|
649
|
+
}
|
|
650
|
+
if (shouldExecute) {
|
|
651
|
+
const result = executeAwkAction(parsed.main.action, fields.slice(1), variables, options);
|
|
652
|
+
if (result.output) {
|
|
653
|
+
output.push(result);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Execute END block
|
|
659
|
+
if (parsed.end) {
|
|
660
|
+
const fields = [''];
|
|
661
|
+
const result = executeAwkAction(parsed.end, fields, variables, options);
|
|
662
|
+
if (result.output) {
|
|
663
|
+
output.push(result);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
// Build final output - printf handles its own newlines, print uses ORS
|
|
667
|
+
let stdout = '';
|
|
668
|
+
for (let i = 0; i < output.length; i++) {
|
|
669
|
+
const item = output[i];
|
|
670
|
+
stdout += item.output;
|
|
671
|
+
// Only add ORS after non-printf outputs that don't already end with newline
|
|
672
|
+
if (!item.isPrintf && !item.output.endsWith('\n')) {
|
|
673
|
+
stdout += options.outputRecordSeparator;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
// Add final ORS if needed (for non-empty output not ending in newline)
|
|
677
|
+
if (stdout.length > 0 && !stdout.endsWith('\n') && options.outputRecordSeparator === '\n') {
|
|
678
|
+
stdout += '\n';
|
|
679
|
+
}
|
|
680
|
+
return { stdout, stderr: '', exitCode: 0 };
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Myers diff algorithm - find shortest edit script
|
|
684
|
+
*/
|
|
685
|
+
function myersDiff(oldLines, newLines) {
|
|
686
|
+
const n = oldLines.length;
|
|
687
|
+
const m = newLines.length;
|
|
688
|
+
const max = n + m;
|
|
689
|
+
const v = { 1: 0 };
|
|
690
|
+
const trace = [];
|
|
691
|
+
// Find the shortest edit script
|
|
692
|
+
outer: for (let d = 0; d <= max; d++) {
|
|
693
|
+
trace.push({ ...v });
|
|
694
|
+
for (let k = -d; k <= d; k += 2) {
|
|
695
|
+
let x;
|
|
696
|
+
if (k === -d || (k !== d && v[k - 1] < v[k + 1])) {
|
|
697
|
+
x = v[k + 1];
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
x = v[k - 1] + 1;
|
|
701
|
+
}
|
|
702
|
+
let y = x - k;
|
|
703
|
+
while (x < n && y < m && oldLines[x] === newLines[y]) {
|
|
704
|
+
x++;
|
|
705
|
+
y++;
|
|
706
|
+
}
|
|
707
|
+
v[k] = x;
|
|
708
|
+
if (x >= n && y >= m) {
|
|
709
|
+
break outer;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Backtrack to find the path
|
|
714
|
+
const edits = [];
|
|
715
|
+
let x = n;
|
|
716
|
+
let y = m;
|
|
717
|
+
for (let d = trace.length - 1; d >= 0; d--) {
|
|
718
|
+
const vPrev = trace[d];
|
|
719
|
+
const k = x - y;
|
|
720
|
+
let prevK;
|
|
721
|
+
if (k === -d || (k !== d && vPrev[k - 1] < vPrev[k + 1])) {
|
|
722
|
+
prevK = k + 1;
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
prevK = k - 1;
|
|
726
|
+
}
|
|
727
|
+
const prevX = vPrev[prevK];
|
|
728
|
+
const prevY = prevX - prevK;
|
|
729
|
+
// Add equal lines (diagonal moves)
|
|
730
|
+
while (x > prevX && y > prevY) {
|
|
731
|
+
x--;
|
|
732
|
+
y--;
|
|
733
|
+
edits.unshift({ type: 'equal', oldIdx: x, newIdx: y });
|
|
734
|
+
}
|
|
735
|
+
if (d > 0) {
|
|
736
|
+
if (x === prevX) {
|
|
737
|
+
// Insert
|
|
738
|
+
y--;
|
|
739
|
+
edits.unshift({ type: 'insert', newIdx: y });
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
// Delete
|
|
743
|
+
x--;
|
|
744
|
+
edits.unshift({ type: 'delete', oldIdx: x });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return edits;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Format diff in normal format
|
|
752
|
+
*/
|
|
753
|
+
function formatNormalDiff(oldLines, newLines, edits) {
|
|
754
|
+
const output = [];
|
|
755
|
+
let i = 0;
|
|
756
|
+
while (i < edits.length) {
|
|
757
|
+
const edit = edits[i];
|
|
758
|
+
if (edit.type === 'equal') {
|
|
759
|
+
i++;
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
// Collect consecutive changes
|
|
763
|
+
const changes = [];
|
|
764
|
+
while (i < edits.length && edits[i].type !== 'equal') {
|
|
765
|
+
changes.push(edits[i]);
|
|
766
|
+
i++;
|
|
767
|
+
}
|
|
768
|
+
const deletes = changes.filter(c => c.type === 'delete');
|
|
769
|
+
const inserts = changes.filter(c => c.type === 'insert');
|
|
770
|
+
if (deletes.length > 0 && inserts.length > 0) {
|
|
771
|
+
// Change
|
|
772
|
+
const oldStart = deletes[0].oldIdx + 1;
|
|
773
|
+
const oldEnd = deletes[deletes.length - 1].oldIdx + 1;
|
|
774
|
+
const newStart = inserts[0].newIdx + 1;
|
|
775
|
+
const newEnd = inserts[inserts.length - 1].newIdx + 1;
|
|
776
|
+
const oldRange = oldStart === oldEnd ? `${oldStart}` : `${oldStart},${oldEnd}`;
|
|
777
|
+
const newRange = newStart === newEnd ? `${newStart}` : `${newStart},${newEnd}`;
|
|
778
|
+
output.push(`${oldRange}c${newRange}`);
|
|
779
|
+
for (const d of deletes) {
|
|
780
|
+
output.push(`< ${oldLines[d.oldIdx]}`);
|
|
781
|
+
}
|
|
782
|
+
output.push('---');
|
|
783
|
+
for (const ins of inserts) {
|
|
784
|
+
output.push(`> ${newLines[ins.newIdx]}`);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
else if (deletes.length > 0) {
|
|
788
|
+
// Delete
|
|
789
|
+
const oldStart = deletes[0].oldIdx + 1;
|
|
790
|
+
const oldEnd = deletes[deletes.length - 1].oldIdx + 1;
|
|
791
|
+
const newPos = (deletes[0].oldIdx || 0);
|
|
792
|
+
const oldRange = oldStart === oldEnd ? `${oldStart}` : `${oldStart},${oldEnd}`;
|
|
793
|
+
output.push(`${oldRange}d${newPos}`);
|
|
794
|
+
for (const d of deletes) {
|
|
795
|
+
output.push(`< ${oldLines[d.oldIdx]}`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else if (inserts.length > 0) {
|
|
799
|
+
// Add
|
|
800
|
+
const oldPos = inserts[0].newIdx;
|
|
801
|
+
const newStart = inserts[0].newIdx + 1;
|
|
802
|
+
const newEnd = inserts[inserts.length - 1].newIdx + 1;
|
|
803
|
+
const newRange = newStart === newEnd ? `${newStart}` : `${newStart},${newEnd}`;
|
|
804
|
+
output.push(`${oldPos}a${newRange}`);
|
|
805
|
+
for (const ins of inserts) {
|
|
806
|
+
output.push(`> ${newLines[ins.newIdx]}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return output.length > 0 ? output.join('\n') + '\n' : '';
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Format diff in unified format
|
|
814
|
+
*/
|
|
815
|
+
function formatUnifiedDiff(file1Path, file2Path, oldLines, newLines, edits, contextLines = 3) {
|
|
816
|
+
const output = [];
|
|
817
|
+
output.push(`--- ${file1Path}`);
|
|
818
|
+
output.push(`+++ ${file2Path}`);
|
|
819
|
+
// Group edits into hunks
|
|
820
|
+
const hunks = [];
|
|
821
|
+
let currentHunk = null;
|
|
822
|
+
let contextBuffer = [];
|
|
823
|
+
let oldLineNum = 0;
|
|
824
|
+
let newLineNum = 0;
|
|
825
|
+
for (const edit of edits) {
|
|
826
|
+
if (edit.type === 'equal') {
|
|
827
|
+
const line = oldLines[edit.oldIdx];
|
|
828
|
+
if (currentHunk) {
|
|
829
|
+
currentHunk.lines.push({ type: 'context', line });
|
|
830
|
+
currentHunk.oldCount++;
|
|
831
|
+
currentHunk.newCount++;
|
|
832
|
+
contextBuffer.push(line);
|
|
833
|
+
if (contextBuffer.length > contextLines * 2) {
|
|
834
|
+
// End current hunk
|
|
835
|
+
// Remove trailing context beyond limit
|
|
836
|
+
while (currentHunk.lines.length > 0 &&
|
|
837
|
+
currentHunk.lines[currentHunk.lines.length - 1].type === 'context' &&
|
|
838
|
+
contextBuffer.length > contextLines) {
|
|
839
|
+
currentHunk.lines.pop();
|
|
840
|
+
currentHunk.oldCount--;
|
|
841
|
+
currentHunk.newCount--;
|
|
842
|
+
contextBuffer.shift();
|
|
843
|
+
}
|
|
844
|
+
hunks.push(currentHunk);
|
|
845
|
+
currentHunk = null;
|
|
846
|
+
contextBuffer = [line];
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
contextBuffer.push(line);
|
|
851
|
+
if (contextBuffer.length > contextLines) {
|
|
852
|
+
contextBuffer.shift();
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
oldLineNum++;
|
|
856
|
+
newLineNum++;
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
if (!currentHunk) {
|
|
860
|
+
// Start new hunk with leading context
|
|
861
|
+
currentHunk = {
|
|
862
|
+
oldStart: Math.max(1, oldLineNum - contextBuffer.length + 1),
|
|
863
|
+
oldCount: contextBuffer.length,
|
|
864
|
+
newStart: Math.max(1, newLineNum - contextBuffer.length + 1),
|
|
865
|
+
newCount: contextBuffer.length,
|
|
866
|
+
lines: contextBuffer.map(l => ({ type: 'context', line: l })),
|
|
867
|
+
};
|
|
868
|
+
contextBuffer = [];
|
|
869
|
+
}
|
|
870
|
+
if (edit.type === 'delete') {
|
|
871
|
+
currentHunk.lines.push({ type: 'delete', line: oldLines[edit.oldIdx] });
|
|
872
|
+
currentHunk.oldCount++;
|
|
873
|
+
oldLineNum++;
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
currentHunk.lines.push({ type: 'add', line: newLines[edit.newIdx] });
|
|
877
|
+
currentHunk.newCount++;
|
|
878
|
+
newLineNum++;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (currentHunk) {
|
|
883
|
+
// Remove trailing context beyond limit
|
|
884
|
+
while (currentHunk.lines.length > 0 &&
|
|
885
|
+
currentHunk.lines[currentHunk.lines.length - 1].type === 'context') {
|
|
886
|
+
const lastNonContext = currentHunk.lines.slice().reverse().findIndex(l => l.type !== 'context');
|
|
887
|
+
if (lastNonContext === -1)
|
|
888
|
+
break;
|
|
889
|
+
const contextAfter = lastNonContext;
|
|
890
|
+
if (contextAfter > contextLines) {
|
|
891
|
+
currentHunk.lines.pop();
|
|
892
|
+
currentHunk.oldCount--;
|
|
893
|
+
currentHunk.newCount--;
|
|
894
|
+
}
|
|
895
|
+
else {
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
hunks.push(currentHunk);
|
|
900
|
+
}
|
|
901
|
+
// Format hunks
|
|
902
|
+
for (const hunk of hunks) {
|
|
903
|
+
output.push(`@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@`);
|
|
904
|
+
for (const line of hunk.lines) {
|
|
905
|
+
if (line.type === 'context') {
|
|
906
|
+
output.push(` ${line.line}`);
|
|
907
|
+
}
|
|
908
|
+
else if (line.type === 'delete') {
|
|
909
|
+
output.push(`-${line.line}`);
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
output.push(`+${line.line}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return output.join('\n') + '\n';
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Format diff in context format
|
|
920
|
+
*/
|
|
921
|
+
function formatContextDiff(file1Path, file2Path, oldLines, newLines, edits) {
|
|
922
|
+
const output = [];
|
|
923
|
+
output.push(`*** ${file1Path}`);
|
|
924
|
+
output.push(`--- ${file2Path}`);
|
|
925
|
+
output.push('***************');
|
|
926
|
+
// For simplicity, output entire file as one hunk
|
|
927
|
+
output.push(`*** 1,${oldLines.length} ****`);
|
|
928
|
+
for (let i = 0; i < oldLines.length; i++) {
|
|
929
|
+
const edit = edits.find(e => e.oldIdx === i && (e.type === 'equal' || e.type === 'delete'));
|
|
930
|
+
if (edit?.type === 'delete') {
|
|
931
|
+
output.push(`! ${oldLines[i]}`);
|
|
932
|
+
}
|
|
933
|
+
else if (edit?.type === 'equal') {
|
|
934
|
+
// Check if there's a change at this position
|
|
935
|
+
const hasChange = edits.some(e => e.type === 'insert' && e.newIdx === edit.newIdx);
|
|
936
|
+
if (hasChange) {
|
|
937
|
+
output.push(`! ${oldLines[i]}`);
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
output.push(` ${oldLines[i]}`);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
output.push(`--- 1,${newLines.length} ----`);
|
|
945
|
+
for (let i = 0; i < newLines.length; i++) {
|
|
946
|
+
const edit = edits.find(e => e.newIdx === i && (e.type === 'equal' || e.type === 'insert'));
|
|
947
|
+
if (edit?.type === 'insert') {
|
|
948
|
+
output.push(`! ${newLines[i]}`);
|
|
949
|
+
}
|
|
950
|
+
else if (edit?.type === 'equal') {
|
|
951
|
+
// Check if there's a change at this position
|
|
952
|
+
const hasChange = edits.some(e => e.type === 'delete' && e.oldIdx === edit.oldIdx);
|
|
953
|
+
if (hasChange) {
|
|
954
|
+
output.push(`! ${newLines[i]}`);
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
output.push(` ${newLines[i]}`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return output.join('\n') + '\n';
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Parse diff command arguments
|
|
965
|
+
* @internal Reserved for future CLI diff support
|
|
966
|
+
*/
|
|
967
|
+
export function parseDiffArgs(args) {
|
|
968
|
+
const options = {};
|
|
969
|
+
const files = [];
|
|
970
|
+
for (const arg of args) {
|
|
971
|
+
if (arg === '-u' || arg === '--unified') {
|
|
972
|
+
options.unified = true;
|
|
973
|
+
}
|
|
974
|
+
else if (arg === '-c' || arg === '--context') {
|
|
975
|
+
options.context = true;
|
|
976
|
+
}
|
|
977
|
+
else if (arg.startsWith('-U')) {
|
|
978
|
+
options.unified = true;
|
|
979
|
+
options.contextLines = parseInt(arg.slice(2), 10);
|
|
980
|
+
}
|
|
981
|
+
else if (arg.startsWith('-C')) {
|
|
982
|
+
options.context = true;
|
|
983
|
+
options.contextLines = parseInt(arg.slice(2), 10);
|
|
984
|
+
}
|
|
985
|
+
else if (!arg.startsWith('-')) {
|
|
986
|
+
files.push(arg);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return { options, files };
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Execute diff (file comparison) command.
|
|
993
|
+
*
|
|
994
|
+
* Compares two text files and outputs the differences. Implements Myers diff
|
|
995
|
+
* algorithm for efficient comparison. Supports normal, unified (-u), and
|
|
996
|
+
* context (-c) output formats.
|
|
997
|
+
*
|
|
998
|
+
* @param file1Content - Content of the first (original) file
|
|
999
|
+
* @param file2Content - Content of the second (new) file
|
|
1000
|
+
* @param file1Path - Path of the first file (for output headers)
|
|
1001
|
+
* @param file2Path - Path of the second file (for output headers)
|
|
1002
|
+
* @param options - Diff options (unified, context, contextLines)
|
|
1003
|
+
* @returns Object with stdout (diff output), stderr, and exitCode (0 if same, 1 if different)
|
|
1004
|
+
*
|
|
1005
|
+
* @example
|
|
1006
|
+
* // Unified diff format
|
|
1007
|
+
* executeDiff('line1\n', 'line1\nline2\n', 'a.txt', 'b.txt', { unified: true })
|
|
1008
|
+
*/
|
|
1009
|
+
export function executeDiff(file1Content, file2Content, file1Path, file2Path, options = {}) {
|
|
1010
|
+
const oldLines = file1Content.split('\n');
|
|
1011
|
+
const newLines = file2Content.split('\n');
|
|
1012
|
+
// Remove trailing empty lines from split
|
|
1013
|
+
if (file1Content.endsWith('\n') && oldLines[oldLines.length - 1] === '') {
|
|
1014
|
+
oldLines.pop();
|
|
1015
|
+
}
|
|
1016
|
+
if (file2Content.endsWith('\n') && newLines[newLines.length - 1] === '') {
|
|
1017
|
+
newLines.pop();
|
|
1018
|
+
}
|
|
1019
|
+
// Check if files are identical
|
|
1020
|
+
if (file1Content === file2Content) {
|
|
1021
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
1022
|
+
}
|
|
1023
|
+
const edits = myersDiff(oldLines, newLines);
|
|
1024
|
+
let stdout;
|
|
1025
|
+
if (options.unified) {
|
|
1026
|
+
stdout = formatUnifiedDiff(file1Path, file2Path, oldLines, newLines, edits, options.contextLines);
|
|
1027
|
+
}
|
|
1028
|
+
else if (options.context) {
|
|
1029
|
+
stdout = formatContextDiff(file1Path, file2Path, oldLines, newLines, edits);
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
stdout = formatNormalDiff(oldLines, newLines, edits);
|
|
1033
|
+
}
|
|
1034
|
+
return { stdout, stderr: '', exitCode: stdout ? 1 : 0 };
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Parse a unified diff patch
|
|
1038
|
+
*/
|
|
1039
|
+
function parseUnifiedPatch(patchContent) {
|
|
1040
|
+
const patches = [];
|
|
1041
|
+
const lines = patchContent.split('\n');
|
|
1042
|
+
let current = null;
|
|
1043
|
+
let currentHunk = null;
|
|
1044
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1045
|
+
const line = lines[i];
|
|
1046
|
+
// File header
|
|
1047
|
+
if (line.startsWith('--- ')) {
|
|
1048
|
+
if (current) {
|
|
1049
|
+
if (currentHunk) {
|
|
1050
|
+
current.hunks.push(currentHunk);
|
|
1051
|
+
}
|
|
1052
|
+
patches.push(current);
|
|
1053
|
+
}
|
|
1054
|
+
current = { oldFile: line.slice(4).split('\t')[0], newFile: '', hunks: [] };
|
|
1055
|
+
currentHunk = null;
|
|
1056
|
+
}
|
|
1057
|
+
else if (line.startsWith('+++ ') && current) {
|
|
1058
|
+
current.newFile = line.slice(4).split('\t')[0];
|
|
1059
|
+
}
|
|
1060
|
+
// Hunk header
|
|
1061
|
+
else if (line.startsWith('@@') && current) {
|
|
1062
|
+
if (currentHunk) {
|
|
1063
|
+
current.hunks.push(currentHunk);
|
|
1064
|
+
}
|
|
1065
|
+
const match = line.match(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
1066
|
+
if (match) {
|
|
1067
|
+
currentHunk = {
|
|
1068
|
+
oldStart: parseInt(match[1], 10),
|
|
1069
|
+
oldCount: parseInt(match[2] || '1', 10),
|
|
1070
|
+
newStart: parseInt(match[3], 10),
|
|
1071
|
+
newCount: parseInt(match[4] || '1', 10),
|
|
1072
|
+
lines: [],
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
// Hunk content
|
|
1077
|
+
else if (currentHunk) {
|
|
1078
|
+
if (line.startsWith(' ')) {
|
|
1079
|
+
currentHunk.lines.push({ type: 'context', content: line.slice(1) });
|
|
1080
|
+
}
|
|
1081
|
+
else if (line.startsWith('-')) {
|
|
1082
|
+
currentHunk.lines.push({ type: 'delete', content: line.slice(1) });
|
|
1083
|
+
}
|
|
1084
|
+
else if (line.startsWith('+')) {
|
|
1085
|
+
currentHunk.lines.push({ type: 'add', content: line.slice(1) });
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (current) {
|
|
1090
|
+
if (currentHunk) {
|
|
1091
|
+
current.hunks.push(currentHunk);
|
|
1092
|
+
}
|
|
1093
|
+
patches.push(current);
|
|
1094
|
+
}
|
|
1095
|
+
return patches;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Apply a patch to content
|
|
1099
|
+
*/
|
|
1100
|
+
function applyPatch(original, patch, options) {
|
|
1101
|
+
const lines = original.split('\n');
|
|
1102
|
+
if (original.endsWith('\n') && lines[lines.length - 1] === '') {
|
|
1103
|
+
lines.pop();
|
|
1104
|
+
}
|
|
1105
|
+
let offset = 0;
|
|
1106
|
+
for (const hunk of patch.hunks) {
|
|
1107
|
+
const startLine = hunk.oldStart - 1 + offset;
|
|
1108
|
+
// Verify context matches (simple check)
|
|
1109
|
+
let contextMatches = true;
|
|
1110
|
+
let lineIdx = startLine;
|
|
1111
|
+
for (const hunkLine of hunk.lines) {
|
|
1112
|
+
if (options.reverse) {
|
|
1113
|
+
if (hunkLine.type === 'add') {
|
|
1114
|
+
if (lines[lineIdx] !== hunkLine.content) {
|
|
1115
|
+
contextMatches = false;
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
1118
|
+
lineIdx++;
|
|
1119
|
+
}
|
|
1120
|
+
else if (hunkLine.type === 'context') {
|
|
1121
|
+
if (lines[lineIdx] !== hunkLine.content) {
|
|
1122
|
+
contextMatches = false;
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
lineIdx++;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
if (hunkLine.type === 'delete' || hunkLine.type === 'context') {
|
|
1130
|
+
if (lines[lineIdx] !== hunkLine.content) {
|
|
1131
|
+
contextMatches = false;
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
lineIdx++;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (!contextMatches) {
|
|
1139
|
+
// Check if already applied (for forward patch) or already reversed
|
|
1140
|
+
let alreadyApplied = true;
|
|
1141
|
+
lineIdx = startLine;
|
|
1142
|
+
for (const hunkLine of hunk.lines) {
|
|
1143
|
+
if (options.reverse) {
|
|
1144
|
+
if (hunkLine.type === 'delete') {
|
|
1145
|
+
if (lines[lineIdx] !== hunkLine.content) {
|
|
1146
|
+
alreadyApplied = false;
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
lineIdx++;
|
|
1150
|
+
}
|
|
1151
|
+
else if (hunkLine.type === 'context') {
|
|
1152
|
+
if (lines[lineIdx] !== hunkLine.content) {
|
|
1153
|
+
alreadyApplied = false;
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
lineIdx++;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
if (hunkLine.type === 'add' || hunkLine.type === 'context') {
|
|
1161
|
+
if (lines[lineIdx] !== hunkLine.content) {
|
|
1162
|
+
alreadyApplied = false;
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
lineIdx++;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (alreadyApplied) {
|
|
1170
|
+
return { result: original, success: false, message: 'Reversed (or previously applied) patch detected!' };
|
|
1171
|
+
}
|
|
1172
|
+
return { result: original, success: false, message: 'Hunk failed to apply' };
|
|
1173
|
+
}
|
|
1174
|
+
if (options.dryRun) {
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
// Apply the hunk
|
|
1178
|
+
const newLines = [];
|
|
1179
|
+
let deleteCount = 0;
|
|
1180
|
+
let addCount = 0;
|
|
1181
|
+
for (const hunkLine of hunk.lines) {
|
|
1182
|
+
if (options.reverse) {
|
|
1183
|
+
if (hunkLine.type === 'delete') {
|
|
1184
|
+
newLines.push(hunkLine.content);
|
|
1185
|
+
addCount++;
|
|
1186
|
+
}
|
|
1187
|
+
else if (hunkLine.type === 'add') {
|
|
1188
|
+
deleteCount++;
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1191
|
+
newLines.push(hunkLine.content);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
else {
|
|
1195
|
+
if (hunkLine.type === 'add') {
|
|
1196
|
+
newLines.push(hunkLine.content);
|
|
1197
|
+
addCount++;
|
|
1198
|
+
}
|
|
1199
|
+
else if (hunkLine.type === 'delete') {
|
|
1200
|
+
deleteCount++;
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
newLines.push(hunkLine.content);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
const contextAndDelete = hunk.lines.filter(l => options.reverse ? (l.type === 'context' || l.type === 'add') : (l.type === 'context' || l.type === 'delete')).length;
|
|
1208
|
+
lines.splice(startLine, contextAndDelete, ...newLines);
|
|
1209
|
+
offset += addCount - deleteCount;
|
|
1210
|
+
}
|
|
1211
|
+
let result = lines.join('\n');
|
|
1212
|
+
if (lines.length > 0) {
|
|
1213
|
+
result += '\n';
|
|
1214
|
+
}
|
|
1215
|
+
return { result, success: true, message: `patching file ${patch.newFile}` };
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Strip path prefix
|
|
1219
|
+
*/
|
|
1220
|
+
function stripPathPrefix(path, level) {
|
|
1221
|
+
if (level === 0)
|
|
1222
|
+
return path;
|
|
1223
|
+
const parts = path.split('/');
|
|
1224
|
+
return parts.slice(level).join('/');
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Parse patch command arguments
|
|
1228
|
+
* @internal Reserved for future CLI patch support
|
|
1229
|
+
*/
|
|
1230
|
+
export function parsePatchArgs(args) {
|
|
1231
|
+
const options = { stripLevel: 0 };
|
|
1232
|
+
let patchFile;
|
|
1233
|
+
for (let i = 0; i < args.length; i++) {
|
|
1234
|
+
const arg = args[i];
|
|
1235
|
+
if (arg === '-R' || arg === '--reverse') {
|
|
1236
|
+
options.reverse = true;
|
|
1237
|
+
}
|
|
1238
|
+
else if (arg === '--dry-run') {
|
|
1239
|
+
options.dryRun = true;
|
|
1240
|
+
}
|
|
1241
|
+
else if (arg.startsWith('-p')) {
|
|
1242
|
+
options.stripLevel = parseInt(arg.slice(2), 10);
|
|
1243
|
+
}
|
|
1244
|
+
else if (arg === '-i') {
|
|
1245
|
+
i++;
|
|
1246
|
+
if (i < args.length) {
|
|
1247
|
+
patchFile = args[i];
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
else if (!arg.startsWith('-')) {
|
|
1251
|
+
patchFile = arg;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return { options, patchFile };
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Execute patch (apply diffs) command.
|
|
1258
|
+
*
|
|
1259
|
+
* Applies a unified diff patch to content. Supports reverse patching (-R),
|
|
1260
|
+
* dry-run mode (--dry-run), and path stripping (-p).
|
|
1261
|
+
*
|
|
1262
|
+
* @param original - Original file content to patch
|
|
1263
|
+
* @param patchContent - The patch content (unified diff format)
|
|
1264
|
+
* @param options - Patch options (reverse, dryRun, stripLevel)
|
|
1265
|
+
* @returns Object with stdout, stderr, exitCode, and optionally result (patched content)
|
|
1266
|
+
*
|
|
1267
|
+
* @example
|
|
1268
|
+
* // Apply a patch
|
|
1269
|
+
* const patch = '--- a.txt\n+++ b.txt\n@@ -1,1 +1,2 @@\n line1\n+line2\n'
|
|
1270
|
+
* executePatch('line1\n', patch, { dryRun: true })
|
|
1271
|
+
*/
|
|
1272
|
+
export function executePatch(original, patchContent, options = {}) {
|
|
1273
|
+
const patches = parseUnifiedPatch(patchContent);
|
|
1274
|
+
if (patches.length === 0) {
|
|
1275
|
+
return { stdout: '', stderr: 'No valid patches found', exitCode: 1 };
|
|
1276
|
+
}
|
|
1277
|
+
const patch = patches[0];
|
|
1278
|
+
const targetFile = stripPathPrefix(patch.newFile, options.stripLevel || 0);
|
|
1279
|
+
const { result, success, message } = applyPatch(original, patch, options);
|
|
1280
|
+
if (!success) {
|
|
1281
|
+
return { stdout: '', stderr: message, exitCode: 1 };
|
|
1282
|
+
}
|
|
1283
|
+
const action = options.dryRun ? 'checking' : 'patching';
|
|
1284
|
+
return {
|
|
1285
|
+
stdout: `${action} file ${targetFile}\n`,
|
|
1286
|
+
stderr: '',
|
|
1287
|
+
exitCode: 0,
|
|
1288
|
+
result: options.dryRun ? original : result,
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Parse tee command arguments
|
|
1293
|
+
*/
|
|
1294
|
+
function parseTeeArgs(args) {
|
|
1295
|
+
const options = { append: false };
|
|
1296
|
+
const files = [];
|
|
1297
|
+
for (const arg of args) {
|
|
1298
|
+
if (arg === '-a' || arg === '--append') {
|
|
1299
|
+
options.append = true;
|
|
1300
|
+
}
|
|
1301
|
+
else if (!arg.startsWith('-')) {
|
|
1302
|
+
files.push(arg);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
return { options, files };
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Execute tee (write to multiple outputs) command.
|
|
1309
|
+
*
|
|
1310
|
+
* Reads from input and writes to both stdout and specified files.
|
|
1311
|
+
* Supports append mode (-a).
|
|
1312
|
+
*
|
|
1313
|
+
* @param input - Input text to process
|
|
1314
|
+
* @param args - Command arguments (e.g., ['-a', 'file1.txt', 'file2.txt'])
|
|
1315
|
+
* @param fs - Optional filesystem capability for file writing
|
|
1316
|
+
* @returns Promise resolving to object with stdout, stderr, and exitCode
|
|
1317
|
+
*
|
|
1318
|
+
* @example
|
|
1319
|
+
* // Write to files while passing through
|
|
1320
|
+
* await executeTee('hello\n', ['output.txt'], fs)
|
|
1321
|
+
* // => { stdout: 'hello\n', stderr: '', exitCode: 0 }
|
|
1322
|
+
*/
|
|
1323
|
+
export async function executeTee(input, args, fs) {
|
|
1324
|
+
const { options, files } = parseTeeArgs(args);
|
|
1325
|
+
// Write to files if fs is available
|
|
1326
|
+
if (fs && files.length > 0) {
|
|
1327
|
+
for (const file of files) {
|
|
1328
|
+
try {
|
|
1329
|
+
if (options.append) {
|
|
1330
|
+
const existing = await fs.exists(file) ? await fs.read(file, { encoding: 'utf-8' }) : '';
|
|
1331
|
+
await fs.write(file, existing + input);
|
|
1332
|
+
}
|
|
1333
|
+
else {
|
|
1334
|
+
await fs.write(file, input);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
catch (error) {
|
|
1338
|
+
// Continue with other files
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
// Always output to stdout
|
|
1343
|
+
return { stdout: input, stderr: '', exitCode: 0 };
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Parse xargs command arguments
|
|
1347
|
+
*/
|
|
1348
|
+
function parseXargsArgs(args) {
|
|
1349
|
+
const options = {};
|
|
1350
|
+
const command = [];
|
|
1351
|
+
let foundCommand = false;
|
|
1352
|
+
for (let i = 0; i < args.length; i++) {
|
|
1353
|
+
const arg = args[i];
|
|
1354
|
+
if (foundCommand) {
|
|
1355
|
+
command.push(arg);
|
|
1356
|
+
continue;
|
|
1357
|
+
}
|
|
1358
|
+
if (arg === '-n') {
|
|
1359
|
+
i++;
|
|
1360
|
+
if (i < args.length) {
|
|
1361
|
+
options.maxArgs = parseInt(args[i], 10);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
else if (arg.startsWith('-n')) {
|
|
1365
|
+
options.maxArgs = parseInt(arg.slice(2), 10);
|
|
1366
|
+
}
|
|
1367
|
+
else if (arg === '-d') {
|
|
1368
|
+
i++;
|
|
1369
|
+
if (i < args.length) {
|
|
1370
|
+
options.delimiter = args[i];
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
else if (arg === '-0') {
|
|
1374
|
+
options.delimiter = '\0';
|
|
1375
|
+
}
|
|
1376
|
+
else if (arg === '-I') {
|
|
1377
|
+
i++;
|
|
1378
|
+
if (i < args.length) {
|
|
1379
|
+
options.placeholder = args[i];
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
else if (arg.startsWith('-I')) {
|
|
1383
|
+
options.placeholder = arg.slice(2);
|
|
1384
|
+
}
|
|
1385
|
+
else if (arg === '-P') {
|
|
1386
|
+
i++;
|
|
1387
|
+
if (i < args.length) {
|
|
1388
|
+
options.parallel = parseInt(args[i], 10);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
else if (arg === '-p') {
|
|
1392
|
+
options.prompt = true;
|
|
1393
|
+
}
|
|
1394
|
+
else if (arg === '-s') {
|
|
1395
|
+
i++;
|
|
1396
|
+
if (i < args.length) {
|
|
1397
|
+
options.maxChars = parseInt(args[i], 10);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
else if (!arg.startsWith('-')) {
|
|
1401
|
+
foundCommand = true;
|
|
1402
|
+
command.push(arg);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return { options, command };
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Split input into arguments for xargs
|
|
1409
|
+
*/
|
|
1410
|
+
function splitXargsInput(input, delimiter) {
|
|
1411
|
+
if (delimiter === '\0') {
|
|
1412
|
+
return input.split('\0').filter(Boolean);
|
|
1413
|
+
}
|
|
1414
|
+
if (delimiter) {
|
|
1415
|
+
return input.split(delimiter).filter(Boolean);
|
|
1416
|
+
}
|
|
1417
|
+
// Default: split on whitespace and newlines
|
|
1418
|
+
return input.split(/[\s\n]+/).filter(Boolean);
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Execute xargs (build and execute command lines) command.
|
|
1422
|
+
*
|
|
1423
|
+
* Builds and executes command lines from standard input. Supports:
|
|
1424
|
+
* - -n (max arguments per command)
|
|
1425
|
+
* - -I (placeholder replacement)
|
|
1426
|
+
* - -0 (null delimiter for filenames with spaces)
|
|
1427
|
+
* - -P (parallel execution)
|
|
1428
|
+
* - -s (max command length)
|
|
1429
|
+
*
|
|
1430
|
+
* @param input - Input containing arguments (one per line or whitespace-separated)
|
|
1431
|
+
* @param args - Command arguments (e.g., ['-n', '1', 'echo'])
|
|
1432
|
+
* @param executor - Function to execute subcommands
|
|
1433
|
+
* @returns Promise resolving to object with stdout, stderr, and exitCode
|
|
1434
|
+
*
|
|
1435
|
+
* @example
|
|
1436
|
+
* // Process one argument at a time
|
|
1437
|
+
* await executeXargs('a\nb\nc', ['-n', '1', 'echo'], cmd => exec(cmd))
|
|
1438
|
+
* // => { stdout: 'a\nb\nc\n', stderr: '', exitCode: 0 }
|
|
1439
|
+
*/
|
|
1440
|
+
export async function executeXargs(input, args, executor) {
|
|
1441
|
+
const { options, command } = parseXargsArgs(args);
|
|
1442
|
+
// Default command is echo
|
|
1443
|
+
const baseCommand = command.length > 0 ? command : ['echo'];
|
|
1444
|
+
// Parse input into arguments
|
|
1445
|
+
const inputArgs = splitXargsInput(input, options.delimiter);
|
|
1446
|
+
if (inputArgs.length === 0) {
|
|
1447
|
+
// Run once with no args
|
|
1448
|
+
const cmd = baseCommand.join(' ');
|
|
1449
|
+
return executor(cmd);
|
|
1450
|
+
}
|
|
1451
|
+
const outputs = [];
|
|
1452
|
+
const errors = [];
|
|
1453
|
+
let exitCode = 0;
|
|
1454
|
+
if (options.placeholder) {
|
|
1455
|
+
// -I mode: run command once per input line, replacing placeholder
|
|
1456
|
+
for (const arg of inputArgs) {
|
|
1457
|
+
const cmdWithArg = baseCommand.map(c => c.replace(new RegExp(options.placeholder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), arg)).join(' ');
|
|
1458
|
+
if (options.prompt) {
|
|
1459
|
+
outputs.push(`${baseCommand[0]} ${arg}?...`);
|
|
1460
|
+
}
|
|
1461
|
+
const result = await executor(cmdWithArg);
|
|
1462
|
+
if (result.stdout)
|
|
1463
|
+
outputs.push(result.stdout.replace(/\n$/, ''));
|
|
1464
|
+
if (result.stderr)
|
|
1465
|
+
errors.push(result.stderr);
|
|
1466
|
+
if (result.exitCode !== 0)
|
|
1467
|
+
exitCode = 123;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
else if (options.maxArgs) {
|
|
1471
|
+
// -n mode: run command with maxArgs arguments at a time
|
|
1472
|
+
for (let i = 0; i < inputArgs.length; i += options.maxArgs) {
|
|
1473
|
+
const batch = inputArgs.slice(i, i + options.maxArgs);
|
|
1474
|
+
const cmd = [...baseCommand, ...batch].join(' ');
|
|
1475
|
+
const result = await executor(cmd);
|
|
1476
|
+
if (result.stdout)
|
|
1477
|
+
outputs.push(result.stdout.replace(/\n$/, ''));
|
|
1478
|
+
if (result.stderr)
|
|
1479
|
+
errors.push(result.stderr);
|
|
1480
|
+
if (result.exitCode !== 0)
|
|
1481
|
+
exitCode = 123;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
else if (options.maxChars) {
|
|
1485
|
+
// -s mode: limit total command line length
|
|
1486
|
+
let currentBatch = [];
|
|
1487
|
+
let currentLen = baseCommand.join(' ').length;
|
|
1488
|
+
for (const arg of inputArgs) {
|
|
1489
|
+
if (currentLen + arg.length + 1 > options.maxChars && currentBatch.length > 0) {
|
|
1490
|
+
const cmd = [...baseCommand, ...currentBatch].join(' ');
|
|
1491
|
+
const result = await executor(cmd);
|
|
1492
|
+
if (result.stdout)
|
|
1493
|
+
outputs.push(result.stdout.replace(/\n$/, ''));
|
|
1494
|
+
if (result.stderr)
|
|
1495
|
+
errors.push(result.stderr);
|
|
1496
|
+
if (result.exitCode !== 0)
|
|
1497
|
+
exitCode = 123;
|
|
1498
|
+
currentBatch = [];
|
|
1499
|
+
currentLen = baseCommand.join(' ').length;
|
|
1500
|
+
}
|
|
1501
|
+
currentBatch.push(arg);
|
|
1502
|
+
currentLen += arg.length + 1;
|
|
1503
|
+
}
|
|
1504
|
+
if (currentBatch.length > 0) {
|
|
1505
|
+
const cmd = [...baseCommand, ...currentBatch].join(' ');
|
|
1506
|
+
const result = await executor(cmd);
|
|
1507
|
+
if (result.stdout)
|
|
1508
|
+
outputs.push(result.stdout.replace(/\n$/, ''));
|
|
1509
|
+
if (result.stderr)
|
|
1510
|
+
errors.push(result.stderr);
|
|
1511
|
+
if (result.exitCode !== 0)
|
|
1512
|
+
exitCode = 123;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
else {
|
|
1516
|
+
// Default: all args in one command
|
|
1517
|
+
const cmd = [...baseCommand, ...inputArgs].join(' ');
|
|
1518
|
+
if (options.prompt) {
|
|
1519
|
+
outputs.push(`${baseCommand[0]} ${inputArgs.join(' ')}?...`);
|
|
1520
|
+
}
|
|
1521
|
+
const result = await executor(cmd);
|
|
1522
|
+
if (result.stdout)
|
|
1523
|
+
outputs.push(result.stdout.replace(/\n$/, ''));
|
|
1524
|
+
if (result.stderr)
|
|
1525
|
+
errors.push(result.stderr);
|
|
1526
|
+
exitCode = result.exitCode;
|
|
1527
|
+
}
|
|
1528
|
+
let stdout = outputs.join('\n');
|
|
1529
|
+
if (outputs.length > 0)
|
|
1530
|
+
stdout += '\n';
|
|
1531
|
+
return { stdout, stderr: errors.join('\n'), exitCode };
|
|
1532
|
+
}
|
|
1533
|
+
// ============================================================================
|
|
1534
|
+
// Exports for TieredExecutor integration
|
|
1535
|
+
// ============================================================================
|
|
1536
|
+
export const TEXT_PROCESSING_COMMANDS = new Set(['sed', 'awk', 'diff', 'patch', 'tee', 'xargs']);
|
|
1537
|
+
/**
|
|
1538
|
+
* Check if a command is a text processing command handled by this module.
|
|
1539
|
+
*
|
|
1540
|
+
* @param cmd - Command name to check
|
|
1541
|
+
* @returns True if the command is handled by this module
|
|
1542
|
+
*
|
|
1543
|
+
* @example
|
|
1544
|
+
* isTextProcessingCommand('sed') // => true
|
|
1545
|
+
* isTextProcessingCommand('ls') // => false
|
|
1546
|
+
*/
|
|
1547
|
+
export function isTextProcessingCommand(cmd) {
|
|
1548
|
+
return TEXT_PROCESSING_COMMANDS.has(cmd);
|
|
1549
|
+
}
|
|
1550
|
+
//# sourceMappingURL=text-processing.js.map
|