@keystrokehq/hosting 0.0.112 → 0.1.0

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/dist/index.cjs CHANGED
@@ -42,8 +42,13 @@ const RUNTIME_ENV_KEYS = [
42
42
  "POSTGRES_SSL",
43
43
  "KEYSTROKE_SCHEDULER_MODULE"
44
44
  ];
45
- /** Provider keys for non-worker runtimes only — hosted workers use the platform LLM proxy. */
46
- const LOCAL_PROVIDER_ENV_KEYS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"];
45
+ /** Provider and gateway env forwarded from the platform into project servers. */
46
+ const PROVIDER_ENV_KEYS = [
47
+ "ANTHROPIC_API_KEY",
48
+ "OPENAI_API_KEY",
49
+ "AI_GATEWAY_API_KEY",
50
+ "AI_GATEWAY_BASE_URL"
51
+ ];
47
52
  /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
48
53
  const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
49
54
  const PROJECT_DATABASE_ENV_KEYS = [
@@ -70,18 +75,18 @@ function projectDatabaseRuntimeEnv(database) {
70
75
  * per-project presigned URL to that deploy's built `dist` — there is no base
71
76
  * image, so it is required.
72
77
  */
73
- function resolveRuntimeLaunch(options, artifactUrl, database) {
78
+ function resolveRuntimeLaunch(options, artifactUrl, database, extraEnv) {
74
79
  if (!artifactUrl) throw new Error("Cannot start a project server without an artifact URL");
75
80
  const env = options.env ?? process.env;
76
81
  return {
77
82
  image: resolveProjectServerImage(env, options.image),
78
- env: buildRuntimeEnv(env, artifactUrl, database),
83
+ env: buildRuntimeEnv(env, artifactUrl, database, extraEnv),
79
84
  port: PROJECT_SERVER_PORT,
80
85
  health: PROJECT_SERVER_HEALTH
81
86
  };
82
87
  }
83
88
  /** Env vars passed into the project server container/machine. */
84
- function buildRuntimeEnv(source, artifactUrl, database) {
89
+ function buildRuntimeEnv(source, artifactUrl, database, extraEnv) {
85
90
  const mode = source.KEYSTROKE_MODE ?? "worker";
86
91
  const entries = new Map([
87
92
  ["PORT", String(PROJECT_SERVER_PORT)],
@@ -101,7 +106,7 @@ function buildRuntimeEnv(source, artifactUrl, database) {
101
106
  }
102
107
  entries.set(key, value);
103
108
  }
104
- if (mode !== "worker") for (const key of LOCAL_PROVIDER_ENV_KEYS) {
109
+ for (const key of PROVIDER_ENV_KEYS) {
105
110
  const value = source[key]?.trim();
106
111
  if (value) entries.set(key, value);
107
112
  }
@@ -113,14 +118,11 @@ function buildRuntimeEnv(source, artifactUrl, database) {
113
118
  const worker = resolveWorkerRuntimeConfig(source);
114
119
  entries.set("PLATFORM_URL", worker.platformUrl);
115
120
  entries.set("WORKER_INTERNAL_TOKEN", worker.workerToken);
116
- entries.set("AI_GATEWAY_BASE_URL", resolveLlmGatewayBaseUrl(worker.platformUrl, database.projectId));
117
121
  }
118
122
  for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) entries.set(key, value);
123
+ if (extraEnv) for (const [key, value] of Object.entries(extraEnv)) entries.set(key, value);
119
124
  return Object.fromEntries(entries);
120
125
  }
121
- function resolveLlmGatewayBaseUrl(platformUrl, projectId) {
122
- return `${platformUrl.replace(/\/$/, "")}/internal/projects/${encodeURIComponent(projectId)}/llm`;
123
- }
124
126
  function resolvePlatformWorkerToken(source = process.env) {
125
127
  const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();
126
128
  if (explicit) return explicit;
@@ -207,7 +209,7 @@ const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
207
209
  function createDockerRuntime(options = {}) {
208
210
  const docker = options.docker ?? new dockerode.default();
209
211
  return { async start(input) {
210
- const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database);
212
+ const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database, input.env);
211
213
  await ensureImage(docker, launch.image);
212
214
  await removeExistingContainer(docker, input.projectId);
213
215
  const container = await docker.createContainer({
@@ -344,7 +346,6 @@ exports.formatDockerEnv = formatDockerEnv;
344
346
  exports.pingProject = pingProject;
345
347
  exports.pingProjectTarget = pingProjectTarget;
346
348
  exports.projectDatabaseRuntimeEnv = projectDatabaseRuntimeEnv;
347
- exports.resolveLlmGatewayBaseUrl = resolveLlmGatewayBaseUrl;
348
349
  exports.resolvePlatformWorkerToken = resolvePlatformWorkerToken;
349
350
  exports.resolveProjectServerImage = resolveProjectServerImage;
350
351
  exports.resolveRuntimeLaunch = resolveRuntimeLaunch;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Docker"],"sources":["../src/constants.ts","../src/runtime-env.ts","../src/worker-runtime-config.ts","../src/wait-for-health.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"sourcesContent":["export const PROJECT_SERVER_IMAGE = \"keystroke/project-server:local\";\n\nexport const PROJECT_SERVER_PORT = 3000;\n\nexport const PROJECT_SERVER_ROOT = \"/app\";\n\nexport const PROJECT_SERVER_FRAMEWORK_ROOT = \"/opt/keystroke\";\n\nexport const PROJECT_SERVER_FRAMEWORK_NODE_MODULES = `${PROJECT_SERVER_FRAMEWORK_ROOT}/node_modules`;\n\nexport const PROJECT_SERVER_HEALTH = {\n path: \"/health\",\n port: PROJECT_SERVER_PORT,\n} as const;\n","import type { ProjectRuntimeDatabase } from \"./runtime\";\nimport {\n PROJECT_SERVER_FRAMEWORK_NODE_MODULES,\n PROJECT_SERVER_HEALTH,\n PROJECT_SERVER_IMAGE,\n PROJECT_SERVER_PORT,\n PROJECT_SERVER_ROOT,\n} from \"./constants\";\nimport type { HostingOptions } from \"./plugin\";\nimport { resolveWorkerRuntimeConfig } from \"./worker-runtime-config\";\n\nconst RUNTIME_ENV_KEYS = [\n \"BETTER_AUTH_SECRET\",\n \"PUBLIC_SERVER_URL\",\n \"PUBLIC_WEB_URL\",\n \"POSTGRES_SSL\",\n // Scheduler backend selector (forwarded so the container consumer matches the\n // platform producer).\n \"KEYSTROKE_SCHEDULER_MODULE\",\n] as const;\n\n/** Provider keys for non-worker runtimes only — hosted workers use the platform LLM proxy. */\nconst LOCAL_PROVIDER_ENV_KEYS = [\"ANTHROPIC_API_KEY\", \"OPENAI_API_KEY\"] as const;\n\n/** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */\nexport const DEV_PLATFORM_WORKER_TOKEN = \"dev-platform-worker-token\";\n\nexport const PROJECT_DATABASE_ENV_KEYS = [\n \"POSTGRES_HOST\",\n \"POSTGRES_PORT\",\n \"POSTGRES_DB\",\n \"POSTGRES_USER\",\n \"POSTGRES_PASSWORD\",\n \"DATABASE_SEARCH_PATH\",\n] as const;\n\nexport type RuntimeLaunchSpec = {\n image: string;\n env: Record<string, string>;\n port: number;\n health: typeof PROJECT_SERVER_HEALTH;\n};\n\n/** Per-project postgres env injected into every project server container/machine. */\nexport function projectDatabaseRuntimeEnv(\n database: ProjectRuntimeDatabase,\n): Record<(typeof PROJECT_DATABASE_ENV_KEYS)[number], string> {\n return {\n POSTGRES_HOST: rewriteLoopbackHost(database.host),\n POSTGRES_PORT: String(database.port),\n POSTGRES_DB: database.database,\n POSTGRES_USER: database.user,\n POSTGRES_PASSWORD: database.password,\n DATABASE_SEARCH_PATH: database.searchPath,\n };\n}\n\n/**\n * Builds the launch spec for a single project server. `artifactUrl` is the\n * per-project presigned URL to that deploy's built `dist` — there is no base\n * image, so it is required.\n */\nexport function resolveRuntimeLaunch(\n options: HostingOptions,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n): RuntimeLaunchSpec {\n if (!artifactUrl) {\n throw new Error(\"Cannot start a project server without an artifact URL\");\n }\n\n const env = options.env ?? process.env;\n\n return {\n image: resolveProjectServerImage(env, options.image),\n env: buildRuntimeEnv(env, artifactUrl, database),\n port: PROJECT_SERVER_PORT,\n health: PROJECT_SERVER_HEALTH,\n };\n}\n\n/** Env vars passed into the project server container/machine. */\nexport function buildRuntimeEnv(\n source: NodeJS.ProcessEnv,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n): Record<string, string> {\n const mode = source.KEYSTROKE_MODE ?? \"worker\";\n const entries = new Map<string, string>([\n // Container bind port — not derived from PUBLIC_SERVER_URL (external URL metadata).\n [\"PORT\", String(PROJECT_SERVER_PORT)],\n [\"KEYSTROKE_ROOT\", PROJECT_SERVER_ROOT],\n [\"KEYSTROKE_RUNTIME_NODE_MODULES\", PROJECT_SERVER_FRAMEWORK_NODE_MODULES],\n [\"ARTIFACT_URL\", artifactUrl],\n [\"KEYSTROKE_MODE\", mode],\n [\"PROJECT_ID\", database.projectId],\n [\"ORGANIZATION_ID\", database.organizationId],\n [\"TENANT_ID\", database.organizationId],\n ]);\n\n for (const key of RUNTIME_ENV_KEYS) {\n const value = source[key]?.trim();\n if (!value) {\n if (key === \"POSTGRES_SSL\") {\n throw new Error(\"POSTGRES_SSL is required\");\n }\n continue;\n }\n entries.set(key, value);\n }\n\n if (mode !== \"worker\") {\n for (const key of LOCAL_PROVIDER_ENV_KEYS) {\n const value = source[key]?.trim();\n if (value) {\n entries.set(key, value);\n }\n }\n }\n\n if (mode !== \"worker\") {\n const credentialsKey = source.CREDENTIALS_ENCRYPTION_KEY;\n if (credentialsKey) {\n entries.set(\"CREDENTIALS_ENCRYPTION_KEY\", credentialsKey);\n }\n }\n\n if (mode === \"worker\") {\n const worker = resolveWorkerRuntimeConfig(source);\n entries.set(\"PLATFORM_URL\", worker.platformUrl);\n entries.set(\"WORKER_INTERNAL_TOKEN\", worker.workerToken);\n entries.set(\n \"AI_GATEWAY_BASE_URL\",\n resolveLlmGatewayBaseUrl(worker.platformUrl, database.projectId),\n );\n }\n\n for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) {\n entries.set(key, value);\n }\n\n return Object.fromEntries(entries);\n}\n\nexport function resolveLlmGatewayBaseUrl(platformUrl: string, projectId: string): string {\n return `${platformUrl.replace(/\\/$/, \"\")}/internal/projects/${encodeURIComponent(projectId)}/llm`;\n}\n\nexport function resolvePlatformWorkerToken(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();\n if (explicit) {\n return explicit;\n }\n\n if (source.NODE_ENV === \"production\") {\n return undefined;\n }\n\n return DEV_PLATFORM_WORKER_TOKEN;\n}\n\n/** Platform API URL reachable from a project-server container. */\nconst DEV_PLATFORM_URL = \"http://localhost:3002\";\n\nexport function resolveWorkerPlatformUrl(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const raw =\n source.PLATFORM_URL?.trim() ??\n source.PUBLIC_PLATFORM_URL?.trim() ??\n (source.NODE_ENV === \"production\" ? undefined : DEV_PLATFORM_URL);\n if (!raw) {\n return undefined;\n }\n\n return rewriteLoopbackUrl(raw);\n}\n\n/** Rewrite loopback hosts so containers can reach Postgres and other host services. */\nexport function rewriteLoopbackHost(host: string): string {\n if (host === \"localhost\" || host === \"127.0.0.1\") {\n return \"host.docker.internal\";\n }\n\n return host;\n}\n\nexport function rewriteLoopbackUrl(url: string): string {\n try {\n const parsed = new URL(url);\n parsed.hostname = rewriteLoopbackHost(parsed.hostname);\n return parsed.toString().replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n}\n\n/** dockerode expects `KEY=value` strings. */\nexport function formatDockerEnv(env: Record<string, string>): string[] {\n return Object.entries(env).map(([key, value]) => `${key}=${value}`);\n}\n\nexport function resolveProjectServerImage(\n env: NodeJS.ProcessEnv = process.env,\n override?: string,\n): string {\n return override ?? env.PROJECT_DOCKER_IMAGE ?? env.TENANT_DOCKER_IMAGE ?? PROJECT_SERVER_IMAGE;\n}\n","import { resolvePlatformWorkerToken, resolveWorkerPlatformUrl } from \"./runtime-env\";\n\n/** Resolved platform ↔ project-server worker auth contract. */\nexport type WorkerRuntimeConfig = {\n /** Platform API base URL reachable from the project-server runtime. */\n platformUrl: string;\n /** Bearer token for platform `/internal` routes. */\n workerToken: string;\n};\n\nexport class WorkerRuntimeConfigError extends Error {\n readonly missing: readonly string[];\n\n constructor(missing: string[]) {\n super(\n `Worker runtime config incomplete (missing: ${missing.join(\", \")}). ` +\n \"In production set PLATFORM_WORKER_TOKEN and PUBLIC_PLATFORM_URL (or PLATFORM_URL).\",\n );\n this.name = \"WorkerRuntimeConfigError\";\n this.missing = missing;\n }\n}\n\n/** Fail fast when the platform cannot start worker-mode project servers. */\nexport function resolveWorkerRuntimeConfig(\n source: NodeJS.ProcessEnv = process.env,\n): WorkerRuntimeConfig {\n const platformUrl = resolveWorkerPlatformUrl(source);\n const workerToken = resolvePlatformWorkerToken(source);\n const missing: string[] = [];\n\n if (!platformUrl) {\n missing.push(\"PUBLIC_PLATFORM_URL or PLATFORM_URL\");\n }\n\n if (!workerToken) {\n missing.push(\"PLATFORM_WORKER_TOKEN\");\n }\n\n if (missing.length > 0) {\n throw new WorkerRuntimeConfigError(missing);\n }\n\n return { platformUrl: platformUrl!, workerToken: workerToken! };\n}\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_INTERVAL_MS = 500;\n\nexport type WaitForHealthOptions = {\n timeoutMs?: number;\n intervalMs?: number;\n fetchImpl?: typeof fetch;\n};\n\nexport async function waitForHealth(\n baseUrl: string,\n options: WaitForHealthOptions = {},\n): Promise<void> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n const deadline = Date.now() + timeoutMs;\n const url = new URL(PROJECT_SERVER_HEALTH.path, baseUrl);\n\n while (Date.now() < deadline) {\n try {\n const response = await fetchImpl(url, { signal: AbortSignal.timeout(2_000) });\n if (response.ok) {\n return;\n }\n } catch {\n // retry until timeout\n }\n\n await sleep(intervalMs);\n }\n\n throw new Error(`Project server health check timed out after ${timeoutMs}ms (${url})`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","import Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n const docker = options.docker ?? new Docker();\n\n return {\n async start(input) {\n const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database);\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.projectId);\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.projectId),\n Labels: {\n \"keystroke.project.id\": input.projectId,\n \"keystroke.project.name\": input.name,\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve project container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(projectId: string): string {\n return `keystroke-project-${projectId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, projectId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(projectId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n","/** Timeout for a single project runtime `/health` probe. */\nexport const RUNTIME_PING_TIMEOUT_MS = 15_000;\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\nimport type { HostingPlugin } from \"./plugin\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport type PingProjectOptions = {\n runtimeId?: string | null;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n plugin?: Pick<HostingPlugin, \"pingRequestHeaders\">;\n};\n\nexport async function pingProject(\n baseUrl: string,\n options: PingProjectOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers = options.plugin?.pingRequestHeaders?.(options.runtimeId ?? null) ?? {};\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_HEALTH.path, baseUrl),\n {\n signal: AbortSignal.timeout(timeoutMs),\n headers,\n },\n );\n\n return response.ok;\n } catch {\n return false;\n }\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport type { ProjectPingTarget } from \"./runtime\";\nimport { pingProject, type PingProjectOptions } from \"./ping-project\";\n\nexport function canPingProjectTarget(\n target: ProjectPingTarget,\n plugin?: Pick<HostingPlugin, \"canPingTarget\">,\n): target is { baseUrl: string; runtimeId: string | null } {\n if (plugin?.canPingTarget) {\n return plugin.canPingTarget(target);\n }\n\n return !!target.baseUrl;\n}\n\nexport async function pingProjectTarget(\n target: ProjectPingTarget,\n options: PingProjectOptions & {\n plugin?: Pick<HostingPlugin, \"canPingTarget\" | \"pingRequestHeaders\">;\n } = {},\n): Promise<boolean> {\n if (!canPingProjectTarget(target, options.plugin)) {\n return false;\n }\n\n return pingProject(target.baseUrl, {\n ...options,\n runtimeId: target.runtimeId,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["Docker"],"sources":["../src/constants.ts","../src/runtime-env.ts","../src/worker-runtime-config.ts","../src/wait-for-health.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"sourcesContent":["export const PROJECT_SERVER_IMAGE = \"keystroke/project-server:local\";\n\nexport const PROJECT_SERVER_PORT = 3000;\n\nexport const PROJECT_SERVER_ROOT = \"/app\";\n\nexport const PROJECT_SERVER_FRAMEWORK_ROOT = \"/opt/keystroke\";\n\nexport const PROJECT_SERVER_FRAMEWORK_NODE_MODULES = `${PROJECT_SERVER_FRAMEWORK_ROOT}/node_modules`;\n\nexport const PROJECT_SERVER_HEALTH = {\n path: \"/health\",\n port: PROJECT_SERVER_PORT,\n} as const;\n","import type { ProjectRuntimeDatabase } from \"./runtime\";\nimport {\n PROJECT_SERVER_FRAMEWORK_NODE_MODULES,\n PROJECT_SERVER_HEALTH,\n PROJECT_SERVER_IMAGE,\n PROJECT_SERVER_PORT,\n PROJECT_SERVER_ROOT,\n} from \"./constants\";\nimport type { HostingOptions } from \"./plugin\";\nimport { resolveWorkerRuntimeConfig } from \"./worker-runtime-config\";\n\nconst RUNTIME_ENV_KEYS = [\n \"BETTER_AUTH_SECRET\",\n \"PUBLIC_SERVER_URL\",\n \"PUBLIC_WEB_URL\",\n \"POSTGRES_SSL\",\n // Scheduler backend selector (forwarded so the container consumer matches the\n // platform producer).\n \"KEYSTROKE_SCHEDULER_MODULE\",\n] as const;\n\n/** Provider and gateway env forwarded from the platform into project servers. */\nconst PROVIDER_ENV_KEYS = [\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AI_GATEWAY_API_KEY\",\n \"AI_GATEWAY_BASE_URL\",\n] as const;\n\n/** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */\nexport const DEV_PLATFORM_WORKER_TOKEN = \"dev-platform-worker-token\";\n\nexport const PROJECT_DATABASE_ENV_KEYS = [\n \"POSTGRES_HOST\",\n \"POSTGRES_PORT\",\n \"POSTGRES_DB\",\n \"POSTGRES_USER\",\n \"POSTGRES_PASSWORD\",\n \"DATABASE_SEARCH_PATH\",\n] as const;\n\nexport type RuntimeLaunchSpec = {\n image: string;\n env: Record<string, string>;\n port: number;\n health: typeof PROJECT_SERVER_HEALTH;\n};\n\n/** Per-project postgres env injected into every project server container/machine. */\nexport function projectDatabaseRuntimeEnv(\n database: ProjectRuntimeDatabase,\n): Record<(typeof PROJECT_DATABASE_ENV_KEYS)[number], string> {\n return {\n POSTGRES_HOST: rewriteLoopbackHost(database.host),\n POSTGRES_PORT: String(database.port),\n POSTGRES_DB: database.database,\n POSTGRES_USER: database.user,\n POSTGRES_PASSWORD: database.password,\n DATABASE_SEARCH_PATH: database.searchPath,\n };\n}\n\n/**\n * Builds the launch spec for a single project server. `artifactUrl` is the\n * per-project presigned URL to that deploy's built `dist` — there is no base\n * image, so it is required.\n */\nexport function resolveRuntimeLaunch(\n options: HostingOptions,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n extraEnv?: Record<string, string>,\n): RuntimeLaunchSpec {\n if (!artifactUrl) {\n throw new Error(\"Cannot start a project server without an artifact URL\");\n }\n\n const env = options.env ?? process.env;\n\n return {\n image: resolveProjectServerImage(env, options.image),\n env: buildRuntimeEnv(env, artifactUrl, database, extraEnv),\n port: PROJECT_SERVER_PORT,\n health: PROJECT_SERVER_HEALTH,\n };\n}\n\n/** Env vars passed into the project server container/machine. */\nexport function buildRuntimeEnv(\n source: NodeJS.ProcessEnv,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n extraEnv?: Record<string, string>,\n): Record<string, string> {\n const mode = source.KEYSTROKE_MODE ?? \"worker\";\n const entries = new Map<string, string>([\n // Container bind port — not derived from PUBLIC_SERVER_URL (external URL metadata).\n [\"PORT\", String(PROJECT_SERVER_PORT)],\n [\"KEYSTROKE_ROOT\", PROJECT_SERVER_ROOT],\n [\"KEYSTROKE_RUNTIME_NODE_MODULES\", PROJECT_SERVER_FRAMEWORK_NODE_MODULES],\n [\"ARTIFACT_URL\", artifactUrl],\n [\"KEYSTROKE_MODE\", mode],\n [\"PROJECT_ID\", database.projectId],\n [\"ORGANIZATION_ID\", database.organizationId],\n [\"TENANT_ID\", database.organizationId],\n ]);\n\n for (const key of RUNTIME_ENV_KEYS) {\n const value = source[key]?.trim();\n if (!value) {\n if (key === \"POSTGRES_SSL\") {\n throw new Error(\"POSTGRES_SSL is required\");\n }\n continue;\n }\n entries.set(key, value);\n }\n\n for (const key of PROVIDER_ENV_KEYS) {\n const value = source[key]?.trim();\n if (value) {\n entries.set(key, value);\n }\n }\n\n if (mode !== \"worker\") {\n const credentialsKey = source.CREDENTIALS_ENCRYPTION_KEY;\n if (credentialsKey) {\n entries.set(\"CREDENTIALS_ENCRYPTION_KEY\", credentialsKey);\n }\n }\n\n if (mode === \"worker\") {\n const worker = resolveWorkerRuntimeConfig(source);\n entries.set(\"PLATFORM_URL\", worker.platformUrl);\n entries.set(\"WORKER_INTERNAL_TOKEN\", worker.workerToken);\n }\n\n for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) {\n entries.set(key, value);\n }\n\n if (extraEnv) {\n for (const [key, value] of Object.entries(extraEnv)) {\n entries.set(key, value);\n }\n }\n\n return Object.fromEntries(entries);\n}\n\nexport function resolvePlatformWorkerToken(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();\n if (explicit) {\n return explicit;\n }\n\n if (source.NODE_ENV === \"production\") {\n return undefined;\n }\n\n return DEV_PLATFORM_WORKER_TOKEN;\n}\n\n/** Platform API URL reachable from a project-server container. */\nconst DEV_PLATFORM_URL = \"http://localhost:3002\";\n\nexport function resolveWorkerPlatformUrl(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const raw =\n source.PLATFORM_URL?.trim() ??\n source.PUBLIC_PLATFORM_URL?.trim() ??\n (source.NODE_ENV === \"production\" ? undefined : DEV_PLATFORM_URL);\n if (!raw) {\n return undefined;\n }\n\n return rewriteLoopbackUrl(raw);\n}\n\n/** Rewrite loopback hosts so containers can reach Postgres and other host services. */\nexport function rewriteLoopbackHost(host: string): string {\n if (host === \"localhost\" || host === \"127.0.0.1\") {\n return \"host.docker.internal\";\n }\n\n return host;\n}\n\nexport function rewriteLoopbackUrl(url: string): string {\n try {\n const parsed = new URL(url);\n parsed.hostname = rewriteLoopbackHost(parsed.hostname);\n return parsed.toString().replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n}\n\n/** dockerode expects `KEY=value` strings. */\nexport function formatDockerEnv(env: Record<string, string>): string[] {\n return Object.entries(env).map(([key, value]) => `${key}=${value}`);\n}\n\nexport function resolveProjectServerImage(\n env: NodeJS.ProcessEnv = process.env,\n override?: string,\n): string {\n return override ?? env.PROJECT_DOCKER_IMAGE ?? env.TENANT_DOCKER_IMAGE ?? PROJECT_SERVER_IMAGE;\n}\n","import { resolvePlatformWorkerToken, resolveWorkerPlatformUrl } from \"./runtime-env\";\n\n/** Resolved platform ↔ project-server worker auth contract. */\nexport type WorkerRuntimeConfig = {\n /** Platform API base URL reachable from the project-server runtime. */\n platformUrl: string;\n /** Bearer token for platform `/internal` routes. */\n workerToken: string;\n};\n\nexport class WorkerRuntimeConfigError extends Error {\n readonly missing: readonly string[];\n\n constructor(missing: string[]) {\n super(\n `Worker runtime config incomplete (missing: ${missing.join(\", \")}). ` +\n \"In production set PLATFORM_WORKER_TOKEN and PUBLIC_PLATFORM_URL (or PLATFORM_URL).\",\n );\n this.name = \"WorkerRuntimeConfigError\";\n this.missing = missing;\n }\n}\n\n/** Fail fast when the platform cannot start worker-mode project servers. */\nexport function resolveWorkerRuntimeConfig(\n source: NodeJS.ProcessEnv = process.env,\n): WorkerRuntimeConfig {\n const platformUrl = resolveWorkerPlatformUrl(source);\n const workerToken = resolvePlatformWorkerToken(source);\n const missing: string[] = [];\n\n if (!platformUrl) {\n missing.push(\"PUBLIC_PLATFORM_URL or PLATFORM_URL\");\n }\n\n if (!workerToken) {\n missing.push(\"PLATFORM_WORKER_TOKEN\");\n }\n\n if (missing.length > 0) {\n throw new WorkerRuntimeConfigError(missing);\n }\n\n return { platformUrl: platformUrl!, workerToken: workerToken! };\n}\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_INTERVAL_MS = 500;\n\nexport type WaitForHealthOptions = {\n timeoutMs?: number;\n intervalMs?: number;\n fetchImpl?: typeof fetch;\n};\n\nexport async function waitForHealth(\n baseUrl: string,\n options: WaitForHealthOptions = {},\n): Promise<void> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n const deadline = Date.now() + timeoutMs;\n const url = new URL(PROJECT_SERVER_HEALTH.path, baseUrl);\n\n while (Date.now() < deadline) {\n try {\n const response = await fetchImpl(url, { signal: AbortSignal.timeout(2_000) });\n if (response.ok) {\n return;\n }\n } catch {\n // retry until timeout\n }\n\n await sleep(intervalMs);\n }\n\n throw new Error(`Project server health check timed out after ${timeoutMs}ms (${url})`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","import Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n const docker = options.docker ?? new Docker();\n\n return {\n async start(input) {\n const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database, input.env);\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.projectId);\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.projectId),\n Labels: {\n \"keystroke.project.id\": input.projectId,\n \"keystroke.project.name\": input.name,\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve project container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(projectId: string): string {\n return `keystroke-project-${projectId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, projectId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(projectId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n","/** Timeout for a single project runtime `/health` probe. */\nexport const RUNTIME_PING_TIMEOUT_MS = 15_000;\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\nimport type { HostingPlugin } from \"./plugin\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport type PingProjectOptions = {\n runtimeId?: string | null;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n plugin?: Pick<HostingPlugin, \"pingRequestHeaders\">;\n};\n\nexport async function pingProject(\n baseUrl: string,\n options: PingProjectOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers = options.plugin?.pingRequestHeaders?.(options.runtimeId ?? null) ?? {};\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_HEALTH.path, baseUrl),\n {\n signal: AbortSignal.timeout(timeoutMs),\n headers,\n },\n );\n\n return response.ok;\n } catch {\n return false;\n }\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport type { ProjectPingTarget } from \"./runtime\";\nimport { pingProject, type PingProjectOptions } from \"./ping-project\";\n\nexport function canPingProjectTarget(\n target: ProjectPingTarget,\n plugin?: Pick<HostingPlugin, \"canPingTarget\">,\n): target is { baseUrl: string; runtimeId: string | null } {\n if (plugin?.canPingTarget) {\n return plugin.canPingTarget(target);\n }\n\n return !!target.baseUrl;\n}\n\nexport async function pingProjectTarget(\n target: ProjectPingTarget,\n options: PingProjectOptions & {\n plugin?: Pick<HostingPlugin, \"canPingTarget\" | \"pingRequestHeaders\">;\n } = {},\n): Promise<boolean> {\n if (!canPingProjectTarget(target, options.plugin)) {\n return false;\n }\n\n return pingProject(target.baseUrl, {\n ...options,\n runtimeId: target.runtimeId,\n });\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -31,7 +31,8 @@ type ProjectRuntimeInput = {
31
31
  artifactId: string;
32
32
  artifactUrl: string;
33
33
  database: ProjectRuntimeDatabase;
34
- hosting?: ProjectRuntimeHosting; /** Previous runtime to retire after a successful cutover. */
34
+ hosting?: ProjectRuntimeHosting; /** Extra env merged into the project server launch spec (e.g. from an llmProxy plugin). */
35
+ env?: Record<string, string>; /** Previous runtime to retire after a successful cutover. */
35
36
  previousRuntimeId?: string | null;
36
37
  };
37
38
  type ProjectRuntimeDestroyInput = {
@@ -121,10 +122,9 @@ declare function projectDatabaseRuntimeEnv(database: ProjectRuntimeDatabase): Re
121
122
  * per-project presigned URL to that deploy's built `dist` — there is no base
122
123
  * image, so it is required.
123
124
  */
124
- declare function resolveRuntimeLaunch(options: HostingOptions, artifactUrl: string, database: ProjectRuntimeDatabase): RuntimeLaunchSpec;
125
+ declare function resolveRuntimeLaunch(options: HostingOptions, artifactUrl: string, database: ProjectRuntimeDatabase, extraEnv?: Record<string, string>): RuntimeLaunchSpec;
125
126
  /** Env vars passed into the project server container/machine. */
126
- declare function buildRuntimeEnv(source: NodeJS.ProcessEnv, artifactUrl: string, database: ProjectRuntimeDatabase): Record<string, string>;
127
- declare function resolveLlmGatewayBaseUrl(platformUrl: string, projectId: string): string;
127
+ declare function buildRuntimeEnv(source: NodeJS.ProcessEnv, artifactUrl: string, database: ProjectRuntimeDatabase, extraEnv?: Record<string, string>): Record<string, string>;
128
128
  declare function resolvePlatformWorkerToken(source?: NodeJS.ProcessEnv): string | undefined;
129
129
  declare function resolveWorkerPlatformUrl(source?: NodeJS.ProcessEnv): string | undefined;
130
130
  /** Rewrite loopback hosts so containers can reach Postgres and other host services. */
@@ -164,5 +164,5 @@ declare function pingProjectTarget(target: ProjectPingTarget, options?: PingProj
164
164
  plugin?: Pick<HostingPlugin, "canPingTarget" | "pingRequestHeaders">;
165
165
  }): Promise<boolean>;
166
166
  //#endregion
167
- export { DEV_PLATFORM_WORKER_TOKEN, type DockerPluginOptions, type HostingOptions, type HostingPlugin, type OrganizationHostingInput, type OrganizationHostingResult, PROJECT_DATABASE_ENV_KEYS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, type PingProjectOptions, type ProjectPingTarget, type ProjectRuntime, type ProjectRuntimeDatabase, type ProjectRuntimeDestroyInput, type ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type WaitForHealthOptions, type WorkerRuntimeConfig, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolveLlmGatewayBaseUrl, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, waitForHealth };
167
+ export { DEV_PLATFORM_WORKER_TOKEN, type DockerPluginOptions, type HostingOptions, type HostingPlugin, type OrganizationHostingInput, type OrganizationHostingResult, PROJECT_DATABASE_ENV_KEYS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, type PingProjectOptions, type ProjectPingTarget, type ProjectRuntime, type ProjectRuntimeDatabase, type ProjectRuntimeDestroyInput, type ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type WaitForHealthOptions, type WorkerRuntimeConfig, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, waitForHealth };
168
168
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/constants.ts","../src/runtime.ts","../src/worker-runtime-config.ts","../src/plugin.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-env.ts","../src/wait-for-health.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/constants.ts","../src/runtime.ts","../src/worker-runtime-config.ts","../src/plugin.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-env.ts","../src/wait-for-health.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -31,7 +31,8 @@ type ProjectRuntimeInput = {
31
31
  artifactId: string;
32
32
  artifactUrl: string;
33
33
  database: ProjectRuntimeDatabase;
34
- hosting?: ProjectRuntimeHosting; /** Previous runtime to retire after a successful cutover. */
34
+ hosting?: ProjectRuntimeHosting; /** Extra env merged into the project server launch spec (e.g. from an llmProxy plugin). */
35
+ env?: Record<string, string>; /** Previous runtime to retire after a successful cutover. */
35
36
  previousRuntimeId?: string | null;
36
37
  };
37
38
  type ProjectRuntimeDestroyInput = {
@@ -121,10 +122,9 @@ declare function projectDatabaseRuntimeEnv(database: ProjectRuntimeDatabase): Re
121
122
  * per-project presigned URL to that deploy's built `dist` — there is no base
122
123
  * image, so it is required.
123
124
  */
124
- declare function resolveRuntimeLaunch(options: HostingOptions, artifactUrl: string, database: ProjectRuntimeDatabase): RuntimeLaunchSpec;
125
+ declare function resolveRuntimeLaunch(options: HostingOptions, artifactUrl: string, database: ProjectRuntimeDatabase, extraEnv?: Record<string, string>): RuntimeLaunchSpec;
125
126
  /** Env vars passed into the project server container/machine. */
126
- declare function buildRuntimeEnv(source: NodeJS.ProcessEnv, artifactUrl: string, database: ProjectRuntimeDatabase): Record<string, string>;
127
- declare function resolveLlmGatewayBaseUrl(platformUrl: string, projectId: string): string;
127
+ declare function buildRuntimeEnv(source: NodeJS.ProcessEnv, artifactUrl: string, database: ProjectRuntimeDatabase, extraEnv?: Record<string, string>): Record<string, string>;
128
128
  declare function resolvePlatformWorkerToken(source?: NodeJS.ProcessEnv): string | undefined;
129
129
  declare function resolveWorkerPlatformUrl(source?: NodeJS.ProcessEnv): string | undefined;
130
130
  /** Rewrite loopback hosts so containers can reach Postgres and other host services. */
@@ -164,5 +164,5 @@ declare function pingProjectTarget(target: ProjectPingTarget, options?: PingProj
164
164
  plugin?: Pick<HostingPlugin, "canPingTarget" | "pingRequestHeaders">;
165
165
  }): Promise<boolean>;
166
166
  //#endregion
167
- export { DEV_PLATFORM_WORKER_TOKEN, type DockerPluginOptions, type HostingOptions, type HostingPlugin, type OrganizationHostingInput, type OrganizationHostingResult, PROJECT_DATABASE_ENV_KEYS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, type PingProjectOptions, type ProjectPingTarget, type ProjectRuntime, type ProjectRuntimeDatabase, type ProjectRuntimeDestroyInput, type ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type WaitForHealthOptions, type WorkerRuntimeConfig, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolveLlmGatewayBaseUrl, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, waitForHealth };
167
+ export { DEV_PLATFORM_WORKER_TOKEN, type DockerPluginOptions, type HostingOptions, type HostingPlugin, type OrganizationHostingInput, type OrganizationHostingResult, PROJECT_DATABASE_ENV_KEYS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, type PingProjectOptions, type ProjectPingTarget, type ProjectRuntime, type ProjectRuntimeDatabase, type ProjectRuntimeDestroyInput, type ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type WaitForHealthOptions, type WorkerRuntimeConfig, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, waitForHealth };
168
168
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/constants.ts","../src/runtime.ts","../src/worker-runtime-config.ts","../src/plugin.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-env.ts","../src/wait-for-health.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/constants.ts","../src/runtime.ts","../src/worker-runtime-config.ts","../src/plugin.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-env.ts","../src/wait-for-health.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -18,8 +18,13 @@ const RUNTIME_ENV_KEYS = [
18
18
  "POSTGRES_SSL",
19
19
  "KEYSTROKE_SCHEDULER_MODULE"
20
20
  ];
21
- /** Provider keys for non-worker runtimes only — hosted workers use the platform LLM proxy. */
22
- const LOCAL_PROVIDER_ENV_KEYS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"];
21
+ /** Provider and gateway env forwarded from the platform into project servers. */
22
+ const PROVIDER_ENV_KEYS = [
23
+ "ANTHROPIC_API_KEY",
24
+ "OPENAI_API_KEY",
25
+ "AI_GATEWAY_API_KEY",
26
+ "AI_GATEWAY_BASE_URL"
27
+ ];
23
28
  /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
24
29
  const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
25
30
  const PROJECT_DATABASE_ENV_KEYS = [
@@ -46,18 +51,18 @@ function projectDatabaseRuntimeEnv(database) {
46
51
  * per-project presigned URL to that deploy's built `dist` — there is no base
47
52
  * image, so it is required.
48
53
  */
49
- function resolveRuntimeLaunch(options, artifactUrl, database) {
54
+ function resolveRuntimeLaunch(options, artifactUrl, database, extraEnv) {
50
55
  if (!artifactUrl) throw new Error("Cannot start a project server without an artifact URL");
51
56
  const env = options.env ?? process.env;
52
57
  return {
53
58
  image: resolveProjectServerImage(env, options.image),
54
- env: buildRuntimeEnv(env, artifactUrl, database),
59
+ env: buildRuntimeEnv(env, artifactUrl, database, extraEnv),
55
60
  port: PROJECT_SERVER_PORT,
56
61
  health: PROJECT_SERVER_HEALTH
57
62
  };
58
63
  }
59
64
  /** Env vars passed into the project server container/machine. */
60
- function buildRuntimeEnv(source, artifactUrl, database) {
65
+ function buildRuntimeEnv(source, artifactUrl, database, extraEnv) {
61
66
  const mode = source.KEYSTROKE_MODE ?? "worker";
62
67
  const entries = new Map([
63
68
  ["PORT", String(PROJECT_SERVER_PORT)],
@@ -77,7 +82,7 @@ function buildRuntimeEnv(source, artifactUrl, database) {
77
82
  }
78
83
  entries.set(key, value);
79
84
  }
80
- if (mode !== "worker") for (const key of LOCAL_PROVIDER_ENV_KEYS) {
85
+ for (const key of PROVIDER_ENV_KEYS) {
81
86
  const value = source[key]?.trim();
82
87
  if (value) entries.set(key, value);
83
88
  }
@@ -89,14 +94,11 @@ function buildRuntimeEnv(source, artifactUrl, database) {
89
94
  const worker = resolveWorkerRuntimeConfig(source);
90
95
  entries.set("PLATFORM_URL", worker.platformUrl);
91
96
  entries.set("WORKER_INTERNAL_TOKEN", worker.workerToken);
92
- entries.set("AI_GATEWAY_BASE_URL", resolveLlmGatewayBaseUrl(worker.platformUrl, database.projectId));
93
97
  }
94
98
  for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) entries.set(key, value);
99
+ if (extraEnv) for (const [key, value] of Object.entries(extraEnv)) entries.set(key, value);
95
100
  return Object.fromEntries(entries);
96
101
  }
97
- function resolveLlmGatewayBaseUrl(platformUrl, projectId) {
98
- return `${platformUrl.replace(/\/$/, "")}/internal/projects/${encodeURIComponent(projectId)}/llm`;
99
- }
100
102
  function resolvePlatformWorkerToken(source = process.env) {
101
103
  const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();
102
104
  if (explicit) return explicit;
@@ -183,7 +185,7 @@ const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
183
185
  function createDockerRuntime(options = {}) {
184
186
  const docker = options.docker ?? new Docker();
185
187
  return { async start(input) {
186
- const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database);
188
+ const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database, input.env);
187
189
  await ensureImage(docker, launch.image);
188
190
  await removeExistingContainer(docker, input.projectId);
189
191
  const container = await docker.createContainer({
@@ -302,6 +304,6 @@ async function pingProjectTarget(target, options = {}) {
302
304
  });
303
305
  }
304
306
  //#endregion
305
- export { DEV_PLATFORM_WORKER_TOKEN, PROJECT_DATABASE_ENV_KEYS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, RUNTIME_PING_TIMEOUT_MS, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolveLlmGatewayBaseUrl, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, waitForHealth };
307
+ export { DEV_PLATFORM_WORKER_TOKEN, PROJECT_DATABASE_ENV_KEYS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, RUNTIME_PING_TIMEOUT_MS, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, waitForHealth };
306
308
 
307
309
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/constants.ts","../src/runtime-env.ts","../src/worker-runtime-config.ts","../src/wait-for-health.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"sourcesContent":["export const PROJECT_SERVER_IMAGE = \"keystroke/project-server:local\";\n\nexport const PROJECT_SERVER_PORT = 3000;\n\nexport const PROJECT_SERVER_ROOT = \"/app\";\n\nexport const PROJECT_SERVER_FRAMEWORK_ROOT = \"/opt/keystroke\";\n\nexport const PROJECT_SERVER_FRAMEWORK_NODE_MODULES = `${PROJECT_SERVER_FRAMEWORK_ROOT}/node_modules`;\n\nexport const PROJECT_SERVER_HEALTH = {\n path: \"/health\",\n port: PROJECT_SERVER_PORT,\n} as const;\n","import type { ProjectRuntimeDatabase } from \"./runtime\";\nimport {\n PROJECT_SERVER_FRAMEWORK_NODE_MODULES,\n PROJECT_SERVER_HEALTH,\n PROJECT_SERVER_IMAGE,\n PROJECT_SERVER_PORT,\n PROJECT_SERVER_ROOT,\n} from \"./constants\";\nimport type { HostingOptions } from \"./plugin\";\nimport { resolveWorkerRuntimeConfig } from \"./worker-runtime-config\";\n\nconst RUNTIME_ENV_KEYS = [\n \"BETTER_AUTH_SECRET\",\n \"PUBLIC_SERVER_URL\",\n \"PUBLIC_WEB_URL\",\n \"POSTGRES_SSL\",\n // Scheduler backend selector (forwarded so the container consumer matches the\n // platform producer).\n \"KEYSTROKE_SCHEDULER_MODULE\",\n] as const;\n\n/** Provider keys for non-worker runtimes only — hosted workers use the platform LLM proxy. */\nconst LOCAL_PROVIDER_ENV_KEYS = [\"ANTHROPIC_API_KEY\", \"OPENAI_API_KEY\"] as const;\n\n/** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */\nexport const DEV_PLATFORM_WORKER_TOKEN = \"dev-platform-worker-token\";\n\nexport const PROJECT_DATABASE_ENV_KEYS = [\n \"POSTGRES_HOST\",\n \"POSTGRES_PORT\",\n \"POSTGRES_DB\",\n \"POSTGRES_USER\",\n \"POSTGRES_PASSWORD\",\n \"DATABASE_SEARCH_PATH\",\n] as const;\n\nexport type RuntimeLaunchSpec = {\n image: string;\n env: Record<string, string>;\n port: number;\n health: typeof PROJECT_SERVER_HEALTH;\n};\n\n/** Per-project postgres env injected into every project server container/machine. */\nexport function projectDatabaseRuntimeEnv(\n database: ProjectRuntimeDatabase,\n): Record<(typeof PROJECT_DATABASE_ENV_KEYS)[number], string> {\n return {\n POSTGRES_HOST: rewriteLoopbackHost(database.host),\n POSTGRES_PORT: String(database.port),\n POSTGRES_DB: database.database,\n POSTGRES_USER: database.user,\n POSTGRES_PASSWORD: database.password,\n DATABASE_SEARCH_PATH: database.searchPath,\n };\n}\n\n/**\n * Builds the launch spec for a single project server. `artifactUrl` is the\n * per-project presigned URL to that deploy's built `dist` — there is no base\n * image, so it is required.\n */\nexport function resolveRuntimeLaunch(\n options: HostingOptions,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n): RuntimeLaunchSpec {\n if (!artifactUrl) {\n throw new Error(\"Cannot start a project server without an artifact URL\");\n }\n\n const env = options.env ?? process.env;\n\n return {\n image: resolveProjectServerImage(env, options.image),\n env: buildRuntimeEnv(env, artifactUrl, database),\n port: PROJECT_SERVER_PORT,\n health: PROJECT_SERVER_HEALTH,\n };\n}\n\n/** Env vars passed into the project server container/machine. */\nexport function buildRuntimeEnv(\n source: NodeJS.ProcessEnv,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n): Record<string, string> {\n const mode = source.KEYSTROKE_MODE ?? \"worker\";\n const entries = new Map<string, string>([\n // Container bind port — not derived from PUBLIC_SERVER_URL (external URL metadata).\n [\"PORT\", String(PROJECT_SERVER_PORT)],\n [\"KEYSTROKE_ROOT\", PROJECT_SERVER_ROOT],\n [\"KEYSTROKE_RUNTIME_NODE_MODULES\", PROJECT_SERVER_FRAMEWORK_NODE_MODULES],\n [\"ARTIFACT_URL\", artifactUrl],\n [\"KEYSTROKE_MODE\", mode],\n [\"PROJECT_ID\", database.projectId],\n [\"ORGANIZATION_ID\", database.organizationId],\n [\"TENANT_ID\", database.organizationId],\n ]);\n\n for (const key of RUNTIME_ENV_KEYS) {\n const value = source[key]?.trim();\n if (!value) {\n if (key === \"POSTGRES_SSL\") {\n throw new Error(\"POSTGRES_SSL is required\");\n }\n continue;\n }\n entries.set(key, value);\n }\n\n if (mode !== \"worker\") {\n for (const key of LOCAL_PROVIDER_ENV_KEYS) {\n const value = source[key]?.trim();\n if (value) {\n entries.set(key, value);\n }\n }\n }\n\n if (mode !== \"worker\") {\n const credentialsKey = source.CREDENTIALS_ENCRYPTION_KEY;\n if (credentialsKey) {\n entries.set(\"CREDENTIALS_ENCRYPTION_KEY\", credentialsKey);\n }\n }\n\n if (mode === \"worker\") {\n const worker = resolveWorkerRuntimeConfig(source);\n entries.set(\"PLATFORM_URL\", worker.platformUrl);\n entries.set(\"WORKER_INTERNAL_TOKEN\", worker.workerToken);\n entries.set(\n \"AI_GATEWAY_BASE_URL\",\n resolveLlmGatewayBaseUrl(worker.platformUrl, database.projectId),\n );\n }\n\n for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) {\n entries.set(key, value);\n }\n\n return Object.fromEntries(entries);\n}\n\nexport function resolveLlmGatewayBaseUrl(platformUrl: string, projectId: string): string {\n return `${platformUrl.replace(/\\/$/, \"\")}/internal/projects/${encodeURIComponent(projectId)}/llm`;\n}\n\nexport function resolvePlatformWorkerToken(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();\n if (explicit) {\n return explicit;\n }\n\n if (source.NODE_ENV === \"production\") {\n return undefined;\n }\n\n return DEV_PLATFORM_WORKER_TOKEN;\n}\n\n/** Platform API URL reachable from a project-server container. */\nconst DEV_PLATFORM_URL = \"http://localhost:3002\";\n\nexport function resolveWorkerPlatformUrl(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const raw =\n source.PLATFORM_URL?.trim() ??\n source.PUBLIC_PLATFORM_URL?.trim() ??\n (source.NODE_ENV === \"production\" ? undefined : DEV_PLATFORM_URL);\n if (!raw) {\n return undefined;\n }\n\n return rewriteLoopbackUrl(raw);\n}\n\n/** Rewrite loopback hosts so containers can reach Postgres and other host services. */\nexport function rewriteLoopbackHost(host: string): string {\n if (host === \"localhost\" || host === \"127.0.0.1\") {\n return \"host.docker.internal\";\n }\n\n return host;\n}\n\nexport function rewriteLoopbackUrl(url: string): string {\n try {\n const parsed = new URL(url);\n parsed.hostname = rewriteLoopbackHost(parsed.hostname);\n return parsed.toString().replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n}\n\n/** dockerode expects `KEY=value` strings. */\nexport function formatDockerEnv(env: Record<string, string>): string[] {\n return Object.entries(env).map(([key, value]) => `${key}=${value}`);\n}\n\nexport function resolveProjectServerImage(\n env: NodeJS.ProcessEnv = process.env,\n override?: string,\n): string {\n return override ?? env.PROJECT_DOCKER_IMAGE ?? env.TENANT_DOCKER_IMAGE ?? PROJECT_SERVER_IMAGE;\n}\n","import { resolvePlatformWorkerToken, resolveWorkerPlatformUrl } from \"./runtime-env\";\n\n/** Resolved platform ↔ project-server worker auth contract. */\nexport type WorkerRuntimeConfig = {\n /** Platform API base URL reachable from the project-server runtime. */\n platformUrl: string;\n /** Bearer token for platform `/internal` routes. */\n workerToken: string;\n};\n\nexport class WorkerRuntimeConfigError extends Error {\n readonly missing: readonly string[];\n\n constructor(missing: string[]) {\n super(\n `Worker runtime config incomplete (missing: ${missing.join(\", \")}). ` +\n \"In production set PLATFORM_WORKER_TOKEN and PUBLIC_PLATFORM_URL (or PLATFORM_URL).\",\n );\n this.name = \"WorkerRuntimeConfigError\";\n this.missing = missing;\n }\n}\n\n/** Fail fast when the platform cannot start worker-mode project servers. */\nexport function resolveWorkerRuntimeConfig(\n source: NodeJS.ProcessEnv = process.env,\n): WorkerRuntimeConfig {\n const platformUrl = resolveWorkerPlatformUrl(source);\n const workerToken = resolvePlatformWorkerToken(source);\n const missing: string[] = [];\n\n if (!platformUrl) {\n missing.push(\"PUBLIC_PLATFORM_URL or PLATFORM_URL\");\n }\n\n if (!workerToken) {\n missing.push(\"PLATFORM_WORKER_TOKEN\");\n }\n\n if (missing.length > 0) {\n throw new WorkerRuntimeConfigError(missing);\n }\n\n return { platformUrl: platformUrl!, workerToken: workerToken! };\n}\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_INTERVAL_MS = 500;\n\nexport type WaitForHealthOptions = {\n timeoutMs?: number;\n intervalMs?: number;\n fetchImpl?: typeof fetch;\n};\n\nexport async function waitForHealth(\n baseUrl: string,\n options: WaitForHealthOptions = {},\n): Promise<void> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n const deadline = Date.now() + timeoutMs;\n const url = new URL(PROJECT_SERVER_HEALTH.path, baseUrl);\n\n while (Date.now() < deadline) {\n try {\n const response = await fetchImpl(url, { signal: AbortSignal.timeout(2_000) });\n if (response.ok) {\n return;\n }\n } catch {\n // retry until timeout\n }\n\n await sleep(intervalMs);\n }\n\n throw new Error(`Project server health check timed out after ${timeoutMs}ms (${url})`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","import Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n const docker = options.docker ?? new Docker();\n\n return {\n async start(input) {\n const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database);\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.projectId);\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.projectId),\n Labels: {\n \"keystroke.project.id\": input.projectId,\n \"keystroke.project.name\": input.name,\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve project container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(projectId: string): string {\n return `keystroke-project-${projectId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, projectId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(projectId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n","/** Timeout for a single project runtime `/health` probe. */\nexport const RUNTIME_PING_TIMEOUT_MS = 15_000;\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\nimport type { HostingPlugin } from \"./plugin\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport type PingProjectOptions = {\n runtimeId?: string | null;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n plugin?: Pick<HostingPlugin, \"pingRequestHeaders\">;\n};\n\nexport async function pingProject(\n baseUrl: string,\n options: PingProjectOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers = options.plugin?.pingRequestHeaders?.(options.runtimeId ?? null) ?? {};\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_HEALTH.path, baseUrl),\n {\n signal: AbortSignal.timeout(timeoutMs),\n headers,\n },\n );\n\n return response.ok;\n } catch {\n return false;\n }\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport type { ProjectPingTarget } from \"./runtime\";\nimport { pingProject, type PingProjectOptions } from \"./ping-project\";\n\nexport function canPingProjectTarget(\n target: ProjectPingTarget,\n plugin?: Pick<HostingPlugin, \"canPingTarget\">,\n): target is { baseUrl: string; runtimeId: string | null } {\n if (plugin?.canPingTarget) {\n return plugin.canPingTarget(target);\n }\n\n return !!target.baseUrl;\n}\n\nexport async function pingProjectTarget(\n target: ProjectPingTarget,\n options: PingProjectOptions & {\n plugin?: Pick<HostingPlugin, \"canPingTarget\" | \"pingRequestHeaders\">;\n } = {},\n): Promise<boolean> {\n if (!canPingProjectTarget(target, options.plugin)) {\n return false;\n }\n\n return pingProject(target.baseUrl, {\n ...options,\n runtimeId: target.runtimeId,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/constants.ts","../src/runtime-env.ts","../src/worker-runtime-config.ts","../src/wait-for-health.ts","../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts"],"sourcesContent":["export const PROJECT_SERVER_IMAGE = \"keystroke/project-server:local\";\n\nexport const PROJECT_SERVER_PORT = 3000;\n\nexport const PROJECT_SERVER_ROOT = \"/app\";\n\nexport const PROJECT_SERVER_FRAMEWORK_ROOT = \"/opt/keystroke\";\n\nexport const PROJECT_SERVER_FRAMEWORK_NODE_MODULES = `${PROJECT_SERVER_FRAMEWORK_ROOT}/node_modules`;\n\nexport const PROJECT_SERVER_HEALTH = {\n path: \"/health\",\n port: PROJECT_SERVER_PORT,\n} as const;\n","import type { ProjectRuntimeDatabase } from \"./runtime\";\nimport {\n PROJECT_SERVER_FRAMEWORK_NODE_MODULES,\n PROJECT_SERVER_HEALTH,\n PROJECT_SERVER_IMAGE,\n PROJECT_SERVER_PORT,\n PROJECT_SERVER_ROOT,\n} from \"./constants\";\nimport type { HostingOptions } from \"./plugin\";\nimport { resolveWorkerRuntimeConfig } from \"./worker-runtime-config\";\n\nconst RUNTIME_ENV_KEYS = [\n \"BETTER_AUTH_SECRET\",\n \"PUBLIC_SERVER_URL\",\n \"PUBLIC_WEB_URL\",\n \"POSTGRES_SSL\",\n // Scheduler backend selector (forwarded so the container consumer matches the\n // platform producer).\n \"KEYSTROKE_SCHEDULER_MODULE\",\n] as const;\n\n/** Provider and gateway env forwarded from the platform into project servers. */\nconst PROVIDER_ENV_KEYS = [\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"AI_GATEWAY_API_KEY\",\n \"AI_GATEWAY_BASE_URL\",\n] as const;\n\n/** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */\nexport const DEV_PLATFORM_WORKER_TOKEN = \"dev-platform-worker-token\";\n\nexport const PROJECT_DATABASE_ENV_KEYS = [\n \"POSTGRES_HOST\",\n \"POSTGRES_PORT\",\n \"POSTGRES_DB\",\n \"POSTGRES_USER\",\n \"POSTGRES_PASSWORD\",\n \"DATABASE_SEARCH_PATH\",\n] as const;\n\nexport type RuntimeLaunchSpec = {\n image: string;\n env: Record<string, string>;\n port: number;\n health: typeof PROJECT_SERVER_HEALTH;\n};\n\n/** Per-project postgres env injected into every project server container/machine. */\nexport function projectDatabaseRuntimeEnv(\n database: ProjectRuntimeDatabase,\n): Record<(typeof PROJECT_DATABASE_ENV_KEYS)[number], string> {\n return {\n POSTGRES_HOST: rewriteLoopbackHost(database.host),\n POSTGRES_PORT: String(database.port),\n POSTGRES_DB: database.database,\n POSTGRES_USER: database.user,\n POSTGRES_PASSWORD: database.password,\n DATABASE_SEARCH_PATH: database.searchPath,\n };\n}\n\n/**\n * Builds the launch spec for a single project server. `artifactUrl` is the\n * per-project presigned URL to that deploy's built `dist` — there is no base\n * image, so it is required.\n */\nexport function resolveRuntimeLaunch(\n options: HostingOptions,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n extraEnv?: Record<string, string>,\n): RuntimeLaunchSpec {\n if (!artifactUrl) {\n throw new Error(\"Cannot start a project server without an artifact URL\");\n }\n\n const env = options.env ?? process.env;\n\n return {\n image: resolveProjectServerImage(env, options.image),\n env: buildRuntimeEnv(env, artifactUrl, database, extraEnv),\n port: PROJECT_SERVER_PORT,\n health: PROJECT_SERVER_HEALTH,\n };\n}\n\n/** Env vars passed into the project server container/machine. */\nexport function buildRuntimeEnv(\n source: NodeJS.ProcessEnv,\n artifactUrl: string,\n database: ProjectRuntimeDatabase,\n extraEnv?: Record<string, string>,\n): Record<string, string> {\n const mode = source.KEYSTROKE_MODE ?? \"worker\";\n const entries = new Map<string, string>([\n // Container bind port — not derived from PUBLIC_SERVER_URL (external URL metadata).\n [\"PORT\", String(PROJECT_SERVER_PORT)],\n [\"KEYSTROKE_ROOT\", PROJECT_SERVER_ROOT],\n [\"KEYSTROKE_RUNTIME_NODE_MODULES\", PROJECT_SERVER_FRAMEWORK_NODE_MODULES],\n [\"ARTIFACT_URL\", artifactUrl],\n [\"KEYSTROKE_MODE\", mode],\n [\"PROJECT_ID\", database.projectId],\n [\"ORGANIZATION_ID\", database.organizationId],\n [\"TENANT_ID\", database.organizationId],\n ]);\n\n for (const key of RUNTIME_ENV_KEYS) {\n const value = source[key]?.trim();\n if (!value) {\n if (key === \"POSTGRES_SSL\") {\n throw new Error(\"POSTGRES_SSL is required\");\n }\n continue;\n }\n entries.set(key, value);\n }\n\n for (const key of PROVIDER_ENV_KEYS) {\n const value = source[key]?.trim();\n if (value) {\n entries.set(key, value);\n }\n }\n\n if (mode !== \"worker\") {\n const credentialsKey = source.CREDENTIALS_ENCRYPTION_KEY;\n if (credentialsKey) {\n entries.set(\"CREDENTIALS_ENCRYPTION_KEY\", credentialsKey);\n }\n }\n\n if (mode === \"worker\") {\n const worker = resolveWorkerRuntimeConfig(source);\n entries.set(\"PLATFORM_URL\", worker.platformUrl);\n entries.set(\"WORKER_INTERNAL_TOKEN\", worker.workerToken);\n }\n\n for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) {\n entries.set(key, value);\n }\n\n if (extraEnv) {\n for (const [key, value] of Object.entries(extraEnv)) {\n entries.set(key, value);\n }\n }\n\n return Object.fromEntries(entries);\n}\n\nexport function resolvePlatformWorkerToken(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();\n if (explicit) {\n return explicit;\n }\n\n if (source.NODE_ENV === \"production\") {\n return undefined;\n }\n\n return DEV_PLATFORM_WORKER_TOKEN;\n}\n\n/** Platform API URL reachable from a project-server container. */\nconst DEV_PLATFORM_URL = \"http://localhost:3002\";\n\nexport function resolveWorkerPlatformUrl(\n source: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const raw =\n source.PLATFORM_URL?.trim() ??\n source.PUBLIC_PLATFORM_URL?.trim() ??\n (source.NODE_ENV === \"production\" ? undefined : DEV_PLATFORM_URL);\n if (!raw) {\n return undefined;\n }\n\n return rewriteLoopbackUrl(raw);\n}\n\n/** Rewrite loopback hosts so containers can reach Postgres and other host services. */\nexport function rewriteLoopbackHost(host: string): string {\n if (host === \"localhost\" || host === \"127.0.0.1\") {\n return \"host.docker.internal\";\n }\n\n return host;\n}\n\nexport function rewriteLoopbackUrl(url: string): string {\n try {\n const parsed = new URL(url);\n parsed.hostname = rewriteLoopbackHost(parsed.hostname);\n return parsed.toString().replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n}\n\n/** dockerode expects `KEY=value` strings. */\nexport function formatDockerEnv(env: Record<string, string>): string[] {\n return Object.entries(env).map(([key, value]) => `${key}=${value}`);\n}\n\nexport function resolveProjectServerImage(\n env: NodeJS.ProcessEnv = process.env,\n override?: string,\n): string {\n return override ?? env.PROJECT_DOCKER_IMAGE ?? env.TENANT_DOCKER_IMAGE ?? PROJECT_SERVER_IMAGE;\n}\n","import { resolvePlatformWorkerToken, resolveWorkerPlatformUrl } from \"./runtime-env\";\n\n/** Resolved platform ↔ project-server worker auth contract. */\nexport type WorkerRuntimeConfig = {\n /** Platform API base URL reachable from the project-server runtime. */\n platformUrl: string;\n /** Bearer token for platform `/internal` routes. */\n workerToken: string;\n};\n\nexport class WorkerRuntimeConfigError extends Error {\n readonly missing: readonly string[];\n\n constructor(missing: string[]) {\n super(\n `Worker runtime config incomplete (missing: ${missing.join(\", \")}). ` +\n \"In production set PLATFORM_WORKER_TOKEN and PUBLIC_PLATFORM_URL (or PLATFORM_URL).\",\n );\n this.name = \"WorkerRuntimeConfigError\";\n this.missing = missing;\n }\n}\n\n/** Fail fast when the platform cannot start worker-mode project servers. */\nexport function resolveWorkerRuntimeConfig(\n source: NodeJS.ProcessEnv = process.env,\n): WorkerRuntimeConfig {\n const platformUrl = resolveWorkerPlatformUrl(source);\n const workerToken = resolvePlatformWorkerToken(source);\n const missing: string[] = [];\n\n if (!platformUrl) {\n missing.push(\"PUBLIC_PLATFORM_URL or PLATFORM_URL\");\n }\n\n if (!workerToken) {\n missing.push(\"PLATFORM_WORKER_TOKEN\");\n }\n\n if (missing.length > 0) {\n throw new WorkerRuntimeConfigError(missing);\n }\n\n return { platformUrl: platformUrl!, workerToken: workerToken! };\n}\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_INTERVAL_MS = 500;\n\nexport type WaitForHealthOptions = {\n timeoutMs?: number;\n intervalMs?: number;\n fetchImpl?: typeof fetch;\n};\n\nexport async function waitForHealth(\n baseUrl: string,\n options: WaitForHealthOptions = {},\n): Promise<void> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n const deadline = Date.now() + timeoutMs;\n const url = new URL(PROJECT_SERVER_HEALTH.path, baseUrl);\n\n while (Date.now() < deadline) {\n try {\n const response = await fetchImpl(url, { signal: AbortSignal.timeout(2_000) });\n if (response.ok) {\n return;\n }\n } catch {\n // retry until timeout\n }\n\n await sleep(intervalMs);\n }\n\n throw new Error(`Project server health check timed out after ${timeoutMs}ms (${url})`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","import Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n const docker = options.docker ?? new Docker();\n\n return {\n async start(input) {\n const launch = resolveRuntimeLaunch(options, input.artifactUrl, input.database, input.env);\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.projectId);\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.projectId),\n Labels: {\n \"keystroke.project.id\": input.projectId,\n \"keystroke.project.name\": input.name,\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve project container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(projectId: string): string {\n return `keystroke-project-${projectId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, projectId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(projectId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n","/** Timeout for a single project runtime `/health` probe. */\nexport const RUNTIME_PING_TIMEOUT_MS = 15_000;\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\nimport type { HostingPlugin } from \"./plugin\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport type PingProjectOptions = {\n runtimeId?: string | null;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n plugin?: Pick<HostingPlugin, \"pingRequestHeaders\">;\n};\n\nexport async function pingProject(\n baseUrl: string,\n options: PingProjectOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers = options.plugin?.pingRequestHeaders?.(options.runtimeId ?? null) ?? {};\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_HEALTH.path, baseUrl),\n {\n signal: AbortSignal.timeout(timeoutMs),\n headers,\n },\n );\n\n return response.ok;\n } catch {\n return false;\n }\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport type { ProjectPingTarget } from \"./runtime\";\nimport { pingProject, type PingProjectOptions } from \"./ping-project\";\n\nexport function canPingProjectTarget(\n target: ProjectPingTarget,\n plugin?: Pick<HostingPlugin, \"canPingTarget\">,\n): target is { baseUrl: string; runtimeId: string | null } {\n if (plugin?.canPingTarget) {\n return plugin.canPingTarget(target);\n }\n\n return !!target.baseUrl;\n}\n\nexport async function pingProjectTarget(\n target: ProjectPingTarget,\n options: PingProjectOptions & {\n plugin?: Pick<HostingPlugin, \"canPingTarget\" | \"pingRequestHeaders\">;\n } = {},\n): Promise<boolean> {\n if (!canPingProjectTarget(target, options.plugin)) {\n return false;\n }\n\n return pingProject(target.baseUrl, {\n ...options,\n runtimeId: target.runtimeId,\n });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keystrokehq/hosting",
3
- "version": "0.0.112",
3
+ "version": "0.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/dallinbentley/keystroke.git",
@@ -37,7 +37,7 @@
37
37
  "typescript": "^6.0.3",
38
38
  "vitest": "^4.1.7",
39
39
  "@keystrokehq/oxlint-config": "0.0.3",
40
- "@keystrokehq/shared": "0.0.61",
40
+ "@keystrokehq/shared": "0.0.62",
41
41
  "@keystrokehq/tsconfig": "0.0.3",
42
42
  "@keystrokehq/tsdown-config": "0.0.3",
43
43
  "@keystrokehq/vitest-config": "0.0.4"