UNPKG

@mesh-tech/mesh-cli

Version:

CLI for Mesh platform development utilities

2,565 lines 88.9 kB
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};

// libs/mesh-cli/src/utils/log.ts
import chalk from "chalk";
function logPrefix(opts) {
  const enabled = opts.envFlag === "1" || opts.envFlag !== "0" && !opts.isTTY;
  return enabled ? `[${opts.now.toISOString().slice(11, 19)}] ` : "";
}
function prefix() {
  return chalk.dim(
    logPrefix({
      isTTY: !!process.stderr.isTTY,
      envFlag: process.env.MESH_LOG_TIMESTAMPS,
      now: /* @__PURE__ */ new Date()
    })
  );
}
function logInfo(message) {
  console.error(prefix() + chalk.blue("\u2139"), message);
}
function logSuccess(message) {
  console.error(prefix() + chalk.green("\u2713"), message);
}
function logWarn(message) {
  console.error(prefix() + chalk.yellow("\u26A0"), message);
}
function logError(message) {
  console.error(prefix() + chalk.red("\u2717"), message);
}
var init_log = __esm({
  "libs/mesh-cli/src/utils/log.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/pulumi.ts
import { execFileSync } from "child_process";
import * as path from "path";
import * as fs from "fs";
function findAppRoot(startDir) {
  let dir = startDir;
  while (true) {
    if (fs.existsSync(path.join(dir, "Pulumi.yaml"))) return dir;
    const parent = path.dirname(dir);
    if (parent === dir) return null;
    dir = parent;
  }
}
function findStackConfigs(appRoot) {
  return fs.readdirSync(appRoot).filter((f) => /^Pulumi\..+\.yaml$/.test(f) && f !== "Pulumi.yaml").map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
}
function getCurrentStack(appRoot) {
  try {
    const result = execFileSync("pulumi", ["stack", "--show-name"], {
      encoding: "utf-8",
      cwd: appRoot,
      stdio: ["pipe", "pipe", "pipe"]
    });
    return result.trim() || null;
  } catch {
    return null;
  }
}
function readStackConfig(appRoot, stack, key) {
  const configFile = path.join(appRoot, `Pulumi.${stack}.yaml`);
  if (!fs.existsSync(configFile)) return null;
  const content = fs.readFileSync(configFile, "utf-8");
  const pattern = new RegExp(`^\\s+${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
  const match = content.match(pattern);
  if (!match) return null;
  return match[1].trim().replace(/^["']|["']$/g, "");
}
function pulumiStackOutput(appRoot, key, extraArgs, env) {
  const execEnv = env ? { ...process.env, ...env } : void 0;
  try {
    const result = execFileSync(
      "pulumi",
      ["stack", "output", key, "--json", ...extraArgs],
      { cwd: appRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: execEnv }
    );
    if (result.includes('"[secret]"')) {
      return execFileSync(
        "pulumi",
        ["stack", "output", key, "--json", "--show-secrets", ...extraArgs],
        { cwd: appRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: execEnv }
      );
    }
    return result;
  } catch (err) {
    const errMsg = err?.stderr ?? "";
    if (errMsg.includes("kms:") || errMsg.includes("KMS") || errMsg.includes("secrets manager")) {
      throw err;
    }
    return execFileSync(
      "pulumi",
      ["stack", "output", key, "--json", "--show-secrets", ...extraArgs],
      { cwd: appRoot, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: execEnv }
    );
  }
}
var init_pulumi = __esm({
  "libs/mesh-cli/src/utils/pulumi.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/auth-preflight.ts
function isExpiredAwsTokenMessage(message) {
  return /expired|invalid.*(security )?token|token.*(is )?invalid|no credential|could not load credentials|could not be found|sso session/i.test(
    message
  );
}
var SSO_LOGIN_FIX;
var init_auth_preflight = __esm({
  "libs/mesh-cli/src/utils/auth-preflight.ts"() {
    "use strict";
    SSO_LOGIN_FIX = "aws sso login --sso-session=mesh   # or: pnpm sso";
  }
});

// libs/mesh-cli/src/utils/errors.ts
import chalk2 from "chalk";
var init_errors = __esm({
  "libs/mesh-cli/src/utils/errors.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/first-party-contexts.ts
function firstPartyDomainFor(context) {
  if (!Object.hasOwn(FIRST_PARTY_CONTEXTS, context)) return null;
  const domain = FIRST_PARTY_CONTEXTS[context];
  return typeof domain === "string" && domain ? domain : null;
}
var FIRST_PARTY_CONTEXTS;
var init_first_party_contexts = __esm({
  "libs/mesh-cli/src/utils/first-party-contexts.ts"() {
    "use strict";
    FIRST_PARTY_CONTEXTS = Object.freeze({
      // Verified 2026-08-14: https://cli.dev.platform.meshtech.io/.well-known/mesh.json
      // answers with the platform's issuer + CLI client id + registryBroker,
      // anonymously. (Repointed from dev.trabian.meshtech.io — the trabian tenant
      // platform lives in a different AWS account with no registry; the
      // platform-dev stack in the registry-owning account is the developer front
      // door. mesh-sandbox#32 / #3587.)
      "mesh.dev": "dev.platform.meshtech.io"
    });
  }
});

// libs/mesh-cli/src/utils/context.ts
var init_context = __esm({
  "libs/mesh-cli/src/utils/context.ts"() {
    "use strict";
    init_log();
  }
});

// libs/mesh-cli/src/utils/bastion.ts
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
var init_bastion = __esm({
  "libs/mesh-cli/src/utils/bastion.ts"() {
    "use strict";
    init_log();
  }
});

// libs/mesh-cli/src/utils/credentials.ts
import {
  SecretsManagerClient,
  GetSecretValueCommand
} from "@aws-sdk/client-secrets-manager";
var init_credentials = __esm({
  "libs/mesh-cli/src/utils/credentials.ts"() {
    "use strict";
    init_log();
  }
});

// libs/mesh-cli/src/utils/vpn.ts
var init_vpn = __esm({
  "libs/mesh-cli/src/utils/vpn.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/index.ts
var init_utils = __esm({
  "libs/mesh-cli/src/utils/index.ts"() {
    "use strict";
    init_errors();
    init_log();
    init_context();
    init_bastion();
    init_credentials();
    init_pulumi();
    init_vpn();
  }
});

// libs/mesh-cli/src/utils/pid.ts
var init_pid = __esm({
  "libs/mesh-cli/src/utils/pid.ts"() {
    "use strict";
    init_errors();
  }
});

// libs/mesh-cli/src/utils/vpn-join.ts
var init_vpn_join = __esm({
  "libs/mesh-cli/src/utils/vpn-join.ts"() {
    "use strict";
    init_login();
  }
});

// libs/mesh-cli/src/utils/pulumi-run.ts
var init_pulumi_run = __esm({
  "libs/mesh-cli/src/utils/pulumi-run.ts"() {
    "use strict";
    init_log();
    init_pulumi();
    init_aws_auth();
    init_login();
  }
});

// libs/mesh-cli/src/utils/kubeconfig.ts
var init_kubeconfig = __esm({
  "libs/mesh-cli/src/utils/kubeconfig.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/temporal-auth.ts
import { execFileSync as execFileSync2 } from "node:child_process";
async function resolveTemporalAuth(tenant, env, platformName = tenant) {
  const { SSMClient: SSMClient2, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
  const ssm = new SSMClient2({ region: process.env.AWS_REGION || "us-east-2" });
  const results = {};
  async function trySSM(name) {
    try {
      const resp = await ssm.send(new GetParameterCommand2({ Name: name, WithDecryption: true }));
      return resp.Parameter?.Value ?? void 0;
    } catch {
      return void 0;
    }
  }
  try {
    const workerPath = `/mesh-platform/${tenant}/${env}/temporal/worker-service-account-key`;
    const exchangePath = `/mesh-platform/${tenant}/${env}/temporal/token-exchange-app`;
    const workerJson = await trySSM(workerPath);
    if (workerJson) {
      const worker = JSON.parse(workerJson);
      results.TEMPORAL_AUTH_CLIENT_ID = worker.clientId;
      results.TEMPORAL_AUTH_CLIENT_SECRET = worker.clientSecret;
    }
    const exchangeJson = await trySSM(exchangePath);
    if (exchangeJson) {
      const exchange = JSON.parse(exchangeJson);
      results.ZITADEL_TOKEN_EXCHANGE_CLIENT_ID = exchange.clientId;
      results.ZITADEL_TOKEN_EXCHANGE_CLIENT_SECRET = exchange.clientSecret;
    }
    const platformJson = await trySSM(`/mesh-platform/${platformName}/${env}/platform/zitadel`);
    if (platformJson) {
      const platform = JSON.parse(platformJson);
      if (platform.endpoint) results.ZITADEL_ISSUER = platform.endpoint;
      if (platform.projectId) results.ZITADEL_PROJECT_ID = platform.projectId;
    }
    if (!results.TEMPORAL_AUTH_CLIENT_ID) {
      const legacyClientId = await trySSM("/platform/temporal/auth/CLIENT_ID");
      const legacyClientSecret = await trySSM("/platform/temporal/auth/CLIENT_SECRET");
      if (legacyClientId && legacyClientSecret) {
        results.TEMPORAL_AUTH_CLIENT_ID = legacyClientId;
        results.TEMPORAL_AUTH_CLIENT_SECRET = legacyClientSecret;
      }
    }
    if (!results.ZITADEL_ISSUER) {
      try {
        const podEnv = execFileSync2(
          "kubectl",
          [
            "get",
            "pods",
            "-n",
            `temporal-${env}`,
            "-l",
            "app.kubernetes.io/component=frontend",
            "-o",
            "jsonpath={.items[0].spec.containers[0].env}"
          ],
          { encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
        ).trim();
        if (podEnv) {
          const envVars = JSON.parse(podEnv);
          for (const v of envVars) {
            if (v.name === "ZITADEL_ISSUER_URL" && v.value && !results.ZITADEL_ISSUER) {
              results.ZITADEL_ISSUER = v.value;
            }
            if (v.name === "ZITADEL_PROJECT_ID" && v.value && !results.ZITADEL_PROJECT_ID) {
              results.ZITADEL_PROJECT_ID = v.value;
            }
          }
        }
      } catch (err) {
        const detail = err instanceof Error ? err.message : String(err);
        const stderr = String(err?.stderr ?? "");
        if (/\.kube[/\\]config|error loading config file/i.test(`${detail} ${stderr}`)) {
          logWarn(
            "kubectl could not read a kubeconfig and fell back to ~/.kube/config (macOS blocks it for shells without Full Disk Access). Run inside the mesh dev tmux session, or export KUBECONFIG=$TMPDIR/mesh-dev-sessions/<session>.kubeconfig."
          );
        }
      }
    }
    if (results.TEMPORAL_AUTH_CLIENT_ID && results.ZITADEL_ISSUER) {
      logSuccess(`Temporal auth resolved (client: ${results.TEMPORAL_AUTH_CLIENT_ID})`);
    } else if (Object.keys(results).length > 0) {
      logWarn("Partial Temporal auth resolved (some credentials missing)");
    }
  } catch (err) {
    logWarn(
      `Could not resolve Temporal auth from SSM: ${err instanceof Error ? err.message : String(err)}`
    );
  }
  return results;
}
async function acquireTemporalBearer(vars = {}) {
  const issuerUrl = vars.ZITADEL_ISSUER ?? process.env.ZITADEL_ISSUER;
  const projectId = vars.ZITADEL_PROJECT_ID ?? process.env.ZITADEL_PROJECT_ID;
  const clientId = vars.TEMPORAL_AUTH_CLIENT_ID ?? process.env.TEMPORAL_AUTH_CLIENT_ID;
  const clientSecret = vars.TEMPORAL_AUTH_CLIENT_SECRET ?? process.env.TEMPORAL_AUTH_CLIENT_SECRET;
  if (!issuerUrl || !clientId || !clientSecret) {
    return void 0;
  }
  const scopes = ["openid"];
  if (projectId) {
    scopes.push(`urn:zitadel:iam:org:project:id:${projectId}:aud`);
    if (process.env.TEMPORAL_M2M_SKIP_ROLES_SCOPE !== "true") {
      scopes.push("urn:zitadel:iam:org:projects:roles");
    }
  }
  const response = await fetch(`${issuerUrl}/oauth/v2/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: clientId,
      client_secret: clientSecret,
      scope: scopes.join(" ")
    })
  });
  if (!response.ok) {
    const text = await response.text();
    throw new Error(
      `Temporal token acquisition failed: ${response.status} ${response.statusText} \u2014 ${text}`
    );
  }
  const data = await response.json();
  return data.access_token;
}
var init_temporal_auth = __esm({
  "libs/mesh-cli/src/utils/temporal-auth.ts"() {
    "use strict";
    init_log();
  }
});

