feat(cli): panda workflow — workflow-engine passthrough + docs#290
Open
Savid wants to merge 3 commits into
Open
feat(cli): panda workflow — workflow-engine passthrough + docs#290Savid wants to merge 3 commits into
panda workflow — workflow-engine passthrough + docs#290Savid wants to merge 3 commits into
Conversation
Contributor
🐼 Smoke eval —
|
| question | result | tokens | tools |
|---|---|---|---|
forky_node_coverage |
✅ | 15,779 | 7 |
tracoor_node_coverage |
✅ | 16,681 | 6 |
mainnet_block_arrival_p50 |
✅ | 16,776 | 8 |
list_datasources |
✅ | 13,136 | 2 |
block_count_24h |
✅ | 21,566 | 9 |
missed_slots_24h |
✅ | 25,598 | 22 |
chartkit_default_arrival_distribution |
✅ | 39,274 | 18 |
storage_upload_session_scoped |
✅ | 21,781 | 15 |
🔭 Langfuse traces (8 runs; ⚠️ = failed)
The report walks this branch's commits against the master baseline and the most recent release. A self-contained copy is in the run's eval-smoke-* artifact.
Add a `panda workflow` CLI command group: a curated CRUD + SSE-streaming client over the external workflow engine's REST API, routed through the local panda server and the credential proxy. It lets an agent turn a plain-language request into a completed workflow run (whiteboard → session → draft → publish → run → follow → outputs → steer). The CLI never sees a workflow-engine token. Requests route `server /api/v1/workflow/*` → `proxy /workflow/*`; the proxy holds the bearer (or forwards the user's own in passthrough mode). The server and CLI never handle it — the same trust shape as other proxy-credentialed upstreams. Design compliance (per AGENTS.md guardrails): - Not a module — the module registry is unchanged. - No new MCP tool — the 3-tool surface (execute_python, manage_session, search) is unchanged. - Docs resources are dialect-rendered via surface.Dialect (CLI gets the full guide; MCP gets a CLI-only stub) with no caller-identity branching. Includes: - Proxy: `/workflow/*` handler with fail-closed token injection, path clamp/clean with traversal rejection, strict header allow-list, SSE-safe flushing, and request metrics. - Server: thin reverse-proxy passthrough at `/api/v1/workflow/*`. - pkg/workflowrelay: shared relay contract (header allow-list, traversal rejection, problem+json) used by both hops. - Config: workflow-engine base URL / auth settings with validation, and proxy auth.scopes for passthrough mode; example blocks ship commented. - CLI: noun-verb commands for whiteboard, session, draft, workflow/run, steer, artifact, dispatch/agent/worker, `docs`, and a raw `api` escape hatch, with percent-encoded path segments, idempotency keys, `--out` for artifact bytes, NDJSON under `--json`, a bounded SSE frame parser, and a run-stream status→exit-code contract. - Docs: `workflow://guide` + `workflow://api` server resources (`panda workflow docs [api]`) and an AGENTS.md entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…route, 401 hint - server relay: preserve a base path on the proxy URL (a proxy mounted under a subpath) instead of overwriting it, matching the string-concat join every other proxy call uses - run follow: the reconnect budget bounds CONSECUTIVE fruitless attempts; a stream that delivered an event resets it, so long runs behind an idle-timeouting LB are not capped at 10 drops over their lifetime - server relay: route bare /api/v1/workflow to the engine API root so 'panda workflow api GET /' is not a misleading 404 - CLI: key the 401 proxy-vs-engine hint on the proxy's exact writeBearerError phrases instead of the substring "proxy" (which the proxy's real 401 bodies never contain) - proxy: drop the dead handlers.WorkflowConfig.AllowedOrgs field (enforcement lives in the authorizer) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, api cmd file split - deflake TestWorkflowHandlerUpstreamDownReturns502: inject a failing transport instead of relying on a closed httptest port staying unbound (a concurrent test's server can re-bind it, turning the dial error into a live 404) - auth-retry header reset restores the pre-relay baseline instead of clearing to empty, so middleware-set response headers survive while a trapped 401's stale upstream headers are still dropped; test added - move workflowAPICmd + helpers out of workflow_docs.go into workflow_api.go, whiteboard-id completer into workflow_whiteboard.go (pure relocation) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
panda workflowCLI command group: a thin, curated CRUD + SSE-streaming client over an external workflow-engine REST API, routed through the local panda server and the credential proxy. It lets an agent turn a plain-language request into a completed workflow run (whiteboard → session → draft → publish → run → follow → outputs → steer).The CLI never sees a workflow-engine token. Requests route
server /api/v1/workflow/*→proxy /workflow/*; the proxy holds the bearer (or forwards the user's own in passthrough mode). The server and CLI never handle it — the same trust shape as other proxy-credentialed upstreams.Design compliance (per
AGENTS.mdguardrails)pkg/app/app.go) is unchanged.execute_python,manage_session,search) is unchanged.surface.Dialect(CLI gets the full guide; MCP gets a CLI-only stub with no CLI-flag syntax).What's included
pkg/proxy/handlers/workflow.go,server_config.go,router.go): a/workflow/*handler with fail-closed token injection, path clamp/clean with../%2e%2e/..;/backslash rejection re-verified afterpath.Clean, a strict header allow-list, SSE-safe flushing (FlushInterval:-1+DisableCompression), a502ErrorHandler, and a request metric emitted on completion.pkg/server/workflow.go,api.go,builder.go): a thinhttputil.ReverseProxypassthrough at/api/v1/workflow/*— disabled→503 short-circuit, explicit upstream path rewrite, inboundAuthorization/Cookie/Host/attribution stripped.pkg/config/config.go,config.example.yaml,proxy-config.example.yaml): workflow-engine base URL / auth settings; enabled iff the base URL is set; validation rejects userinfo/path/query/fragment/out-of-range-port/unknown auth mode. Example blocks ship commented out (base URL is the enable marker) with a restart note.pkg/cli/workflow*.go): noun-verb commands for whiteboard, session, draft, workflow/run, steer, artifact, dispatch/agent/worker,docs, and a rawapiescape hatch. Percent-encoded path segments, UUIDv4Idempotency-Key(with--idempotency-keyoverride),--outfor artifact bytes, NDJSON under--json, a bounded SSE frame parser, a run-stream status→exit-code contract, and RFC 7807problem+json+text/plainerror hints.pkg/resource/workflow.go,workflowdocs/):workflow://guide+workflow://apiserver resources (panda workflow docs [api]), a CLI-only getting-started section, and anAGENTS.mdentry.Verification
go build ./...passes.These are doc/sign-off obligations, not defects — the code is consistent with them:
apihatch reaches the full upstream surface with the injected token — by design; use a least-privilege (non-root) principal./api/v1surface.🤖 Generated with Claude Code