Skip to content

safe-bash: make agentCommands portable by default with optional regexExecutor injection #669

Description

@kamilio

The complete /portable preset delivered by #649 makes the Worker integration possible. Please simplify the public API so that portability is the default and hosts inject a regex executor only when they need to override it. This is a follow-up API design request, not a claim that #649 is still unfixed.

The proposed public API is below; these names are a design request, not documentation of the current API:

agentCommands(); // Safe, bounded, environment-neutral default.
agentCommands({ regexExecutor: createBoundedRegexExecutor() }); // Optional override.
agentCommands({ regexExecutor: customExecutor }); // Host-supplied implementation.
agentCommands({ regexExecutor: createWorkerThreadRegexExecutor() }); // Explicit Node opt-in.

Shell, MemoryFileSystem, and agentCommands should be available from the ordinary package entry. The proposed createWorkerThreadRegexExecutor factory must come from a host-specific entry so importing the default API never loads Node worker threads. regexExecutor describes the dependency's job more clearly than the generic provider.

The current provider's createWorker factory and worker-shaped messaging protocol are a lower-level abstraction. A simpler public executor contract may require an adapter over that existing implementation; this is not just a rename of createWorker, and current provider and proposed executor types should not be treated as interchangeable. Keep the worker protocol internal where possible, with a documented bounded-execution and cancellation contract for custom executors.

agentCommands() should supply a built-in bounded, environment-neutral executor. Callers should not need to select a separate portable command preset and wire up an executor just to use the same command inventory in a Worker. A Node worker-thread executor can remain an explicit opt-in executor from a host-specific entry.

The dependency graph matters as much as the function signature: importing the default public entry must not eagerly load unavailable Node filesystem or worker-thread integrations. Merely choosing a portable executor after importing a Node-dependent root would leave the original startup problem intact. Keep Node filesystem adapters, Node execution, and other host runtimes behind explicit host-specific imports/options.

Please retain these constraints when designing the default:

  • Preserve execution budgets, cancellation, bounded regex semantics, and explicit unsupported-mode errors. A synchronous native JavaScript RegExp fallback with only an outer timeout is not a bounded executor and must not become the portable default.
  • Apply the same executor policy to every relevant command, including expr, egrep, and fgrep, rather than maintaining divergent command lists for different environments.
  • Define ownership and disposal for both the default executor and an injected executor; avoid leaking resources or unexpectedly disposing an executor shared by its caller.
  • Preserve existing Node behavior through an explicit opt-in implementation. Keep existing /portable consumers working through an alias or a documented migration. Any incompatible root-export or semantic change needs an explicit compatibility/versioning plan, not a silent refactor.
  • Keep networking and host execution capabilities explicit opt-ins; portable-by-default must not enable previously unavailable host capabilities.

Acceptance: the ordinary agentCommands() import and representative workflows from the complete command inventory run in actual workerd and browser environments as well as Node/Bun; an injected executor is honored consistently; the explicit Node executor retains its supported behavior; limits, cancellation, unsupported modes, and cleanup have regression coverage. If a fully bounded built-in executor needs a restricted regex dialect or another dependency, document that tradeoff rather than weakening the guarantees.

This request is separate from #662's Bun/Miniflare test-host lifecycle issue. Our application can use the released /portable API in the meantime; no breaking upstream implementation is requested without the compatibility plan above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions