{"version":3,"file":"ops.mjs","names":[],"sources":["../../src/commands/ops.ts"],"sourcesContent":["import { dirname, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { Context, Effect } from \"effect\";\nimport { probePortBindable } from \"../app\";\nimport { buildCiInfraPlan, type CiInfraPlan } from \"../cli/infra\";\nimport { getStatus } from \"../cli/status\";\nimport { findConfigPath, MISSING_CONFIG_MESSAGE } from \"../config\";\nimport { readDevLatestLog, resolveDevLatestFile } from \"../dev-logs\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport { pointerFingerprint } from \"../fingerprint\";\nimport { ownerOfPort } from \"../infra/port-ownership\";\nimport { killProcessGroupEscalating, reapGroup } from \"../process-kill\";\nimport { isPidAlive, pruneDeadEffect, readRegistry, unregisterPid } from \"../process-registry\";\nimport { openResolution } from \"../resolution/session\";\nimport { computeDeployedVersionStatus, type DeployedVersionStatus } from \"../version-status\";\nimport { type BosBuilder, type BosDeps, BosDepsTag } from \"./shared\";\n\nexport function registerOps(builder: BosBuilder) {\n  return {\n    status: builder.status.handler(async ({ context }) => {\n      try {\n        const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n        const configPath = findConfigPath();\n        if (!configPath) {\n          return {\n            status: \"error\" as const,\n            packages: [],\n            envFile: \"missing\" as const,\n            error: `${MISSING_CONFIG_MESSAGE} in current directory`,\n          };\n        }\n\n        const projectDir = resolve(dirname(configPath));\n        const status = await getStatus(projectDir);\n        return {\n          ...status,\n          deployedVersion: await deployedVersionStatus(deps),\n        };\n      } catch (error) {\n        return {\n          status: \"error\" as const,\n          packages: [],\n          envFile: \"missing\" as const,\n          error: error instanceof Error ? error.message : \"Unknown error\",\n        };\n      }\n    }),\n\n    logs: builder.logs.handler(async ({ input, context }) => {\n      try {\n        const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n        const configDir = deps.session?.root ?? process.cwd();\n        const text = await readDevLatestLog(configDir, { tail: input.tail });\n        const service = input.service;\n        const lines = text\n          .split(\"\\n\")\n          .filter((line) => line.length > 0)\n          .filter((line) => {\n            if (!service) return true;\n            const match = /\\] \\[([^\\]]+)\\] \\[(?:OUT|ERR)\\] /.exec(line);\n            return match?.[1] === service || match?.[1] === `plugin:${service}`;\n          });\n        return {\n          logFile: resolveDevLatestFile(configDir),\n          lines,\n        };\n      } catch (error) {\n        return {\n          logFile: \"unknown\",\n          lines: [\n            error instanceof Error\n              ? `Failed to read logs: ${error.message}`\n              : \"Failed to read logs\",\n          ],\n        };\n      }\n    }),\n\n    ps: builder.ps.handler(async () => {\n      try {\n        const entries = await Effect.runPromise(pruneDeadEffect(readRegistry()));\n        return {\n          status: \"ok\" as const,\n          entries,\n        };\n      } catch (error) {\n        return {\n          status: \"error\" as const,\n          entries: [],\n          error: error instanceof Error ? error.message : \"Unknown error\",\n        };\n      }\n    }),\n\n    kill: builder.kill.handler(async ({ input }) => {\n      try {\n        const configPath = findConfigPath();\n        const targetConfigDir = input.all\n          ? undefined\n          : (input.configDir ?? (configPath ? resolve(dirname(configPath)) : undefined));\n\n        const targets = targetConfigDir\n          ? readRegistry().filter((entry) => entry.configDir === targetConfigDir)\n          : readRegistry();\n\n        const killed: Array<{ pid: number; configDir: string }> = [];\n        const skipped: Array<{ pid: number; reason: string }> = [];\n\n        for (const entry of targets) {\n          const wasAlive = isPidAlive(entry.pid);\n          if (wasAlive) {\n            await Effect.runPromise(\n              killProcessGroupEscalating(entry.pid, {\n                terminateMs: 5000,\n                signal: input.signal === \"SIGKILL\" ? \"SIGKILL\" : \"SIGTERM\",\n              }),\n            );\n          }\n\n          const reapedChildren: number[] = [];\n          for (const childPid of entry.childPids ?? []) {\n            if (isPidAlive(childPid)) {\n              reapGroup(childPid);\n              reapedChildren.push(childPid);\n            }\n          }\n\n          const ports = Object.values(entry.ports ?? {}).filter(\n            (port) => Number.isFinite(port) && port > 0,\n          );\n          const stillBound: number[] = [];\n          for (const port of ports) {\n            let bindable = false;\n            for (let attempt = 0; attempt < 4; attempt++) {\n              bindable = await Effect.runPromise(probePortBindable(port));\n              if (bindable) break;\n              await new Promise((resolve) => setTimeout(resolve, 500));\n            }\n            if (!bindable) stillBound.push(port);\n          }\n\n          if (stillBound.length > 0) {\n            const owner = await Effect.runPromise(ownerOfPort(stillBound[0]));\n            skipped.push({\n              pid: entry.pid,\n              reason: `ports still bound after kill: ${stillBound.join(\", \")}${\n                owner ? ` — held by pid ${owner.pid} (${owner.command})` : \" — owner unknown\"\n              }`,\n            });\n            continue;\n          }\n\n          unregisterPid(entry.pid);\n          killed.push({ pid: entry.pid, configDir: entry.configDir });\n          if (!wasAlive && reapedChildren.length > 0) {\n            skipped.push({\n              pid: entry.pid,\n              reason: `process already exited; reaped orphaned children ${reapedChildren.join(\", \")}`,\n            });\n          }\n        }\n\n        return {\n          status: \"killed\" as const,\n          killed,\n          skipped,\n        };\n      } catch (error) {\n        return {\n          status: \"error\" as const,\n          killed: [],\n          skipped: [],\n          error: error instanceof Error ? error.message : \"Unknown error\",\n        };\n      }\n    }),\n\n    infraExport: builder.infraExport.handler(async ({ input, context }) => {\n      const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n      const configDir = input.configDir ?? deps.session?.root ?? process.cwd();\n      const ci = deps.session?.runtime ? buildCiInfraPlan(deps.session.runtime) : null;\n      if (!ci) {\n        const refreshed = await openResolution({ cwd: configDir });\n        if (!refreshed?.runtime) {\n          throw new Error(\"No resolved runtime config available for infra export\");\n        }\n        return buildCiInfraPlan(refreshed.runtime);\n      }\n      const result: CiInfraPlan & { account: string; gateway: string } = {\n        ...ci,\n        account: deps.session?.config?.account ?? ci.account,\n        gateway:\n          ci.gateway ?? deps.session?.config?.domain ?? deps.session?.config?.account ?? ci.account,\n      };\n      return result;\n    }),\n  };\n}\n\nasync function deployedVersionStatus(deps: BosDeps): Promise<DeployedVersionStatus | undefined> {\n  const config = deps.session?.config;\n  if (!config?.account || !config.domain) return undefined;\n  const { account, domain } = config;\n  let publishedFingerprint: string | undefined;\n  try {\n    const published = await fetchBosConfigFromFastKv<Record<string, unknown>>(\n      `bos://${account}/${domain}`,\n    );\n    publishedFingerprint = pointerFingerprint(published as never);\n  } catch {\n    return undefined;\n  }\n\n  const hostUrl = deps.session?.runtime?.host?.url;\n  if (!hostUrl) {\n    return computeDeployedVersionStatus({ publishedFingerprint, servedFingerprint: null });\n  }\n  try {\n    const response = await fetch(new URL(\"/.well-known/version\", hostUrl));\n    if (!response.ok) {\n      return computeDeployedVersionStatus({\n        publishedFingerprint,\n        servedFingerprint: null,\n        servedError: `version endpoint returned ${response.status}`,\n      });\n    }\n    const body = (await response.json()) as { fingerprint?: string };\n    return computeDeployedVersionStatus({\n      publishedFingerprint,\n      servedFingerprint: body.fingerprint ?? null,\n    });\n  } catch (error) {\n    return computeDeployedVersionStatus({\n      publishedFingerprint,\n      servedFingerprint: null,\n      servedError: error instanceof Error ? error.message : String(error),\n    });\n  }\n}\n"],"mappings":"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"}