@keystrokehq/cli 0.1.5 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/dist/dist-BPdyQaUt.mjs +19268 -0
- package/dist/dist-BPdyQaUt.mjs.map +1 -0
- package/dist/dist-Dz6iW5q5.mjs +3 -0
- package/dist/{dist-B9XaHV_2.mjs → dist-IOphuHYN.mjs} +57 -20
- package/dist/dist-IOphuHYN.mjs.map +1 -0
- package/dist/{dist-C3YClLXV.mjs → dist-YV-kApfg.mjs} +790 -73
- package/dist/dist-YV-kApfg.mjs.map +1 -0
- package/dist/index.mjs +559 -225
- package/dist/index.mjs.map +1 -1
- package/dist/{maybe-auto-update-DHt-mVf1.mjs → maybe-auto-update-BiR_kXZX.mjs} +2 -2
- package/dist/{maybe-auto-update-DHt-mVf1.mjs.map → maybe-auto-update-BiR_kXZX.mjs.map} +1 -1
- package/dist/pack-artifact-DVnIKrsg-BtNTTQcz.mjs +112 -0
- package/dist/pack-artifact-DVnIKrsg-BtNTTQcz.mjs.map +1 -0
- package/dist/skills-bundle/_AGENTS.md +7 -7
- package/dist/skills-bundle/skills/keystroke-actions/SKILL.md +39 -7
- package/dist/skills-bundle/skills/keystroke-actions/references/catalog-and-imports.md +25 -19
- package/dist/skills-bundle/skills/keystroke-agents/SKILL.md +7 -5
- package/dist/skills-bundle/skills/keystroke-apps/SKILL.md +133 -0
- package/dist/skills-bundle/skills/keystroke-apps/references/cli-and-catalog.md +66 -0
- package/dist/skills-bundle/skills/keystroke-cli/SKILL.md +3 -3
- package/dist/skills-bundle/skills/keystroke-cli/references/api-targets.md +6 -5
- package/dist/skills-bundle/skills/keystroke-deploy/SKILL.md +2 -2
- package/dist/skills-bundle/skills/keystroke-files/SKILL.md +6 -5
- package/dist/skills-bundle/skills/keystroke-gateways/SKILL.md +2 -2
- package/dist/skills-bundle/skills/keystroke-gateways/references/slack-setup.md +1 -1
- package/dist/skills-bundle/skills/keystroke-skills/SKILL.md +1 -1
- package/dist/skills-bundle/skills/keystroke-workflows/SKILL.md +1 -1
- package/dist/skills-bundle/skills/keystroke-workflows/references/authoring.md +2 -2
- package/dist/templates/hello-world/.env.example +1 -15
- package/dist/templates/hello-world/README.md +1 -1
- package/dist/templates/hello-world/package.json +1 -1
- package/dist/{version-BOm_5ar9.mjs → version-BGuC7Cpu.mjs} +10 -14
- package/dist/version-BGuC7Cpu.mjs.map +1 -0
- package/package.json +8 -2
- package/dist/dist-B9XaHV_2.mjs.map +0 -1
- package/dist/dist-C3YClLXV.mjs.map +0 -1
- package/dist/dist-D_-88U7y.mjs +0 -1887
- package/dist/dist-D_-88U7y.mjs.map +0 -1
- package/dist/dist-iKd6nzBK.mjs +0 -3
- package/dist/skills-bundle/skills/keystroke-credentials/SKILL.md +0 -108
- package/dist/skills-bundle/skills/keystroke-credentials/references/cli-and-oauth.md +0 -51
- package/dist/version-BOm_5ar9.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { l as getCliConfigDir, r as detectPackageManager, s as resolveCliRoot, t as readCliVersion } from "./version-
|
|
2
|
+
import { l as getCliConfigDir, r as detectPackageManager, s as resolveCliRoot, t as readCliVersion } from "./version-BGuC7Cpu.mjs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
@@ -405,4 +405,4 @@ async function maybeAutoUpdate(argv) {
|
|
|
405
405
|
//#endregion
|
|
406
406
|
export { maybeAutoUpdate };
|
|
407
407
|
|
|
408
|
-
//# sourceMappingURL=maybe-auto-update-
|
|
408
|
+
//# sourceMappingURL=maybe-auto-update-BiR_kXZX.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maybe-auto-update-DHt-mVf1.mjs","names":[],"sources":["../src/update/compare-version.ts","../src/update/detect-cli-install.ts","../src/update/detect-release-age-block.ts","../src/update/fetch-latest-version.ts","../src/update/read-pnpm-release-age-minutes.ts","../src/update/resolve-global-pnpm-root.ts","../src/update/build-update-command.ts","../src/update/run-package-manager-update.ts","../src/update/update-block-cache.ts","../src/update/maybe-auto-update.ts"],"sourcesContent":["function parseVersion(version: string): [number, number, number] {\n const normalized = version.trim().replace(/^v/, \"\").split(\"-\")[0] ?? \"\";\n const [major = 0, minor = 0, patch = 0] = normalized.split(\".\").map((part) => {\n const value = Number.parseInt(part, 10);\n return Number.isFinite(value) ? value : 0;\n });\n\n return [major, minor, patch];\n}\n\nexport function isNewerVersion(latest: string, current: string): boolean {\n const [latestMajor, latestMinor, latestPatch] = parseVersion(latest);\n const [currentMajor, currentMinor, currentPatch] = parseVersion(current);\n\n if (latestMajor !== currentMajor) {\n return latestMajor > currentMajor;\n }\n\n if (latestMinor !== currentMinor) {\n return latestMinor > currentMinor;\n }\n\n return latestPatch > currentPatch;\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\nfunction realpathSafe(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\nfunction 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 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 `Add @keystrokehq/cli to minimumReleaseAgeExclude to install immediately.`\n );\n}\n","const REGISTRY_URL = \"https://registry.npmjs.org/@keystrokehq%2Fcli\";\nconst REQUEST_TIMEOUT_MS = 3_000;\n\ntype RegistryPackage = {\n \"dist-tags\"?: {\n latest?: string;\n };\n time?: Record<string, string>;\n};\n\nexport type CliRegistryRelease = {\n version: string;\n publishedAt?: string;\n};\n\nexport async function fetchLatestCliRelease(): Promise<CliRegistryRelease | undefined> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n try {\n const response = await fetch(REGISTRY_URL, {\n signal: controller.signal,\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n return undefined;\n }\n\n const payload = (await response.json()) as RegistryPackage;\n const version = payload[\"dist-tags\"]?.latest?.trim();\n if (!version) {\n return undefined;\n }\n\n return {\n version,\n publishedAt: payload.time?.[version],\n };\n } catch {\n return undefined;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nexport async function fetchLatestCliVersion(): Promise<string | undefined> {\n const release = await fetchLatestCliRelease();\n return release?.version;\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 { resolveGlobalPnpmRoot } from \"./resolve-global-pnpm-root\";\n\nconst PACKAGE_NAME = \"@keystrokehq/cli@latest\";\n\nexport function buildUpdateCommand(install: CliInstallInfo): {\n command: string;\n args: string[];\n cwd?: string;\n} {\n const { kind, packageManager } = install;\n\n if (kind === \"global\") {\n switch (packageManager) {\n case \"pnpm\":\n return {\n command: \"pnpm\",\n args: [\"add\", \"-g\", PACKAGE_NAME],\n cwd: resolveGlobalPnpmRoot(install.packageRoot),\n };\n case \"yarn\":\n return { command: \"yarn\", args: [\"global\", \"add\", PACKAGE_NAME] };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-g\", PACKAGE_NAME] };\n case \"npm\":\n default:\n return { command: \"npm\", args: [\"install\", \"-g\", PACKAGE_NAME] };\n }\n }\n\n const cwd = install.projectRoot;\n switch (packageManager) {\n case \"pnpm\":\n return { command: \"pnpm\", args: [\"add\", \"-D\", PACKAGE_NAME], cwd };\n case \"yarn\":\n return { command: \"yarn\", args: [\"add\", \"-D\", PACKAGE_NAME], cwd };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-D\", PACKAGE_NAME], cwd };\n case \"npm\":\n default:\n return { command: \"npm\", args: [\"install\", \"-D\", PACKAGE_NAME], cwd };\n }\n}\n","import { spawnSync } from \"node:child_process\";\n\nimport { buildUpdateCommand } from \"./build-update-command\";\nimport type { CliInstallInfo } from \"./detect-cli-install\";\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\nexport function runPackageManagerUpdate(install: CliInstallInfo): PackageManagerRunResult {\n const { command, args, cwd } = buildUpdateCommand(install);\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 return runPackageManager(command, args, cwd);\n }\n }\n\n return firstAttempt;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { getCliConfigDir } from \"../config\";\n\nexport type UpdateBlockCache = {\n registryLatest?: string;\n blockedUntil?: string;\n lastFailedAt?: string;\n};\n\nconst CACHE_FILE = \"update-check.json\";\nconst GENERIC_RETRY_MS = 24 * 60 * 60 * 1000;\n\nfunction cachePath(configDir = getCliConfigDir()): string {\n return join(configDir, CACHE_FILE);\n}\n\nexport function readUpdateBlockCache(configDir?: string): UpdateBlockCache {\n const path = cachePath(configDir);\n if (!existsSync(path)) {\n return {};\n }\n\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as UpdateBlockCache;\n } catch {\n return {};\n }\n}\n\nexport function writeUpdateBlockCache(cache: UpdateBlockCache, configDir?: string): void {\n const dir = configDir ?? getCliConfigDir();\n mkdirSync(dir, { recursive: true });\n writeFileSync(cachePath(dir), `${JSON.stringify(cache, null, 2)}\\n`, \"utf8\");\n}\n\nexport function shouldSkipCachedUpdate(\n cache: UpdateBlockCache,\n registryLatest: string,\n now = Date.now(),\n): boolean {\n if (cache.registryLatest !== registryLatest) {\n return false;\n }\n\n if (cache.blockedUntil) {\n const blockedUntilMs = Date.parse(cache.blockedUntil);\n if (Number.isFinite(blockedUntilMs) && blockedUntilMs > now) {\n return true;\n }\n }\n\n if (cache.lastFailedAt) {\n const lastFailedMs = Date.parse(cache.lastFailedAt);\n if (Number.isFinite(lastFailedMs) && now - lastFailedMs < GENERIC_RETRY_MS) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function clearUpdateBlockCache(configDir?: string): void {\n writeUpdateBlockCache({}, configDir);\n}\n\nexport function recordReleaseAgeBlock(options: {\n registryLatest: string;\n retryAfter?: Date;\n configDir?: string;\n}): void {\n writeUpdateBlockCache(\n {\n registryLatest: options.registryLatest,\n blockedUntil: options.retryAfter?.toISOString(),\n },\n options.configDir,\n );\n}\n\nexport function recordGenericUpdateFailure(registryLatest: string, configDir?: string): void {\n writeUpdateBlockCache(\n {\n registryLatest,\n lastFailedAt: new Date().toISOString(),\n },\n configDir,\n );\n}\n","import { spawnSync } from \"node:child_process\";\n\nimport { resolveCliRoot } from \"../project/resolve-cli-root\";\nimport { readCliVersion } from \"../version\";\nimport { isNewerVersion } from \"./compare-version\";\nimport { detectCliInstall } from \"./detect-cli-install\";\nimport { formatReleaseAgeBlockMessage, isReleaseAgeBlock } from \"./detect-release-age-block\";\nimport { fetchLatestCliRelease } from \"./fetch-latest-version\";\nimport {\n computeReleaseAgeRetryAfter,\n readPnpmMinimumReleaseAgeMinutes,\n} from \"./read-pnpm-release-age-minutes\";\nimport { runPackageManagerUpdate } from \"./run-package-manager-update\";\nimport {\n clearUpdateBlockCache,\n readUpdateBlockCache,\n recordGenericUpdateFailure,\n recordReleaseAgeBlock,\n shouldSkipCachedUpdate,\n} from \"./update-block-cache\";\n\nfunction shouldSkipAutoUpdate(argv: string[]): boolean {\n if (\n process.env.KEYSTROKE_DEV ||\n process.env.KEYSTROKE_SKIP_UPDATE ||\n process.env.KEYSTROKE_UPDATING\n ) {\n return true;\n }\n\n if (process.env.CI === \"true\" || process.env.CI === \"1\") {\n return true;\n }\n\n const args = argv.slice(2);\n if (args.length === 0) {\n return false;\n }\n\n return args.every(\n (arg) =>\n arg === \"-V\" ||\n arg === \"--version\" ||\n arg === \"-h\" ||\n arg === \"--help\" ||\n arg.startsWith(\"-V\"),\n );\n}\n\nfunction reexecCli(argv: string[]): never {\n const result = spawnSync(process.execPath, argv.slice(1), {\n stdio: \"inherit\",\n env: {\n ...process.env,\n KEYSTROKE_UPDATING: \"1\",\n },\n });\n\n process.exit(result.status ?? 1);\n}\n\nfunction releaseAgeRetryAfter(\n install: NonNullable<ReturnType<typeof detectCliInstall>>,\n publishedAt: string | undefined,\n): Date | undefined {\n if (install.packageManager !== \"pnpm\") {\n return undefined;\n }\n\n return computeReleaseAgeRetryAfter(publishedAt, readPnpmMinimumReleaseAgeMinutes());\n}\n\nexport async function maybeAutoUpdate(argv: string[]): Promise<void> {\n if (shouldSkipAutoUpdate(argv)) {\n return;\n }\n\n const install = detectCliInstall(resolveCliRoot(import.meta.url));\n if (!install || (install.kind === \"local\" && !install.projectRoot)) {\n return;\n }\n\n const currentVersion = readCliVersion();\n const release = await fetchLatestCliRelease();\n\n if (!release || !isNewerVersion(release.version, currentVersion)) {\n clearUpdateBlockCache();\n return;\n }\n\n const cache = readUpdateBlockCache();\n if (shouldSkipCachedUpdate(cache, release.version)) {\n return;\n }\n\n process.stderr.write(\n `Updating @keystrokehq/cli ${currentVersion} -> ${release.version} via ${install.packageManager}...\\n`,\n );\n\n const result = runPackageManagerUpdate(install);\n const installedVersion = readCliVersion();\n\n if (isNewerVersion(installedVersion, currentVersion)) {\n clearUpdateBlockCache();\n process.stderr.write(`Updated @keystrokehq/cli ${currentVersion} -> ${installedVersion}.\\n`);\n reexecCli(argv);\n }\n\n if (result.ok) {\n clearUpdateBlockCache();\n return;\n }\n\n if (isReleaseAgeBlock(result.output)) {\n const retryAfter = releaseAgeRetryAfter(install, release.publishedAt);\n recordReleaseAgeBlock({\n registryLatest: release.version,\n retryAfter,\n });\n process.stderr.write(\n `${formatReleaseAgeBlockMessage({\n currentVersion,\n availableVersion: release.version,\n retryAfter,\n })}\\n`,\n );\n return;\n }\n\n recordGenericUpdateFailure(release.version);\n process.stderr.write(\"Auto-update failed; continuing with the current version.\\n\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"maybe-auto-update-BiR_kXZX.mjs","names":[],"sources":["../src/update/compare-version.ts","../src/update/detect-cli-install.ts","../src/update/detect-release-age-block.ts","../src/update/fetch-latest-version.ts","../src/update/read-pnpm-release-age-minutes.ts","../src/update/resolve-global-pnpm-root.ts","../src/update/build-update-command.ts","../src/update/run-package-manager-update.ts","../src/update/update-block-cache.ts","../src/update/maybe-auto-update.ts"],"sourcesContent":["function parseVersion(version: string): [number, number, number] {\n const normalized = version.trim().replace(/^v/, \"\").split(\"-\")[0] ?? \"\";\n const [major = 0, minor = 0, patch = 0] = normalized.split(\".\").map((part) => {\n const value = Number.parseInt(part, 10);\n return Number.isFinite(value) ? value : 0;\n });\n\n return [major, minor, patch];\n}\n\nexport function isNewerVersion(latest: string, current: string): boolean {\n const [latestMajor, latestMinor, latestPatch] = parseVersion(latest);\n const [currentMajor, currentMinor, currentPatch] = parseVersion(current);\n\n if (latestMajor !== currentMajor) {\n return latestMajor > currentMajor;\n }\n\n if (latestMinor !== currentMinor) {\n return latestMinor > currentMinor;\n }\n\n return latestPatch > currentPatch;\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\nfunction realpathSafe(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\nfunction 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 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 `Add @keystrokehq/cli to minimumReleaseAgeExclude to install immediately.`\n );\n}\n","const REGISTRY_URL = \"https://registry.npmjs.org/@keystrokehq%2Fcli\";\nconst REQUEST_TIMEOUT_MS = 3_000;\n\ntype RegistryPackage = {\n \"dist-tags\"?: {\n latest?: string;\n };\n time?: Record<string, string>;\n};\n\nexport type CliRegistryRelease = {\n version: string;\n publishedAt?: string;\n};\n\nexport async function fetchLatestCliRelease(): Promise<CliRegistryRelease | undefined> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\n try {\n const response = await fetch(REGISTRY_URL, {\n signal: controller.signal,\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n return undefined;\n }\n\n const payload = (await response.json()) as RegistryPackage;\n const version = payload[\"dist-tags\"]?.latest?.trim();\n if (!version) {\n return undefined;\n }\n\n return {\n version,\n publishedAt: payload.time?.[version],\n };\n } catch {\n return undefined;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nexport async function fetchLatestCliVersion(): Promise<string | undefined> {\n const release = await fetchLatestCliRelease();\n return release?.version;\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 { resolveGlobalPnpmRoot } from \"./resolve-global-pnpm-root\";\n\nconst PACKAGE_NAME = \"@keystrokehq/cli@latest\";\n\nexport function buildUpdateCommand(install: CliInstallInfo): {\n command: string;\n args: string[];\n cwd?: string;\n} {\n const { kind, packageManager } = install;\n\n if (kind === \"global\") {\n switch (packageManager) {\n case \"pnpm\":\n return {\n command: \"pnpm\",\n args: [\"add\", \"-g\", PACKAGE_NAME],\n cwd: resolveGlobalPnpmRoot(install.packageRoot),\n };\n case \"yarn\":\n return { command: \"yarn\", args: [\"global\", \"add\", PACKAGE_NAME] };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-g\", PACKAGE_NAME] };\n case \"npm\":\n default:\n return { command: \"npm\", args: [\"install\", \"-g\", PACKAGE_NAME] };\n }\n }\n\n const cwd = install.projectRoot;\n switch (packageManager) {\n case \"pnpm\":\n return { command: \"pnpm\", args: [\"add\", \"-D\", PACKAGE_NAME], cwd };\n case \"yarn\":\n return { command: \"yarn\", args: [\"add\", \"-D\", PACKAGE_NAME], cwd };\n case \"bun\":\n return { command: \"bun\", args: [\"add\", \"-D\", PACKAGE_NAME], cwd };\n case \"npm\":\n default:\n return { command: \"npm\", args: [\"install\", \"-D\", PACKAGE_NAME], cwd };\n }\n}\n","import { spawnSync } from \"node:child_process\";\n\nimport { buildUpdateCommand } from \"./build-update-command\";\nimport type { CliInstallInfo } from \"./detect-cli-install\";\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\nexport function runPackageManagerUpdate(install: CliInstallInfo): PackageManagerRunResult {\n const { command, args, cwd } = buildUpdateCommand(install);\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 return runPackageManager(command, args, cwd);\n }\n }\n\n return firstAttempt;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { getCliConfigDir } from \"../config\";\n\nexport type UpdateBlockCache = {\n registryLatest?: string;\n blockedUntil?: string;\n lastFailedAt?: string;\n};\n\nconst CACHE_FILE = \"update-check.json\";\nconst GENERIC_RETRY_MS = 24 * 60 * 60 * 1000;\n\nfunction cachePath(configDir = getCliConfigDir()): string {\n return join(configDir, CACHE_FILE);\n}\n\nexport function readUpdateBlockCache(configDir?: string): UpdateBlockCache {\n const path = cachePath(configDir);\n if (!existsSync(path)) {\n return {};\n }\n\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as UpdateBlockCache;\n } catch {\n return {};\n }\n}\n\nexport function writeUpdateBlockCache(cache: UpdateBlockCache, configDir?: string): void {\n const dir = configDir ?? getCliConfigDir();\n mkdirSync(dir, { recursive: true });\n writeFileSync(cachePath(dir), `${JSON.stringify(cache, null, 2)}\\n`, \"utf8\");\n}\n\nexport function shouldSkipCachedUpdate(\n cache: UpdateBlockCache,\n registryLatest: string,\n now = Date.now(),\n): boolean {\n if (cache.registryLatest !== registryLatest) {\n return false;\n }\n\n if (cache.blockedUntil) {\n const blockedUntilMs = Date.parse(cache.blockedUntil);\n if (Number.isFinite(blockedUntilMs) && blockedUntilMs > now) {\n return true;\n }\n }\n\n if (cache.lastFailedAt) {\n const lastFailedMs = Date.parse(cache.lastFailedAt);\n if (Number.isFinite(lastFailedMs) && now - lastFailedMs < GENERIC_RETRY_MS) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function clearUpdateBlockCache(configDir?: string): void {\n writeUpdateBlockCache({}, configDir);\n}\n\nexport function recordReleaseAgeBlock(options: {\n registryLatest: string;\n retryAfter?: Date;\n configDir?: string;\n}): void {\n writeUpdateBlockCache(\n {\n registryLatest: options.registryLatest,\n blockedUntil: options.retryAfter?.toISOString(),\n },\n options.configDir,\n );\n}\n\nexport function recordGenericUpdateFailure(registryLatest: string, configDir?: string): void {\n writeUpdateBlockCache(\n {\n registryLatest,\n lastFailedAt: new Date().toISOString(),\n },\n configDir,\n );\n}\n","import { spawnSync } from \"node:child_process\";\n\nimport { resolveCliRoot } from \"../project/resolve-cli-root\";\nimport { readCliVersion } from \"../version\";\nimport { isNewerVersion } from \"./compare-version\";\nimport { detectCliInstall } from \"./detect-cli-install\";\nimport { formatReleaseAgeBlockMessage, isReleaseAgeBlock } from \"./detect-release-age-block\";\nimport { fetchLatestCliRelease } from \"./fetch-latest-version\";\nimport {\n computeReleaseAgeRetryAfter,\n readPnpmMinimumReleaseAgeMinutes,\n} from \"./read-pnpm-release-age-minutes\";\nimport { runPackageManagerUpdate } from \"./run-package-manager-update\";\nimport {\n clearUpdateBlockCache,\n readUpdateBlockCache,\n recordGenericUpdateFailure,\n recordReleaseAgeBlock,\n shouldSkipCachedUpdate,\n} from \"./update-block-cache\";\n\nfunction shouldSkipAutoUpdate(argv: string[]): boolean {\n if (\n process.env.KEYSTROKE_DEV ||\n process.env.KEYSTROKE_SKIP_UPDATE ||\n process.env.KEYSTROKE_UPDATING\n ) {\n return true;\n }\n\n if (process.env.CI === \"true\" || process.env.CI === \"1\") {\n return true;\n }\n\n const args = argv.slice(2);\n if (args.length === 0) {\n return false;\n }\n\n return args.every(\n (arg) =>\n arg === \"-V\" ||\n arg === \"--version\" ||\n arg === \"-h\" ||\n arg === \"--help\" ||\n arg.startsWith(\"-V\"),\n );\n}\n\nfunction reexecCli(argv: string[]): never {\n const result = spawnSync(process.execPath, argv.slice(1), {\n stdio: \"inherit\",\n env: {\n ...process.env,\n KEYSTROKE_UPDATING: \"1\",\n },\n });\n\n process.exit(result.status ?? 1);\n}\n\nfunction releaseAgeRetryAfter(\n install: NonNullable<ReturnType<typeof detectCliInstall>>,\n publishedAt: string | undefined,\n): Date | undefined {\n if (install.packageManager !== \"pnpm\") {\n return undefined;\n }\n\n return computeReleaseAgeRetryAfter(publishedAt, readPnpmMinimumReleaseAgeMinutes());\n}\n\nexport async function maybeAutoUpdate(argv: string[]): Promise<void> {\n if (shouldSkipAutoUpdate(argv)) {\n return;\n }\n\n const install = detectCliInstall(resolveCliRoot(import.meta.url));\n if (!install || (install.kind === \"local\" && !install.projectRoot)) {\n return;\n }\n\n const currentVersion = readCliVersion();\n const release = await fetchLatestCliRelease();\n\n if (!release || !isNewerVersion(release.version, currentVersion)) {\n clearUpdateBlockCache();\n return;\n }\n\n const cache = readUpdateBlockCache();\n if (shouldSkipCachedUpdate(cache, release.version)) {\n return;\n }\n\n process.stderr.write(\n `Updating @keystrokehq/cli ${currentVersion} -> ${release.version} via ${install.packageManager}...\\n`,\n );\n\n const result = runPackageManagerUpdate(install);\n const installedVersion = readCliVersion();\n\n if (isNewerVersion(installedVersion, currentVersion)) {\n clearUpdateBlockCache();\n process.stderr.write(`Updated @keystrokehq/cli ${currentVersion} -> ${installedVersion}.\\n`);\n reexecCli(argv);\n }\n\n if (result.ok) {\n clearUpdateBlockCache();\n return;\n }\n\n if (isReleaseAgeBlock(result.output)) {\n const retryAfter = releaseAgeRetryAfter(install, release.publishedAt);\n recordReleaseAgeBlock({\n registryLatest: release.version,\n retryAfter,\n });\n process.stderr.write(\n `${formatReleaseAgeBlockMessage({\n currentVersion,\n availableVersion: release.version,\n retryAfter,\n })}\\n`,\n );\n return;\n }\n\n recordGenericUpdateFailure(release.version);\n process.stderr.write(\"Auto-update failed; continuing with the current version.\\n\");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Bt as ROUTE_MANIFEST_REL_PATH, In as parseStoredRouteManifest } from "./dist-YV-kApfg.mjs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
//#region ../../packages/storage/dist/pack-artifact-DVnIKrsg.mjs
|
|
8
|
+
/**
|
|
9
|
+
* Pack a directory tree that contains a `dist/` folder into a gzip tarball
|
|
10
|
+
* suitable for project-server extraction.
|
|
11
|
+
*/
|
|
12
|
+
function packDistTree(rootContainingDist) {
|
|
13
|
+
const tempDir = mkdtempSync(join(tmpdir(), "keystroke-artifact-pack-"));
|
|
14
|
+
const archivePath = join(tempDir, "artifact.tgz");
|
|
15
|
+
try {
|
|
16
|
+
const result = spawnSync("tar", [
|
|
17
|
+
"-czf",
|
|
18
|
+
archivePath,
|
|
19
|
+
"--exclude=._*",
|
|
20
|
+
"--exclude=.DS_Store",
|
|
21
|
+
"-C",
|
|
22
|
+
rootContainingDist,
|
|
23
|
+
"dist"
|
|
24
|
+
], {
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
env: {
|
|
27
|
+
...process.env,
|
|
28
|
+
COPYFILE_DISABLE: "1"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (result.status !== 0) throw new Error(result.stderr?.trim() || "Failed to pack project artifact");
|
|
32
|
+
return readFileSync(archivePath);
|
|
33
|
+
} finally {
|
|
34
|
+
rmSync(tempDir, {
|
|
35
|
+
recursive: true,
|
|
36
|
+
force: true
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Extract a packed project artifact tarball into `destDir` (creates `destDir/dist/`). */
|
|
41
|
+
function extractProjectArtifact(archive, destDir) {
|
|
42
|
+
const tempDir = mkdtempSync(join(tmpdir(), "keystroke-artifact-extract-"));
|
|
43
|
+
const archivePath = join(tempDir, "artifact.tgz");
|
|
44
|
+
try {
|
|
45
|
+
writeFileSync(archivePath, archive);
|
|
46
|
+
const result = spawnSync("tar", [
|
|
47
|
+
"-xzf",
|
|
48
|
+
archivePath,
|
|
49
|
+
"-C",
|
|
50
|
+
destDir
|
|
51
|
+
], {
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
env: {
|
|
54
|
+
...process.env,
|
|
55
|
+
COPYFILE_DISABLE: "1"
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
if (result.status !== 0) throw new Error(result.stderr?.trim() || "Failed to extract project artifact");
|
|
59
|
+
} finally {
|
|
60
|
+
rmSync(tempDir, {
|
|
61
|
+
recursive: true,
|
|
62
|
+
force: true
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function moduleFileOf(entry) {
|
|
67
|
+
return "moduleFile" in entry && typeof entry.moduleFile === "string" ? entry.moduleFile : void 0;
|
|
68
|
+
}
|
|
69
|
+
/** Replace manifest rows for rebuilt modules while keeping untouched routes and metadata. */
|
|
70
|
+
function mergeStoredRouteManifest(base, rebuiltEntries) {
|
|
71
|
+
const rebuiltModuleFiles = new Set(rebuiltEntries.map(moduleFileOf).filter((value) => Boolean(value)));
|
|
72
|
+
const keptEntries = base.entries.filter((entry) => {
|
|
73
|
+
const moduleFile = moduleFileOf(entry);
|
|
74
|
+
if (!moduleFile) return true;
|
|
75
|
+
return !rebuiltModuleFiles.has(moduleFile);
|
|
76
|
+
});
|
|
77
|
+
const filteredRebuilt = rebuiltEntries.filter((entry) => entry.kind !== "health");
|
|
78
|
+
return {
|
|
79
|
+
...base,
|
|
80
|
+
entries: [...keptEntries, ...filteredRebuilt]
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async function mergeFilteredArtifact(input) {
|
|
84
|
+
const mergeRoot = mkdtempSync(join(tmpdir(), "keystroke-artifact-merge-"));
|
|
85
|
+
try {
|
|
86
|
+
extractProjectArtifact(input.baseArchive, mergeRoot);
|
|
87
|
+
const manifestPath = join(mergeRoot, ROUTE_MANIFEST_REL_PATH);
|
|
88
|
+
const mergedManifest = mergeStoredRouteManifest(parseStoredRouteManifest(JSON.parse(readFileSync(manifestPath, "utf8"))), input.filtered.manifestEntries);
|
|
89
|
+
writeFileSync(manifestPath, `${JSON.stringify(mergedManifest, null, 2)}\n`);
|
|
90
|
+
for (const file of input.filtered.files) {
|
|
91
|
+
const destination = join(mergeRoot, "dist", file.relativePath);
|
|
92
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
93
|
+
writeFileSync(destination, file.contents);
|
|
94
|
+
if (file.sourceMap) writeFileSync(`${destination}.map`, file.sourceMap);
|
|
95
|
+
}
|
|
96
|
+
return packDistTree(mergeRoot);
|
|
97
|
+
} finally {
|
|
98
|
+
rmSync(mergeRoot, {
|
|
99
|
+
recursive: true,
|
|
100
|
+
force: true
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Pack `dist/` into a gzip tarball suitable for `/app` extraction in the project server image. */
|
|
105
|
+
function packProjectArtifact(projectRoot) {
|
|
106
|
+
if (!existsSync(join(projectRoot, "dist"))) throw new Error("dist/ not found — run keystroke build first");
|
|
107
|
+
return packDistTree(projectRoot);
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { packProjectArtifact as n, mergeFilteredArtifact as t };
|
|
111
|
+
|
|
112
|
+
//# sourceMappingURL=pack-artifact-DVnIKrsg-BtNTTQcz.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-artifact-DVnIKrsg-BtNTTQcz.mjs","names":[],"sources":["../../../packages/storage/dist/pack-artifact-DVnIKrsg.mjs"],"sourcesContent":["import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport { tmpdir } from \"node:os\";\nimport { ROUTE_MANIFEST_REL_PATH, parseStoredRouteManifest } from \"@keystrokehq/shared\";\n//#region src/pack-dir.ts\n/**\n* Pack a directory tree that contains a `dist/` folder into a gzip tarball\n* suitable for project-server extraction.\n*/\nfunction packDistTree(rootContainingDist) {\n\tconst tempDir = mkdtempSync(join(tmpdir(), \"keystroke-artifact-pack-\"));\n\tconst archivePath = join(tempDir, \"artifact.tgz\");\n\ttry {\n\t\tconst result = spawnSync(\"tar\", [\n\t\t\t\"-czf\",\n\t\t\tarchivePath,\n\t\t\t\"--exclude=._*\",\n\t\t\t\"--exclude=.DS_Store\",\n\t\t\t\"-C\",\n\t\t\trootContainingDist,\n\t\t\t\"dist\"\n\t\t], {\n\t\t\tencoding: \"utf8\",\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\tCOPYFILE_DISABLE: \"1\"\n\t\t\t}\n\t\t});\n\t\tif (result.status !== 0) throw new Error(result.stderr?.trim() || \"Failed to pack project artifact\");\n\t\treturn readFileSync(archivePath);\n\t} finally {\n\t\trmSync(tempDir, {\n\t\t\trecursive: true,\n\t\t\tforce: true\n\t\t});\n\t}\n}\n//#endregion\n//#region src/extract-artifact.ts\n/** Extract a packed project artifact tarball into `destDir` (creates `destDir/dist/`). */\nfunction extractProjectArtifact(archive, destDir) {\n\tconst tempDir = mkdtempSync(join(tmpdir(), \"keystroke-artifact-extract-\"));\n\tconst archivePath = join(tempDir, \"artifact.tgz\");\n\ttry {\n\t\twriteFileSync(archivePath, archive);\n\t\tconst result = spawnSync(\"tar\", [\n\t\t\t\"-xzf\",\n\t\t\tarchivePath,\n\t\t\t\"-C\",\n\t\t\tdestDir\n\t\t], {\n\t\t\tencoding: \"utf8\",\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\tCOPYFILE_DISABLE: \"1\"\n\t\t\t}\n\t\t});\n\t\tif (result.status !== 0) throw new Error(result.stderr?.trim() || \"Failed to extract project artifact\");\n\t} finally {\n\t\trmSync(tempDir, {\n\t\t\trecursive: true,\n\t\t\tforce: true\n\t\t});\n\t}\n}\n//#endregion\n//#region src/merge-route-manifest.ts\nfunction moduleFileOf(entry) {\n\treturn \"moduleFile\" in entry && typeof entry.moduleFile === \"string\" ? entry.moduleFile : void 0;\n}\n/** Replace manifest rows for rebuilt modules while keeping untouched routes and metadata. */\nfunction mergeStoredRouteManifest(base, rebuiltEntries) {\n\tconst rebuiltModuleFiles = new Set(rebuiltEntries.map(moduleFileOf).filter((value) => Boolean(value)));\n\tconst keptEntries = base.entries.filter((entry) => {\n\t\tconst moduleFile = moduleFileOf(entry);\n\t\tif (!moduleFile) return true;\n\t\treturn !rebuiltModuleFiles.has(moduleFile);\n\t});\n\tconst filteredRebuilt = rebuiltEntries.filter((entry) => entry.kind !== \"health\");\n\treturn {\n\t\t...base,\n\t\tentries: [...keptEntries, ...filteredRebuilt]\n\t};\n}\n//#endregion\n//#region src/merge-filtered-artifact.ts\nasync function mergeFilteredArtifact(input) {\n\tconst mergeRoot = mkdtempSync(join(tmpdir(), \"keystroke-artifact-merge-\"));\n\ttry {\n\t\textractProjectArtifact(input.baseArchive, mergeRoot);\n\t\tconst manifestPath = join(mergeRoot, ROUTE_MANIFEST_REL_PATH);\n\t\tconst mergedManifest = mergeStoredRouteManifest(parseStoredRouteManifest(JSON.parse(readFileSync(manifestPath, \"utf8\"))), input.filtered.manifestEntries);\n\t\twriteFileSync(manifestPath, `${JSON.stringify(mergedManifest, null, 2)}\\n`);\n\t\tfor (const file of input.filtered.files) {\n\t\t\tconst destination = join(mergeRoot, \"dist\", file.relativePath);\n\t\t\tmkdirSync(dirname(destination), { recursive: true });\n\t\t\twriteFileSync(destination, file.contents);\n\t\t\tif (file.sourceMap) writeFileSync(`${destination}.map`, file.sourceMap);\n\t\t}\n\t\treturn packDistTree(mergeRoot);\n\t} finally {\n\t\trmSync(mergeRoot, {\n\t\t\trecursive: true,\n\t\t\tforce: true\n\t\t});\n\t}\n}\n//#endregion\n//#region src/pack-artifact.ts\n/** Pack `dist/` into a gzip tarball suitable for `/app` extraction in the project server image. */\nfunction packProjectArtifact(projectRoot) {\n\tif (!existsSync(join(projectRoot, \"dist\"))) throw new Error(\"dist/ not found — run keystroke build first\");\n\treturn packDistTree(projectRoot);\n}\n//#endregion\nexport { packDistTree as a, extractProjectArtifact as i, mergeFilteredArtifact as n, mergeStoredRouteManifest as r, packProjectArtifact as t };\n\n//# sourceMappingURL=pack-artifact-DVnIKrsg.mjs.map"],"mappings":";;;;;;;;;;;AAUA,SAAS,aAAa,oBAAoB;CACzC,MAAM,UAAU,YAAY,KAAK,OAAO,GAAG,0BAA0B,CAAC;CACtE,MAAM,cAAc,KAAK,SAAS,cAAc;CAChD,IAAI;EACH,MAAM,SAAS,UAAU,OAAO;GAC/B;GACA;GACA;GACA;GACA;GACA;GACA;EACD,GAAG;GACF,UAAU;GACV,KAAK;IACJ,GAAG,QAAQ;IACX,kBAAkB;GACnB;EACD,CAAC;EACD,IAAI,OAAO,WAAW,GAAG,MAAM,IAAI,MAAM,OAAO,QAAQ,KAAK,KAAK,iCAAiC;EACnG,OAAO,aAAa,WAAW;CAChC,UAAU;EACT,OAAO,SAAS;GACf,WAAW;GACX,OAAO;EACR,CAAC;CACF;AACD;;AAIA,SAAS,uBAAuB,SAAS,SAAS;CACjD,MAAM,UAAU,YAAY,KAAK,OAAO,GAAG,6BAA6B,CAAC;CACzE,MAAM,cAAc,KAAK,SAAS,cAAc;CAChD,IAAI;EACH,cAAc,aAAa,OAAO;EAClC,MAAM,SAAS,UAAU,OAAO;GAC/B;GACA;GACA;GACA;EACD,GAAG;GACF,UAAU;GACV,KAAK;IACJ,GAAG,QAAQ;IACX,kBAAkB;GACnB;EACD,CAAC;EACD,IAAI,OAAO,WAAW,GAAG,MAAM,IAAI,MAAM,OAAO,QAAQ,KAAK,KAAK,oCAAoC;CACvG,UAAU;EACT,OAAO,SAAS;GACf,WAAW;GACX,OAAO;EACR,CAAC;CACF;AACD;AAGA,SAAS,aAAa,OAAO;CAC5B,OAAO,gBAAgB,SAAS,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAK;AAChG;;AAEA,SAAS,yBAAyB,MAAM,gBAAgB;CACvD,MAAM,qBAAqB,IAAI,IAAI,eAAe,IAAI,YAAY,EAAE,QAAQ,UAAU,QAAQ,KAAK,CAAC,CAAC;CACrG,MAAM,cAAc,KAAK,QAAQ,QAAQ,UAAU;EAClD,MAAM,aAAa,aAAa,KAAK;EACrC,IAAI,CAAC,YAAY,OAAO;EACxB,OAAO,CAAC,mBAAmB,IAAI,UAAU;CAC1C,CAAC;CACD,MAAM,kBAAkB,eAAe,QAAQ,UAAU,MAAM,SAAS,QAAQ;CAChF,OAAO;EACN,GAAG;EACH,SAAS,CAAC,GAAG,aAAa,GAAG,eAAe;CAC7C;AACD;AAGA,eAAe,sBAAsB,OAAO;CAC3C,MAAM,YAAY,YAAY,KAAK,OAAO,GAAG,2BAA2B,CAAC;CACzE,IAAI;EACH,uBAAuB,MAAM,aAAa,SAAS;EACnD,MAAM,eAAe,KAAK,WAAW,uBAAuB;EAC5D,MAAM,iBAAiB,yBAAyB,yBAAyB,KAAK,MAAM,aAAa,cAAc,MAAM,CAAC,CAAC,GAAG,MAAM,SAAS,eAAe;EACxJ,cAAc,cAAc,GAAG,KAAK,UAAU,gBAAgB,MAAM,CAAC,EAAE,GAAG;EAC1E,KAAK,MAAM,QAAQ,MAAM,SAAS,OAAO;GACxC,MAAM,cAAc,KAAK,WAAW,QAAQ,KAAK,YAAY;GAC7D,UAAU,QAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;GACnD,cAAc,aAAa,KAAK,QAAQ;GACxC,IAAI,KAAK,WAAW,cAAc,GAAG,YAAY,OAAO,KAAK,SAAS;EACvE;EACA,OAAO,aAAa,SAAS;CAC9B,UAAU;EACT,OAAO,WAAW;GACjB,WAAW;GACX,OAAO;EACR,CAAC;CACF;AACD;;AAIA,SAAS,oBAAoB,aAAa;CACzC,IAAI,CAAC,WAAW,KAAK,aAAa,MAAM,CAAC,GAAG,MAAM,IAAI,MAAM,6CAA6C;CACzG,OAAO,aAAa,WAAW;AAChC"}
|
|
@@ -12,7 +12,7 @@ The CLI routes runtime commands to one of two API targets:
|
|
|
12
12
|
|
|
13
13
|
| Target | Use for |
|
|
14
14
|
| --------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
15
|
-
| **Local** (
|
|
15
|
+
| **Local** (default `:3002`) | **Development and testing** — iterate on `src/`, run workflows/agents on your machine |
|
|
16
16
|
| **Cloud** (`platformUrl` + `activeProjectId`) | **Invoking and listing deployed resources** — production runs, triggers, remote audit after deploy |
|
|
17
17
|
|
|
18
18
|
```bash
|
|
@@ -37,7 +37,7 @@ Full routing rules: [cli skill](.agents/skills/keystroke-cli/SKILL.md) → [api-
|
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
# .env is created from .env.example on init — set ANTHROPIC_API_KEY + integration keys
|
|
40
|
-
keystroke start # API :
|
|
40
|
+
keystroke start # API :3002, dashboard :3000
|
|
41
41
|
keystroke health
|
|
42
42
|
keystroke agent prompt hello --message "Hi"
|
|
43
43
|
```
|
|
@@ -60,8 +60,8 @@ Required for cloud/platform commands and some trigger operations. Not required f
|
|
|
60
60
|
| Agents | `src/agents/` | `@keystrokehq/keystroke/agent` |
|
|
61
61
|
| Workflows | `src/workflows/` | `@keystrokehq/keystroke/workflow` |
|
|
62
62
|
| Triggers | `src/triggers/` | `@keystrokehq/keystroke/trigger` |
|
|
63
|
-
|
|
|
64
|
-
|
|
|
63
|
+
| Apps | `src/apps/` | `@keystrokehq/keystroke/app`, `keystroke app`, `keystroke connect` |
|
|
64
|
+
| Integrations | npm packages + catalog | `@keystrokehq/exa`, `@keystrokehq/slack`, … — discover with `keystroke app search` |
|
|
65
65
|
| Skills / files | `src/skills/`, `src/files/` | attached on `defineAgent` |
|
|
66
66
|
|
|
67
67
|
Default-export each module — the server discovers files under `src/`.
|
|
@@ -77,7 +77,7 @@ A few things that are easy to skip and cause common failures:
|
|
|
77
77
|
- **Research real APIs before mirroring them** — web search and fetch the actual reference & explore relevant docs; don't guess endpoints or payload shapes.
|
|
78
78
|
- **Run before you depend or deploy** — run the workflow/agent locally and read the real output (`keystroke workflow runs get <key> <run-id> --include steps,trace`) before wiring dependent steps.
|
|
79
79
|
- **Cover obvious edge cases** and write simple tests as needed (null field, empty array, a step that throws) — see [workflows skill](.agents/skills/keystroke-workflows/SKILL.md).
|
|
80
|
-
- **
|
|
80
|
+
- **Connected apps aren't in `.env`** — `keystroke deploy` never uploads `.env`; connect apps on the cloud target with `keystroke connect <slug>`. See [apps skill](.agents/skills/keystroke-apps/SKILL.md).
|
|
81
81
|
- **Shipping to prod** — full vs filtered deploy, WIP ignore directives: [deploy skill](.agents/skills/keystroke-deploy/SKILL.md).
|
|
82
82
|
|
|
83
83
|
## Audit & debug (CLI)
|
|
@@ -92,7 +92,7 @@ keystroke agent sessions get <agent-key> <session-id> --include messages,trace
|
|
|
92
92
|
keystroke workflow run <workflow-key> --input '{}'
|
|
93
93
|
keystroke workflow runs list <workflow-key>
|
|
94
94
|
keystroke workflow runs get <workflow-key> <run-id> --include steps,trace
|
|
95
|
-
keystroke
|
|
95
|
+
keystroke app list
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
**Cloud** (deployed — `keystroke config use cloud` or after deploy):
|
|
@@ -118,4 +118,4 @@ Read `src/` first — existing agents, workflows, actions, and triggers are your
|
|
|
118
118
|
| [gateways](.agents/skills/keystroke-gateways/SKILL.md) | Slack / messaging bindings |
|
|
119
119
|
| [skills](.agents/skills/keystroke-skills/SKILL.md) | `src/skills/` playbooks |
|
|
120
120
|
| [files](.agents/skills/keystroke-files/SKILL.md) | Agent workspace files |
|
|
121
|
-
| [
|
|
121
|
+
| [apps](.agents/skills/keystroke-apps/SKILL.md) | Connectable apps, catalog, connect |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: keystroke-actions
|
|
3
|
-
description: Define keystroke actions
|
|
3
|
+
description: Define keystroke actions — workflow steps and agent tools. App-backed actions flow through synced apps (app.action); catalog integrations import from npm packages. Use when authoring src/actions/ or wiring integrations.
|
|
4
4
|
metadata:
|
|
5
5
|
keystroke-domain: actions
|
|
6
6
|
---
|
|
@@ -9,6 +9,8 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
One **executable unit** used everywhere: workflow steps, agent tools, and codemode host calls.
|
|
11
11
|
|
|
12
|
+
Anything that calls an external API **flows through an app** — sync the app into `src/apps/` first, connect it, then author with `app.action()`. See [apps skill](.agents/skills/keystroke-apps/SKILL.md).
|
|
13
|
+
|
|
12
14
|
## Actions are leaf units — never call an action from an action
|
|
13
15
|
|
|
14
16
|
An action's `run` must **not** call another action (yours or an integration action like `postMessage`). Composition belongs in a **workflow**; an integration action is used directly as a workflow step or agent tool — not wrapped in a custom action.
|
|
@@ -28,7 +30,32 @@ async run(input) {
|
|
|
28
30
|
|
|
29
31
|
This is enforced at runtime (calling an action inside an action throws) and by lint (`no-restricted-imports` blocks importing `@keystrokehq/*/actions` inside `src/actions/`). An action **may** call an agent — see below.
|
|
30
32
|
|
|
31
|
-
##
|
|
33
|
+
## App-backed actions (custom integrations)
|
|
34
|
+
|
|
35
|
+
1. **App first** — create in the dashboard or `keystroke app create`, then `keystroke app sync <slug>` → `src/apps/<name>/app.ts`
|
|
36
|
+
2. **Connect** — `keystroke connect <slug>` (see [apps skill](.agents/skills/keystroke-apps/SKILL.md))
|
|
37
|
+
3. **Action** — `app.action()` in `src/actions/`, reading `credentials[app.slug]` in `run`
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { z } from "zod";
|
|
41
|
+
import { kwatch } from "../apps/kwatch/app";
|
|
42
|
+
|
|
43
|
+
export const kwatchListAlerts = kwatch.action({
|
|
44
|
+
slug: "kwatch-list-alerts",
|
|
45
|
+
input: z.object({}),
|
|
46
|
+
output: z.object({ ok: z.boolean(), alertCount: z.number() }),
|
|
47
|
+
async run(_input, credentials) {
|
|
48
|
+
const { apiKey } = credentials["keystroke/kwatch"];
|
|
49
|
+
return { ok: true, alertCount: 0 };
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Do not use bare `defineAction` for external API work — the app owns the connection.
|
|
55
|
+
|
|
56
|
+
## Pure actions (no app)
|
|
57
|
+
|
|
58
|
+
Use `defineAction` directly only when the step needs no app connection (pure logic, local transforms):
|
|
32
59
|
|
|
33
60
|
```ts
|
|
34
61
|
import { defineAction } from "@keystrokehq/action";
|
|
@@ -44,11 +71,16 @@ export const triage = defineAction({
|
|
|
44
71
|
});
|
|
45
72
|
```
|
|
46
73
|
|
|
47
|
-
|
|
74
|
+
## Catalog integration actions
|
|
75
|
+
|
|
76
|
+
Official integrations ship as npm packages with pre-built actions. Discover with `keystroke app search`, connect with `keystroke connect <slug>`, then import in workflows or agent tools — not in `src/actions/`:
|
|
48
77
|
|
|
49
|
-
|
|
78
|
+
```ts
|
|
79
|
+
import { exaSearch } from "@keystrokehq/exa/actions";
|
|
80
|
+
import { postMessage } from "@keystrokehq/slack/actions";
|
|
81
|
+
```
|
|
50
82
|
|
|
51
|
-
|
|
83
|
+
Detail: [catalog-and-imports.md](references/catalog-and-imports.md).
|
|
52
84
|
|
|
53
85
|
## Call an agent from an action (allowed)
|
|
54
86
|
|
|
@@ -75,6 +107,6 @@ export const researchSignup = defineAction({
|
|
|
75
107
|
|
|
76
108
|
## Next references
|
|
77
109
|
|
|
78
|
-
- [catalog-and-imports.md](references/catalog-and-imports.md) —
|
|
110
|
+
- [catalog-and-imports.md](references/catalog-and-imports.md) — catalog discovery, npm imports
|
|
79
111
|
|
|
80
|
-
Related: [
|
|
112
|
+
Related: [apps](.agents/skills/keystroke-apps/SKILL.md), [workflows](.agents/skills/keystroke-workflows/SKILL.md), [agents](.agents/skills/keystroke-agents/SKILL.md).
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# Integration actions
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Official catalog integrations
|
|
4
4
|
|
|
5
|
-
1.
|
|
6
|
-
2.
|
|
7
|
-
3.
|
|
8
|
-
|
|
9
|
-
## Use integration actions
|
|
10
|
-
|
|
11
|
-
Import in `src/workflows/` or on `defineAgent` `tools` — not in `src/actions/`:
|
|
5
|
+
1. Discover: `keystroke app search <query>` → `app show <slug>` → `app actions <slug>`
|
|
6
|
+
2. Connect: `keystroke connect <slug>`
|
|
7
|
+
3. Add npm package (e.g. `@keystrokehq/exa`, `@keystrokehq/googlesuper`, `@keystrokehq/slack`)
|
|
8
|
+
4. Import in `src/workflows/` or on `defineAgent` `tools` — not in `src/actions/`:
|
|
12
9
|
|
|
13
10
|
```ts
|
|
14
11
|
import { exaSearch, exaAnswer } from "@keystrokehq/exa/actions";
|
|
@@ -16,21 +13,30 @@ import { postMessage } from "@keystrokehq/slack/actions";
|
|
|
16
13
|
import { googlesuperSendEmail } from "@keystrokehq/googlesuper/actions";
|
|
17
14
|
```
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
Catalog packages ship a pre-built app + actions — you do not author `src/apps/` for these.
|
|
17
|
+
|
|
18
|
+
## Custom integrations (app-first)
|
|
19
|
+
|
|
20
|
+
All custom API work flows through a synced app:
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
1. Create the app (dashboard or `keystroke app create --name ... --field ...`)
|
|
23
|
+
2. Sync: `keystroke app sync <slug>` → `src/apps/<name>/app.ts`
|
|
24
|
+
3. Connect: `keystroke connect <slug>`
|
|
25
|
+
4. Author actions in `src/actions/` with `app.action()`:
|
|
22
26
|
|
|
23
27
|
```ts
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
slug: "
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
import { internalApi } from "../apps/internal-api/app";
|
|
30
|
+
|
|
31
|
+
export const fetchStatus = internalApi.action({
|
|
32
|
+
slug: "fetch-status",
|
|
33
|
+
input: z.object({}),
|
|
34
|
+
output: z.object({ ok: z.boolean() }),
|
|
35
|
+
async run(_input, credentials) {
|
|
36
|
+
const { apiKey } = credentials["acme/internal-api"];
|
|
37
|
+
return { ok: true };
|
|
32
38
|
},
|
|
33
39
|
});
|
|
34
40
|
```
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
Re-run `keystroke app sync <slug>` after template changes on the platform. Full app lifecycle: [apps skill](../../keystroke-apps/SKILL.md).
|
|
@@ -28,11 +28,13 @@ export default defineAgent({
|
|
|
28
28
|
**Skills + files** — static playbooks and docs in the workspace:
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
+
import { defineSandbox } from "@keystrokehq/sandbox";
|
|
32
|
+
|
|
31
33
|
defineAgent({
|
|
32
34
|
slug: "support",
|
|
33
|
-
systemPrompt: "Read /workspace/product-guide.md before answering.",
|
|
35
|
+
systemPrompt: "Read /workspace/agent/product-guide.md before answering.",
|
|
34
36
|
skills: ["support"],
|
|
35
|
-
files: true,
|
|
37
|
+
sandbox: defineSandbox({ files: true }),
|
|
36
38
|
});
|
|
37
39
|
```
|
|
38
40
|
|
|
@@ -58,8 +60,8 @@ Follow up in the same session: `--session-id <id>`.
|
|
|
58
60
|
|
|
59
61
|
## Workspace
|
|
60
62
|
|
|
61
|
-
- Skills →
|
|
62
|
-
- Files from `src/files/{key}/` → `/workspace/`
|
|
63
|
+
- Skills → `/workspace/agent/skills/{key}/`
|
|
64
|
+
- Files from `src/files/{key}/` → `/workspace/agent/`
|
|
63
65
|
- Pass `module: import.meta.url` so skills/files resolve in dev without a rebuild
|
|
64
66
|
|
|
65
67
|
## Next references
|
|
@@ -68,4 +70,4 @@ Follow up in the same session: `--session-id <id>`.
|
|
|
68
70
|
- [tools-mcp-codemode.md](references/tools-mcp-codemode.md) — actions as tools, MCP, codemode
|
|
69
71
|
- [workflows-and-testing.md](references/workflows-and-testing.md) — sessions, workflow handoff
|
|
70
72
|
|
|
71
|
-
Related: [actions](.agents/skills/keystroke-actions/SKILL.md), [workflows](.agents/skills/keystroke-workflows/SKILL.md), [files](.agents/skills/keystroke-files/SKILL.md), [
|
|
73
|
+
Related: [actions](.agents/skills/keystroke-actions/SKILL.md), [workflows](.agents/skills/keystroke-workflows/SKILL.md), [files](.agents/skills/keystroke-files/SKILL.md), [apps](.agents/skills/keystroke-apps/SKILL.md).
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: keystroke-apps
|
|
3
|
+
description: Connectable apps — defineApp, app.action, catalog discovery via keystroke app search, custom app create/sync, and connect. Use when wiring integrations, OAuth, API keys, or a deployed run fails because an app is not connected.
|
|
4
|
+
metadata:
|
|
5
|
+
keystroke-domain: apps
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Apps
|
|
9
|
+
|
|
10
|
+
An **app** is a connectable integration: a slug, an auth kind, and (for custom apps) a field template. **Connected** = secrets for that app have been uploaded via the connect flow.
|
|
11
|
+
|
|
12
|
+
Secrets never live in source or `.env`. Each **target** (local server vs cloud project) has its own connections — `keystroke deploy` uploads code only, not connections. Connect apps on **every target** you run on.
|
|
13
|
+
|
|
14
|
+
Custom actions that call external APIs **must** go through an app — sync it into `src/apps/` first, then use `app.action()` in `src/actions/`. See [actions skill](.agents/skills/keystroke-actions/SKILL.md).
|
|
15
|
+
|
|
16
|
+
## Two sources of apps
|
|
17
|
+
|
|
18
|
+
| Source | How you get it | Author in code |
|
|
19
|
+
| ------ | -------------- | -------------- |
|
|
20
|
+
| **Catalog** | ~1000 Composio integrations + first-party (`exa`, `slack`, `googlesuper`) | npm package `@keystrokehq/<slug>` — discover via CLI |
|
|
21
|
+
| **Custom** | Dashboard UI or `keystroke app create`, then `keystroke app sync <slug>` | `src/apps/<name>/app.ts` from sync |
|
|
22
|
+
|
|
23
|
+
## Discover catalog integrations
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
keystroke auth login
|
|
27
|
+
keystroke app search github # live Composio catalog
|
|
28
|
+
keystroke app show github
|
|
29
|
+
keystroke app actions github --search issue
|
|
30
|
+
keystroke app action GITHUB_CREATE_ISSUE
|
|
31
|
+
keystroke app list # apps registered in your org
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Discovery is read-only — there is no `app install` yet. To connect an app, its slug must be in your org registry (`app list`).
|
|
35
|
+
|
|
36
|
+
## Create and sync custom apps
|
|
37
|
+
|
|
38
|
+
Create in the dashboard or CLI, then pull the template into your project:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
keystroke app create \
|
|
42
|
+
--name "Internal API" \
|
|
43
|
+
--description "Our internal service" \
|
|
44
|
+
--field apiKey:secret
|
|
45
|
+
|
|
46
|
+
keystroke app sync acme/internal-api # writes src/apps/internal-api/app.ts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Re-run `sync` after template changes on the platform. Then author actions with `app.action()` in `src/actions/`.
|
|
50
|
+
|
|
51
|
+
## Author with defineApp
|
|
52
|
+
|
|
53
|
+
Synced apps land as `defineApp` in `src/apps/<name>/app.ts`:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { defineApp } from "@keystrokehq/keystroke/app";
|
|
57
|
+
import { z } from "zod";
|
|
58
|
+
|
|
59
|
+
export const kwatch = defineApp({
|
|
60
|
+
slug: "keystroke/kwatch",
|
|
61
|
+
auth: "api_key",
|
|
62
|
+
credential: { apiKey: z.string() },
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Actions use `app.action()` — the app binding is automatic:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { kwatch } from "../apps/kwatch/app";
|
|
70
|
+
|
|
71
|
+
export const kwatchListAlerts = kwatch.action({
|
|
72
|
+
slug: "kwatch-list-alerts",
|
|
73
|
+
input: z.object({}),
|
|
74
|
+
output: z.object({ ok: z.boolean(), alertCount: z.number() }),
|
|
75
|
+
async run(_input, credentials) {
|
|
76
|
+
const { apiKey } = credentials["keystroke/kwatch"];
|
|
77
|
+
return { ok: true, alertCount: 0 };
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Auth kinds: `keystroke` (MCP/Composio catalog apps), `api_key` (custom fields), `oauth` (token resolved at runtime). In `run`, read connected fields as `credentials[app.slug]`.
|
|
83
|
+
|
|
84
|
+
## Connect an app
|
|
85
|
+
|
|
86
|
+
`keystroke connect <slug>` opens the dashboard connect flow (OAuth or API-key form, depending on the app). This is how secrets get attached to an app — not via `.env`.
|
|
87
|
+
|
|
88
|
+
**Official integrations** — the cleanest path:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
keystroke connect google
|
|
92
|
+
keystroke connect slack
|
|
93
|
+
keystroke connect exa
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Custom apps** — sync the template first, then connect:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
keystroke app sync acme/internal-api
|
|
100
|
+
keystroke connect acme/internal-api
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Run `connect` against **each target** you deploy to (local while developing; cloud after `keystroke config use cloud`). The dashboard **Apps** page offers the same flow.
|
|
104
|
+
|
|
105
|
+
Field names in the connect form match the app's synced template. Default scope is **project** — see [cli-and-catalog.md](references/cli-and-catalog.md).
|
|
106
|
+
|
|
107
|
+
## Before deploy: connect on cloud
|
|
108
|
+
|
|
109
|
+
Cloud starts with **no connected apps**. After the first deploy:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
keystroke deploy --project <id>
|
|
113
|
+
keystroke config use cloud
|
|
114
|
+
keystroke connect google
|
|
115
|
+
keystroke connect exa
|
|
116
|
+
keystroke app list
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
A runtime error about a missing connection almost always means the app was connected locally but not on the cloud target.
|
|
120
|
+
|
|
121
|
+
## Audit
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
keystroke app list # org-registered apps
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Failed integration call → confirm target (`keystroke config show`), the app is connected on that target, and the action uses the right slug.
|
|
128
|
+
|
|
129
|
+
## Next references
|
|
130
|
+
|
|
131
|
+
- [cli-and-catalog.md](references/cli-and-catalog.md) — `keystroke app` commands, connect, scopes, local vs cloud
|
|
132
|
+
|
|
133
|
+
Related: [cli](.agents/skills/keystroke-cli/SKILL.md), [actions](.agents/skills/keystroke-actions/SKILL.md), [gateways](.agents/skills/keystroke-gateways/SKILL.md).
|