@keystrokehq/cli 0.1.44 → 0.1.46
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 +32 -2
- package/dist/configs/tsconfig.base.json +23 -0
- package/dist/{templates/hello-world/vitest.config.ts → configs/vitest.config.mjs} +9 -2
- package/dist/{templates/hello-world/vitest.setup.integration.ts → configs/vitest.setup.integration.mjs} +3 -1
- package/dist/dist-BPmBLa_o.mjs +3 -0
- package/dist/{dist-8dMGGwLh.mjs → dist-BmO-d0ms.mjs} +17 -5
- package/dist/dist-BmO-d0ms.mjs.map +1 -0
- package/dist/{dist-CU5W5V2Z.mjs → dist-CTHhFGjP.mjs} +3 -3
- package/dist/{dist-CU5W5V2Z.mjs.map → dist-CTHhFGjP.mjs.map} +1 -1
- package/dist/index.mjs +221 -53
- package/dist/index.mjs.map +1 -1
- package/dist/{maybe-auto-update-4tu8zRcS.mjs → maybe-auto-update-DPhUOdEF.mjs} +4 -98
- package/dist/maybe-auto-update-DPhUOdEF.mjs.map +1 -0
- package/dist/skills-bundle/_AGENTS.mcp.md +1 -1
- package/dist/skills-bundle/_AGENTS.md +18 -4
- package/dist/templates/hello-world/README.md +8 -60
- package/dist/templates/hello-world/package.json +6 -11
- package/dist/templates/hello-world/tsconfig.json +1 -14
- package/dist/{version-D2psJMX3.mjs → version-key3f4tc.mjs} +112 -18
- package/dist/version-key3f4tc.mjs.map +1 -0
- package/package.json +9 -7
- package/dist/dist-8dMGGwLh.mjs.map +0 -1
- package/dist/dist-C7qKTDlJ.mjs +0 -3
- package/dist/maybe-auto-update-4tu8zRcS.mjs.map +0 -1
- package/dist/version-D2psJMX3.mjs.map +0 -1
- /package/dist/{templates/hello-world/.oxlintrc.json → configs/oxlintrc.json} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
2
|
+
import { a as detectCliInstall, i as isReleaseAgeBlock, m as getCliConfigDir, n as resolveCliRoot, r as formatReleaseAgeBlockMessage, t as readCliVersion } from "./version-key3f4tc.mjs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
//#region src/update/compare-version.ts
|
|
7
7
|
function parseVersion(version) {
|
|
@@ -23,100 +23,6 @@ function isNewerVersion(latest, current) {
|
|
|
23
23
|
return latestPatch > currentPatch;
|
|
24
24
|
}
|
|
25
25
|
//#endregion
|
|
26
|
-
//#region src/update/detect-cli-install.ts
|
|
27
|
-
function realpathSafe(path) {
|
|
28
|
-
try {
|
|
29
|
-
return realpathSync(path);
|
|
30
|
-
} catch {
|
|
31
|
-
return path;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function detectManagerFromLockfile(dir) {
|
|
35
|
-
if (existsSync(join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
36
|
-
if (existsSync(join(dir, "bun.lockb")) || existsSync(join(dir, "bun.lock"))) return "bun";
|
|
37
|
-
if (existsSync(join(dir, "yarn.lock"))) return "yarn";
|
|
38
|
-
if (existsSync(join(dir, "package-lock.json"))) return "npm";
|
|
39
|
-
}
|
|
40
|
-
function findLocalProjectRoot(packageRoot) {
|
|
41
|
-
const normalizedRoot = realpathSafe(packageRoot);
|
|
42
|
-
let dir = dirname(packageRoot);
|
|
43
|
-
while (dir !== dirname(dir)) {
|
|
44
|
-
const installedCliPath = join(dir, "node_modules", "@keystrokehq", "cli");
|
|
45
|
-
if (existsSync(join(dir, "package.json")) && realpathSafe(installedCliPath) === normalizedRoot) return dir;
|
|
46
|
-
dir = dirname(dir);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function globalRootForManager(manager) {
|
|
50
|
-
const { command, args } = {
|
|
51
|
-
pnpm: {
|
|
52
|
-
command: "pnpm",
|
|
53
|
-
args: ["root", "-g"]
|
|
54
|
-
},
|
|
55
|
-
npm: {
|
|
56
|
-
command: "npm",
|
|
57
|
-
args: ["root", "-g"]
|
|
58
|
-
},
|
|
59
|
-
yarn: {
|
|
60
|
-
command: "yarn",
|
|
61
|
-
args: ["global", "dir"]
|
|
62
|
-
},
|
|
63
|
-
bun: {
|
|
64
|
-
command: "bun",
|
|
65
|
-
args: ["pm", "bin"]
|
|
66
|
-
}
|
|
67
|
-
}[manager];
|
|
68
|
-
const result = spawnSync(command, args, { encoding: "utf8" });
|
|
69
|
-
if (result.status !== 0) return;
|
|
70
|
-
const root = result.stdout.trim();
|
|
71
|
-
return root ? realpathSafe(root) : void 0;
|
|
72
|
-
}
|
|
73
|
-
function detectGlobalManager(packageRoot) {
|
|
74
|
-
const normalizedRoot = realpathSafe(packageRoot);
|
|
75
|
-
if (normalizedRoot.includes(`${join("", ".pnpm")}`)) return "pnpm";
|
|
76
|
-
for (const manager of [
|
|
77
|
-
"pnpm",
|
|
78
|
-
"npm",
|
|
79
|
-
"yarn",
|
|
80
|
-
"bun"
|
|
81
|
-
]) {
|
|
82
|
-
const globalRoot = globalRootForManager(manager);
|
|
83
|
-
if (globalRoot && normalizedRoot.startsWith(globalRoot)) return manager;
|
|
84
|
-
}
|
|
85
|
-
if (process.env.PNPM_HOME) return "pnpm";
|
|
86
|
-
return detectPackageManager();
|
|
87
|
-
}
|
|
88
|
-
function detectCliInstall(packageRoot) {
|
|
89
|
-
const normalizedRoot = realpathSafe(packageRoot);
|
|
90
|
-
const projectRoot = findLocalProjectRoot(normalizedRoot);
|
|
91
|
-
if (projectRoot) return {
|
|
92
|
-
kind: "local",
|
|
93
|
-
packageManager: detectManagerFromLockfile(projectRoot) ?? detectPackageManager(),
|
|
94
|
-
packageRoot: normalizedRoot,
|
|
95
|
-
projectRoot
|
|
96
|
-
};
|
|
97
|
-
return {
|
|
98
|
-
kind: "global",
|
|
99
|
-
packageManager: detectGlobalManager(normalizedRoot),
|
|
100
|
-
packageRoot: normalizedRoot
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
//#endregion
|
|
104
|
-
//#region src/update/detect-release-age-block.ts
|
|
105
|
-
const RELEASE_AGE_PATTERNS = [
|
|
106
|
-
/minimumReleaseAge/i,
|
|
107
|
-
/minimum-release-age/i,
|
|
108
|
-
/ERR_PNPM_NO_MATURE_MATCHING_VERSION/,
|
|
109
|
-
/ERR_PNPM_MINIMUM_RELEASE_AGE/,
|
|
110
|
-
/does not meet the minimumReleaseAge constraint/
|
|
111
|
-
];
|
|
112
|
-
function isReleaseAgeBlock(output) {
|
|
113
|
-
return RELEASE_AGE_PATTERNS.some((pattern) => pattern.test(output));
|
|
114
|
-
}
|
|
115
|
-
function formatReleaseAgeBlockMessage(options) {
|
|
116
|
-
const retry = options.retryAfter !== void 0 ? ` Retry after ${options.retryAfter.toLocaleString()}.` : "";
|
|
117
|
-
return `@keystrokehq/cli ${options.availableVersion} is available but blocked by your minimum-release-age setting. You're on ${options.currentVersion}.${retry} Add @keystrokehq/cli to minimumReleaseAgeExclude to install immediately.`;
|
|
118
|
-
}
|
|
119
|
-
//#endregion
|
|
120
26
|
//#region src/update/fetch-latest-version.ts
|
|
121
27
|
const REGISTRY_URL = "https://registry.npmjs.org/@keystrokehq%2Fcli";
|
|
122
28
|
const REQUEST_TIMEOUT_MS = 3e3;
|
|
@@ -405,4 +311,4 @@ async function maybeAutoUpdate(argv) {
|
|
|
405
311
|
//#endregion
|
|
406
312
|
export { maybeAutoUpdate };
|
|
407
313
|
|
|
408
|
-
//# sourceMappingURL=maybe-auto-update-
|
|
314
|
+
//# sourceMappingURL=maybe-auto-update-DPhUOdEF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maybe-auto-update-DPhUOdEF.mjs","names":[],"sources":["../src/update/compare-version.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","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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"}
|
|
@@ -69,7 +69,7 @@ Workflows also render as an interactive **canvas** in the platform. Keep durable
|
|
|
69
69
|
- **Verify integration prerequisites** — before composing a catalog action into a workflow, run it once with `keystroke app execute` (via `exec_command`) to confirm the IDs and fields exist in the connected account.
|
|
70
70
|
- **Connected apps aren't in `.env`** — deploy never uploads `.env`; use the `connect_app` tool to get a link for the user to connect integrations in the web app (a browser cannot open in this workspace).
|
|
71
71
|
- **Filtered deploys** — determine if you should do a full vs filtered deploy, or use WIP ignore directives (`search_docs "filtered deploy"`).
|
|
72
|
-
- **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.
|
|
72
|
+
- **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.
|
|
73
73
|
- **Actions are leaf units** — an action never calls another action (including integration actions like `postMessage`). Compose actions in a workflow, or attach an integration action directly as a workflow step or agent tool. An action *can* call an agent, letting you create "subagent tools" or "agent steps".
|
|
74
74
|
|
|
75
75
|
## Audit & debug
|
|
@@ -23,7 +23,7 @@ Keystroke separates *where you write code* from *where it runs*:
|
|
|
23
23
|
| **Local codebase** | This directory — `keystroke.config.ts` + `src/`. Source of truth. | Your machine / Git |
|
|
24
24
|
| **Platform project** | Org-owned cloud runtime (its own server, URL, credentials, run history). Inactive until first deploy. | Keystroke cloud |
|
|
25
25
|
|
|
26
|
-
`keystroke deploy` builds `src/`, uploads
|
|
26
|
+
`keystroke deploy` builds `src/` into `dist/`, runs lint and typecheck, uploads the `dist/` artifact, and promotes it as the project's single live runtime. A deploy replaces what's running — there is no separate dev and prod within one project.
|
|
27
27
|
|
|
28
28
|
### The web platform
|
|
29
29
|
|
|
@@ -36,6 +36,8 @@ Keystroke discovers everything under `src/` by convention.
|
|
|
36
36
|
```
|
|
37
37
|
my-app/
|
|
38
38
|
keystroke.config.ts # project configuration
|
|
39
|
+
tsconfig.json # extends @keystrokehq/cli/tsconfig.json (committed)
|
|
40
|
+
package.json # dev/build/lint/typecheck/test scripts
|
|
39
41
|
src/
|
|
40
42
|
agents/ # LLM agents
|
|
41
43
|
actions/ # reusable leaf units of work
|
|
@@ -48,14 +50,26 @@ my-app/
|
|
|
48
50
|
|
|
49
51
|
Each primitive imports from `@keystrokehq/keystroke/<piece>` (`/agent`, `/action`, `/workflow`, `/trigger`, `/sandbox`). Integrations are `@keystrokehq/<slug>` packages.
|
|
50
52
|
|
|
53
|
+
### Dev tooling
|
|
54
|
+
|
|
55
|
+
Lint, typecheck, and test run through the CLI. Projects depend on `@keystrokehq/keystroke`, `zod`, and `@keystrokehq/cli` — not oxlint, TypeScript, vitest, or `@types/node`. Config files live in the CLI bundle; running these commands does not write files into the project.
|
|
56
|
+
|
|
57
|
+
**Deploy is the gate.** `keystroke deploy` runs lint and typecheck before it builds and ships — do not run them separately before every deploy. Attempt deploy, fix what it reports, redeploy. Use the commands below only when you want a faster local loop (especially `keystroke test`).
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
keystroke deploy # lint + typecheck + build + ship dist/ — start here
|
|
61
|
+
keystroke test # optional local loop (unit + integration)
|
|
62
|
+
pnpm test # same — package.json scripts call the CLI
|
|
63
|
+
```
|
|
64
|
+
|
|
51
65
|
## The deploy-first loop
|
|
52
66
|
|
|
53
67
|
Keystroke is deploy-first: build, ship, then run and inspect what's live. Deploy often.
|
|
54
68
|
|
|
55
69
|
1. **Auth once** — `keystroke auth login` (token is stored and reused).
|
|
56
70
|
2. **Verify prerequisites** — `keystroke app execute <app> <tool> --input '{...}'` runs any connected catalog action against the cloud with no deploy and no server. Before wiring an integration into a workflow, execute it once for real: confirm the IDs, custom fields, and labels you're about to hard-code actually exist in the *connected* account. Schema inspection and hand-rolled dry-run flags prove nothing about the mutating path.
|
|
57
|
-
3. **Edit** primitives under `src/`. Unit-test workflow logic in-process with `executeWorkflow`
|
|
58
|
-
4. **Deploy** — `keystroke deploy` (full) or `keystroke deploy --filter agents/support` (one module).
|
|
71
|
+
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.
|
|
72
|
+
4. **Deploy** — `keystroke deploy` (full) or `keystroke deploy --filter agents/support` (one module). Deploy runs lint and typecheck first — attempt it directly; do not pre-run lint, typecheck, or build.
|
|
59
73
|
5. **Run** — `keystroke workflow run <slug> --input '{...}'` / `keystroke agent prompt <slug> --message "..."`.
|
|
60
74
|
6. **Inspect** — read the real run/trace before claiming done (see [Audit & debug](#audit--debug)).
|
|
61
75
|
7. Repeat.
|
|
@@ -63,7 +77,7 @@ Keystroke is deploy-first: build, ship, then run and inspect what's live. Deploy
|
|
|
63
77
|
```bash
|
|
64
78
|
keystroke auth status # current user + org
|
|
65
79
|
keystroke project list # your platform projects
|
|
66
|
-
keystroke deploy # build + ship
|
|
80
|
+
keystroke deploy # lint + typecheck + build + ship dist/ (first deploy must be full)
|
|
67
81
|
keystroke workflow run greeting --input '{"name":"Ada"}' # run a workflow
|
|
68
82
|
keystroke agent prompt hello --message "Hi" # prompt an agent
|
|
69
83
|
keystroke app execute github github_get_the_authenticated_user # run a connected catalog action
|
|
@@ -1,67 +1,15 @@
|
|
|
1
1
|
# {{projectName}}
|
|
2
2
|
|
|
3
|
-
Keystroke project — agents, workflows, actions, and triggers
|
|
3
|
+
A Keystroke project — agents, workflows, actions, and triggers live in `src/`.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Keystroke is deploy-first: build in `src/`, deploy to your platform project, then run and inspect what's deployed with the CLI.
|
|
5
|
+
Build, lint, typecheck, and test are bundled into the CLI, so this project only depends on
|
|
6
|
+
`@keystrokehq/keystroke`, `zod`, and `@keystrokehq/cli`.
|
|
8
7
|
|
|
9
8
|
```bash
|
|
10
|
-
pnpm install
|
|
11
|
-
keystroke auth login
|
|
12
|
-
keystroke project link --project <slug>
|
|
13
|
-
keystroke deploy
|
|
9
|
+
pnpm install
|
|
10
|
+
keystroke auth login # once
|
|
11
|
+
keystroke project link --project <slug> # keystroke project list
|
|
12
|
+
keystroke deploy # lint + typecheck + build + ship dist/ (no pre-steps needed)
|
|
14
13
|
```
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
Prompt the hello agent and run the greeting workflow against your deployed project:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
keystroke agent prompt hello --message "Hi"
|
|
21
|
-
keystroke workflow run greeting --input '{"name":"Ada"}'
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Use `--filter agents/hello` (or `workflows/greeting`) to redeploy a single module fast.
|
|
25
|
-
<!-- example:end -->
|
|
26
|
-
|
|
27
|
-
## Run locally (optional)
|
|
28
|
-
|
|
29
|
-
To iterate offline without deploying, run a local server. Create a `.env` with `ANTHROPIC_API_KEY` and any integration keys, then:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
keystroke dev # watch src/, rebuild, restart the API (API :3002, dashboard :3000)
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The same `keystroke agent` / `keystroke workflow` commands then target your local server. Login at `http://localhost:3000` (`admin@example.com` / `adminadmin` by default).
|
|
36
|
-
|
|
37
|
-
## Linting
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
pnpm lint # oxlint
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Testing
|
|
44
|
-
|
|
45
|
-
Unit tests run without a server or API keys. Integration tests load `.env` and skip when required keys are unset.
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
pnpm test:unit # src/**/*.test.ts
|
|
49
|
-
pnpm test:int # src/**/*.int.test.ts (needs ANTHROPIC_API_KEY for agent tests)
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
<!-- example:start -->
|
|
53
|
-
Example: `src/workflows/greeting.test.ts` uses `executeWorkflow` to test the `greeting` workflow in-process.
|
|
54
|
-
<!-- example:end -->
|
|
55
|
-
|
|
56
|
-
## Layout
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
src/
|
|
60
|
-
actions/ # defineAction — shared steps for agents and workflows
|
|
61
|
-
agents/ # defineAgent
|
|
62
|
-
files/ # agent workspace files
|
|
63
|
-
skills/ # agent skills (SKILL.md)
|
|
64
|
-
triggers/ # cron, webhook, poll attachments
|
|
65
|
-
workflows/ # defineWorkflow
|
|
66
|
-
keystroke.config.ts # optional server config + platform project/org slugs
|
|
67
|
-
```
|
|
15
|
+
Iterate locally with `keystroke dev`. See **`AGENTS.md`** for the full authoring guide, commands, and project layout.
|
|
@@ -7,20 +7,15 @@
|
|
|
7
7
|
"dev": "keystroke dev",
|
|
8
8
|
"build": "keystroke build",
|
|
9
9
|
"start": "keystroke start",
|
|
10
|
-
"lint": "
|
|
11
|
-
"typecheck": "
|
|
12
|
-
"test
|
|
13
|
-
"test:int": "vitest run --project integration"
|
|
10
|
+
"lint": "keystroke lint",
|
|
11
|
+
"typecheck": "keystroke typecheck",
|
|
12
|
+
"test": "keystroke test"
|
|
14
13
|
},
|
|
15
14
|
"dependencies": {
|
|
16
|
-
"@keystrokehq/keystroke": "
|
|
17
|
-
"zod": "
|
|
15
|
+
"@keystrokehq/keystroke": "latest",
|
|
16
|
+
"zod": "latest"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
|
-
"@keystrokehq/cli": "
|
|
21
|
-
"@types/node": "{{catalog:@types/node}}",
|
|
22
|
-
"oxlint": "{{catalog:oxlint}}",
|
|
23
|
-
"typescript": "{{catalog:typescript}}",
|
|
24
|
-
"vitest": "{{catalog:vitest}}"
|
|
19
|
+
"@keystrokehq/cli": "latest"
|
|
25
20
|
}
|
|
26
21
|
}
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"extends": "@keystrokehq/cli/tsconfig.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
|
-
"strict": true,
|
|
5
|
-
"esModuleInterop": true,
|
|
6
|
-
"skipLibCheck": true,
|
|
7
|
-
"isolatedModules": true,
|
|
8
|
-
"moduleDetection": "force",
|
|
9
|
-
"verbatimModuleSyntax": true,
|
|
10
|
-
"noUncheckedIndexedAccess": true,
|
|
11
|
-
"moduleResolution": "bundler",
|
|
12
|
-
"module": "ESNext",
|
|
13
|
-
"target": "ES2022",
|
|
14
|
-
"lib": ["ES2022"],
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
"types": ["node"],
|
|
17
4
|
"rootDir": "src"
|
|
18
5
|
},
|
|
19
6
|
"include": ["src"]
|
|
@@ -3,7 +3,7 @@ import { dt as LOCAL_PLATFORM_ORIGIN, er as originFromPublicUrl } from "./dist-D
|
|
|
3
3
|
import Conf from "conf";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
//#region src/resolve-platform-url.ts
|
|
@@ -77,21 +77,6 @@ function getPlatformUrl(config) {
|
|
|
77
77
|
return resolvePlatformUrlForWebUrl(getWebUrl(config), { fallback: config.get("platformUrl") });
|
|
78
78
|
}
|
|
79
79
|
//#endregion
|
|
80
|
-
//#region src/project/resolve-cli-root.ts
|
|
81
|
-
function isCliPackage(dir) {
|
|
82
|
-
const packageJsonPath = join(dir, "package.json");
|
|
83
|
-
if (!existsSync(packageJsonPath)) return false;
|
|
84
|
-
return JSON.parse(readFileSync(packageJsonPath, "utf8")).name === "@keystrokehq/cli";
|
|
85
|
-
}
|
|
86
|
-
function resolveCliRoot(fromModuleUrl) {
|
|
87
|
-
let dir = dirname(fileURLToPath(fromModuleUrl));
|
|
88
|
-
while (dir !== dirname(dir)) {
|
|
89
|
-
if (isCliPackage(dir)) return dir;
|
|
90
|
-
dir = dirname(dir);
|
|
91
|
-
}
|
|
92
|
-
throw new Error("Could not resolve keystroke CLI package root");
|
|
93
|
-
}
|
|
94
|
-
//#endregion
|
|
95
80
|
//#region src/init/package-manager.ts
|
|
96
81
|
const MANAGERS = [
|
|
97
82
|
"pnpm",
|
|
@@ -157,12 +142,121 @@ function buildPlaygroundWorkspace(monorepoRoot) {
|
|
|
157
142
|
}).status !== 0) throw new Error("workspace build failed (pnpm run build)");
|
|
158
143
|
}
|
|
159
144
|
//#endregion
|
|
145
|
+
//#region src/update/detect-cli-install.ts
|
|
146
|
+
function realpathSafe(path) {
|
|
147
|
+
try {
|
|
148
|
+
return realpathSync(path);
|
|
149
|
+
} catch {
|
|
150
|
+
return path;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function detectManagerFromLockfile(dir) {
|
|
154
|
+
if (existsSync(join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
155
|
+
if (existsSync(join(dir, "bun.lockb")) || existsSync(join(dir, "bun.lock"))) return "bun";
|
|
156
|
+
if (existsSync(join(dir, "yarn.lock"))) return "yarn";
|
|
157
|
+
if (existsSync(join(dir, "package-lock.json"))) return "npm";
|
|
158
|
+
}
|
|
159
|
+
function findLocalProjectRoot(packageRoot) {
|
|
160
|
+
const normalizedRoot = realpathSafe(packageRoot);
|
|
161
|
+
let dir = dirname(packageRoot);
|
|
162
|
+
while (dir !== dirname(dir)) {
|
|
163
|
+
const installedCliPath = join(dir, "node_modules", "@keystrokehq", "cli");
|
|
164
|
+
if (existsSync(join(dir, "package.json")) && realpathSafe(installedCliPath) === normalizedRoot) return dir;
|
|
165
|
+
dir = dirname(dir);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function globalRootForManager(manager) {
|
|
169
|
+
const { command, args } = {
|
|
170
|
+
pnpm: {
|
|
171
|
+
command: "pnpm",
|
|
172
|
+
args: ["root", "-g"]
|
|
173
|
+
},
|
|
174
|
+
npm: {
|
|
175
|
+
command: "npm",
|
|
176
|
+
args: ["root", "-g"]
|
|
177
|
+
},
|
|
178
|
+
yarn: {
|
|
179
|
+
command: "yarn",
|
|
180
|
+
args: ["global", "dir"]
|
|
181
|
+
},
|
|
182
|
+
bun: {
|
|
183
|
+
command: "bun",
|
|
184
|
+
args: ["pm", "bin"]
|
|
185
|
+
}
|
|
186
|
+
}[manager];
|
|
187
|
+
const result = spawnSync(command, args, { encoding: "utf8" });
|
|
188
|
+
if (result.status !== 0) return;
|
|
189
|
+
const root = result.stdout.trim();
|
|
190
|
+
return root ? realpathSafe(root) : void 0;
|
|
191
|
+
}
|
|
192
|
+
function detectGlobalManager(packageRoot) {
|
|
193
|
+
const normalizedRoot = realpathSafe(packageRoot);
|
|
194
|
+
if (normalizedRoot.includes(`${join("", ".pnpm")}`)) return "pnpm";
|
|
195
|
+
for (const manager of [
|
|
196
|
+
"pnpm",
|
|
197
|
+
"npm",
|
|
198
|
+
"yarn",
|
|
199
|
+
"bun"
|
|
200
|
+
]) {
|
|
201
|
+
const globalRoot = globalRootForManager(manager);
|
|
202
|
+
if (globalRoot && normalizedRoot.startsWith(globalRoot)) return manager;
|
|
203
|
+
}
|
|
204
|
+
if (process.env.PNPM_HOME) return "pnpm";
|
|
205
|
+
return detectPackageManager();
|
|
206
|
+
}
|
|
207
|
+
function detectCliInstall(packageRoot) {
|
|
208
|
+
const normalizedRoot = realpathSafe(packageRoot);
|
|
209
|
+
const projectRoot = findLocalProjectRoot(normalizedRoot);
|
|
210
|
+
if (projectRoot) return {
|
|
211
|
+
kind: "local",
|
|
212
|
+
packageManager: detectManagerFromLockfile(projectRoot) ?? detectPackageManager(),
|
|
213
|
+
packageRoot: normalizedRoot,
|
|
214
|
+
projectRoot
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
kind: "global",
|
|
218
|
+
packageManager: detectGlobalManager(normalizedRoot),
|
|
219
|
+
packageRoot: normalizedRoot
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/update/detect-release-age-block.ts
|
|
224
|
+
const RELEASE_AGE_PATTERNS = [
|
|
225
|
+
/minimumReleaseAge/i,
|
|
226
|
+
/minimum-release-age/i,
|
|
227
|
+
/ERR_PNPM_NO_MATURE_MATCHING_VERSION/,
|
|
228
|
+
/ERR_PNPM_MINIMUM_RELEASE_AGE/,
|
|
229
|
+
/does not meet the minimumReleaseAge constraint/
|
|
230
|
+
];
|
|
231
|
+
function isReleaseAgeBlock(output) {
|
|
232
|
+
return RELEASE_AGE_PATTERNS.some((pattern) => pattern.test(output));
|
|
233
|
+
}
|
|
234
|
+
function formatReleaseAgeBlockMessage(options) {
|
|
235
|
+
const retry = options.retryAfter !== void 0 ? ` Retry after ${options.retryAfter.toLocaleString()}.` : "";
|
|
236
|
+
return `@keystrokehq/cli ${options.availableVersion} is available but blocked by your minimum-release-age setting. You're on ${options.currentVersion}.${retry} Add @keystrokehq/cli to minimumReleaseAgeExclude to install immediately.`;
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/project/resolve-cli-root.ts
|
|
240
|
+
function isCliPackage(dir) {
|
|
241
|
+
const packageJsonPath = join(dir, "package.json");
|
|
242
|
+
if (!existsSync(packageJsonPath)) return false;
|
|
243
|
+
return JSON.parse(readFileSync(packageJsonPath, "utf8")).name === "@keystrokehq/cli";
|
|
244
|
+
}
|
|
245
|
+
function resolveCliRoot(fromModuleUrl) {
|
|
246
|
+
let dir = dirname(fileURLToPath(fromModuleUrl));
|
|
247
|
+
while (dir !== dirname(dir)) {
|
|
248
|
+
if (isCliPackage(dir)) return dir;
|
|
249
|
+
dir = dirname(dir);
|
|
250
|
+
}
|
|
251
|
+
throw new Error("Could not resolve keystroke CLI package root");
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
160
254
|
//#region src/version.ts
|
|
161
255
|
function readCliVersion() {
|
|
162
256
|
const packageJsonPath = join(resolveCliRoot(import.meta.url), "package.json");
|
|
163
257
|
return JSON.parse(readFileSync(packageJsonPath, "utf8")).version ?? "0.0.0";
|
|
164
258
|
}
|
|
165
259
|
//#endregion
|
|
166
|
-
export {
|
|
260
|
+
export { getPlatformUrl as _, detectCliInstall as a, DEFAULT_WEB_URL as b, detectPackageManager as c, resolveGithubPackagesToken as d, resolvePackageManager as f, getEffectiveApiTarget as g, getConfigDir as h, isReleaseAgeBlock as i, installDependencies as l, getCliConfigDir as m, resolveCliRoot as n, detectManagerFromLockfile as o, createCliConfig as p, formatReleaseAgeBlockMessage as r, buildPlaygroundWorkspace as s, readCliVersion as t, installPlaygroundDependencies as u, getWebUrl as v, resolvePlatformUrlForWebUrl as x, DEFAULT_PLATFORM_URL as y };
|
|
167
261
|
|
|
168
|
-
//# sourceMappingURL=version-
|
|
262
|
+
//# sourceMappingURL=version-key3f4tc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-key3f4tc.mjs","names":[],"sources":["../src/resolve-platform-url.ts","../src/config.ts","../src/init/package-manager.ts","../src/update/detect-cli-install.ts","../src/update/detect-release-age-block.ts","../src/project/resolve-cli-root.ts","../src/version.ts"],"sourcesContent":["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 {\n DEFAULT_PLATFORM_URL,\n DEFAULT_WEB_URL,\n knownPlatformUrlForWebUrl,\n resolvePlatformUrlForWebUrl,\n} from \"./resolve-platform-url\";\n\nexport type ApiTargetMode = \"local\" | \"platform\";\n\nexport type CliConfig = {\n webUrl: string;\n platformUrl: string;\n activeOrganizationId?: string;\n apiTarget?: ApiTargetMode;\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\nexport function getEffectiveApiTarget(config: Conf<CliConfig>): ApiTargetMode {\n const explicit = config.get(\"apiTarget\");\n if (explicit === \"local\" || explicit === \"platform\") {\n return explicit;\n }\n\n if (process.env.KEYSTROKE_API_KEY?.trim()) {\n return \"platform\";\n }\n\n return \"local\";\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 activeOrganizationId: {\n type: \"string\",\n },\n apiTarget: {\n type: \"string\",\n enum: [\"local\", \"platform\"],\n },\n },\n });\n\n syncPlatformUrlWithWebUrl(config);\n return config;\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 { 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, `keystroke dev` fails 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\nfunction realpathSafe(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\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 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","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"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keystrokehq/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/keystrokehq/keystroke.git",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"dist"
|
|
14
14
|
],
|
|
15
15
|
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
"./tsconfig.json": "./dist/configs/tsconfig.base.json"
|
|
18
|
+
},
|
|
16
19
|
"publishConfig": {
|
|
17
20
|
"access": "public",
|
|
18
21
|
"provenance": true,
|
|
@@ -24,30 +27,29 @@
|
|
|
24
27
|
"@inquirer/prompts": "^8.4.3",
|
|
25
28
|
"@napi-rs/keyring": "^1.3.0",
|
|
26
29
|
"@parcel/watcher": "^2.5.6",
|
|
30
|
+
"@types/node": "^25.9.1",
|
|
27
31
|
"better-sqlite3": "^12.10.0",
|
|
28
32
|
"commander": "^14.0.3",
|
|
29
33
|
"conf": "^15.1.0",
|
|
30
34
|
"drizzle-orm": "^0.45.2",
|
|
35
|
+
"oxlint": "^1.66.0",
|
|
31
36
|
"picomatch": "^4.0.4",
|
|
32
37
|
"tsdown": "^0.22.0",
|
|
33
|
-
"typescript": "^6.0.3"
|
|
38
|
+
"typescript": "^6.0.3",
|
|
39
|
+
"vitest": "^4.1.7"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^25.9.1",
|
|
37
42
|
"better-auth": "^1.6.11",
|
|
38
43
|
"es-module-lexer": "^2.1.0",
|
|
39
|
-
"oxlint": "^1.66.0",
|
|
40
44
|
"tsdown": "^0.22.0",
|
|
41
45
|
"tsx": "^4.22.3",
|
|
42
|
-
"vitest": "^4.1.7",
|
|
43
46
|
"@keystrokehq/oxlint-config": "0.0.4",
|
|
44
47
|
"@keystrokehq/tsconfig": "0.0.3",
|
|
45
48
|
"@keystrokehq/tsdown-config": "0.0.5",
|
|
46
49
|
"@keystrokehq/vitest-config": "0.0.7"
|
|
47
50
|
},
|
|
48
51
|
"scripts": {
|
|
49
|
-
"build": "
|
|
50
|
-
"generate:catalog-versions": "tsx scripts/generate-catalog-versions.ts",
|
|
52
|
+
"build": "tsdown && node scripts/copy-templates.mjs && node scripts/copy-skills-bundle.mjs && node scripts/copy-configs.mjs",
|
|
51
53
|
"pack:portable": "node scripts/pack.mjs",
|
|
52
54
|
"verify:package": "node scripts/verify-package.mjs",
|
|
53
55
|
"link-dev": "bash scripts/link-dev.sh",
|