@namzu/sdk 45.1.0 → 48.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +436 -8
- package/README.md +7 -8
- package/dist/advisory/history.d.ts.map +1 -1
- package/dist/advisory/history.js +21 -1
- package/dist/advisory/history.js.map +1 -1
- package/dist/advisory/index.d.ts +1 -1
- package/dist/advisory/index.d.ts.map +1 -1
- package/dist/advisory/index.js +1 -1
- package/dist/advisory/index.js.map +1 -1
- package/dist/advisory/registry.d.ts +17 -0
- package/dist/advisory/registry.d.ts.map +1 -1
- package/dist/advisory/registry.js +26 -0
- package/dist/advisory/registry.js.map +1 -1
- package/dist/agents/PipelineAgent.d.ts +2 -46
- package/dist/agents/PipelineAgent.d.ts.map +1 -1
- package/dist/agents/PipelineAgent.js +2 -242
- package/dist/agents/PipelineAgent.js.map +1 -1
- package/dist/agents/QueryAgent.d.ts +21 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -0
- package/dist/agents/QueryAgent.js +214 -0
- package/dist/agents/QueryAgent.js.map +1 -0
- package/dist/agents/ReactiveAgent.d.ts +4 -16
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +4 -183
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts +2 -20
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +2 -266
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts +3 -43
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +3 -418
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/defineAgent.d.ts +2 -1
- package/dist/agents/defineAgent.d.ts.map +1 -1
- package/dist/agents/defineAgent.js +6 -1
- package/dist/agents/defineAgent.js.map +1 -1
- package/dist/agents/examples/PipelineAgent.d.ts +46 -0
- package/dist/agents/examples/PipelineAgent.d.ts.map +1 -0
- package/dist/agents/examples/PipelineAgent.js +242 -0
- package/dist/agents/examples/PipelineAgent.js.map +1 -0
- package/dist/agents/examples/RouterAgent.d.ts +22 -0
- package/dist/agents/examples/RouterAgent.d.ts.map +1 -0
- package/dist/agents/examples/RouterAgent.js +268 -0
- package/dist/agents/examples/RouterAgent.js.map +1 -0
- package/dist/agents/examples/SupervisorAgent.d.ts +45 -0
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -0
- package/dist/agents/examples/SupervisorAgent.js +406 -0
- package/dist/agents/examples/SupervisorAgent.js.map +1 -0
- package/dist/agents/explore.d.ts +6 -3
- package/dist/agents/explore.d.ts.map +1 -1
- package/dist/agents/explore.js +6 -3
- package/dist/agents/explore.js.map +1 -1
- package/dist/agents/forward-options.d.ts +11 -0
- package/dist/agents/forward-options.d.ts.map +1 -0
- package/dist/agents/forward-options.js +15 -0
- package/dist/agents/forward-options.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/runAgent.d.ts +19 -8
- package/dist/agents/runAgent.d.ts.map +1 -1
- package/dist/agents/runAgent.js +99 -29
- package/dist/agents/runAgent.js.map +1 -1
- package/dist/authorization/command-line.d.ts +2 -0
- package/dist/authorization/command-line.d.ts.map +1 -1
- package/dist/authorization/command-line.js +2 -2
- package/dist/authorization/command-line.js.map +1 -1
- package/dist/authorization/gate.d.ts +13 -2
- package/dist/authorization/gate.d.ts.map +1 -1
- package/dist/authorization/gate.js +37 -5
- package/dist/authorization/gate.js.map +1 -1
- package/dist/authorization/program.d.ts +151 -0
- package/dist/authorization/program.d.ts.map +1 -0
- package/dist/authorization/program.js +713 -0
- package/dist/authorization/program.js.map +1 -0
- package/dist/authorization/reexec-wrapper.d.ts +106 -0
- package/dist/authorization/reexec-wrapper.d.ts.map +1 -0
- package/dist/authorization/reexec-wrapper.js +532 -0
- package/dist/authorization/reexec-wrapper.js.map +1 -0
- package/dist/authorization/rules.d.ts +9 -0
- package/dist/authorization/rules.d.ts.map +1 -1
- package/dist/authorization/rules.js +27 -1
- package/dist/authorization/rules.js.map +1 -1
- package/dist/authorization/shell-lexer.d.ts +39 -0
- package/dist/authorization/shell-lexer.d.ts.map +1 -1
- package/dist/authorization/shell-lexer.js +113 -69
- package/dist/authorization/shell-lexer.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +6 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/capabilities/index.d.ts +54 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +92 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/config/registry.d.ts +2 -1
- package/dist/config/registry.d.ts.map +1 -1
- package/dist/config/registry.js +3 -2
- package/dist/config/registry.js.map +1 -1
- package/dist/connector/index.d.ts +4 -4
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +3 -3
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/adapter.d.ts +16 -1
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +60 -4
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/connector/mcp/client.d.ts +13 -4
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +20 -3
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/discovery.d.ts +24 -1
- package/dist/connector/mcp/discovery.d.ts.map +1 -1
- package/dist/connector/mcp/discovery.js +44 -0
- package/dist/connector/mcp/discovery.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +2 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +11 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/mcp-toolset.d.ts +133 -0
- package/dist/connector/mcp/mcp-toolset.d.ts.map +1 -0
- package/dist/connector/mcp/mcp-toolset.js +399 -0
- package/dist/connector/mcp/mcp-toolset.js.map +1 -0
- package/dist/connector/tools/index.d.ts +2 -2
- package/dist/connector/tools/index.d.ts.map +1 -1
- package/dist/connector/tools/index.js +1 -1
- package/dist/connector/tools/index.js.map +1 -1
- package/dist/connector/tools/router.d.ts +15 -13
- package/dist/connector/tools/router.d.ts.map +1 -1
- package/dist/connector/tools/router.js +30 -51
- package/dist/connector/tools/router.js.map +1 -1
- package/dist/contracts/a2a.d.ts +2 -2
- package/dist/directory/derive-supervisor.d.ts +2 -2
- package/dist/directory/derive-supervisor.d.ts.map +1 -1
- package/dist/directory/derive-supervisor.js +6 -7
- package/dist/directory/derive-supervisor.js.map +1 -1
- package/dist/directory/derive.d.ts.map +1 -1
- package/dist/directory/derive.js +5 -5
- package/dist/directory/derive.js.map +1 -1
- package/dist/execution/code-runtime/types.d.ts +2 -2
- package/dist/execution/code-runtime/types.js +1 -1
- package/dist/invariants/index.d.ts +2 -1
- package/dist/invariants/index.d.ts.map +1 -1
- package/dist/invariants/index.js +3 -2
- package/dist/invariants/index.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +9 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/connector/environment.d.ts +6 -0
- package/dist/manager/connector/environment.d.ts.map +1 -1
- package/dist/manager/connector/environment.js +11 -4
- package/dist/manager/connector/environment.js.map +1 -1
- package/dist/manager/connector/index.d.ts +2 -2
- package/dist/manager/connector/index.d.ts.map +1 -1
- package/dist/manager/connector/index.js +2 -2
- package/dist/manager/connector/index.js.map +1 -1
- package/dist/manager/connector/tenant.d.ts +6 -0
- package/dist/manager/connector/tenant.d.ts.map +1 -1
- package/dist/manager/connector/tenant.js +11 -4
- package/dist/manager/connector/tenant.js.map +1 -1
- package/dist/manager/index.d.ts +2 -2
- package/dist/manager/index.d.ts.map +1 -1
- package/dist/manager/index.js +2 -2
- package/dist/manager/index.js.map +1 -1
- package/dist/manager/resident/learning-observation.d.ts +4 -4
- package/dist/manager/resident/learning.d.ts +36 -36
- package/dist/manager/session/attribution.d.ts +22 -0
- package/dist/manager/session/attribution.d.ts.map +1 -0
- package/dist/manager/session/attribution.js +64 -0
- package/dist/manager/session/attribution.js.map +1 -0
- package/dist/manager/session/turn-recorder.d.ts +14 -0
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +208 -55
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/peers/address.d.ts +36 -0
- package/dist/peers/address.d.ts.map +1 -0
- package/dist/peers/address.js +52 -0
- package/dist/peers/address.js.map +1 -0
- package/dist/peers/client.d.ts +47 -0
- package/dist/peers/client.d.ts.map +1 -0
- package/dist/peers/client.js +132 -0
- package/dist/peers/client.js.map +1 -0
- package/dist/peers/dir.d.ts +88 -0
- package/dist/peers/dir.d.ts.map +1 -0
- package/dist/peers/dir.js +139 -0
- package/dist/peers/dir.js.map +1 -0
- package/dist/peers/endpoint.d.ts +144 -0
- package/dist/peers/endpoint.d.ts.map +1 -0
- package/dist/peers/endpoint.js +420 -0
- package/dist/peers/endpoint.js.map +1 -0
- package/dist/peers/envelope.d.ts +42 -0
- package/dist/peers/envelope.d.ts.map +1 -0
- package/dist/peers/envelope.js +110 -0
- package/dist/peers/envelope.js.map +1 -0
- package/dist/peers/index.d.ts +30 -0
- package/dist/peers/index.d.ts.map +1 -0
- package/dist/peers/index.js +22 -0
- package/dist/peers/index.js.map +1 -0
- package/dist/peers/protocol.d.ts +645 -0
- package/dist/peers/protocol.d.ts.map +1 -0
- package/dist/peers/protocol.js +151 -0
- package/dist/peers/protocol.js.map +1 -0
- package/dist/peers/record.d.ts +71 -0
- package/dist/peers/record.d.ts.map +1 -0
- package/dist/peers/record.js +45 -0
- package/dist/peers/record.js.map +1 -0
- package/dist/peers/registry.d.ts +54 -0
- package/dist/peers/registry.d.ts.map +1 -0
- package/dist/peers/registry.js +213 -0
- package/dist/peers/registry.js.map +1 -0
- package/dist/plugin/define.d.ts +24 -0
- package/dist/plugin/define.d.ts.map +1 -0
- package/dist/plugin/define.js +42 -0
- package/dist/plugin/define.js.map +1 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -0
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts +27 -3
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +242 -92
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/resolver.d.ts +2 -2
- package/dist/plugin/resolver.d.ts.map +1 -1
- package/dist/plugin/resolver.js.map +1 -1
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +17 -3
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/pricing/catalogue.generated.d.ts.map +1 -1
- package/dist/pricing/catalogue.generated.js +28 -4
- package/dist/pricing/catalogue.generated.js.map +1 -1
- package/dist/probe/errors.d.ts +2 -1
- package/dist/probe/errors.d.ts.map +1 -1
- package/dist/probe/errors.js +3 -2
- package/dist/probe/errors.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +18 -7
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +11 -6
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/prompt/contributions.d.ts +4 -1
- package/dist/prompt/contributions.d.ts.map +1 -1
- package/dist/prompt/contributions.js +7 -2
- package/dist/prompt/contributions.js.map +1 -1
- package/dist/prompt/index.d.ts +1 -1
- package/dist/prompt/index.d.ts.map +1 -1
- package/dist/prompt/index.js +1 -1
- package/dist/prompt/index.js.map +1 -1
- package/dist/provider/collect-chat-completion.d.ts +6 -1
- package/dist/provider/collect-chat-completion.d.ts.map +1 -1
- package/dist/provider/collect-chat-completion.js +88 -10
- package/dist/provider/collect-chat-completion.js.map +1 -1
- package/dist/provider/mock.d.ts.map +1 -1
- package/dist/provider/mock.js +23 -6
- package/dist/provider/mock.js.map +1 -1
- package/dist/provider/registry.d.ts +2 -1
- package/dist/provider/registry.d.ts.map +1 -1
- package/dist/provider/registry.js +3 -2
- package/dist/provider/registry.js.map +1 -1
- package/dist/provider/tool-call-framing.d.ts +164 -0
- package/dist/provider/tool-call-framing.d.ts.map +1 -0
- package/dist/provider/tool-call-framing.js +163 -0
- package/dist/provider/tool-call-framing.js.map +1 -0
- package/dist/public-runtime.d.ts +27 -14
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +49 -11
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +9 -6
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +13 -5
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +16 -5
- package/dist/public-types.d.ts.map +1 -1
- package/dist/read-model/registry.d.ts +2 -1
- package/dist/read-model/registry.d.ts.map +1 -1
- package/dist/read-model/registry.js +3 -2
- package/dist/read-model/registry.js.map +1 -1
- package/dist/registry/ManagedRegistry.d.ts +30 -0
- package/dist/registry/ManagedRegistry.d.ts.map +1 -1
- package/dist/registry/ManagedRegistry.js +45 -6
- package/dist/registry/ManagedRegistry.js.map +1 -1
- package/dist/registry/collision.d.ts +57 -0
- package/dist/registry/collision.d.ts.map +1 -0
- package/dist/registry/collision.js +41 -0
- package/dist/registry/collision.js.map +1 -0
- package/dist/registry/command/index.d.ts +5 -3
- package/dist/registry/command/index.d.ts.map +1 -1
- package/dist/registry/command/index.js +6 -4
- package/dist/registry/command/index.js.map +1 -1
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +2 -2
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/plugin/index.d.ts.map +1 -1
- package/dist/registry/plugin/index.js +14 -1
- package/dist/registry/plugin/index.js.map +1 -1
- package/dist/registry/tool/callable.d.ts +22 -0
- package/dist/registry/tool/callable.d.ts.map +1 -0
- package/dist/registry/tool/callable.js +28 -0
- package/dist/registry/tool/callable.js.map +1 -0
- package/dist/registry/tool/execute.d.ts +14 -77
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +15 -720
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/portable.d.ts +25 -0
- package/dist/registry/tool/portable.d.ts.map +1 -1
- package/dist/registry/tool/portable.js +50 -0
- package/dist/registry/tool/portable.js.map +1 -1
- package/dist/registry/tool/presentation.d.ts +6 -5
- package/dist/registry/tool/presentation.d.ts.map +1 -1
- package/dist/registry/tool/presentation.js +3 -3
- package/dist/registry/tool/presentation.js.map +1 -1
- package/dist/runtime/bidi/session.d.ts +5 -4
- package/dist/runtime/bidi/session.d.ts.map +1 -1
- package/dist/runtime/bidi/session.js +18 -1
- package/dist/runtime/bidi/session.js.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.d.ts +82 -13
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +227 -17
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +37 -5
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +103 -14
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/file-evidence-replay.d.ts +1 -1
- package/dist/runtime/query/file-evidence-replay.d.ts.map +1 -1
- package/dist/runtime/query/file-evidence-replay.js +8 -7
- package/dist/runtime/query/file-evidence-replay.js.map +1 -1
- package/dist/runtime/query/fork/prepare.d.ts +4 -1
- package/dist/runtime/query/fork/prepare.d.ts.map +1 -1
- package/dist/runtime/query/fork/prepare.js +4 -0
- package/dist/runtime/query/fork/prepare.js.map +1 -1
- package/dist/runtime/query/guardrail-presets.d.ts +4 -3
- package/dist/runtime/query/guardrail-presets.d.ts.map +1 -1
- package/dist/runtime/query/guardrail-presets.js +4 -3
- package/dist/runtime/query/guardrail-presets.js.map +1 -1
- package/dist/runtime/query/index.d.ts +23 -10
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +149 -70
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +8 -1
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +2 -2
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +55 -2
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +342 -116
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/iteration/tool-input.d.ts +79 -0
- package/dist/runtime/query/iteration/tool-input.d.ts.map +1 -0
- package/dist/runtime/query/iteration/tool-input.js +291 -0
- package/dist/runtime/query/iteration/tool-input.js.map +1 -0
- package/dist/runtime/query/observation-context.d.ts +2 -2
- package/dist/runtime/query/observation-context.d.ts.map +1 -1
- package/dist/runtime/query/observation-context.js.map +1 -1
- package/dist/runtime/query/prelude-lease.d.ts +18 -0
- package/dist/runtime/query/prelude-lease.d.ts.map +1 -0
- package/dist/runtime/query/prelude-lease.js +121 -0
- package/dist/runtime/query/prelude-lease.js.map +1 -0
- package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
- package/dist/runtime/query/prepare-turn.js +386 -187
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/runtime/query/prompt-cache.d.ts +2 -2
- package/dist/runtime/query/prompt-cache.d.ts.map +1 -1
- package/dist/runtime/query/prompt.d.ts +2 -2
- package/dist/runtime/query/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js +1 -1
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/runtime/query/resume-session.d.ts.map +1 -1
- package/dist/runtime/query/resume-session.js +37 -2
- package/dist/runtime/query/resume-session.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +65 -3
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +143 -22
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +3 -2
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/runtime/query/turn-state.d.ts.map +1 -1
- package/dist/runtime/query/turn-state.js +5 -0
- package/dist/runtime/query/turn-state.js.map +1 -1
- package/dist/runtime/system-events.d.ts +112 -0
- package/dist/runtime/system-events.d.ts.map +1 -0
- package/dist/runtime/system-events.js +183 -0
- package/dist/runtime/system-events.js.map +1 -0
- package/dist/scheduler/delegating.d.ts +2 -1
- package/dist/scheduler/delegating.d.ts.map +1 -1
- package/dist/scheduler/delegating.js +3 -2
- package/dist/scheduler/delegating.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/registry.d.ts +18 -0
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +20 -0
- package/dist/skills/registry.js.map +1 -1
- package/dist/store/evidence/index-page.d.ts +2 -2
- package/dist/store/session-log/conformance.d.ts +1 -1
- package/dist/store/session-log/conformance.d.ts.map +1 -1
- package/dist/store/session-log/conformance.js +49 -8
- package/dist/store/session-log/conformance.js.map +1 -1
- package/dist/store/session-log/core.d.ts +8 -3
- package/dist/store/session-log/core.d.ts.map +1 -1
- package/dist/store/session-log/core.js +2 -1
- package/dist/store/session-log/core.js.map +1 -1
- package/dist/store/session-log/fold.d.ts +12 -0
- package/dist/store/session-log/fold.d.ts.map +1 -1
- package/dist/store/session-log/fold.js +57 -7
- package/dist/store/session-log/fold.js.map +1 -1
- package/dist/store/session-log/index.d.ts +2 -2
- package/dist/store/session-log/index.d.ts.map +1 -1
- package/dist/store/session-log/index.js +1 -1
- package/dist/store/session-log/index.js.map +1 -1
- package/dist/test-support/toolset.d.ts +19 -0
- package/dist/test-support/toolset.d.ts.map +1 -0
- package/dist/test-support/toolset.js +20 -0
- package/dist/test-support/toolset.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts +29 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +19 -9
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/browser-url.d.ts +1 -1
- package/dist/tools/builtins/browser-url.js +1 -1
- package/dist/tools/builtins/browser.d.ts +6 -6
- package/dist/tools/builtins/computer-use-coordinates.d.ts +65 -0
- package/dist/tools/builtins/computer-use-coordinates.d.ts.map +1 -0
- package/dist/tools/builtins/computer-use-coordinates.js +123 -0
- package/dist/tools/builtins/computer-use-coordinates.js.map +1 -0
- package/dist/tools/builtins/computer-use-image.d.ts +77 -0
- package/dist/tools/builtins/computer-use-image.d.ts.map +1 -0
- package/dist/tools/builtins/computer-use-image.js +223 -0
- package/dist/tools/builtins/computer-use-image.js.map +1 -0
- package/dist/tools/builtins/computer-use.d.ts +519 -14
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +1196 -183
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +9 -1
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/index.d.ts +2 -1
- package/dist/tools/builtins/index.d.ts.map +1 -1
- package/dist/tools/builtins/index.js +1 -1
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/json-string-hint.d.ts +15 -0
- package/dist/tools/builtins/json-string-hint.d.ts.map +1 -0
- package/dist/tools/builtins/json-string-hint.js +17 -0
- package/dist/tools/builtins/json-string-hint.js.map +1 -0
- package/dist/tools/builtins/search-tools.d.ts.map +1 -1
- package/dist/tools/builtins/search-tools.js +7 -15
- package/dist/tools/builtins/search-tools.js.map +1 -1
- package/dist/tools/builtins/skill.d.ts +74 -0
- package/dist/tools/builtins/skill.d.ts.map +1 -1
- package/dist/tools/builtins/skill.js +214 -174
- package/dist/tools/builtins/skill.js.map +1 -1
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +11 -0
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/command-shell.d.ts +10 -0
- package/dist/tools/command-shell.d.ts.map +1 -1
- package/dist/tools/command-shell.js +45 -1
- package/dist/tools/command-shell.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js +5 -0
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
- package/dist/tools/coordinator/ask-user-question.js +26 -13
- package/dist/tools/coordinator/ask-user-question.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +5 -0
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/coordinator/question-options.d.ts +78 -0
- package/dist/tools/coordinator/question-options.d.ts.map +1 -0
- package/dist/tools/coordinator/question-options.js +153 -0
- package/dist/tools/coordinator/question-options.js.map +1 -0
- package/dist/tools/defineTool.d.ts +12 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +24 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/memory/search.d.ts.map +1 -1
- package/dist/tools/memory/search.js +4 -1
- package/dist/tools/memory/search.js.map +1 -1
- package/dist/tools/memory/update.d.ts.map +1 -1
- package/dist/tools/memory/update.js +8 -5
- package/dist/tools/memory/update.js.map +1 -1
- package/dist/tools/render-nonce.d.ts +47 -0
- package/dist/tools/render-nonce.d.ts.map +1 -0
- package/dist/tools/render-nonce.js +63 -0
- package/dist/tools/render-nonce.js.map +1 -0
- package/dist/tools/roster.d.ts +46 -19
- package/dist/tools/roster.d.ts.map +1 -1
- package/dist/tools/roster.js +63 -33
- package/dist/tools/roster.js.map +1 -1
- package/dist/tools/schedules/present.d.ts.map +1 -1
- package/dist/tools/schedules/present.js +2 -0
- package/dist/tools/schedules/present.js.map +1 -1
- package/dist/tools/schedules/schedule-tool.d.ts +6 -5
- package/dist/tools/schedules/schedule-tool.d.ts.map +1 -1
- package/dist/tools/schedules/schedule-tool.js +208 -37
- package/dist/tools/schedules/schedule-tool.js.map +1 -1
- package/dist/tools/schedules/types.d.ts +120 -6
- package/dist/tools/schedules/types.d.ts.map +1 -1
- package/dist/tools/trusted-read-only.d.ts +9 -5
- package/dist/tools/trusted-read-only.d.ts.map +1 -1
- package/dist/tools/trusted-read-only.js +14 -10
- package/dist/tools/trusted-read-only.js.map +1 -1
- package/dist/tools/untrusted-envelope.d.ts +67 -28
- package/dist/tools/untrusted-envelope.d.ts.map +1 -1
- package/dist/tools/untrusted-envelope.js +108 -59
- package/dist/tools/untrusted-envelope.js.map +1 -1
- package/dist/toolsets/combine.d.ts +44 -0
- package/dist/toolsets/combine.d.ts.map +1 -0
- package/dist/toolsets/combine.js +153 -0
- package/dist/toolsets/combine.js.map +1 -0
- package/dist/toolsets/manager.d.ts +201 -0
- package/dist/toolsets/manager.d.ts.map +1 -0
- package/dist/toolsets/manager.js +837 -0
- package/dist/toolsets/manager.js.map +1 -0
- package/dist/toolsets/source-glob.d.ts +15 -0
- package/dist/toolsets/source-glob.d.ts.map +1 -0
- package/dist/toolsets/source-glob.js +23 -0
- package/dist/toolsets/source-glob.js.map +1 -0
- package/dist/toolsets/toolset.d.ts +20 -0
- package/dist/toolsets/toolset.d.ts.map +1 -0
- package/dist/toolsets/toolset.js +28 -0
- package/dist/toolsets/toolset.js.map +1 -0
- package/dist/toolsets/types.d.ts +128 -0
- package/dist/toolsets/types.d.ts.map +1 -0
- package/dist/toolsets/types.js +33 -0
- package/dist/toolsets/types.js.map +1 -0
- package/dist/toolsets/wrappers.d.ts +59 -0
- package/dist/toolsets/wrappers.d.ts.map +1 -0
- package/dist/toolsets/wrappers.js +165 -0
- package/dist/toolsets/wrappers.js.map +1 -0
- package/dist/types/agent/base.d.ts +27 -16
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/index.d.ts +1 -0
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/index.js +1 -0
- package/dist/types/agent/index.js.map +1 -1
- package/dist/types/agent/pipeline.d.ts +2 -0
- package/dist/types/agent/pipeline.d.ts.map +1 -1
- package/dist/types/agent/query.d.ts +117 -0
- package/dist/types/agent/query.d.ts.map +1 -0
- package/dist/types/agent/query.js +2 -0
- package/dist/types/agent/query.js.map +1 -0
- package/dist/types/agent/reactive.d.ts +2 -129
- package/dist/types/agent/reactive.d.ts.map +1 -1
- package/dist/types/agent/router.d.ts +2 -0
- package/dist/types/agent/router.d.ts.map +1 -1
- package/dist/types/agent/supervisor.d.ts +5 -2
- package/dist/types/agent/supervisor.d.ts.map +1 -1
- package/dist/types/authorization/index.d.ts +70 -12
- package/dist/types/authorization/index.d.ts.map +1 -1
- package/dist/types/authorization/index.js +11 -0
- package/dist/types/authorization/index.js.map +1 -1
- package/dist/types/computer-use/index.d.ts +176 -0
- package/dist/types/computer-use/index.d.ts.map +1 -1
- package/dist/types/computer-use/index.js.map +1 -1
- package/dist/types/connector/mcp.d.ts +32 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/errors/index.d.ts +15 -0
- package/dist/types/errors/index.d.ts.map +1 -1
- package/dist/types/errors/index.js.map +1 -1
- package/dist/types/hitl/index.d.ts +57 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/message/index.d.ts +130 -9
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +4 -1
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +22 -4
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +1 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +5 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +8 -3
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +39 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/sandbox/index.d.ts +1 -1
- package/dist/types/session/events.d.ts +14 -3
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/tool/index.d.ts +171 -91
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/toolset/index.d.ts +10 -40
- package/dist/types/toolset/index.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/advisory/history.ts +27 -2
- package/src/advisory/index.ts +1 -1
- package/src/advisory/registry.ts +33 -0
- package/src/agents/AGENTS.md +14 -7
- package/src/agents/PipelineAgent.ts +2 -303
- package/src/agents/QueryAgent.ts +269 -0
- package/src/agents/ReactiveAgent.ts +6 -216
- package/src/agents/RouterAgent.ts +2 -337
- package/src/agents/SupervisorAgent.ts +3 -476
- package/src/agents/defineAgent.ts +13 -2
- package/src/agents/examples/PipelineAgent.ts +302 -0
- package/src/agents/examples/RouterAgent.ts +338 -0
- package/src/agents/examples/SupervisorAgent.ts +472 -0
- package/src/agents/explore.ts +6 -3
- package/src/agents/forward-options.ts +37 -0
- package/src/agents/index.ts +2 -0
- package/src/agents/runAgent.ts +144 -37
- package/src/authorization/command-line.ts +2 -2
- package/src/authorization/gate.ts +46 -5
- package/src/authorization/program.ts +773 -0
- package/src/authorization/reexec-wrapper.ts +589 -0
- package/src/authorization/rules.ts +36 -1
- package/src/authorization/shell-lexer.ts +136 -63
- package/src/bridge/sse/mapper.ts +6 -0
- package/src/capabilities/index.ts +157 -0
- package/src/config/registry.ts +4 -1
- package/src/connector/index.ts +9 -2
- package/src/connector/mcp/adapter.ts +58 -3
- package/src/connector/mcp/client.ts +20 -4
- package/src/connector/mcp/discovery.ts +60 -0
- package/src/connector/mcp/index.ts +13 -0
- package/src/connector/mcp/mcp-toolset.ts +515 -0
- package/src/connector/tools/index.ts +2 -2
- package/src/connector/tools/router.ts +35 -58
- package/src/directory/derive-supervisor.ts +12 -8
- package/src/directory/derive.ts +8 -5
- package/src/execution/code-runtime/types.ts +2 -2
- package/src/invariants/index.ts +4 -1
- package/src/manager/agent/lifecycle.ts +9 -0
- package/src/manager/connector/environment.ts +17 -4
- package/src/manager/connector/index.ts +2 -2
- package/src/manager/connector/tenant.ts +17 -4
- package/src/manager/index.ts +2 -2
- package/src/manager/session/attribution.ts +79 -0
- package/src/manager/session/turn-recorder.ts +211 -55
- package/src/peers/address.ts +58 -0
- package/src/peers/client.ts +202 -0
- package/src/peers/dir.ts +176 -0
- package/src/peers/endpoint.ts +564 -0
- package/src/peers/envelope.ts +121 -0
- package/src/peers/index.ts +96 -0
- package/src/peers/protocol.ts +196 -0
- package/src/peers/record.ts +56 -0
- package/src/peers/registry.ts +241 -0
- package/src/plugin/define.ts +68 -0
- package/src/plugin/index.ts +2 -0
- package/src/plugin/lifecycle.ts +286 -106
- package/src/plugin/resolver.ts +6 -3
- package/src/plugin/shell-hook.ts +21 -5
- package/src/pricing/catalogue.generated.ts +28 -4
- package/src/pricing/rates.source.json +27 -6
- package/src/probe/errors.ts +4 -1
- package/src/prompt/coding-agent-doctrine.ts +22 -10
- package/src/prompt/contributions.ts +12 -2
- package/src/prompt/index.ts +1 -0
- package/src/provider/collect-chat-completion.ts +96 -11
- package/src/provider/mock.ts +24 -6
- package/src/provider/registry.ts +4 -1
- package/src/provider/tool-call-framing.ts +226 -0
- package/src/public-runtime.ts +152 -9
- package/src/public-tools.ts +43 -5
- package/src/public-types.ts +65 -6
- package/src/read-model/registry.ts +7 -2
- package/src/registry/ManagedRegistry.ts +57 -6
- package/src/registry/collision.ts +64 -0
- package/src/registry/command/index.ts +7 -3
- package/src/registry/index.ts +3 -9
- package/src/registry/plugin/index.ts +14 -1
- package/src/registry/tool/callable.ts +36 -0
- package/src/registry/tool/execute.ts +16 -839
- package/src/registry/tool/portable.ts +59 -0
- package/src/registry/tool/presentation.ts +6 -5
- package/src/runtime/bidi/session.ts +24 -5
- package/src/runtime/query/executor/tool-call-admission.ts +288 -22
- package/src/runtime/query/executor.ts +129 -16
- package/src/runtime/query/file-evidence-replay.ts +8 -7
- package/src/runtime/query/fork/prepare.ts +6 -1
- package/src/runtime/query/guardrail-presets.ts +4 -3
- package/src/runtime/query/index.ts +199 -101
- package/src/runtime/query/iteration/index.ts +13 -3
- package/src/runtime/query/iteration/phases/compaction.ts +1 -1
- package/src/runtime/query/iteration/phases/context.ts +2 -2
- package/src/runtime/query/iteration/phases/tool-review.ts +59 -2
- package/src/runtime/query/iteration/stream-turn.ts +390 -136
- package/src/runtime/query/iteration/tool-input.ts +284 -0
- package/src/runtime/query/observation-context.ts +2 -2
- package/src/runtime/query/prelude-lease.ts +132 -0
- package/src/runtime/query/prepare-turn.ts +477 -198
- package/src/runtime/query/prompt-cache.ts +2 -2
- package/src/runtime/query/prompt.ts +4 -4
- package/src/runtime/query/resume-session.ts +42 -3
- package/src/runtime/query/review-policy.ts +190 -24
- package/src/runtime/query/tooling.ts +3 -6
- package/src/runtime/query/turn-state.ts +4 -0
- package/src/runtime/system-events.ts +246 -0
- package/src/scheduler/delegating.ts +7 -2
- package/src/skills/index.ts +1 -1
- package/src/skills/registry.ts +33 -0
- package/src/store/session-log/conformance.ts +51 -8
- package/src/store/session-log/core.ts +10 -4
- package/src/store/session-log/fold.ts +33 -7
- package/src/store/session-log/index.ts +2 -0
- package/src/test-support/toolset.ts +23 -0
- package/src/tools/builtins/bash.ts +27 -10
- package/src/tools/builtins/browser-url.ts +1 -1
- package/src/tools/builtins/computer-use-coordinates.ts +144 -0
- package/src/tools/builtins/computer-use-image.ts +278 -0
- package/src/tools/builtins/computer-use.ts +1495 -191
- package/src/tools/builtins/edit.ts +12 -2
- package/src/tools/builtins/index.ts +7 -1
- package/src/tools/builtins/json-string-hint.ts +16 -0
- package/src/tools/builtins/search-tools.ts +7 -20
- package/src/tools/builtins/skill.ts +304 -174
- package/src/tools/builtins/write-file.ts +12 -0
- package/src/tools/command-shell.ts +46 -1
- package/src/tools/coordinator/agent.ts +6 -0
- package/src/tools/coordinator/ask-user-question.ts +29 -15
- package/src/tools/coordinator/index.ts +6 -0
- package/src/tools/coordinator/question-options.ts +195 -0
- package/src/tools/defineTool.ts +38 -0
- package/src/tools/memory/search.ts +4 -1
- package/src/tools/memory/update.ts +8 -5
- package/src/tools/render-nonce.ts +69 -0
- package/src/tools/roster.ts +84 -33
- package/src/tools/schedules/present.ts +2 -0
- package/src/tools/schedules/schedule-tool.ts +238 -39
- package/src/tools/schedules/types.ts +120 -6
- package/src/tools/trusted-read-only.ts +19 -10
- package/src/tools/untrusted-envelope.ts +121 -57
- package/src/toolsets/combine.ts +181 -0
- package/src/toolsets/manager.ts +996 -0
- package/src/toolsets/source-glob.ts +22 -0
- package/src/toolsets/toolset.ts +31 -0
- package/src/toolsets/types.ts +154 -0
- package/src/toolsets/wrappers.ts +191 -0
- package/src/types/agent/base.ts +29 -16
- package/src/types/agent/index.ts +1 -0
- package/src/types/agent/pipeline.ts +2 -0
- package/src/types/agent/query.ts +129 -0
- package/src/types/agent/reactive.ts +5 -142
- package/src/types/agent/router.ts +2 -0
- package/src/types/agent/supervisor.ts +5 -2
- package/src/types/authorization/index.ts +33 -0
- package/src/types/computer-use/index.ts +202 -0
- package/src/types/connector/mcp.ts +32 -0
- package/src/types/errors/index.ts +15 -0
- package/src/types/hitl/index.ts +57 -0
- package/src/types/message/index.ts +135 -7
- package/src/types/plugin/index.ts +44 -21
- package/src/types/provider/chat.ts +5 -0
- package/src/types/provider/config.ts +8 -3
- package/src/types/provider/stream.ts +39 -0
- package/src/types/sandbox/index.ts +1 -1
- package/src/types/session/events.ts +14 -3
- package/src/types/tool/index.ts +173 -105
- package/src/types/toolset/index.ts +10 -47
- package/dist/registry/toolset/catalog.d.ts +0 -42
- package/dist/registry/toolset/catalog.d.ts.map +0 -1
- package/dist/registry/toolset/catalog.js +0 -234
- package/dist/registry/toolset/catalog.js.map +0 -1
- package/src/registry/toolset/catalog.ts +0 -308
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a command line actually execs a program.
|
|
3
|
+
*
|
|
4
|
+
* Three consumers each answered "what program does this command run" their
|
|
5
|
+
* own way — the live `bash` tool's escalation, the scheduled-run floor's
|
|
6
|
+
* `pkill`/`systemctl`/… detection, and a scheduled script's static check —
|
|
7
|
+
* and each guessed differently about a re-exec wrapper (`sudo`, `env`,
|
|
8
|
+
* `nice`, `timeout`, …) standing in front of the real program. A security
|
|
9
|
+
* review found that every one of them only ever looked at
|
|
10
|
+
* `command.words[command.assignments]`, so `env $(echo git) push`,
|
|
11
|
+
* `timeout 5 $(echo systemctl) stop …` and the rest ran unverified: the
|
|
12
|
+
* wrapper's own name is what each check saw, never the word it was actually
|
|
13
|
+
* about to run.
|
|
14
|
+
*
|
|
15
|
+
* `programPositions` is the one place that answer now lives. It unwraps a
|
|
16
|
+
* chain of re-exec wrappers — each with its OWN real option grammar, so
|
|
17
|
+
* `stdbuf -oL prog` and `timeout 5 prog` are read correctly rather than
|
|
18
|
+
* assumed to put the program right after the wrapper's name — and reports
|
|
19
|
+
* every position in the command where a program is actually named: the
|
|
20
|
+
* (possibly wrapped) head, and, for `find`, each `-exec`/`-execdir`/`-ok`/
|
|
21
|
+
* `-okdir` clause's own head.
|
|
22
|
+
*
|
|
23
|
+
* A position is `known` (the literal, non-expanding word that names the
|
|
24
|
+
* program) or `unknown` (why it could not be resolved: the word itself
|
|
25
|
+
* expands, an option this does not recognise stood in the way, or an
|
|
26
|
+
* `xargs` program that is a shell or a `{}` placeholder). Fail closed: an
|
|
27
|
+
* option this does not model makes the position unknown, never "assumed to
|
|
28
|
+
* be the program" and never "assumed harmless".
|
|
29
|
+
*
|
|
30
|
+
* `source`/`.` and `eval` are read the same way a nested `bash -c` payload
|
|
31
|
+
* already is, not as an automatic unknown: `source path`/`. path` with a
|
|
32
|
+
* LITERAL path is known — the position is the `source`/`.` word itself,
|
|
33
|
+
* exactly as `bash path` is known without anyone reading what `path`
|
|
34
|
+
* contains — and unknown only when the path itself expands (`source "$X"`).
|
|
35
|
+
* `eval`'s literal argument words are joined and lexed as a command line of
|
|
36
|
+
* their own, and every position in the commands that come out of that is
|
|
37
|
+
* folded into the result — unknown when that reading is opaque, when any
|
|
38
|
+
* argument word expands, or, transitively, when a position inside the
|
|
39
|
+
* lexed payload is itself unknown (`eval 'env $(echo git) push'`).
|
|
40
|
+
* Consistency review (2026-09-24): treating `eval`/`source`/`.` as
|
|
41
|
+
* automatically unknown "wherever they stand, even literal" made
|
|
42
|
+
* `source .venv/bin/activate` ask alongside `source "$X"`, which is exactly
|
|
43
|
+
* the asymmetry `bash script.sh` (known) versus `bash -c "$X"` (unknown)
|
|
44
|
+
* does not have — the same module read a literal argument two different
|
|
45
|
+
* ways depending only on which command carried it. An `unknown` position
|
|
46
|
+
* still carries `word` when a single word is the reason (the word itself
|
|
47
|
+
* expands, so its value is not known before the line runs) — a caller that
|
|
48
|
+
* only needs "is there a word here that could name any of several tools"
|
|
49
|
+
* (the scheduled-run floor's `pkill`/`systemctl`/… detection) reads that
|
|
50
|
+
* word whether the position is known or not; `unknown` with no `word` is a
|
|
51
|
+
* structural failure with no single culprit (too many wrappers, an
|
|
52
|
+
* unrecognised option, an opaque `eval` payload, a poisoned environment).
|
|
53
|
+
*
|
|
54
|
+
* `hasPoisoningPrefix`, `poisonsLaterCommands` and `poisonedProgramPosition`
|
|
55
|
+
* are the other half a caller iterating a whole script needs: a command
|
|
56
|
+
* that sets `PATH`, `LD_PRELOAD`, `LD_LIBRARY_PATH`, `BASH_ENV`, `ENV` or
|
|
57
|
+
* `IFS` — as a leading assignment (which affects that same command's own
|
|
58
|
+
* program position) or as an `export`/bare assignment (which affects every
|
|
59
|
+
* command after it in the same script) — makes a LATER literal program name
|
|
60
|
+
* unverifiable too: which binary a literal `ls` resolves to depends on
|
|
61
|
+
* `PATH` at the moment it runs, not on the text of the word `ls`.
|
|
62
|
+
* `resolveScriptPrograms` threads that state through a whole script's
|
|
63
|
+
* commands in one pass, for a caller that does not want to write the loop
|
|
64
|
+
* itself.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
import { RUNTIME_EVALUATORS } from './command-line.js'
|
|
68
|
+
import { skipOneReexecWrapper } from './reexec-wrapper.js'
|
|
69
|
+
import {
|
|
70
|
+
NESTED_SHELLS,
|
|
71
|
+
type ShellCommand,
|
|
72
|
+
type ShellDialect,
|
|
73
|
+
type ShellWord,
|
|
74
|
+
basename,
|
|
75
|
+
lexShellCommandLine,
|
|
76
|
+
nestedShellCommand,
|
|
77
|
+
} from './shell-lexer.js'
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Where a program is named, or why it could not be resolved. `word` is set
|
|
81
|
+
* whenever the position resolves to a specific word — including an unknown
|
|
82
|
+
* one whose OWN text is what is unresolvable (it expands, so its value is
|
|
83
|
+
* not known before the line runs); a caller that only cares "is there a
|
|
84
|
+
* word here that might be any program name" (the scheduled-run floor's
|
|
85
|
+
* scheduler-tool detection) reads `word` regardless of `unknown`. `unknown`
|
|
86
|
+
* with no `word` is a structural failure with no single culprit word — an
|
|
87
|
+
* option on a wrapper this does not recognise, too many wrappers, an opaque
|
|
88
|
+
* `eval` payload, or a poisoned resolution environment.
|
|
89
|
+
*/
|
|
90
|
+
export type ProgramPosition =
|
|
91
|
+
| { readonly word: ShellWord; readonly unknown?: undefined }
|
|
92
|
+
| { readonly word?: ShellWord; readonly unknown: string }
|
|
93
|
+
|
|
94
|
+
/** How many re-exec wrappers a chain unwraps before giving up (`sudo env nice … prog`). */
|
|
95
|
+
const MAX_WRAPPER_DEPTH = 8
|
|
96
|
+
|
|
97
|
+
const LITERAL = (w: ShellWord): boolean => !w.expands
|
|
98
|
+
|
|
99
|
+
/** `NAME=value` read off an assignment word's own text, or null. */
|
|
100
|
+
function assignmentName(word: ShellWord): string | null {
|
|
101
|
+
const text = word.text.replace(/\\\n/g, '')
|
|
102
|
+
const match = /^([A-Za-z_][A-Za-z0-9_]*)\+?=/.exec(text)
|
|
103
|
+
return match && word.value.startsWith(match[0]) ? (match[1] as string) : null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type Consumed =
|
|
107
|
+
| { readonly rest: readonly ShellWord[] }
|
|
108
|
+
| { readonly unknown: string }
|
|
109
|
+
| { readonly none: true }
|
|
110
|
+
|
|
111
|
+
/** A `-c`/`--command` argument found among a wrapper's options — never produced by {@link skipOptions}, only by {@link findDashC}. */
|
|
112
|
+
type WithDashC = { readonly payload: ShellWord }
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* `wrapper NAME=value…` sets `NAME` for the wrapped command only (`env
|
|
116
|
+
* NAME=value prog`, `sudo NAME=value prog`) — the same taint a leading
|
|
117
|
+
* command-prefix assignment gives its own command, just spelled through the
|
|
118
|
+
* wrapper instead of in front of it.
|
|
119
|
+
*/
|
|
120
|
+
function poisonedByWrapperAssignment(wrapper: string, name: string): string {
|
|
121
|
+
return `${wrapper} ${name}=… sets ${name} for the command that follows, so a literal name after it cannot be trusted to resolve to what its text says`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Scan `words` for a bare `-c`/`--command COMMAND` pair among options this
|
|
126
|
+
* wrapper recognises (everything else validated against `flags`/`withArg`,
|
|
127
|
+
* the same as {@link skipOptions}). `--command=X` is not modelled — a
|
|
128
|
+
* documented, deliberately conservative gap: it falls through to the
|
|
129
|
+
* unrecognised-option path, `unknown` never a silent pass. Returns the `-c`
|
|
130
|
+
* payload when found, the words left after options when it is not (a valid
|
|
131
|
+
* outcome some callers use — `flock`'s positional form, `script`'s bare
|
|
132
|
+
* form), or `unknown` on a bad option or a `-c` given no argument.
|
|
133
|
+
*/
|
|
134
|
+
function findDashC(
|
|
135
|
+
words: readonly ShellWord[],
|
|
136
|
+
flags: ReadonlySet<string>,
|
|
137
|
+
withArg: ReadonlySet<string>,
|
|
138
|
+
optionalAttached: readonly string[] = [],
|
|
139
|
+
): WithDashC | { readonly rest: readonly ShellWord[] } | { readonly unknown: string } {
|
|
140
|
+
let i = 0
|
|
141
|
+
while (i < words.length) {
|
|
142
|
+
const w = words[i] as ShellWord
|
|
143
|
+
if (!LITERAL(w)) return { rest: words.slice(i) }
|
|
144
|
+
if (w.value === '-c' || w.value === '--command') {
|
|
145
|
+
const payload = words[i + 1]
|
|
146
|
+
if (payload === undefined) return { unknown: `${w.value} was given no command` }
|
|
147
|
+
return { payload }
|
|
148
|
+
}
|
|
149
|
+
if (w.value === '--') return { rest: words.slice(i + 1) }
|
|
150
|
+
if (flags.has(w.value)) {
|
|
151
|
+
i += 1
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
if (
|
|
155
|
+
optionalAttached.some((option) =>
|
|
156
|
+
option.startsWith('--')
|
|
157
|
+
? w.value.startsWith(`${option}=`)
|
|
158
|
+
: w.value.startsWith(option) && w.value.length > option.length,
|
|
159
|
+
)
|
|
160
|
+
) {
|
|
161
|
+
i += 1
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
if (withArg.has(w.value)) {
|
|
165
|
+
i += 2
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
const eq = w.value.indexOf('=')
|
|
169
|
+
if (w.value.startsWith('--') && eq > 0 && withArg.has(w.value.slice(0, eq))) {
|
|
170
|
+
i += 1
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
if (/^--?[A-Za-z]/.test(w.value)) {
|
|
174
|
+
return { unknown: `an option this does not read: ${w.value}` }
|
|
175
|
+
}
|
|
176
|
+
return { rest: words.slice(i) }
|
|
177
|
+
}
|
|
178
|
+
return { rest: words.slice(i) }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Skip a wrapper's own recognised, argument-taking or bare options over
|
|
183
|
+
* `words`, stopping at the first word that is not one of them (the
|
|
184
|
+
* boundary between the wrapper's own invocation and whatever runs next).
|
|
185
|
+
* An option-shaped word (`-x`, `--long`) this does not recognise is
|
|
186
|
+
* `unknown`, never treated as the program; a word this cannot classify
|
|
187
|
+
* because it expands is left for the caller, which already knows to call
|
|
188
|
+
* an expanding program word unknown. `explicit`, when given, names options
|
|
189
|
+
* that ARE recognised but never lead to a named program at all (`sudo -i`
|
|
190
|
+
* runs a runtime-chosen login shell) — checked before `flags`/`withArg`, so
|
|
191
|
+
* the reason is explicit rather than an accident of `flags` swallowing them.
|
|
192
|
+
*/
|
|
193
|
+
function skipOptions(
|
|
194
|
+
words: readonly ShellWord[],
|
|
195
|
+
flags: ReadonlySet<string>,
|
|
196
|
+
withArg: ReadonlySet<string>,
|
|
197
|
+
explicit?: ReadonlyMap<string, string>,
|
|
198
|
+
): Consumed {
|
|
199
|
+
let i = 0
|
|
200
|
+
while (i < words.length) {
|
|
201
|
+
const w = words[i] as ShellWord
|
|
202
|
+
if (!LITERAL(w)) return { rest: words.slice(i) }
|
|
203
|
+
if (w.value === '--') return { rest: words.slice(i + 1) }
|
|
204
|
+
const reason = explicit?.get(w.value)
|
|
205
|
+
if (reason !== undefined) return { unknown: reason }
|
|
206
|
+
if (flags.has(w.value)) {
|
|
207
|
+
i += 1
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
210
|
+
if (withArg.has(w.value)) {
|
|
211
|
+
i += 2
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
const eq = w.value.indexOf('=')
|
|
215
|
+
if (w.value.startsWith('--') && eq > 0 && withArg.has(w.value.slice(0, eq))) {
|
|
216
|
+
i += 1
|
|
217
|
+
continue
|
|
218
|
+
}
|
|
219
|
+
// A short option's value attached to its flag (`-c2`, `-n10`, `-oL`),
|
|
220
|
+
// the conventional form for `ionice`, `nice`, `stdbuf` and `taskset`'s
|
|
221
|
+
// `-c` list — one token, no separate argument word.
|
|
222
|
+
if (!w.value.startsWith('--') && w.value.length > 2 && withArg.has(w.value.slice(0, 2))) {
|
|
223
|
+
i += 1
|
|
224
|
+
continue
|
|
225
|
+
}
|
|
226
|
+
if (/^--?[A-Za-z]/.test(w.value)) {
|
|
227
|
+
return { unknown: `an option this does not read: ${w.value}` }
|
|
228
|
+
}
|
|
229
|
+
return { rest: words.slice(i) }
|
|
230
|
+
}
|
|
231
|
+
return { none: true }
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** `xargs`' own options, then its program (the first non-option word, `echo` when there is none). */
|
|
235
|
+
function unwrapXargs(words: readonly ShellWord[]): Consumed {
|
|
236
|
+
const flags = new Set(['-0', '-o', '-p', '-r', '-t', '-x', '--null', '--open-tty', '--verbose'])
|
|
237
|
+
const withArg = new Set(['-a', '-d', '-E', '-e', '-I', '-i', '-L', '-l', '-n', '-P', '-s'])
|
|
238
|
+
const skipped = skipOptions(words, flags, withArg)
|
|
239
|
+
if ('unknown' in skipped) return skipped
|
|
240
|
+
const rest = 'none' in skipped ? [] : skipped.rest
|
|
241
|
+
const program = rest[0]
|
|
242
|
+
if (program === undefined) {
|
|
243
|
+
// No program named: xargs' own default is `echo`, always safe.
|
|
244
|
+
return {
|
|
245
|
+
rest: [{ text: 'echo', value: 'echo', expands: false, quoted: false, substitutes: false }],
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!LITERAL(program)) return { rest }
|
|
249
|
+
if (program.value === '{}' || NESTED_SHELLS.has(basename(program.value))) {
|
|
250
|
+
return {
|
|
251
|
+
unknown: `xargs' program is built from its input, not named in the line: ${program.text}`,
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return { rest }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Special command runners. Plain re-exec wrappers are all read by the
|
|
259
|
+
* shared skipOneReexecWrapper, also used by the shell lexer to find -c.
|
|
260
|
+
*/
|
|
261
|
+
function unwrapOnce(words: readonly ShellWord[]): Consumed | WithDashC | null {
|
|
262
|
+
const head = words[0] as ShellWord
|
|
263
|
+
const name = basename(head.value)
|
|
264
|
+
const rest = words.slice(1)
|
|
265
|
+
switch (name) {
|
|
266
|
+
case 'xargs':
|
|
267
|
+
return unwrapXargs(rest)
|
|
268
|
+
case 'su':
|
|
269
|
+
case 'runuser': {
|
|
270
|
+
const found = findDashC(
|
|
271
|
+
rest,
|
|
272
|
+
new Set(['-p', '--preserve-environment', '-P', '--pty', '-l', '--login', '-']),
|
|
273
|
+
new Set([
|
|
274
|
+
'-s',
|
|
275
|
+
'--shell',
|
|
276
|
+
'-g',
|
|
277
|
+
'--group',
|
|
278
|
+
'-G',
|
|
279
|
+
'--supp-group',
|
|
280
|
+
'-w',
|
|
281
|
+
'--whitelist-environment',
|
|
282
|
+
]),
|
|
283
|
+
)
|
|
284
|
+
if ('unknown' in found) return found
|
|
285
|
+
if ('payload' in found) return found
|
|
286
|
+
// No `-c`/`--command` at all: both start the target user's login
|
|
287
|
+
// or default shell — an interactive session, not a fixed next
|
|
288
|
+
// command this can name.
|
|
289
|
+
return {
|
|
290
|
+
unknown: `${name} without -c/--command starts an interactive login shell, not a named program`,
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
case 'script': {
|
|
294
|
+
// Per util-linux `script --help`: `-t[<file>], --timing[=<file>]`
|
|
295
|
+
// is a DEPRECATED ALIAS for `-T`/`--log-timing` with an OPTIONAL
|
|
296
|
+
// value (default file is stderr when bare) — it never consumes a
|
|
297
|
+
// separate following word, unlike the real `-T <file>`. Modelling
|
|
298
|
+
// `-t` as taking one used to swallow a following `-c` as `-t`'s own
|
|
299
|
+
// value, reading `script` as known and never looking at `-c`'s
|
|
300
|
+
// argument at all.
|
|
301
|
+
const flags = new Set([
|
|
302
|
+
'-a',
|
|
303
|
+
'--append',
|
|
304
|
+
'-e',
|
|
305
|
+
'--return',
|
|
306
|
+
'-f',
|
|
307
|
+
'--flush',
|
|
308
|
+
'--force',
|
|
309
|
+
'-q',
|
|
310
|
+
'--quiet',
|
|
311
|
+
'-t',
|
|
312
|
+
'--timing',
|
|
313
|
+
])
|
|
314
|
+
const withArg = new Set([
|
|
315
|
+
'-I',
|
|
316
|
+
'--log-in',
|
|
317
|
+
'-O',
|
|
318
|
+
'--log-out',
|
|
319
|
+
'-B',
|
|
320
|
+
'--log-io',
|
|
321
|
+
'-T',
|
|
322
|
+
'--log-timing',
|
|
323
|
+
'-m',
|
|
324
|
+
'--logging-format',
|
|
325
|
+
'-E',
|
|
326
|
+
'--echo',
|
|
327
|
+
'-o',
|
|
328
|
+
'--output-limit',
|
|
329
|
+
])
|
|
330
|
+
// `script [options] [<file>] [-- <command> [<argument>...]]`: `--`
|
|
331
|
+
// is an alternative to `-c`, running the command directly as an
|
|
332
|
+
// argv (not a joined string re-lexed through a shell) rather than
|
|
333
|
+
// recording an interactive session.
|
|
334
|
+
const dashDash = rest.findIndex((w) => LITERAL(w) && w.value === '--')
|
|
335
|
+
if (dashDash >= 0) {
|
|
336
|
+
const before = skipOptions(rest.slice(0, dashDash), flags, withArg)
|
|
337
|
+
if ('unknown' in before) return before
|
|
338
|
+
const after = rest.slice(dashDash + 1)
|
|
339
|
+
return after.length === 0 ? { none: true } : { rest: after }
|
|
340
|
+
}
|
|
341
|
+
const found = findDashC(rest, flags, withArg, ['-t', '--timing'])
|
|
342
|
+
if ('unknown' in found) return found
|
|
343
|
+
if ('payload' in found) return found
|
|
344
|
+
// No `-c` and no `--`: `script` itself is the program — it
|
|
345
|
+
// records a terminal session (an interactive shell as its own
|
|
346
|
+
// child), and its only other argument is a log file name, not a
|
|
347
|
+
// command to name.
|
|
348
|
+
return { none: true }
|
|
349
|
+
}
|
|
350
|
+
case 'flock': {
|
|
351
|
+
const found = findDashC(
|
|
352
|
+
rest,
|
|
353
|
+
new Set([
|
|
354
|
+
'-s',
|
|
355
|
+
'--shared',
|
|
356
|
+
'-x',
|
|
357
|
+
'-e',
|
|
358
|
+
'--exclusive',
|
|
359
|
+
'-n',
|
|
360
|
+
'--nb',
|
|
361
|
+
'--nonblock',
|
|
362
|
+
'-o',
|
|
363
|
+
'--close',
|
|
364
|
+
'-F',
|
|
365
|
+
'--no-fork',
|
|
366
|
+
'--verbose',
|
|
367
|
+
]),
|
|
368
|
+
new Set(['-w', '--timeout', '-E', '--conflict-exit-code']),
|
|
369
|
+
)
|
|
370
|
+
if ('unknown' in found) return found
|
|
371
|
+
if ('payload' in found) return found
|
|
372
|
+
// Positional form (`flock file|directory command [args…]`): what
|
|
373
|
+
// is left after options starts with the locked file/directory
|
|
374
|
+
// (or a bare fd, with no command — nothing execs), then the real
|
|
375
|
+
// argv, run directly — no shell, no joining.
|
|
376
|
+
const target = found.rest[0]
|
|
377
|
+
if (target === undefined) return { none: true }
|
|
378
|
+
const remaining = found.rest.slice(1)
|
|
379
|
+
return remaining.length === 0 ? { none: true } : { rest: remaining }
|
|
380
|
+
}
|
|
381
|
+
default:
|
|
382
|
+
return null
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* `source path [args…]` / `. path [args…]`: known at `head` (the
|
|
388
|
+
* `source`/`.` word itself) when `path` is a literal word, exactly as
|
|
389
|
+
* `bash path` is known without reading what `path` contains — trailing
|
|
390
|
+
* words are the sourced script's own positional parameters, not inspected
|
|
391
|
+
* either, the same as `bash path`'s do not name a second program. Unknown,
|
|
392
|
+
* naming `path` itself, when it expands or is absent.
|
|
393
|
+
*/
|
|
394
|
+
function resolveSourceOrDot(head: ShellWord, args: readonly ShellWord[]): ProgramPosition {
|
|
395
|
+
const path = args[0]
|
|
396
|
+
if (path === undefined) {
|
|
397
|
+
return { word: head, unknown: `${basename(head.value)} was given nothing to run` }
|
|
398
|
+
}
|
|
399
|
+
if (!LITERAL(path)) {
|
|
400
|
+
return { word: path, unknown: `the file this runs is decided at runtime: ${path.text}` }
|
|
401
|
+
}
|
|
402
|
+
return { word: head }
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* `eval word…`: bash joins `eval`'s arguments with a space and reads the
|
|
407
|
+
* result as a command line. When every argument word is literal, that joined
|
|
408
|
+
* text is lexed the same way a `bash -c` payload already is, and every
|
|
409
|
+
* position in the commands it contains (poisoning threaded across them,
|
|
410
|
+
* same as any script) is folded into the result — unknown when the reading
|
|
411
|
+
* is opaque or incomplete, and transitively unknown when a position inside
|
|
412
|
+
* it is. An expanding argument word is unknown outright: its value, and so
|
|
413
|
+
* what `eval` will even read, is not known before the line runs. `eval`
|
|
414
|
+
* with no arguments does nothing in bash and names no position at all.
|
|
415
|
+
*/
|
|
416
|
+
function resolveEvalPayload(
|
|
417
|
+
args: readonly ShellWord[],
|
|
418
|
+
dialect: ShellDialect,
|
|
419
|
+
): readonly ProgramPosition[] {
|
|
420
|
+
if (args.length === 0) return []
|
|
421
|
+
for (const arg of args) {
|
|
422
|
+
if (!LITERAL(arg)) {
|
|
423
|
+
return [{ word: arg, unknown: `eval's payload is decided at runtime: ${arg.text}` }]
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const payload = args.map((arg) => arg.value).join(' ')
|
|
427
|
+
const reading = lexShellCommandLine(payload, { dialect })
|
|
428
|
+
if (reading.opaque || !reading.complete) {
|
|
429
|
+
return [
|
|
430
|
+
{
|
|
431
|
+
unknown: `eval's payload cannot be read (${reading.reasons[0] ?? 'a construct the lexer does not model'}): ${payload}`,
|
|
432
|
+
},
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
return resolveScriptPrograms(reading.commands, dialect).flatMap((entry) => entry.positions)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** The final position a chain of re-exec wrappers resolves to, from `words` (already literal, non-empty). */
|
|
439
|
+
function resolveChain(
|
|
440
|
+
words: readonly ShellWord[],
|
|
441
|
+
dialect: ShellDialect,
|
|
442
|
+
): readonly ProgramPosition[] {
|
|
443
|
+
let current = words
|
|
444
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth += 1) {
|
|
445
|
+
const head = current[0]
|
|
446
|
+
if (head === undefined) return [{ unknown: 'no program follows this wrapper' }]
|
|
447
|
+
if (!LITERAL(head)) {
|
|
448
|
+
return [{ word: head, unknown: `the program is decided at runtime: ${head.text}` }]
|
|
449
|
+
}
|
|
450
|
+
const name = basename(head.value)
|
|
451
|
+
if (name === 'eval') return resolveEvalPayload(current.slice(1), dialect)
|
|
452
|
+
if (name === 'watch') return resolveWatchArgs(current.slice(1), dialect)
|
|
453
|
+
if (RUNTIME_EVALUATORS.has(name)) {
|
|
454
|
+
// `source`/`.`: `eval` is handled above, on its own, since a
|
|
455
|
+
// literal `eval` reads its payload rather than being the position
|
|
456
|
+
// itself.
|
|
457
|
+
return [resolveSourceOrDot(head, current.slice(1))]
|
|
458
|
+
}
|
|
459
|
+
const wrapper = skipOneReexecWrapper(current)
|
|
460
|
+
if (wrapper !== null) {
|
|
461
|
+
if ('unknown' in wrapper) return [{ unknown: wrapper.unknown }]
|
|
462
|
+
if ('none' in wrapper) return [{ word: head }]
|
|
463
|
+
for (const assignment of wrapper.assignments) {
|
|
464
|
+
if (DYNAMIC_RESOLUTION_VARIABLES.has(assignment)) {
|
|
465
|
+
return [{ unknown: poisonedByWrapperAssignment(name, assignment) }]
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (wrapper.rest.length === 0) return [{ word: head }]
|
|
469
|
+
current = wrapper.rest
|
|
470
|
+
continue
|
|
471
|
+
}
|
|
472
|
+
const unwrapped = unwrapOnce(current)
|
|
473
|
+
if (unwrapped === null) return [{ word: head }]
|
|
474
|
+
if ('unknown' in unwrapped) return [{ unknown: unwrapped.unknown }]
|
|
475
|
+
if ('none' in unwrapped) return [{ word: head }]
|
|
476
|
+
if ('payload' in unwrapped) return resolveDashCPayload(unwrapped.payload, dialect)
|
|
477
|
+
if (unwrapped.rest.length === 0) return [{ word: head }]
|
|
478
|
+
current = unwrapped.rest
|
|
479
|
+
}
|
|
480
|
+
return [{ unknown: 'too many re-exec wrappers to follow' }]
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* A `-c COMMAND` payload (`su -c`, `runuser -c`, `script -c`, `flock -c`):
|
|
485
|
+
* known — recursed into, the same as a nested `bash -c '<literal>'` payload
|
|
486
|
+
* — when literal, unknown when it expands.
|
|
487
|
+
*/
|
|
488
|
+
function resolveDashCPayload(
|
|
489
|
+
payload: ShellWord,
|
|
490
|
+
dialect: ShellDialect,
|
|
491
|
+
): readonly ProgramPosition[] {
|
|
492
|
+
if (!LITERAL(payload)) {
|
|
493
|
+
return [
|
|
494
|
+
{ word: payload, unknown: `the command this runs is decided at runtime: ${payload.text}` },
|
|
495
|
+
]
|
|
496
|
+
}
|
|
497
|
+
const reading = lexShellCommandLine(payload.value, { dialect })
|
|
498
|
+
if (reading.opaque || !reading.complete) {
|
|
499
|
+
return [
|
|
500
|
+
{
|
|
501
|
+
unknown: `this -c payload cannot be read (${reading.reasons[0] ?? 'a construct the lexer does not model'}): ${payload.value}`,
|
|
502
|
+
},
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
return resolveScriptPrograms(reading.commands, dialect).flatMap((entry) => entry.positions)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* `watch [options] command…` joins its trailing words with a space and runs
|
|
510
|
+
* them via a shell, exactly the way `eval` does — except with `-x`/`--exec`,
|
|
511
|
+
* which execs the argv directly, no shell, no joining, read the ordinary way.
|
|
512
|
+
*/
|
|
513
|
+
function resolveWatchArgs(
|
|
514
|
+
rest: readonly ShellWord[],
|
|
515
|
+
dialect: ShellDialect,
|
|
516
|
+
): readonly ProgramPosition[] {
|
|
517
|
+
const usesExec = rest.some((w) => LITERAL(w) && (w.value === '-x' || w.value === '--exec'))
|
|
518
|
+
const skipped = skipOptions(
|
|
519
|
+
rest,
|
|
520
|
+
new Set([
|
|
521
|
+
'-d',
|
|
522
|
+
'--differences',
|
|
523
|
+
'-p',
|
|
524
|
+
'--precise',
|
|
525
|
+
'-t',
|
|
526
|
+
'--no-title',
|
|
527
|
+
'-b',
|
|
528
|
+
'--beep',
|
|
529
|
+
'-e',
|
|
530
|
+
'--errexit',
|
|
531
|
+
'-g',
|
|
532
|
+
'--chgexit',
|
|
533
|
+
'-c',
|
|
534
|
+
'--color',
|
|
535
|
+
'-x',
|
|
536
|
+
'--exec',
|
|
537
|
+
]),
|
|
538
|
+
new Set(['-n', '--interval']),
|
|
539
|
+
)
|
|
540
|
+
if ('unknown' in skipped) return [{ unknown: skipped.unknown }]
|
|
541
|
+
const afterOptions = 'rest' in skipped ? skipped.rest : []
|
|
542
|
+
if (afterOptions.length === 0) return []
|
|
543
|
+
return usesExec ? resolveChain(afterOptions, dialect) : resolveEvalPayload(afterOptions, dialect)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** `find`'s own `-exec`/`-execdir`/`-ok`/`-okdir` clauses, each an independent program position. */
|
|
547
|
+
function findExecPositions(words: readonly ShellWord[], dialect: ShellDialect): ProgramPosition[] {
|
|
548
|
+
const positions: ProgramPosition[] = []
|
|
549
|
+
for (let i = 0; i < words.length; i += 1) {
|
|
550
|
+
const w = words[i] as ShellWord
|
|
551
|
+
if (!LITERAL(w) || !/^-(?:exec|execdir|ok|okdir)$/.test(w.value)) continue
|
|
552
|
+
const clauseStart = i + 1
|
|
553
|
+
let k = clauseStart
|
|
554
|
+
while (k < words.length) {
|
|
555
|
+
const term = words[k] as ShellWord
|
|
556
|
+
if (LITERAL(term) && (term.value === ';' || term.value === '+')) break
|
|
557
|
+
k += 1
|
|
558
|
+
}
|
|
559
|
+
const clause = words.slice(clauseStart, k)
|
|
560
|
+
if (clause.length === 0) {
|
|
561
|
+
positions.push({ unknown: `find's ${w.value} clause has no program` })
|
|
562
|
+
} else {
|
|
563
|
+
// find execs the entire argv after -exec, including any re-exec
|
|
564
|
+
// wrappers. Inspecting only its first word would mistake `env` for
|
|
565
|
+
// the program and miss a dynamic name further into the clause.
|
|
566
|
+
for (const position of resolveChain(clause, dialect)) {
|
|
567
|
+
if (position.word && LITERAL(position.word) && position.word.value.includes('{}')) {
|
|
568
|
+
positions.push({
|
|
569
|
+
unknown: `find's own \`{}\` placeholder stands for the program, not a name in the line`,
|
|
570
|
+
})
|
|
571
|
+
} else {
|
|
572
|
+
positions.push(position)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
// The outer lexer only sees `find`, so it cannot read a shell -c
|
|
576
|
+
// payload inside this clause. Read it here with the same wrapper
|
|
577
|
+
// and shell-option grammar the lexer uses for a top-level shell.
|
|
578
|
+
const nested = nestedShellCommand(clause)
|
|
579
|
+
if (nested !== null) {
|
|
580
|
+
if ('opaque' in nested) {
|
|
581
|
+
positions.push({ unknown: nested.opaque })
|
|
582
|
+
} else if (nested.shell === 'zsh' || nested.shell === 'ksh' || nested.shell === 'mksh') {
|
|
583
|
+
positions.push({ unknown: `nested ${nested.shell} is not modeled` })
|
|
584
|
+
} else if (LITERAL(nested.payload) && nested.payload.value.includes('{}')) {
|
|
585
|
+
positions.push({
|
|
586
|
+
unknown: 'find substitutes `{}` into this shell payload before it runs',
|
|
587
|
+
})
|
|
588
|
+
} else {
|
|
589
|
+
positions.push(
|
|
590
|
+
...resolveDashCPayload(nested.payload, nested.shell === 'bash' ? 'bash' : 'sh'),
|
|
591
|
+
)
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
i = k
|
|
596
|
+
}
|
|
597
|
+
return positions
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Every position in `command` where a program is actually exec'd: the
|
|
602
|
+
* (possibly re-exec-wrapped) head, and, for `find`, each `-exec`-family
|
|
603
|
+
* clause's own head. Empty for a command with no words at all. `dialect` is
|
|
604
|
+
* the shell that will run the line — needed only to read a literal `eval`
|
|
605
|
+
* payload the same way its own commands would be.
|
|
606
|
+
*/
|
|
607
|
+
export function programPositions(
|
|
608
|
+
command: ShellCommand,
|
|
609
|
+
dialect: ShellDialect,
|
|
610
|
+
): readonly ProgramPosition[] {
|
|
611
|
+
const head = command.words[command.assignments]
|
|
612
|
+
if (head === undefined) return []
|
|
613
|
+
const positions: ProgramPosition[] = [
|
|
614
|
+
...resolveChain(command.words.slice(command.assignments), dialect),
|
|
615
|
+
]
|
|
616
|
+
for (const position of positions.slice()) {
|
|
617
|
+
if (!position.word || !LITERAL(position.word) || basename(position.word.value) !== 'find')
|
|
618
|
+
continue
|
|
619
|
+
const index = command.words.indexOf(position.word)
|
|
620
|
+
if (index >= 0) positions.push(...findExecPositions(command.words.slice(index + 1), dialect))
|
|
621
|
+
}
|
|
622
|
+
return positions
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** Variables whose value governs how a LATER literal program name resolves to a binary or to code. */
|
|
626
|
+
export const DYNAMIC_RESOLUTION_VARIABLES: ReadonlySet<string> = new Set([
|
|
627
|
+
'PATH',
|
|
628
|
+
'LD_PRELOAD',
|
|
629
|
+
'LD_LIBRARY_PATH',
|
|
630
|
+
'BASH_ENV',
|
|
631
|
+
'ENV',
|
|
632
|
+
'IFS',
|
|
633
|
+
])
|
|
634
|
+
|
|
635
|
+
function assignsPoisoningVariable(words: readonly ShellWord[]): boolean {
|
|
636
|
+
for (const word of words) {
|
|
637
|
+
const name = assignmentName(word)
|
|
638
|
+
if (name !== null && DYNAMIC_RESOLUTION_VARIABLES.has(name)) return true
|
|
639
|
+
}
|
|
640
|
+
return false
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Whether `command`'s OWN leading assignment (`PATH=x prog`) sets one of
|
|
645
|
+
* {@link DYNAMIC_RESOLUTION_VARIABLES} — which affects only `prog`'s own
|
|
646
|
+
* position, the way a real command-prefix assignment is scoped to that one
|
|
647
|
+
* invocation and does not leak to later commands. A caller iterating a
|
|
648
|
+
* whole script applies this to THIS command's own {@link programPositions}.
|
|
649
|
+
*/
|
|
650
|
+
export function hasPoisoningPrefix(command: ShellCommand): boolean {
|
|
651
|
+
return assignsPoisoningVariable(command.words.slice(0, command.assignments))
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Whether `command` sets one of {@link DYNAMIC_RESOLUTION_VARIABLES} for
|
|
656
|
+
* every command AFTER it in the same script: an `export NAME=value` (or a
|
|
657
|
+
* bare re-export, `export NAME`, of an already-set one), or a standalone
|
|
658
|
+
* assignment with no trailing command (`PATH=x` alone), both of which are
|
|
659
|
+
* real, persisting shell-variable assignments rather than a one-call
|
|
660
|
+
* prefix. A caller iterating a whole script applies this to every command
|
|
661
|
+
* that follows, not to `command` itself: a literal `ls` after either one
|
|
662
|
+
* cannot be trusted to resolve to what its text says, since which binary
|
|
663
|
+
* it runs — or what code a shell reads on startup (`BASH_ENV`, `ENV`) —
|
|
664
|
+
* depends on these variables' runtime value.
|
|
665
|
+
*/
|
|
666
|
+
export function poisonsLaterCommands(command: ShellCommand): boolean {
|
|
667
|
+
const head = command.words[command.assignments]
|
|
668
|
+
if (head === undefined) return hasPoisoningPrefix(command)
|
|
669
|
+
if (!LITERAL(head)) return false
|
|
670
|
+
const args = command.words.slice(command.assignments + 1)
|
|
671
|
+
if (head.value === 'export') {
|
|
672
|
+
// `export -n NAME` REMOVES the export attribute — the opposite of
|
|
673
|
+
// poisoning — for every name in the same invocation; bash reads it
|
|
674
|
+
// once for the whole command, not per name.
|
|
675
|
+
if (args.some((w) => LITERAL(w) && w.value === '-n')) return false
|
|
676
|
+
return args.some((arg) => namesPoisoningVariable(arg))
|
|
677
|
+
}
|
|
678
|
+
if (head.value === 'declare' || head.value === 'typeset') {
|
|
679
|
+
// `declare -x`/`typeset -x` (and a cluster with it, `-gx`, `-xg`, …,
|
|
680
|
+
// but never `+x`, which removes the attribute) marks a name exported,
|
|
681
|
+
// the same as `export`. `declare` without `-x` only sets a local
|
|
682
|
+
// attribute (type, read-only, …) and does not export anything.
|
|
683
|
+
const exports = args.some((w) => LITERAL(w) && /^-[a-zA-Z]*x[a-zA-Z]*$/.test(w.value))
|
|
684
|
+
if (!exports) return false
|
|
685
|
+
return args.some((arg) => {
|
|
686
|
+
if (LITERAL(arg) && /^[-+]/.test(arg.value)) return false // an option, not a name
|
|
687
|
+
return namesPoisoningVariable(arg)
|
|
688
|
+
})
|
|
689
|
+
}
|
|
690
|
+
return false
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* `assignmentName`'s prefix, or the word's own literal value for a bare
|
|
695
|
+
* name (`export PATH`, `declare -x PATH`) — read regardless of whether the
|
|
696
|
+
* word's VALUE expands, since only the `NAME` portion (always literal in an
|
|
697
|
+
* assignment word) or the bare word's exact text decides which variable is
|
|
698
|
+
* named.
|
|
699
|
+
*/
|
|
700
|
+
function namesPoisoningVariable(arg: ShellWord): boolean {
|
|
701
|
+
const name = assignmentName(arg) ?? (LITERAL(arg) ? arg.value : null)
|
|
702
|
+
return name !== null && DYNAMIC_RESOLUTION_VARIABLES.has(name)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* The `unknown` position a command's own program name becomes once
|
|
707
|
+
* {@link hasPoisoningPrefix} or an earlier {@link poisonsLaterCommands} is
|
|
708
|
+
* true for it, in place of whatever {@link programPositions} would
|
|
709
|
+
* otherwise resolve — a literal name is no longer trustworthy once the
|
|
710
|
+
* environment it resolves against is unknown.
|
|
711
|
+
*/
|
|
712
|
+
export function poisonedProgramPosition(): ProgramPosition {
|
|
713
|
+
return {
|
|
714
|
+
unknown:
|
|
715
|
+
'PATH, LD_PRELOAD, LD_LIBRARY_PATH, BASH_ENV, ENV or IFS was set earlier in this line, so a literal program name here cannot be trusted to resolve to what its text says',
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/** One command's {@link programPositions}, paired with the command itself. */
|
|
720
|
+
export interface CommandProgramPositions {
|
|
721
|
+
readonly command: ShellCommand
|
|
722
|
+
readonly positions: readonly ProgramPosition[]
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* {@link programPositions} for every command a script reads, in order,
|
|
727
|
+
* threading {@link hasPoisoningPrefix} and {@link poisonsLaterCommands}
|
|
728
|
+
* through so a literal program name after an earlier `PATH=`, `export
|
|
729
|
+
* PATH=`, … comes back {@link poisonedProgramPosition} rather than trusted
|
|
730
|
+
* at face value. This is the one loop the live `bash` tool's escalation, the
|
|
731
|
+
* scheduled-run floor and `verifyScheduledScript` would otherwise each write
|
|
732
|
+
* for themselves — exactly the duplication that let `env $(echo git) push`
|
|
733
|
+
* pass one consumer's check and not another's.
|
|
734
|
+
*/
|
|
735
|
+
export function resolveScriptPrograms(
|
|
736
|
+
commands: readonly ShellCommand[],
|
|
737
|
+
dialect: ShellDialect,
|
|
738
|
+
): readonly CommandProgramPositions[] {
|
|
739
|
+
let poisoned = false
|
|
740
|
+
const out: CommandProgramPositions[] = []
|
|
741
|
+
for (const command of commands) {
|
|
742
|
+
const selfPoisoned = poisoned || hasPoisoningPrefix(command)
|
|
743
|
+
const positions = selfPoisoned
|
|
744
|
+
? [poisonedProgramPosition()]
|
|
745
|
+
: programPositions(command, dialect)
|
|
746
|
+
out.push({ command, positions })
|
|
747
|
+
if (poisonsLaterCommands(command)) poisoned = true
|
|
748
|
+
}
|
|
749
|
+
return out
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Why a command line's own program name is not knowable ahead of running
|
|
754
|
+
* it, in one call: the line itself, unread whole (`bash -c "$X"`'s outer
|
|
755
|
+
* line is opaque — the lexer could not follow the payload at all, which is
|
|
756
|
+
* a stronger statement than any one position being unresolvable), or a
|
|
757
|
+
* specific command's unresolvable position via {@link resolveScriptPrograms}.
|
|
758
|
+
* The live `bash` tool's escalation (`executor.ts`) calls this directly
|
|
759
|
+
* rather than re-implementing it, so its own tests import the same function
|
|
760
|
+
* they are testing instead of a hand-written mirror that can drift from it.
|
|
761
|
+
*/
|
|
762
|
+
export function unknownProgramInLine(value: string, dialect: ShellDialect): string | undefined {
|
|
763
|
+
const reading = lexShellCommandLine(value, { dialect })
|
|
764
|
+
if (reading.opaque || !reading.complete) {
|
|
765
|
+
return `${value}: the line cannot be read (${reading.reasons[0] ?? 'a construct the lexer does not model'}), so its program cannot be verified`
|
|
766
|
+
}
|
|
767
|
+
for (const { command, positions } of resolveScriptPrograms(reading.commands, dialect)) {
|
|
768
|
+
for (const position of positions) {
|
|
769
|
+
if (position.unknown !== undefined) return `${command.text}: ${position.unknown}`
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return undefined
|
|
773
|
+
}
|