@mesh-tech/mesh-cli 0.20.1 → 0.20.3
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/README.md +6 -5
- package/dist/bin/mesh.d.ts +0 -12
- package/dist/bin/mesh.js +99 -50
- package/dist/bin/mesh.js.map +2 -3
- package/dist/build-info.json +2 -2
- package/dist/src/commands/agent-api-client.d.ts +0 -70
- package/dist/src/commands/agent-api-client.js +0 -80
- package/dist/src/commands/app-check.d.ts +0 -56
- package/dist/src/commands/app-check.js +0 -72
- package/dist/src/commands/artifacts.d.ts +0 -11
- package/dist/src/commands/artifacts.js +0 -33
- package/dist/src/commands/cluster.d.ts +0 -39
- package/dist/src/commands/cluster.js +0 -73
- package/dist/src/commands/conversations.d.ts +0 -36
- package/dist/src/commands/conversations.js +0 -59
- package/dist/src/commands/create-app.d.ts +4 -88
- package/dist/src/commands/create-app.js +16 -155
- package/dist/src/commands/db/connect.d.ts +0 -7
- package/dist/src/commands/db/connect.js +0 -9
- package/dist/src/commands/db/credentials.d.ts +0 -7
- package/dist/src/commands/db/credentials.js +0 -10
- package/dist/src/commands/db/env.d.ts +0 -6
- package/dist/src/commands/db/env.js +0 -14
- package/dist/src/commands/db/exec.d.ts +0 -43
- package/dist/src/commands/db/exec.js +0 -69
- package/dist/src/commands/db/index.d.ts +0 -10
- package/dist/src/commands/db/index.js +0 -10
- package/dist/src/commands/db/psql.d.ts +0 -7
- package/dist/src/commands/db/psql.js +0 -18
- package/dist/src/commands/deploy.d.ts +0 -38
- package/dist/src/commands/deploy.js +1 -76
- package/dist/src/commands/dev-doctor.d.ts +0 -82
- package/dist/src/commands/dev-doctor.js +3 -132
- package/dist/src/commands/dev-launch.d.ts +0 -52
- package/dist/src/commands/dev-launch.js +0 -56
- package/dist/src/commands/dev-token-server.d.ts +0 -18
- package/dist/src/commands/dev-token-server.js +1 -24
- package/dist/src/commands/dev.d.ts +0 -246
- package/dist/src/commands/dev.js +5 -963
- package/dist/src/commands/docs.d.ts +0 -1
- package/dist/src/commands/docs.js +0 -12
- package/dist/src/commands/hub/index.d.ts +0 -60
- package/dist/src/commands/hub/index.js +2 -139
- package/dist/src/commands/init/wizard.d.ts +5 -116
- package/dist/src/commands/init/wizard.js +42 -110
- package/dist/src/commands/init.d.ts +0 -55
- package/dist/src/commands/init.js +0 -70
- package/dist/src/commands/install-shim.d.ts +0 -23
- package/dist/src/commands/install-shim.js +2 -29
- package/dist/src/commands/local/auth-provision.d.ts +0 -80
- package/dist/src/commands/local/auth-provision.js +0 -109
- package/dist/src/commands/local/dev-local.d.ts +0 -91
- package/dist/src/commands/local/dev-local.js +0 -147
- package/dist/src/commands/local/docker-runner.d.ts +0 -22
- package/dist/src/commands/local/docker-runner.js +2 -35
- package/dist/src/commands/local/helpers.d.ts +0 -14
- package/dist/src/commands/local/helpers.js +0 -13
- package/dist/src/commands/local/hub-local.d.ts +0 -96
- package/dist/src/commands/local/hub-local.js +0 -111
- package/dist/src/commands/local/index.d.ts +0 -14
- package/dist/src/commands/local/index.js +0 -86
- package/dist/src/commands/local/mocks.d.ts +0 -269
- package/dist/src/commands/local/mocks.js +1 -274
- package/dist/src/commands/local/seed-hub-catalog.d.ts +0 -25
- package/dist/src/commands/local/seed-hub-catalog.js +0 -41
- package/dist/src/commands/local/seed-zitadel.d.ts +0 -82
- package/dist/src/commands/local/seed-zitadel.js +0 -158
- package/dist/src/commands/local/seed.d.ts +0 -64
- package/dist/src/commands/local/seed.js +0 -78
- package/dist/src/commands/local/stack.d.ts +0 -141
- package/dist/src/commands/local/stack.js +0 -153
- package/dist/src/commands/login.d.ts +0 -265
- package/dist/src/commands/login.js +4 -418
- package/dist/src/commands/peer-addressing.d.ts +0 -39
- package/dist/src/commands/peer-addressing.js +0 -37
- package/dist/src/commands/port-ownership.d.ts +0 -44
- package/dist/src/commands/port-ownership.js +0 -47
- package/dist/src/commands/registry-publish.d.ts +0 -1
- package/dist/src/commands/registry-publish.js +0 -46
- package/dist/src/commands/registry.d.ts +4 -237
- package/dist/src/commands/registry.js +2 -316
- package/dist/src/commands/secrets/exec.d.ts +0 -16
- package/dist/src/commands/secrets/exec.js +0 -24
- package/dist/src/commands/secrets/index.d.ts +0 -9
- package/dist/src/commands/secrets/index.js +0 -10
- package/dist/src/commands/secrets/migrate-config.d.ts +0 -17
- package/dist/src/commands/secrets/migrate-config.js +0 -40
- package/dist/src/commands/secrets/reindex.d.ts +0 -38
- package/dist/src/commands/secrets/reindex.js +0 -39
- package/dist/src/commands/secrets/set.d.ts +0 -47
- package/dist/src/commands/secrets/set.js +0 -107
- package/dist/src/commands/site.d.ts +0 -1
- package/dist/src/commands/site.js +0 -61
- package/dist/src/commands/skills.d.ts +0 -61
- package/dist/src/commands/skills.js +0 -83
- package/dist/src/commands/stack.d.ts +0 -15
- package/dist/src/commands/stack.js +1 -128
- package/dist/src/commands/temporal.d.ts +0 -30
- package/dist/src/commands/temporal.js.map +0 -1
- package/dist/src/commands/tenant.d.ts +0 -31
- package/dist/src/commands/tenant.js +0 -55
- package/dist/src/commands/tunnel/index.d.ts +0 -40
- package/dist/src/commands/tunnel/index.js +0 -141
- package/dist/src/commands/vcs/clone.d.ts +0 -1
- package/dist/src/commands/vcs/clone.js +0 -4
- package/dist/src/commands/vcs/common.d.ts +0 -32
- package/dist/src/commands/vcs/common.js +0 -41
- package/dist/src/commands/vcs/drafts.d.ts +0 -1
- package/dist/src/commands/vcs/drafts.js +0 -1
- package/dist/src/commands/vcs/get.d.ts +0 -1
- package/dist/src/commands/vcs/get.js +0 -11
- package/dist/src/commands/vcs/index.d.ts +0 -1
- package/dist/src/commands/vcs/index.js +0 -1
- package/dist/src/commands/vcs/propose.d.ts +0 -26
- package/dist/src/commands/vcs/propose.js +3 -34
- package/dist/src/commands/vcs/review.d.ts +0 -1
- package/dist/src/commands/vcs/review.js +0 -1
- package/dist/src/commands/vcs/rm.d.ts +0 -11
- package/dist/src/commands/vcs/rm.js +0 -14
- package/dist/src/commands/vcs-enrich.d.ts +0 -12
- package/dist/src/commands/vcs-enrich.js +0 -12
- package/dist/src/commands/vpn/index.d.ts +0 -17
- package/dist/src/commands/vpn/index.js +0 -102
- package/dist/src/commands/vpn/tunnel.d.ts +0 -9
- package/dist/src/commands/vpn/tunnel.js +0 -37
- package/dist/src/commands/workflow.d.ts +0 -22
- package/dist/src/commands/workflow.js +0 -80
- package/dist/src/docs/assemble.d.ts +0 -117
- package/dist/src/docs/assemble.js +0 -142
- package/dist/src/docs/cli-reference.d.ts +0 -58
- package/dist/src/docs/cli-reference.js +0 -77
- package/dist/src/docs/discover.d.ts +0 -40
- package/dist/src/docs/discover.js +2 -60
- package/dist/src/docs/portal.d.ts +0 -44
- package/dist/src/docs/portal.js +0 -49
- package/dist/src/docs/registry-docs.d.ts +0 -29
- package/dist/src/docs/registry-docs.js +0 -40
- package/dist/src/docs/schema.d.ts +0 -36
- package/dist/src/docs/schema.js +0 -52
- package/dist/src/docs/serve.d.ts +0 -18
- package/dist/src/docs/serve.js +2 -30
- package/dist/src/docs/start.d.ts +0 -42
- package/dist/src/docs/start.js +0 -70
- package/dist/src/eval/agent-eval.d.mts +0 -12
- package/dist/src/eval/agent-eval.mjs +0 -22
- package/dist/src/index.d.ts +0 -6
- package/dist/src/index.js.map +0 -1
- package/dist/src/program.d.ts +0 -20
- package/dist/src/program.js +0 -24
- package/dist/src/utils/auth-preflight.d.ts +0 -137
- package/dist/src/utils/auth-preflight.js +0 -149
- package/dist/src/utils/aws-auth.d.ts +0 -163
- package/dist/src/utils/aws-auth.js +3 -232
- package/dist/src/utils/bastion.d.ts +0 -27
- package/dist/src/utils/bastion.js +0 -28
- package/dist/src/utils/build-info.d.ts +0 -22
- package/dist/src/utils/build-info.js +0 -27
- package/dist/src/utils/cache-home.d.ts +0 -23
- package/dist/src/utils/cache-home.js +0 -25
- package/dist/src/utils/capture-history.d.ts +0 -54
- package/dist/src/utils/capture-history.js +1 -55
- package/dist/src/utils/context.d.ts +0 -23
- package/dist/src/utils/context.js +0 -43
- package/dist/src/utils/credentials.d.ts +0 -38
- package/dist/src/utils/credentials.js +0 -39
- package/dist/src/utils/deploy-preflight.d.ts +0 -38
- package/dist/src/utils/deploy-preflight.js +1 -71
- package/dist/src/utils/errors.d.ts +0 -53
- package/dist/src/utils/errors.js +0 -49
- package/dist/src/utils/first-party-contexts.d.ts +0 -38
- package/dist/src/utils/first-party-contexts.js +0 -44
- package/dist/src/utils/index.d.ts +0 -4
- package/dist/src/utils/index.js +0 -4
- package/dist/src/utils/kubeconfig.d.ts +0 -40
- package/dist/src/utils/kubeconfig.js +0 -44
- package/dist/src/utils/log.d.ts +0 -20
- package/dist/src/utils/log.js +0 -20
- package/dist/src/utils/mesh-json.d.ts +0 -46
- package/dist/src/utils/mesh-json.js +0 -39
- package/dist/src/utils/pid.d.ts +0 -42
- package/dist/src/utils/pid.js +0 -48
- package/dist/src/utils/pulumi-run.d.ts +0 -14
- package/dist/src/utils/pulumi-run.js +0 -42
- package/dist/src/utils/pulumi.d.ts +0 -34
- package/dist/src/utils/pulumi.js +0 -38
- package/dist/src/utils/reachability.d.ts +0 -27
- package/dist/src/utils/reachability.js +0 -40
- package/dist/src/utils/recover-conversation.d.ts +0 -110
- package/dist/src/utils/recover-conversation.js +0 -108
- package/dist/src/utils/registry-broker.d.ts +5 -84
- package/dist/src/utils/registry-broker.js +0 -62
- package/dist/src/utils/registry-identity.d.ts +0 -149
- package/dist/src/utils/registry-identity.js +0 -143
- package/dist/src/utils/scaffold-versions.d.ts +0 -41
- package/dist/src/utils/scaffold-versions.js +0 -42
- package/dist/src/utils/socks-forward.d.ts +0 -24
- package/dist/src/utils/socks-forward.js +2 -25
- package/dist/src/utils/stack-flag.d.ts +0 -13
- package/dist/src/utils/stack-flag.js +0 -21
- package/dist/src/utils/tailscale-targets.d.ts +0 -37
- package/dist/src/utils/tailscale-targets.js +0 -33
- package/dist/src/utils/tailscale.d.ts +0 -177
- package/dist/src/utils/tailscale.js +6 -337
- package/dist/src/utils/temporal-auth.d.ts +0 -41
- package/dist/src/utils/temporal-auth.js +0 -52
- package/dist/src/utils/temporal-codec.d.ts +0 -89
- package/dist/src/utils/temporal-codec.js.map +0 -1
- package/dist/src/utils/tunnel-ownership.d.ts +0 -79
- package/dist/src/utils/tunnel-ownership.js +0 -74
- package/dist/src/utils/vpn-join.d.ts +0 -16
- package/dist/src/utils/vpn-join.js +0 -17
- package/dist/src/utils/vpn.d.ts +0 -42
- package/dist/src/utils/vpn.js +0 -52
- package/dist/src/utils/workflow-fingerprint.d.ts +0 -39
- package/dist/src/utils/workflow-fingerprint.js +1 -63
- package/dist/src/utils/worktree-identity.d.ts +0 -62
- package/dist/src/utils/worktree-identity.js +0 -88
- package/package.json +1 -1
- package/skills/core/SKILL.md +10 -11
- package/templates/api-auth/api/src/index.ts.hbs +1 -1
- package/templates/api-auth/api/src/spicedb-http-provider.ts.hbs +2 -2
- package/templates/api-role-gating/api/src/authn.ts.hbs +2 -2
- package/templates/api-role-gating/index.ts.hbs +1 -1
- package/templates/api-role-gating/skills/develop/SKILL.md.hbs +1 -1
- package/templates/platform-repo/core/Pulumi.{{tenant}}-{{env}}.yaml.hbs +2 -2
- package/templates/platform-repo/platform/index.ts +1 -1
- package/dist/bin/mesh.d.ts.map +0 -1
- package/dist/src/commands/agent-api-client.d.ts.map +0 -1
- package/dist/src/commands/agent-api-client.js.map +0 -1
- package/dist/src/commands/app-check.d.ts.map +0 -1
- package/dist/src/commands/app-check.js.map +0 -1
- package/dist/src/commands/artifacts.d.ts.map +0 -1
- package/dist/src/commands/artifacts.js.map +0 -1
- package/dist/src/commands/cluster.d.ts.map +0 -1
- package/dist/src/commands/cluster.js.map +0 -1
- package/dist/src/commands/conversations.d.ts.map +0 -1
- package/dist/src/commands/conversations.js.map +0 -1
- package/dist/src/commands/create-app.d.ts.map +0 -1
- package/dist/src/commands/create-app.js.map +0 -1
- package/dist/src/commands/db/connect.d.ts.map +0 -1
- package/dist/src/commands/db/connect.js.map +0 -1
- package/dist/src/commands/db/credentials.d.ts.map +0 -1
- package/dist/src/commands/db/credentials.js.map +0 -1
- package/dist/src/commands/db/env.d.ts.map +0 -1
- package/dist/src/commands/db/env.js.map +0 -1
- package/dist/src/commands/db/exec.d.ts.map +0 -1
- package/dist/src/commands/db/exec.js.map +0 -1
- package/dist/src/commands/db/index.d.ts.map +0 -1
- package/dist/src/commands/db/index.js.map +0 -1
- package/dist/src/commands/db/psql.d.ts.map +0 -1
- package/dist/src/commands/db/psql.js.map +0 -1
- package/dist/src/commands/deploy.d.ts.map +0 -1
- package/dist/src/commands/deploy.js.map +0 -1
- package/dist/src/commands/dev-doctor.d.ts.map +0 -1
- package/dist/src/commands/dev-doctor.js.map +0 -1
- package/dist/src/commands/dev-launch.d.ts.map +0 -1
- package/dist/src/commands/dev-launch.js.map +0 -1
- package/dist/src/commands/dev-token-server.d.ts.map +0 -1
- package/dist/src/commands/dev-token-server.js.map +0 -1
- package/dist/src/commands/dev.d.ts.map +0 -1
- package/dist/src/commands/dev.js.map +0 -1
- package/dist/src/commands/docs.d.ts.map +0 -1
- package/dist/src/commands/docs.js.map +0 -1
- package/dist/src/commands/hub/index.d.ts.map +0 -1
- package/dist/src/commands/hub/index.js.map +0 -1
- package/dist/src/commands/init/wizard.d.ts.map +0 -1
- package/dist/src/commands/init/wizard.js.map +0 -1
- package/dist/src/commands/init.d.ts.map +0 -1
- package/dist/src/commands/init.js.map +0 -1
- package/dist/src/commands/install-shim.d.ts.map +0 -1
- package/dist/src/commands/install-shim.js.map +0 -1
- package/dist/src/commands/local/auth-provision.d.ts.map +0 -1
- package/dist/src/commands/local/auth-provision.js.map +0 -1
- package/dist/src/commands/local/dev-local.d.ts.map +0 -1
- package/dist/src/commands/local/dev-local.js.map +0 -1
- package/dist/src/commands/local/docker-runner.d.ts.map +0 -1
- package/dist/src/commands/local/docker-runner.js.map +0 -1
- package/dist/src/commands/local/helpers.d.ts.map +0 -1
- package/dist/src/commands/local/helpers.js.map +0 -1
- package/dist/src/commands/local/hub-local.d.ts.map +0 -1
- package/dist/src/commands/local/hub-local.js.map +0 -1
- package/dist/src/commands/local/index.d.ts.map +0 -1
- package/dist/src/commands/local/index.js.map +0 -1
- package/dist/src/commands/local/mocks.d.ts.map +0 -1
- package/dist/src/commands/local/mocks.js.map +0 -1
- package/dist/src/commands/local/seed-hub-catalog.d.ts.map +0 -1
- package/dist/src/commands/local/seed-hub-catalog.js.map +0 -1
- package/dist/src/commands/local/seed-zitadel.d.ts.map +0 -1
- package/dist/src/commands/local/seed-zitadel.js.map +0 -1
- package/dist/src/commands/local/seed.d.ts.map +0 -1
- package/dist/src/commands/local/seed.js.map +0 -1
- package/dist/src/commands/local/stack.d.ts.map +0 -1
- package/dist/src/commands/local/stack.js.map +0 -1
- package/dist/src/commands/login.d.ts.map +0 -1
- package/dist/src/commands/login.js.map +0 -1
- package/dist/src/commands/peer-addressing.d.ts.map +0 -1
- package/dist/src/commands/peer-addressing.js.map +0 -1
- package/dist/src/commands/port-ownership.d.ts.map +0 -1
- package/dist/src/commands/port-ownership.js.map +0 -1
- package/dist/src/commands/registry-publish.d.ts.map +0 -1
- package/dist/src/commands/registry-publish.js.map +0 -1
- package/dist/src/commands/registry.d.ts.map +0 -1
- package/dist/src/commands/registry.js.map +0 -1
- package/dist/src/commands/secrets/exec.d.ts.map +0 -1
- package/dist/src/commands/secrets/exec.js.map +0 -1
- package/dist/src/commands/secrets/index.d.ts.map +0 -1
- package/dist/src/commands/secrets/index.js.map +0 -1
- package/dist/src/commands/secrets/migrate-config.d.ts.map +0 -1
- package/dist/src/commands/secrets/migrate-config.js.map +0 -1
- package/dist/src/commands/secrets/reindex.d.ts.map +0 -1
- package/dist/src/commands/secrets/reindex.js.map +0 -1
- package/dist/src/commands/secrets/set.d.ts.map +0 -1
- package/dist/src/commands/secrets/set.js.map +0 -1
- package/dist/src/commands/site.d.ts.map +0 -1
- package/dist/src/commands/site.js.map +0 -1
- package/dist/src/commands/skills.d.ts.map +0 -1
- package/dist/src/commands/skills.js.map +0 -1
- package/dist/src/commands/stack.d.ts.map +0 -1
- package/dist/src/commands/stack.js.map +0 -1
- package/dist/src/commands/temporal.d.ts.map +0 -1
- package/dist/src/commands/tenant.d.ts.map +0 -1
- package/dist/src/commands/tenant.js.map +0 -1
- package/dist/src/commands/tunnel/index.d.ts.map +0 -1
- package/dist/src/commands/tunnel/index.js.map +0 -1
- package/dist/src/commands/vcs/clone.d.ts.map +0 -1
- package/dist/src/commands/vcs/clone.js.map +0 -1
- package/dist/src/commands/vcs/common.d.ts.map +0 -1
- package/dist/src/commands/vcs/common.js.map +0 -1
- package/dist/src/commands/vcs/drafts.d.ts.map +0 -1
- package/dist/src/commands/vcs/drafts.js.map +0 -1
- package/dist/src/commands/vcs/get.d.ts.map +0 -1
- package/dist/src/commands/vcs/get.js.map +0 -1
- package/dist/src/commands/vcs/index.d.ts.map +0 -1
- package/dist/src/commands/vcs/index.js.map +0 -1
- package/dist/src/commands/vcs/propose.d.ts.map +0 -1
- package/dist/src/commands/vcs/propose.js.map +0 -1
- package/dist/src/commands/vcs/review.d.ts.map +0 -1
- package/dist/src/commands/vcs/review.js.map +0 -1
- package/dist/src/commands/vcs/rm.d.ts.map +0 -1
- package/dist/src/commands/vcs/rm.js.map +0 -1
- package/dist/src/commands/vcs-enrich.d.ts.map +0 -1
- package/dist/src/commands/vcs-enrich.js.map +0 -1
- package/dist/src/commands/vpn/index.d.ts.map +0 -1
- package/dist/src/commands/vpn/index.js.map +0 -1
- package/dist/src/commands/vpn/tunnel.d.ts.map +0 -1
- package/dist/src/commands/vpn/tunnel.js.map +0 -1
- package/dist/src/commands/workflow.d.ts.map +0 -1
- package/dist/src/commands/workflow.js.map +0 -1
- package/dist/src/docs/assemble.d.ts.map +0 -1
- package/dist/src/docs/assemble.js.map +0 -1
- package/dist/src/docs/cli-reference.d.ts.map +0 -1
- package/dist/src/docs/cli-reference.js.map +0 -1
- package/dist/src/docs/discover.d.ts.map +0 -1
- package/dist/src/docs/discover.js.map +0 -1
- package/dist/src/docs/portal.d.ts.map +0 -1
- package/dist/src/docs/portal.js.map +0 -1
- package/dist/src/docs/registry-docs.d.ts.map +0 -1
- package/dist/src/docs/registry-docs.js.map +0 -1
- package/dist/src/docs/schema.d.ts.map +0 -1
- package/dist/src/docs/schema.js.map +0 -1
- package/dist/src/docs/serve.d.ts.map +0 -1
- package/dist/src/docs/serve.js.map +0 -1
- package/dist/src/docs/start.d.ts.map +0 -1
- package/dist/src/docs/start.js.map +0 -1
- package/dist/src/eval/agent-eval.d.mts.map +0 -1
- package/dist/src/eval/agent-eval.mjs.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/program.d.ts.map +0 -1
- package/dist/src/program.js.map +0 -1
- package/dist/src/utils/auth-preflight.d.ts.map +0 -1
- package/dist/src/utils/auth-preflight.js.map +0 -1
- package/dist/src/utils/aws-auth.d.ts.map +0 -1
- package/dist/src/utils/aws-auth.js.map +0 -1
- package/dist/src/utils/bastion.d.ts.map +0 -1
- package/dist/src/utils/bastion.js.map +0 -1
- package/dist/src/utils/build-info.d.ts.map +0 -1
- package/dist/src/utils/build-info.js.map +0 -1
- package/dist/src/utils/cache-home.d.ts.map +0 -1
- package/dist/src/utils/cache-home.js.map +0 -1
- package/dist/src/utils/capture-history.d.ts.map +0 -1
- package/dist/src/utils/capture-history.js.map +0 -1
- package/dist/src/utils/context.d.ts.map +0 -1
- package/dist/src/utils/context.js.map +0 -1
- package/dist/src/utils/credentials.d.ts.map +0 -1
- package/dist/src/utils/credentials.js.map +0 -1
- package/dist/src/utils/deploy-preflight.d.ts.map +0 -1
- package/dist/src/utils/deploy-preflight.js.map +0 -1
- package/dist/src/utils/errors.d.ts.map +0 -1
- package/dist/src/utils/errors.js.map +0 -1
- package/dist/src/utils/first-party-contexts.d.ts.map +0 -1
- package/dist/src/utils/first-party-contexts.js.map +0 -1
- package/dist/src/utils/index.d.ts.map +0 -1
- package/dist/src/utils/index.js.map +0 -1
- package/dist/src/utils/kubeconfig.d.ts.map +0 -1
- package/dist/src/utils/kubeconfig.js.map +0 -1
- package/dist/src/utils/log.d.ts.map +0 -1
- package/dist/src/utils/log.js.map +0 -1
- package/dist/src/utils/mesh-json.d.ts.map +0 -1
- package/dist/src/utils/mesh-json.js.map +0 -1
- package/dist/src/utils/pid.d.ts.map +0 -1
- package/dist/src/utils/pid.js.map +0 -1
- package/dist/src/utils/pulumi-run.d.ts.map +0 -1
- package/dist/src/utils/pulumi-run.js.map +0 -1
- package/dist/src/utils/pulumi.d.ts.map +0 -1
- package/dist/src/utils/pulumi.js.map +0 -1
- package/dist/src/utils/reachability.d.ts.map +0 -1
- package/dist/src/utils/reachability.js.map +0 -1
- package/dist/src/utils/recover-conversation.d.ts.map +0 -1
- package/dist/src/utils/recover-conversation.js.map +0 -1
- package/dist/src/utils/registry-broker.d.ts.map +0 -1
- package/dist/src/utils/registry-broker.js.map +0 -1
- package/dist/src/utils/registry-identity.d.ts.map +0 -1
- package/dist/src/utils/registry-identity.js.map +0 -1
- package/dist/src/utils/scaffold-versions.d.ts.map +0 -1
- package/dist/src/utils/scaffold-versions.js.map +0 -1
- package/dist/src/utils/socks-forward.d.ts.map +0 -1
- package/dist/src/utils/socks-forward.js.map +0 -1
- package/dist/src/utils/stack-flag.d.ts.map +0 -1
- package/dist/src/utils/stack-flag.js.map +0 -1
- package/dist/src/utils/tailscale-targets.d.ts.map +0 -1
- package/dist/src/utils/tailscale-targets.js.map +0 -1
- package/dist/src/utils/tailscale.d.ts.map +0 -1
- package/dist/src/utils/tailscale.js.map +0 -1
- package/dist/src/utils/temporal-auth.d.ts.map +0 -1
- package/dist/src/utils/temporal-auth.js.map +0 -1
- package/dist/src/utils/temporal-codec.d.ts.map +0 -1
- package/dist/src/utils/tunnel-ownership.d.ts.map +0 -1
- package/dist/src/utils/tunnel-ownership.js.map +0 -1
- package/dist/src/utils/vpn-join.d.ts.map +0 -1
- package/dist/src/utils/vpn-join.js.map +0 -1
- package/dist/src/utils/vpn.d.ts.map +0 -1
- package/dist/src/utils/vpn.js.map +0 -1
- package/dist/src/utils/workflow-fingerprint.d.ts.map +0 -1
- package/dist/src/utils/workflow-fingerprint.js.map +0 -1
- package/dist/src/utils/worktree-identity.d.ts.map +0 -1
- package/dist/src/utils/worktree-identity.js.map +0 -1
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mesh login — Authenticate with Zitadel via OIDC PKCE or Device Code flow.
|
|
3
|
-
*
|
|
4
|
-
* Opens browser to Zitadel (PKCE) or prints a code to enter in the browser
|
|
5
|
-
* (Device Code). Exchanges for tokens and caches at ~/.config/mesh/credentials.json.
|
|
6
|
-
*
|
|
7
|
-
* Config is read from ~/.config/mesh/config.json, keyed by context
|
|
8
|
-
* (e.g., "mesh.dev"). After deploying the platform, copy the CLI client ID
|
|
9
|
-
* from the Pulumi stack output into the config file.
|
|
10
|
-
*
|
|
11
|
-
* In containers/SSH sessions, device code flow is used automatically.
|
|
12
|
-
*
|
|
13
|
-
* `mesh login` is for a DEPLOYED platform only — the tenant platform you
|
|
14
|
-
* deploy to, reach the Hub of, or tunnel into. Package-registry access is a
|
|
15
|
-
* separate, global identity owned by `mesh registry login`; a platform login
|
|
16
|
-
* never writes ~/.npmrc, and the reserved `registry` credential key is refused
|
|
17
|
-
* here so the two can never be confused.
|
|
18
|
-
*
|
|
19
|
-
* Usage:
|
|
20
|
-
* mesh login acme.dev — authenticate to the acme dev platform
|
|
21
|
-
* mesh login acme.dev --device — force device code flow
|
|
22
|
-
* mesh login acme.dev --status — show current auth state (Zitadel session if
|
|
23
|
-
* there is one, otherwise whatever the AWS
|
|
24
|
-
* credential chain resolves — SSO or key pair)
|
|
25
|
-
* mesh logout acme.dev — clear cached credentials
|
|
26
|
-
*/
|
|
27
1
|
import * as http from "http";
|
|
28
2
|
import * as crypto from "crypto";
|
|
29
3
|
import * as fs from "fs";
|
|
@@ -34,9 +8,6 @@ import { MeshCliError } from "../utils/errors.js";
|
|
|
34
8
|
import { assumeRoleCredentials, assumeRoleWithWebIdentity, describeAssumeFailure, probeAwsIdentity, renderCredentialProcessProfile, tokenIssuer, resolveStableMeshBin, selectRoleForCaller, toCredentialProcessJson, upsertManagedAwsConfigSection, } from "../utils/aws-auth.js";
|
|
35
9
|
import { firstPartyDomainFor } from "../utils/first-party-contexts.js";
|
|
36
10
|
import { tailscaleBackendState } from "./vpn/index.js";
|
|
37
|
-
// =============================================================================
|
|
38
|
-
// Paths (XDG Base Directory Specification)
|
|
39
|
-
// =============================================================================
|
|
40
11
|
const CONFIG_DIR = path.join(process.env.XDG_CONFIG_HOME ?? path.join(process.env.HOME ?? "~", ".config"), "mesh");
|
|
41
12
|
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
42
13
|
const CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
|
|
@@ -59,12 +30,6 @@ export function writeContextConfig(context, config) {
|
|
|
59
30
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
60
31
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
61
32
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Forget a cached context config so the next use runs discovery again.
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* clearContextConfig("registry"); // `mesh registry logout` — re-discovers the registry next time
|
|
67
|
-
*/
|
|
68
33
|
export function clearContextConfig(context) {
|
|
69
34
|
const existing = readConfig();
|
|
70
35
|
if (!(context in existing))
|
|
@@ -77,26 +42,12 @@ function getContextConfig(context) {
|
|
|
77
42
|
const config = readConfig();
|
|
78
43
|
return config[context] ?? null;
|
|
79
44
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Parse a mesh context like "mesh.dev" or "acme.uat" into tenant + env.
|
|
82
|
-
* Returns null if the context doesn't match the expected {tenant}.{env} pattern.
|
|
83
|
-
*/
|
|
84
45
|
function parseTenantEnv(context) {
|
|
85
46
|
const parts = context.split(".");
|
|
86
47
|
if (parts.length !== 2 || !parts[0] || !parts[1])
|
|
87
48
|
return null;
|
|
88
49
|
return { tenant: parts[0], env: parts[1] };
|
|
89
50
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Attempt auto-discovery of platform config from SSM Parameter Store.
|
|
92
|
-
*
|
|
93
|
-
* Uses ambient AWS credentials to read the zitadel config export at
|
|
94
|
-
* /mesh-platform/{tenant}/{env}/platform/zitadel. This is the preferred
|
|
95
|
-
* discovery path for users who already have AWS SSO access to the platform.
|
|
96
|
-
*
|
|
97
|
-
* Returns null if AWS credentials are unavailable, SSM read fails, or the
|
|
98
|
-
* parameter doesn't contain the required fields.
|
|
99
|
-
*/
|
|
100
51
|
async function discoverConfigFromSsm(context) {
|
|
101
52
|
const parsed = parseTenantEnv(context);
|
|
102
53
|
if (!parsed)
|
|
@@ -142,9 +93,6 @@ async function discoverConfigFromSsm(context) {
|
|
|
142
93
|
const message = err instanceof Error ? err.message : String(err);
|
|
143
94
|
if (message.includes("ExpiredToken") || message.includes("credentials")) {
|
|
144
95
|
logWarn(`SSM discovery failed: AWS credentials expired or unavailable.`);
|
|
145
|
-
// Do NOT send someone to `aws sso login` when an anonymous path is about
|
|
146
|
-
// to be tried — on the install journey that is the exact misdirection
|
|
147
|
-
// this feature removes. Only mention AWS when nothing else can resolve.
|
|
148
96
|
if (firstPartyDomainFor(context) || context.split(".").length - 1 >= 2) {
|
|
149
97
|
logInfo(` Falling back to anonymous HTTPS discovery — no AWS needed.`);
|
|
150
98
|
}
|
|
@@ -163,15 +111,6 @@ async function discoverConfigFromSsm(context) {
|
|
|
163
111
|
return null;
|
|
164
112
|
}
|
|
165
113
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Attempt auto-discovery of platform config from a well-known endpoint.
|
|
168
|
-
* Returns null if discovery fails for any reason (network, timeout, bad data).
|
|
169
|
-
*
|
|
170
|
-
* `contextKey` is the name the resulting config is CACHED under, which is not
|
|
171
|
-
* always the domain probed: a pinned first-party alias (`mesh.dev`) probes
|
|
172
|
-
* `dev.platform.meshtech.io` but must be cached under `mesh.dev`, or the very
|
|
173
|
-
* next command re-runs discovery and the alias buys nothing.
|
|
174
|
-
*/
|
|
175
114
|
async function discoverConfig(domain, contextKey = domain, opts = {}) {
|
|
176
115
|
const url = `https://cli.${domain}/.well-known/mesh.json`;
|
|
177
116
|
if (!opts.quiet)
|
|
@@ -216,18 +155,6 @@ async function discoverConfig(domain, contextKey = domain, opts = {}) {
|
|
|
216
155
|
return null;
|
|
217
156
|
}
|
|
218
157
|
}
|
|
219
|
-
/**
|
|
220
|
-
* Anonymous discovery against `https://cli.<domain>/.well-known/mesh.json`.
|
|
221
|
-
*
|
|
222
|
-
* `quiet: true` returns the payload WITHOUT caching it under `contextKey` and
|
|
223
|
-
* without the progress lines — for a caller that stores the record itself
|
|
224
|
-
* under a name that is not a platform context (the package registry's
|
|
225
|
-
* reserved key). Warnings still print, because a failed discovery is the
|
|
226
|
-
* caller's problem either way.
|
|
227
|
-
*
|
|
228
|
-
* @example
|
|
229
|
-
* const cfg = await discoverConfigFromWellKnown("dev.platform.meshtech.io", "registry", { quiet: true });
|
|
230
|
-
*/
|
|
231
158
|
export function discoverConfigFromWellKnown(domain, contextKey = domain, opts = {}) {
|
|
232
159
|
return discoverConfig(domain, contextKey, opts);
|
|
233
160
|
}
|
|
@@ -244,22 +171,7 @@ function readAllCredentials() {
|
|
|
244
171
|
function readCredentials(context) {
|
|
245
172
|
return readAllCredentials()[context] ?? null;
|
|
246
173
|
}
|
|
247
|
-
/** Monotonic counter mixed into temp-file names alongside the pid, so two
|
|
248
|
-
* writes from the same process in the same tick (e.g. rapid retries) never
|
|
249
|
-
* collide on a temp path. */
|
|
250
174
|
let atomicWriteCounter = 0;
|
|
251
|
-
/**
|
|
252
|
-
* Write `data` to `path` atomically: write to a temp file in the SAME
|
|
253
|
-
* directory as `path` (so the subsequent rename is same-filesystem, hence
|
|
254
|
-
* atomic), then `fs.renameSync` it into place. This guarantees concurrent
|
|
255
|
-
* readers (e.g. other `credential_process` invocations racing a refresh)
|
|
256
|
-
* only ever observe a fully-written file — never a truncated one from an
|
|
257
|
-
* interleaved `fs.writeFileSync`.
|
|
258
|
-
*
|
|
259
|
-
* On any error, best-effort removes the temp file before rethrowing.
|
|
260
|
-
*
|
|
261
|
-
* Exported for unit testing.
|
|
262
|
-
*/
|
|
263
175
|
export function atomicWriteFileSync(path, data, mode) {
|
|
264
176
|
const tmpPath = `${path}.${process.pid}.${atomicWriteCounter++}.tmp`;
|
|
265
177
|
try {
|
|
@@ -271,7 +183,6 @@ export function atomicWriteFileSync(path, data, mode) {
|
|
|
271
183
|
fs.unlinkSync(tmpPath);
|
|
272
184
|
}
|
|
273
185
|
catch {
|
|
274
|
-
// best-effort cleanup — ignore (e.g. write itself never created the file)
|
|
275
186
|
}
|
|
276
187
|
throw err;
|
|
277
188
|
}
|
|
@@ -340,9 +251,6 @@ async function refreshTokens(issuer, clientId, refreshToken) {
|
|
|
340
251
|
}
|
|
341
252
|
return resp.json();
|
|
342
253
|
}
|
|
343
|
-
// =============================================================================
|
|
344
|
-
// Login Flow
|
|
345
|
-
// =============================================================================
|
|
346
254
|
function login(context, config) {
|
|
347
255
|
return new Promise((resolve, reject) => {
|
|
348
256
|
const codeVerifier = generateCodeVerifier();
|
|
@@ -356,10 +264,6 @@ function login(context, config) {
|
|
|
356
264
|
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
357
265
|
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
358
266
|
authUrl.searchParams.set("state", state);
|
|
359
|
-
// Tracked teardown — without this `mesh login` hangs after success
|
|
360
|
-
// because (a) the 2-minute timeout below kept the event loop alive,
|
|
361
|
-
// and (b) the browser's keep-alive socket survived `server.close()`
|
|
362
|
-
// (which only stops new connections, not active ones).
|
|
363
267
|
let timeoutId;
|
|
364
268
|
const server = http.createServer(async (req, res) => {
|
|
365
269
|
try {
|
|
@@ -395,15 +299,12 @@ function login(context, config) {
|
|
|
395
299
|
reject(new Error("No authorization code received"));
|
|
396
300
|
return;
|
|
397
301
|
}
|
|
398
|
-
// Exchange code for tokens
|
|
399
302
|
const tokens = await exchangeCode(config.issuer, config.clientId, code, codeVerifier);
|
|
400
303
|
const idPayload = decodeJwtPayload(tokens.id_token);
|
|
401
304
|
const email = idPayload.email ??
|
|
402
305
|
idPayload.preferred_username ??
|
|
403
306
|
"unknown";
|
|
404
307
|
const sub = idPayload.sub;
|
|
405
|
-
// Parse tenants from access token
|
|
406
|
-
// (the Zitadel Action enriches the access token with urn:mesh:tenants)
|
|
407
308
|
let tenants = [];
|
|
408
309
|
try {
|
|
409
310
|
const accessPayload = decodeJwtPayload(tokens.access_token);
|
|
@@ -411,9 +312,7 @@ function login(context, config) {
|
|
|
411
312
|
accessPayload["urn:mesh:tenants"] ?? [];
|
|
412
313
|
}
|
|
413
314
|
catch {
|
|
414
|
-
// Access token may be opaque (non-JWT) in some configs
|
|
415
315
|
}
|
|
416
|
-
// Cache credentials
|
|
417
316
|
writeCredentials(context, {
|
|
418
317
|
idToken: tokens.id_token,
|
|
419
318
|
accessToken: tokens.access_token,
|
|
@@ -437,8 +336,6 @@ function login(context, config) {
|
|
|
437
336
|
reject(err);
|
|
438
337
|
}
|
|
439
338
|
});
|
|
440
|
-
// Defined after `server` so the closure captures it; covers
|
|
441
|
-
// success, error, and timeout paths.
|
|
442
339
|
const teardown = () => {
|
|
443
340
|
if (timeoutId)
|
|
444
341
|
clearTimeout(timeoutId);
|
|
@@ -463,27 +360,13 @@ function login(context, config) {
|
|
|
463
360
|
logInfo(`Open this URL in your browser:\n${url}`);
|
|
464
361
|
}
|
|
465
362
|
});
|
|
466
|
-
// Timeout after 2 minutes. We keep a handle so success / error
|
|
467
|
-
// paths can clear it via `teardown()` — otherwise the timer keeps
|
|
468
|
-
// the Node event loop alive for 2 minutes after a successful
|
|
469
|
-
// login and the CLI appears to hang.
|
|
470
363
|
timeoutId = setTimeout(() => {
|
|
471
364
|
teardown();
|
|
472
365
|
reject(new Error("Login timed out (2 minutes)"));
|
|
473
366
|
}, 120_000);
|
|
474
367
|
});
|
|
475
368
|
}
|
|
476
|
-
/** Number of fresh device codes to issue before giving up. A code's TTL is
|
|
477
|
-
* short (a couple of minutes), so if the user is slow we quietly issue another
|
|
478
|
-
* rather than failing the whole login (we hit this repeatedly in practice). */
|
|
479
369
|
const MAX_DEVICE_CODES = 3;
|
|
480
|
-
/**
|
|
481
|
-
* Run device-code login, re-issuing a fresh code (up to `maxCodes`) if the user
|
|
482
|
-
* doesn't finish before a code expires. `attempt` performs ONE code's
|
|
483
|
-
* request+poll and resolves `"success"` or `"expired"`; it rejects only on a
|
|
484
|
-
* hard error (denied / network). Extracted + injectable so the reissue loop is
|
|
485
|
-
* unit-testable without real network calls.
|
|
486
|
-
*/
|
|
487
370
|
export async function deviceLoginWithReissue(attempt, maxCodes = MAX_DEVICE_CODES) {
|
|
488
371
|
for (let n = 1; n <= maxCodes; n++) {
|
|
489
372
|
if (n > 1)
|
|
@@ -497,7 +380,6 @@ async function deviceCodeLogin(context, config) {
|
|
|
497
380
|
await deviceLoginWithReissue(() => attemptDeviceCode(context, config));
|
|
498
381
|
}
|
|
499
382
|
async function attemptDeviceCode(context, config) {
|
|
500
|
-
// Step 1: Request device authorization
|
|
501
383
|
const resp = await fetch(`${config.issuer}/oauth/v2/device_authorization`, {
|
|
502
384
|
method: "POST",
|
|
503
385
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -512,14 +394,12 @@ async function attemptDeviceCode(context, config) {
|
|
|
512
394
|
}
|
|
513
395
|
const deviceAuth = await resp.json();
|
|
514
396
|
const openUrl = deviceAuth.verification_uri_complete ?? deviceAuth.verification_uri;
|
|
515
|
-
// Step 2: Display instructions
|
|
516
397
|
console.log();
|
|
517
398
|
logInfo(`Open this URL in your browser:\n`);
|
|
518
399
|
logInfo(` ${openUrl}\n`);
|
|
519
400
|
logInfo(`Code: ${deviceAuth.user_code}`);
|
|
520
401
|
console.log();
|
|
521
402
|
logInfo("Waiting for authorization...");
|
|
522
|
-
// Try to open browser (works on host, harmless failure in container)
|
|
523
403
|
try {
|
|
524
404
|
if (process.platform === "darwin") {
|
|
525
405
|
execFileSync("open", [openUrl], { stdio: "ignore" });
|
|
@@ -528,8 +408,7 @@ async function attemptDeviceCode(context, config) {
|
|
|
528
408
|
execFileSync("xdg-open", [openUrl], { stdio: "ignore" });
|
|
529
409
|
}
|
|
530
410
|
}
|
|
531
|
-
catch {
|
|
532
|
-
// Step 3: Poll for token
|
|
411
|
+
catch { }
|
|
533
412
|
const deadline = Date.now() + deviceAuth.expires_in * 1000;
|
|
534
413
|
let interval = deviceAuth.interval * 1000;
|
|
535
414
|
while (Date.now() < deadline) {
|
|
@@ -553,7 +432,7 @@ async function attemptDeviceCode(context, config) {
|
|
|
553
432
|
const accessPayload = decodeJwtPayload(tokens.access_token);
|
|
554
433
|
tenants = accessPayload["urn:mesh:tenants"] ?? [];
|
|
555
434
|
}
|
|
556
|
-
catch {
|
|
435
|
+
catch { }
|
|
557
436
|
writeCredentials(context, {
|
|
558
437
|
idToken: tokens.id_token,
|
|
559
438
|
accessToken: tokens.access_token,
|
|
@@ -577,50 +456,27 @@ async function attemptDeviceCode(context, config) {
|
|
|
577
456
|
interval += 1000;
|
|
578
457
|
continue;
|
|
579
458
|
case "expired_token":
|
|
580
|
-
return "expired";
|
|
459
|
+
return "expired";
|
|
581
460
|
case "access_denied":
|
|
582
461
|
throw new Error("Authorization denied by user.");
|
|
583
462
|
default:
|
|
584
463
|
throw new Error(`Token exchange failed: ${error.error} — ${error.error_description ?? ""}`);
|
|
585
464
|
}
|
|
586
465
|
}
|
|
587
|
-
return "expired";
|
|
466
|
+
return "expired";
|
|
588
467
|
}
|
|
589
|
-
/**
|
|
590
|
-
* Detect an environment where the PKCE browser-callback flow won't work — a
|
|
591
|
-
* container, a remote/SSH session, or any non-interactive shell (piped output,
|
|
592
|
-
* headless, agent/CI-run). In all of these the callback flow has no browser to
|
|
593
|
-
* open and no interactive terminal, so it can only hang until the 2-minute
|
|
594
|
-
* timeout. Device-code flow (print a URL + code to complete elsewhere) is the
|
|
595
|
-
* correct fallback, so callers use it whenever this returns true.
|
|
596
|
-
*
|
|
597
|
-
* Exported for tests.
|
|
598
|
-
*/
|
|
599
468
|
export function isRemoteEnvironment() {
|
|
600
|
-
// Docker / devcontainer
|
|
601
469
|
if (process.env.REMOTE_CONTAINERS || process.env.CODESPACES)
|
|
602
470
|
return true;
|
|
603
471
|
if (fs.existsSync("/.dockerenv"))
|
|
604
472
|
return true;
|
|
605
|
-
// SSH session
|
|
606
473
|
if (process.env.SSH_CLIENT || process.env.SSH_TTY)
|
|
607
474
|
return true;
|
|
608
|
-
// No interactive terminal on stdin/stdout — `isTTY` is `undefined` (falsy)
|
|
609
|
-
// when the stream is not a TTY. This is the case that bit us: an agent- or
|
|
610
|
-
// script-driven shell over Tailscale has none of the env vars above set, so
|
|
611
|
-
// the old check fell through to the browser flow and timed out. Device flow
|
|
612
|
-
// still works when a browser IS available (just copy the URL+code), so
|
|
613
|
-
// preferring it here is strictly safer than a guaranteed timeout.
|
|
614
475
|
if (!process.stdout.isTTY || !process.stdin.isTTY)
|
|
615
476
|
return true;
|
|
616
477
|
return false;
|
|
617
478
|
}
|
|
618
|
-
/**
|
|
619
|
-
* After a successful login, hint about VPN if Tailscale is not connected.
|
|
620
|
-
* Non-blocking — never fails or delays the login flow.
|
|
621
|
-
*/
|
|
622
479
|
function hintVpnIfDisconnected(context) {
|
|
623
|
-
// The package registry is not a platform: no VPN sits behind it.
|
|
624
480
|
if (context === RESERVED_REGISTRY_CONTEXT)
|
|
625
481
|
return;
|
|
626
482
|
try {
|
|
@@ -632,28 +488,11 @@ function hintVpnIfDisconnected(context) {
|
|
|
632
488
|
}
|
|
633
489
|
}
|
|
634
490
|
catch {
|
|
635
|
-
// Tailscale not installed or not queryable — skip hint
|
|
636
491
|
}
|
|
637
492
|
}
|
|
638
|
-
/**
|
|
639
|
-
* The account id embedded in an IAM role ARN
|
|
640
|
-
* (`arn:aws:iam::123456789012:role/...`), or undefined when there is no ARN
|
|
641
|
-
* to read one from.
|
|
642
|
-
*/
|
|
643
493
|
export function accountIdFromRoleArn(roleArn) {
|
|
644
494
|
return roleArn?.match(/^arn:aws:iam::(\d{12}):/)?.[1];
|
|
645
495
|
}
|
|
646
|
-
/**
|
|
647
|
-
* Decide everything `--status` says (and its exit code) when there is no
|
|
648
|
-
* usable Zitadel session — pure so the wording and the exit decision are
|
|
649
|
-
* pinned by tests.
|
|
650
|
-
*
|
|
651
|
-
* The probe only proves "some AWS identity works on this machine"; it says
|
|
652
|
-
* nothing about whether that identity belongs to `context`. So the claim is
|
|
653
|
-
* scoped to the credentials — unless the context config carries a role ARN,
|
|
654
|
-
* whose account id makes the context check free: verify when we can, say so
|
|
655
|
-
* when we can't.
|
|
656
|
-
*/
|
|
657
496
|
export function renderAwsFallbackStatus(context, reason, identity, expectedAccountId) {
|
|
658
497
|
if (!identity) {
|
|
659
498
|
return {
|
|
@@ -759,14 +598,12 @@ async function showStatus(context, config) {
|
|
|
759
598
|
await showAwsCredentialStatus(context, `Zitadel token expired at ${creds.expiresAt}`, config);
|
|
760
599
|
return;
|
|
761
600
|
}
|
|
762
|
-
// Parse tenants from access token
|
|
763
601
|
let tenants = [];
|
|
764
602
|
try {
|
|
765
603
|
const accessPayload = decodeJwtPayload(creds.accessToken);
|
|
766
604
|
tenants = accessPayload["urn:mesh:tenants"] ?? [];
|
|
767
605
|
}
|
|
768
606
|
catch {
|
|
769
|
-
// Access token may be opaque
|
|
770
607
|
}
|
|
771
608
|
logSuccess(`Logged in as ${creds.email ?? "unknown"} (${context})`);
|
|
772
609
|
logInfo(`Subject: ${creds.sub}`);
|
|
@@ -775,9 +612,6 @@ async function showStatus(context, config) {
|
|
|
775
612
|
logInfo(`Deployable tenants: ${tenants.join(", ")}`);
|
|
776
613
|
}
|
|
777
614
|
}
|
|
778
|
-
// =============================================================================
|
|
779
|
-
// Commands
|
|
780
|
-
// =============================================================================
|
|
781
615
|
export function registerLoginCommand(program) {
|
|
782
616
|
program
|
|
783
617
|
.command("login")
|
|
@@ -795,10 +629,6 @@ export function registerLoginCommand(program) {
|
|
|
795
629
|
refuseReservedRegistryContext(context, "login");
|
|
796
630
|
let config = getContextConfig(context);
|
|
797
631
|
if (!config) {
|
|
798
|
-
// One chain, shared with `ensureLogin` — see discoverConfigGuarded.
|
|
799
|
-
// Deliberately NOT re-implemented here: this call site and that one
|
|
800
|
-
// drifted apart once already, and the difference was whether a clean
|
|
801
|
-
// laptop could resolve a short context at all.
|
|
802
632
|
config = await discoverConfigGuarded(context);
|
|
803
633
|
if (!config) {
|
|
804
634
|
logError(renderNoConfigHelp(context));
|
|
@@ -827,9 +657,6 @@ export function registerLoginCommand(program) {
|
|
|
827
657
|
logError(`Login failed: ${err.message}`);
|
|
828
658
|
process.exit(1);
|
|
829
659
|
}
|
|
830
|
-
// Platform sign-in and package-registry access are two identities. A
|
|
831
|
-
// platform login never writes ~/.npmrc; it only points at the command
|
|
832
|
-
// that does, and only when this machine has no registry access yet.
|
|
833
660
|
await hintRegistryIfMissing();
|
|
834
661
|
});
|
|
835
662
|
program
|
|
@@ -842,34 +669,14 @@ export function registerLoginCommand(program) {
|
|
|
842
669
|
logSuccess(`Logged out of ${context}. Credentials cleared.`);
|
|
843
670
|
});
|
|
844
671
|
}
|
|
845
|
-
/**
|
|
846
|
-
* The credential key the package registry's own session lives under. Not a
|
|
847
|
-
* platform: `mesh login` / `mesh logout` refuse it, and the post-login hints
|
|
848
|
-
* (VPN, tenants) never apply to it. `utils/registry-identity.ts` owns the
|
|
849
|
-
* registry side of this contract and re-exports the key.
|
|
850
|
-
*/
|
|
851
672
|
export const RESERVED_REGISTRY_CONTEXT = "registry";
|
|
852
|
-
/** Printed once when `--no-registry` is passed: the flag no longer changes anything. */
|
|
853
673
|
export const NO_REGISTRY_DEPRECATED = "--no-registry is deprecated and ignored: mesh login no longer touches the registry.";
|
|
854
|
-
/** Printed after a platform login on a machine with no package-registry access. */
|
|
855
674
|
export const REGISTRY_SEPARATE_HINT = "Package registry access is separate from platform sign-in — run: mesh registry login";
|
|
856
|
-
/**
|
|
857
|
-
* The reserved `registry` credential key is not a platform. `mesh login
|
|
858
|
-
* registry` would try to discover a platform named "registry" and cache a
|
|
859
|
-
* platform session on top of the package registry's; refuse it and name the
|
|
860
|
-
* command that owns that key.
|
|
861
|
-
*/
|
|
862
675
|
function refuseReservedRegistryContext(context, verb) {
|
|
863
676
|
if (context !== RESERVED_REGISTRY_CONTEXT)
|
|
864
677
|
return;
|
|
865
678
|
throw new MeshCliError(`"registry" is the package registry's own session, not a platform context.`, { remediation: { command: `mesh registry ${verb}` } });
|
|
866
679
|
}
|
|
867
|
-
/**
|
|
868
|
-
* After a successful platform login: one line pointing at `mesh registry
|
|
869
|
-
* login` when the machine has neither a registry session nor a working
|
|
870
|
-
* `~/.npmrc` token. Silent when either exists, and silent when the registry
|
|
871
|
-
* cannot be reached (a network fault is not a missing grant).
|
|
872
|
-
*/
|
|
873
680
|
async function hintRegistryIfMissing() {
|
|
874
681
|
try {
|
|
875
682
|
const { readRegistrySession } = await import("../utils/registry-identity.js");
|
|
@@ -882,20 +689,8 @@ async function hintRegistryIfMissing() {
|
|
|
882
689
|
}
|
|
883
690
|
}
|
|
884
691
|
catch {
|
|
885
|
-
// A hint must never fail a login that already succeeded.
|
|
886
692
|
}
|
|
887
693
|
}
|
|
888
|
-
/**
|
|
889
|
-
* The "no configuration found" explanation for a context that neither SSM,
|
|
890
|
-
* the pinned first-party table, nor the well-known endpoint could resolve —
|
|
891
|
-
* every path tried, then the `config.json` snippet to add by hand.
|
|
892
|
-
*
|
|
893
|
-
* Shared by the `login` command and the `mesh init` wizard's platform step so
|
|
894
|
-
* a discovery failure reads the same wherever it happens.
|
|
895
|
-
*
|
|
896
|
-
* @example
|
|
897
|
-
* logError(renderNoConfigHelp("acme.dev"));
|
|
898
|
-
*/
|
|
899
694
|
export function renderNoConfigHelp(context) {
|
|
900
695
|
const pinned = firstPartyDomainFor(context);
|
|
901
696
|
return (`No configuration found for "${context}".\n\n` +
|
|
@@ -917,22 +712,6 @@ export function renderNoConfigHelp(context) {
|
|
|
917
712
|
` }\n` +
|
|
918
713
|
` }\n`);
|
|
919
714
|
}
|
|
920
|
-
// =============================================================================
|
|
921
|
-
// Exports (for use by other mesh commands)
|
|
922
|
-
// =============================================================================
|
|
923
|
-
/**
|
|
924
|
-
* Get a valid access token for the given context, refreshing if needed.
|
|
925
|
-
*
|
|
926
|
-
* Returns the id token (a JWT with `sub` claim) suitable for
|
|
927
|
-
* Authorization: Bearer headers. Returns null if no credentials are
|
|
928
|
-
* cached or refresh fails.
|
|
929
|
-
*/
|
|
930
|
-
/**
|
|
931
|
-
* True while a cached token is still valid, treating anything within `marginMs`
|
|
932
|
-
* of expiry as already stale. The margin lets pollers (e.g. the dev-user
|
|
933
|
-
* token-server) re-mint BEFORE expiry instead of handing out a token that dies
|
|
934
|
-
* before the caller's next poll.
|
|
935
|
-
*/
|
|
936
715
|
export function tokenStillValid(expiresAt, marginMs = 0, now = Date.now()) {
|
|
937
716
|
return new Date(expiresAt).getTime() - marginMs > now;
|
|
938
717
|
}
|
|
@@ -948,16 +727,6 @@ async function getValidToken(context, opts = {}) {
|
|
|
948
727
|
}
|
|
949
728
|
return remintToken(context, config, creds);
|
|
950
729
|
}
|
|
951
|
-
/**
|
|
952
|
-
* Trade the session's refresh token for a new token set and cache it. Null
|
|
953
|
-
* when the session holds no refresh token or the issuer refuses the exchange.
|
|
954
|
-
*
|
|
955
|
-
* A token the issuer hands over is returned even when it cannot be written to
|
|
956
|
-
* disk, with a warning: the exchange spends the refresh token — Zitadel
|
|
957
|
-
* rotates it in the same response — so treating an uncacheable token set as no
|
|
958
|
-
* token at all discards a session that is still usable for this run and is
|
|
959
|
-
* already gone from the issuer's side.
|
|
960
|
-
*/
|
|
961
730
|
async function remintToken(context, config, creds) {
|
|
962
731
|
if (!creds.refreshToken)
|
|
963
732
|
return null;
|
|
@@ -973,7 +742,6 @@ async function remintToken(context, config, creds) {
|
|
|
973
742
|
email = decodeJwtPayload(tokens.id_token).email ?? creds.email;
|
|
974
743
|
}
|
|
975
744
|
catch {
|
|
976
|
-
// An id token this CLI cannot read still authenticates; keep the known email.
|
|
977
745
|
}
|
|
978
746
|
try {
|
|
979
747
|
writeCredentials(context, {
|
|
@@ -991,17 +759,6 @@ async function remintToken(context, config, creds) {
|
|
|
991
759
|
}
|
|
992
760
|
return tokens.id_token;
|
|
993
761
|
}
|
|
994
|
-
/**
|
|
995
|
-
* Re-mint a context's session now, whether or not its token has expired.
|
|
996
|
-
* Zitadel writes project roles into a token when it issues it, so a role
|
|
997
|
-
* granted after the sign-in stays invisible until a new token is minted —
|
|
998
|
-
* this is how a caller picks the grant up without a second browser trip.
|
|
999
|
-
* Null when there is no session, no refresh token, or the issuer refuses.
|
|
1000
|
-
*
|
|
1001
|
-
* @example
|
|
1002
|
-
* const idToken = await forceRefreshToken("registry");
|
|
1003
|
-
* if (idToken) console.log("session re-minted");
|
|
1004
|
-
*/
|
|
1005
762
|
export async function forceRefreshToken(context) {
|
|
1006
763
|
const config = getContextConfig(context);
|
|
1007
764
|
if (!config)
|
|
@@ -1011,19 +768,6 @@ export async function forceRefreshToken(context) {
|
|
|
1011
768
|
return null;
|
|
1012
769
|
return remintToken(context, config, creds);
|
|
1013
770
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Probe deployer creds for `context`/`roleArn`. Order matters:
|
|
1016
|
-
* 1. No cached Zitadel session → `no-session`.
|
|
1017
|
-
* 2. Cached session AND ambient `AWS_*` env keys present → `stale-env-override`
|
|
1018
|
-
* (the frozen triple would override the self-refreshing profile at
|
|
1019
|
-
* runtime — the exact `mesh dev` trap). Guarded on a cached session so
|
|
1020
|
-
* legitimate CI env-key credentials, which have no `mesh login` session,
|
|
1021
|
-
* are not misreported.
|
|
1022
|
-
* 3. Session can't be refreshed → `expired-session`.
|
|
1023
|
-
* 4. AssumeRoleWithWebIdentity fails → `assume-denied` (valid identity, wrong
|
|
1024
|
-
* IAM/role/policy).
|
|
1025
|
-
* 5. Otherwise → `ok` with the STS TTL.
|
|
1026
|
-
*/
|
|
1027
771
|
export async function probeCredentials(context, roleArn) {
|
|
1028
772
|
const cached = readCredentials(context);
|
|
1029
773
|
if (!cached)
|
|
@@ -1047,56 +791,23 @@ export async function probeCredentials(context, roleArn) {
|
|
|
1047
791
|
const ttlSeconds = Math.max(0, Math.round((Date.parse(sts.Expiration) - Date.now()) / 1000));
|
|
1048
792
|
return { state: "ok", ttlSeconds, expiresAt: sts.Expiration, email: cached.email };
|
|
1049
793
|
}
|
|
1050
|
-
// =============================================================================
|
|
1051
|
-
// AWS credential export (--export flag)
|
|
1052
|
-
// =============================================================================
|
|
1053
|
-
/**
|
|
1054
|
-
* Single-quote a value for shell-safe inclusion in an `export` statement.
|
|
1055
|
-
* Single-quoted strings in POSIX shells don't expand `$` or other metachars;
|
|
1056
|
-
* embedded single-quotes are escaped via the `'\''` trick.
|
|
1057
|
-
*/
|
|
1058
794
|
function shellSingleQuote(value) {
|
|
1059
795
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
1060
796
|
}
|
|
1061
|
-
/**
|
|
1062
|
-
* Render the `--static` output: raw temporary AWS credentials as shell
|
|
1063
|
-
* `export` statements (the old frozen ~1h triple). Pure — no I/O. Includes a
|
|
1064
|
-
* trailing newline. Exported for unit testing.
|
|
1065
|
-
*/
|
|
1066
797
|
export function renderExportStaticLines(args) {
|
|
1067
798
|
return (`export AWS_ACCESS_KEY_ID=${shellSingleQuote(args.accessKey)}\n` +
|
|
1068
799
|
`export AWS_SECRET_ACCESS_KEY=${shellSingleQuote(args.secretKey)}\n` +
|
|
1069
800
|
`export AWS_SESSION_TOKEN=${shellSingleQuote(args.sessionToken)}\n` +
|
|
1070
801
|
`export AWS_REGION=${shellSingleQuote(args.region)}\n`);
|
|
1071
802
|
}
|
|
1072
|
-
/**
|
|
1073
|
-
* Render the default `--export` output: point the shell at a self-refreshing
|
|
1074
|
-
* `credential_process` profile. The leading `unset` clears any stale STS
|
|
1075
|
-
* triple already in the shell so it can't shadow the profile. Pure — no I/O.
|
|
1076
|
-
* Includes a trailing newline. Exported for unit testing.
|
|
1077
|
-
*
|
|
1078
|
-
* Deliberately does NOT export `AWS_CONFIG_FILE`: the profile lives in the
|
|
1079
|
-
* user's REAL AWS config file (managed marker section — see
|
|
1080
|
-
* `upsertManagedAwsConfigSection`), so their other profiles (e.g.
|
|
1081
|
-
* `AWS_PROFILE=mesh-dev` InfraAdmin) keep working in the same shell.
|
|
1082
|
-
* Redirecting AWS_CONFIG_FILE to a mesh-private file used to clobber
|
|
1083
|
-
* ~/.aws/config for every subsequent AWS command.
|
|
1084
|
-
*/
|
|
1085
803
|
export function renderExportProfileLines(args) {
|
|
1086
804
|
return (`unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN\n` +
|
|
1087
805
|
`export AWS_PROFILE=${shellSingleQuote(args.profileName)}\n` +
|
|
1088
806
|
`export AWS_REGION=${shellSingleQuote(args.region)}\n`);
|
|
1089
807
|
}
|
|
1090
|
-
/**
|
|
1091
|
-
* The AWS config file the managed profile section is written into: the
|
|
1092
|
-
* user's `$AWS_CONFIG_FILE` when THEY have set one, else `~/.aws/config` —
|
|
1093
|
-
* the same file `aws configure sso` manages. Exported for unit testing.
|
|
1094
|
-
*/
|
|
1095
808
|
export function resolveUserAwsConfigPath(env = process.env) {
|
|
1096
809
|
return env.AWS_CONFIG_FILE ?? path.join(env.HOME ?? "~", ".aws", "config");
|
|
1097
810
|
}
|
|
1098
|
-
/** True if `dir` can be created (if needed) and written. Used to detect an
|
|
1099
|
-
* unwritable `~/.aws` (e.g. a sandbox with no `~/.aws` grant) without throwing. */
|
|
1100
811
|
function canWriteDir(dir) {
|
|
1101
812
|
try {
|
|
1102
813
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -1107,16 +818,6 @@ function canWriteDir(dir) {
|
|
|
1107
818
|
return false;
|
|
1108
819
|
}
|
|
1109
820
|
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Where to write the managed AWS profile, with a safehouse fallback. Normally
|
|
1112
|
-
* the user's real `~/.aws/config` (augmented in place, so their other tools see
|
|
1113
|
-
* the profile — hence `redirected: false`, we don't touch their `AWS_CONFIG_FILE`).
|
|
1114
|
-
* But when `~/.aws` is not writable (a sandbox without a `~/.aws` grant), fall
|
|
1115
|
-
* back to a mesh-managed file under `~/.config/mesh` we CAN write, and set
|
|
1116
|
-
* `redirected: true` so the caller also exports `AWS_CONFIG_FILE` (otherwise the
|
|
1117
|
-
* AWS SDK, looking only at `~/.aws/config`, would never find the profile). An
|
|
1118
|
-
* explicit `AWS_CONFIG_FILE` always wins and is never redirected.
|
|
1119
|
-
*/
|
|
1120
821
|
export function resolveAwsConfigTarget(env = process.env) {
|
|
1121
822
|
if (env.AWS_CONFIG_FILE)
|
|
1122
823
|
return { configPath: env.AWS_CONFIG_FILE, redirected: false };
|
|
@@ -1125,15 +826,6 @@ export function resolveAwsConfigTarget(env = process.env) {
|
|
|
1125
826
|
return { configPath: real, redirected: false };
|
|
1126
827
|
return { configPath: path.join(CONFIG_DIR, "aws-config"), redirected: true };
|
|
1127
828
|
}
|
|
1128
|
-
/**
|
|
1129
|
-
* Resolve which IAM role ARN to use, honoring the same precedence in both
|
|
1130
|
-
* `--export` and `--credential-process`: explicit `--role` wins, then
|
|
1131
|
-
* `MESH_AWS_ROLE`, then the context's cached `defaultRole`. Pure aside from
|
|
1132
|
-
* reading `process.env.MESH_AWS_ROLE` — returns an explanatory error instead
|
|
1133
|
-
* of throwing so callers can route it to stderr and exit non-zero.
|
|
1134
|
-
*
|
|
1135
|
-
* Exported for unit testing.
|
|
1136
|
-
*/
|
|
1137
829
|
export function resolveRoleOrExplain(opts, config) {
|
|
1138
830
|
const role = opts.role ?? process.env.MESH_AWS_ROLE ?? config.defaultRole;
|
|
1139
831
|
if (!role) {
|
|
@@ -1144,22 +836,6 @@ export function resolveRoleOrExplain(opts, config) {
|
|
|
1144
836
|
}
|
|
1145
837
|
return { role };
|
|
1146
838
|
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Ensure a valid Zitadel ID token is cached for the given context — refreshing
|
|
1149
|
-
* or running the full login flow as needed. Returns null on failure.
|
|
1150
|
-
*/
|
|
1151
|
-
/**
|
|
1152
|
-
* Run the sign-in itself — browser PKCE, or the device-code flow when asked
|
|
1153
|
-
* for or when there is no browser to open (`isRemoteEnvironment`) — and cache
|
|
1154
|
-
* the resulting session under `context`. Rejects when the user denies, the
|
|
1155
|
-
* flow times out, or the issuer refuses.
|
|
1156
|
-
*
|
|
1157
|
-
* The one sign-in routine every caller shares: the `login` command, on-demand
|
|
1158
|
-
* login from other commands, and the package registry's own session.
|
|
1159
|
-
*
|
|
1160
|
-
* @example
|
|
1161
|
-
* await runLoginFlow("acme.dev", config, { device: false });
|
|
1162
|
-
*/
|
|
1163
839
|
export async function runLoginFlow(context, config, opts = {}) {
|
|
1164
840
|
const useDevice = opts.device || isRemoteEnvironment();
|
|
1165
841
|
if (useDevice) {
|
|
@@ -1184,28 +860,12 @@ async function ensureValidToken(context, config, opts) {
|
|
|
1184
860
|
}
|
|
1185
861
|
return await getValidToken(context);
|
|
1186
862
|
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Ensure a valid login session for `context`, running the login flow (device
|
|
1189
|
-
* code in remote/non-TTY, browser callback otherwise) if none is cached. Returns
|
|
1190
|
-
* the cached credentials, or null if no config is available to log in with.
|
|
1191
|
-
*
|
|
1192
|
-
* For use by other commands (e.g. `secrets set`, the AWS-cred resolver) so they
|
|
1193
|
-
* can trigger login on demand instead of just failing with guidance.
|
|
1194
|
-
*/
|
|
1195
863
|
export async function ensureLogin(context, opts = {}) {
|
|
1196
864
|
const existing = readCredentials(context);
|
|
1197
865
|
if (existing && new Date(existing.expiresAt) > new Date())
|
|
1198
866
|
return existing;
|
|
1199
|
-
// interactive: false → cached-session-or-nothing. Callers with their own
|
|
1200
|
-
// fallback chain (e.g. the AWS-cred resolver's SSO path) use this so an
|
|
1201
|
-
// expired cache doesn't stall headless runs in a device-code poll.
|
|
1202
867
|
if (opts.interactive === false)
|
|
1203
868
|
return null;
|
|
1204
|
-
// Same guarded discovery chain as the `login` command: SSM first, and the
|
|
1205
|
-
// well-known HTTPS endpoint only for domain-looking contexts (≥2 dots).
|
|
1206
|
-
// Hitting `https://cli.<short-name>/.well-known/mesh.json` for an uncached
|
|
1207
|
-
// context like "mesh.dev" would let whoever registers that domain seed our
|
|
1208
|
-
// cached issuer/clientId (login phishing).
|
|
1209
869
|
const config = getContextConfig(context) ?? (await discoverConfigGuarded(context));
|
|
1210
870
|
if (!config) {
|
|
1211
871
|
logWarn(`No login config for "${context}". Run: mesh login ${context}`);
|
|
@@ -1216,23 +876,6 @@ export async function ensureLogin(context, opts = {}) {
|
|
|
1216
876
|
return null;
|
|
1217
877
|
return readCredentials(context);
|
|
1218
878
|
}
|
|
1219
|
-
/**
|
|
1220
|
-
* The one discovery chain, used by both the `login` command and `ensureLogin`:
|
|
1221
|
-
*
|
|
1222
|
-
* 1. SSM, when the context parses as `{tenant}.{env}` — the primary path for
|
|
1223
|
-
* internal users who already have AWS SSO access.
|
|
1224
|
-
* 2. The anonymous HTTPS well-known endpoint at the PINNED domain for a known
|
|
1225
|
-
* first-party short context (`mesh.dev` → `dev.platform.meshtech.io`). This
|
|
1226
|
-
* is what makes `mesh login mesh.dev` work on a laptop with no AWS at all
|
|
1227
|
-
* — the P0 case. The domain comes from a compiled-in table, never from the
|
|
1228
|
-
* context string, so it is not a phishing surface.
|
|
1229
|
-
* 3. The anonymous HTTPS endpoint at the context itself, only when it looks
|
|
1230
|
-
* like a real domain (≥2 dots).
|
|
1231
|
-
*
|
|
1232
|
-
* Never hits `https://cli.<short-name>/…` for an UNKNOWN short context — that
|
|
1233
|
-
* guard is the anti-phishing property and step 2 is deliberately a pinned
|
|
1234
|
-
* allow-list rather than a relaxation of it.
|
|
1235
|
-
*/
|
|
1236
879
|
async function discoverConfigGuarded(context) {
|
|
1237
880
|
let config = null;
|
|
1238
881
|
if (parseTenantEnv(context)) {
|
|
@@ -1250,21 +893,11 @@ async function discoverConfigGuarded(context) {
|
|
|
1250
893
|
}
|
|
1251
894
|
return config;
|
|
1252
895
|
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Resolve AWS credentials via Zitadel JWT → AssumeRoleWithWebIdentity, then
|
|
1255
|
-
* print them as `export` statements on stdout. All status/error output goes
|
|
1256
|
-
* to stderr so the caller can safely `eval "$(mesh login ... --export ...)"`.
|
|
1257
|
-
*/
|
|
1258
896
|
async function exportAwsCredentials(context, config, opts) {
|
|
1259
897
|
const token = await ensureValidToken(context, config, opts);
|
|
1260
898
|
if (!token) {
|
|
1261
899
|
process.exit(1);
|
|
1262
900
|
}
|
|
1263
|
-
// Pick a role for the caller. `resolveRoleOrExplain` handles the
|
|
1264
|
-
// --role / MESH_AWS_ROLE / cached-defaultRole precedence (shared with
|
|
1265
|
-
// --credential-process); on top of that, when we fell back to the cached
|
|
1266
|
-
// default we ask `selectRoleForCaller` to upgrade to the context's
|
|
1267
|
-
// `adminRole` when the Zitadel JWT carries an admin claim.
|
|
1268
901
|
const resolvedRole = resolveRoleOrExplain(opts, config);
|
|
1269
902
|
if ("error" in resolvedRole) {
|
|
1270
903
|
logError("--export requires --role <arn> (or MESH_AWS_ROLE env var, or a cached defaultRole).\n" +
|
|
@@ -1283,7 +916,6 @@ async function exportAwsCredentials(context, config, opts) {
|
|
|
1283
916
|
if (config.adminRole && roleArn === config.adminRole && !opts.role) {
|
|
1284
917
|
logInfo(`Caller has admin Zitadel role — assuming ${roleArn.split("/").pop()} (admin variant)`);
|
|
1285
918
|
}
|
|
1286
|
-
// Persist --role so subsequent runs can omit it.
|
|
1287
919
|
if (opts.role && opts.role !== config.defaultRole) {
|
|
1288
920
|
writeContextConfig(context, { ...config, defaultRole: opts.role });
|
|
1289
921
|
logInfo(`Saved default role for ${context}`);
|
|
@@ -1293,8 +925,6 @@ async function exportAwsCredentials(context, config, opts) {
|
|
|
1293
925
|
process.env.AWS_DEFAULT_REGION ??
|
|
1294
926
|
"us-east-2";
|
|
1295
927
|
const roleName = roleArn.split("/").pop() ?? roleArn;
|
|
1296
|
-
// --static: resolve a frozen STS triple now (the old behavior). Consumers
|
|
1297
|
-
// that need raw credentials (no `mesh` binary on refresh) opt into this.
|
|
1298
928
|
if (opts.static) {
|
|
1299
929
|
const creds = readCredentials(context);
|
|
1300
930
|
const sessionName = (creds?.email ?? "mesh-cli-export")
|
|
@@ -1316,30 +946,13 @@ async function exportAwsCredentials(context, config, opts) {
|
|
|
1316
946
|
logSuccess(`Exported static AWS credentials for ${roleName} in ${region}`);
|
|
1317
947
|
return;
|
|
1318
948
|
}
|
|
1319
|
-
// Default: manage a `[profile mesh-<context>]` block inside the user's REAL
|
|
1320
|
-
// AWS config file ($AWS_CONFIG_FILE if they set one, else ~/.aws/config) as
|
|
1321
|
-
// an idempotent marker-delimited section — the same file `aws configure sso`
|
|
1322
|
-
// manages — and export only AWS_PROFILE. The profile's `credential_process`
|
|
1323
|
-
// re-invokes `mesh login … --credential-process`, so `eval`-ed shells
|
|
1324
|
-
// auto-refresh instead of dying ~1h in. We must NOT export AWS_CONFIG_FILE
|
|
1325
|
-
// pointing at a mesh-private file: that hides ~/.aws/config from the whole
|
|
1326
|
-
// shell and breaks the user's other profiles (e.g. AWS_PROFILE=mesh-dev).
|
|
1327
949
|
const meshBin = resolveStableMeshBin(process.argv[1]);
|
|
1328
950
|
const sanitized = context.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
1329
951
|
const profileName = `mesh-${sanitized}`;
|
|
1330
|
-
// Safehouse-friendly: falls back to a mesh-managed config when ~/.aws is not
|
|
1331
|
-
// writable (and then `redirected` tells us to export AWS_CONFIG_FILE too).
|
|
1332
952
|
const { configPath, redirected } = resolveAwsConfigTarget();
|
|
1333
953
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
1334
|
-
// Read → build → atomic-write back-to-back (all sync) so the lost-update
|
|
1335
|
-
// window against concurrent writers (another `mesh login --export`, the
|
|
1336
|
-
// user's own editor, `aws configure`) is as small as possible; the temp+
|
|
1337
|
-
// rename write means concurrent READERS never observe a truncated file.
|
|
1338
954
|
const existedBefore = fs.existsSync(configPath);
|
|
1339
955
|
const existing = existedBefore ? fs.readFileSync(configPath, "utf-8") : "";
|
|
1340
|
-
// Preserve the user's existing mode (rename replaces the inode, so the
|
|
1341
|
-
// temp file's mode wins) — never chmod their config out from under them.
|
|
1342
|
-
// Restrict to 0600 only when WE create the file.
|
|
1343
956
|
const mode = existedBefore ? fs.statSync(configPath).mode & 0o777 : 0o600;
|
|
1344
957
|
atomicWriteFileSync(configPath, upsertManagedAwsConfigSection(existing, context, renderCredentialProcessProfile({
|
|
1345
958
|
profileName,
|
|
@@ -1349,28 +962,12 @@ async function exportAwsCredentials(context, config, opts) {
|
|
|
1349
962
|
meshBin,
|
|
1350
963
|
})), mode);
|
|
1351
964
|
process.stdout.write(renderExportProfileLines({ profileName, region }));
|
|
1352
|
-
// When we redirected away from an unwritable ~/.aws, the profile lives in a
|
|
1353
|
-
// mesh-managed file — export AWS_CONFIG_FILE so the AWS SDK finds it.
|
|
1354
965
|
if (redirected) {
|
|
1355
966
|
process.stdout.write(`export AWS_CONFIG_FILE=${shellSingleQuote(configPath)}\n`);
|
|
1356
967
|
}
|
|
1357
968
|
logSuccess(`Exported self-refreshing AWS profile ${profileName} for ${roleName} in ${region} ` +
|
|
1358
969
|
`(managed section in ${configPath})`);
|
|
1359
970
|
}
|
|
1360
|
-
// =============================================================================
|
|
1361
|
-
// AWS credential_process (--credential-process flag)
|
|
1362
|
-
// =============================================================================
|
|
1363
|
-
/**
|
|
1364
|
-
* Resolve AWS credentials via Zitadel JWT → AssumeRoleWithWebIdentity, then
|
|
1365
|
-
* print them as AWS `credential_process` JSON on stdout — nothing else may
|
|
1366
|
-
* reach stdout in this mode, since the AWS SDK parses it directly. All
|
|
1367
|
-
* status/error output goes to stderr.
|
|
1368
|
-
*
|
|
1369
|
-
* Unlike `--export`, this does NOT run an interactive login flow when no
|
|
1370
|
-
* session is cached (there's no terminal/browser to drive it from a child
|
|
1371
|
-
* process) — it only refreshes an existing session via its refresh token
|
|
1372
|
-
* (`getValidToken`). Callers must `mesh login <context>` at least once.
|
|
1373
|
-
*/
|
|
1374
971
|
async function credentialProcessAwsCredentials(context, config, opts) {
|
|
1375
972
|
const token = await getValidToken(context);
|
|
1376
973
|
if (!token) {
|
|
@@ -1398,21 +995,10 @@ async function credentialProcessAwsCredentials(context, config, opts) {
|
|
|
1398
995
|
process.stdout.write(toCredentialProcessJson(stsCreds) + "\n");
|
|
1399
996
|
}
|
|
1400
997
|
export { CONFIG_DIR, CONFIG_FILE, CREDENTIALS_FILE };
|
|
1401
|
-
/** Every cached context config, keyed by context name. */
|
|
1402
998
|
export function readAllContextConfigs() {
|
|
1403
999
|
return readConfig();
|
|
1404
1000
|
}
|
|
1405
|
-
/**
|
|
1406
|
-
* The discovery chain on its own, without the browser flow that follows it.
|
|
1407
|
-
*
|
|
1408
|
-
* Exported so the clean-laptop bootstrap — SSM → pinned first-party alias →
|
|
1409
|
-
* full-domain, and the anti-phishing refusal for an unknown short context — can
|
|
1410
|
-
* be exercised end-to-end against the live well-known endpoint. The `login`
|
|
1411
|
-
* command's own path is otherwise only reachable by completing an interactive
|
|
1412
|
-
* sign-in, which is why this leg had no coverage above the unit tier.
|
|
1413
|
-
*/
|
|
1414
1001
|
export async function discoverConfigForContext(context) {
|
|
1415
1002
|
return getContextConfig(context) ?? (await discoverConfigGuarded(context));
|
|
1416
1003
|
}
|
|
1417
1004
|
export { readCredentials, readAllCredentials, getContextConfig, decodeJwtPayload, getValidToken, clearCredentials };
|
|
1418
|
-
//# sourceMappingURL=login.js.map
|