// libs/mesh-cli/src/utils/reachability.ts
var init_reachability = __esm({
  "libs/mesh-cli/src/utils/reachability.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/workflow-fingerprint.ts
var init_workflow_fingerprint = __esm({
  "libs/mesh-cli/src/utils/workflow-fingerprint.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/dev-launch.ts
var init_dev_launch = __esm({
  "libs/mesh-cli/src/commands/dev-launch.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/local/seed.ts
var LOCAL_TENANT, LOCAL_ENV, APP_TENANTS_PARAM, DATA_BUCKET_SEEDS, FABRIC_CHECK_PATH, TEMPORAL_NAMESPACE;
var init_seed = __esm({
  "libs/mesh-cli/src/commands/local/seed.ts"() {
    "use strict";
    init_log();
    init_errors();
    LOCAL_TENANT = "local";
    LOCAL_ENV = "dev";
    APP_TENANTS_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/app-tenants`;
    DATA_BUCKET_SEEDS = [
      {
        key: "samples/pipes/fragment-0001.json",
        body: JSON.stringify(
          {
            id: "sample-0001",
            kind: "pipes-fragment",
            account: "0001",
            asOf: "2026-01-01",
            lines: [{ amount: "125.00", currency: "USD", description: "Sample credit" }]
          },
          null,
          2
        )
      }
    ];
    FABRIC_CHECK_PATH = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/.fabric-check`;
    TEMPORAL_NAMESPACE = `${LOCAL_TENANT}-${LOCAL_ENV}`;
  }
});

// libs/mesh-cli/src/commands/local/helpers.ts
var init_helpers = __esm({
  "libs/mesh-cli/src/commands/local/helpers.ts"() {
    "use strict";
    init_seed();
  }
});

// libs/mesh-cli/src/utils/cache-home.ts
var init_cache_home = __esm({
  "libs/mesh-cli/src/utils/cache-home.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/local/stack.ts
import { parse as parseYaml } from "yaml";
var DEFAULT_HUB_PORT;
var init_stack = __esm({
  "libs/mesh-cli/src/commands/local/stack.ts"() {
    "use strict";
    init_helpers();
    init_cache_home();
    init_errors();
    init_log();
    DEFAULT_HUB_PORT = "9000";
  }
});

// libs/mesh-cli/src/commands/local/mocks.ts
var init_mocks = __esm({
  "libs/mesh-cli/src/commands/local/mocks.ts"() {
    "use strict";
    init_log();
    init_errors();
    init_seed();
    init_helpers();
    init_stack();
  }
});

// libs/api-registry/src/hub-roles.ts
function hubOperatorRoleKeys(tenants) {
  const cleaned = [...new Set(tenants.filter((t) => t !== ""))].sort();
  for (const tenant of cleaned) {
    for (const sep of [HUB_ROLE_KEY_SEPARATOR, HUB_ROLE_APP_SEPARATOR]) {
      if (tenant.includes(sep)) {
        throw new Error(
          `hubOperatorRoleKeys: tenant name "${tenant}" contains the role-key separator "${sep}" \u2014 it would parse as a different tenant's or app's grant`
        );
      }
    }
  }
  return [
    ...HUB_BASE_ROLES,
    ...cleaned.flatMap(
      (tenant) => HUB_RESTRICTED_ROLES.map((role) => `${role}${HUB_ROLE_KEY_SEPARATOR}${tenant}`)
    )
  ];
}
var HUB_STAFF_ROLES, HUB_RESTRICTED_ROLES, HUB_BASE_ROLES, HUB_ROLE_KEY_SEPARATOR, HUB_ROLE_APP_SEPARATOR, RESTRICTED;
var init_hub_roles = __esm({
  "libs/api-registry/src/hub-roles.ts"() {
    "use strict";
    HUB_STAFF_ROLES = ["ops", "admin"];
    HUB_RESTRICTED_ROLES = ["developer", "auditor", "vendor"];
    HUB_BASE_ROLES = [
      ...HUB_STAFF_ROLES,
      ...HUB_RESTRICTED_ROLES
    ];
    HUB_ROLE_KEY_SEPARATOR = ":";
    HUB_ROLE_APP_SEPARATOR = "/";
    RESTRICTED = new Set(HUB_RESTRICTED_ROLES);
  }
});

// libs/api-registry/src/index.ts
import { z } from "zod";
var rateLimitSpecSchema, rateLimitDefaultsSchema, integrationHealthSchema, apiSurfaceKindSchema, apiSurfaceSchema, apiRegistryEntrySchema, integrationStatusSchema;
var init_src = __esm({
  "libs/api-registry/src/index.ts"() {
    "use strict";
    init_hub_roles();
    rateLimitSpecSchema = z.object({
      rps: z.number().int().min(1),
      burst: z.number().int().min(1)
    });
    rateLimitDefaultsSchema = z.record(z.string().min(1), rateLimitSpecSchema);
    integrationHealthSchema = z.union([
      z.object({ op: z.string().trim().min(1) }),
      z.object({ unavailable: z.string().trim().min(1) })
    ]);
    apiSurfaceKindSchema = z.enum(["app", "vendor"]);
    apiSurfaceSchema = z.object({
      http: z.object({ url: z.string().min(1) }).optional(),
      nexus: z.object({ endpoint: z.string().min(1), taskQueue: z.string().min(1) }).optional()
    });
    apiRegistryEntrySchema = z.object({
      schemaVersion: z.number().int().positive().default(1),
      name: z.string().min(1),
      kind: apiSurfaceKindSchema.optional(),
      provider: z.string().optional(),
      version: z.string().optional(),
      title: z.string().optional(),
      description: z.string().optional(),
      surfaces: apiSurfaceSchema,
      credentials: z.object({ keyedBy: z.string().nullish() }).optional(),
      docs: z.object({
        url: z.string().min(1),
        contentHash: z.string().min(1),
        siteUrl: z.string().min(1).optional(),
        siteInternalUrl: z.string().min(1).optional()
      }),
      enabledOps: z.array(z.string()),
      // The consumer schema strips unknown keys, so a producer-side field that is
      // not mirrored here never reaches a consumer — mirror every addition.
      status: z.object({ url: z.string().min(1) }).optional(),
      appVersion: z.string().min(1).optional(),
      rateLimits: rateLimitDefaultsSchema.optional(),
      health: integrationHealthSchema.optional(),
      producedRateClasses: z.array(z.string().min(1)).optional()
    });
    integrationStatusSchema = z.object({
      contract: z.literal("v1"),
      name: z.string().min(1),
      provider: z.string().optional(),
      type: z.string().optional(),
      title: z.string().optional(),
      definitionVersion: z.string().optional(),
      packageVersion: z.string().min(1).optional(),
      tenant: z.string().min(1),
      instanceKey: z.string().optional(),
      surfaces: z.array(z.enum(["http", "nexus"])),
      coreMode: z.enum(["mock", "live"]),
      coreModeSource: z.string().optional(),
      enabledOps: z.array(z.string()),
      credentialsWired: z.boolean(),
      rateLimits: rateLimitDefaultsSchema.optional(),
      health: integrationHealthSchema.optional(),
      startedAt: z.string().min(1)
    });
  }
});

// libs/mesh-cli/src/commands/local/seed-zitadel.ts
var HUB_DEFAULT_REDIRECT_URI, HUB_ROLES, ZITADEL_SSM_PARAM, TEST_USERS_SSM_PREFIX;
var init_seed_zitadel = __esm({
  "libs/mesh-cli/src/commands/local/seed-zitadel.ts"() {
    "use strict";
    init_src();
    init_log();
    init_errors();
    init_login();
    init_stack();
    init_seed();
    init_cache_home();
    HUB_DEFAULT_REDIRECT_URI = `http://localhost:${DEFAULT_HUB_PORT}/oauth2/callback`;
    HUB_ROLES = hubOperatorRoleKeys([LOCAL_TENANT]);
    ZITADEL_SSM_PARAM = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/platform/zitadel`;
    TEST_USERS_SSM_PREFIX = `/mesh-platform/${LOCAL_TENANT}/${LOCAL_ENV}/temporal/test-users`;
  }
});

// libs/mesh-cli/src/commands/local/auth-provision.ts
var init_auth_provision = __esm({
  "libs/mesh-cli/src/commands/local/auth-provision.ts"() {
    "use strict";
    init_log();
    init_seed();
    init_seed_zitadel();
    init_helpers();
  }
});

// libs/mesh-cli/src/utils/mesh-json.ts
var init_mesh_json = __esm({
  "libs/mesh-cli/src/utils/mesh-json.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/local/dev-local.ts
var init_dev_local = __esm({
  "libs/mesh-cli/src/commands/local/dev-local.ts"() {
    "use strict";
    init_mocks();
    init_helpers();
    init_errors();
    init_seed();
    init_seed();
    init_seed_zitadel();
    init_auth_provision();
    init_stack();
    init_login();
    init_mesh_json();
  }
});

// libs/mesh-cli/src/commands/local/docker-runner.ts
var init_docker_runner = __esm({
  "libs/mesh-cli/src/commands/local/docker-runner.ts"() {
    "use strict";
    init_dev_local();
    init_cache_home();
  }
});

// libs/mesh-cli/src/utils/stack-flag.ts
var init_stack_flag = __esm({
  "libs/mesh-cli/src/utils/stack-flag.ts"() {
    "use strict";
    init_log();
  }
});

// libs/mesh-cli/src/commands/peer-addressing.ts
var init_peer_addressing = __esm({
  "libs/mesh-cli/src/commands/peer-addressing.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/dev-doctor.ts
var init_dev_doctor = __esm({
  "libs/mesh-cli/src/commands/dev-doctor.ts"() {
    "use strict";
    init_dev();
    init_auth_preflight();
    init_login();
  }
});

// libs/mesh-cli/src/utils/worktree-identity.ts
var init_worktree_identity = __esm({
  "libs/mesh-cli/src/utils/worktree-identity.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/dev.ts
import { Option } from "commander";
import { SecretsManagerClient as SecretsManagerClient2, GetSecretValueCommand as GetSecretValueCommand2 } from "@aws-sdk/client-secrets-manager";
var init_dev = __esm({
  "libs/mesh-cli/src/commands/dev.ts"() {
    "use strict";
    init_utils();
    init_aws_auth();
    init_login();
    init_vpn_join();
    init_pulumi_run();
    init_tailscale();
    init_pulumi();
    init_kubeconfig();
    init_temporal_auth();
    init_reachability();
    init_workflow_fingerprint();
    init_dev_launch();
    init_dev_local();
    init_auth_provision();
    init_seed();
    init_mocks();
    init_dev_local();
    init_stack();
    init_docker_runner();
    init_errors();
    init_stack_flag();
    init_peer_addressing();
    init_dev_doctor();
    init_worktree_identity();
  }
});

// libs/mesh-cli/src/utils/tailscale-targets.ts
var init_tailscale_targets = __esm({
  "libs/mesh-cli/src/utils/tailscale-targets.ts"() {
    "use strict";
    init_dev();
    init_login();
  }
});

// libs/mesh-cli/src/utils/socks-forward.ts
var init_socks_forward = __esm({
  "libs/mesh-cli/src/utils/socks-forward.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/tunnel-ownership.ts
var init_tunnel_ownership = __esm({
  "libs/mesh-cli/src/utils/tunnel-ownership.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/utils/tailscale.ts
var init_tailscale = __esm({
  "libs/mesh-cli/src/utils/tailscale.ts"() {
    "use strict";
    init_login();
    init_aws_auth();
    init_log();
    init_errors();
    init_pid();
    init_bastion();
    init_tailscale_targets();
    init_socks_forward();
    init_tunnel_ownership();
  }
});

// libs/mesh-cli/src/commands/vpn/tunnel.ts
var init_tunnel = __esm({
  "libs/mesh-cli/src/commands/vpn/tunnel.ts"() {
    "use strict";
    init_log();
    init_login();
    init_tailscale();
  }
});

// libs/mesh-cli/src/commands/vpn/index.ts
import { execFileSync as execFileSync3 } from "child_process";
function tailscaleStatus() {
  try {
    const json = execFileSync3("tailscale", ["status", "--json"], {
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"]
    });
    return JSON.parse(json);
  } catch {
    return null;
  }
}
function tailscaleBackendState() {
  return tailscaleStatus()?.BackendState ?? null;
}
var init_vpn2 = __esm({
  "libs/mesh-cli/src/commands/vpn/index.ts"() {
    "use strict";
    init_utils();
    init_login();
    init_tunnel();
    init_tailscale();
  }
});

// libs/mesh-cli/src/commands/login.ts
import * as http from "http";
import * as crypto from "crypto";
import * as fs2 from "fs";
import * as path2 from "path";
import { execFileSync as execFileSync4 } from "child_process";
function readConfig() {
  if (!fs2.existsSync(CONFIG_FILE)) return {};
  try {
    return JSON.parse(fs2.readFileSync(CONFIG_FILE, "utf-8"));
  } catch {
    return {};
  }
}
function writeContextConfig(context, config) {
  const existing = readConfig();
  existing[context] = config;
  fs2.mkdirSync(CONFIG_DIR, { recursive: true });
  fs2.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
}
function getContextConfig(context) {
  const config = readConfig();
  return config[context] ?? null;
}
function parseTenantEnv(context) {
  const parts = context.split(".");
  if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
  return { tenant: parts[0], env: parts[1] };
}
async function discoverConfigFromSsm(context) {
  const parsed = parseTenantEnv(context);
  if (!parsed) return null;
  const { tenant, env } = parsed;
  const ssmPath = `/mesh-platform/${tenant}/${env}/platform/zitadel`;
  logInfo(`Attempting SSM discovery from ${ssmPath}...`);
  try {
    const { SSMClient: SSMClient2, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
    const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
    const ssm = new SSMClient2({ region });
    const resp = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
    const raw = resp.Parameter?.Value;
    if (!raw) {
      logWarn(`SSM parameter ${ssmPath} has no value`);
      return null;
    }
    const data = JSON.parse(raw);
    const issuer = data.endpoint;
    const clientId = data.cliClientId;
    if (typeof issuer !== "string" || !issuer) {
      logWarn("SSM zitadel entry missing 'endpoint' field");
      return null;
    }
    if (typeof clientId !== "string" || !clientId) {
      logWarn(
        "SSM zitadel entry missing 'cliClientId' field.\n  The platform needs to be deployed with an updated ZitadelPlatformIdentity\n  that exports cliClientId. Until then, contact your platform admin."
      );
      return null;
    }
    const config = { issuer, clientId };
    if (typeof data.vpn === "string") config.vpn = data.vpn;
    if (typeof data.vpnJoinBroker === "string") config.vpnJoinBroker = data.vpnJoinBroker;
    if (typeof data.registryBroker === "string") config.registryBroker = data.registryBroker;
    writeContextConfig(context, config);
    logSuccess(`Discovered platform configuration for ${context} via SSM`);
    return config;
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    if (message.includes("ExpiredToken") || message.includes("credentials")) {
      logWarn(`SSM discovery failed: AWS credentials expired or unavailable.`);
      if (firstPartyDomainFor(context) || context.split(".").length - 1 >= 2) {
        logInfo(`  Falling back to anonymous HTTPS discovery \u2014 no AWS needed.`);
      } else {
        logInfo(`  If you have an AWS account: aws sso login --profile <profile>`);
        logInfo(`  Otherwise use the platform's full domain, e.g. mesh login dev.<tenant>.meshtech.io`);
      }
    } else if (message.includes("ParameterNotFound")) {
      logWarn(`SSM parameter not found: ${ssmPath}`);
      logInfo("  This platform context may not be deployed.");
    } else {
      logWarn(`SSM discovery failed: ${message}`);
    }
    return null;
  }
}
async function discoverConfig(domain, contextKey = domain, opts = {}) {
  const url = `https://cli.${domain}/.well-known/mesh.json`;
  if (!opts.quiet) logInfo(`Attempting discovery from ${url}...`);
  try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 5e3);
    const resp = await fetch(url, { signal: controller.signal });
    clearTimeout(timeout);
    if (!resp.ok) {
      logWarn(`Discovery endpoint returned ${resp.status}`);
      return null;
    }
    const data = await resp.json();
    const issuer = data.issuer;
    const clientId = data.clientId;
    if (typeof issuer !== "string" || !issuer || typeof clientId !== "string" || !clientId) {
      logWarn("Discovery endpoint returned invalid config (missing issuer or clientId)");
      return null;
    }
    const config = { issuer, clientId };
    if (typeof data.vpn === "string") config.vpn = data.vpn;
    if (typeof data.vpnJoinBroker === "string") config.vpnJoinBroker = data.vpnJoinBroker;
    if (typeof data.registryBroker === "string") config.registryBroker = data.registryBroker;
    if (!opts.quiet) {
      writeContextConfig(contextKey, config);
      logSuccess(`Discovered platform configuration for ${contextKey}`);
    }
    return config;
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    if (message.includes("abort")) {
      logWarn("Discovery timed out");
    } else {
      logWarn(`Discovery failed: ${message}`);
    }
    return null;
  }
}
function readAllCredentials() {
  if (!fs2.existsSync(CREDENTIALS_FILE)) return {};
  try {
    return JSON.parse(fs2.readFileSync(CREDENTIALS_FILE, "utf-8"));
  } catch {
    return {};
  }
}
function readCredentials(context) {
  return readAllCredentials()[context] ?? null;
}
function atomicWriteFileSync(path4, data, mode) {
  const tmpPath = `${path4}.${process.pid}.${atomicWriteCounter++}.tmp`;
  try {
    fs2.writeFileSync(tmpPath, data, { mode });
    fs2.renameSync(tmpPath, path4);
  } catch (err) {
    try {
      fs2.unlinkSync(tmpPath);
    } catch {
    }
    throw err;
  }
}
function writeCredentials(context, creds) {
  fs2.mkdirSync(CONFIG_DIR, { recursive: true });
  const all = readAllCredentials();
  all[context] = creds;
  atomicWriteFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), 384);
}
function base64url(buffer) {
  return buffer.toString("base64url");
}
function generateCodeVerifier() {
  return base64url(crypto.randomBytes(32));
}
function generateCodeChallenge(verifier) {
  return base64url(crypto.createHash("sha256").update(verifier).digest());
}
function decodeJwtPayload(token) {
  const parts = token.split(".");
  if (parts.length !== 3) throw new Error("Invalid JWT");
  return JSON.parse(Buffer.from(parts[1], "base64url").toString());
}
async function exchangeCode(issuer, clientId, code, codeVerifier) {
  const body = new URLSearchParams({
    grant_type: "authorization_code",
    code,
    redirect_uri: REDIRECT_URI,
    client_id: clientId,
    code_verifier: codeVerifier
  });
  const resp = await fetch(`${issuer}/oauth/v2/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: body.toString()
  });
  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`Token exchange failed (${resp.status}): ${text}`);
  }
  return resp.json();
}
async function refreshTokens(issuer, clientId, refreshToken) {
  const body = new URLSearchParams({
    grant_type: "refresh_token",
    refresh_token: refreshToken,
    client_id: clientId
  });
  const resp = await fetch(`${issuer}/oauth/v2/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: body.toString()
  });
  if (!resp.ok) {
    throw new Error(`Token refresh failed (${resp.status})`);
  }
  return resp.json();
}
function login(context, config) {
  return new Promise((resolve3, reject) => {
    const codeVerifier = generateCodeVerifier();
    const codeChallenge = generateCodeChallenge(codeVerifier);
    const state = base64url(crypto.randomBytes(16));
    const authUrl = new URL(`${config.issuer}/oauth/v2/authorize`);
    authUrl.searchParams.set("client_id", config.clientId);
    authUrl.searchParams.set("redirect_uri", REDIRECT_URI);
    authUrl.searchParams.set("response_type", "code");
    authUrl.searchParams.set("scope", SCOPES);
    authUrl.searchParams.set("code_challenge", codeChallenge);
    authUrl.searchParams.set("code_challenge_method", "S256");
    authUrl.searchParams.set("state", state);
    let timeoutId;
    const server = http.createServer(async (req, res) => {
      try {
        const url = new URL(req.url ?? "/", `http://localhost:${REDIRECT_PORT}`);
        if (url.pathname !== "/callback") {
          res.writeHead(404);
          res.end("Not found");
          return;
        }
        const error = url.searchParams.get("error");
        if (error) {
          const desc = url.searchParams.get("error_description") ?? error;
          const safeDesc = desc.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
          res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
          res.end(
            `<html><body><h2>Login failed</h2><p>${safeDesc}</p></body></html>`
          );
          teardown();
          reject(new Error(desc));
          return;
        }
        const returnedState = url.searchParams.get("state");
        if (returnedState !== state) {
          res.writeHead(400);
          res.end("State mismatch");
          teardown();
          reject(new Error("State mismatch"));
          return;
        }
        const code = url.searchParams.get("code");
        if (!code) {
          res.writeHead(400);
          res.end("No code");
          teardown();
          reject(new Error("No authorization code received"));
          return;
        }
        const tokens = await exchangeCode(
          config.issuer,
          config.clientId,
          code,
          codeVerifier
        );
        const idPayload = decodeJwtPayload(tokens.id_token);
        const email = idPayload.email ?? idPayload.preferred_username ?? "unknown";
        const sub = idPayload.sub;
        let tenants = [];
        try {
          const accessPayload = decodeJwtPayload(tokens.access_token);
          tenants = accessPayload["urn:mesh:tenants"] ?? [];
        } catch {
        }
        writeCredentials(context, {
          idToken: tokens.id_token,
          accessToken: tokens.access_token,
          refreshToken: tokens.refresh_token,
          expiresAt: new Date(
            Date.now() + tokens.expires_in * 1e3
          ).toISOString(),
          email,
          sub
        });
        res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
        res.end(
          `<html><body><h2>&#x2705; Logged in to ${context}!</h2><p>You can close this tab.</p></body></html>`
        );
        logSuccess(`Logged in as ${email} (${context})`);
        if (tenants.length > 0) {
          logInfo(`Deployable tenants: ${tenants.join(", ")}`);
        }
        hintVpnIfDisconnected(context);
        teardown();
        resolve3();
      } catch (err) {
        teardown();
        reject(err);
      }
    });
    const teardown = () => {
      if (timeoutId) clearTimeout(timeoutId);
      server.closeAllConnections?.();
      server.close();
    };
    server.listen(REDIRECT_PORT, () => {
      logInfo(`Opening browser for authentication (${context})...`);
      const url = authUrl.toString();
      try {
        if (process.platform === "darwin") {
          execFileSync4("open", [url], { stdio: "ignore" });
        } else if (process.platform === "linux") {
          execFileSync4("xdg-open", [url], { stdio: "ignore" });
        } else {
          logInfo(`Open this URL in your browser:
${url}`);
        }
      } catch {
        logInfo(`Open this URL in your browser:
${url}`);
      }
    });
    timeoutId = setTimeout(() => {
      teardown();
      reject(new Error(loginTimeoutMessage(context)));
    }, 12e4);
  });
}
async function deviceLoginWithReissue(attempt, maxCodes = MAX_DEVICE_CODES) {
  for (let n = 1; n <= maxCodes; n++) {
    if (n > 1) logWarn(`That code expired \u2014 issuing a fresh one (${n}/${maxCodes})\u2026`);
    if (await attempt(n) === "success") return;
  }
  throw new Error(
    `Device login not completed after ${maxCodes} codes. Re-run the command when you're ready to authorize.`
  );
}
async function deviceCodeLogin(context, config) {
  await deviceLoginWithReissue(() => attemptDeviceCode(context, config));
}
async function attemptDeviceCode(context, config) {
  const resp = await fetch(`${config.issuer}/oauth/v2/device_authorization`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      client_id: config.clientId,
      scope: SCOPES
    }).toString()
  });
  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`Device authorization request failed (${resp.status}): ${text}`);
  }
  const deviceAuth = await resp.json();
  const openUrl = deviceAuth.verification_uri_complete ?? deviceAuth.verification_uri;
  console.log();
  logInfo(`Open this URL in your browser:
`);
  logInfo(`  ${openUrl}
`);
  logInfo(`Code: ${deviceAuth.user_code}`);
  console.log();
  logInfo("Waiting for authorization...");
  try {
    if (process.platform === "darwin") {
      execFileSync4("open", [openUrl], { stdio: "ignore" });
    } else if (process.platform === "linux") {
      execFileSync4("xdg-open", [openUrl], { stdio: "ignore" });
    }
  } catch {
  }
  const deadline = Date.now() + deviceAuth.expires_in * 1e3;
  let interval = deviceAuth.interval * 1e3;
  while (Date.now() < deadline) {
    await new Promise((r) => setTimeout(r, interval));
    const tokenResp = await fetch(`${config.issuer}/oauth/v2/token`, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        client_id: config.clientId,
        grant_type: "urn:ietf:params:oauth:grant-type:device_code",
        device_code: deviceAuth.device_code
      }).toString()
    });
    if (tokenResp.ok) {
      const tokens = await tokenResp.json();
      const idPayload = decodeJwtPayload(tokens.id_token);
      const email = idPayload.email ?? idPayload.preferred_username ?? "unknown";
      const sub = idPayload.sub;
      let tenants = [];
      try {
        const accessPayload = decodeJwtPayload(tokens.access_token);
        tenants = accessPayload["urn:mesh:tenants"] ?? [];
      } catch {
      }
      writeCredentials(context, {
        idToken: tokens.id_token,
        accessToken: tokens.access_token,
        refreshToken: tokens.refresh_token,
        expiresAt: new Date(Date.now() + tokens.expires_in * 1e3).toISOString(),
        email,
        sub
      });
      logSuccess(`Logged in as ${email} (${context})`);
      if (tenants.length > 0) {
        logInfo(`Deployable tenants: ${tenants.join(", ")}`);
      }
      hintVpnIfDisconnected(context);
      return "success";
    }
    const error = await tokenResp.json();
    switch (error.error) {
      case "authorization_pending":
        continue;
      case "slow_down":
        interval += 1e3;
        continue;
      case "expired_token":
        return "expired";
      // caller re-issues a fresh code
      case "access_denied":
        throw new Error("Authorization denied by user.");
      default:
        throw new Error(`Token exchange failed: ${error.error} \u2014 ${error.error_description ?? ""}`);
    }
  }
  return "expired";
}
function loginTimeoutMessage(context) {
  const base = "Login timed out (2 minutes)";
  if (context !== "local") return base;
  return `${base}. The local platform already has a signed-up user \u2014 sign in as dev@local.mesh / LocalDev1! instead of registering. If you did register, its confirmation email is in the local mailbox at http://localhost:8025; confirm it there and run the command again.`;
}
function isRemoteEnvironment() {
  if (process.env.REMOTE_CONTAINERS || process.env.CODESPACES) return true;
  if (fs2.existsSync("/.dockerenv")) return true;
  if (process.env.SSH_CLIENT || process.env.SSH_TTY) return true;
  if (!process.stdout.isTTY || !process.stdin.isTTY) return true;
  return false;
}
function hintVpnIfDisconnected(context) {
  if (context === RESERVED_REGISTRY_CONTEXT) return;
  try {
    const state = tailscaleBackendState();
    if (state !== "Running") {
      console.log("");
      logInfo(`VPN not connected. To access dev services, run:`);
      logInfo(`  mesh vpn connect ${context}`);
    }
  } catch {
  }
}
function tokenStillValid(expiresAt, marginMs = 0, now = Date.now()) {
  return new Date(expiresAt).getTime() - marginMs > now;
}
async function getValidToken(context, opts = {}) {
  const config = getContextConfig(context);
  if (!config) return null;
  const creds = readCredentials(context);
  if (!creds) return null;
  if (tokenStillValid(creds.expiresAt, opts.marginMs ?? 0)) {
    return creds.idToken;
  }
  return remintToken(context, config, creds);
}
async function remintToken(context, config, creds) {
  if (!creds.refreshToken) return null;
  let tokens;
  try {
    tokens = await refreshTokens(config.issuer, config.clientId, creds.refreshToken);
  } catch {
    return null;
  }
  let email = creds.email;
  try {
    email = decodeJwtPayload(tokens.id_token).email ?? creds.email;
  } catch {
  }
  try {
    writeCredentials(context, {
      ...creds,
      idToken: tokens.id_token,
      accessToken: tokens.access_token,
      refreshToken: tokens.refresh_token ?? creds.refreshToken,
      expiresAt: new Date(Date.now() + tokens.expires_in * 1e3).toISOString(),
      email
    });
  } catch (err) {
    logWarn(
      `The refreshed ${context} session could not be cached (${err instanceof Error ? err.message : String(err)}) \u2014 this run continues on the new token, but the next one may need a fresh sign-in.`
    );
  }
  return tokens.id_token;
}
async function runLoginFlow(context, config, opts = {}) {
  const useDevice = opts.device || isRemoteEnvironment();
  if (useDevice) {
    logInfo("Using device code flow");
    await deviceCodeLogin(context, config);
  } else {
    await login(context, config);
  }
}
async function ensureValidToken(context, config, opts) {
  let token = await getValidToken(context);
  if (token) return token;
  logInfo(`No valid Zitadel session for ${context} \u2014 running login flow`);
  try {
    await runLoginFlow(context, config, opts);
  } catch (err) {
    logError(`Login failed: ${err.message}`);
    return null;
  }
  return await getValidToken(context);
}
async function ensureLogin(context, opts = {}) {
  const existing = readCredentials(context);
  if (existing && new Date(existing.expiresAt) > /* @__PURE__ */ new Date()) return existing;
  if (opts.interactive === false) return null;
  const config = getContextConfig(context) ?? await discoverConfigGuarded(context);
  if (!config) {
    logWarn(`No login config for "${context}". Run: mesh login ${context}`);
    return null;
  }
  const token = await ensureValidToken(context, config, opts);
  if (!token) return null;
  return readCredentials(context);
}
async function discoverConfigGuarded(context) {
  let config = null;
  if (parseTenantEnv(context)) {
    config = await discoverConfigFromSsm(context);
  }
  if (!config) {
    const pinned = firstPartyDomainFor(context);
    if (pinned) {
      logInfo(`"${context}" is a known Mesh platform \u2014 resolving via ${pinned} (no AWS needed)`);
      config = await discoverConfig(pinned, context);
    }
  }
  if (!config && context.split(".").length - 1 >= 2) {
    config = await discoverConfig(context);
  }
  return config;
}
var CONFIG_DIR, CONFIG_FILE, CREDENTIALS_FILE, REDIRECT_PORT, REDIRECT_URI, SCOPES, atomicWriteCounter, MAX_DEVICE_CODES, RESERVED_REGISTRY_CONTEXT;
var init_login = __esm({
  "libs/mesh-cli/src/commands/login.ts"() {
    "use strict";
    init_log();
    init_errors();
    init_aws_auth();
    init_first_party_contexts();
    init_vpn2();
    CONFIG_DIR = path2.join(
      process.env.XDG_CONFIG_HOME ?? path2.join(process.env.HOME ?? "~", ".config"),
      "mesh"
    );
    CONFIG_FILE = path2.join(CONFIG_DIR, "config.json");
    CREDENTIALS_FILE = path2.join(CONFIG_DIR, "credentials.json");
    REDIRECT_PORT = 9876;
    REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/callback`;
    SCOPES = "openid email profile offline_access urn:zitadel:iam:org:project:id:zitadel:aud";
    atomicWriteCounter = 0;
    MAX_DEVICE_CODES = 3;
    RESERVED_REGISTRY_CONTEXT = "registry";
  }
});

// libs/mesh-cli/src/utils/aws-auth.ts
import * as fs3 from "fs";
import * as path3 from "path";
import {
  AssumeRoleCommand,
  AssumeRoleWithWebIdentityCommand,
  GetCallerIdentityCommand,
  STSClient
} from "@aws-sdk/client-sts";
function derivePlatformContext(appRoot, stack) {
  const configFile = path3.join(appRoot, `Pulumi.${stack}.yaml`);
  if (!fs3.existsSync(configFile)) return null;
  const content = fs3.readFileSync(configFile, "utf-8");
  const jsonMatch = content.match(/mesh:platform:\s*'(\{[^']+\})'/);
  if (jsonMatch) {
    try {
      const parsed = JSON.parse(jsonMatch[1]);
      if (parsed.name && parsed.env) return `${parsed.name}.${parsed.env}`;
    } catch {
    }
  }
  const nameMatch = content.match(/mesh:platform:[\s\S]*?name:\s*(\S+)/);
  const envMatch = content.match(/mesh:platform:[\s\S]*?env:\s*(\S+)/);
  if (nameMatch && envMatch) {
    return `${nameMatch[1]}.${envMatch[1]}`;
  }
  return null;
}
function newStsClient() {
  return new STSClient({ region: DEFAULT_REGION });
}
function toEnv(credentials) {
  const { AccessKeyId, SecretAccessKey, SessionToken } = credentials;
  if (!AccessKeyId || !SecretAccessKey || !SessionToken) return null;
  return {
    AWS_ACCESS_KEY_ID: AccessKeyId,
    AWS_SECRET_ACCESS_KEY: SecretAccessKey,
    AWS_SESSION_TOKEN: SessionToken
  };
}
function isMaxSessionDurationError(err) {
  if (!(err instanceof Error)) return false;
  const message = err.message ?? "";
  return message.includes("MaxSessionDuration") || message.includes("DurationSeconds");
}
async function assumeRole(roleArn, sessionName = "mesh-cli", durationSeconds = 43200) {
  const sts = newStsClient();
  for (const duration of [durationSeconds, 3600]) {
    try {
      const resp = await sts.send(
        new AssumeRoleCommand({
          RoleArn: roleArn,
          RoleSessionName: sessionName,
          DurationSeconds: duration
        })
      );
      if (!resp.Credentials) return null;
      return toEnv(resp.Credentials);
    } catch (err) {
      if (!isMaxSessionDurationError(err) || duration === 3600) {
        const message = err instanceof Error ? err.message : String(err);
        logWarn(`AssumeRole failed: ${message}`);
        if (isExpiredAwsTokenMessage(message)) {
          logWarn(`  Your AWS SSO session looks expired or missing \u2014 refresh with: ${SSO_LOGIN_FIX}`);
        }
        return null;
      }
    }
  }
  return null;
}
async function assumeRoleCredentials(roleArn, idToken, sessionName, durationSeconds = 43200) {
  const sts = newStsClient();
  for (const duration of [durationSeconds, 3600]) {
    try {
      const resp = await sts.send(
        new AssumeRoleWithWebIdentityCommand({
          RoleArn: roleArn,
          RoleSessionName: sessionName,
          WebIdentityToken: idToken,
          DurationSeconds: duration
        })
      );
      if (!resp.Credentials) return null;
      const env = toEnv(resp.Credentials);
      if (!env) return null;
      return {
        // `env` is a `Record<string, string>` (index-signature typed), so
        // `noUncheckedIndexedAccess` widens these to `string | undefined`
        // even though `toEnv` guarantees they're set — assert accordingly.
        AccessKeyId: env.AWS_ACCESS_KEY_ID,
        SecretAccessKey: env.AWS_SECRET_ACCESS_KEY,
        SessionToken: env.AWS_SESSION_TOKEN,
        Expiration: resp.Credentials.Expiration.toISOString()
      };
    } catch (err) {
      if (!isMaxSessionDurationError(err) || duration === 3600) {
        logWarn(
          `AssumeRoleWithWebIdentity failed: ${err instanceof Error ? err.message : String(err)}`
        );
        return null;
      }
    }
  }
  return null;
}
async function assumeRoleWithWebIdentity(roleArn, idToken, sessionName, durationSeconds = 43200) {
  const creds = await assumeRoleCredentials(roleArn, idToken, sessionName, durationSeconds);
  if (!creds) return null;
  return toEnv(creds);
}
async function resolveAwsCredentials(roleArn, appRoot, stack) {
  const roleName = roleArn.split("/").pop() ?? roleArn;
  if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SESSION_TOKEN) {
    logInfo("Using existing AWS credentials from environment");
    return {
      env: {
        AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
        AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY ?? "",
        AWS_SESSION_TOKEN: process.env.AWS_SESSION_TOKEN
      },
      method: "environment"
    };
  }
  const platformContext = derivePlatformContext(appRoot, stack);
  if (platformContext) {
    const meshCreds = await ensureLogin(platformContext, { interactive: false });
    if (meshCreds) {
      const sessionName = (meshCreds.email ?? "mesh-cli").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
      logInfo(`Assuming ${roleName} via Zitadel (${meshCreds.email ?? platformContext})`);
      const creds2 = await assumeRoleWithWebIdentity(roleArn, meshCreds.idToken, sessionName);
      if (creds2) {
        logSuccess(`Assumed ${roleName} via Zitadel`);
        return { env: creds2, method: "zitadel" };
      }
      logWarn(
        `Zitadel JWT auth failed \u2014 falling back to AWS SSO.
  (Try: mesh login ${platformContext})`
      );
    }
  }
  logInfo(`Assuming ${roleName} via AWS SSO`);
  const creds = await assumeRole(roleArn, "mesh-cli");
  if (creds) {
    logSuccess(`Assumed ${roleName}`);
    return { env: creds, method: "sso" };
  }
  if (platformContext && process.stdout.isTTY) {
    const meshCreds = await ensureLogin(platformContext);
    if (meshCreds) {
      const sessionName = (meshCreds.email ?? "mesh-cli").replace(/[^a-zA-Z0-9=,.@-]/g, "_").slice(0, 64);
      logInfo(`Assuming ${roleName} via Zitadel (${meshCreds.email ?? platformContext})`);
      const zCreds = await assumeRoleWithWebIdentity(roleArn, meshCreds.idToken, sessionName);
      if (zCreds) {
        logSuccess(`Assumed ${roleName} via Zitadel`);
        return { env: zCreds, method: "zitadel" };
      }
    }
  }
  return null;
}
var DEFAULT_REGION;
var init_aws_auth = __esm({
  "libs/mesh-cli/src/utils/aws-auth.ts"() {
    "use strict";
    init_log();
    init_auth_preflight();
    init_login();
    DEFAULT_REGION = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
  }
});

// libs/mesh-cli/src/utils/capture-history.ts
var capture_history_exports = {};
__export(capture_history_exports, {
  decryptHistoryPayloads: () => decryptHistoryPayloads,
  serializeHistoryToFixture: () => serializeHistoryToFixture
});
import { historyToJSON } from "@temporalio/common/lib/proto-utils.js";
function isPayload(value) {
  return value != null && typeof value === "object" && "metadata" in value && "data" in value;
}
async function decryptHistoryPayloads(node, decrypt) {
  if (node == null || typeof node !== "object") return 0;
  if (isPayload(node)) {
    const inner = await decrypt(node);
    if (inner === null) return 0;
    node.metadata = inner.metadata ?? {};
    node.data = inner.data ?? new Uint8Array(0);
    return 1;
  }
  let count = 0;
  if (Array.isArray(node)) {
    for (const item of node) count += await decryptHistoryPayloads(item, decrypt);
  } else {
    for (const value of Object.values(node)) {
      count += await decryptHistoryPayloads(value, decrypt);
    }
  }
  return count;
}
async function serializeHistoryToFixture(events, decrypter) {
  let decryptedPayloads = 0;
  if (decrypter) {
    for (const event of events) {
      decryptedPayloads += await decryptHistoryPayloads(event, decrypter);
    }
  }
  const fixture = JSON.parse(historyToJSON({ events }));
  return { fixture, eventCount: events.length, decryptedPayloads };
}
var init_capture_history = __esm({
  "libs/mesh-cli/src/utils/capture-history.ts"() {
    "use strict";
  }
});

// libs/mesh-cli/src/commands/temporal.ts
init_log();
import { spawnSync } from "node:child_process";
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
import { homedir } from "node:os";
import { dirname as dirname4, join as join4, resolve as resolve2 } from "node:path";

// libs/mesh-cli/src/utils/recover-conversation.ts
async function classifyHistoryEvents(rawEvents, decodePayload) {
  const activityTypeByScheduledId = /* @__PURE__ */ new Map();
  const events = [];
  for (const event of rawEvents) {
    const eventId = String(event.eventId);
    const scheduled = event.activityTaskScheduledEventAttributes;
    if (scheduled) {
      const name = scheduled.activityType?.name;
      if (name) activityTypeByScheduledId.set(eventId, name);
      continue;
    }
    const signal = event.workflowExecutionSignaledEventAttributes;
    if (signal?.signalName === "submit_user_message") {
      events.push({
        eventId,
        kind: "userSignal",
        payload: await decodePayload(signal.input?.payloads)
      });
      continue;
    }
    const completed = event.activityTaskCompletedEventAttributes;
    if (completed) {
      const scheduledId = String(completed.scheduledEventId);
      if (activityTypeByScheduledId.get(scheduledId) === "callLLM") {
        events.push({
          eventId,
          kind: "callLLMResult",
          payload: await decodePayload(completed.result?.payloads)
        });
      }
    }
  }
  return events;
}
async function extractSnapshotMessages(rawEvents, decodePayload) {
  let lastCallLLMScheduledId;
  let lastCallLLMInputPayloads;
  const activityTypeByScheduledId = /* @__PURE__ */ new Map();
  let lastCallLLMResultPayloads;
  for (const event of rawEvents) {
    const eventId = String(event.eventId);
    const scheduled = event.activityTaskScheduledEventAttributes;
    if (scheduled) {
      const name = scheduled.activityType?.name;
      if (name) activityTypeByScheduledId.set(eventId, name);
      if (name === "callLLM") {
        lastCallLLMScheduledId = eventId;
        lastCallLLMInputPayloads = scheduled.input?.payloads;
        lastCallLLMResultPayloads = void 0;
      }
      continue;
    }
    const completed = event.activityTaskCompletedEventAttributes;
    if (completed) {
      const scheduledId = String(completed.scheduledEventId);
      if (activityTypeByScheduledId.get(scheduledId) === "callLLM") {
        lastCallLLMResultPayloads = completed.result?.payloads;
      }
    }
  }
  if (lastCallLLMScheduledId === void 0) return [];
  const inputArgs = await decodePayload(lastCallLLMInputPayloads);
  const inputMessages = messagesFrom(inputArgs);
  const result = await decodePayload(lastCallLLMResultPayloads);
  const resultMessages = messagesFrom(result);
  return [...inputMessages, ...resultMessages];
}
function messagesFrom(payload) {
  if (isRecord(payload) && Array.isArray(payload.messages)) {
    return payload.messages;
  }
  return [];
}
function extractText(content) {
  if (typeof content === "string") return content;
  if (Array.isArray(content)) {
    return content.filter(
      (p) => !!p && typeof p === "object" && p.type === "text"
    ).map((p) => typeof p.text === "string" ? p.text : "").join("");
  }
  return "";
}
function summarizeArgs(args) {
  try {
    const str = JSON.stringify(args ?? {});
    return str.length > 100 ? str.substring(0, 97) + "..." : str;
  } catch {
    return "...";
  }
}
function formatToolCall(part) {
  const name = typeof part.toolName === "string" ? part.toolName : "?";
  const args = "input" in part ? part.input : part.args;
  return `${name}(${summarizeArgs(args)})`;
}
function isRecord(v) {
  return !!v && typeof v === "object";
}
function extractUserText(payload) {
  if (!isRecord(payload)) return null;
  const messages = payload.messages;
  if (Array.isArray(messages)) {
    const parts = [];
    for (const m of messages) {
      if (isRecord(m) && m.role === "user") {
        const t = extractText(m.content);
        if (t) parts.push(t);
      }
    }
    if (parts.length > 0) return parts.join("\n");
  }
  for (const field of ["content", "text", "message"]) {
    const v = payload[field];
    if (typeof v === "string" && v) return v;
    const t = extractText(v);
    if (t) return t;
  }
  return null;
}
function extractAssistantTurn(payload) {
  if (!isRecord(payload)) return null;
  const messages = payload.messages;
  if (!Array.isArray(messages)) {
    const t = extractText(payload.content) || extractText(payload.text);
    return t ? { text: t, toolCalls: [] } : null;
  }
  let text = "";
  const toolCalls = [];
  for (const m of messages) {
    if (!isRecord(m) || m.role !== "assistant") continue;
    text += extractText(m.content);
    if (Array.isArray(m.content)) {
      for (const p of m.content) {
        if (isRecord(p) && p.type === "tool-call") {
          toolCalls.push(formatToolCall(p));
        }
      }
    }
  }
  if (!text && toolCalls.length === 0) return null;
  return { text, toolCalls };
}
function reconstructTranscript(events) {
  const turns = [];
  for (const event of events) {
    if (event.kind === "userSignal") {
      const text = extractUserText(event.payload);
      turns.push(
        text !== null ? { role: "user", text, eventId: event.eventId } : { role: "user", text: placeholder(event.eventId), eventId: event.eventId }
      );
    } else if (event.kind === "callLLMResult") {
      const turn = extractAssistantTurn(event.payload);
      if (turn) {
        turns.push({
          role: "assistant",
          text: turn.text,
          eventId: event.eventId,
          ...turn.toolCalls.length > 0 ? { toolCalls: turn.toolCalls } : {}
        });
      } else {
        turns.push({
          role: "assistant",
          text: placeholder(event.eventId),
          eventId: event.eventId
        });
      }
    }
  }
  return turns;
}
function placeholder(eventId) {
  return `_[unparseable turn: ${eventId}]_`;
}
function renderTranscriptMarkdown(turns) {
  const blocks = [];
  for (const turn of turns) {
    const heading = turn.role === "user" ? "## User" : "## Assistant";
    const lines = [heading, ""];
    if (turn.text) lines.push(turn.text);
    if (turn.toolCalls && turn.toolCalls.length > 0) {
      if (turn.text) lines.push("");
      for (const tc of turn.toolCalls) lines.push(`\u2192 tool: ${tc}`);
    }
    blocks.push(lines.join("\n"));
  }
  return blocks.join("\n\n") + "\n";
}

// libs/mesh-cli/src/commands/temporal.ts
init_pulumi();
init_aws_auth();
init_temporal_auth();

// libs/mesh-cli/src/utils/temporal-codec.ts
init_log();
import { execFileSync as execFileSync5 } from "child_process";
import { webcrypto as crypto2 } from "node:crypto";
function resolveTemporalEncodingKeyFromK8s(namespace) {
  const secretName = `${namespace}-temporal-encoding-key`;
  try {
    const b64 = execFileSync5(
      "kubectl",
      [
        "get",
        "secret",
        secretName,
        "-n",
        namespace,
        "-o",
        "jsonpath={.data.TEMPORAL_ENCODING_KEY}"
      ],
      { encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
    ).trim();
    if (!b64) return void 0;
    return Buffer.from(b64, "base64").toString("utf-8");
  } catch {
    return void 0;
  }
}
var ENCRYPTED_ENCODING = "binary/encrypted";
var IV_LENGTH = 12;
async function deriveKey(encodingKey) {
  const keyData = await crypto2.subtle.digest(
    "SHA-256",
    new TextEncoder().encode(encodingKey)
  );
  return crypto2.subtle.importKey(
    "raw",
    keyData,
    { name: "AES-GCM" },
    false,
    ["decrypt"]
  );
}
async function aesGcmDecrypt(data, key) {
  const iv = data.slice(0, IV_LENGTH);
  const ciphertextWithTag = data.slice(IV_LENGTH);
  const decrypted = await crypto2.subtle.decrypt(
    { name: "AES-GCM", iv },
    key,
    ciphertextWithTag
  );
  return new Uint8Array(decrypted);
}
async function createPayloadDecrypter(encodingKey) {
  const key = await deriveKey(encodingKey);
  return async (payload) => {
    if (!payload?.metadata) return null;
    const encodingBytes = payload.metadata["encoding"];
    if (!encodingBytes) return null;
    const encoding = new TextDecoder().decode(encodingBytes);
    if (encoding !== ENCRYPTED_ENCODING) return null;
    if (!payload.data) return "[encrypted payload missing data]";
    let inner;
    try {
      const decrypted = await aesGcmDecrypt(payload.data, key);
      inner = decodePayloadProtobuf(decrypted);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      return `[undecryptable: ${msg}]`;
    }
    return decodeInnerPayload(inner);
  };
}
async function createRawPayloadDecrypter(encodingKey) {
  const key = await deriveKey(encodingKey);
  return async (payload) => {
    if (!payload?.metadata) return null;
    const encodingBytes = payload.metadata["encoding"];
    if (!encodingBytes) return null;
    const encoding = new TextDecoder().decode(encodingBytes);
    if (encoding !== ENCRYPTED_ENCODING) return null;
    if (!payload.data) {
      throw new Error("encrypted payload has no data to decrypt");
    }
    const decrypted = await aesGcmDecrypt(payload.data, key);
    return decodePayloadProtobuf(decrypted);
  };
}
function readVarint(buf, pos) {
  let value = 0;
  let shift = 0;
  let next = pos;
  while (next < buf.length) {
    const byte = buf[next];
    next += 1;
    value |= (byte & 127) << shift;
    if ((byte & 128) === 0) return { value, next };
    shift += 7;
    if (shift >= 32) throw new Error("varint too large");
  }
  throw new Error("truncated varint");
}
function readLengthDelimited(buf, pos) {
  const { value: length, next: afterLen } = readVarint(buf, pos);
  const end = afterLen + length;
  if (end > buf.length) throw new Error("truncated length-delimited field");
  return { bytes: buf.slice(afterLen, end), next: end };
}
function decodePayloadProtobuf(buf) {
  const metadata = {};
  let data;
  let pos = 0;
  while (pos < buf.length) {
    const { value: tag, next: afterTag } = readVarint(buf, pos);
    const fieldNumber = tag >>> 3;
    const wireType = tag & 7;
    pos = afterTag;
    if (wireType === 2) {
      const { bytes, next } = readLengthDelimited(buf, pos);
      pos = next;
      if (fieldNumber === 1) {
        const entry = decodeMetadataEntry(bytes);
        if (entry) metadata[entry.key] = entry.value;
      } else if (fieldNumber === 2) {
        data = bytes;
      }
    } else if (wireType === 0) {
      pos = readVarint(buf, pos).next;
    } else if (wireType === 1) {
      pos += 8;
    } else if (wireType === 5) {
      pos += 4;
    } else {
      throw new Error(`unsupported wire type ${wireType} at pos ${pos}`);
    }
  }
  return { metadata, data: data ?? new Uint8Array(0) };
}
function decodeMetadataEntry(buf) {
  let key;
  let value;
  let pos = 0;
  while (pos < buf.length) {
    const { value: tag, next: afterTag } = readVarint(buf, pos);
    const fieldNumber = tag >>> 3;
    const wireType = tag & 7;
    pos = afterTag;
    if (wireType !== 2) {
      if (wireType === 0) pos = readVarint(buf, pos).next;
      else if (wireType === 1) pos += 8;
      else if (wireType === 5) pos += 4;
      else throw new Error(`unsupported metadata wire type ${wireType}`);
      continue;
    }
    const { bytes, next } = readLengthDelimited(buf, pos);
    pos = next;
    if (fieldNumber === 1) key = new TextDecoder().decode(bytes);
    else if (fieldNumber === 2) value = bytes;
  }
  if (key === void 0 || value === void 0) return null;
  return { key, value };
}
function decodeInnerPayload(payload) {
  const meta = payload.metadata ?? {};
  const encodingBytes = meta["encoding"];
  const encoding = encodingBytes ? new TextDecoder().decode(encodingBytes) : "unknown";
  const data = payload.data ?? new Uint8Array(0);
  if (encoding === "binary/null") return "null";
  if (encoding === "json/plain") {
    const text = new TextDecoder().decode(data);
    try {
      return JSON.stringify(JSON.parse(text), null, 2);
    } catch {
      return text;
    }
  }
  if (encoding === "binary/plain") return `[binary, ${data.length} bytes]`;
  return `[encoding=${encoding}, ${data.length} bytes]`;
}
function resolveEncodingKey(namespace) {
  const fromK8s = resolveTemporalEncodingKeyFromK8s(namespace);
  const key = fromK8s ?? process.env.TEMPORAL_ENCODING_KEY;
  if (!key) {
    logWarn(
      `Could not read TEMPORAL_ENCODING_KEY from secret ${namespace}/${namespace}-temporal-encoding-key, and TEMPORAL_ENCODING_KEY is not set. Configure kubectl (namespace read access), or export TEMPORAL_ENCODING_KEY (e.g. from a \`mesh dev\` worker env).`
    );
    return void 0;
  }
  logSuccess(
    fromK8s ? `Resolved encoding key from ${namespace}-temporal-encoding-key` : "Resolved encoding key from TEMPORAL_ENCODING_KEY env"
  );
  return key;
}
async function buildDecrypterForNamespace(namespace) {
  const key = resolveEncodingKey(namespace);
  if (!key) return void 0;
  return createPayloadDecrypter(key);
}
async function buildRawDecrypterForNamespace(namespace) {
  const key = resolveEncodingKey(namespace);
  if (!key) return void 0;
  return createRawPayloadDecrypter(key);
}

// libs/mesh-cli/src/commands/temporal.ts
async function resolveConnection(options) {
  if (options.address && options.namespace) {
    return { address: options.address, namespace: options.namespace };
  }
  if (!options.address && process.env.TEMPORAL_ADDRESS) {
    const address = process.env.TEMPORAL_ADDRESS;
    const namespace2 = options.namespace ?? process.env.TEMPORAL_NAMESPACE;
    if (namespace2) return { address, namespace: namespace2 };
  }
  const appRoot = findAppRoot(process.cwd());
  if (!appRoot) {
    throw new Error(
      "Not in a Mesh app directory (no Pulumi.yaml found).\nRun from an app directory, or provide --address and --namespace."
    );
  }
  const stack = options.stack ?? getCurrentStack(appRoot);
  if (!stack) {
    const stacks = findStackConfigs(appRoot);
    throw new Error(
      `No Pulumi stack selected.
` + (stacks.length > 0 ? `Available: ${stacks.join(", ")}
Use: mesh temporal --stack <name> ...` : "No stack configs found in this directory.")
    );
  }
  const sa = stack ? ["--stack", stack] : [];
  const deployerRole = readStackConfig(appRoot, stack, "mesh:deployerRole");
  const awsCreds = deployerRole ? (await resolveAwsCredentials(deployerRole, appRoot, stack))?.env : void 0;
  let devOutput;
  let appOutput;
  try {
    const result = pulumiStackOutput(appRoot, "app", sa, awsCreds);
    appOutput = JSON.parse(result);
    devOutput = appOutput.dev ?? appOutput;
  } catch {
    try {
      const result = pulumiStackOutput(appRoot, "dev", sa, awsCreds);
      devOutput = JSON.parse(result);
      appOutput = devOutput;
    } catch {
      throw new Error(
        `Could not read stack output for '${stack}'.
Ensure you've run: mesh deploy up
Or provide --address and --namespace explicitly.`
      );
    }
  }
  const tunnel = devOutput?.tunnels?.temporal;
  const namespace = options.namespace ?? appOutput?.namespace ?? devOutput?.namespace;
  if (!tunnel) {
    throw new Error("No temporal tunnel found in stack output.");
  }
  if (!namespace) {
    throw new Error("No temporal namespace found in stack output.");
  }
  const platform = devOutput?.platform ?? appOutput?.platform;
  const auth = platform ? {
    tenant: platform.tenant ?? "mesh",
    env: platform.env ?? "dev",
    platformName: platform.name ?? platform.tenant ?? "mesh"
  } : void 0;
  return {
    address: options.address ?? `${tunnel.host}:${tunnel.port}`,
    namespace,
    auth
  };
}
async function resolveBearerToken(auth) {
  try {
    const haveEnvCreds = !!process.env.TEMPORAL_AUTH_CLIENT_ID && !!process.env.ZITADEL_ISSUER;
    const vars = !haveEnvCreds && auth ? await resolveTemporalAuth(auth.tenant, auth.env, auth.platformName) : {};
    return await acquireTemporalBearer(vars);
  } catch (err) {
    logWarn(
      `Temporal auth unavailable (${err instanceof Error ? err.message : String(err)}); connecting unauthenticated`
    );
    return void 0;
  }
}
var EVENT_TYPE_NAMES = {
  1: "WorkflowExecutionStarted",
  2: "WorkflowExecutionCompleted",
  3: "WorkflowExecutionFailed",
  4: "WorkflowExecutionTimedOut",
  5: "WorkflowTaskScheduled",
  6: "WorkflowTaskStarted",
  7: "WorkflowTaskCompleted",
  8: "WorkflowTaskTimedOut",
  9: "WorkflowTaskFailed",
  10: "ActivityTaskScheduled",
  11: "ActivityTaskStarted",
  12: "ActivityTaskCompleted",
  13: "ActivityTaskFailed",
  14: "ActivityTaskTimedOut",
  15: "ActivityTaskCancelRequested",
  16: "ActivityTaskCanceled",
  17: "TimerStarted",
  18: "TimerFired",
  19: "TimerCanceled",
  20: "WorkflowExecutionCancelRequested",
  21: "WorkflowExecutionCanceled",
  24: "WorkflowExecutionSignaled",
  // not 22/23 which were removed
  25: "WorkflowExecutionTerminated",
  26: "WorkflowExecutionSignaled",
  29: "WorkflowPropertiesModified",
  40: "ChildWorkflowExecutionStarted",
  41: "ChildWorkflowExecutionCompleted",
  42: "ChildWorkflowExecutionFailed",
  43: "ChildWorkflowExecutionCanceled",
  44: "ChildWorkflowExecutionTimedOut",
  45: "ChildWorkflowExecutionTerminated",
  46: "WorkflowPropertiesModified",
  48: "NexusOperationScheduled",
  49: "NexusOperationStarted",
  50: "NexusOperationCompleted",
  51: "NexusOperationFailed",
  52: "NexusOperationCanceled",
  53: "NexusOperationTimedOut"
};
var STATUS_NAMES = {
  0: "UNSPECIFIED",
  1: "RUNNING",
  2: "COMPLETED",
  3: "FAILED",
  4: "CANCELED",
  5: "TERMINATED",
  6: "CONTINUED_AS_NEW",
  7: "TIMED_OUT"
};
async function describeWorkflow(workflowId, runId, options) {
  const conn = await connect(options);
  try {
    const desc = await conn.connection.workflowService.describeWorkflowExecution({
      namespace: conn.namespace,
      execution: { workflowId, runId }
    });
    const info = desc.workflowExecutionInfo;
    if (!info) {
      logError("No workflow execution info returned.");
      return;
    }
    console.log(`Workflow:   ${workflowId}`);
    console.log(`Run ID:     ${info.execution?.runId ?? runId ?? "?"}`);
    console.log(`Type:       ${info.type?.name ?? "?"}`);
    console.log(`Status:     ${STATUS_NAMES[info.status ?? 0] ?? info.status}`);
    console.log(`Task Queue: ${info.taskQueue ?? "?"}`);
    console.log(`Namespace:  ${conn.namespace}`);
    const pending = desc.pendingActivities ?? [];
    if (pending.length > 0) {
      console.log(`
Pending Activities (${pending.length}):`);
      for (const pa of pending) {
        console.log(`  - ${pa.activityType?.name ?? "?"} (attempt ${pa.attempt}, state: ${pa.state})`);
      }
    }
    const pendingNexus = desc.pendingNexusOperations ?? [];
    if (pendingNexus.length > 0) {
      console.log(`
Pending Nexus Operations (${pendingNexus.length}):`);
      for (const pn of pendingNexus) {
        console.log(`  - ${pn.operation ?? "?"} \u2192 ${pn.endpoint ?? "?"} (state: ${pn.state})`);
      }
    }
  } finally {
    conn.connection.close();
  }
}
async function* iterHistoryEvents(workflowService, namespace, workflowId, runId, maximumPageSize = 100) {
  let nextPageToken;
  do {
    const resp = await workflowService.getWorkflowExecutionHistory({
      namespace,
      execution: { workflowId, runId },
      maximumPageSize,
      nextPageToken
    });
    for (const event of resp.history?.events ?? []) {
      yield event;
    }
    nextPageToken = resp.nextPageToken?.length ? resp.nextPageToken : void 0;
  } while (nextPageToken);
}
async function showHistory(workflowId, runId, options) {
  const conn = await connect(options);
  const maxEvents = options.follow ? Infinity : parseInt(options.limit ?? "200", 10);
  const skipTypes = /* @__PURE__ */ new Set([5, 6, 7, 8, 9]);
  const decrypter = options.showPayloads ? await buildDecrypterForNamespace(conn.namespace) : void 0;
  try {
    let totalShown = 0;
    let lastEventId = 0;
    console.log(`History for ${workflowId} (namespace: ${conn.namespace})${options.follow ? " [following]" : ""}
`);
    for await (const event of iterHistoryEvents(
      conn.connection.workflowService,
      conn.namespace,
      workflowId,
      runId
    )) {
      lastEventId = Number(event.eventId);
      const eventType = event.eventType ?? 0;
      if (options.compact !== false && skipTypes.has(eventType)) continue;
      await printEvent(event, decrypter);
      totalShown++;
      if (totalShown >= maxEvents) break;
    }
    if (!options.follow) {
      console.log(`
${totalShown} events shown.`);
      return;
    }
    const terminalTypes = /* @__PURE__ */ new Set([2, 3, 4, 21, 27, 28]);
    let done = false;
    while (!done) {
      try {
        const resp = await conn.connection.workflowService.getWorkflowExecutionHistory({
          namespace: conn.namespace,
          execution: { workflowId, runId },
          maximumPageSize: 100,
          waitNewEvent: true,
          // Start after the last event we've seen
          ...lastEventId > 0 ? { skipArchival: true } : {}
        });
        for (const event of resp.history?.events ?? []) {
          const eid = Number(event.eventId);
          if (eid <= lastEventId) continue;
          lastEventId = eid;
          const eventType = event.eventType ?? 0;
          if (options.compact !== false && skipTypes.has(eventType)) continue;
          await printEvent(event, decrypter);
          totalShown++;
          if (terminalTypes.has(eventType)) {
            done = true;
          }
        }
      } catch (err) {
        if (err?.code === 5 || err?.message?.includes("not found")) {
          console.log("\nWorkflow completed or not found.");
          done = true;
        } else {
          throw err;
        }
      }
    }
    console.log(`
${totalShown} events shown (workflow ended).`);
  } finally {
    conn.connection.close();
  }
}
async function printEvent(event, decrypter) {
  const eventType = event.eventType ?? 0;
  const typeName = EVENT_TYPE_NAMES[eventType] ?? `Unknown(${eventType})`;
  const id = String(event.eventId).padStart(4);
  const detail = extractEventDetail(event);
  const detailStr = detail ? ` \u2014 ${detail}` : "";
  console.log(`${id}  ${typeName}${detailStr}`);
  if (!decrypter) return;
  for (const group of extractEventPayloads(event)) {
    const rendered = await renderPayloads(group.payloads, decrypter);
    if (rendered === null) continue;
    console.log(`      ${group.label}:`);
    for (const line of rendered.split("\n")) {
      console.log(`        ${line}`);
    }
  }
}
function extractEventPayloads(event) {
  const groups = [];
  const pushIfAny = (label, payloads) => {
    if (payloads && payloads.length > 0) groups.push({ label, payloads });
  };
  if (event.workflowExecutionStartedEventAttributes) {
    pushIfAny("input", event.workflowExecutionStartedEventAttributes.input?.payloads);
  }
  if (event.workflowExecutionCompletedEventAttributes) {
    pushIfAny("result", event.workflowExecutionCompletedEventAttributes.result?.payloads);
  }
  if (event.workflowExecutionFailedEventAttributes) {
    const failure = event.workflowExecutionFailedEventAttributes.failure;
    if (failure?.message) groups.push({ label: "failure", payloads: [{ __failure: failure }] });
  }
  if (event.workflowExecutionSignaledEventAttributes) {
    pushIfAny("signal input", event.workflowExecutionSignaledEventAttributes.input?.payloads);
  }
  if (event.activityTaskScheduledEventAttributes) {
    pushIfAny("input", event.activityTaskScheduledEventAttributes.input?.payloads);
  }
  if (event.activityTaskCompletedEventAttributes) {
    pushIfAny("result", event.activityTaskCompletedEventAttributes.result?.payloads);
  }
  if (event.activityTaskFailedEventAttributes) {
    const failure = event.activityTaskFailedEventAttributes.failure;
    if (failure?.message) groups.push({ label: "failure", payloads: [{ __failure: failure }] });
  }
  if (event.nexusOperationScheduledEventAttributes) {
    const input = event.nexusOperationScheduledEventAttributes.input;
    if (input) pushIfAny("input", [input]);
  }
  if (event.nexusOperationCompletedEventAttributes) {
    const result = event.nexusOperationCompletedEventAttributes.result;
    if (result) pushIfAny("result", [result]);
  }
  if (event.nexusOperationFailedEventAttributes) {
    const failure = event.nexusOperationFailedEventAttributes.failure;
    if (failure?.message) groups.push({ label: "failure", payloads: [{ __failure: failure }] });
  }
  return groups;
}
async function renderPayloads(payloads, decrypter) {
  const parts = [];
  for (const payload of payloads) {
    if (payload && typeof payload === "object" && "__failure" in payload) {
      const failure = payload.__failure;
      parts.push(renderFailure(failure));
      continue;
    }
    const decoded = await decrypter(payload);
    if (decoded === null) {
      parts.push("[non-encrypted payload]");
    } else {
      parts.push(decoded);
    }
  }
  return parts.length > 0 ? parts.join("\n---\n") : null;
}
function renderFailure(failure) {
  const lines = [failure.message ?? "(no message)"];
  if (failure.stackTrace) lines.push(failure.stackTrace);
  return lines.join("\n");
}
function extractEventDetail(event) {
  if (event.activityTaskScheduledEventAttributes) {
    const a = event.activityTaskScheduledEventAttributes;
    return `activity: ${a.activityType?.name ?? "?"}, queue: ${a.taskQueue?.name ?? "?"}`;
  }
  if (event.activityTaskCompletedEventAttributes) {
    return `scheduledId: ${event.activityTaskCompletedEventAttributes.scheduledEventId}`;
  }
  if (event.activityTaskFailedEventAttributes) {
    const a = event.activityTaskFailedEventAttributes;
    const msg = a.failure?.message ?? "";
    return `scheduledId: ${a.scheduledEventId}${msg ? `, error: ${msg.slice(0, 120)}` : ""}`;
  }
  if (event.activityTaskTimedOutEventAttributes) {
    return `scheduledId: ${event.activityTaskTimedOutEventAttributes.scheduledEventId}`;
  }
  if (event.nexusOperationScheduledEventAttributes) {
    const a = event.nexusOperationScheduledEventAttributes;
    return `endpoint: ${a.endpoint ?? "?"}, operation: ${a.operation ?? "?"}, service: ${a.service ?? "?"}`;
  }
  if (event.nexusOperationCompletedEventAttributes) {
    return `scheduledId: ${event.nexusOperationCompletedEventAttributes.scheduledEventId}`;
  }
  if (event.nexusOperationFailedEventAttributes) {
    const a = event.nexusOperationFailedEventAttributes;
    return `scheduledId: ${a.scheduledEventId}, error: ${a.failure?.message?.slice(0, 120) ?? "?"}`;
  }
  if (event.nexusOperationTimedOutEventAttributes) {
    return `scheduledId: ${event.nexusOperationTimedOutEventAttributes.scheduledEventId}`;
  }
  if (event.workflowExecutionSignaledEventAttributes) {
    return `signal: ${event.workflowExecutionSignaledEventAttributes.signalName ?? "?"}`;
  }
  if (event.workflowExecutionStartedEventAttributes) {
    const a = event.workflowExecutionStartedEventAttributes;
    return `type: ${a.workflowType?.name ?? "?"}, queue: ${a.taskQueue?.name ?? "?"}`;
  }
  if (event.workflowExecutionCompletedEventAttributes) return null;
  if (event.workflowExecutionFailedEventAttributes) {
    return `error: ${event.workflowExecutionFailedEventAttributes.failure?.message?.slice(0, 120) ?? "?"}`;
  }
  if (event.timerStartedEventAttributes) {
    const dur = event.timerStartedEventAttributes.startToFireTimeout;
    return `timerId: ${event.timerStartedEventAttributes.timerId}, duration: ${dur?.seconds ?? "?"}s`;
  }
  if (event.timerFiredEventAttributes) {
    return `timerId: ${event.timerFiredEventAttributes.timerId}`;
  }
  return null;
}
async function recoverConversation(workflowId, runId, options) {
  const conn = await connect(options);
  const decrypter = await buildDecrypterForNamespace(conn.namespace);
  if (!decrypter) {
    conn.connection.close();
    throw new Error(
      "Cannot decrypt conversation payloads: no encoding key available.\nSet TEMPORAL_ENCODING_KEY (e.g. from a `mesh dev` worker env) or configure kubectl namespace read access, then retry."
    );
  }
  try {
    const decodePayload = async (payloads) => {
      const first = payloads?.[0];
      if (first === void 0) return void 0;
      const decoded = await decrypter(first);
      if (decoded === null) return void 0;
      try {
        return JSON.parse(decoded);
      } catch {
        return void 0;
      }
    };
    const rawEvents = [];
    for await (const event of iterHistoryEvents(
      conn.connection.workflowService,
      conn.namespace,
      workflowId,
      runId
    )) {
      rawEvents.push(event);
    }
    if (options.snapshot) {
      const messages = await extractSnapshotMessages(rawEvents, decodePayload);
      const blob = {
        conversationId: workflowId,
        messages,
        artifacts: [],
        focus: null
      };
      const output2 = JSON.stringify(blob, null, 2) + "\n";
      if (options.out) {
        writeFileSync2(options.out, output2);
        logSuccess(
          `Wrote snapshot (${messages.length} messages) to ${options.out}`
        );
      } else {
        process.stdout.write(output2);
      }
      return;
    }
    const events = await classifyHistoryEvents(rawEvents, decodePayload);
    const turns = reconstructTranscript(events);
    const output = options.json ? JSON.stringify(
      {
        turns: turns.map((t) => ({
          role: t.role,
          text: t.text,
          ...t.toolCalls ? { toolCalls: t.toolCalls } : {}
        }))
      },
      null,
      2
    ) + "\n" : renderTranscriptMarkdown(turns);
    if (options.out) {
      writeFileSync2(options.out, output);
      logSuccess(`Wrote ${turns.length} turns to ${options.out}`);
    } else {
      process.stdout.write(output);
    }
  } finally {
    conn.connection.close();
  }
}
async function captureHistory(workflowId, runId, options) {
  const conn = await connect(options);
  const decrypter = await buildRawDecrypterForNamespace(conn.namespace);
  if (!decrypter) {
    conn.connection.close();
    throw new Error(
      "Cannot decrypt history payloads: no encoding key available.\nA replay fixture needs decrypted payloads. Set TEMPORAL_ENCODING_KEY (e.g. from a `mesh dev` worker env) or configure kubectl namespace read access, then retry."
    );
  }
  try {
    const events = [];
    for await (const event of iterHistoryEvents(
      conn.connection.workflowService,
      conn.namespace,
      workflowId,
      runId
    )) {
      events.push(event);
    }
    if (events.length === 0) {
      throw new Error(
        `No history events found for workflow '${workflowId}'` + (runId ? ` (run ${runId})` : "") + `. Check the workflow id and namespace (${conn.namespace}).`
      );
    }
    const { serializeHistoryToFixture: serializeHistoryToFixture2 } = await Promise.resolve().then(() => (init_capture_history(), capture_history_exports));
    const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture2(events, decrypter);
    const outPath = options.out ?? join4(homedir(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
    mkdirSync2(dirname4(outPath), { recursive: true });
    if (options.out) warnIfNotGitIgnored(options.out);
    writeFileSync2(outPath, JSON.stringify(fixture, null, 2) + "\n");
    logSuccess(
      `Wrote replay history (${eventCount} events, ${decryptedPayloads} payloads decrypted) to ${outPath}`
    );
    logWarn(
      "\u26A0  This file contains DECRYPTED payloads \u2014 real customer/conversation data.\n   For LOCAL replay debugging ONLY. Do NOT commit it. (CI golden fixtures are synthetic; this is the debugging counterpart.)"
    );
  } finally {
    conn.connection.close();
  }
}
function sanitizeFileId(id) {
  return id.replace(/[^A-Za-z0-9._-]/g, "_");
}
function warnIfNotGitIgnored(outPath) {
  const abs = resolve2(outPath);
  try {
    const res = spawnSync("git", ["-C", dirname4(abs), "check-ignore", "-q", abs], {
      stdio: "ignore"
    });
    if (res.status !== 1) return;
  } catch {
    return;
  }
  logWarn(
    `\u26A0\u26A0\u26A0  OUTPUT PATH IS INSIDE A GIT REPO AND IS *NOT* GITIGNORED  \u26A0\u26A0\u26A0
   ${abs}
   This fixture contains DECRYPTED production payloads. Add it to .gitignore or move it outside the repo \u2014 do NOT commit it.`
  );
}
async function terminateWorkflow(workflowId, runId, options) {
  const conn = await connect(options);
  try {
    await conn.connection.workflowService.terminateWorkflowExecution({
      namespace: conn.namespace,
      workflowExecution: { workflowId, runId },
      reason: options.reason ?? "terminated via mesh temporal",
      identity: "mesh-cli"
    });
    logSuccess(
      `Terminated ${workflowId}${runId ? ` (run ${runId})` : ""} in namespace ${conn.namespace}`
    );
  } finally {
    conn.connection.close();
  }
}
async function connect(options) {
  const info = await resolveConnection(options);
  logInfo(`Connecting to ${info.address} (namespace: ${info.namespace})`);
  const { Connection } = await import("@temporalio/client");
  const token = await resolveBearerToken(info.auth);
  const metadata = token ? { authorization: `Bearer ${token}` } : void 0;
  const connection = await Connection.connect({
    address: info.address,
    metadata,
    channelArgs: { "grpc.max_receive_message_length": 256 * 1024 * 1024 }
  });
  return { connection, namespace: info.namespace };
}
function registerTemporalCommands(program) {
  const temporal = program.command("temporal").description("Inspect Temporal workflows for the current app/stack");
  const sharedOpts = (cmd) => cmd.option("--stack <stack>", "Pulumi stack name (auto-detected from current directory)").option("--address <addr>", "Temporal server address (default: from stack output)").option("--namespace <ns>", "Temporal namespace (default: from stack output)");
  sharedOpts(
    temporal.command("describe <workflowId> [runId]").description("Show workflow status and metadata")
  ).action(async (workflowId, runId, opts) => {
    try {
      await describeWorkflow(workflowId, runId, opts);
    } catch (err) {
      logError(err instanceof Error ? err.message : String(err));
      process.exitCode = 1;
    }
  });
  sharedOpts(
    temporal.command("terminate <workflowId> [runId]").description("Terminate a running workflow (e.g. a wedged/looping conversation)")
  ).option("--reason <reason>", "Termination reason (recorded in workflow history)").action(async (workflowId, runId, opts) => {
    try {
      await terminateWorkflow(workflowId, runId, opts);
    } catch (err) {
      logError(err instanceof Error ? err.message : String(err));
      process.exitCode = 1;
    }
  });
  sharedOpts(
    temporal.command("history <workflowId> [runId]").description("Show workflow event history").option("-n, --limit <n>", "Maximum events to show (default: 200)").option("-f, --follow", "Tail the history, waiting for new events until the workflow completes").option("--no-compact", "Show all events including WorkflowTask scheduling noise").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) => {
    try {
      await showHistory(workflowId, runId, opts);
    } catch (err) {
      logError(err instanceof Error ? err.message : String(err));
      process.exitCode = 1;
    }
  });
  sharedOpts(
    temporal.command("recover-conversation <workflowId> [runId]").description(
      "Reconstruct an agent conversation transcript from durable Temporal history (for when the worker can no longer replay it). Requires the encoding key \u2014 reads TEMPORAL_ENCODING_KEY (or the app's K8s secret)."
    ).option("--out <path>", "Write the transcript to a file instead of stdout").option("--json", "Emit a structured { turns: [...] } JSON array instead of markdown").option(
      "--snapshot",
      "Emit a store-ready conversation blob { conversationId, messages, artifacts, focus } with STRUCTURED ModelMessage[] (tool-call parts intact) for a full-fidelity backfill"
    )
  ).action(async (workflowId, runId, opts) => {
    try {
      await recoverConversation(workflowId, runId, opts);
    } catch (err) {
      logError(err instanceof Error ? err.message : String(err));
      process.exitCode = 1;
    }
  });
  sharedOpts(
    temporal.command("capture-history <workflowId> [runId]").description(
      "Capture a workflow's full history to a replay fixture JSON (proto3-JSON, DECRYPTED payloads) for LOCAL replay debugging. Requires the encoding key. Defaults to ~/.mesh/replay-histories/<workflowId>.json (outside any repo) \u2014 do NOT commit the output."
    ).option(
      "--out <path>",
      "Output path (default: ~/.mesh/replay-histories/<workflowId>.json; a non-gitignored path inside a git repo triggers a loud warning)"
    )
  ).action(async (workflowId, runId, opts) => {
    try {
      await captureHistory(workflowId, runId, opts);
    } catch (err) {
      logError(err instanceof Error ? err.message : String(err));
      process.exitCode = 1;
    }
  });
}
export {
  connect,
  registerTemporalCommands
};