@keystrokehq/cli 0.1.162 → 0.1.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _i as originFromPublicUrl, zi as LOCAL_PLATFORM_ORIGIN } from "./dist-Cy2nsWsI.mjs";
2
+ import { _i as originFromPublicUrl, zi as LOCAL_PLATFORM_ORIGIN } from "./dist-BgPOKEEt.mjs";
3
3
  import Conf from "conf";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
@@ -702,4 +702,4 @@ function runPackageManagerUpdate(install, distTag) {
702
702
  //#endregion
703
703
  export { resolvePlatformUrlForWebUrl as A, createCliConfig as C, getWebUrl as D, getPlatformUrl as E, ExpectedCliError as M, isExpectedCliError as N, DEFAULT_PLATFORM_URL as O, resolveCliRoot as S, getCredentialStorage as T, isLocalSdkSpec as _, formatReleaseAgeBlockMessage as a, shouldUpdateCliVersion as b, detectManagerFromLockfile as c, installDependencies as d, installPlaygroundDependencies as f, ensureSdkCurrent as g, CLI_PACKAGE_NAME as h, readPnpmMinimumReleaseAgeMinutes as i, parseCredentialStorage as j, DEFAULT_WEB_URL as k, buildPlaygroundWorkspace as l, resolvePackageManager as m, ensureMinimumReleaseAgeExclude as n, isReleaseAgeBlock as o, resolveGithubPackagesToken as p, computeReleaseAgeRetryAfter as r, detectCliInstall as s, runPackageManagerUpdate as t, detectPackageManager as u, fetchLatestCliRelease as v, getCliConfigDir as w, readCliVersion as x, resolveCliDistTag as y };
704
704
 
705
- //# sourceMappingURL=run-package-manager-update-CuLe-Jux.mjs.map
705
+ //# sourceMappingURL=run-package-manager-update-D_nLqg4e.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-package-manager-update-CuLe-Jux.mjs","names":["CLI_PACKAGE_NAME"],"sources":["../src/errors/expected-cli-error.ts","../src/auth/credential-storage.ts","../src/resolve-platform-url.ts","../src/config.ts","../src/project/resolve-cli-root.ts","../src/version.ts","../../../packages/shared/dist/cli-release.mjs","../src/update/ensure-sdk-current.ts","../src/init/package-manager.ts","../src/update/detect-cli-install.ts","../src/update/detect-release-age-block.ts","../src/update/read-pnpm-release-age-minutes.ts","../src/update/resolve-global-pnpm-root.ts","../src/update/build-update-command.ts","../src/update/ensure-release-age-exclude.ts","../src/update/run-package-manager-update.ts"],"sourcesContent":["/**\n * User/project failures that are expected CLI exits (lint, typecheck, validation, etc.).\n * These should not become Error Tracking issues.\n */\nexport class ExpectedCliError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ExpectedCliError\";\n }\n}\n\nexport function isExpectedCliError(error: unknown): error is ExpectedCliError {\n return error instanceof ExpectedCliError;\n}\n","import { ExpectedCliError } from \"../errors/expected-cli-error\";\n\nexport const CREDENTIAL_STORAGE_VALUES = [\"keychain\", \"file\"] as const;\n\nexport type CredentialStorage = (typeof CREDENTIAL_STORAGE_VALUES)[number];\n\nexport function isCredentialStorage(value: unknown): value is CredentialStorage {\n return value === \"keychain\" || value === \"file\";\n}\n\nexport function parseCredentialStorage(value: string): CredentialStorage {\n if (!isCredentialStorage(value)) {\n throw new ExpectedCliError(`Invalid credential storage \"${value}\". Use keychain or file.`);\n }\n return value;\n}\n","import { LOCAL_PLATFORM_ORIGIN, originFromPublicUrl } from \"@keystrokehq/shared\";\n\nexport const DEFAULT_WEB_URL = \"https://keystroke.ai\";\nexport const DEFAULT_PLATFORM_URL = \"https://api.keystroke.ai\";\nexport const LOCAL_WEB_URL = \"http://localhost:3000\";\n\nexport type ResolvePlatformUrlOptions = {\n platformUrl?: string;\n /** Used when webUrl does not match a known deployment. */\n fallback?: string;\n};\n\nfunction webOriginFromUrl(webUrl: string): string {\n return originFromPublicUrl(webUrl, webUrl.replace(/\\/+$/, \"\"));\n}\n\n/** Known web origins with a fixed platform API — undefined for custom/staging URLs. */\nexport function knownPlatformUrlForWebUrl(webUrl: string): string | undefined {\n const webOrigin = webOriginFromUrl(webUrl);\n\n if (webOrigin === LOCAL_WEB_URL || webOrigin === \"http://127.0.0.1:3000\") {\n return LOCAL_PLATFORM_ORIGIN;\n }\n\n if (webOrigin === DEFAULT_WEB_URL) {\n return DEFAULT_PLATFORM_URL;\n }\n\n return undefined;\n}\n\nexport function resolvePlatformUrlForWebUrl(\n webUrl: string,\n options: ResolvePlatformUrlOptions = {},\n): string {\n const explicit = options.platformUrl?.trim();\n if (explicit) {\n return explicit.replace(/\\/+$/, \"\");\n }\n\n const known = knownPlatformUrlForWebUrl(webUrl);\n if (known) {\n return known;\n }\n\n const fallback = options.fallback?.trim() || DEFAULT_PLATFORM_URL;\n return fallback.replace(/\\/+$/, \"\");\n}\n","import Conf from \"conf\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport { isCredentialStorage, type CredentialStorage } from \"./auth/credential-storage\";\nimport {\n DEFAULT_PLATFORM_URL,\n DEFAULT_WEB_URL,\n knownPlatformUrlForWebUrl,\n resolvePlatformUrlForWebUrl,\n} from \"./resolve-platform-url\";\n\nexport type CliConfig = {\n webUrl: string;\n platformUrl: string;\n /** Where session tokens are stored. Defaults to OS keychain when unset. */\n credentialStorage?: CredentialStorage;\n};\n\nexport function getCliConfigDir(cwd = join(homedir(), \".keystroke\")): string {\n return cwd;\n}\n\nexport function getConfigDir(config: Conf<CliConfig>): string {\n return dirname(config.path);\n}\n\nfunction syncPlatformUrlWithWebUrl(config: Conf<CliConfig>): void {\n const webUrl = config.get(\"webUrl\");\n const known = knownPlatformUrlForWebUrl(webUrl);\n if (!known) {\n return;\n }\n\n const stored = config.get(\"platformUrl\");\n if (stored !== known) {\n config.set(\"platformUrl\", known);\n }\n}\n\nexport function createCliConfig(cwd = getCliConfigDir()): Conf<CliConfig> {\n const config = new Conf<CliConfig>({\n projectName: \"keystroke\",\n cwd,\n schema: {\n webUrl: {\n type: \"string\",\n default: DEFAULT_WEB_URL,\n },\n platformUrl: {\n type: \"string\",\n default: DEFAULT_PLATFORM_URL,\n },\n credentialStorage: {\n type: \"string\",\n enum: [\"keychain\", \"file\"],\n },\n },\n });\n\n syncPlatformUrlWithWebUrl(config);\n return config;\n}\n\n/** Effective credential backend — keychain unless the user explicitly chose file. */\nexport function getCredentialStorage(config: Conf<CliConfig>): CredentialStorage {\n const stored = config.get(\"credentialStorage\");\n return isCredentialStorage(stored) ? stored : \"keychain\";\n}\n\nexport function getWebUrl(config: Conf<CliConfig>): string {\n return config.get(\"webUrl\");\n}\n\nexport function getPlatformUrl(config: Conf<CliConfig>): string {\n const apiKey = process.env.KEYSTROKE_API_KEY?.trim();\n const fromEnv = process.env.KEYSTROKE_PLATFORM_URL?.trim();\n if (apiKey && fromEnv) {\n return fromEnv.replace(/\\/+$/, \"\");\n }\n return resolvePlatformUrlForWebUrl(getWebUrl(config), {\n fallback: config.get(\"platformUrl\"),\n });\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nfunction isCliPackage(dir: string): boolean {\n const packageJsonPath = join(dir, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { name?: string };\n return pkg.name === \"@keystrokehq/cli\";\n}\n\nexport function resolveCliRoot(fromModuleUrl: string): string {\n let dir = dirname(fileURLToPath(fromModuleUrl));\n\n while (dir !== dirname(dir)) {\n if (isCliPackage(dir)) {\n return dir;\n }\n\n dir = dirname(dir);\n }\n\n throw new Error(\"Could not resolve keystroke CLI package root\");\n}\n","import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolveCliRoot } from \"./project/resolve-cli-root\";\n\nexport function readCliVersion(): string {\n const packageJsonPath = join(resolveCliRoot(import.meta.url), \"package.json\");\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n}\n","//#region src/cli-release.ts\nconst STAGING_PLATFORM_HOST = \"staging-api.keystroke.ai\";\nconst CLI_PACKAGE_NAME = \"@keystrokehq/cli\";\nconst REGISTRY_URL = \"https://registry.npmjs.org/@keystrokehq%2Fcli\";\nconst REQUEST_TIMEOUT_MS = 3e3;\n/**\n* npm dist-tag for `@keystrokehq/cli` auto-update / explicit update.\n*\n* Priority: KEYSTROKE_CLI_DIST_TAG → KEYSTROKE_ENVIRONMENT=staging →\n* staging platform URL → latest.\n*/\nfunction resolveCliDistTag(env = process.env) {\n\tconst explicit = env.KEYSTROKE_CLI_DIST_TAG?.trim();\n\tif (explicit) return explicit;\n\tif (env.KEYSTROKE_ENVIRONMENT?.trim() === \"staging\") return \"staging\";\n\tfor (const key of [\"KEYSTROKE_PLATFORM_URL\", \"PUBLIC_PLATFORM_URL\"]) {\n\t\tconst raw = env[key]?.trim();\n\t\tif (!raw) continue;\n\t\ttry {\n\t\t\tif (new URL(raw).hostname === STAGING_PLATFORM_HOST) return \"staging\";\n\t\t} catch {}\n\t}\n\treturn \"latest\";\n}\nfunction cliPackageSpec(distTagOrVersion = resolveCliDistTag()) {\n\treturn `${CLI_PACKAGE_NAME}@${distTagOrVersion}`;\n}\nfunction parseVersion(version) {\n\tconst [major = 0, minor = 0, patch = 0] = (version.trim().replace(/^v/, \"\").split(\"-\")[0] ?? \"\").split(\".\").map((part) => {\n\t\tconst value = Number.parseInt(part, 10);\n\t\treturn Number.isFinite(value) ? value : 0;\n\t});\n\treturn [\n\t\tmajor,\n\t\tminor,\n\t\tpatch\n\t];\n}\n/** `0.0.0-staging.<timestamp>` → timestamp, else undefined. */\nfunction stagingSnapshotTimestamp(version) {\n\tconst match = version.trim().replace(/^v/, \"\").match(/^0\\.0\\.0-staging\\.(\\d+)$/);\n\tif (!match?.[1]) return;\n\tconst value = Number.parseInt(match[1], 10);\n\treturn Number.isFinite(value) ? value : void 0;\n}\nfunction isStagingSnapshotVersion(version) {\n\treturn stagingSnapshotTimestamp(version) !== void 0;\n}\nfunction isNewerVersion(latest, current) {\n\tconst latestStaging = stagingSnapshotTimestamp(latest);\n\tconst currentStaging = stagingSnapshotTimestamp(current);\n\tif (latestStaging !== void 0 && currentStaging !== void 0) return latestStaging > currentStaging;\n\tconst [latestMajor, latestMinor, latestPatch] = parseVersion(latest);\n\tconst [currentMajor, currentMinor, currentPatch] = parseVersion(current);\n\tif (latestMajor !== currentMajor) return latestMajor > currentMajor;\n\tif (latestMinor !== currentMinor) return latestMinor > currentMinor;\n\treturn latestPatch > currentPatch;\n}\n/**\n* Whether auto-update / `keystroke update` should install `registryVersion`.\n* Handles channel switches (prod ↔ staging) where semver alone would refuse.\n*/\nfunction shouldUpdateCliVersion(input) {\n\tconst { registryVersion, currentVersion, distTag } = input;\n\tif (registryVersion.trim() === currentVersion.trim()) return false;\n\tconst currentIsStaging = isStagingSnapshotVersion(currentVersion);\n\tconst registryIsStaging = isStagingSnapshotVersion(registryVersion);\n\tif (distTag === \"staging\") {\n\t\tif (!currentIsStaging) return true;\n\t\treturn isNewerVersion(registryVersion, currentVersion);\n\t}\n\tif (currentIsStaging && !registryIsStaging) return true;\n\treturn isNewerVersion(registryVersion, currentVersion);\n}\nasync function fetchLatestCliRelease(distTag = resolveCliDistTag(), options) {\n\tconst timeoutMs = options?.timeoutMs ?? REQUEST_TIMEOUT_MS;\n\tconst fetchImpl = options?.fetchImpl ?? fetch;\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), timeoutMs);\n\ttry {\n\t\tconst response = await fetchImpl(REGISTRY_URL, {\n\t\t\tsignal: controller.signal,\n\t\t\theaders: { Accept: \"application/json\" }\n\t\t});\n\t\tif (!response.ok) return;\n\t\tconst payload = await response.json();\n\t\tconst version = payload[\"dist-tags\"]?.[distTag]?.trim();\n\t\tif (!version) return;\n\t\treturn {\n\t\t\tversion,\n\t\t\tpublishedAt: payload.time?.[version],\n\t\t\tdistTag\n\t\t};\n\t} catch {\n\t\treturn;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\nasync function fetchLatestCliVersion(distTag = resolveCliDistTag(), options) {\n\treturn (await fetchLatestCliRelease(distTag, options))?.version;\n}\n//#endregion\nexport { CLI_PACKAGE_NAME, cliPackageSpec, fetchLatestCliRelease, fetchLatestCliVersion, isNewerVersion, isStagingSnapshotVersion, resolveCliDistTag, shouldUpdateCliVersion };\n\n//# sourceMappingURL=cli-release.mjs.map","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ExpectedCliError } from \"../errors/expected-cli-error\";\nimport { isNewerVersion } from \"./compare-version\";\n\nexport const SDK_PACKAGE_NAME = \"@keystrokehq/keystroke\";\nexport const CLI_PACKAGE_NAME = \"@keystrokehq/cli\";\n\nconst KEYSTROKE_SCOPE = \"@keystrokehq/\";\nconst REGISTRY_TIMEOUT_MS = 3_000;\n\ntype ManagedPackage = {\n name: string;\n};\n\ntype OutdatedPackage = {\n name: string;\n installed?: string;\n latest?: string;\n};\n\nfunction shouldSkip(): boolean {\n return Boolean(\n process.env.KEYSTROKE_SKIP_UPDATE || process.env.CI === \"true\" || process.env.CI === \"1\",\n );\n}\n\nfunction readPackageJson(root: string):\n | {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n }\n | undefined {\n const path = join(root, \"package.json\");\n if (!existsSync(path)) {\n return undefined;\n }\n\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n } catch {\n return undefined;\n }\n}\n\n/** Local/workspace specs are left alone; everything else (ranges and exact pins) must stay current. */\nexport function isLocalSdkSpec(spec: string | undefined): boolean {\n if (!spec) {\n return true;\n }\n\n const trimmed = spec.trim();\n return (\n trimmed.startsWith(\"workspace:\") || trimmed.startsWith(\"link:\") || trimmed.startsWith(\"file:\")\n );\n}\n\nfunction listManagedKeystrokePackages(root: string): ManagedPackage[] {\n const pkg = readPackageJson(root);\n if (!pkg) {\n return [];\n }\n\n const packages: ManagedPackage[] = [];\n\n for (const [name, spec] of Object.entries({\n ...pkg.dependencies,\n ...pkg.devDependencies,\n })) {\n // CLI is global + self-updating; never gate project deps on a local copy.\n if (name === CLI_PACKAGE_NAME) {\n continue;\n }\n if (name.startsWith(KEYSTROKE_SCOPE) && !isLocalSdkSpec(spec)) {\n packages.push({ name });\n }\n }\n\n return packages;\n}\n\nfunction readInstalledVersion(root: string, packageName: string): string | undefined {\n const path = join(root, \"node_modules\", ...packageName.split(\"/\"), \"package.json\");\n if (!existsSync(path)) {\n return undefined;\n }\n\n try {\n const pkg = JSON.parse(readFileSync(path, \"utf8\")) as { version?: string };\n const version = pkg.version?.trim();\n return version || undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function fetchLatestPackageVersion(packageName: string): Promise<string | undefined> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);\n\n try {\n const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {\n signal: controller.signal,\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n return undefined;\n }\n\n const payload = (await response.json()) as {\n \"dist-tags\"?: { latest?: string };\n };\n return payload[\"dist-tags\"]?.latest?.trim() || undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction formatOutdatedLine(pkg: OutdatedPackage): string {\n if (pkg.installed && pkg.latest) {\n return `- ${pkg.name} (${pkg.installed} → ${pkg.latest})`;\n }\n if (pkg.latest) {\n return `- ${pkg.name} (not installed → ${pkg.latest})`;\n }\n return `- ${pkg.name}`;\n}\n\nasync function packagesNeedingUpdate(\n root: string,\n packages: ManagedPackage[],\n): Promise<OutdatedPackage[]> {\n const checks = await Promise.all(\n packages.map(async (pkg): Promise<OutdatedPackage | undefined> => {\n const installed = readInstalledVersion(root, pkg.name);\n const latest = await fetchLatestPackageVersion(pkg.name);\n\n // Unknown registry: only fail when the package isn't present locally.\n if (!latest) {\n return installed ? undefined : { name: pkg.name, installed };\n }\n\n if (!installed || isNewerVersion(latest, installed)) {\n return { name: pkg.name, installed, latest };\n }\n\n return undefined;\n }),\n );\n\n return checks.flatMap((pkg) => (pkg ? [pkg] : []));\n}\n\n/**\n * Require @keystrokehq/* project dependencies to be on latest before build/deploy.\n * Skips `@keystrokehq/cli` (global + self-updating). Throws instead of auto-installing.\n */\nexport async function ensureSdkCurrent(root: string): Promise<void> {\n if (shouldSkip()) {\n return;\n }\n\n const packages = listManagedKeystrokePackages(root);\n if (packages.length === 0) {\n return;\n }\n\n const outdated = await packagesNeedingUpdate(root, packages);\n if (outdated.length === 0) {\n return;\n }\n\n const lines = outdated.map(formatOutdatedLine).join(\"\\n\");\n throw new ExpectedCliError(\n `@keystrokehq packages are behind npm latest:\\n${lines}\\n\\n` +\n `Run \\`keystroke update\\`, then retry.`,\n );\n}\n","import { spawnSync } from \"node:child_process\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nconst MANAGERS: PackageManager[] = [\"pnpm\", \"npm\", \"yarn\", \"bun\"];\n\nexport function detectPackageManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent ?? \"\";\n\n for (const manager of MANAGERS) {\n if (userAgent.startsWith(manager)) {\n return manager;\n }\n }\n\n if (process.env.PNPM_HOME) {\n return \"pnpm\";\n }\n\n return \"npm\";\n}\n\nexport function resolvePackageManager(explicit?: string): PackageManager {\n if (!explicit) {\n return detectPackageManager();\n }\n\n const normalized = explicit.trim().toLowerCase();\n if (!MANAGERS.includes(normalized as PackageManager)) {\n throw new Error(`Unsupported package manager \"${explicit}\". Use npm, pnpm, yarn, or bun.`);\n }\n\n return normalized as PackageManager;\n}\n\n/** Resolve GitHub Packages auth for @keystrokehq/* when NODE_AUTH_TOKEN is unset. */\nexport function resolveGithubPackagesToken(): string | undefined {\n if (process.env.NODE_AUTH_TOKEN) {\n return process.env.NODE_AUTH_TOKEN;\n }\n\n for (const key of [\"GITHUB_TOKEN\", \"GH_TOKEN\"] as const) {\n const value = process.env[key];\n if (value) {\n return value;\n }\n }\n\n const gh = spawnSync(\"gh\", [\"auth\", \"token\"], { encoding: \"utf8\" });\n if (gh.status === 0) {\n const token = gh.stdout.trim();\n if (token) {\n return token;\n }\n }\n\n return undefined;\n}\n\nexport function installDependencies(cwd: string, manager: PackageManager): void {\n const token = resolveGithubPackagesToken();\n const env = token ? { ...process.env, NODE_AUTH_TOKEN: token } : process.env;\n\n const result = spawnSync(manager, [\"install\"], {\n cwd,\n stdio: \"inherit\",\n env,\n });\n\n if (result.status !== 0) {\n throw new Error(`${manager} install failed`);\n }\n}\n\nexport function installPlaygroundDependencies(cwd: string): void {\n const result = spawnSync(\"pnpm\", [\"install\", \"--ignore-workspace\"], {\n cwd,\n stdio: \"inherit\",\n env: process.env,\n });\n\n if (result.status !== 0) {\n throw new Error(\"pnpm install failed\");\n }\n}\n\n/**\n * Build every workspace package so the playground's `link:` deps resolve to\n * compiled `dist/`. Without this, local imports fail with\n * `ERR_MODULE_NOT_FOUND` against whichever package was never built. Turbo\n * caches, so this is only slow on the first run.\n */\nexport function buildPlaygroundWorkspace(monorepoRoot: string): void {\n // Build with a clean Node env: the `keystroke-dev` wrapper sets\n // NODE_OPTIONS=--conditions=development, which would make tsdown/turbo resolve\n // their own deps to src/ and fail. Dropping NODE_OPTIONS builds to dist/ normally.\n const { NODE_OPTIONS: _drop, ...buildEnv } = process.env;\n const result = spawnSync(\"pnpm\", [\"run\", \"build\"], {\n cwd: monorepoRoot,\n stdio: \"inherit\",\n env: buildEnv,\n });\n\n if (result.status !== 0) {\n throw new Error(\"workspace build failed (pnpm run build)\");\n }\n}\n","import { existsSync, realpathSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\n\nimport { detectPackageManager, type PackageManager } from \"../init/package-manager\";\n\nexport type CliInstallKind = \"global\" | \"local\";\n\nexport type CliInstallInfo = {\n kind: CliInstallKind;\n packageManager: PackageManager;\n packageRoot: string;\n projectRoot?: string;\n /**\n * When the CLI was installed with `npm install -g --prefix <dir>` (Modal /\n * session images use `/root/.keystroke`), updates must pass the same prefix\n * so npm does not fight a `/usr/local/bin/keystroke` symlink.\n */\n npmPrefix?: string;\n};\n\nfunction realpathSafe(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\n/**\n * Resolve an npm `--prefix` root from a global package path.\n * Layout: `<prefix>/lib/node_modules/@keystrokehq/cli`.\n */\nexport function resolveNpmInstallPrefix(packageRoot: string): string | undefined {\n const normalized = packageRoot.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n const match = normalized.match(/^(.*)\\/lib\\/node_modules\\/@keystrokehq\\/cli$/);\n const prefix = match?.[1]?.trim();\n return prefix && prefix.length > 0 ? prefix : undefined;\n}\n\nexport function detectManagerFromLockfile(dir: string): PackageManager | undefined {\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) {\n return \"pnpm\";\n }\n\n if (existsSync(join(dir, \"bun.lockb\")) || existsSync(join(dir, \"bun.lock\"))) {\n return \"bun\";\n }\n\n if (existsSync(join(dir, \"yarn.lock\"))) {\n return \"yarn\";\n }\n\n if (existsSync(join(dir, \"package-lock.json\"))) {\n return \"npm\";\n }\n\n return undefined;\n}\n\nfunction findLocalProjectRoot(packageRoot: string): string | undefined {\n const normalizedRoot = realpathSafe(packageRoot);\n let dir = dirname(packageRoot);\n\n while (dir !== dirname(dir)) {\n const installedCliPath = join(dir, \"node_modules\", \"@keystrokehq\", \"cli\");\n if (\n existsSync(join(dir, \"package.json\")) &&\n realpathSafe(installedCliPath) === normalizedRoot\n ) {\n return dir;\n }\n\n dir = dirname(dir);\n }\n\n return undefined;\n}\n\nfunction globalRootForManager(manager: PackageManager): string | undefined {\n const commands: Record<PackageManager, { command: string; args: string[] }> = {\n pnpm: { command: \"pnpm\", args: [\"root\", \"-g\"] },\n npm: { command: \"npm\", args: [\"root\", \"-g\"] },\n yarn: { command: \"yarn\", args: [\"global\", \"dir\"] },\n bun: { command: \"bun\", args: [\"pm\", \"bin\"] },\n };\n\n const { command, args } = commands[manager];\n const result = spawnSync(command, args, { encoding: \"utf8\" });\n\n if (result.status !== 0) {\n return undefined;\n }\n\n const root = result.stdout.trim();\n return root ? realpathSafe(root) : undefined;\n}\n\nfunction detectGlobalManager(packageRoot: string): PackageManager {\n const normalizedRoot = realpathSafe(packageRoot);\n\n if (normalizedRoot.includes(`${join(\"\", \".pnpm\")}`)) {\n return \"pnpm\";\n }\n\n for (const manager of [\"pnpm\", \"npm\", \"yarn\", \"bun\"] as const) {\n const globalRoot = globalRootForManager(manager);\n if (globalRoot && normalizedRoot.startsWith(globalRoot)) {\n return manager;\n }\n }\n\n if (process.env.PNPM_HOME) {\n return \"pnpm\";\n }\n\n return detectPackageManager();\n}\n\nexport function detectCliInstall(packageRoot: string): CliInstallInfo | undefined {\n const normalizedRoot = realpathSafe(packageRoot);\n const projectRoot = findLocalProjectRoot(normalizedRoot);\n\n if (projectRoot) {\n const packageManager = detectManagerFromLockfile(projectRoot) ?? detectPackageManager();\n\n return {\n kind: \"local\",\n packageManager,\n packageRoot: normalizedRoot,\n projectRoot,\n };\n }\n\n const npmPrefix = resolveNpmInstallPrefix(normalizedRoot);\n if (npmPrefix) {\n return {\n kind: \"global\",\n packageManager: \"npm\",\n packageRoot: normalizedRoot,\n npmPrefix,\n };\n }\n\n return {\n kind: \"global\",\n packageManager: detectGlobalManager(normalizedRoot),\n packageRoot: normalizedRoot,\n };\n}\n","const RELEASE_AGE_PATTERNS = [\n /minimumReleaseAge/i,\n /minimum-release-age/i,\n /ERR_PNPM_NO_MATURE_MATCHING_VERSION/,\n /ERR_PNPM_MINIMUM_RELEASE_AGE/,\n /does not meet the minimumReleaseAge constraint/,\n];\n\nexport function isReleaseAgeBlock(output: string): boolean {\n return RELEASE_AGE_PATTERNS.some((pattern) => pattern.test(output));\n}\n\nexport function formatReleaseAgeBlockMessage(options: {\n currentVersion: string;\n availableVersion: string;\n retryAfter?: Date;\n}): string {\n const retry =\n options.retryAfter !== undefined ? ` Retry after ${options.retryAfter.toLocaleString()}.` : \"\";\n\n return (\n `@keystrokehq/cli ${options.availableVersion} is available but blocked by your ` +\n `minimum-release-age setting. You're on ${options.currentVersion}.${retry} ` +\n `Run \\`keystroke update\\` again — it bypasses minimum-release-age for @keystrokehq packages and retries.`\n );\n}\n","import { spawnSync } from \"node:child_process\";\n\nexport function readPnpmMinimumReleaseAgeMinutes(): number | undefined {\n const result = spawnSync(\"pnpm\", [\"config\", \"get\", \"minimum-release-age\"], {\n encoding: \"utf8\",\n });\n\n if (result.status !== 0) {\n return undefined;\n }\n\n const value = result.stdout.trim();\n if (!value || value === \"undefined\" || value === \"0\") {\n return undefined;\n }\n\n const minutes = Number.parseInt(value, 10);\n return Number.isFinite(minutes) && minutes > 0 ? minutes : undefined;\n}\n\nexport function computeReleaseAgeRetryAfter(\n publishedAt: string | undefined,\n minimumReleaseAgeMinutes: number | undefined,\n): Date | undefined {\n if (!publishedAt || !minimumReleaseAgeMinutes) {\n return undefined;\n }\n\n const publishedMs = Date.parse(publishedAt);\n if (!Number.isFinite(publishedMs)) {\n return undefined;\n }\n\n return new Date(publishedMs + minimumReleaseAgeMinutes * 60_000);\n}\n","/** pnpm global installs live in `<pnpm-home>/global/<major>/`. */\nexport function resolveGlobalPnpmRoot(packageRoot: string): string | undefined {\n const normalized = packageRoot.replace(/\\\\/g, \"/\");\n const match = normalized.match(/^(.*\\/global\\/\\d+)\\//);\n return match?.[1];\n}\n","import type { CliInstallInfo } from \"./detect-cli-install\";\nimport { cliPackageSpec, resolveCliDistTag } from \"./resolve-cli-dist-tag\";\nimport { resolveGlobalPnpmRoot } from \"./resolve-global-pnpm-root\";\n\nexport function buildUpdateCommand(\n install: CliInstallInfo,\n distTag: string = resolveCliDistTag(),\n): {\n command: string;\n args: string[];\n cwd?: string;\n} {\n const { kind, packageManager } = install;\n const packageName = cliPackageSpec(distTag);\n\n if (kind === \"global\") {\n switch (packageManager) {\n case \"pnpm\":\n return {\n command: \"pnpm\",\n args: [\"add\", \"-g\", packageName],\n cwd: resolveGlobalPnpmRoot(install.packageRoot),\n };\n case \"yarn\":\n return { command: \"yarn\", args: [\"global\", \"add\", packageName] };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-g\", packageName] };\n case \"npm\":\n default:\n if (install.npmPrefix) {\n return {\n command: \"npm\",\n args: [\"install\", \"-g\", \"--prefix\", install.npmPrefix, packageName],\n };\n }\n return { command: \"npm\", args: [\"install\", \"-g\", packageName] };\n }\n }\n\n const cwd = install.projectRoot;\n switch (packageManager) {\n case \"pnpm\":\n return { command: \"pnpm\", args: [\"add\", \"-D\", packageName], cwd };\n case \"yarn\":\n return { command: \"yarn\", args: [\"add\", \"-D\", packageName], cwd };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-D\", packageName], cwd };\n case \"npm\":\n default:\n return { command: \"npm\", args: [\"install\", \"-D\", packageName], cwd };\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst EXCLUDE_PREFIX = \"minimum-release-age-exclude[]=\";\n\nfunction npmrcPath(root: string): string {\n return join(root, \".npmrc\");\n}\n\n/** Package names already listed as `minimum-release-age-exclude[]=` in an .npmrc body. */\nexport function parseMinimumReleaseAgeExcludes(contents: string): string[] {\n const found: string[] = [];\n for (const rawLine of contents.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line.startsWith(EXCLUDE_PREFIX)) {\n continue;\n }\n const value = line.slice(EXCLUDE_PREFIX.length).trim();\n if (value) {\n found.push(value);\n }\n }\n return found;\n}\n\n/**\n * Ensure each package is listed in the project `.npmrc` as\n * `minimum-release-age-exclude[]=<name>` so `pnpm add pkg@latest` is not held\n * back by minimum-release-age. Creates `.npmrc` when missing.\n *\n * Returns the packages that were newly added (empty when already present).\n */\nexport function ensureMinimumReleaseAgeExclude(root: string, packageNames: string[]): string[] {\n const unique = [...new Set(packageNames.map((name) => name.trim()).filter(Boolean))].sort();\n if (unique.length === 0) {\n return [];\n }\n\n const path = npmrcPath(root);\n const existing = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n const already = new Set(parseMinimumReleaseAgeExcludes(existing));\n const added = unique.filter((name) => !already.has(name));\n if (added.length === 0) {\n return [];\n }\n\n const trimmed = existing.replace(/\\s+$/, \"\");\n const next = [\n ...(trimmed.length > 0 ? [trimmed] : []),\n ...added.map((name) => `${EXCLUDE_PREFIX}${name}`),\n \"\",\n ].join(\"\\n\");\n writeFileSync(path, next, \"utf8\");\n return added;\n}\n","import { spawnSync } from \"node:child_process\";\n\nimport { buildUpdateCommand } from \"./build-update-command\";\nimport type { CliInstallInfo } from \"./detect-cli-install\";\nimport { isReleaseAgeBlock } from \"./detect-release-age-block\";\nimport { ensureMinimumReleaseAgeExclude } from \"./ensure-release-age-exclude\";\nimport { CLI_PACKAGE_NAME } from \"./ensure-sdk-current\";\n\nexport function updateSpawnEnv(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n const pnpmHome = env.PNPM_HOME;\n\n if (!pnpmHome) {\n return env;\n }\n\n const pathKey = process.platform === \"win32\" ? \"Path\" : \"PATH\";\n const path = env[pathKey] ?? \"\";\n const segments = path.split(process.platform === \"win32\" ? \";\" : \":\");\n const binDir = `${pnpmHome}/bin`;\n const prefix = [binDir, pnpmHome].filter((dir) => !segments.includes(dir));\n\n if (prefix.length > 0) {\n env[pathKey] = [...prefix, path].filter(Boolean).join(process.platform === \"win32\" ? \";\" : \":\");\n }\n\n return env;\n}\n\nexport type PackageManagerRunResult = {\n ok: boolean;\n output: string;\n};\n\nfunction runPackageManager(command: string, args: string[], cwd?: string): PackageManagerRunResult {\n const result = spawnSync(command, args, {\n cwd,\n encoding: \"utf8\",\n env: updateSpawnEnv(),\n });\n\n const output = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`;\n\n return {\n ok: result.status === 0,\n output,\n };\n}\n\nfunction migratePnpmGlobal(cwd: string): boolean {\n const result = spawnSync(\"pnpm\", [\"install\"], {\n cwd,\n encoding: \"utf8\",\n env: {\n ...updateSpawnEnv(),\n CI: \"true\",\n },\n });\n\n return result.status === 0;\n}\n\n/** Bypass minimum-release-age for this one CLI install without editing user config. */\nfunction withCliReleaseAgeExclude(args: string[]): string[] {\n return [...args, `--config.minimum-release-age-exclude[]=${CLI_PACKAGE_NAME}`];\n}\n\nexport function runPackageManagerUpdate(\n install: CliInstallInfo,\n distTag?: string,\n): PackageManagerRunResult {\n const { command, args, cwd } = buildUpdateCommand(install, distTag);\n const firstAttempt = runPackageManager(command, args, cwd);\n\n if (firstAttempt.ok) {\n return firstAttempt;\n }\n\n if (install.kind === \"global\" && install.packageManager === \"pnpm\" && cwd) {\n if (migratePnpmGlobal(cwd)) {\n const migrated = runPackageManager(command, args, cwd);\n if (migrated.ok) {\n return migrated;\n }\n }\n }\n\n if (install.packageManager === \"pnpm\" && isReleaseAgeBlock(firstAttempt.output)) {\n if (install.kind === \"local\" && install.projectRoot) {\n const added = ensureMinimumReleaseAgeExclude(install.projectRoot, [CLI_PACKAGE_NAME]);\n if (added.length > 0) {\n process.stderr.write(\n `Added ${CLI_PACKAGE_NAME} to .npmrc minimum-release-age-exclude; retrying CLI update...\\n`,\n );\n }\n return runPackageManager(command, args, cwd);\n }\n\n process.stderr.write(\n `Retrying CLI update with temporary minimum-release-age-exclude for ${CLI_PACKAGE_NAME}...\\n`,\n );\n return runPackageManager(command, withCliReleaseAgeExclude(args), cwd);\n }\n\n return firstAttempt;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"run-package-manager-update-D_nLqg4e.mjs","names":["CLI_PACKAGE_NAME"],"sources":["../src/errors/expected-cli-error.ts","../src/auth/credential-storage.ts","../src/resolve-platform-url.ts","../src/config.ts","../src/project/resolve-cli-root.ts","../src/version.ts","../../../packages/shared/dist/cli-release.mjs","../src/update/ensure-sdk-current.ts","../src/init/package-manager.ts","../src/update/detect-cli-install.ts","../src/update/detect-release-age-block.ts","../src/update/read-pnpm-release-age-minutes.ts","../src/update/resolve-global-pnpm-root.ts","../src/update/build-update-command.ts","../src/update/ensure-release-age-exclude.ts","../src/update/run-package-manager-update.ts"],"sourcesContent":["/**\n * User/project failures that are expected CLI exits (lint, typecheck, validation, etc.).\n * These should not become Error Tracking issues.\n */\nexport class ExpectedCliError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ExpectedCliError\";\n }\n}\n\nexport function isExpectedCliError(error: unknown): error is ExpectedCliError {\n return error instanceof ExpectedCliError;\n}\n","import { ExpectedCliError } from \"../errors/expected-cli-error\";\n\nexport const CREDENTIAL_STORAGE_VALUES = [\"keychain\", \"file\"] as const;\n\nexport type CredentialStorage = (typeof CREDENTIAL_STORAGE_VALUES)[number];\n\nexport function isCredentialStorage(value: unknown): value is CredentialStorage {\n return value === \"keychain\" || value === \"file\";\n}\n\nexport function parseCredentialStorage(value: string): CredentialStorage {\n if (!isCredentialStorage(value)) {\n throw new ExpectedCliError(`Invalid credential storage \"${value}\". Use keychain or file.`);\n }\n return value;\n}\n","import { LOCAL_PLATFORM_ORIGIN, originFromPublicUrl } from \"@keystrokehq/shared\";\n\nexport const DEFAULT_WEB_URL = \"https://keystroke.ai\";\nexport const DEFAULT_PLATFORM_URL = \"https://api.keystroke.ai\";\nexport const LOCAL_WEB_URL = \"http://localhost:3000\";\n\nexport type ResolvePlatformUrlOptions = {\n platformUrl?: string;\n /** Used when webUrl does not match a known deployment. */\n fallback?: string;\n};\n\nfunction webOriginFromUrl(webUrl: string): string {\n return originFromPublicUrl(webUrl, webUrl.replace(/\\/+$/, \"\"));\n}\n\n/** Known web origins with a fixed platform API — undefined for custom/staging URLs. */\nexport function knownPlatformUrlForWebUrl(webUrl: string): string | undefined {\n const webOrigin = webOriginFromUrl(webUrl);\n\n if (webOrigin === LOCAL_WEB_URL || webOrigin === \"http://127.0.0.1:3000\") {\n return LOCAL_PLATFORM_ORIGIN;\n }\n\n if (webOrigin === DEFAULT_WEB_URL) {\n return DEFAULT_PLATFORM_URL;\n }\n\n return undefined;\n}\n\nexport function resolvePlatformUrlForWebUrl(\n webUrl: string,\n options: ResolvePlatformUrlOptions = {},\n): string {\n const explicit = options.platformUrl?.trim();\n if (explicit) {\n return explicit.replace(/\\/+$/, \"\");\n }\n\n const known = knownPlatformUrlForWebUrl(webUrl);\n if (known) {\n return known;\n }\n\n const fallback = options.fallback?.trim() || DEFAULT_PLATFORM_URL;\n return fallback.replace(/\\/+$/, \"\");\n}\n","import Conf from \"conf\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport { isCredentialStorage, type CredentialStorage } from \"./auth/credential-storage\";\nimport {\n DEFAULT_PLATFORM_URL,\n DEFAULT_WEB_URL,\n knownPlatformUrlForWebUrl,\n resolvePlatformUrlForWebUrl,\n} from \"./resolve-platform-url\";\n\nexport type CliConfig = {\n webUrl: string;\n platformUrl: string;\n /** Where session tokens are stored. Defaults to OS keychain when unset. */\n credentialStorage?: CredentialStorage;\n};\n\nexport function getCliConfigDir(cwd = join(homedir(), \".keystroke\")): string {\n return cwd;\n}\n\nexport function getConfigDir(config: Conf<CliConfig>): string {\n return dirname(config.path);\n}\n\nfunction syncPlatformUrlWithWebUrl(config: Conf<CliConfig>): void {\n const webUrl = config.get(\"webUrl\");\n const known = knownPlatformUrlForWebUrl(webUrl);\n if (!known) {\n return;\n }\n\n const stored = config.get(\"platformUrl\");\n if (stored !== known) {\n config.set(\"platformUrl\", known);\n }\n}\n\nexport function createCliConfig(cwd = getCliConfigDir()): Conf<CliConfig> {\n const config = new Conf<CliConfig>({\n projectName: \"keystroke\",\n cwd,\n schema: {\n webUrl: {\n type: \"string\",\n default: DEFAULT_WEB_URL,\n },\n platformUrl: {\n type: \"string\",\n default: DEFAULT_PLATFORM_URL,\n },\n credentialStorage: {\n type: \"string\",\n enum: [\"keychain\", \"file\"],\n },\n },\n });\n\n syncPlatformUrlWithWebUrl(config);\n return config;\n}\n\n/** Effective credential backend — keychain unless the user explicitly chose file. */\nexport function getCredentialStorage(config: Conf<CliConfig>): CredentialStorage {\n const stored = config.get(\"credentialStorage\");\n return isCredentialStorage(stored) ? stored : \"keychain\";\n}\n\nexport function getWebUrl(config: Conf<CliConfig>): string {\n return config.get(\"webUrl\");\n}\n\nexport function getPlatformUrl(config: Conf<CliConfig>): string {\n const apiKey = process.env.KEYSTROKE_API_KEY?.trim();\n const fromEnv = process.env.KEYSTROKE_PLATFORM_URL?.trim();\n if (apiKey && fromEnv) {\n return fromEnv.replace(/\\/+$/, \"\");\n }\n return resolvePlatformUrlForWebUrl(getWebUrl(config), {\n fallback: config.get(\"platformUrl\"),\n });\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nfunction isCliPackage(dir: string): boolean {\n const packageJsonPath = join(dir, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n return false;\n }\n\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { name?: string };\n return pkg.name === \"@keystrokehq/cli\";\n}\n\nexport function resolveCliRoot(fromModuleUrl: string): string {\n let dir = dirname(fileURLToPath(fromModuleUrl));\n\n while (dir !== dirname(dir)) {\n if (isCliPackage(dir)) {\n return dir;\n }\n\n dir = dirname(dir);\n }\n\n throw new Error(\"Could not resolve keystroke CLI package root\");\n}\n","import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { resolveCliRoot } from \"./project/resolve-cli-root\";\n\nexport function readCliVersion(): string {\n const packageJsonPath = join(resolveCliRoot(import.meta.url), \"package.json\");\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n}\n","//#region src/cli-release.ts\nconst STAGING_PLATFORM_HOST = \"staging-api.keystroke.ai\";\nconst CLI_PACKAGE_NAME = \"@keystrokehq/cli\";\nconst REGISTRY_URL = \"https://registry.npmjs.org/@keystrokehq%2Fcli\";\nconst REQUEST_TIMEOUT_MS = 3e3;\n/**\n* npm dist-tag for `@keystrokehq/cli` auto-update / explicit update.\n*\n* Priority: KEYSTROKE_CLI_DIST_TAG → KEYSTROKE_ENVIRONMENT=staging →\n* staging platform URL → latest.\n*/\nfunction resolveCliDistTag(env = process.env) {\n\tconst explicit = env.KEYSTROKE_CLI_DIST_TAG?.trim();\n\tif (explicit) return explicit;\n\tif (env.KEYSTROKE_ENVIRONMENT?.trim() === \"staging\") return \"staging\";\n\tfor (const key of [\"KEYSTROKE_PLATFORM_URL\", \"PUBLIC_PLATFORM_URL\"]) {\n\t\tconst raw = env[key]?.trim();\n\t\tif (!raw) continue;\n\t\ttry {\n\t\t\tif (new URL(raw).hostname === STAGING_PLATFORM_HOST) return \"staging\";\n\t\t} catch {}\n\t}\n\treturn \"latest\";\n}\nfunction cliPackageSpec(distTagOrVersion = resolveCliDistTag()) {\n\treturn `${CLI_PACKAGE_NAME}@${distTagOrVersion}`;\n}\nfunction parseVersion(version) {\n\tconst [major = 0, minor = 0, patch = 0] = (version.trim().replace(/^v/, \"\").split(\"-\")[0] ?? \"\").split(\".\").map((part) => {\n\t\tconst value = Number.parseInt(part, 10);\n\t\treturn Number.isFinite(value) ? value : 0;\n\t});\n\treturn [\n\t\tmajor,\n\t\tminor,\n\t\tpatch\n\t];\n}\n/** `0.0.0-staging.<timestamp>` → timestamp, else undefined. */\nfunction stagingSnapshotTimestamp(version) {\n\tconst match = version.trim().replace(/^v/, \"\").match(/^0\\.0\\.0-staging\\.(\\d+)$/);\n\tif (!match?.[1]) return;\n\tconst value = Number.parseInt(match[1], 10);\n\treturn Number.isFinite(value) ? value : void 0;\n}\nfunction isStagingSnapshotVersion(version) {\n\treturn stagingSnapshotTimestamp(version) !== void 0;\n}\nfunction isNewerVersion(latest, current) {\n\tconst latestStaging = stagingSnapshotTimestamp(latest);\n\tconst currentStaging = stagingSnapshotTimestamp(current);\n\tif (latestStaging !== void 0 && currentStaging !== void 0) return latestStaging > currentStaging;\n\tconst [latestMajor, latestMinor, latestPatch] = parseVersion(latest);\n\tconst [currentMajor, currentMinor, currentPatch] = parseVersion(current);\n\tif (latestMajor !== currentMajor) return latestMajor > currentMajor;\n\tif (latestMinor !== currentMinor) return latestMinor > currentMinor;\n\treturn latestPatch > currentPatch;\n}\n/**\n* Whether auto-update / `keystroke update` should install `registryVersion`.\n* Handles channel switches (prod ↔ staging) where semver alone would refuse.\n*/\nfunction shouldUpdateCliVersion(input) {\n\tconst { registryVersion, currentVersion, distTag } = input;\n\tif (registryVersion.trim() === currentVersion.trim()) return false;\n\tconst currentIsStaging = isStagingSnapshotVersion(currentVersion);\n\tconst registryIsStaging = isStagingSnapshotVersion(registryVersion);\n\tif (distTag === \"staging\") {\n\t\tif (!currentIsStaging) return true;\n\t\treturn isNewerVersion(registryVersion, currentVersion);\n\t}\n\tif (currentIsStaging && !registryIsStaging) return true;\n\treturn isNewerVersion(registryVersion, currentVersion);\n}\nasync function fetchLatestCliRelease(distTag = resolveCliDistTag(), options) {\n\tconst timeoutMs = options?.timeoutMs ?? REQUEST_TIMEOUT_MS;\n\tconst fetchImpl = options?.fetchImpl ?? fetch;\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), timeoutMs);\n\ttry {\n\t\tconst response = await fetchImpl(REGISTRY_URL, {\n\t\t\tsignal: controller.signal,\n\t\t\theaders: { Accept: \"application/json\" }\n\t\t});\n\t\tif (!response.ok) return;\n\t\tconst payload = await response.json();\n\t\tconst version = payload[\"dist-tags\"]?.[distTag]?.trim();\n\t\tif (!version) return;\n\t\treturn {\n\t\t\tversion,\n\t\t\tpublishedAt: payload.time?.[version],\n\t\t\tdistTag\n\t\t};\n\t} catch {\n\t\treturn;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t}\n}\nasync function fetchLatestCliVersion(distTag = resolveCliDistTag(), options) {\n\treturn (await fetchLatestCliRelease(distTag, options))?.version;\n}\n//#endregion\nexport { CLI_PACKAGE_NAME, cliPackageSpec, fetchLatestCliRelease, fetchLatestCliVersion, isNewerVersion, isStagingSnapshotVersion, resolveCliDistTag, shouldUpdateCliVersion };\n\n//# sourceMappingURL=cli-release.mjs.map","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ExpectedCliError } from \"../errors/expected-cli-error\";\nimport { isNewerVersion } from \"./compare-version\";\n\nexport const SDK_PACKAGE_NAME = \"@keystrokehq/keystroke\";\nexport const CLI_PACKAGE_NAME = \"@keystrokehq/cli\";\n\nconst KEYSTROKE_SCOPE = \"@keystrokehq/\";\nconst REGISTRY_TIMEOUT_MS = 3_000;\n\ntype ManagedPackage = {\n name: string;\n};\n\ntype OutdatedPackage = {\n name: string;\n installed?: string;\n latest?: string;\n};\n\nfunction shouldSkip(): boolean {\n return Boolean(\n process.env.KEYSTROKE_SKIP_UPDATE || process.env.CI === \"true\" || process.env.CI === \"1\",\n );\n}\n\nfunction readPackageJson(root: string):\n | {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n }\n | undefined {\n const path = join(root, \"package.json\");\n if (!existsSync(path)) {\n return undefined;\n }\n\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n } catch {\n return undefined;\n }\n}\n\n/** Local/workspace specs are left alone; everything else (ranges and exact pins) must stay current. */\nexport function isLocalSdkSpec(spec: string | undefined): boolean {\n if (!spec) {\n return true;\n }\n\n const trimmed = spec.trim();\n return (\n trimmed.startsWith(\"workspace:\") || trimmed.startsWith(\"link:\") || trimmed.startsWith(\"file:\")\n );\n}\n\nfunction listManagedKeystrokePackages(root: string): ManagedPackage[] {\n const pkg = readPackageJson(root);\n if (!pkg) {\n return [];\n }\n\n const packages: ManagedPackage[] = [];\n\n for (const [name, spec] of Object.entries({\n ...pkg.dependencies,\n ...pkg.devDependencies,\n })) {\n // CLI is global + self-updating; never gate project deps on a local copy.\n if (name === CLI_PACKAGE_NAME) {\n continue;\n }\n if (name.startsWith(KEYSTROKE_SCOPE) && !isLocalSdkSpec(spec)) {\n packages.push({ name });\n }\n }\n\n return packages;\n}\n\nfunction readInstalledVersion(root: string, packageName: string): string | undefined {\n const path = join(root, \"node_modules\", ...packageName.split(\"/\"), \"package.json\");\n if (!existsSync(path)) {\n return undefined;\n }\n\n try {\n const pkg = JSON.parse(readFileSync(path, \"utf8\")) as { version?: string };\n const version = pkg.version?.trim();\n return version || undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function fetchLatestPackageVersion(packageName: string): Promise<string | undefined> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);\n\n try {\n const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`, {\n signal: controller.signal,\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok) {\n return undefined;\n }\n\n const payload = (await response.json()) as {\n \"dist-tags\"?: { latest?: string };\n };\n return payload[\"dist-tags\"]?.latest?.trim() || undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nfunction formatOutdatedLine(pkg: OutdatedPackage): string {\n if (pkg.installed && pkg.latest) {\n return `- ${pkg.name} (${pkg.installed} → ${pkg.latest})`;\n }\n if (pkg.latest) {\n return `- ${pkg.name} (not installed → ${pkg.latest})`;\n }\n return `- ${pkg.name}`;\n}\n\nasync function packagesNeedingUpdate(\n root: string,\n packages: ManagedPackage[],\n): Promise<OutdatedPackage[]> {\n const checks = await Promise.all(\n packages.map(async (pkg): Promise<OutdatedPackage | undefined> => {\n const installed = readInstalledVersion(root, pkg.name);\n const latest = await fetchLatestPackageVersion(pkg.name);\n\n // Unknown registry: only fail when the package isn't present locally.\n if (!latest) {\n return installed ? undefined : { name: pkg.name, installed };\n }\n\n if (!installed || isNewerVersion(latest, installed)) {\n return { name: pkg.name, installed, latest };\n }\n\n return undefined;\n }),\n );\n\n return checks.flatMap((pkg) => (pkg ? [pkg] : []));\n}\n\n/**\n * Require @keystrokehq/* project dependencies to be on latest before build/deploy.\n * Skips `@keystrokehq/cli` (global + self-updating). Throws instead of auto-installing.\n */\nexport async function ensureSdkCurrent(root: string): Promise<void> {\n if (shouldSkip()) {\n return;\n }\n\n const packages = listManagedKeystrokePackages(root);\n if (packages.length === 0) {\n return;\n }\n\n const outdated = await packagesNeedingUpdate(root, packages);\n if (outdated.length === 0) {\n return;\n }\n\n const lines = outdated.map(formatOutdatedLine).join(\"\\n\");\n throw new ExpectedCliError(\n `@keystrokehq packages are behind npm latest:\\n${lines}\\n\\n` +\n `Run \\`keystroke update\\`, then retry.`,\n );\n}\n","import { spawnSync } from \"node:child_process\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n\nconst MANAGERS: PackageManager[] = [\"pnpm\", \"npm\", \"yarn\", \"bun\"];\n\nexport function detectPackageManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent ?? \"\";\n\n for (const manager of MANAGERS) {\n if (userAgent.startsWith(manager)) {\n return manager;\n }\n }\n\n if (process.env.PNPM_HOME) {\n return \"pnpm\";\n }\n\n return \"npm\";\n}\n\nexport function resolvePackageManager(explicit?: string): PackageManager {\n if (!explicit) {\n return detectPackageManager();\n }\n\n const normalized = explicit.trim().toLowerCase();\n if (!MANAGERS.includes(normalized as PackageManager)) {\n throw new Error(`Unsupported package manager \"${explicit}\". Use npm, pnpm, yarn, or bun.`);\n }\n\n return normalized as PackageManager;\n}\n\n/** Resolve GitHub Packages auth for @keystrokehq/* when NODE_AUTH_TOKEN is unset. */\nexport function resolveGithubPackagesToken(): string | undefined {\n if (process.env.NODE_AUTH_TOKEN) {\n return process.env.NODE_AUTH_TOKEN;\n }\n\n for (const key of [\"GITHUB_TOKEN\", \"GH_TOKEN\"] as const) {\n const value = process.env[key];\n if (value) {\n return value;\n }\n }\n\n const gh = spawnSync(\"gh\", [\"auth\", \"token\"], { encoding: \"utf8\" });\n if (gh.status === 0) {\n const token = gh.stdout.trim();\n if (token) {\n return token;\n }\n }\n\n return undefined;\n}\n\nexport function installDependencies(cwd: string, manager: PackageManager): void {\n const token = resolveGithubPackagesToken();\n const env = token ? { ...process.env, NODE_AUTH_TOKEN: token } : process.env;\n\n const result = spawnSync(manager, [\"install\"], {\n cwd,\n stdio: \"inherit\",\n env,\n });\n\n if (result.status !== 0) {\n throw new Error(`${manager} install failed`);\n }\n}\n\nexport function installPlaygroundDependencies(cwd: string): void {\n const result = spawnSync(\"pnpm\", [\"install\", \"--ignore-workspace\"], {\n cwd,\n stdio: \"inherit\",\n env: process.env,\n });\n\n if (result.status !== 0) {\n throw new Error(\"pnpm install failed\");\n }\n}\n\n/**\n * Build every workspace package so the playground's `link:` deps resolve to\n * compiled `dist/`. Without this, local imports fail with\n * `ERR_MODULE_NOT_FOUND` against whichever package was never built. Turbo\n * caches, so this is only slow on the first run.\n */\nexport function buildPlaygroundWorkspace(monorepoRoot: string): void {\n // Build with a clean Node env: the `keystroke-dev` wrapper sets\n // NODE_OPTIONS=--conditions=development, which would make tsdown/turbo resolve\n // their own deps to src/ and fail. Dropping NODE_OPTIONS builds to dist/ normally.\n const { NODE_OPTIONS: _drop, ...buildEnv } = process.env;\n const result = spawnSync(\"pnpm\", [\"run\", \"build\"], {\n cwd: monorepoRoot,\n stdio: \"inherit\",\n env: buildEnv,\n });\n\n if (result.status !== 0) {\n throw new Error(\"workspace build failed (pnpm run build)\");\n }\n}\n","import { existsSync, realpathSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport { dirname, join } from \"node:path\";\n\nimport { detectPackageManager, type PackageManager } from \"../init/package-manager\";\n\nexport type CliInstallKind = \"global\" | \"local\";\n\nexport type CliInstallInfo = {\n kind: CliInstallKind;\n packageManager: PackageManager;\n packageRoot: string;\n projectRoot?: string;\n /**\n * When the CLI was installed with `npm install -g --prefix <dir>` (Modal /\n * session images use `/root/.keystroke`), updates must pass the same prefix\n * so npm does not fight a `/usr/local/bin/keystroke` symlink.\n */\n npmPrefix?: string;\n};\n\nfunction realpathSafe(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\n/**\n * Resolve an npm `--prefix` root from a global package path.\n * Layout: `<prefix>/lib/node_modules/@keystrokehq/cli`.\n */\nexport function resolveNpmInstallPrefix(packageRoot: string): string | undefined {\n const normalized = packageRoot.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n const match = normalized.match(/^(.*)\\/lib\\/node_modules\\/@keystrokehq\\/cli$/);\n const prefix = match?.[1]?.trim();\n return prefix && prefix.length > 0 ? prefix : undefined;\n}\n\nexport function detectManagerFromLockfile(dir: string): PackageManager | undefined {\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) {\n return \"pnpm\";\n }\n\n if (existsSync(join(dir, \"bun.lockb\")) || existsSync(join(dir, \"bun.lock\"))) {\n return \"bun\";\n }\n\n if (existsSync(join(dir, \"yarn.lock\"))) {\n return \"yarn\";\n }\n\n if (existsSync(join(dir, \"package-lock.json\"))) {\n return \"npm\";\n }\n\n return undefined;\n}\n\nfunction findLocalProjectRoot(packageRoot: string): string | undefined {\n const normalizedRoot = realpathSafe(packageRoot);\n let dir = dirname(packageRoot);\n\n while (dir !== dirname(dir)) {\n const installedCliPath = join(dir, \"node_modules\", \"@keystrokehq\", \"cli\");\n if (\n existsSync(join(dir, \"package.json\")) &&\n realpathSafe(installedCliPath) === normalizedRoot\n ) {\n return dir;\n }\n\n dir = dirname(dir);\n }\n\n return undefined;\n}\n\nfunction globalRootForManager(manager: PackageManager): string | undefined {\n const commands: Record<PackageManager, { command: string; args: string[] }> = {\n pnpm: { command: \"pnpm\", args: [\"root\", \"-g\"] },\n npm: { command: \"npm\", args: [\"root\", \"-g\"] },\n yarn: { command: \"yarn\", args: [\"global\", \"dir\"] },\n bun: { command: \"bun\", args: [\"pm\", \"bin\"] },\n };\n\n const { command, args } = commands[manager];\n const result = spawnSync(command, args, { encoding: \"utf8\" });\n\n if (result.status !== 0) {\n return undefined;\n }\n\n const root = result.stdout.trim();\n return root ? realpathSafe(root) : undefined;\n}\n\nfunction detectGlobalManager(packageRoot: string): PackageManager {\n const normalizedRoot = realpathSafe(packageRoot);\n\n if (normalizedRoot.includes(`${join(\"\", \".pnpm\")}`)) {\n return \"pnpm\";\n }\n\n for (const manager of [\"pnpm\", \"npm\", \"yarn\", \"bun\"] as const) {\n const globalRoot = globalRootForManager(manager);\n if (globalRoot && normalizedRoot.startsWith(globalRoot)) {\n return manager;\n }\n }\n\n if (process.env.PNPM_HOME) {\n return \"pnpm\";\n }\n\n return detectPackageManager();\n}\n\nexport function detectCliInstall(packageRoot: string): CliInstallInfo | undefined {\n const normalizedRoot = realpathSafe(packageRoot);\n const projectRoot = findLocalProjectRoot(normalizedRoot);\n\n if (projectRoot) {\n const packageManager = detectManagerFromLockfile(projectRoot) ?? detectPackageManager();\n\n return {\n kind: \"local\",\n packageManager,\n packageRoot: normalizedRoot,\n projectRoot,\n };\n }\n\n const npmPrefix = resolveNpmInstallPrefix(normalizedRoot);\n if (npmPrefix) {\n return {\n kind: \"global\",\n packageManager: \"npm\",\n packageRoot: normalizedRoot,\n npmPrefix,\n };\n }\n\n return {\n kind: \"global\",\n packageManager: detectGlobalManager(normalizedRoot),\n packageRoot: normalizedRoot,\n };\n}\n","const RELEASE_AGE_PATTERNS = [\n /minimumReleaseAge/i,\n /minimum-release-age/i,\n /ERR_PNPM_NO_MATURE_MATCHING_VERSION/,\n /ERR_PNPM_MINIMUM_RELEASE_AGE/,\n /does not meet the minimumReleaseAge constraint/,\n];\n\nexport function isReleaseAgeBlock(output: string): boolean {\n return RELEASE_AGE_PATTERNS.some((pattern) => pattern.test(output));\n}\n\nexport function formatReleaseAgeBlockMessage(options: {\n currentVersion: string;\n availableVersion: string;\n retryAfter?: Date;\n}): string {\n const retry =\n options.retryAfter !== undefined ? ` Retry after ${options.retryAfter.toLocaleString()}.` : \"\";\n\n return (\n `@keystrokehq/cli ${options.availableVersion} is available but blocked by your ` +\n `minimum-release-age setting. You're on ${options.currentVersion}.${retry} ` +\n `Run \\`keystroke update\\` again — it bypasses minimum-release-age for @keystrokehq packages and retries.`\n );\n}\n","import { spawnSync } from \"node:child_process\";\n\nexport function readPnpmMinimumReleaseAgeMinutes(): number | undefined {\n const result = spawnSync(\"pnpm\", [\"config\", \"get\", \"minimum-release-age\"], {\n encoding: \"utf8\",\n });\n\n if (result.status !== 0) {\n return undefined;\n }\n\n const value = result.stdout.trim();\n if (!value || value === \"undefined\" || value === \"0\") {\n return undefined;\n }\n\n const minutes = Number.parseInt(value, 10);\n return Number.isFinite(minutes) && minutes > 0 ? minutes : undefined;\n}\n\nexport function computeReleaseAgeRetryAfter(\n publishedAt: string | undefined,\n minimumReleaseAgeMinutes: number | undefined,\n): Date | undefined {\n if (!publishedAt || !minimumReleaseAgeMinutes) {\n return undefined;\n }\n\n const publishedMs = Date.parse(publishedAt);\n if (!Number.isFinite(publishedMs)) {\n return undefined;\n }\n\n return new Date(publishedMs + minimumReleaseAgeMinutes * 60_000);\n}\n","/** pnpm global installs live in `<pnpm-home>/global/<major>/`. */\nexport function resolveGlobalPnpmRoot(packageRoot: string): string | undefined {\n const normalized = packageRoot.replace(/\\\\/g, \"/\");\n const match = normalized.match(/^(.*\\/global\\/\\d+)\\//);\n return match?.[1];\n}\n","import type { CliInstallInfo } from \"./detect-cli-install\";\nimport { cliPackageSpec, resolveCliDistTag } from \"./resolve-cli-dist-tag\";\nimport { resolveGlobalPnpmRoot } from \"./resolve-global-pnpm-root\";\n\nexport function buildUpdateCommand(\n install: CliInstallInfo,\n distTag: string = resolveCliDistTag(),\n): {\n command: string;\n args: string[];\n cwd?: string;\n} {\n const { kind, packageManager } = install;\n const packageName = cliPackageSpec(distTag);\n\n if (kind === \"global\") {\n switch (packageManager) {\n case \"pnpm\":\n return {\n command: \"pnpm\",\n args: [\"add\", \"-g\", packageName],\n cwd: resolveGlobalPnpmRoot(install.packageRoot),\n };\n case \"yarn\":\n return { command: \"yarn\", args: [\"global\", \"add\", packageName] };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-g\", packageName] };\n case \"npm\":\n default:\n if (install.npmPrefix) {\n return {\n command: \"npm\",\n args: [\"install\", \"-g\", \"--prefix\", install.npmPrefix, packageName],\n };\n }\n return { command: \"npm\", args: [\"install\", \"-g\", packageName] };\n }\n }\n\n const cwd = install.projectRoot;\n switch (packageManager) {\n case \"pnpm\":\n return { command: \"pnpm\", args: [\"add\", \"-D\", packageName], cwd };\n case \"yarn\":\n return { command: \"yarn\", args: [\"add\", \"-D\", packageName], cwd };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-D\", packageName], cwd };\n case \"npm\":\n default:\n return { command: \"npm\", args: [\"install\", \"-D\", packageName], cwd };\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst EXCLUDE_PREFIX = \"minimum-release-age-exclude[]=\";\n\nfunction npmrcPath(root: string): string {\n return join(root, \".npmrc\");\n}\n\n/** Package names already listed as `minimum-release-age-exclude[]=` in an .npmrc body. */\nexport function parseMinimumReleaseAgeExcludes(contents: string): string[] {\n const found: string[] = [];\n for (const rawLine of contents.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line.startsWith(EXCLUDE_PREFIX)) {\n continue;\n }\n const value = line.slice(EXCLUDE_PREFIX.length).trim();\n if (value) {\n found.push(value);\n }\n }\n return found;\n}\n\n/**\n * Ensure each package is listed in the project `.npmrc` as\n * `minimum-release-age-exclude[]=<name>` so `pnpm add pkg@latest` is not held\n * back by minimum-release-age. Creates `.npmrc` when missing.\n *\n * Returns the packages that were newly added (empty when already present).\n */\nexport function ensureMinimumReleaseAgeExclude(root: string, packageNames: string[]): string[] {\n const unique = [...new Set(packageNames.map((name) => name.trim()).filter(Boolean))].sort();\n if (unique.length === 0) {\n return [];\n }\n\n const path = npmrcPath(root);\n const existing = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n const already = new Set(parseMinimumReleaseAgeExcludes(existing));\n const added = unique.filter((name) => !already.has(name));\n if (added.length === 0) {\n return [];\n }\n\n const trimmed = existing.replace(/\\s+$/, \"\");\n const next = [\n ...(trimmed.length > 0 ? [trimmed] : []),\n ...added.map((name) => `${EXCLUDE_PREFIX}${name}`),\n \"\",\n ].join(\"\\n\");\n writeFileSync(path, next, \"utf8\");\n return added;\n}\n","import { spawnSync } from \"node:child_process\";\n\nimport { buildUpdateCommand } from \"./build-update-command\";\nimport type { CliInstallInfo } from \"./detect-cli-install\";\nimport { isReleaseAgeBlock } from \"./detect-release-age-block\";\nimport { ensureMinimumReleaseAgeExclude } from \"./ensure-release-age-exclude\";\nimport { CLI_PACKAGE_NAME } from \"./ensure-sdk-current\";\n\nexport function updateSpawnEnv(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n const pnpmHome = env.PNPM_HOME;\n\n if (!pnpmHome) {\n return env;\n }\n\n const pathKey = process.platform === \"win32\" ? \"Path\" : \"PATH\";\n const path = env[pathKey] ?? \"\";\n const segments = path.split(process.platform === \"win32\" ? \";\" : \":\");\n const binDir = `${pnpmHome}/bin`;\n const prefix = [binDir, pnpmHome].filter((dir) => !segments.includes(dir));\n\n if (prefix.length > 0) {\n env[pathKey] = [...prefix, path].filter(Boolean).join(process.platform === \"win32\" ? \";\" : \":\");\n }\n\n return env;\n}\n\nexport type PackageManagerRunResult = {\n ok: boolean;\n output: string;\n};\n\nfunction runPackageManager(command: string, args: string[], cwd?: string): PackageManagerRunResult {\n const result = spawnSync(command, args, {\n cwd,\n encoding: \"utf8\",\n env: updateSpawnEnv(),\n });\n\n const output = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`;\n\n return {\n ok: result.status === 0,\n output,\n };\n}\n\nfunction migratePnpmGlobal(cwd: string): boolean {\n const result = spawnSync(\"pnpm\", [\"install\"], {\n cwd,\n encoding: \"utf8\",\n env: {\n ...updateSpawnEnv(),\n CI: \"true\",\n },\n });\n\n return result.status === 0;\n}\n\n/** Bypass minimum-release-age for this one CLI install without editing user config. */\nfunction withCliReleaseAgeExclude(args: string[]): string[] {\n return [...args, `--config.minimum-release-age-exclude[]=${CLI_PACKAGE_NAME}`];\n}\n\nexport function runPackageManagerUpdate(\n install: CliInstallInfo,\n distTag?: string,\n): PackageManagerRunResult {\n const { command, args, cwd } = buildUpdateCommand(install, distTag);\n const firstAttempt = runPackageManager(command, args, cwd);\n\n if (firstAttempt.ok) {\n return firstAttempt;\n }\n\n if (install.kind === \"global\" && install.packageManager === \"pnpm\" && cwd) {\n if (migratePnpmGlobal(cwd)) {\n const migrated = runPackageManager(command, args, cwd);\n if (migrated.ok) {\n return migrated;\n }\n }\n }\n\n if (install.packageManager === \"pnpm\" && isReleaseAgeBlock(firstAttempt.output)) {\n if (install.kind === \"local\" && install.projectRoot) {\n const added = ensureMinimumReleaseAgeExclude(install.projectRoot, [CLI_PACKAGE_NAME]);\n if (added.length > 0) {\n process.stderr.write(\n `Added ${CLI_PACKAGE_NAME} to .npmrc minimum-release-age-exclude; retrying CLI update...\\n`,\n );\n }\n return runPackageManager(command, args, cwd);\n }\n\n process.stderr.write(\n `Retrying CLI update with temporary minimum-release-age-exclude for ${CLI_PACKAGE_NAME}...\\n`,\n );\n return runPackageManager(command, withCliReleaseAgeExclude(args), cwd);\n }\n\n return firstAttempt;\n}\n"],"mappings":"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"}
@@ -75,7 +75,7 @@ Workflows also render as an interactive **canvas** in the platform. Keep durable
75
75
  - **Search the catalog, then research the system** — check `keystroke apps search` thoroughly before assuming an integration is missing. For anything custom or unfamiliar, web-fetch/search the real docs — `llms.txt`, OpenAPI/GraphQL specs, API reference — before mirroring endpoints or payloads. Never guess.
76
76
  - **Run before you claim you are finished** — deploy with `keystroke deploy` via `exec_command`, run it in the cloud, and read the real output before activating live triggers or claiming you are finished. Chat with agents and run workflows to confirm they behave correctly.
77
77
  - **Cover obvious edge cases** and write simple tests as needed (null field, empty array, a step that throws).
78
- - **Verify integration prerequisites** — before composing a **catalog** action into a workflow, run it once with `keystroke apps execute` (via `exec_command`) to confirm the IDs and fields exist in the connected account. Custom org apps (`{org}/{name}`) cannot use `apps execute` — smoke-test with deploy + `keystroke workflows run` on a thin workflow that calls `defineApp(...).action(...)`.
78
+ - **Verify integration prerequisites** — before composing a **catalog** action or **custom MCP** tool into a workflow/agent, run it once with `keystroke apps execute` (via `exec_command`) to confirm the IDs and fields exist in the connected account. Other custom org apps (`{org}/{name}` without `source:"mcp"`) cannot use `apps execute` for project `defineApp(...).action(...)` code — smoke-test with deploy + `keystroke workflows run` on a thin workflow.
79
79
  - **Connect integrations in the web app** — use the `connect_app` tool to get a link for the user (a browser cannot open in this workspace). Keystroke projects don't use `.env`.
80
80
  - **Filtered deploys** — determine if you should do a full vs filtered deploy, or use WIP ignore directives (`search_docs "filtered deploy"`).
81
81
  - **Dependencies** — use the project's existing package manager (`package.json` `packageManager` field or lockfile); default to npm only on a freshly created project with no lockfile. Deploy uploads only the built `dist/` artifact — a lockfile is not part of what ships to the platform.
@@ -119,13 +119,13 @@ To discover apps/integrations and their actions, query the live catalog with the
119
119
  keystroke apps search "{query}" # find an app and its slug in the full catalog — start here
120
120
  keystroke apps list # apps registered/connectable in your org
121
121
  keystroke apps get <slug> # catalog toolkit OR custom org template (see kind)
122
- keystroke apps actions list <app> --search <q> # catalog toolkit OR live MCP tools (kind:mcp) after connect
122
+ keystroke apps actions list <app> --search <q> # catalog: toolkit; MCP: live remote tools (not TS yet); other custom: guidance
123
123
  keystroke apps actions get <app> <tool> # schema + how to run/import one catalog action
124
- keystroke apps execute <app> <tool> --input '{...}' # run a connected catalog action (no server)
124
+ keystroke apps execute <app> <tool> --input '{...}' # catalog toolkit action OR custom MCP remote tool
125
125
  keystroke credentials list # what's already connected — check before asking the user to connect
126
126
  ```
127
127
 
128
- Custom org apps (`{org}/{name}`) do not have catalog toolkit actions use `apps get` / `apps sync` / `defineApp(...).action(...)` instead. Do **not** use `apps execute` for them (catalog-only); smoke-test with a thin workflow via `keystroke workflows run <slug> --input '{...}' --wait` after deploy.
128
+ Custom org apps (`{org}/{name}`) do not have catalog toolkit actions. Custom MCP: after connect, `apps actions list` returns the live remote `tools/list` (`kind:mcp`) — smoke-test with `apps execute <org>/<name> <tool>` (no `--version`), then wrap what you need in `app.action(...)`. Other non-MCP custom apps: use `apps get` / `apps sync` / `defineApp(...).action(...)`, and smoke-test with a thin workflow via `keystroke workflows run <slug> --input '{...}' --wait` after deploy.
129
129
 
130
130
  **Gateway / channel binding:** use the `setup_agent_slack` tool (kind `keystroke` or `custom`), then follow its `nextCommands` via `exec_command` and share any OAuth/wizard URLs with the user. Keystroke path: OAuth for the Keystroke Slack App (catalog slug `slackbot`) if needed, then `keystroke channels setup <slug> [--channel <name>]`. Custom @handle: `keystroke channels setup <slug> --custom --print-url` and share the wizard link — user finishes in the browser. Do not use a Personal Slack Account (catalog slug `slack`, user token) for External Channels. Full detail: `search_docs "external channels"` → `/learn/agents/external-channels`.
131
131
 
@@ -139,7 +139,7 @@ If a **credentialed** integration isn't supported, build a proper custom app —
139
139
  2. Author with `defineApp` in `src/apps/` + `app.action(...)`.
140
140
  3. Have the user connect (`connect_app` link), then deploy.
141
141
 
142
- For MCP: `search_docs "custom apps MCP"` first, then connect, then `keystroke apps actions list <org>/<name>` for the live tool registry. Do not use `defineMcp`. Credential-free project actions stay as plain `defineAction`. `search_docs "custom integration"`.
142
+ For MCP: `search_docs "custom apps MCP"` first, then connect, `keystroke apps actions list <org>/<name>` to inspect **remote** tools, `keystroke apps execute <org>/<name> <tool> --input '{...}'` to smoke-test, `keystroke apps sync <org>/<name>`, author `app.action(...)` wrappers, and put the app in `tools: [app]` (all registered actions) or pick actions. Do not use `defineMcp`. Credential-free project actions stay as plain `defineAction`. `search_docs "custom integration"`.
143
143
 
144
144
  ## Report friction and feedback to the Keystroke team
145
145
 
@@ -89,7 +89,7 @@ pnpm test # same — package.json scripts call the CLI
89
89
  Build, ship, then run and inspect what's live. Deploy often.
90
90
 
91
91
  1. **Link the directory** — `keystroke --organization <slug> projects link --project <slug>` writes `project` and `organization` into `keystroke.config.ts` (or pass the flags on every command). Commands that need auth tell you how to log in.
92
- 2. **Verify prerequisites** — for **catalog** integrations, run each action once for real with `keystroke apps execute` before wiring it in. Custom org apps (`{org}/{name}`) are different smoke-test via a thin workflow (see [Integrations & credentials](#integrations--credentials)).
92
+ 2. **Verify prerequisites** — for **catalog** integrations and **custom MCP** apps, run each tool once for real with `keystroke apps execute` before wiring it in. Other custom org apps (`{org}/{name}` without `source:"mcp"`) smoke-test via a thin workflow (see [Integrations & credentials](#integrations--credentials)).
93
93
  3. **Edit** primitives under `src/`. Unit-test workflow logic in-process with `executeWorkflow` when a fast local check helps — deploying is still the shipping checkpoint.
94
94
  4. **Deploy** — `keystroke deploy` (full) or `keystroke deploy --filter agents/support` (one module).
95
95
  5. **Run** — `keystroke workflows run <slug> --input '{...}' --wait` / `keystroke agents prompt <slug> --message "..." --wait`. Prefer `--wait` over `sleep`-and-recheck loops and choose its duration with `--timeout <seconds>`. A timeout means it is still running, not that it failed; continue with `keystroke workflows runs get <slug> <runId> --wait` or `keystroke agents sessions get <slug> <sessionId> --wait`.
@@ -259,21 +259,23 @@ run: async ({ state, setState }) => {
259
259
 
260
260
  Keystroke has 1,000+ built-in integrations, and it's easy to build a custom one for any HTTP API or MCP server (including private/internal). **Research first, then build:** search the catalog thoroughly before assuming an app is missing; for anything custom or unfamiliar, web-fetch/search the system's real docs — `llms.txt`, OpenAPI/GraphQL specs, API reference — before writing code. Never guess endpoints or payloads.
261
261
 
262
- Discover apps and actions with the CLI — the docs integration index is **not** reliable for discovery. One `<app> <tool>` shape flows through discovery → inspect → run for **catalog** apps; `apps actions get` / `apps actions list` print the next step after their output. Custom org apps (`{org}/{name}` from `apps create`) differ by source: non-MCP custom apps return `kind:"custom"` guidance (actions live in project code via `defineApp(...).action(...)`); **MCP** apps (`source:"mcp"`) return `kind:"mcp"` with the live tool registry after connect. Always `keystroke docs search` Keystroke docs first for custom/MCP patterns.
262
+ Discover apps and actions with the CLI — the docs integration index is **not** reliable for discovery. One `<app> <tool>` shape flows through discovery → inspect → run for **catalog** apps; `apps actions get` / `apps actions list` print the next step after their output. **`apps actions list` is not one behavior:** catalog apps return ready toolkit actions; custom MCP apps (`source:"mcp"`) return the live remote `tools/list` after connect (`kind:"mcp"`) those tools may **not** exist as TypeScript `app.action(...)` yet; other custom org apps return `kind:"custom"` guidance (actions live in project code). Always `keystroke docs search` Keystroke docs first for custom/MCP patterns.
263
263
 
264
264
  Think about testing as you build. Local tests don't link cloud credentials or the Keystroke client, so unit-test pure logic locally.
265
265
 
266
266
  **Catalog apps** — check needed apps/credentials and run each action once for real with `keystroke apps execute` (no deploy, no server) before wiring it into a workflow or agent — confirm the IDs, custom fields, and labels you're about to hard-code exist in the *connected* account. Schema inspection and dry-run flags prove nothing about the mutating path.
267
267
 
268
- **Custom org apps** (`{org}/{name}` from `apps create` / `apps sync`) — `apps execute` is catalog-only and will fail with a clear error. Actions live in project code as `defineApp(...).action(...)`. Smoke-test by deploying a thin workflow that calls the action, then `keystroke workflows run <workflow-slug> --input '{...}' --wait`. For MCP custom apps, use `keystroke apps actions list <org>/<name>` after connect for the live tool registry, then the same workflow smoke-test for anything you wrap in `app.action(...)`.
268
+ **Custom MCP apps** (`source:"mcp"`) after connect, `keystroke apps actions list <org>/<name>` lists **remote** MCP tools (not authored TypeScript yet). Smoke-test a remote tool with the same command: `keystroke apps execute <org>/<name> <tool> --input '{...}'` (no `--version`; tool names keep vendor case). Then wrap what you need in `app.action(...)` and attach the app or actions.
269
+
270
+ **Other custom org apps** (`{org}/{name}` from `apps create` / `apps sync`) — `apps execute` cannot run `defineApp(...).action(...)` project code. Smoke-test by deploying a thin workflow that calls the action, then `keystroke workflows run <workflow-slug> --input '{...}' --wait`.
269
271
 
270
272
  ```bash
271
273
  keystroke apps list # all registered apps
272
274
  keystroke apps search "{query}" # find an app + slug in the full catalog
273
275
  keystroke apps get <slug> # catalog toolkit OR custom credential template (kind)
274
- keystroke apps actions list <app> --search <q> # catalog toolkit OR live MCP tools (kind:mcp) after connect
276
+ keystroke apps actions list <app> --search <q> # catalog: toolkit actions; MCP: live remote tools (not TS yet); other custom: guidance
275
277
  keystroke apps actions get <app> <tool> # schema + how to run/import one catalog action
276
- keystroke apps execute <app> <tool> --input '{...}' # run a connected catalog action (no server)
278
+ keystroke apps execute <app> <tool> --input '{...}' # catalog toolkit action OR custom MCP remote tool
277
279
  keystroke credentials list # what's already connected
278
280
  keystroke connect <slug> # connect an app credential (opens the web connect flow; --print-url prints it instead)
279
281
  keystroke credentials create <key> --set apiKey=@env:MY_KEY # static API key
@@ -335,7 +337,7 @@ Missing a built-in **credentialed** integration? After researching the system's
335
337
  2. **Code authoring** — always `defineApp` in `src/apps/` + `app.action(...)`. Do **not** invent `src/credentials/foo.ts` + a lone `defineAction` for a new API.
336
338
  3. **Connect** — `keystroke connect <slug>` (or `credentials create` when you already have the secret), then deploy.
337
339
 
338
- For MCP apps: `keystroke docs search "custom apps MCP"` first, then `connect`, then `keystroke apps actions list <org>/<name>` for the live tool registry. Do not use `defineMcp`. Plain `defineAction` (no credentials) does not need an app. See `/learn/credentials/custom-integrations`.
340
+ For MCP apps: `keystroke docs search "custom apps MCP"` first, then `connect`, `keystroke apps actions list <org>/<name>` to inspect **remote** tools (unlike catalog list — wrappers may not exist yet), `keystroke apps sync <org>/<name>`, author `app.action(...)` wrappers, and put the app in `tools: [app]` (all registered actions) or pick individual actions. Do not use `defineMcp`. Plain `defineAction` (no credentials) does not need an app. See `/learn/credentials/custom-integrations`.
339
341
 
340
342
  ## Skills & files
341
343
 
@@ -388,7 +390,7 @@ Invoke/inspect commands print JSON to stdout (exit 0 on success, 1 on failure)
388
390
  - [ ] Deployed the change and read the real run output/trace — not just "it should work".
389
391
  - [ ] Prompted agents / ran workflows with realistic input and verified behavior.
390
392
  - [ ] Researched real APIs before mirroring them in custom integration actions — no guessed endpoints or payloads.
391
- - [ ] Catalog: executed each mutating integration action once via `keystroke apps execute` before composing it — a passing dry-run flag doesn't count. Custom org apps: smoke-tested via deploy + `workflows run` on a thin workflow that calls the action (not `apps execute`).
393
+ - [ ] Catalog / custom MCP: executed each mutating tool once via `keystroke apps execute` before composing it — a passing dry-run flag doesn't count. Other custom org apps: smoke-tested via deploy + `workflows run` on a thin workflow that calls `defineApp(...).action(...)`.
392
394
  - [ ] Covered obvious edge cases (null field, empty array, a step that throws) with a simple test where warranted.
393
395
  - [ ] Checked `keystroke credentials list` before connecting; connected any new apps with `keystroke connect`.
394
396
  - [ ] Tested manually before deploying attached cron/polling triggers, or used `triggers disable` to pause until ready.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keystrokehq/cli",
3
- "version": "0.1.162",
3
+ "version": "0.1.163",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/keystrokehq/keystroke.git",
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as buildApp } from "./dist-DeMzV-BR.mjs";
3
- export { buildApp };