@keystrokehq/cli 0.0.110 → 0.0.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,28 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { f as string, i as array, u as object } from "./schemas-DwAYtN9C.mjs";
2
+ import { a as array, d as object, p as string, t as entryIdFromFile } from "./discovery-XqwkB9H_-CQLSzR72.mjs";
3
3
  import { isBuiltin } from "node:module";
4
- import { basename, join, relative, sep } from "node:path";
4
+ import { basename, join, relative } from "node:path";
5
5
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
6
6
  import "node:url";
7
7
  import "node:fs/promises";
8
8
  import { build, mergeConfig } from "tsdown";
9
- //#region ../../packages/server/dist/discovery-XqwkB9H_.mjs
10
- const SOURCE_EXT = /\.(ts|mts|mjs|js)$/;
11
- const DECLARATION_FILE = /\.d\.(ts|mts|cts)$/;
12
- function entryIdFromFile(rootDir, filePath, options) {
13
- if (DECLARATION_FILE.test(filePath)) return null;
14
- const baseName = filePath.split(sep).at(-1) ?? "";
15
- if (/\.(int\.)?test\.(ts|mts)$/.test(baseName)) return null;
16
- const segments = relative(rootDir, filePath).replace(SOURCE_EXT, "").split(sep).filter((segment) => segment.length > 0);
17
- if (segments.length === 1) return segments[0] ?? null;
18
- const last = segments.at(-1);
19
- const entryNames = new Set([options.nestedEntry]);
20
- if (options.allowIndex !== false) entryNames.add("index");
21
- if (!last || !entryNames.has(last)) return null;
22
- const id = segments.slice(0, -1).join("/");
23
- return id.length > 0 ? id : null;
24
- }
25
- //#endregion
26
9
  //#region ../../packages/sandbox/dist/files-B0H9_dP4.mjs
