@mesh-tech/mesh-cli 0.1.1 → 0.12.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/assets/intent/agents-fence.md +10 -0
- package/assets/intent/intent-claude-gate.mjs +255 -0
- package/assets/log-shipper.mjs +94 -0
- package/bin/mesh.mjs +80 -0
- package/dist/bin/mesh.d.ts +7 -9
- package/dist/bin/mesh.d.ts.map +1 -1
- package/dist/bin/mesh.js +20826 -37
- package/dist/bin/mesh.js.map +7 -1
- package/dist/build-info.json +4 -0
- package/dist/src/commands/agent-api-client.d.ts +79 -0
- package/dist/src/commands/agent-api-client.d.ts.map +1 -0
- package/dist/src/commands/agent-api-client.js +170 -0
- package/dist/src/commands/agent-api-client.js.map +1 -0
- package/dist/src/commands/artifacts.d.ts +13 -0
- package/dist/src/commands/artifacts.d.ts.map +1 -0
- package/dist/src/commands/artifacts.js +140 -0
- package/dist/src/commands/artifacts.js.map +1 -0
- package/dist/src/commands/cluster.d.ts +45 -0
- package/dist/src/commands/cluster.d.ts.map +1 -0
- package/dist/src/commands/cluster.js +259 -0
- package/dist/src/commands/cluster.js.map +1 -0
- package/dist/src/commands/conversations.d.ts +61 -0
- package/dist/src/commands/conversations.d.ts.map +1 -0
- package/dist/src/commands/conversations.js +239 -0
- package/dist/src/commands/conversations.js.map +1 -0
- package/dist/src/commands/create-app.d.ts +26 -3
- package/dist/src/commands/create-app.d.ts.map +1 -1
- package/dist/src/commands/create-app.js +465 -107
- package/dist/src/commands/create-app.js.map +1 -1
- package/dist/src/commands/deploy.d.ts +19 -0
- package/dist/src/commands/deploy.d.ts.map +1 -1
- package/dist/src/commands/deploy.js +83 -142
- package/dist/src/commands/deploy.js.map +1 -1
- package/dist/src/commands/dev-doctor.d.ts +137 -0
- package/dist/src/commands/dev-doctor.d.ts.map +1 -0
- package/dist/src/commands/dev-doctor.js +671 -0
- package/dist/src/commands/dev-doctor.js.map +1 -0
- package/dist/src/commands/dev-launch.d.ts +59 -0
- package/dist/src/commands/dev-launch.d.ts.map +1 -0
- package/dist/src/commands/dev-launch.js +118 -0
- package/dist/src/commands/dev-launch.js.map +1 -0
- package/dist/src/commands/dev-token-server.d.ts +32 -0
- package/dist/src/commands/dev-token-server.d.ts.map +1 -0
- package/dist/src/commands/dev-token-server.js +68 -0
- package/dist/src/commands/dev-token-server.js.map +1 -0
- package/dist/src/commands/dev.d.ts +289 -2
- package/dist/src/commands/dev.d.ts.map +1 -1
- package/dist/src/commands/dev.js +2501 -574
- package/dist/src/commands/dev.js.map +1 -1
- package/dist/src/commands/docs.d.ts +3 -0
- package/dist/src/commands/docs.d.ts.map +1 -0
- package/dist/src/commands/docs.js +262 -0
- package/dist/src/commands/docs.js.map +1 -0
- package/dist/src/commands/hub/index.d.ts +99 -0
- package/dist/src/commands/hub/index.d.ts.map +1 -0
- package/dist/src/commands/hub/index.js +452 -0
- package/dist/src/commands/hub/index.js.map +1 -0
- package/dist/src/commands/init.d.ts +65 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +370 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/install-shim.d.ts +21 -0
- package/dist/src/commands/install-shim.d.ts.map +1 -0
- package/dist/src/commands/install-shim.js +132 -0
- package/dist/src/commands/install-shim.js.map +1 -0
- package/dist/src/commands/local/auth-provision.d.ts +71 -0
- package/dist/src/commands/local/auth-provision.d.ts.map +1 -0
- package/dist/src/commands/local/auth-provision.js +286 -0
- package/dist/src/commands/local/auth-provision.js.map +1 -0
- package/dist/src/commands/local/dev-local.d.ts +97 -0
- package/dist/src/commands/local/dev-local.d.ts.map +1 -0
- package/dist/src/commands/local/dev-local.js +306 -0
- package/dist/src/commands/local/dev-local.js.map +1 -0
- package/dist/src/commands/local/docker-runner.d.ts +37 -0
- package/dist/src/commands/local/docker-runner.d.ts.map +1 -0
- package/dist/src/commands/local/docker-runner.js +104 -0
- package/dist/src/commands/local/docker-runner.js.map +1 -0
- package/dist/src/commands/local/helpers.d.ts +22 -0
- package/dist/src/commands/local/helpers.d.ts.map +1 -0
- package/dist/src/commands/local/helpers.js +46 -0
- package/dist/src/commands/local/helpers.js.map +1 -0
- package/dist/src/commands/local/hub-local.d.ts +51 -0
- package/dist/src/commands/local/hub-local.d.ts.map +1 -0
- package/dist/src/commands/local/hub-local.js +197 -0
- package/dist/src/commands/local/hub-local.js.map +1 -0
- package/dist/src/commands/local/index.d.ts +23 -0
- package/dist/src/commands/local/index.d.ts.map +1 -0
- package/dist/src/commands/local/index.js +386 -0
- package/dist/src/commands/local/index.js.map +1 -0
- package/dist/src/commands/local/mocks.d.ts +361 -0
- package/dist/src/commands/local/mocks.d.ts.map +1 -0
- package/dist/src/commands/local/mocks.js +756 -0
- package/dist/src/commands/local/mocks.js.map +1 -0
- package/dist/src/commands/local/seed-zitadel.d.ts +151 -0
- package/dist/src/commands/local/seed-zitadel.d.ts.map +1 -0
- package/dist/src/commands/local/seed-zitadel.js +408 -0
- package/dist/src/commands/local/seed-zitadel.js.map +1 -0
- package/dist/src/commands/local/seed.d.ts +106 -0
- package/dist/src/commands/local/seed.d.ts.map +1 -0
- package/dist/src/commands/local/seed.js +252 -0
- package/dist/src/commands/local/seed.js.map +1 -0
- package/dist/src/commands/local/stack.d.ts +173 -0
- package/dist/src/commands/local/stack.d.ts.map +1 -0
- package/dist/src/commands/local/stack.js +499 -0
- package/dist/src/commands/local/stack.js.map +1 -0
- package/dist/src/commands/login.d.ts +256 -12
- package/dist/src/commands/login.d.ts.map +1 -1
- package/dist/src/commands/login.js +978 -50
- package/dist/src/commands/login.js.map +1 -1
- package/dist/src/commands/peer-addressing.d.ts +51 -0
- package/dist/src/commands/peer-addressing.d.ts.map +1 -0
- package/dist/src/commands/peer-addressing.js +64 -0
- package/dist/src/commands/peer-addressing.js.map +1 -0
- package/dist/src/commands/port-ownership.d.ts +69 -0
- package/dist/src/commands/port-ownership.d.ts.map +1 -0
- package/dist/src/commands/port-ownership.js +161 -0
- package/dist/src/commands/port-ownership.js.map +1 -0
- package/dist/src/commands/registry-publish.d.ts +3 -0
- package/dist/src/commands/registry-publish.d.ts.map +1 -0
- package/dist/src/commands/registry-publish.js +161 -0
- package/dist/src/commands/registry-publish.js.map +1 -0
- package/dist/src/commands/registry.d.ts +146 -2
- package/dist/src/commands/registry.d.ts.map +1 -1
- package/dist/src/commands/registry.js +541 -85
- package/dist/src/commands/registry.js.map +1 -1
- package/dist/src/commands/secrets/index.d.ts.map +1 -1
- package/dist/src/commands/secrets/index.js +15 -1
- package/dist/src/commands/secrets/index.js.map +1 -1
- package/dist/src/commands/secrets/migrate-config.d.ts +24 -0
- package/dist/src/commands/secrets/migrate-config.d.ts.map +1 -0
- package/dist/src/commands/secrets/migrate-config.js +277 -0
- package/dist/src/commands/secrets/migrate-config.js.map +1 -0
- package/dist/src/commands/secrets/set.d.ts +1 -0
- package/dist/src/commands/secrets/set.d.ts.map +1 -1
- package/dist/src/commands/secrets/set.js +96 -2
- package/dist/src/commands/secrets/set.js.map +1 -1
- package/dist/src/commands/skills.d.ts +34 -0
- package/dist/src/commands/skills.d.ts.map +1 -0
- package/dist/src/commands/skills.js +207 -0
- package/dist/src/commands/skills.js.map +1 -0
- package/dist/src/commands/stack.d.ts.map +1 -1
- package/dist/src/commands/stack.js +361 -23
- package/dist/src/commands/stack.js.map +1 -1
- package/dist/src/commands/temporal.d.ts +15 -0
- package/dist/src/commands/temporal.d.ts.map +1 -0
- package/dist/src/commands/temporal.js +781 -0
- package/dist/src/commands/temporal.js.map +1 -0
- package/dist/src/commands/tenant.d.ts +56 -0
- package/dist/src/commands/tenant.d.ts.map +1 -0
- package/dist/src/commands/tenant.js +233 -0
- package/dist/src/commands/tenant.js.map +1 -0
- package/dist/src/commands/tunnel/index.d.ts +49 -3
- package/dist/src/commands/tunnel/index.d.ts.map +1 -1
- package/dist/src/commands/tunnel/index.js +331 -8
- package/dist/src/commands/tunnel/index.js.map +1 -1
- package/dist/src/commands/vcs/clone.d.ts +3 -0
- package/dist/src/commands/vcs/clone.d.ts.map +1 -0
- package/dist/src/commands/vcs/clone.js +20 -0
- package/dist/src/commands/vcs/clone.js.map +1 -0
- package/dist/src/commands/vcs/common.d.ts +21 -0
- package/dist/src/commands/vcs/common.d.ts.map +1 -0
- package/dist/src/commands/vcs/common.js +62 -0
- package/dist/src/commands/vcs/common.js.map +1 -0
- package/dist/src/commands/vcs/drafts.d.ts +13 -0
- package/dist/src/commands/vcs/drafts.d.ts.map +1 -0
- package/dist/src/commands/vcs/drafts.js +45 -0
- package/dist/src/commands/vcs/drafts.js.map +1 -0
- package/dist/src/commands/vcs/get.d.ts +8 -0
- package/dist/src/commands/vcs/get.d.ts.map +1 -0
- package/dist/src/commands/vcs/get.js +61 -0
- package/dist/src/commands/vcs/get.js.map +1 -0
- package/dist/src/commands/vcs/index.d.ts +3 -0
- package/dist/src/commands/vcs/index.d.ts.map +1 -0
- package/dist/src/commands/vcs/index.js +58 -0
- package/dist/src/commands/vcs/index.js.map +1 -0
- package/dist/src/commands/vcs/propose.d.ts +23 -0
- package/dist/src/commands/vcs/propose.d.ts.map +1 -0
- package/dist/src/commands/vcs/propose.js +94 -0
- package/dist/src/commands/vcs/propose.js.map +1 -0
- package/dist/src/commands/vcs/review.d.ts +15 -0
- package/dist/src/commands/vcs/review.d.ts.map +1 -0
- package/dist/src/commands/vcs/review.js +35 -0
- package/dist/src/commands/vcs/review.js.map +1 -0
- package/dist/src/commands/vcs-enrich.d.ts +15 -0
- package/dist/src/commands/vcs-enrich.d.ts.map +1 -0
- package/dist/src/commands/vcs-enrich.js +28 -0
- package/dist/src/commands/vcs-enrich.js.map +1 -0
- package/dist/src/commands/vpn/index.d.ts +24 -0
- package/dist/src/commands/vpn/index.d.ts.map +1 -0
- package/dist/src/commands/vpn/index.js +543 -0
- package/dist/src/commands/vpn/index.js.map +1 -0
- package/dist/src/commands/vpn/tunnel.d.ts +11 -0
- package/dist/src/commands/vpn/tunnel.d.ts.map +1 -0
- package/dist/src/commands/vpn/tunnel.js +188 -0
- package/dist/src/commands/vpn/tunnel.js.map +1 -0
- package/dist/src/commands/workflow.d.ts +24 -0
- package/dist/src/commands/workflow.d.ts.map +1 -0
- package/dist/src/commands/workflow.js +577 -0
- package/dist/src/commands/workflow.js.map +1 -0
- package/dist/src/docs/assemble.d.ts +217 -0
- package/dist/src/docs/assemble.d.ts.map +1 -0
- package/dist/src/docs/assemble.js +739 -0
- package/dist/src/docs/assemble.js.map +1 -0
- package/dist/src/docs/cli-reference.d.ts +93 -0
- package/dist/src/docs/cli-reference.d.ts.map +1 -0
- package/dist/src/docs/cli-reference.js +256 -0
- package/dist/src/docs/cli-reference.js.map +1 -0
- package/dist/src/docs/discover.d.ts +60 -0
- package/dist/src/docs/discover.d.ts.map +1 -0
- package/dist/src/docs/discover.js +233 -0
- package/dist/src/docs/discover.js.map +1 -0
- package/dist/src/docs/portal.d.ts +79 -0
- package/dist/src/docs/portal.d.ts.map +1 -0
- package/dist/src/docs/portal.js +242 -0
- package/dist/src/docs/portal.js.map +1 -0
- package/dist/src/docs/registry-docs.d.ts +43 -0
- package/dist/src/docs/registry-docs.d.ts.map +1 -0
- package/dist/src/docs/registry-docs.js +138 -0
- package/dist/src/docs/registry-docs.js.map +1 -0
- package/dist/src/docs/schema.d.ts +48 -0
- package/dist/src/docs/schema.d.ts.map +1 -0
- package/dist/src/docs/schema.js +96 -0
- package/dist/src/docs/schema.js.map +1 -0
- package/dist/src/docs/serve.d.ts +27 -0
- package/dist/src/docs/serve.d.ts.map +1 -0
- package/dist/src/docs/serve.js +102 -0
- package/dist/src/docs/serve.js.map +1 -0
- package/dist/src/docs/start.d.ts +80 -0
- package/dist/src/docs/start.d.ts.map +1 -0
- package/dist/src/docs/start.js +313 -0
- package/dist/src/docs/start.js.map +1 -0
- package/dist/src/eval/agent-eval.d.mts +14 -0
- package/dist/src/eval/agent-eval.d.mts.map +1 -0
- package/dist/src/eval/agent-eval.mjs +273 -0
- package/dist/src/eval/agent-eval.mjs.map +1 -0
- package/dist/src/index.js +624 -7
- package/dist/src/index.js.map +7 -1
- package/dist/src/program.d.ts +26 -0
- package/dist/src/program.d.ts.map +1 -0
- package/dist/src/program.js +76 -0
- package/dist/src/program.js.map +1 -0
- package/dist/src/utils/auth-preflight.d.ts +159 -0
- package/dist/src/utils/auth-preflight.d.ts.map +1 -0
- package/dist/src/utils/auth-preflight.js +298 -0
- package/dist/src/utils/auth-preflight.js.map +1 -0
- package/dist/src/utils/aws-auth.d.ts +181 -0
- package/dist/src/utils/aws-auth.d.ts.map +1 -0
- package/dist/src/utils/aws-auth.js +527 -0
- package/dist/src/utils/aws-auth.js.map +1 -0
- package/dist/src/utils/bastion.d.ts +1 -1
- package/dist/src/utils/bastion.d.ts.map +1 -1
- package/dist/src/utils/bastion.js +29 -8
- package/dist/src/utils/bastion.js.map +1 -1
- package/dist/src/utils/build-info.d.ts +33 -0
- package/dist/src/utils/build-info.d.ts.map +1 -0
- package/dist/src/utils/build-info.js +102 -0
- package/dist/src/utils/build-info.js.map +1 -0
- package/dist/src/utils/capture-history.d.ts +62 -0
- package/dist/src/utils/capture-history.d.ts.map +1 -0
- package/dist/src/utils/capture-history.js +94 -0
- package/dist/src/utils/capture-history.js.map +1 -0
- package/dist/src/utils/deploy-preflight.d.ts +52 -0
- package/dist/src/utils/deploy-preflight.d.ts.map +1 -0
- package/dist/src/utils/deploy-preflight.js +183 -0
- package/dist/src/utils/deploy-preflight.js.map +1 -0
- package/dist/src/utils/errors.d.ts +75 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +106 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/first-party-contexts.d.ts +40 -0
- package/dist/src/utils/first-party-contexts.d.ts.map +1 -0
- package/dist/src/utils/first-party-contexts.js +53 -0
- package/dist/src/utils/first-party-contexts.js.map +1 -0
- package/dist/src/utils/index.d.ts +2 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/kubeconfig.d.ts +62 -0
- package/dist/src/utils/kubeconfig.d.ts.map +1 -0
- package/dist/src/utils/kubeconfig.js +118 -0
- package/dist/src/utils/kubeconfig.js.map +1 -0
- package/dist/src/utils/log.d.ts +27 -1
- package/dist/src/utils/log.d.ts.map +1 -1
- package/dist/src/utils/log.js +59 -5
- package/dist/src/utils/log.js.map +1 -1
- package/dist/src/utils/pid.d.ts +47 -0
- package/dist/src/utils/pid.d.ts.map +1 -0
- package/dist/src/utils/pid.js +83 -0
- package/dist/src/utils/pid.js.map +1 -0
- package/dist/src/utils/pulumi-run.d.ts +23 -0
- package/dist/src/utils/pulumi-run.d.ts.map +1 -0
- package/dist/src/utils/pulumi-run.js +112 -0
- package/dist/src/utils/pulumi-run.js.map +1 -0
- package/dist/src/utils/pulumi.d.ts +15 -0
- package/dist/src/utils/pulumi.d.ts.map +1 -1
- package/dist/src/utils/pulumi.js +35 -0
- package/dist/src/utils/pulumi.js.map +1 -1
- package/dist/src/utils/reachability.d.ts +29 -0
- package/dist/src/utils/reachability.d.ts.map +1 -0
- package/dist/src/utils/reachability.js +81 -0
- package/dist/src/utils/reachability.js.map +1 -0
- package/dist/src/utils/recover-conversation.d.ts +154 -0
- package/dist/src/utils/recover-conversation.d.ts.map +1 -0
- package/dist/src/utils/recover-conversation.js +314 -0
- package/dist/src/utils/recover-conversation.js.map +1 -0
- package/dist/src/utils/registry-broker.d.ts +148 -0
- package/dist/src/utils/registry-broker.d.ts.map +1 -0
- package/dist/src/utils/registry-broker.js +180 -0
- package/dist/src/utils/registry-broker.js.map +1 -0
- package/dist/src/utils/socks-forward.d.ts +40 -0
- package/dist/src/utils/socks-forward.d.ts.map +1 -0
- package/dist/src/utils/socks-forward.js +97 -0
- package/dist/src/utils/socks-forward.js.map +1 -0
- package/dist/src/utils/stack-flag.d.ts +23 -0
- package/dist/src/utils/stack-flag.d.ts.map +1 -0
- package/dist/src/utils/stack-flag.js +41 -0
- package/dist/src/utils/stack-flag.js.map +1 -0
- package/dist/src/utils/tailscale-targets.d.ts +52 -0
- package/dist/src/utils/tailscale-targets.d.ts.map +1 -0
- package/dist/src/utils/tailscale-targets.js +93 -0
- package/dist/src/utils/tailscale-targets.js.map +1 -0
- package/dist/src/utils/tailscale.d.ts +277 -0
- package/dist/src/utils/tailscale.d.ts.map +1 -0
- package/dist/src/utils/tailscale.js +1007 -0
- package/dist/src/utils/tailscale.js.map +1 -0
- package/dist/src/utils/temporal-auth.d.ts +43 -0
- package/dist/src/utils/temporal-auth.d.ts.map +1 -0
- package/dist/src/utils/temporal-auth.js +176 -0
- package/dist/src/utils/temporal-auth.js.map +1 -0
- package/dist/src/utils/temporal-codec.d.ts +101 -0
- package/dist/src/utils/temporal-codec.d.ts.map +1 -0
- package/dist/src/utils/temporal-codec.js +319 -0
- package/dist/src/utils/temporal-codec.js.map +1 -0
- package/dist/src/utils/tunnel-ownership.d.ts +130 -0
- package/dist/src/utils/tunnel-ownership.d.ts.map +1 -0
- package/dist/src/utils/tunnel-ownership.js +274 -0
- package/dist/src/utils/tunnel-ownership.js.map +1 -0
- package/dist/src/utils/vpn-join.d.ts +32 -0
- package/dist/src/utils/vpn-join.d.ts.map +1 -0
- package/dist/src/utils/vpn-join.js +62 -0
- package/dist/src/utils/vpn-join.js.map +1 -0
- package/dist/src/utils/vpn.d.ts +62 -0
- package/dist/src/utils/vpn.d.ts.map +1 -0
- package/dist/src/utils/vpn.js +183 -0
- package/dist/src/utils/vpn.js.map +1 -0
- package/dist/src/utils/workflow-fingerprint.d.ts +49 -0
- package/dist/src/utils/workflow-fingerprint.d.ts.map +1 -0
- package/dist/src/utils/workflow-fingerprint.js +135 -0
- package/dist/src/utils/workflow-fingerprint.js.map +1 -0
- package/dist/src/utils/worktree-identity.d.ts +86 -0
- package/dist/src/utils/worktree-identity.d.ts.map +1 -0
- package/dist/src/utils/worktree-identity.js +164 -0
- package/dist/src/utils/worktree-identity.js.map +1 -0
- package/fragments/agents/AGENTS.md.hbs +92 -0
- package/fragments/agents/CLAUDE.md.hbs +8 -0
- package/fragments/base/Pulumi.dev.yaml.hbs +10 -0
- package/fragments/base/Pulumi.yaml.hbs +9 -0
- package/fragments/base/index.ts.hbs +107 -0
- package/fragments/base/package.json.hbs +22 -0
- package/{templates/api-web-db/api → fragments/base}/tsconfig.json +3 -4
- package/fragments/bucket/api/src/storage.ts.hbs +81 -0
- package/fragments/database/api/src/db.ts.hbs +23 -0
- package/fragments/database/prisma/schema.prisma.hbs +18 -0
- package/{templates/api-web-db → fragments/service}/api/.dockerignore +2 -0
- package/fragments/service/api/package.json.hbs +40 -0
- package/fragments/service/api/src/index.ts.hbs +58 -0
- package/{templates/api-web-db → fragments/service/api}/tsconfig.json +5 -3
- package/fragments/temporal/worker/.dockerignore +4 -0
- package/fragments/temporal/worker/Dockerfile +15 -0
- package/{templates/temporal-api-worker → fragments/temporal}/worker/package.json.hbs +3 -2
- package/fragments/temporal/worker/src/activities.ts.hbs +14 -0
- package/fragments/temporal/worker/src/worker.ts.hbs +27 -0
- package/fragments/temporal/worker/src/workflows.ts.hbs +24 -0
- package/fragments/temporal/worker/tsconfig.json +13 -0
- package/package.json +36 -22
- package/skills/core/SKILL.md +365 -0
- package/skills/pull-remote-agent-context/SKILL.md +194 -0
- package/stack/docker-compose.hub.yml +134 -0
- package/stack/docker-compose.yml +447 -0
- package/stack/hub/Dockerfile.api +18 -0
- package/stack/hub/Dockerfile.auth +10 -0
- package/stack/hub/Dockerfile.ui +10 -0
- package/stack/init/01-databases.sql +12 -0
- package/stack/observability/blackbox.yml +20 -0
- package/stack/observability/otel-collector.yaml +73 -0
- package/stack/observability/prometheus.yml +36 -0
- package/stack/observability/tempo.yaml +25 -0
- package/stack/temporal/local.yaml +44 -0
- package/templates/api-auth/AGENTS.md.hbs +16 -0
- package/templates/api-auth/Pulumi.dev.yaml.hbs +8 -0
- package/templates/api-auth/Pulumi.yaml.hbs +8 -0
- package/templates/api-auth/README.md.hbs +67 -0
- package/templates/api-auth/api/package.json.hbs +28 -0
- package/templates/api-auth/api/src/index.ts.hbs +96 -0
- package/templates/api-auth/api/src/schema.ts.hbs +35 -0
- package/templates/api-auth/api/src/seed-authz.ts.hbs +48 -0
- package/templates/api-auth/api/src/spicedb-http-provider.ts.hbs +89 -0
- package/templates/api-auth/index.ts.hbs +65 -0
- package/templates/api-auth/package.json.hbs +20 -0
- package/templates/api-auth/skills/develop/SKILL.md.hbs +32 -0
- package/templates/api-role-gating/AGENTS.md.hbs +20 -0
- package/templates/api-role-gating/Pulumi.dev.yaml.hbs +8 -0
- package/templates/api-role-gating/Pulumi.yaml.hbs +8 -0
- package/templates/api-role-gating/README.md.hbs +64 -0
- package/templates/api-role-gating/api/Dockerfile +17 -0
- package/templates/api-role-gating/api/package.json.hbs +28 -0
- package/templates/api-role-gating/api/src/authn.ts.hbs +29 -0
- package/templates/api-role-gating/api/src/authz.ts.hbs +12 -0
- package/templates/api-role-gating/api/src/main.ts.hbs +58 -0
- package/templates/api-role-gating/api/src/schema.ts.hbs +22 -0
- package/templates/api-role-gating/api/tsconfig.json +13 -0
- package/templates/api-role-gating/index.ts.hbs +107 -0
- package/templates/api-role-gating/package.json.hbs +21 -0
- package/templates/api-role-gating/skills/develop/SKILL.md.hbs +60 -0
- package/templates/api-role-gating/tsconfig.json +10 -0
- package/templates/apps-repo/README.md.hbs +24 -0
- package/templates/apps-repo/gitignore +41 -0
- package/templates/apps-repo/npmrc.hbs +7 -0
- package/templates/apps-repo/package.json.hbs +15 -0
- package/templates/apps-repo/pnpm-workspace.yaml +3 -0
- package/templates/apps-repo/tsconfig.json +18 -0
- package/templates/external-service/AGENTS.md.hbs +20 -0
- package/templates/external-service/Pulumi.dev.yaml.hbs +8 -0
- package/templates/external-service/Pulumi.yaml.hbs +8 -0
- package/templates/external-service/README.md.hbs +56 -0
- package/templates/external-service/api/Dockerfile +15 -0
- package/templates/external-service/api/package.json.hbs +25 -0
- package/templates/external-service/api/src/index.ts.hbs +81 -0
- package/templates/external-service/index.ts.hbs +82 -0
- package/templates/external-service/mocks/vendorpay/package.json.hbs +9 -0
- package/templates/external-service/mocks/vendorpay/server.mjs.hbs +61 -0
- package/templates/external-service/package.json.hbs +34 -0
- package/templates/external-service/skills/develop/SKILL.md.hbs +55 -0
- package/templates/external-service/tsconfig.json +10 -0
- package/templates/platform-repo/AGENTS.md.hbs +18 -0
- package/templates/platform-repo/README.md.hbs +38 -0
- package/templates/platform-repo/core/Pulumi.yaml.hbs +12 -0
- package/templates/platform-repo/core/Pulumi.{{tenant}}-{{env}}.yaml.hbs +57 -0
- package/templates/platform-repo/core/index.ts +62 -0
- package/templates/platform-repo/core/package.json.hbs +22 -0
- package/templates/platform-repo/core/tsconfig.json +8 -0
- package/templates/platform-repo/package.json.hbs +15 -0
- package/templates/platform-repo/platform/Pulumi.yaml.hbs +12 -0
- package/templates/platform-repo/platform/Pulumi.{{tenant}}-{{env}}.yaml.hbs +59 -0
- package/templates/platform-repo/platform/index.ts +95 -0
- package/templates/platform-repo/platform/package.json.hbs +26 -0
- package/templates/platform-repo/platform/tsconfig.json +8 -0
- package/templates/platform-repo/pnpm-workspace.yaml +3 -0
- package/templates/workflow/AGENTS.md.hbs +15 -0
- package/templates/workflow/Pulumi.dev.yaml.hbs +8 -0
- package/templates/workflow/Pulumi.yaml.hbs +8 -0
- package/templates/workflow/README.md.hbs +45 -0
- package/templates/workflow/api/.dockerignore +4 -0
- package/templates/workflow/api/Dockerfile +15 -0
- package/templates/{temporal-api-worker → workflow}/api/package.json.hbs +8 -6
- package/templates/workflow/api/src/index.ts.hbs +130 -0
- package/templates/workflow/api/tsconfig.json +14 -0
- package/templates/workflow/index.ts.hbs +67 -0
- package/templates/workflow/package.json.hbs +20 -0
- package/templates/workflow/skills/develop/SKILL.md.hbs +32 -0
- package/templates/workflow/tsconfig.json +10 -0
- package/templates/workflow/worker/.dockerignore +4 -0
- package/templates/workflow/worker/Dockerfile +13 -0
- package/templates/workflow/worker/package.json.hbs +25 -0
- package/templates/workflow/worker/src/worker.ts.hbs +24 -0
- package/templates/workflow/worker/tsconfig.json +14 -0
- package/dist/src/commands/devbox.d.ts +0 -22
- package/dist/src/commands/devbox.d.ts.map +0 -1
- package/dist/src/commands/devbox.js +0 -463
- package/dist/src/commands/devbox.js.map +0 -1
- package/templates/api-web-db/api/Dockerfile +0 -16
- package/templates/api-web-db/api/package.json.hbs +0 -21
- package/templates/api-web-db/api/src/index.ts.hbs +0 -123
- package/templates/api-web-db/package.json.hbs +0 -27
- package/templates/api-web-db/sst.config.ts.hbs +0 -151
- package/templates/api-web-db/web/.dockerignore +0 -3
- package/templates/api-web-db/web/Dockerfile +0 -17
- package/templates/api-web-db/web/app/lib/db.server.ts +0 -36
- package/templates/api-web-db/web/app/root.tsx +0 -29
- package/templates/api-web-db/web/app/routes/home.tsx.hbs +0 -114
- package/templates/api-web-db/web/app/routes.ts +0 -3
- package/templates/api-web-db/web/package.json.hbs +0 -30
- package/templates/api-web-db/web/prisma/schema.prisma +0 -25
- package/templates/api-web-db/web/prisma.config.ts +0 -18
- package/templates/api-web-db/web/react-router.config.ts +0 -5
- package/templates/api-web-db/web/tsconfig.json +0 -19
- package/templates/api-web-db/web/vite.config.ts +0 -9
- package/templates/temporal-api-worker/README.md.hbs +0 -133
- package/templates/temporal-api-worker/api/src/index.ts.hbs +0 -145
- package/templates/temporal-api-worker/package.json.hbs +0 -29
- package/templates/temporal-api-worker/sst.config.ts.hbs +0 -161
- package/templates/temporal-api-worker/worker/Dockerfile +0 -11
- package/templates/temporal-api-worker/worker/src/bundle-workflows.ts +0 -38
- package/templates/temporal-api-worker/worker/src/worker.ts.hbs +0 -93
- /package/templates/{temporal-api-worker → api-auth}/api/.dockerignore +0 -0
- /package/templates/{temporal-api-worker → api-auth}/api/Dockerfile +0 -0
- /package/templates/{temporal-api-worker → api-auth}/api/tsconfig.json +0 -0
- /package/templates/{temporal-api-worker → api-auth}/tsconfig.json +0 -0
- /package/templates/{temporal-api-worker/worker → api-role-gating/api}/.dockerignore +0 -0
- /package/templates/{temporal-api-worker/worker → external-service/api}/tsconfig.json +0 -0
- /package/templates/{temporal-api-worker → workflow}/api/src/encryption-codec.ts +0 -0
- /package/templates/{temporal-api-worker → workflow}/worker/src/activities.ts.hbs +0 -0
- /package/templates/{temporal-api-worker → workflow}/worker/src/encryption-codec.ts +0 -0
- /package/templates/{temporal-api-worker → workflow}/worker/src/workflows.ts.hbs +0 -0
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mesh temporal — inspect Temporal workflows for the current app/stack.
|
|
3
|
+
*
|
|
4
|
+
* Commands:
|
|
5
|
+
* mesh temporal describe <workflowId> [runId] Show workflow status and metadata
|
|
6
|
+
* mesh temporal history <workflowId> [runId] Show workflow event history
|
|
7
|
+
*
|
|
8
|
+
* Connection info is resolved from the app's Pulumi stack output (same as mesh dev).
|
|
9
|
+
* Uses the raw gRPC API so we can render events without a codec proxy; when
|
|
10
|
+
* `--show-payloads` is set we resolve the app's `TEMPORAL_ENCODING_KEY` from
|
|
11
|
+
* its Kubernetes secret and decrypt inline via the shared codec helper.
|
|
12
|
+
*/
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
14
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { dirname, join, resolve } from "node:path";
|
|
17
|
+
import { logError, logInfo, logSuccess, logWarn } from "../utils/log.js";
|
|
18
|
+
import { classifyHistoryEvents, extractSnapshotMessages, reconstructTranscript, renderTranscriptMarkdown, } from "../utils/recover-conversation.js";
|
|
19
|
+
import { findAppRoot, findStackConfigs, getCurrentStack, pulumiStackOutput, readStackConfig, } from "../utils/pulumi.js";
|
|
20
|
+
import { resolveAwsCredentials } from "../utils/aws-auth.js";
|
|
21
|
+
import { resolveTemporalAuth, acquireTemporalBearer } from "../utils/temporal-auth.js";
|
|
22
|
+
import { buildDecrypterForNamespace, buildRawDecrypterForNamespace, } from "../utils/temporal-codec.js";
|
|
23
|
+
/**
|
|
24
|
+
* Resolve Temporal address and namespace from the current app's stack output.
|
|
25
|
+
* Falls back to env vars if stack output isn't available.
|
|
26
|
+
*/
|
|
27
|
+
async function resolveConnection(options) {
|
|
28
|
+
// Explicit overrides take precedence
|
|
29
|
+
if (options.address && options.namespace) {
|
|
30
|
+
return { address: options.address, namespace: options.namespace };
|
|
31
|
+
}
|
|
32
|
+
// Try from environment (set by mesh dev)
|
|
33
|
+
if (!options.address && process.env.TEMPORAL_ADDRESS) {
|
|
34
|
+
const address = process.env.TEMPORAL_ADDRESS;
|
|
35
|
+
const namespace = options.namespace ?? process.env.TEMPORAL_NAMESPACE;
|
|
36
|
+
if (namespace)
|
|
37
|
+
return { address, namespace };
|
|
38
|
+
}
|
|
39
|
+
// Resolve from Pulumi stack output
|
|
40
|
+
const appRoot = findAppRoot(process.cwd());
|
|
41
|
+
if (!appRoot) {
|
|
42
|
+
throw new Error("Not in a Mesh app directory (no Pulumi.yaml found).\n" +
|
|
43
|
+
"Run from an app directory, or provide --address and --namespace.");
|
|
44
|
+
}
|
|
45
|
+
const stack = options.stack ?? getCurrentStack(appRoot);
|
|
46
|
+
if (!stack) {
|
|
47
|
+
const stacks = findStackConfigs(appRoot);
|
|
48
|
+
throw new Error(`No Pulumi stack selected.\n` +
|
|
49
|
+
(stacks.length > 0
|
|
50
|
+
? `Available: ${stacks.join(", ")}\nUse: mesh temporal --stack <name> ...`
|
|
51
|
+
: "No stack configs found in this directory."));
|
|
52
|
+
}
|
|
53
|
+
const sa = stack ? ["--stack", stack] : [];
|
|
54
|
+
// Resolve AWS credentials the same way mesh dev does
|
|
55
|
+
const deployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
|
|
56
|
+
const awsCreds = deployerRole
|
|
57
|
+
? (await resolveAwsCredentials(deployerRole, appRoot, stack))?.env
|
|
58
|
+
: undefined;
|
|
59
|
+
// Pulumi's `app` output from env.register() is a struct containing a
|
|
60
|
+
// secret-tainted `dev` field (secret because service env vars include values
|
|
61
|
+
// like TEMPORAL_ENCODING_KEY). pulumiStackOutput transparently retries with
|
|
62
|
+
// --show-secrets when it sees a `"[secret]"` placeholder, so we can reach
|
|
63
|
+
// the non-sensitive tunnel/namespace fields nested inside.
|
|
64
|
+
let devOutput;
|
|
65
|
+
let appOutput;
|
|
66
|
+
try {
|
|
67
|
+
const result = pulumiStackOutput(appRoot, "app", sa, awsCreds);
|
|
68
|
+
appOutput = JSON.parse(result);
|
|
69
|
+
devOutput = appOutput.dev ?? appOutput;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Legacy stacks that exported `dev` directly (pre-env.register() format).
|
|
73
|
+
try {
|
|
74
|
+
const result = pulumiStackOutput(appRoot, "dev", sa, awsCreds);
|
|
75
|
+
devOutput = JSON.parse(result);
|
|
76
|
+
appOutput = devOutput;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new Error(`Could not read stack output for '${stack}'.\n` +
|
|
80
|
+
"Ensure you've run: mesh deploy up\n" +
|
|
81
|
+
"Or provide --address and --namespace explicitly.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const tunnel = devOutput?.tunnels?.temporal;
|
|
85
|
+
// namespace lives at the app output level, not inside dev
|
|
86
|
+
const namespace = options.namespace ?? appOutput?.namespace ?? devOutput?.namespace;
|
|
87
|
+
if (!tunnel) {
|
|
88
|
+
throw new Error("No temporal tunnel found in stack output.");
|
|
89
|
+
}
|
|
90
|
+
if (!namespace) {
|
|
91
|
+
throw new Error("No temporal namespace found in stack output.");
|
|
92
|
+
}
|
|
93
|
+
// Auth context for minting a Bearer, mirroring resolveTemporalAuthVars in dev.ts:
|
|
94
|
+
// tenant/env/name come from the PLATFORM block (platformName is the platform's
|
|
95
|
+
// name, not the app tenant — it keys the Zitadel issuer lookup).
|
|
96
|
+
const platform = (devOutput?.platform ?? appOutput?.platform);
|
|
97
|
+
const auth = platform
|
|
98
|
+
? {
|
|
99
|
+
tenant: platform.tenant ?? "mesh",
|
|
100
|
+
env: platform.env ?? "dev",
|
|
101
|
+
platformName: platform.name ?? platform.tenant ?? "mesh",
|
|
102
|
+
}
|
|
103
|
+
: undefined;
|
|
104
|
+
return {
|
|
105
|
+
address: options.address ?? `${tunnel.host}:${tunnel.port}`,
|
|
106
|
+
namespace,
|
|
107
|
+
auth,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Mint a Zitadel Bearer for the connection, or return undefined to connect
|
|
112
|
+
* unauthenticated (correct for local/non-enforcing Temporal). Prefers ambient
|
|
113
|
+
* env creds (e.g. exported by `mesh dev`); otherwise resolves the app's M2M
|
|
114
|
+
* credentials from SSM via the auth context.
|
|
115
|
+
*/
|
|
116
|
+
async function resolveBearerToken(auth) {
|
|
117
|
+
try {
|
|
118
|
+
const haveEnvCreds = !!process.env.TEMPORAL_AUTH_CLIENT_ID && !!process.env.ZITADEL_ISSUER;
|
|
119
|
+
const vars = !haveEnvCreds && auth
|
|
120
|
+
? await resolveTemporalAuth(auth.tenant, auth.env, auth.platformName)
|
|
121
|
+
: {};
|
|
122
|
+
return await acquireTemporalBearer(vars);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
logWarn(`Temporal auth unavailable (${err instanceof Error ? err.message : String(err)}); ` +
|
|
126
|
+
`connecting unauthenticated`);
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── Event type names ─────────────────────────────────────────────────────────
|
|
131
|
+
// Temporal event type enum → human-readable name
|
|
132
|
+
const EVENT_TYPE_NAMES = {
|
|
133
|
+
1: "WorkflowExecutionStarted",
|
|
134
|
+
2: "WorkflowExecutionCompleted",
|
|
135
|
+
3: "WorkflowExecutionFailed",
|
|
136
|
+
4: "WorkflowExecutionTimedOut",
|
|
137
|
+
5: "WorkflowTaskScheduled",
|
|
138
|
+
6: "WorkflowTaskStarted",
|
|
139
|
+
7: "WorkflowTaskCompleted",
|
|
140
|
+
8: "WorkflowTaskTimedOut",
|
|
141
|
+
9: "WorkflowTaskFailed",
|
|
142
|
+
10: "ActivityTaskScheduled",
|
|
143
|
+
11: "ActivityTaskStarted",
|
|
144
|
+
12: "ActivityTaskCompleted",
|
|
145
|
+
13: "ActivityTaskFailed",
|
|
146
|
+
14: "ActivityTaskTimedOut",
|
|
147
|
+
15: "ActivityTaskCancelRequested",
|
|
148
|
+
16: "ActivityTaskCanceled",
|
|
149
|
+
17: "TimerStarted",
|
|
150
|
+
18: "TimerFired",
|
|
151
|
+
19: "TimerCanceled",
|
|
152
|
+
20: "WorkflowExecutionCancelRequested",
|
|
153
|
+
21: "WorkflowExecutionCanceled",
|
|
154
|
+
24: "WorkflowExecutionSignaled", // not 22/23 which were removed
|
|
155
|
+
25: "WorkflowExecutionTerminated",
|
|
156
|
+
26: "WorkflowExecutionSignaled",
|
|
157
|
+
29: "WorkflowPropertiesModified",
|
|
158
|
+
40: "ChildWorkflowExecutionStarted",
|
|
159
|
+
41: "ChildWorkflowExecutionCompleted",
|
|
160
|
+
42: "ChildWorkflowExecutionFailed",
|
|
161
|
+
43: "ChildWorkflowExecutionCanceled",
|
|
162
|
+
44: "ChildWorkflowExecutionTimedOut",
|
|
163
|
+
45: "ChildWorkflowExecutionTerminated",
|
|
164
|
+
46: "WorkflowPropertiesModified",
|
|
165
|
+
48: "NexusOperationScheduled",
|
|
166
|
+
49: "NexusOperationStarted",
|
|
167
|
+
50: "NexusOperationCompleted",
|
|
168
|
+
51: "NexusOperationFailed",
|
|
169
|
+
52: "NexusOperationCanceled",
|
|
170
|
+
53: "NexusOperationTimedOut",
|
|
171
|
+
};
|
|
172
|
+
// Workflow status enum → name
|
|
173
|
+
const STATUS_NAMES = {
|
|
174
|
+
0: "UNSPECIFIED",
|
|
175
|
+
1: "RUNNING",
|
|
176
|
+
2: "COMPLETED",
|
|
177
|
+
3: "FAILED",
|
|
178
|
+
4: "CANCELED",
|
|
179
|
+
5: "TERMINATED",
|
|
180
|
+
6: "CONTINUED_AS_NEW",
|
|
181
|
+
7: "TIMED_OUT",
|
|
182
|
+
};
|
|
183
|
+
// ── Commands ─────────────────────────────────────────────────────────────────
|
|
184
|
+
async function describeWorkflow(workflowId, runId, options) {
|
|
185
|
+
const conn = await connect(options);
|
|
186
|
+
try {
|
|
187
|
+
const desc = await conn.connection.workflowService.describeWorkflowExecution({
|
|
188
|
+
namespace: conn.namespace,
|
|
189
|
+
execution: { workflowId, runId },
|
|
190
|
+
});
|
|
191
|
+
const info = desc.workflowExecutionInfo;
|
|
192
|
+
if (!info) {
|
|
193
|
+
logError("No workflow execution info returned.");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
console.log(`Workflow: ${workflowId}`);
|
|
197
|
+
console.log(`Run ID: ${info.execution?.runId ?? runId ?? "?"}`);
|
|
198
|
+
console.log(`Type: ${info.type?.name ?? "?"}`);
|
|
199
|
+
console.log(`Status: ${STATUS_NAMES[info.status ?? 0] ?? info.status}`);
|
|
200
|
+
console.log(`Task Queue: ${info.taskQueue ?? "?"}`);
|
|
201
|
+
console.log(`Namespace: ${conn.namespace}`);
|
|
202
|
+
// Pending activities
|
|
203
|
+
const pending = desc.pendingActivities ?? [];
|
|
204
|
+
if (pending.length > 0) {
|
|
205
|
+
console.log(`\nPending Activities (${pending.length}):`);
|
|
206
|
+
for (const pa of pending) {
|
|
207
|
+
console.log(` - ${pa.activityType?.name ?? "?"} (attempt ${pa.attempt}, state: ${pa.state})`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Pending nexus operations
|
|
211
|
+
const pendingNexus = desc.pendingNexusOperations ?? [];
|
|
212
|
+
if (pendingNexus.length > 0) {
|
|
213
|
+
console.log(`\nPending Nexus Operations (${pendingNexus.length}):`);
|
|
214
|
+
for (const pn of pendingNexus) {
|
|
215
|
+
console.log(` - ${pn.operation ?? "?"} → ${pn.endpoint ?? "?"} (state: ${pn.state})`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
conn.connection.close();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Page through a workflow's execution history, yielding each event in order.
|
|
225
|
+
* Shared by `history` (existing-history phase) and `recover-conversation` so the
|
|
226
|
+
* pagination logic lives in one place. Follow-mode long-polling is separate
|
|
227
|
+
* (it uses `waitNewEvent` + terminal detection) and stays in `showHistory`.
|
|
228
|
+
*/
|
|
229
|
+
async function* iterHistoryEvents(workflowService, namespace, workflowId, runId, maximumPageSize = 100) {
|
|
230
|
+
let nextPageToken;
|
|
231
|
+
do {
|
|
232
|
+
const resp = await workflowService.getWorkflowExecutionHistory({
|
|
233
|
+
namespace,
|
|
234
|
+
execution: { workflowId, runId },
|
|
235
|
+
maximumPageSize,
|
|
236
|
+
nextPageToken,
|
|
237
|
+
});
|
|
238
|
+
for (const event of resp.history?.events ?? []) {
|
|
239
|
+
yield event;
|
|
240
|
+
}
|
|
241
|
+
nextPageToken = resp.nextPageToken?.length ? resp.nextPageToken : undefined;
|
|
242
|
+
} while (nextPageToken);
|
|
243
|
+
}
|
|
244
|
+
async function showHistory(workflowId, runId, options) {
|
|
245
|
+
const conn = await connect(options);
|
|
246
|
+
const maxEvents = options.follow ? Infinity : parseInt(options.limit ?? "200", 10);
|
|
247
|
+
// Compact mode skips WorkflowTask* events (scheduling noise)
|
|
248
|
+
const skipTypes = new Set([5, 6, 7, 8, 9]); // WorkflowTask*
|
|
249
|
+
// Resolve the per-app encoding key lazily — only when the caller asks for
|
|
250
|
+
// payload bodies. Failure to resolve is non-fatal: we print the history
|
|
251
|
+
// without payloads and the warning from buildDecrypterForNamespace tells
|
|
252
|
+
// the user why.
|
|
253
|
+
const decrypter = options.showPayloads
|
|
254
|
+
? await buildDecrypterForNamespace(conn.namespace)
|
|
255
|
+
: undefined;
|
|
256
|
+
try {
|
|
257
|
+
let totalShown = 0;
|
|
258
|
+
let lastEventId = 0;
|
|
259
|
+
console.log(`History for ${workflowId} (namespace: ${conn.namespace})${options.follow ? " [following]" : ""}\n`);
|
|
260
|
+
// Fetch existing history first
|
|
261
|
+
for await (const event of iterHistoryEvents(conn.connection.workflowService, conn.namespace, workflowId, runId)) {
|
|
262
|
+
lastEventId = Number(event.eventId);
|
|
263
|
+
const eventType = event.eventType ?? 0;
|
|
264
|
+
if (options.compact !== false && skipTypes.has(eventType))
|
|
265
|
+
continue;
|
|
266
|
+
await printEvent(event, decrypter);
|
|
267
|
+
totalShown++;
|
|
268
|
+
if (totalShown >= maxEvents)
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
if (!options.follow) {
|
|
272
|
+
console.log(`\n${totalShown} events shown.`);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// Follow mode: long-poll for new events until workflow completes
|
|
276
|
+
const terminalTypes = new Set([2, 3, 4, 21, 27, 28]); // Completed, Failed, TimedOut, Canceled, Terminated, ContinuedAsNew
|
|
277
|
+
let done = false;
|
|
278
|
+
while (!done) {
|
|
279
|
+
try {
|
|
280
|
+
const resp = await conn.connection.workflowService.getWorkflowExecutionHistory({
|
|
281
|
+
namespace: conn.namespace,
|
|
282
|
+
execution: { workflowId, runId },
|
|
283
|
+
maximumPageSize: 100,
|
|
284
|
+
waitNewEvent: true,
|
|
285
|
+
// Start after the last event we've seen
|
|
286
|
+
...(lastEventId > 0 ? { skipArchival: true } : {}),
|
|
287
|
+
});
|
|
288
|
+
for (const event of resp.history?.events ?? []) {
|
|
289
|
+
const eid = Number(event.eventId);
|
|
290
|
+
if (eid <= lastEventId)
|
|
291
|
+
continue; // already shown
|
|
292
|
+
lastEventId = eid;
|
|
293
|
+
const eventType = event.eventType ?? 0;
|
|
294
|
+
if (options.compact !== false && skipTypes.has(eventType))
|
|
295
|
+
continue;
|
|
296
|
+
await printEvent(event, decrypter);
|
|
297
|
+
totalShown++;
|
|
298
|
+
// Check if workflow reached a terminal state
|
|
299
|
+
if (terminalTypes.has(eventType)) {
|
|
300
|
+
done = true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
// Connection reset or workflow completed between polls
|
|
306
|
+
if (err?.code === 5 /* NOT_FOUND */ || err?.message?.includes("not found")) {
|
|
307
|
+
console.log("\nWorkflow completed or not found.");
|
|
308
|
+
done = true;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
console.log(`\n${totalShown} events shown (workflow ended).`);
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
conn.connection.close();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function printEvent(event, decrypter) {
|
|
322
|
+
const eventType = event.eventType ?? 0;
|
|
323
|
+
const typeName = EVENT_TYPE_NAMES[eventType] ?? `Unknown(${eventType})`;
|
|
324
|
+
const id = String(event.eventId).padStart(4);
|
|
325
|
+
const detail = extractEventDetail(event);
|
|
326
|
+
const detailStr = detail ? ` — ${detail}` : "";
|
|
327
|
+
console.log(`${id} ${typeName}${detailStr}`);
|
|
328
|
+
if (!decrypter)
|
|
329
|
+
return;
|
|
330
|
+
// Print any named payload groups attached to this event — activity input/
|
|
331
|
+
// output, signal input, workflow start args, etc. — decrypted and pretty-
|
|
332
|
+
// printed under the header line with a 6-space indent so they align under
|
|
333
|
+
// the event ID column.
|
|
334
|
+
for (const group of extractEventPayloads(event)) {
|
|
335
|
+
const rendered = await renderPayloads(group.payloads, decrypter);
|
|
336
|
+
if (rendered === null)
|
|
337
|
+
continue;
|
|
338
|
+
console.log(` ${group.label}:`);
|
|
339
|
+
for (const line of rendered.split("\n")) {
|
|
340
|
+
console.log(` ${line}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Pick out the payload-bearing fields on a history event. Returns an empty
|
|
346
|
+
* array for events that carry no interesting data (workflow task lifecycle,
|
|
347
|
+
* timers, property modifications, etc.).
|
|
348
|
+
*/
|
|
349
|
+
function extractEventPayloads(event) {
|
|
350
|
+
const groups = [];
|
|
351
|
+
const pushIfAny = (label, payloads) => {
|
|
352
|
+
if (payloads && payloads.length > 0)
|
|
353
|
+
groups.push({ label, payloads });
|
|
354
|
+
};
|
|
355
|
+
if (event.workflowExecutionStartedEventAttributes) {
|
|
356
|
+
pushIfAny("input", event.workflowExecutionStartedEventAttributes.input?.payloads);
|
|
357
|
+
}
|
|
358
|
+
if (event.workflowExecutionCompletedEventAttributes) {
|
|
359
|
+
pushIfAny("result", event.workflowExecutionCompletedEventAttributes.result?.payloads);
|
|
360
|
+
}
|
|
361
|
+
if (event.workflowExecutionFailedEventAttributes) {
|
|
362
|
+
const failure = event.workflowExecutionFailedEventAttributes.failure;
|
|
363
|
+
if (failure?.message)
|
|
364
|
+
groups.push({ label: "failure", payloads: [{ __failure: failure }] });
|
|
365
|
+
}
|
|
366
|
+
if (event.workflowExecutionSignaledEventAttributes) {
|
|
367
|
+
pushIfAny("signal input", event.workflowExecutionSignaledEventAttributes.input?.payloads);
|
|
368
|
+
}
|
|
369
|
+
if (event.activityTaskScheduledEventAttributes) {
|
|
370
|
+
pushIfAny("input", event.activityTaskScheduledEventAttributes.input?.payloads);
|
|
371
|
+
}
|
|
372
|
+
if (event.activityTaskCompletedEventAttributes) {
|
|
373
|
+
pushIfAny("result", event.activityTaskCompletedEventAttributes.result?.payloads);
|
|
374
|
+
}
|
|
375
|
+
if (event.activityTaskFailedEventAttributes) {
|
|
376
|
+
const failure = event.activityTaskFailedEventAttributes.failure;
|
|
377
|
+
if (failure?.message)
|
|
378
|
+
groups.push({ label: "failure", payloads: [{ __failure: failure }] });
|
|
379
|
+
}
|
|
380
|
+
if (event.nexusOperationScheduledEventAttributes) {
|
|
381
|
+
const input = event.nexusOperationScheduledEventAttributes.input;
|
|
382
|
+
if (input)
|
|
383
|
+
pushIfAny("input", [input]);
|
|
384
|
+
}
|
|
385
|
+
if (event.nexusOperationCompletedEventAttributes) {
|
|
386
|
+
const result = event.nexusOperationCompletedEventAttributes.result;
|
|
387
|
+
if (result)
|
|
388
|
+
pushIfAny("result", [result]);
|
|
389
|
+
}
|
|
390
|
+
if (event.nexusOperationFailedEventAttributes) {
|
|
391
|
+
const failure = event.nexusOperationFailedEventAttributes.failure;
|
|
392
|
+
if (failure?.message)
|
|
393
|
+
groups.push({ label: "failure", payloads: [{ __failure: failure }] });
|
|
394
|
+
}
|
|
395
|
+
return groups;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Render one or more payloads into a multi-line string, or return `null`
|
|
399
|
+
* when nothing rendered (e.g. all payloads were non-encrypted and empty).
|
|
400
|
+
*
|
|
401
|
+
* Failures (`{ __failure: ... }`) are rendered specially since they're a
|
|
402
|
+
* structured error, not an encrypted payload.
|
|
403
|
+
*/
|
|
404
|
+
async function renderPayloads(payloads, decrypter) {
|
|
405
|
+
const parts = [];
|
|
406
|
+
for (const payload of payloads) {
|
|
407
|
+
if (payload && typeof payload === "object" && "__failure" in payload) {
|
|
408
|
+
const failure = payload.__failure;
|
|
409
|
+
parts.push(renderFailure(failure));
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const decoded = await decrypter(payload);
|
|
413
|
+
if (decoded === null) {
|
|
414
|
+
parts.push("[non-encrypted payload]");
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
parts.push(decoded);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return parts.length > 0 ? parts.join("\n---\n") : null;
|
|
421
|
+
}
|
|
422
|
+
function renderFailure(failure) {
|
|
423
|
+
const lines = [failure.message ?? "(no message)"];
|
|
424
|
+
if (failure.stackTrace)
|
|
425
|
+
lines.push(failure.stackTrace);
|
|
426
|
+
return lines.join("\n");
|
|
427
|
+
}
|
|
428
|
+
/** Extract a human-readable detail string from event attributes. */
|
|
429
|
+
function extractEventDetail(event) {
|
|
430
|
+
// Activity scheduled
|
|
431
|
+
if (event.activityTaskScheduledEventAttributes) {
|
|
432
|
+
const a = event.activityTaskScheduledEventAttributes;
|
|
433
|
+
return `activity: ${a.activityType?.name ?? "?"}, queue: ${a.taskQueue?.name ?? "?"}`;
|
|
434
|
+
}
|
|
435
|
+
// Activity completed
|
|
436
|
+
if (event.activityTaskCompletedEventAttributes) {
|
|
437
|
+
return `scheduledId: ${event.activityTaskCompletedEventAttributes.scheduledEventId}`;
|
|
438
|
+
}
|
|
439
|
+
// Activity failed
|
|
440
|
+
if (event.activityTaskFailedEventAttributes) {
|
|
441
|
+
const a = event.activityTaskFailedEventAttributes;
|
|
442
|
+
const msg = a.failure?.message ?? "";
|
|
443
|
+
return `scheduledId: ${a.scheduledEventId}${msg ? `, error: ${msg.slice(0, 120)}` : ""}`;
|
|
444
|
+
}
|
|
445
|
+
// Activity timed out
|
|
446
|
+
if (event.activityTaskTimedOutEventAttributes) {
|
|
447
|
+
return `scheduledId: ${event.activityTaskTimedOutEventAttributes.scheduledEventId}`;
|
|
448
|
+
}
|
|
449
|
+
// Nexus scheduled
|
|
450
|
+
if (event.nexusOperationScheduledEventAttributes) {
|
|
451
|
+
const a = event.nexusOperationScheduledEventAttributes;
|
|
452
|
+
return `endpoint: ${a.endpoint ?? "?"}, operation: ${a.operation ?? "?"}, service: ${a.service ?? "?"}`;
|
|
453
|
+
}
|
|
454
|
+
// Nexus completed/failed/timed out
|
|
455
|
+
if (event.nexusOperationCompletedEventAttributes) {
|
|
456
|
+
return `scheduledId: ${event.nexusOperationCompletedEventAttributes.scheduledEventId}`;
|
|
457
|
+
}
|
|
458
|
+
if (event.nexusOperationFailedEventAttributes) {
|
|
459
|
+
const a = event.nexusOperationFailedEventAttributes;
|
|
460
|
+
return `scheduledId: ${a.scheduledEventId}, error: ${a.failure?.message?.slice(0, 120) ?? "?"}`;
|
|
461
|
+
}
|
|
462
|
+
if (event.nexusOperationTimedOutEventAttributes) {
|
|
463
|
+
return `scheduledId: ${event.nexusOperationTimedOutEventAttributes.scheduledEventId}`;
|
|
464
|
+
}
|
|
465
|
+
// Signal
|
|
466
|
+
if (event.workflowExecutionSignaledEventAttributes) {
|
|
467
|
+
return `signal: ${event.workflowExecutionSignaledEventAttributes.signalName ?? "?"}`;
|
|
468
|
+
}
|
|
469
|
+
// Workflow started
|
|
470
|
+
if (event.workflowExecutionStartedEventAttributes) {
|
|
471
|
+
const a = event.workflowExecutionStartedEventAttributes;
|
|
472
|
+
return `type: ${a.workflowType?.name ?? "?"}, queue: ${a.taskQueue?.name ?? "?"}`;
|
|
473
|
+
}
|
|
474
|
+
// Workflow completed/failed
|
|
475
|
+
if (event.workflowExecutionCompletedEventAttributes)
|
|
476
|
+
return null;
|
|
477
|
+
if (event.workflowExecutionFailedEventAttributes) {
|
|
478
|
+
return `error: ${event.workflowExecutionFailedEventAttributes.failure?.message?.slice(0, 120) ?? "?"}`;
|
|
479
|
+
}
|
|
480
|
+
// Timer
|
|
481
|
+
if (event.timerStartedEventAttributes) {
|
|
482
|
+
const dur = event.timerStartedEventAttributes.startToFireTimeout;
|
|
483
|
+
return `timerId: ${event.timerStartedEventAttributes.timerId}, duration: ${dur?.seconds ?? "?"}s`;
|
|
484
|
+
}
|
|
485
|
+
if (event.timerFiredEventAttributes) {
|
|
486
|
+
return `timerId: ${event.timerFiredEventAttributes.timerId}`;
|
|
487
|
+
}
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Reconstruct an agent conversation's transcript from its durable Temporal
|
|
492
|
+
* history — for when the worker can no longer replay it (code drift) but the
|
|
493
|
+
* history is intact. Fetches the full history (same client/decrypt path as
|
|
494
|
+
* `history`), classifies the `submit_user_message` signals (human turns) and
|
|
495
|
+
* `callLLM` activity completions (assistant turns), decrypts their payloads,
|
|
496
|
+
* and renders an ordered transcript.
|
|
497
|
+
*/
|
|
498
|
+
async function recoverConversation(workflowId, runId, options) {
|
|
499
|
+
const conn = await connect(options);
|
|
500
|
+
// The history is encrypted — without the key there's nothing to reconstruct,
|
|
501
|
+
// so (unlike `history`) a missing key is fatal here.
|
|
502
|
+
const decrypter = await buildDecrypterForNamespace(conn.namespace);
|
|
503
|
+
if (!decrypter) {
|
|
504
|
+
conn.connection.close();
|
|
505
|
+
throw new Error("Cannot decrypt conversation payloads: no encoding key available.\n" +
|
|
506
|
+
"Set TEMPORAL_ENCODING_KEY (e.g. from a `mesh dev` worker env) or configure " +
|
|
507
|
+
"kubectl namespace read access, then retry.");
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
// Decode one payload group (signal input / activity result) into a plain
|
|
511
|
+
// object. The decrypter yields pretty-printed JSON; parse it back. Any
|
|
512
|
+
// failure returns undefined so the pure reconstructor emits a placeholder.
|
|
513
|
+
const decodePayload = async (payloads) => {
|
|
514
|
+
const first = payloads?.[0];
|
|
515
|
+
if (first === undefined)
|
|
516
|
+
return undefined;
|
|
517
|
+
const decoded = await decrypter(first);
|
|
518
|
+
if (decoded === null)
|
|
519
|
+
return undefined;
|
|
520
|
+
try {
|
|
521
|
+
return JSON.parse(decoded);
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
return undefined;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
// Collect the full history in order, then classify it (which activity
|
|
528
|
+
// completions were `callLLM`, which signals were `submit_user_message`)
|
|
529
|
+
// via the shared pure helper.
|
|
530
|
+
const rawEvents = [];
|
|
531
|
+
for await (const event of iterHistoryEvents(conn.connection.workflowService, conn.namespace, workflowId, runId)) {
|
|
532
|
+
rawEvents.push(event);
|
|
533
|
+
}
|
|
534
|
+
// --snapshot: emit the store-ready conversation blob with STRUCTURED
|
|
535
|
+
// ModelMessage[] (tool-call parts intact) for a full-fidelity backfill,
|
|
536
|
+
// rather than the flattened text turns. artifacts/focus are left empty —
|
|
537
|
+
// the artifact index isn't reconstructable from history here, and the store
|
|
538
|
+
// defaults them.
|
|
539
|
+
if (options.snapshot) {
|
|
540
|
+
const messages = await extractSnapshotMessages(rawEvents, decodePayload);
|
|
541
|
+
const blob = {
|
|
542
|
+
conversationId: workflowId,
|
|
543
|
+
messages,
|
|
544
|
+
artifacts: [],
|
|
545
|
+
focus: null,
|
|
546
|
+
};
|
|
547
|
+
const output = JSON.stringify(blob, null, 2) + "\n";
|
|
548
|
+
if (options.out) {
|
|
549
|
+
writeFileSync(options.out, output);
|
|
550
|
+
logSuccess(`Wrote snapshot (${messages.length} messages) to ${options.out}`);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
process.stdout.write(output);
|
|
554
|
+
}
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const events = await classifyHistoryEvents(rawEvents, decodePayload);
|
|
558
|
+
const turns = reconstructTranscript(events);
|
|
559
|
+
const output = options.json
|
|
560
|
+
? JSON.stringify({
|
|
561
|
+
turns: turns.map((t) => ({
|
|
562
|
+
role: t.role,
|
|
563
|
+
text: t.text,
|
|
564
|
+
...(t.toolCalls ? { toolCalls: t.toolCalls } : {}),
|
|
565
|
+
})),
|
|
566
|
+
}, null, 2) + "\n"
|
|
567
|
+
: renderTranscriptMarkdown(turns);
|
|
568
|
+
if (options.out) {
|
|
569
|
+
writeFileSync(options.out, output);
|
|
570
|
+
logSuccess(`Wrote ${turns.length} turns to ${options.out}`);
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
process.stdout.write(output);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
finally {
|
|
577
|
+
conn.connection.close();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Capture a workflow's FULL durable Temporal history to a replay fixture JSON
|
|
582
|
+
* file — for LOCAL replay debugging of a specific production break. This is the
|
|
583
|
+
* real-history counterpart of the CI gate's synthetic golden fixtures: the
|
|
584
|
+
* output is the same proto3-JSON `{ events }` shape `@mesh-tech/agent-sdk`'s
|
|
585
|
+
* `historyToFixture` produces, so it runs through `replayFixture` locally. It
|
|
586
|
+
* is never committed to the replay fixtures dir — those are synthetic goldens;
|
|
587
|
+
* this file holds real decrypted production data (see warning below).
|
|
588
|
+
*
|
|
589
|
+
* Payloads are DECRYPTED (via the same codec `history --show-payloads` uses) so
|
|
590
|
+
* the fixture replays under agent-sdk's plaintext converter. Because that means
|
|
591
|
+
* the file contains real customer/conversation data, the default output path is
|
|
592
|
+
* `~/.mesh/replay-histories/` — outside any git repo — and a loud warning is
|
|
593
|
+
* printed. An explicit `--out` inside a git repo is checked with
|
|
594
|
+
* `git check-ignore`; a non-ignored path gets a loud warning (but is not
|
|
595
|
+
* blocked). Like `recover-conversation`, a missing encoding key is fatal — an
|
|
596
|
+
* encrypted fixture can't be replayed.
|
|
597
|
+
*/
|
|
598
|
+
async function captureHistory(workflowId, runId, options) {
|
|
599
|
+
const conn = await connect(options);
|
|
600
|
+
// A fixture whose payloads stay encrypted can't be replayed (the replayer
|
|
601
|
+
// uses a codec-free converter), so — like recover-conversation — treat a
|
|
602
|
+
// missing key as fatal rather than emitting a useless file.
|
|
603
|
+
const decrypter = await buildRawDecrypterForNamespace(conn.namespace);
|
|
604
|
+
if (!decrypter) {
|
|
605
|
+
conn.connection.close();
|
|
606
|
+
throw new Error("Cannot decrypt history payloads: no encoding key available.\n" +
|
|
607
|
+
"A replay fixture needs decrypted payloads. Set TEMPORAL_ENCODING_KEY " +
|
|
608
|
+
"(e.g. from a `mesh dev` worker env) or configure kubectl namespace read " +
|
|
609
|
+
"access, then retry.");
|
|
610
|
+
}
|
|
611
|
+
try {
|
|
612
|
+
const events = [];
|
|
613
|
+
for await (const event of iterHistoryEvents(conn.connection.workflowService, conn.namespace, workflowId, runId)) {
|
|
614
|
+
events.push(event);
|
|
615
|
+
}
|
|
616
|
+
if (events.length === 0) {
|
|
617
|
+
throw new Error(`No history events found for workflow '${workflowId}'` +
|
|
618
|
+
(runId ? ` (run ${runId})` : "") +
|
|
619
|
+
`. Check the workflow id and namespace (${conn.namespace}).`);
|
|
620
|
+
}
|
|
621
|
+
// Lazy import — pulls in @temporalio/common only when actually capturing,
|
|
622
|
+
// so the CLI still loads without the Temporal SDK installed.
|
|
623
|
+
const { serializeHistoryToFixture } = await import("../utils/capture-history.js");
|
|
624
|
+
const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture(events, decrypter);
|
|
625
|
+
const outPath = options.out ??
|
|
626
|
+
join(homedir(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
|
|
627
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
628
|
+
if (options.out)
|
|
629
|
+
warnIfNotGitIgnored(options.out);
|
|
630
|
+
writeFileSync(outPath, JSON.stringify(fixture, null, 2) + "\n");
|
|
631
|
+
logSuccess(`Wrote replay history (${eventCount} events, ${decryptedPayloads} payloads decrypted) to ${outPath}`);
|
|
632
|
+
logWarn("⚠ This file contains DECRYPTED payloads — real customer/conversation " +
|
|
633
|
+
"data.\n" +
|
|
634
|
+
" For LOCAL replay debugging ONLY. Do NOT commit it. (CI golden " +
|
|
635
|
+
"fixtures are synthetic; this is the debugging counterpart.)");
|
|
636
|
+
}
|
|
637
|
+
finally {
|
|
638
|
+
conn.connection.close();
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
/** Make a workflow id safe to use as a filename (slashes, spaces, etc.). */
|
|
642
|
+
function sanitizeFileId(id) {
|
|
643
|
+
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* A replay fixture holds real DECRYPTED production data, so when the user
|
|
647
|
+
* points `--out` at a path inside a git repo we check whether that path is
|
|
648
|
+
* git-ignored (`git check-ignore`) and print a LOUD warning when it is not —
|
|
649
|
+
* we warn, we don't block. Best-effort: silent when git is unavailable or the
|
|
650
|
+
* path is outside any repo (exit code 128), since there is nothing to commit
|
|
651
|
+
* it into.
|
|
652
|
+
*/
|
|
653
|
+
function warnIfNotGitIgnored(outPath) {
|
|
654
|
+
const abs = resolve(outPath);
|
|
655
|
+
try {
|
|
656
|
+
// Exit 0 = ignored, 1 = in a repo but NOT ignored, 128 = not a repo.
|
|
657
|
+
const res = spawnSync("git", ["-C", dirname(abs), "check-ignore", "-q", abs], {
|
|
658
|
+
stdio: "ignore",
|
|
659
|
+
});
|
|
660
|
+
if (res.status !== 1)
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
return; // git missing/unusable — can't check, stay quiet
|
|
665
|
+
}
|
|
666
|
+
logWarn("⚠⚠⚠ OUTPUT PATH IS INSIDE A GIT REPO AND IS *NOT* GITIGNORED ⚠⚠⚠\n" +
|
|
667
|
+
` ${abs}\n` +
|
|
668
|
+
" This fixture contains DECRYPTED production payloads. Add it to " +
|
|
669
|
+
".gitignore or move it outside the repo — do NOT commit it.");
|
|
670
|
+
}
|
|
671
|
+
async function terminateWorkflow(workflowId, runId, options) {
|
|
672
|
+
const conn = await connect(options);
|
|
673
|
+
try {
|
|
674
|
+
await conn.connection.workflowService.terminateWorkflowExecution({
|
|
675
|
+
namespace: conn.namespace,
|
|
676
|
+
workflowExecution: { workflowId, runId },
|
|
677
|
+
reason: options.reason ?? "terminated via mesh temporal",
|
|
678
|
+
identity: "mesh-cli",
|
|
679
|
+
});
|
|
680
|
+
logSuccess(`Terminated ${workflowId}${runId ? ` (run ${runId})` : ""} in namespace ${conn.namespace}`);
|
|
681
|
+
}
|
|
682
|
+
finally {
|
|
683
|
+
conn.connection.close();
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
async function connect(options) {
|
|
687
|
+
const info = await resolveConnection(options);
|
|
688
|
+
logInfo(`Connecting to ${info.address} (namespace: ${info.namespace})`);
|
|
689
|
+
// Dynamic import — @temporalio/client is only available in workspaces
|
|
690
|
+
// that have it installed. The mesh-cli uses it from the monorepo.
|
|
691
|
+
const { Connection } = await import("@temporalio/client");
|
|
692
|
+
// Attach a Zitadel Bearer when the server enforces auth. Without it the
|
|
693
|
+
// MeshAuthorizer denies calls with "Request unauthorized" (e.g. terminate).
|
|
694
|
+
const token = await resolveBearerToken(info.auth);
|
|
695
|
+
const metadata = token ? { authorization: `Bearer ${token}` } : undefined;
|
|
696
|
+
const connection = await Connection.connect({ address: info.address, metadata });
|
|
697
|
+
return { connection, namespace: info.namespace };
|
|
698
|
+
}
|
|
699
|
+
// ── Registration ─────────────────────────────────────────────────────────────
|
|
700
|
+
export function registerTemporalCommands(program) {
|
|
701
|
+
const temporal = program
|
|
702
|
+
.command("temporal")
|
|
703
|
+
.description("Inspect Temporal workflows for the current app/stack");
|
|
704
|
+
const sharedOpts = (cmd) => cmd
|
|
705
|
+
.option("--stack <stack>", "Pulumi stack name (auto-detected from current directory)")
|
|
706
|
+
.option("--address <addr>", "Temporal server address (default: from stack output)")
|
|
707
|
+
.option("--namespace <ns>", "Temporal namespace (default: from stack output)");
|
|
708
|
+
sharedOpts(temporal
|
|
709
|
+
.command("describe <workflowId> [runId]")
|
|
710
|
+
.description("Show workflow status and metadata")).action(async (workflowId, runId, opts) => {
|
|
711
|
+
try {
|
|
712
|
+
await describeWorkflow(workflowId, runId, opts);
|
|
713
|
+
}
|
|
714
|
+
catch (err) {
|
|
715
|
+
logError(err instanceof Error ? err.message : String(err));
|
|
716
|
+
process.exitCode = 1;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
sharedOpts(temporal
|
|
720
|
+
.command("terminate <workflowId> [runId]")
|
|
721
|
+
.description("Terminate a running workflow (e.g. a wedged/looping conversation)"))
|
|
722
|
+
.option("--reason <reason>", "Termination reason (recorded in workflow history)")
|
|
723
|
+
.action(async (workflowId, runId, opts) => {
|
|
724
|
+
try {
|
|
725
|
+
await terminateWorkflow(workflowId, runId, opts);
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
logError(err instanceof Error ? err.message : String(err));
|
|
729
|
+
process.exitCode = 1;
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
sharedOpts(temporal
|
|
733
|
+
.command("history <workflowId> [runId]")
|
|
734
|
+
.description("Show workflow event history")
|
|
735
|
+
.option("-n, --limit <n>", "Maximum events to show (default: 200)")
|
|
736
|
+
.option("-f, --follow", "Tail the history, waiting for new events until the workflow completes")
|
|
737
|
+
.option("--no-compact", "Show all events including WorkflowTask scheduling noise")
|
|
738
|
+
.option("-p, --show-payloads", "Decrypt and print activity inputs/outputs (reads TEMPORAL_ENCODING_KEY from the app's K8s secret via kubectl)")).action(async (workflowId, runId, opts) => {
|
|
739
|
+
try {
|
|
740
|
+
await showHistory(workflowId, runId, opts);
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
logError(err instanceof Error ? err.message : String(err));
|
|
744
|
+
process.exitCode = 1;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
sharedOpts(temporal
|
|
748
|
+
.command("recover-conversation <workflowId> [runId]")
|
|
749
|
+
.description("Reconstruct an agent conversation transcript from durable Temporal history " +
|
|
750
|
+
"(for when the worker can no longer replay it). Requires the encoding key — " +
|
|
751
|
+
"reads TEMPORAL_ENCODING_KEY (or the app's K8s secret).")
|
|
752
|
+
.option("--out <path>", "Write the transcript to a file instead of stdout")
|
|
753
|
+
.option("--json", "Emit a structured { turns: [...] } JSON array instead of markdown")
|
|
754
|
+
.option("--snapshot", "Emit a store-ready conversation blob { conversationId, messages, artifacts, focus } " +
|
|
755
|
+
"with STRUCTURED ModelMessage[] (tool-call parts intact) for a full-fidelity backfill")).action(async (workflowId, runId, opts) => {
|
|
756
|
+
try {
|
|
757
|
+
await recoverConversation(workflowId, runId, opts);
|
|
758
|
+
}
|
|
759
|
+
catch (err) {
|
|
760
|
+
logError(err instanceof Error ? err.message : String(err));
|
|
761
|
+
process.exitCode = 1;
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
sharedOpts(temporal
|
|
765
|
+
.command("capture-history <workflowId> [runId]")
|
|
766
|
+
.description("Capture a workflow's full history to a replay fixture JSON (proto3-JSON, " +
|
|
767
|
+
"DECRYPTED payloads) for LOCAL replay debugging. Requires the encoding key. " +
|
|
768
|
+
"Defaults to ~/.mesh/replay-histories/<workflowId>.json (outside any repo) — " +
|
|
769
|
+
"do NOT commit the output.")
|
|
770
|
+
.option("--out <path>", "Output path (default: ~/.mesh/replay-histories/<workflowId>.json; a non-gitignored " +
|
|
771
|
+
"path inside a git repo triggers a loud warning)")).action(async (workflowId, runId, opts) => {
|
|
772
|
+
try {
|
|
773
|
+
await captureHistory(workflowId, runId, opts);
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
logError(err instanceof Error ? err.message : String(err));
|
|
777
|
+
process.exitCode = 1;
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
//# sourceMappingURL=temporal.js.map
|