27
10
  const SandboxFileContentSchema = object({
28
11
  path: string(),
@@ -311,4 +294,4 @@ async function watchApp(options = {}) {
311
294
  //#endregion
312
295
  export { buildApp, resolveRuntimeBuildArtifact, watchApp };
313
296
 
314
- //# sourceMappingURL=dist-D1ip549D.mjs.map
297
+ //# sourceMappingURL=dist-IKyaKHgU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dist-IKyaKHgU.mjs","names":["z.object","z.string","z.array"],"sources":["../../../packages/sandbox/dist/files-B0H9_dP4.mjs","../../../packages/tsdown-config/deps.js","../../../packages/tsdown-config/index.js","../../../packages/build/dist/index.mjs"],"sourcesContent":["import { basename, dirname, join, relative, resolve } from \"node:path\";\nimport { access, mkdir, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n//#region src/sandbox-dirs.ts\nfunction resolveSandboxRoot(workspacesRoot, scope) {\n\tif (scope.type === \"agent\") return join(workspacesRoot, scope.agentId);\n\treturn join(workspacesRoot, \"runs\", scope.runId);\n}\nasync function ensureWorkspaceDir(workspaceRoot) {\n\tawait mkdir(workspaceRoot, { recursive: true });\n}\n//#endregion\n//#region src/files/schemas.ts\nconst SandboxFileContentSchema = z.object({\n\tpath: z.string(),\n\tfile: z.string()\n});\n/** Destination path under `/workspace`. For `dir`, used as a prefix. */\nconst SandboxFileSchema = z.object({\n\tpath: z.string(),\n\t/** Single file body — typically from a build-time import (`import doc from \"./doc.md\"`). */\n\tfile: z.string().optional(),\n\t/** Directory contents — each path is relative to `path`. */\n\tdir: z.array(SandboxFileContentSchema).optional()\n});\nconst SandboxDefinitionSchema = z.object({\n\tkey: z.string().optional(),\n\tfiles: z.array(SandboxFileSchema).optional()\n});\n//#endregion\n//#region src/files/define-sandbox.ts\n/** Define a reusable sandbox file layout. */\nfunction defineSandbox(def) {\n\treturn def;\n}\n//#endregion\n//#region src/files/flatten-sandbox-files.ts\n/** Expand directory entries into flat `{ path, file }` records under `/workspace`. */\nfunction flattenSandboxFiles(files) {\n\tif (!files?.length) return [];\n\tconst flattened = [];\n\tfor (const entry of files) {\n\t\tif (entry.dir !== void 0) {\n\t\t\tconst prefix = entry.path.replace(/\\/+$/, \"\");\n\t\t\tfor (const child of entry.dir) flattened.push({\n\t\t\t\tpath: prefix ? `${prefix}/${child.path}` : child.path,\n\t\t\t\tfile: child.file\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.file === void 0) continue;\n\t\tflattened.push({\n\t\t\tpath: entry.path,\n\t\t\tfile: entry.file\n\t\t});\n\t}\n\treturn flattened;\n}\n//#endregion\n//#region src/files/materialize-sandbox.ts\nasync function materializeSandbox(options) {\n\tconst root = resolveSandboxRoot(options.workspacesRoot, options.scope);\n\tawait ensureWorkspaceDir(root);\n\tconst files = flattenSandboxFiles(options.definition?.files);\n\tif (files.length) await applySandboxFiles(root, files, options.scope);\n\treturn {\n\t\troot,\n\t\tscope: options.scope,\n\t\t...options.definition !== void 0 ? { definition: options.definition } : {}\n\t};\n}\nasync function applySandboxFiles(root, files, scope) {\n\tfor (const entry of files) {\n\t\tconst hostPath = resolve(root, entry.path);\n\t\tif (scope.type === \"agent\") try {\n\t\t\tawait access(hostPath);\n\t\t\tcontinue;\n\t\t} catch {}\n\t\tawait mkdir(dirname(hostPath), { recursive: true });\n\t\tawait writeFile(hostPath, entry.file);\n\t}\n}\n//#endregion\n//#region src/files/pack-dir.ts\nconst SKIP_DIRS$1 = new Set([\".git\", \"node_modules\"]);\n/** Recursively read a host file or directory into sandbox-relative `{ path, file }` entries. */\nfunction packDirFromDisk(rootPath) {\n\tconst stat = statSync(rootPath);\n\tif (stat.isFile()) return [{\n\t\tpath: basename(rootPath),\n\t\tfile: readFileSync(rootPath, \"utf8\")\n\t}];\n\tif (!stat.isDirectory()) throw new Error(`Expected a file or directory at ${rootPath}`);\n\tconst files = [];\n\twalkDir(rootPath, rootPath, files);\n\treturn files;\n}\nfunction walkDir(rootPath, currentPath, files) {\n\tfor (const name of readdirSync(currentPath)) {\n\t\tif (SKIP_DIRS$1.has(name)) continue;\n\t\tconst entryPath = join(currentPath, name);\n\t\tconst stat = statSync(entryPath);\n\t\tif (stat.isDirectory()) {\n\t\t\twalkDir(rootPath, entryPath, files);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!stat.isFile()) continue;\n\t\tfiles.push({\n\t\t\tpath: relative(rootPath, entryPath),\n\t\t\tfile: readFileSync(entryPath, \"utf8\")\n\t\t});\n\t}\n}\n//#endregion\n//#region src/files/pack-asset-dirs.ts\nconst SKIP_DIRS = new Set([\".git\", \"node_modules\"]);\n/** Pack every subdir under `src/skills/` and `src/files/` into an asset manifest. */\nfunction packAssetDirs(appRoot, srcDir = \"src\") {\n\treturn {\n\t\tskills: packAssetRoot(join(appRoot, srcDir, \"skills\")),\n\t\tfiles: packAssetRoot(join(appRoot, srcDir, \"files\"))\n\t};\n}\nfunction packAssetRoot(rootPath) {\n\tif (!statSync(rootPath, { throwIfNoEntry: false })?.isDirectory()) return {};\n\tconst manifest = {};\n\tfor (const name of readdirSync(rootPath)) {\n\t\tif (SKIP_DIRS.has(name)) continue;\n\t\tconst entryPath = join(rootPath, name);\n\t\tif (!statSync(entryPath).isDirectory()) continue;\n\t\tmanifest[name] = packDirFromDisk(entryPath);\n\t}\n\treturn manifest;\n}\n//#endregion\n//#region src/files/resolve-app-root.ts\n/** Walk upward from a module file to find the user app root (`src/agents`, `src/skills`, etc.). */\nfunction resolveAppRoot(fromPath) {\n\tlet dir = statSync(fromPath).isDirectory() ? fromPath : dirname(fromPath);\n\twhile (dir !== dirname(dir)) {\n\t\tif (hasAppLayout(dir)) return dir;\n\t\tdir = dirname(dir);\n\t}\n\treturn process.env.KEYSTROKE_ROOT ?? process.cwd();\n}\n/** Resolve app root from an agent (or other) module URL. */\nfunction resolveAppRootFromModule(moduleUrl) {\n\treturn resolveAppRoot(fileURLToPath(moduleUrl));\n}\nfunction hasAppLayout(dir) {\n\tconst src = join(dir, \"src\");\n\tconst dist = join(dir, \"dist\");\n\treturn existsSync(join(src, \"agents\")) || existsSync(join(src, \"skills\")) || existsSync(join(src, \"files\")) || existsSync(join(dist, \"agents\")) || existsSync(join(dist, \".keystroke\", \"assets.mjs\"));\n}\n//#endregion\nexport { materializeSandbox as a, SandboxDefinitionSchema as c, ensureWorkspaceDir as d, resolveSandboxRoot as f, packDirFromDisk as i, SandboxFileContentSchema as l, resolveAppRootFromModule as n, flattenSandboxFiles as o, packAssetDirs as r, defineSandbox as s, resolveAppRoot as t, SandboxFileSchema as u };\n\n//# sourceMappingURL=files-B0H9_dP4.mjs.map","import { isBuiltin } from \"node:module\";\n\n/** Native addons kept external — resolved from the CLI runtime at app start. */\nexport const NATIVE_RUNTIME_DEPS = [\"pg\", \"better-sqlite3\", \"@parcel/watcher\"];\n\n/** Kept external so .d.ts emission does not inline internal/inferred types. */\nexport const LIBRARY_EXTERNAL_DEPS = [\"better-auth\", /^@better-auth\\//, /^better-auth\\//];\n\n/** Non-auth runtime deps kept external for published entry bundles (platform, etc.). */\nexport const BUNDLED_ENTRY_RUNTIME_EXTERNAL_DEPS = [\n ...NATIVE_RUNTIME_DEPS,\n \"dockerode\",\n \"ssh2\",\n \"cpu-features\",\n \"microsandbox\",\n /^@aws-sdk\\//,\n /^@napi-rs\\//,\n \"hono\",\n /^@hono\\//,\n \"undici\",\n];\n\n/** Kept external when bundling published entry points (cli, keystroke, platform). */\nexport const BUNDLED_ENTRY_EXTERNAL_DEPS = [\n ...BUNDLED_ENTRY_RUNTIME_EXTERNAL_DEPS,\n ...LIBRARY_EXTERNAL_DEPS,\n];\n\n/** Bundle into CLI — device login uses Better Auth client only. */\nexport const BETTER_AUTH_CLIENT_BUNDLE_PATTERNS = [\n /^better-auth\\/client$/,\n /^better-auth\\/client\\//,\n];\n\n/** Server adapters and root entry — must not ship in the CLI bundle. */\nexport const BETTER_AUTH_SERVER_EXTERNAL_PATTERNS = [\n /^better-auth$/,\n /^@better-auth\\//,\n /^better-auth\\/(?!client(\\/|$)).+/,\n];\n\nexport function isNativeRuntimeDep(id) {\n return NATIVE_RUNTIME_DEPS.some((dep) => id === dep || id.startsWith(`${dep}/`));\n}\n\nfunction packageName(id) {\n if (id.startsWith(\"@\")) {\n const [scope, name] = id.split(\"/\");\n return name ? `${scope}/${name}` : id;\n }\n\n return id.split(\"/\")[0] ?? id;\n}\n\nfunction isRuntimeExternal(id) {\n const name = packageName(id);\n if (isNativeRuntimeDep(name)) {\n return true;\n }\n\n if (name.startsWith(\"@keystrokehq/\")) {\n return true;\n }\n\n if (name === \"better-auth\" || name.startsWith(\"@better-auth/\")) {\n return true;\n }\n\n return false;\n}\n\n/** Library packages resolve npm deps from node_modules at runtime. */\nexport function libraryBuildDepsConfig() {\n return {\n alwaysBundle: (_id) => null,\n neverBundle: [...NATIVE_RUNTIME_DEPS, /^@keystrokehq\\//, ...LIBRARY_EXTERNAL_DEPS],\n onlyBundle: false,\n };\n}\n\n/** Bundle npm deps into dist; resolve @keystrokehq/* and natives from the runtime. */\nexport function userAppBuildDepsConfig() {\n return {\n alwaysBundle: (id, _importer) => {\n if (id.startsWith(\".\") || id.startsWith(\"/\") || isBuiltin(id)) {\n return null;\n }\n\n return isRuntimeExternal(id) ? null : true;\n },\n neverBundle: [...NATIVE_RUNTIME_DEPS, /^@keystrokehq\\//, ...LIBRARY_EXTERNAL_DEPS],\n onlyBundle: false,\n };\n}\n\n/**\n * Published entry packages (cli, keystroke, platform): declare bundled @keystrokehq/*\n * in dependencies (changesets release graph ignores devDependencies). Inlining is\n * driven by alwaysBundle below, not by dep kind. Runtime npm deps stay external.\n */\nexport function bundledEntryDepsConfig() {\n return {\n alwaysBundle: [/^@keystrokehq\\//],\n neverBundle: [...BUNDLED_ENTRY_EXTERNAL_DEPS],\n onlyBundle: false,\n };\n}\n\n/** Published CLI: inline better-auth client + @better-auth/*; keep server adapters external. */\nexport const BETTER_AUTH_CLI_BUNDLE_PATTERNS = [\n ...BETTER_AUTH_CLIENT_BUNDLE_PATTERNS,\n /^@better-auth\\//,\n];\n\nexport const BETTER_AUTH_CLI_EXTERNAL_PATTERNS = [\n /^better-auth$/,\n /^better-auth\\/(?!client(\\/|$)).+/,\n /^@better-auth\\/drizzle-adapter/,\n \"drizzle-orm\",\n];\n\n/** Rolldown/tsdown — native bindings; stay in CLI node_modules at runtime. */\nexport const CLI_BUILD_TOOL_EXTERNAL_PATTERNS = [\"tsdown\", \"rolldown\", /^@rolldown\\//, \"tsx\"];\n\n/** CLI: inline better-auth/client; keep server + drizzle adapters external. */\nexport function cliBundledEntryDepsConfig() {\n return {\n alwaysBundle: [/^@keystrokehq\\//, ...BETTER_AUTH_CLI_BUNDLE_PATTERNS],\n neverBundle: [\n ...BUNDLED_ENTRY_RUNTIME_EXTERNAL_DEPS,\n ...BETTER_AUTH_CLI_EXTERNAL_PATTERNS,\n ...CLI_BUILD_TOOL_EXTERNAL_PATTERNS,\n ],\n onlyBundle: false,\n };\n}\n","import { bundledEntryDepsConfig, libraryBuildDepsConfig, userAppBuildDepsConfig } from \"./deps.js\";\n\nexport {\n isNativeRuntimeDep,\n NATIVE_RUNTIME_DEPS,\n bundledEntryDepsConfig,\n cliBundledEntryDepsConfig,\n libraryBuildDepsConfig,\n userAppBuildDepsConfig,\n} from \"./deps.js\";\n\nexport const baseTsdownConfig = {\n format: [\"esm\", \"cjs\"],\n dts: true,\n clean: true,\n sourcemap: true,\n target: \"node20\",\n deps: libraryBuildDepsConfig(),\n};\n\n/** tsdown config for published bundles (cli, keystroke, platform). */\nexport const bundledEntryTsdownConfig = {\n format: [\"esm\", \"cjs\"],\n dts: true,\n clean: true,\n sourcemap: true,\n target: \"node20\",\n deps: bundledEntryDepsConfig(),\n};\n","import { t as resolveModuleDirs } from \"./resolve-module-dirs-BPHQhRGy.mjs\";\nimport { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { entryIdFromFile } from \"@keystrokehq/server/discovery\";\nimport { packAssetDirs } from \"@keystrokehq/sandbox/files\";\nimport { baseTsdownConfig, userAppBuildDepsConfig } from \"@keystrokehq/tsdown-config\";\nimport { build, mergeConfig } from \"tsdown\";\n//#region src/resolve-runtime-artifact.ts\nfunction packageRoot(nodeModules, scope, name) {\n\tconst direct = scope ? join(nodeModules, scope, name) : join(nodeModules, name);\n\tif (existsSync(join(direct, \"package.json\"))) return direct;\n\tconst pnpmDir = join(nodeModules, \".pnpm\");\n\tif (!existsSync(pnpmDir)) return null;\n\tfor (const entry of readdirSync(pnpmDir)) {\n\t\tconst candidate = scope ? join(pnpmDir, entry, \"node_modules\", scope, name) : join(pnpmDir, entry, \"node_modules\", name);\n\t\tif (existsSync(join(candidate, \"package.json\"))) return candidate;\n\t}\n\treturn null;\n}\n/** Resolve a file shipped with `@keystrokehq/build` inside the CLI runtime. */\nfunction resolveRuntimeBuildArtifact(runtimeNodeModules, relativePath) {\n\tconst pkgRoot = packageRoot(runtimeNodeModules, \"@keystrokehq\", \"build\");\n\tif (!pkgRoot) throw new Error(\"Keystroke runtime is missing @keystrokehq/build\");\n\tconst artifact = join(pkgRoot, relativePath);\n\tif (!existsSync(artifact)) throw new Error(`Keystroke runtime artifact not found: ${relativePath}`);\n\treturn artifact;\n}\n//#endregion\n//#region src/vitest-plugin.ts\nconst ASSETS_MODULE$1 = \"@keystrokehq/assets\";\nconst virtualPrefix = `\\0${ASSETS_MODULE$1}:`;\n/** Vitest plugin resolving `@keystrokehq/assets` from disk. */\nfunction agentAssetsVitestPlugin(appRoot = process.cwd()) {\n\tlet manifest = null;\n\treturn {\n\t\tname: \"keystroke-assets\",\n\t\tresolveId(source) {\n\t\t\tif (source !== ASSETS_MODULE$1) return null;\n\t\t\treturn `${virtualPrefix}manifest`;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== `${virtualPrefix}manifest`) return null;\n\t\t\tmanifest ??= packAssetDirs(appRoot);\n\t\t\treturn `export default ${JSON.stringify(manifest)};`;\n\t\t}\n\t};\n}\nfunction findAppRootFromConfig(configFile) {\n\tif (!configFile) return process.cwd();\n\treturn dirname(configFile);\n}\nfunction agentAssetsVitestPluginFromConfig(configFile) {\n\treturn agentAssetsVitestPlugin(findAppRootFromConfig(configFile));\n}\n//#endregion\n//#region src/index.ts\nconst ASSETS_MODULE = \"@keystrokehq/assets\";\nconst VIRTUAL_PREFIX = `\\0${ASSETS_MODULE}:`;\nfunction walkTypeScriptSources(dir) {\n\tif (!existsSync(dir)) return [];\n\tconst files = [];\n\tfor (const name of readdirSync(dir)) {\n\t\tconst path = join(dir, name);\n\t\tif (statSync(path).isDirectory()) files.push(...walkTypeScriptSources(path));\n\t\telse if (/\\.(ts|mts)$/.test(name) && !/\\.(int\\.)?test\\.(ts|mts)$/.test(name)) files.push(path);\n\t}\n\treturn files;\n}\nfunction discoveredEntries(dir, nestedEntry, prefix) {\n\tconst entries = {};\n\tfor (const filePath of walkTypeScriptSources(dir)) {\n\t\tconst id = entryIdFromFile(dir, filePath, { nestedEntry });\n\t\tif (!id) continue;\n\t\tentries[`${prefix}/${id}`] = filePath;\n\t}\n\treturn entries;\n}\nfunction renderAssetModule(manifest) {\n\treturn `export default ${JSON.stringify(manifest)};\\n`;\n}\n/** Rolldown plugin: pack src/skills + src/files and expose `@keystrokehq/assets`. */\nfunction agentAssetsPlugin(options) {\n\tconst srcDir = options.srcDir ?? \"src\";\n\tlet manifest = null;\n\treturn {\n\t\tname: \"keystroke-assets\",\n\t\tbuildStart() {\n\t\t\tmanifest = packAssetDirs(options.root, srcDir);\n\t\t},\n\t\tresolveId(source) {\n\t\t\tif (source !== ASSETS_MODULE) return null;\n\t\t\treturn `${VIRTUAL_PREFIX}manifest`;\n\t\t},\n\t\tload(id) {\n\t\t\tif (id !== `${VIRTUAL_PREFIX}manifest` || !manifest) return null;\n\t\t\treturn renderAssetModule(manifest);\n\t\t},\n\t\tbuildEnd() {\n\t\t\tif (!manifest) return;\n\t\t\tconst outDir = join(options.root, options.outDir ?? \"dist\", \".keystroke\");\n\t\t\tmkdirSync(outDir, { recursive: true });\n\t\t\twriteFileSync(join(outDir, \"assets.mjs\"), renderAssetModule(manifest));\n\t\t}\n\t};\n}\n/** Full tsdown config for user keystroke apps (config + discovered modules only). */\nfunction createAppBuildConfig(options = {}) {\n\tconst root = options.root ?? process.cwd();\n\tconst srcDir = options.srcDir ?? \"src\";\n\tconst outDir = options.outDir ?? \"dist\";\n\tconst srcRoot = join(root, srcDir);\n\tconst configEntry = existsSync(join(root, \"keystroke.config.ts\")) ? join(root, \"keystroke.config.ts\") : void 0;\n\tconst entries = {\n\t\t...configEntry ? { config: configEntry } : {},\n\t\t...discoveredEntries(join(srcRoot, \"agents\"), \"agent\", \"agents\"),\n\t\t...discoveredEntries(join(srcRoot, \"workflows\"), \"workflow\", \"workflows\"),\n\t\t...discoveredEntries(join(srcRoot, \"triggers\"), \"trigger\", \"triggers\")\n\t};\n\tif (Object.keys(entries).length === 0) throw new Error(\"Nothing to build — add keystroke.config.ts or modules under src/\");\n\treturn mergeConfig(baseTsdownConfig, {\n\t\tentry: entries,\n\t\tformat: [\"esm\"],\n\t\toutDir: join(root, outDir),\n\t\tclean: options.clean ?? true,\n\t\tdts: false,\n\t\tloader: { \".md\": \"text\" },\n\t\tdeps: userAppBuildDepsConfig(),\n\t\tplugins: [agentAssetsPlugin({\n\t\t\troot,\n\t\t\tsrcDir,\n\t\t\toutDir\n\t\t})]\n\t});\n}\n/** Build user agents, workflows, triggers, and config to `dist/`. */\nasync function buildApp(options = {}) {\n\tconst root = options.root ?? process.cwd();\n\tconst previous = process.cwd();\n\ttry {\n\t\tprocess.chdir(root);\n\t\tawait build(createAppBuildConfig({\n\t\t\t...options,\n\t\t\troot\n\t\t}));\n\t} finally {\n\t\tprocess.chdir(previous);\n\t}\n}\n/**\n* Watch `src/` and rebuild `dist/` on change.\n*\n* NOT true hot reload — each change runs a full tsdown rebuild. Callers should\n* restart the API server in `onRebuild` so discovery picks up new/changed modules.\n*/\nasync function watchApp(options = {}) {\n\tconst { runWatchApp } = await import(\"./watch-app-DTIeKrbl.mjs\");\n\tawait runWatchApp(buildApp, options);\n}\n//#endregion\nexport { agentAssetsVitestPlugin, agentAssetsVitestPluginFromConfig, buildApp, createAppBuildConfig, resolveModuleDirs, resolveRuntimeBuildArtifact, watchApp };\n\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { a as boolean, c as literal, d as record, f as string, h as _coercedNumber, i as array, l as number$1, m as unknown, n as ZodType, o as custom, p as union, r as _enum, s as discriminatedUnion, t as ZodNumber, u as object } from "./schemas-DwAYtN9C.mjs";
2
+ import { a as array, c as discriminatedUnion, d as object, f as record, g as _coercedNumber, h as unknown, i as _enum, l as literal, m as union, n as ZodNumber, o as boolean, p as string, r as ZodType, s as custom, t as entryIdFromFile, u as number$1 } from "./discovery-XqwkB9H_-CQLSzR72.mjs";
3
3
  import { a as installPlaygroundDependencies, i as installDependencies, n as buildPlaygroundWorkspace, o as resolvePackageManager, s as resolveCliRoot, t as readCliVersion } from "./version-DESgLEkE.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { Command } from "commander";
6
6
  import { Entry } from "@napi-rs/keyring";
7
7
  import Conf from "conf";
8
8
  import { homedir, platform, tmpdir } from "node:os";
9
- import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
9
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
10
  import { confirm, input, select } from "@inquirer/prompts";
11
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
11
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
12
12
  import { spawn, spawnSync } from "node:child_process";
13
+ import { createHash } from "node:crypto";
13
14
  import { pathToFileURL } from "node:url";
14
15
  import { access, copyFile, cp, mkdir, readFile, readdir, stat, symlink, unlink, writeFile } from "node:fs/promises";
15
16
  //#region ../../node_modules/.pnpm/ky@2.0.2/node_modules/ky/distribution/errors/KyError.js
@@ -2063,13 +2064,13 @@ const OrganizationUserRoleSchema = _enum([
2063
2064
  "admin",
2064
2065
  "builder"
2065
2066
  ]);
2066
- const isoDateTime$2 = string().datetime();
2067
+ const isoDateTime$3 = string().datetime();
2067
2068
  const OrganizationSchema = object({
2068
2069
  id: string(),
2069
2070
  name: string(),
2070
2071
  slug: OrganizationSlugSchema,
2071
- createdAt: isoDateTime$2,
2072
- updatedAt: isoDateTime$2
2072
+ createdAt: isoDateTime$3,
2073
+ updatedAt: isoDateTime$3
2073
2074
  });
2074
2075
  const ProjectSchema = object({
2075
2076
  id: string(),
@@ -2080,8 +2081,8 @@ const ProjectSchema = object({
2080
2081
  baseUrl: string().nullable(),
2081
2082
  runtimeId: string().nullable(),
2082
2083
  lastError: string().nullable(),
2083
- createdAt: isoDateTime$2,
2084
- updatedAt: isoDateTime$2
2084
+ createdAt: isoDateTime$3,
2085
+ updatedAt: isoDateTime$3
2085
2086
  });
2086
2087
  const UserOrganizationSchema = object({
2087
2088
  organization: OrganizationSchema,
@@ -2561,7 +2562,7 @@ const OrganizationMemberStatusSchema = _enum([
2561
2562
  "suspended"
2562
2563
  ]);
2563
2564
  const InvitableOrganizationMemberRoleSchema = _enum(["admin", "builder"]);
2564
- const isoDateTime$1 = string().datetime();
2565
+ const isoDateTime$2 = string().datetime();
2565
2566
  const OrganizationMemberSchema = object({
2566
2567
  id: string(),
2567
2568
  name: string(),
@@ -2569,8 +2570,8 @@ const OrganizationMemberSchema = object({
2569
2570
  role: OrganizationMemberRoleSchema,
2570
2571
  status: OrganizationMemberStatusSchema,
2571
2572
  avatarUrl: string().optional(),
2572
- joinedAt: isoDateTime$1.optional(),
2573
- lastActiveAt: isoDateTime$1.nullable().optional()
2573
+ joinedAt: isoDateTime$2.optional(),
2574
+ lastActiveAt: isoDateTime$2.nullable().optional()
2574
2575
  });
2575
2576
  const ListOrganizationMembersResponseSchema = object({ members: array(OrganizationMemberSchema) });
2576
2577
  const InviteOrganizationMembersRequestSchema = object({
@@ -2601,14 +2602,14 @@ const ListOrganizationInvitationsResponseSchema = object({ invitations: array(ob
2601
2602
  const AcceptOrganizationInvitationResponseSchema = object({ organization: UserOrganizationSchema });
2602
2603
  const DeclineOrganizationInvitationResponseSchema = object({ success: literal(true) });
2603
2604
  const ProjectArtifactStatusSchema = _enum(["pending", "ready"]);
2604
- const isoDateTime = string().datetime();
2605
+ const isoDateTime$1 = string().datetime();
2605
2606
  const ProjectArtifactSchema = object({
2606
2607
  id: string(),
2607
2608
  projectId: string(),
2608
2609
  storageKey: string(),
2609
2610
  status: ProjectArtifactStatusSchema,
2610
- createdAt: isoDateTime,
2611
- updatedAt: isoDateTime
2611
+ createdAt: isoDateTime$1,
2612
+ updatedAt: isoDateTime$1
2612
2613
  });
2613
2614
  const CreateProjectArtifactResponseSchema = object({
2614
2615
  artifact: ProjectArtifactSchema,
@@ -2619,6 +2620,76 @@ const CompleteProjectArtifactResponseSchema = object({
2619
2620
  artifact: ProjectArtifactSchema,
2620
2621
  project: ProjectSchema
2621
2622
  });
2623
+ const isoDateTime = string().datetime();
2624
+ const sha256Hex = string().regex(/^[a-f0-9]{64}$/, "Expected a lowercase hex sha256 digest");
2625
+ /**
2626
+ * A single entry in a deploy's file tree, as served to the dashboard. Carries
2627
+ * no contents: `id` is a stable, path-derived handle used for selection and
2628
+ * `?file=` deep links; `hash` is the content address used to fetch the bytes
2629
+ * lazily and to cache them immutably in the browser.
2630
+ */
2631
+ const ProjectFileSummarySchema = object({
2632
+ id: string(),
2633
+ path: string().min(1),
2634
+ hash: sha256Hex
2635
+ });
2636
+ /** Maps a runtime resource key (agent/workflow/skill) to its source file path. */
2637
+ const ProjectSourceResourceRefSchema = object({
2638
+ key: string().min(1),
2639
+ path: string().min(1)
2640
+ });
2641
+ const ProjectSourceResourcesSchema = object({
2642
+ agents: array(ProjectSourceResourceRefSchema).default([]),
2643
+ workflows: array(ProjectSourceResourceRefSchema).default([]),
2644
+ skills: array(ProjectSourceResourceRefSchema).default([])
2645
+ });
2646
+ const emptyResources = () => ({
2647
+ agents: [],
2648
+ workflows: [],
2649
+ skills: []
2650
+ });
2651
+ /** Response for the file-tree listing of a project's active deploy. */
2652
+ const ListProjectFilesResponseSchema = object({
2653
+ artifactId: string().nullable(),
2654
+ files: array(ProjectFileSummarySchema),
2655
+ resources: ProjectSourceResourcesSchema.default(emptyResources())
2656
+ });
2657
+ object({
2658
+ path: string().min(1),
2659
+ contents: string(),
2660
+ hash: sha256Hex
2661
+ });
2662
+ /** Request: ask the platform which blobs are missing and get presigned PUTs. */
2663
+ const PresignProjectSourceRequestSchema = object({ blobs: array(object({
2664
+ hash: sha256Hex,
2665
+ size: number$1().int().nonnegative()
2666
+ })) });
2667
+ /** Response: presigned PUTs for the subset of blobs not already stored. */
2668
+ const PresignProjectSourceResponseSchema = object({ uploads: array(object({
2669
+ hash: sha256Hex,
2670
+ url: string().url()
2671
+ })) });
2672
+ /** Request body the CLI sends to finalize a deploy's source manifest. */
2673
+ const UploadProjectSourceManifestRequestSchema = object({
2674
+ files: array(object({
2675
+ path: string().min(1),
2676
+ hash: sha256Hex
2677
+ })),
2678
+ resources: ProjectSourceResourcesSchema.default(emptyResources())
2679
+ });
2680
+ const UploadProjectSourceResponseSchema = object({
2681
+ ok: literal(true),
2682
+ fileCount: number$1().int().nonnegative()
2683
+ });
2684
+ object({
2685
+ files: array(object({
2686
+ id: string(),
2687
+ path: string().min(1),
2688
+ hash: sha256Hex
2689
+ })),
2690
+ resources: ProjectSourceResourcesSchema.default(emptyResources()),
2691
+ generatedAt: isoDateTime
2692
+ });
2622
2693
  //#endregion
2623
2694
  //#region ../../packages/sdk/dist/index.mjs
2624
2695
  function normalizeBaseUrl$1(baseUrl) {
@@ -3123,6 +3194,26 @@ function createArtifactsResource(http) {
3123
3194
  } catch (error) {
3124
3195
  throw await toPlatformError(error);
3125
3196
  }
3197
+ },
3198
+ /** Ask which source blobs are missing and get presigned PUTs for them. */
3199
+ async presignSource(projectId, artifactId, blobs) {
3200
+ const body = PresignProjectSourceRequestSchema.parse({ blobs });
3201
+ try {
3202
+ const data = await http.post(`/api/projects/${encodeURIComponent(projectId)}/artifacts/${encodeURIComponent(artifactId)}/source/presign`, { json: body }).json();
3203
+ return PresignProjectSourceResponseSchema.parse(data);
3204
+ } catch (error) {
3205
+ throw await toPlatformError(error);
3206
+ }
3207
+ },
3208
+ /** Finalize the deploy's source manifest (path -> hash tree + resource refs). */
3209
+ async uploadManifest(projectId, artifactId, manifest) {
3210
+ const body = UploadProjectSourceManifestRequestSchema.parse(manifest);
3211
+ try {
3212
+ const data = await http.post(`/api/projects/${encodeURIComponent(projectId)}/artifacts/${encodeURIComponent(artifactId)}/source/manifest`, { json: body }).json();
3213
+ return UploadProjectSourceResponseSchema.parse(data);
3214
+ } catch (error) {
3215
+ throw await toPlatformError(error);
3216
+ }
3126
3217
  }
3127
3218
  };
3128
3219
  }
@@ -3148,6 +3239,27 @@ function createGatewayAttachmentsResource(http) {
3148
3239
  }
3149
3240
  };
3150
3241
  }
3242
+ function createProjectFilesResource(http) {
3243
+ return {
3244
+ /** File tree (no contents) for a project's active deploy; empty before first deploy. */
3245
+ async listForProject(projectId) {
3246
+ try {
3247
+ const data = await http.get(`/api/projects/${encodeURIComponent(projectId)}/files`).json();
3248
+ return ListProjectFilesResponseSchema.parse(data);
3249
+ } catch (error) {
3250
+ throw await toPlatformError(error);
3251
+ }
3252
+ },
3253
+ /** Lazily fetch a single file's contents by path for a specific deploy. */
3254
+ async getContent(projectId, artifactId, path) {
3255
+ try {
3256
+ return await http.get(`/api/projects/${encodeURIComponent(projectId)}/artifacts/${encodeURIComponent(artifactId)}/files/content`, { searchParams: { path } }).text();
3257
+ } catch (error) {
3258
+ throw await toPlatformError(error);
3259
+ }
3260
+ }
3261
+ };
3262
+ }
3151
3263
  function createHealthResource(http) {
3152
3264
  return { async check() {
3153
3265
  try {
@@ -4863,141 +4975,6 @@ const deploymentSeed = [{
4863
4975
  createdAt: "2026-04-18T15:44:00Z",
4864
4976
  active: false
4865
4977
  }];
4866
- const projectFileSeed = [
4867
- {
4868
- id: "file-agents-support-agent",
4869
- projectId: "project_personal_repo",
4870
- path: "src/agents/support-agent.ts",
4871
- language: "typescript",
4872
- updatedAt: "2026-04-20T15:44:00Z",
4873
- contents: `import { defineAgent } from "@keystrokehq/agent";
4874
-
4875
- export const supportAgent = defineAgent({
4876
- key: "support-agent",
4877
- name: "Support Agent",
4878
- model: "claude-4-sonnet",
4879
- instructions: "Triage support requests and draft helpful replies.",
4880
- });
4881
- `
4882
- },
4883
- {
4884
- id: "file-workflows-daily-digest",
4885
- projectId: "project_personal_repo",
4886
- path: "src/workflows/daily-digest.ts",
4887
- language: "typescript",
4888
- updatedAt: "2026-04-20T15:12:00Z",
4889
- contents: `import { defineWorkflow } from "@keystrokehq/workflow";
4890
-
4891
- export const dailyDigest = defineWorkflow({
4892
- key: "daily-digest",
4893
- name: "Daily Digest",
4894
- trigger: "schedule.daily",
4895
- async run(ctx) {
4896
- await ctx.step("collect-updates");
4897
- await ctx.step("send-summary");
4898
- },
4899
- });
4900
- `
4901
- },
4902
- {
4903
- id: "file-skills-attio-sync",
4904
- projectId: "project_personal_repo",
4905
- path: ".agents/skills/attio-sync/SKILL.md",
4906
- language: "markdown",
4907
- updatedAt: "2026-04-19T18:05:00Z",
4908
- contents: `# Attio Sync
4909
-
4910
- Use this skill when a workflow needs to enrich company or person records from Attio.
4911
-
4912
- ## Inputs
4913
-
4914
- - company domain
4915
- - contact email
4916
- - enrichment depth
4917
-
4918
- ## Output
4919
-
4920
- Normalized CRM metadata ready for downstream workflow steps.
4921
- `
4922
- },
4923
- {
4924
- id: "file-skills-doc-coauthor",
4925
- projectId: "project_personal_repo",
4926
- path: ".agents/skills/doc-coauthor/SKILL.md",
4927
- language: "markdown",
4928
- updatedAt: "2026-05-30T10:24:00Z",
4929
- contents: `# Doc Co-author
4930
-
4931
- Use this skill when a workflow needs to collaboratively write, edit, and track
4932
- versions of a document.
4933
-
4934
- ## Inputs
4935
-
4936
- - document title
4937
- - source outline or draft
4938
- - target audience
4939
-
4940
- ## Output
4941
-
4942
- A revised document with tracked changes and a short summary of edits.
4943
- `
4944
- },
4945
- {
4946
- id: "file-skills-doc-coauthor-reference",
4947
- projectId: "project_personal_repo",
4948
- path: ".agents/skills/doc-coauthor/reference.md",
4949
- language: "markdown",
4950
- updatedAt: "2026-05-30T10:24:00Z",
4951
- contents: `# Reference
4952
-
4953
- Style and structure guidance the co-author follows when editing docs.
4954
-
4955
- ## Voice
4956
-
4957
- - Prefer active voice and short sentences.
4958
- - Keep headings consistent with the existing document.
4959
-
4960
- ## Versioning
4961
-
4962
- - Summarize each revision pass.
4963
- - Never drop the reader's original intent.
4964
- `
4965
- },
4966
- {
4967
- id: "file-skills-doc-coauthor-template",
4968
- projectId: "project_personal_repo",
4969
- path: ".agents/skills/doc-coauthor/templates/outline.md",
4970
- language: "markdown",
4971
- updatedAt: "2026-05-30T10:24:00Z",
4972
- contents: `# {{title}}
4973
-
4974
- ## Summary
4975
-
4976
- ## Background
4977
-
4978
- ## Details
4979
-
4980
- ## Next steps
4981
- `
4982
- },
4983
- {
4984
- id: "file-actions-send-slack",
4985
- projectId: "project_personal_repo",
4986
- path: "src/actions/send-slack-message.ts",
4987
- language: "typescript",
4988
- updatedAt: "2026-04-18T12:30:00Z",
4989
- contents: `import { defineAction } from "@keystrokehq/action";
4990
-
4991
- export const sendSlackMessage = defineAction({
4992
- key: "send-slack-message",
4993
- name: "Send Slack message",
4994
- async run({ channel, text }) {
4995
- return { channel, text, sent: true };
4996
- },
4997
- });
4998
- `
4999
- }
5000
- ];
5001
4978
  /** Review personas for mocked project members and API key authors (not org membership). */
5002
4979
  const mockPersonaSeed = [
5003
4980
  {
@@ -5316,16 +5293,6 @@ function createProjectMetricsResource() {
5316
5293
  plannedEndpoint: "GET /api/projects/metrics"
5317
5294
  }, async () => ({})) };
5318
5295
  }
5319
- function createProjectFilesResource() {
5320
- return { listForProject: mock({
5321
- domain: "projectFiles",
5322
- method: "listForProject",
5323
- plannedEndpoint: "GET /api/projects/:id/files"
5324
- }, async (projectId) => projectFileSeed.map((file) => ({
5325
- ...file,
5326
- projectId
5327
- }))) };
5328
- }
5329
5296
  const projectMembersStore = /* @__PURE__ */ new Map();
5330
5297
  function getProjectMembers(projectId) {
5331
5298
  let members = projectMembersStore.get(projectId);
@@ -6941,7 +6908,7 @@ function createPlatformClient(options) {
6941
6908
  deployments: createDeploymentsResource(),
6942
6909
  gatewayAttachments: createGatewayAttachmentsResource(http),
6943
6910
  projectMetrics: createProjectMetricsResource(),
6944
- projectFiles: createProjectFilesResource(),
6911
+ projectFiles: createProjectFilesResource(http),
6945
6912
  channels: createChannelsResource(),
6946
6913
  members: createMembersResource(http),
6947
6914
  invitations: createInvitationsResource(http),
@@ -9278,7 +9245,7 @@ function registerBuildCommand(program) {
9278
9245
  program.command("build").description("Build the keystroke project for production").option("--dir <path>", "Project directory", process.cwd()).action(async (options) => {
9279
9246
  try {
9280
9247
  const root = resolveProjectRoot(options.dir);
9281
- const { buildApp } = await import("./dist-D1ip549D.mjs");
9248
+ const { buildApp } = await import("./dist-IKyaKHgU.mjs");
9282
9249
  await buildApp({ root });
9283
9250
  process.stdout.write(`Built ${root}\n`);
9284
9251
  } catch (error) {
@@ -9321,9 +9288,140 @@ function packProjectArtifact(projectRoot) {
9321
9288
  }
9322
9289
  }
9323
9290
  //#endregion
9291
+ //#region src/project/collect-source.ts
9292
+ const IGNORED_DIRS = new Set([
9293
+ "node_modules",
9294
+ "dist",
9295
+ ".git",
9296
+ ".turbo",
9297
+ "build",
9298
+ "coverage",
9299
+ ".keystroke",
9300
+ ".cache",
9301
+ "tmp"
9302
+ ]);
9303
+ const MAX_FILE_BYTES = 256 * 1024;
9304
+ const MAX_TOTAL_BYTES = 8 * 1024 * 1024;
9305
+ const MAX_FILES = 2e3;
9306
+ function toPosix(path) {
9307
+ return path.split(sep).join("/");
9308
+ }
9309
+ function isProbablyBinary(buffer) {
9310
+ const sampleLength = Math.min(buffer.length, 8e3);
9311
+ for (let index = 0; index < sampleLength; index += 1) if (buffer[index] === 0) return true;
9312
+ return false;
9313
+ }
9314
+ function walkFiles(root, dir, out, totals) {
9315
+ for (const name of readdirSync(dir).sort()) {
9316
+ if (out.length >= MAX_FILES || totals.bytes >= MAX_TOTAL_BYTES) return;
9317
+ const absolute = join(dir, name);
9318
+ const stats = statSync(absolute);
9319
+ if (stats.isDirectory()) {
9320
+ if (IGNORED_DIRS.has(name)) continue;
9321
+ walkFiles(root, absolute, out, totals);
9322
+ continue;
9323
+ }
9324
+ if (!stats.isFile() || stats.size > MAX_FILE_BYTES) continue;
9325
+ const buffer = readFileSync(absolute);
9326
+ if (isProbablyBinary(buffer)) continue;
9327
+ totals.bytes += buffer.byteLength;
9328
+ out.push({
9329
+ path: toPosix(relative(root, absolute)),
9330
+ contents: buffer.toString("utf8"),
9331
+ hash: createHash("sha256").update(buffer).digest("hex")
9332
+ });
9333
+ }
9334
+ }
9335
+ function posixPrefix(root, srcRoot, subdir) {
9336
+ return `${toPosix(relative(root, join(srcRoot, subdir)))}/`;
9337
+ }
9338
+ function collectModuleRefs(srcRoot, root, files, subdir, nestedEntry) {
9339
+ const dir = join(srcRoot, subdir);
9340
+ const prefix = posixPrefix(root, srcRoot, subdir);
9341
+ const refs = [];
9342
+ for (const file of files) {
9343
+ if (!file.path.startsWith(prefix) || !/\.(ts|mts)$/.test(file.path)) continue;
9344
+ const key = entryIdFromFile(dir, join(root, file.path), { nestedEntry });
9345
+ if (key) refs.push({
9346
+ key,
9347
+ path: file.path
9348
+ });
9349
+ }
9350
+ return refs;
9351
+ }
9352
+ function collectSkillRefs(srcRoot, root, files) {
9353
+ const dir = join(srcRoot, "skills");
9354
+ const prefix = posixPrefix(root, srcRoot, "skills");
9355
+ const refs = [];
9356
+ for (const file of files) {
9357
+ if (!file.path.startsWith(prefix) || !file.path.endsWith("SKILL.md")) continue;
9358
+ const key = toPosix(relative(dir, join(root, file.path)).replace(/\/?SKILL\.md$/, ""));
9359
+ if (key) refs.push({
9360
+ key,
9361
+ path: file.path
9362
+ });
9363
+ }
9364
+ return refs;
9365
+ }
9366
+ /**
9367
+ * Collect the project's actual source code into a deploy snapshot: every text
9368
+ * file under the project root (minus build output / deps) plus a map of each
9369
+ * agent / workflow / skill key to its source path. The map mirrors the same
9370
+ * path-derived ids the build step uses.
9371
+ */
9372
+ function collectProjectSource(root) {
9373
+ const files = [];
9374
+ walkFiles(root, root, files, { bytes: 0 });
9375
+ const srcRoot = join(root, "src");
9376
+ return {
9377
+ files,
9378
+ resources: {
9379
+ agents: collectModuleRefs(srcRoot, root, files, "agents", "agent"),
9380
+ workflows: collectModuleRefs(srcRoot, root, files, "workflows", "workflow"),
9381
+ skills: collectSkillRefs(srcRoot, root, files)
9382
+ }
9383
+ };
9384
+ }
9385
+ //#endregion
9324
9386
  //#region src/commands/deploy.ts
9325
9387
  const POLL_INTERVAL_MS = 2e3;
9326
9388
  const DEPLOY_TIMEOUT_MS = 12e4;
9389
+ const SOURCE_BLOB_CONTENT_TYPE = "text/plain; charset=utf-8";
9390
+ /**
9391
+ * Upload the project's source so the dashboard can render a read-only file
9392
+ * view. Content-addressed: only blobs the platform reports missing are PUT, so
9393
+ * unchanged files are never re-uploaded. The manifest (path -> hash) is sent
9394
+ * last and marks the snapshot complete.
9395
+ */
9396
+ async function uploadProjectSourceSnapshot(client, projectId, artifactId, source) {
9397
+ const contentsByHash = /* @__PURE__ */ new Map();
9398
+ const blobsByHash = /* @__PURE__ */ new Map();
9399
+ for (const file of source.files) {
9400
+ contentsByHash.set(file.hash, file.contents);
9401
+ if (!blobsByHash.has(file.hash)) blobsByHash.set(file.hash, {
9402
+ hash: file.hash,
9403
+ size: Buffer.byteLength(file.contents)
9404
+ });
9405
+ }
9406
+ const { uploads } = await client.artifacts.presignSource(projectId, artifactId, [...blobsByHash.values()]);
9407
+ await Promise.all(uploads.map(async (upload) => {
9408
+ const contents = contentsByHash.get(upload.hash);
9409
+ if (contents === void 0) return;
9410
+ const response = await fetch(upload.url, {
9411
+ method: "PUT",
9412
+ body: contents,
9413
+ headers: { "Content-Type": SOURCE_BLOB_CONTENT_TYPE }
9414
+ });
9415
+ if (!response.ok) throw new Error(`Source blob upload failed (${response.status})`);
9416
+ }));
9417
+ await client.artifacts.uploadManifest(projectId, artifactId, {
9418
+ files: source.files.map((file) => ({
9419
+ path: file.path,
9420
+ hash: file.hash
9421
+ })),
9422
+ resources: source.resources
9423
+ });
9424
+ }
9327
9425
  async function sleep(ms) {
9328
9426
  await new Promise((resolve) => {
9329
9427
  setTimeout(resolve, ms);
@@ -9334,7 +9432,7 @@ async function runDeploy(options) {
9334
9432
  const config = createCliConfig();
9335
9433
  const client = createCliPlatformClient(config);
9336
9434
  if (!options.skipBuild) {
9337
- const { buildApp } = await import("./dist-D1ip549D.mjs");
9435
+ const { buildApp } = await import("./dist-IKyaKHgU.mjs");
9338
9436
  await buildApp({ root });
9339
9437
  }
9340
9438
  const archive = packProjectArtifact(root);
@@ -9342,12 +9440,19 @@ async function runDeploy(options) {
9342
9440
  if (!active) active = await selectActiveOrganization(config);
9343
9441
  client.setActiveOrganizationId(active.organization.id);
9344
9442
  const project = await client.projects.get(options.projectId);
9443
+ const source = collectProjectSource(root);
9345
9444
  const created = await client.artifacts.create(project.id);
9346
- const upload = await fetch(created.uploadUrl, {
9445
+ const uploadPromise = fetch(created.uploadUrl, {
9347
9446
  method: "PUT",
9348
9447
  body: archive,
9349
9448
  headers: { "Content-Type": "application/gzip" }
9350
9449
  });
9450
+ const sourcePromise = uploadProjectSourceSnapshot(client, project.id, created.artifact.id, source).catch((error) => {
9451
+ const message = error instanceof Error ? error.message : "unknown error";
9452
+ process.stderr.write(`Warning: failed to upload project source files: ${message}\n`);
9453
+ });
9454
+ const upload = await uploadPromise;
9455
+ await sourcePromise;
9351
9456
  if (!upload.ok) throw new Error(`Artifact upload failed (${upload.status})`);
9352
9457
  const completed = await client.artifacts.complete(project.id, created.artifact.id);
9353
9458
  process.stdout.write(`Deploy ${completed.artifact.id} uploaded\n`);
@@ -9425,7 +9530,7 @@ function runtimeChildEnv(parentEnv, overrides) {
9425
9530
  //#region src/project/bootstrap-run.ts
9426
9531
  /** Node args + env for `@keystrokehq/build` bootstrap (shared by start + dev). */
9427
9532
  async function resolveBootstrapRun(options) {
9428
- const { resolveRuntimeBuildArtifact } = await import("./dist-D1ip549D.mjs");
9533
+ const { resolveRuntimeBuildArtifact } = await import("./dist-IKyaKHgU.mjs");
9429
9534
  const loader = pathToFileURL(resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/runtime-loader.mjs")).href;
9430
9535
  const bootstrap = resolveRuntimeBuildArtifact(options.runtimeNodeModules, "dist/bootstrap.mjs");
9431
9536
  const args = [`--import=${loader}`];
@@ -9575,7 +9680,7 @@ async function runDev(options) {
9575
9680
  process.on("SIGINT", shutdown);
9576
9681
  process.on("SIGTERM", shutdown);
9577
9682
  try {
9578
- const { watchApp } = await import("./dist-D1ip549D.mjs");
9683
+ const { watchApp } = await import("./dist-IKyaKHgU.mjs");
9579
9684
  await watchApp({
9580
9685
  root,
9581
9686
  clean: false,
@@ -9970,7 +10075,7 @@ async function runStart(options) {
9970
10075
  const apiPort = Number(new URL(serverUrl).port || 80);
9971
10076
  const runtimeNodeModules = resolveCliRuntimeNodeModules(resolveCliRoot(import.meta.url));
9972
10077
  ensureNativeDeps(runtimeNodeModules);
9973
- const { buildApp } = await import("./dist-D1ip549D.mjs");
10078
+ const { buildApp } = await import("./dist-IKyaKHgU.mjs");
9974
10079
  await buildApp({
9975
10080
  root,
9976
10081
  clean: false