@keystrokehq/hosting 0.0.68 → 0.0.74

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,7 +42,8 @@ const RUNTIME_ENV_KEYS = [
42
42
  "ANTHROPIC_API_KEY",
43
43
  "OPENAI_API_KEY"
44
44
  ];
45
- const WORKER_RUNTIME_ENV_KEYS = ["PLATFORM_URL", "PLATFORM_WORKER_TOKEN"];
45
+ /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
46
+ const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
46
47
  const PROJECT_DATABASE_ENV_KEYS = [
47
48
  "POSTGRES_HOST",
48
49
  "POSTGRES_PORT",
@@ -99,23 +100,40 @@ function buildRuntimeEnv(source, artifactUrl, database) {
99
100
  if (credentialsKey) entries.set("CREDENTIALS_ENCRYPTION_KEY", credentialsKey);
100
101
  }
101
102
  if (mode === "worker") {
102
- for (const key of WORKER_RUNTIME_ENV_KEYS) {
103
- const value = source[key];
104
- if (value) entries.set(key, value);
105
- }
106
- const platformUrl = source.PLATFORM_URL ?? source.PUBLIC_WEB_URL;
107
- if (platformUrl) entries.set("PLATFORM_URL", platformUrl);
108
- const workerToken = source.PLATFORM_WORKER_TOKEN ?? source.WORKER_INTERNAL_TOKEN;
109
- if (workerToken) entries.set("WORKER_INTERNAL_TOKEN", workerToken);
103
+ const worker = resolveWorkerRuntimeConfig(source);
104
+ entries.set("PLATFORM_URL", worker.platformUrl);
105
+ entries.set("WORKER_INTERNAL_TOKEN", worker.workerToken);
110
106
  }
111
107
  for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) entries.set(key, value);
112
108
  return Object.fromEntries(entries);
113
109
  }
110
+ function resolvePlatformWorkerToken(source = process.env) {
111
+ const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();
112
+ if (explicit) return explicit;
113
+ if (source.NODE_ENV === "production") return;
114
+ return DEV_PLATFORM_WORKER_TOKEN;
115
+ }
116
+ /** Platform API URL reachable from a project-server container. */
117
+ const DEV_PLATFORM_URL = "http://localhost:3002";
118
+ function resolveWorkerPlatformUrl(source = process.env) {
119
+ const raw = source.PLATFORM_URL?.trim() ?? source.PUBLIC_PLATFORM_URL?.trim() ?? (source.NODE_ENV === "production" ? void 0 : DEV_PLATFORM_URL);
120
+ if (!raw) return;
121
+ return rewriteLoopbackUrl(raw);
122
+ }
114
123
  /** Rewrite loopback hosts so containers can reach Postgres and other host services. */
115
124
  function rewriteLoopbackHost(host) {
116
125
  if (host === "localhost" || host === "127.0.0.1") return "host.docker.internal";
117
126
  return host;
118
127
  }
128
+ function rewriteLoopbackUrl(url) {
129
+ try {
130
+ const parsed = new URL(url);
131
+ parsed.hostname = rewriteLoopbackHost(parsed.hostname);
132
+ return parsed.toString().replace(/\/$/, "");
133
+ } catch {
134
+ return url;
135
+ }
136
+ }
119
137
  /** dockerode expects `KEY=value` strings. */
120
138
  function formatDockerEnv(env) {
121
139
  return Object.entries(env).map(([key, value]) => `${key}=${value}`);
@@ -124,6 +142,29 @@ function resolveProjectServerImage(env = process.env, override) {
124
142
  return override ?? env.PROJECT_DOCKER_IMAGE ?? env.TENANT_DOCKER_IMAGE ?? "keystroke/project-server:local";
125
143
  }
126
144
  //#endregion
145
+ //#region src/worker-runtime-config.ts
146
+ var WorkerRuntimeConfigError = class extends Error {
147
+ missing;
148
+ constructor(missing) {
149
+ super(`Worker runtime config incomplete (missing: ${missing.join(", ")}). In production set PLATFORM_WORKER_TOKEN and PUBLIC_PLATFORM_URL (or PLATFORM_URL).`);
150
+ this.name = "WorkerRuntimeConfigError";
151
+ this.missing = missing;
152
+ }
153
+ };
154
+ /** Fail fast when the platform cannot start worker-mode project servers. */
155
+ function resolveWorkerRuntimeConfig(source = process.env) {
156
+ const platformUrl = resolveWorkerPlatformUrl(source);
157
+ const workerToken = resolvePlatformWorkerToken(source);
158
+ const missing = [];
159
+ if (!platformUrl) missing.push("PUBLIC_PLATFORM_URL or PLATFORM_URL");
160
+ if (!workerToken) missing.push("PLATFORM_WORKER_TOKEN");
161
+ if (missing.length > 0) throw new WorkerRuntimeConfigError(missing);
162
+ return {
163
+ platformUrl,
164
+ workerToken
165
+ };
166
+ }
167
+ //#endregion
127
168
  //#region src/wait-for-health.ts
128
169
  const DEFAULT_TIMEOUT_MS = 12e4;
129
170
  const DEFAULT_INTERVAL_MS = 500;
@@ -223,9 +264,14 @@ async function ensureImage(docker, image) {
223
264
  //#endregion
224
265
  //#region src/docker/plugin.ts
225
266
  function dockerPlugin(options = {}) {
267
+ const env = options.env ?? process.env;
226
268
  return {
227
269
  name: "docker",
228
- createRuntime: () => createDockerRuntime(options)
270
+ workerRuntime: resolveWorkerRuntimeConfig(env),
271
+ createRuntime: () => createDockerRuntime({
272
+ ...options,
273
+ env
274
+ })
229
275
  };
230
276
  }
231
277
  //#endregion
@@ -266,6 +312,7 @@ async function pingProjectTarget(target, options = {}) {
266
312
  });
267
313
  }
268
314
  //#endregion
315
+ exports.DEV_PLATFORM_WORKER_TOKEN = DEV_PLATFORM_WORKER_TOKEN;
269
316
  exports.PROJECT_DATABASE_ENV_KEYS = PROJECT_DATABASE_ENV_KEYS;
270
317
  exports.PROJECT_SERVER_FRAMEWORK_NODE_MODULES = PROJECT_SERVER_FRAMEWORK_NODE_MODULES;
271
318
  exports.PROJECT_SERVER_FRAMEWORK_ROOT = PROJECT_SERVER_FRAMEWORK_ROOT;
@@ -274,6 +321,7 @@ exports.PROJECT_SERVER_IMAGE = PROJECT_SERVER_IMAGE;
274
321
  exports.PROJECT_SERVER_PORT = PROJECT_SERVER_PORT;
275
322
  exports.PROJECT_SERVER_ROOT = PROJECT_SERVER_ROOT;
276
323
  exports.RUNTIME_PING_TIMEOUT_MS = RUNTIME_PING_TIMEOUT_MS;
324
+ exports.WorkerRuntimeConfigError = WorkerRuntimeConfigError;
277
325
  exports.buildRuntimeEnv = buildRuntimeEnv;
278
326
  exports.canPingProjectTarget = canPingProjectTarget;
279
327
  exports.defaultHostingPlugin = defaultHostingPlugin;
@@ -282,9 +330,13 @@ exports.formatDockerEnv = formatDockerEnv;
282
330
  exports.pingProject = pingProject;
283
331
  exports.pingProjectTarget = pingProjectTarget;
284
332
  exports.projectDatabaseRuntimeEnv = projectDatabaseRuntimeEnv;
333
+ exports.resolvePlatformWorkerToken = resolvePlatformWorkerToken;
285
334
  exports.resolveProjectServerImage = resolveProjectServerImage;
286
335
  exports.resolveRuntimeLaunch = resolveRuntimeLaunch;
336
+ exports.resolveWorkerPlatformUrl = resolveWorkerPlatformUrl;
337
+ exports.resolveWorkerRuntimeConfig = resolveWorkerRuntimeConfig;
287
338
  exports.rewriteLoopbackHost = rewriteLoopbackHost;
339
+ exports.rewriteLoopbackUrl = rewriteLoopbackUrl;
288
340
  exports.waitForHealth = waitForHealth;
289
341
 
290
342
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Docker"],"sources":["../src/constants.ts","../src/runtime-env.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\";\n\nconst RUNTIME_ENV_KEYS = [\n \"BETTER_AUTH_SECRET\",\n \"PUBLIC_SERVER_URL\",\n \"PUBLIC_WEB_URL\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n] as const;\n\nconst WORKER_RUNTIME_ENV_KEYS = [\"PLATFORM_URL\", \"PLATFORM_WORKER_TOKEN\"] as const;\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];\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 for (const key of WORKER_RUNTIME_ENV_KEYS) {\n const value = source[key];\n if (value) {\n entries.set(key, value);\n }\n }\n\n const platformUrl = source.PLATFORM_URL ?? source.PUBLIC_WEB_URL;\n if (platformUrl) {\n entries.set(\"PLATFORM_URL\", platformUrl);\n }\n\n const workerToken = source.PLATFORM_WORKER_TOKEN ?? source.WORKER_INTERNAL_TOKEN;\n if (workerToken) {\n entries.set(\"WORKER_INTERNAL_TOKEN\", workerToken);\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\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\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 { 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\";\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 return {\n name: \"docker\",\n createRuntime: () => createDockerRuntime(options),\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 \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\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): 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];\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 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);\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
@@ -44,6 +44,19 @@ type ProjectPingTarget = {
44
44
  runtimeId: string | null;
45
45
  };
46
46
  //#endregion
47
+ //#region src/worker-runtime-config.d.ts
48
+ /** Resolved platform ↔ project-server worker auth contract. */
49
+ type WorkerRuntimeConfig = {
50
+ /** Platform API base URL reachable from the project-server runtime. */platformUrl: string; /** Bearer token for platform `/internal` routes. */
51
+ workerToken: string;
52
+ };
53
+ declare class WorkerRuntimeConfigError extends Error {
54
+ readonly missing: readonly string[];
55
+ constructor(missing: string[]);
56
+ }
57
+ /** Fail fast when the platform cannot start worker-mode project servers. */
58
+ declare function resolveWorkerRuntimeConfig(source?: NodeJS.ProcessEnv): WorkerRuntimeConfig;
59
+ //#endregion
47
60
  //#region src/plugin.d.ts
48
61
  type HostingOptions = {
49
62
  image?: string;
@@ -60,7 +73,8 @@ type OrganizationHostingResult = {
60
73
  appName: string;
61
74
  };
62
75
  type HostingPlugin = {
63
- name: string;
76
+ name: string; /** Validated at plugin creation — shared by platform internal routes and project-server containers. */
77
+ workerRuntime: WorkerRuntimeConfig;
64
78
  createRuntime(): ProjectRuntime;
65
79
  provisionOrganization?(input: OrganizationHostingInput): Promise<OrganizationHostingResult>;
66
80
  deprovisionOrganization?(result: OrganizationHostingResult): Promise<void>;
@@ -84,6 +98,8 @@ declare function dockerPlugin(options?: DockerPluginOptions): HostingPlugin;
84
98
  declare function defaultHostingPlugin(options?: DockerPluginOptions): HostingPlugin;
85
99
  //#endregion
86
100
  //#region src/runtime-env.d.ts
101
+ /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
102
+ declare const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
87
103
  declare const PROJECT_DATABASE_ENV_KEYS: readonly ["POSTGRES_HOST", "POSTGRES_PORT", "POSTGRES_DB", "POSTGRES_USER", "POSTGRES_PASSWORD", "DATABASE_SEARCH_PATH"];
88
104
  type RuntimeLaunchSpec = {
89
105
  image: string;
@@ -101,8 +117,11 @@ declare function projectDatabaseRuntimeEnv(database: ProjectRuntimeDatabase): Re
101
117
  declare function resolveRuntimeLaunch(options: HostingOptions, artifactUrl: string, database: ProjectRuntimeDatabase): RuntimeLaunchSpec;
102
118
  /** Env vars passed into the project server container/machine. */
103
119
  declare function buildRuntimeEnv(source: NodeJS.ProcessEnv, artifactUrl: string, database: ProjectRuntimeDatabase): Record<string, string>;
120
+ declare function resolvePlatformWorkerToken(source?: NodeJS.ProcessEnv): string | undefined;
121
+ declare function resolveWorkerPlatformUrl(source?: NodeJS.ProcessEnv): string | undefined;
104
122
  /** Rewrite loopback hosts so containers can reach Postgres and other host services. */
105
123
  declare function rewriteLoopbackHost(host: string): string;
124
+ declare function rewriteLoopbackUrl(url: string): string;
106
125
  /** dockerode expects `KEY=value` strings. */
107
126
  declare function formatDockerEnv(env: Record<string, string>): string[];
108
127
  declare function resolveProjectServerImage(env?: NodeJS.ProcessEnv, override?: string): string;
@@ -137,5 +156,5 @@ declare function pingProjectTarget(target: ProjectPingTarget, options?: PingProj
137
156
  plugin?: Pick<HostingPlugin, "canPingTarget" | "pingRequestHeaders">;
138
157
  }): Promise<boolean>;
139
158
  //#endregion
140
- export { 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 ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type WaitForHealthOptions, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolveProjectServerImage, resolveRuntimeLaunch, rewriteLoopbackHost, waitForHealth };
159
+ 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 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 };
141
160
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/constants.ts","../src/runtime.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":";;;cAAa,oBAAA;AAAA,cAEA,mBAAA;AAAA,cAEA,mBAAA;AAAA,cAEA,6BAAA;AAAA,cAEA,qCAAA;AAAA,cAEA,qBAAA;EAAA,SAGH,IAAA;EAAA,SAAA,IAAA;AAAA;;;KCbE,sBAAA;EACV,IAAA;EACA,IAAA;EACA,QAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;EACA,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,qBAAA;EACV,OAAO;AAAA;AAAA,KAGG,mBAAA;EACV,SAAA;EACA,IAAA;EACA,WAAA;EACA,QAAA,EAAU,sBAAA;EACV,OAAA,GAAU,qBAAqB;AAAA;AAAA,KAGrB,oBAAA;EACV,SAAA;EACA,OAAO;AAAA;AAAA,KAGG,cAAA;EACV,KAAA,CAAM,KAAA,EAAO,mBAAA,GAAsB,OAAA,CAAQ,oBAAA;AAAA;AAAA,KAGjC,iBAAA;EACV,OAAA;EACA,SAAS;AAAA;;;KChCC,cAAA;EACV,KAAA;EACA,GAAA,GAAM,MAAA,CAAO,UAAA;EACb,SAAA,UAAmB,KAAK;EACxB,eAAA;AAAA;AAAA,KAGU,wBAAA;EACV,cAAA;EACA,IAAA;EACA,IAAA;AAAA;AAAA,KAGU,yBAAA;EACV,OAAO;AAAA;AAAA,KAGG,aAAA;EACV,IAAA;EACA,aAAA,IAAiB,cAAA;EACjB,qBAAA,EAAuB,KAAA,EAAO,wBAAA,GAA2B,OAAA,CAAQ,yBAAA;EACjE,uBAAA,EAAyB,MAAA,EAAQ,yBAAA,GAA4B,OAAA;EAC7D,aAAA,EACE,MAAA,EAAQ,iBAAA,GACP,MAAA;IAAY,OAAA;IAAiB,SAAA;EAAA;EAChC,kBAAA,EAAoB,SAAA,kBAA2B,MAAA;AAAA;;;KCnBrC,mBAAA,GAAsB,cAAA;EAChC,MAAA,GAAS,MAAM;AAAA;;;iBCFD,YAAA,CAAa,OAAA,GAAS,mBAAA,GAA2B,aAAa;;;;iBCH9D,oBAAA,CAAqB,OAAA,GAAS,mBAAA,GAA2B,aAAa;;;cCgBzE,yBAAA;AAAA,KASD,iBAAA;EACV,KAAA;EACA,GAAA,EAAK,MAAA;EACL,IAAA;EACA,MAAA,SAAe,qBAAqB;AAAA;;iBAItB,yBAAA,CACd,QAAA,EAAU,sBAAA,GACT,MAAA,SAAe,yBAAA;ANrCc;AAEhC;;;;AAFgC,iBMqDhB,oBAAA,CACd,OAAA,EAAS,cAAA,EACT,WAAA,UACA,QAAA,EAAU,sBAAA,GACT,iBAAA;ANrDH;AAAA,iBMqEgB,eAAA,CACd,MAAA,EAAQ,MAAA,CAAO,UAAA,EACf,WAAA,UACA,QAAA,EAAU,sBAAA,GACT,MAAA;;iBAuDa,mBAAA,CAAoB,IAAY;ANhIN;AAAA,iBMyI1B,eAAA,CAAgB,GAA2B,EAAtB,MAAM;AAAA,iBAI3B,yBAAA,CACd,GAAA,GAAK,MAAA,CAAO,UAAwB,EACpC,QAAA;;;KChJU,oBAAA;EACV,SAAA;EACA,UAAA;EACA,SAAA,UAAmB,KAAK;AAAA;AAAA,iBAGJ,aAAA,CACpB,OAAA,UACA,OAAA,GAAS,oBAAA,GACR,OAAO;;;;cCbG,uBAAA;;;KCGD,kBAAA;EACV,SAAA;EACA,SAAA,UAAmB,KAAA;EACnB,SAAA;EACA,MAAA,GAAS,IAAA,CAAK,aAAA;AAAA;AAAA,iBAGM,WAAA,CACpB,OAAA,UACA,OAAA,GAAS,kBAAA,GACR,OAAO;;;iBCVM,oBAAA,CACd,MAAA,EAAQ,iBAAA,EACR,MAAA,GAAS,IAAA,CAAK,aAAA,qBACb,MAAA;EAAY,OAAA;EAAiB,SAAA;AAAA;AAAA,iBAQV,iBAAA,CACpB,MAAA,EAAQ,iBAAA,EACR,OAAA,GAAS,kBAAA;EACP,MAAA,GAAS,IAAA,CAAK,aAAA;AAAA,IAEf,OAAA"}
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":";;;cAAa,oBAAA;AAAA,cAEA,mBAAA;AAAA,cAEA,mBAAA;AAAA,cAEA,6BAAA;AAAA,cAEA,qCAAA;AAAA,cAEA,qBAAA;EAAA,SAGH,IAAA;EAAA,SAAA,IAAA;AAAA;;;KCbE,sBAAA;EACV,IAAA;EACA,IAAA;EACA,QAAA;EACA,IAAA;EACA,QAAA;EACA,UAAA;EACA,SAAA;EACA,cAAA;AAAA;AAAA,KAGU,qBAAA;EACV,OAAO;AAAA;AAAA,KAGG,mBAAA;EACV,SAAA;EACA,IAAA;EACA,WAAA;EACA,QAAA,EAAU,sBAAA;EACV,OAAA,GAAU,qBAAqB;AAAA;AAAA,KAGrB,oBAAA;EACV,SAAA;EACA,OAAO;AAAA;AAAA,KAGG,cAAA;EACV,KAAA,CAAM,KAAA,EAAO,mBAAA,GAAsB,OAAA,CAAQ,oBAAA;AAAA;AAAA,KAGjC,iBAAA;EACV,OAAA;EACA,SAAS;AAAA;;;;KC/BC,mBAAA;yEAEV,WAAA,UFL+B;EEO/B,WAAW;AAAA;AAAA,cAGA,wBAAA,SAAiC,KAAK;EAAA,SACxC,OAAA;cAEG,OAAA;AAAA;;iBAWE,0BAAA,CACd,MAAA,GAAQ,MAAA,CAAO,UAAA,GACd,mBAAmB;;;KCvBV,cAAA;EACV,KAAA;EACA,GAAA,GAAM,MAAA,CAAO,UAAA;EACb,SAAA,UAAmB,KAAK;EACxB,eAAA;AAAA;AAAA,KAGU,wBAAA;EACV,cAAA;EACA,IAAA;EACA,IAAA;AAAA;AAAA,KAGU,yBAAA;EACV,OAAO;AAAA;AAAA,KAGG,aAAA;EACV,IAAA,UHfW;EGiBX,aAAA,EAAe,mBAAA;EACf,aAAA,IAAiB,cAAA;EACjB,qBAAA,EAAuB,KAAA,EAAO,wBAAA,GAA2B,OAAA,CAAQ,yBAAA;EACjE,uBAAA,EAAyB,MAAA,EAAQ,yBAAA,GAA4B,OAAA;EAC7D,aAAA,EACE,MAAA,EAAQ,iBAAA,GACP,MAAA;IAAY,OAAA;IAAiB,SAAA;EAAA;EAChC,kBAAA,EAAoB,SAAA,kBAA2B,MAAA;AAAA;;;KCtBrC,mBAAA,GAAsB,cAAA;EAChC,MAAA,GAAS,MAAM;AAAA;;;iBCDD,YAAA,CAAa,OAAA,GAAS,mBAAA,GAA2B,aAAa;;;;iBCJ9D,oBAAA,CAAqB,OAAA,GAAS,mBAAA,GAA2B,aAAa;;;ANJtF;AAAA,cOoBa,yBAAA;AAAA,cAEA,yBAAA;AAAA,KASD,iBAAA;EACV,KAAA;EACA,GAAA,EAAK,MAAA;EACL,IAAA;EACA,MAAA,SAAe,qBAAqB;AAAA;APjCN;AAAA,iBOqChB,yBAAA,CACd,QAAA,EAAU,sBAAA,GACT,MAAA,SAAe,yBAAA;;;;APrCc;AAEhC;iBOmDgB,oBAAA,CACd,OAAA,EAAS,cAAA,EACT,WAAA,UACA,QAAA,EAAU,sBAAA,GACT,iBAAA;;iBAgBa,eAAA,CACd,MAAA,EAAQ,MAAA,CAAO,UAAA,EACf,WAAA,UACA,QAAA,EAAU,sBAAA,GACT,MAAA;AAAA,iBAyCa,0BAAA,CACd,MAAA,GAAQ,MAAA,CAAO,UAAwB;AAAA,iBAiBzB,wBAAA,CACd,MAAA,GAAQ,MAAA,CAAO,UAAwB;;iBAczB,mBAAA,CAAoB,IAAY;AAAA,iBAQhC,kBAAA,CAAmB,GAAW;AP3JI;AAAA,iBOsKlC,eAAA,CAAgB,GAA2B,EAAtB,MAAM;AAAA,iBAI3B,yBAAA,CACd,GAAA,GAAK,MAAA,CAAO,UAAwB,EACpC,QAAA;;;KC/KU,oBAAA;EACV,SAAA;EACA,UAAA;EACA,SAAA,UAAmB,KAAK;AAAA;AAAA,iBAGJ,aAAA,CACpB,OAAA,UACA,OAAA,GAAS,oBAAA,GACR,OAAO;;;;cCbG,uBAAA;;;KCGD,kBAAA;EACV,SAAA;EACA,SAAA,UAAmB,KAAA;EACnB,SAAA;EACA,MAAA,GAAS,IAAA,CAAK,aAAA;AAAA;AAAA,iBAGM,WAAA,CACpB,OAAA,UACA,OAAA,GAAS,kBAAA,GACR,OAAO;;;iBCVM,oBAAA,CACd,MAAA,EAAQ,iBAAA,EACR,MAAA,GAAS,IAAA,CAAK,aAAA,qBACb,MAAA;EAAY,OAAA;EAAiB,SAAA;AAAA;AAAA,iBAQV,iBAAA,CACpB,MAAA,EAAQ,iBAAA,EACR,OAAA,GAAS,kBAAA;EACP,MAAA,GAAS,IAAA,CAAK,aAAA;AAAA,IAEf,OAAA"}
package/dist/index.d.mts CHANGED
@@ -44,6 +44,19 @@ type ProjectPingTarget = {
44
44
  runtimeId: string | null;
45
45
  };
46
46
  //#endregion
47
+ //#region src/worker-runtime-config.d.ts
48
+ /** Resolved platform ↔ project-server worker auth contract. */
49
+ type WorkerRuntimeConfig = {
50
+ /** Platform API base URL reachable from the project-server runtime. */platformUrl: string; /** Bearer token for platform `/internal` routes. */
51
+ workerToken: string;
52
+ };
53
+ declare class WorkerRuntimeConfigError extends Error {
54
+ readonly missing: readonly string[];
55
+ constructor(missing: string[]);
56
+ }
57
+ /** Fail fast when the platform cannot start worker-mode project servers. */
58
+ declare function resolveWorkerRuntimeConfig(source?: NodeJS.ProcessEnv): WorkerRuntimeConfig;
59
+ //#endregion
47
60
  //#region src/plugin.d.ts
48
61
  type HostingOptions = {
49
62
  image?: string;
@@ -60,7 +73,8 @@ type OrganizationHostingResult = {
60
73
  appName: string;
61
74
  };
62
75
  type HostingPlugin = {
63
- name: string;
76
+ name: string; /** Validated at plugin creation — shared by platform internal routes and project-server containers. */
77
+ workerRuntime: WorkerRuntimeConfig;
64
78
  createRuntime(): ProjectRuntime;
65
79
  provisionOrganization?(input: OrganizationHostingInput): Promise<OrganizationHostingResult>;
66
80
  deprovisionOrganization?(result: OrganizationHostingResult): Promise<void>;
@@ -84,6 +98,8 @@ declare function dockerPlugin(options?: DockerPluginOptions): HostingPlugin;
84
98
  declare function defaultHostingPlugin(options?: DockerPluginOptions): HostingPlugin;
85
99
  //#endregion
86
100
  //#region src/runtime-env.d.ts
101
+ /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
102
+ declare const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
87
103
  declare const PROJECT_DATABASE_ENV_KEYS: readonly ["POSTGRES_HOST", "POSTGRES_PORT", "POSTGRES_DB", "POSTGRES_USER", "POSTGRES_PASSWORD", "DATABASE_SEARCH_PATH"];
88
104
  type RuntimeLaunchSpec = {
89
105
  image: string;
@@ -101,8 +117,11 @@ declare function projectDatabaseRuntimeEnv(database: ProjectRuntimeDatabase): Re
101
117
  declare function resolveRuntimeLaunch(options: HostingOptions, artifactUrl: string, database: ProjectRuntimeDatabase): RuntimeLaunchSpec;
102
118
  /** Env vars passed into the project server container/machine. */
103
119
  declare function buildRuntimeEnv(source: NodeJS.ProcessEnv, artifactUrl: string, database: ProjectRuntimeDatabase): Record<string, string>;
120
+ declare function resolvePlatformWorkerToken(source?: NodeJS.ProcessEnv): string | undefined;
121
+ declare function resolveWorkerPlatformUrl(source?: NodeJS.ProcessEnv): string | undefined;
104
122
  /** Rewrite loopback hosts so containers can reach Postgres and other host services. */
105
123
  declare function rewriteLoopbackHost(host: string): string;
124
+ declare function rewriteLoopbackUrl(url: string): string;
106
125
  /** dockerode expects `KEY=value` strings. */
107
126
  declare function formatDockerEnv(env: Record<string, string>): string[];
108
127
  declare function resolveProjectServerImage(env?: NodeJS.ProcessEnv, override?: string): string;
@@ -137,5 +156,5 @@ declare function pingProjectTarget(target: ProjectPingTarget, options?: PingProj
137
156
  plugin?: Pick<HostingPlugin, "canPingTarget" | "pingRequestHeaders">;
138
157
  }): Promise<boolean>;
139
158
  //#endregion
140
- export { 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 ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type WaitForHealthOptions, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolveProjectServerImage, resolveRuntimeLaunch, rewriteLoopbackHost, waitForHealth };
159
+ 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 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 };
141
160
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/constants.ts","../src/runtime.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,7 +18,8 @@ const RUNTIME_ENV_KEYS = [
18
18
  "ANTHROPIC_API_KEY",
19
19
  "OPENAI_API_KEY"
20
20
  ];
21
- const WORKER_RUNTIME_ENV_KEYS = ["PLATFORM_URL", "PLATFORM_WORKER_TOKEN"];
21
+ /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
22
+ const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
22
23
  const PROJECT_DATABASE_ENV_KEYS = [
23
24
  "POSTGRES_HOST",
24
25
  "POSTGRES_PORT",
@@ -75,23 +76,40 @@ function buildRuntimeEnv(source, artifactUrl, database) {
75
76
  if (credentialsKey) entries.set("CREDENTIALS_ENCRYPTION_KEY", credentialsKey);
76
77
  }
77
78
  if (mode === "worker") {
78
- for (const key of WORKER_RUNTIME_ENV_KEYS) {
79
- const value = source[key];
80
- if (value) entries.set(key, value);
81
- }
82
- const platformUrl = source.PLATFORM_URL ?? source.PUBLIC_WEB_URL;
83
- if (platformUrl) entries.set("PLATFORM_URL", platformUrl);
84
- const workerToken = source.PLATFORM_WORKER_TOKEN ?? source.WORKER_INTERNAL_TOKEN;
85
- if (workerToken) entries.set("WORKER_INTERNAL_TOKEN", workerToken);
79
+ const worker = resolveWorkerRuntimeConfig(source);
80
+ entries.set("PLATFORM_URL", worker.platformUrl);
81
+ entries.set("WORKER_INTERNAL_TOKEN", worker.workerToken);
86
82
  }
87
83
  for (const [key, value] of Object.entries(projectDatabaseRuntimeEnv(database))) entries.set(key, value);
88
84
  return Object.fromEntries(entries);
89
85
  }
86
+ function resolvePlatformWorkerToken(source = process.env) {
87
+ const explicit = source.PLATFORM_WORKER_TOKEN?.trim() ?? source.WORKER_INTERNAL_TOKEN?.trim();
88
+ if (explicit) return explicit;
89
+ if (source.NODE_ENV === "production") return;
90
+ return DEV_PLATFORM_WORKER_TOKEN;
91
+ }
92
+ /** Platform API URL reachable from a project-server container. */
93
+ const DEV_PLATFORM_URL = "http://localhost:3002";
94
+ function resolveWorkerPlatformUrl(source = process.env) {
95
+ const raw = source.PLATFORM_URL?.trim() ?? source.PUBLIC_PLATFORM_URL?.trim() ?? (source.NODE_ENV === "production" ? void 0 : DEV_PLATFORM_URL);
96
+ if (!raw) return;
97
+ return rewriteLoopbackUrl(raw);
98
+ }
90
99
  /** Rewrite loopback hosts so containers can reach Postgres and other host services. */
91
100
  function rewriteLoopbackHost(host) {
92
101
  if (host === "localhost" || host === "127.0.0.1") return "host.docker.internal";
93
102
  return host;
94
103
  }
104
+ function rewriteLoopbackUrl(url) {
105
+ try {
106
+ const parsed = new URL(url);
107
+ parsed.hostname = rewriteLoopbackHost(parsed.hostname);
108
+ return parsed.toString().replace(/\/$/, "");
109
+ } catch {
110
+ return url;
111
+ }
112
+ }
95
113
  /** dockerode expects `KEY=value` strings. */
96
114
  function formatDockerEnv(env) {
97
115
  return Object.entries(env).map(([key, value]) => `${key}=${value}`);
@@ -100,6 +118,29 @@ function resolveProjectServerImage(env = process.env, override) {
100
118
  return override ?? env.PROJECT_DOCKER_IMAGE ?? env.TENANT_DOCKER_IMAGE ?? "keystroke/project-server:local";
101
119
  }
102
120
  //#endregion
121
+ //#region src/worker-runtime-config.ts
122
+ var WorkerRuntimeConfigError = class extends Error {
123
+ missing;
124
+ constructor(missing) {
125
+ super(`Worker runtime config incomplete (missing: ${missing.join(", ")}). In production set PLATFORM_WORKER_TOKEN and PUBLIC_PLATFORM_URL (or PLATFORM_URL).`);
126
+ this.name = "WorkerRuntimeConfigError";
127
+ this.missing = missing;
128
+ }
129
+ };
130
+ /** Fail fast when the platform cannot start worker-mode project servers. */
131
+ function resolveWorkerRuntimeConfig(source = process.env) {
132
+ const platformUrl = resolveWorkerPlatformUrl(source);
133
+ const workerToken = resolvePlatformWorkerToken(source);
134
+ const missing = [];
135
+ if (!platformUrl) missing.push("PUBLIC_PLATFORM_URL or PLATFORM_URL");
136
+ if (!workerToken) missing.push("PLATFORM_WORKER_TOKEN");
137
+ if (missing.length > 0) throw new WorkerRuntimeConfigError(missing);
138
+ return {
139
+ platformUrl,
140
+ workerToken
141
+ };
142
+ }
143
+ //#endregion
103
144
  //#region src/wait-for-health.ts
104
145
  const DEFAULT_TIMEOUT_MS = 12e4;
105
146
  const DEFAULT_INTERVAL_MS = 500;
@@ -199,9 +240,14 @@ async function ensureImage(docker, image) {
199
240
  //#endregion
200
241
  //#region src/docker/plugin.ts
201
242
  function dockerPlugin(options = {}) {
243
+ const env = options.env ?? process.env;
202
244
  return {
203
245
  name: "docker",
204
- createRuntime: () => createDockerRuntime(options)
246
+ workerRuntime: resolveWorkerRuntimeConfig(env),
247
+ createRuntime: () => createDockerRuntime({
248
+ ...options,
249
+ env
250
+ })
205
251
  };
206
252
  }
207
253
  //#endregion
@@ -242,6 +288,6 @@ async function pingProjectTarget(target, options = {}) {
242
288
  });
243
289
  }
244
290
  //#endregion
245
- export { 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, buildRuntimeEnv, canPingProjectTarget, defaultHostingPlugin, dockerPlugin, formatDockerEnv, pingProject, pingProjectTarget, projectDatabaseRuntimeEnv, resolveProjectServerImage, resolveRuntimeLaunch, rewriteLoopbackHost, waitForHealth };
291
+ 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 };
246
292
 
247
293
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/constants.ts","../src/runtime-env.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\";\n\nconst RUNTIME_ENV_KEYS = [\n \"BETTER_AUTH_SECRET\",\n \"PUBLIC_SERVER_URL\",\n \"PUBLIC_WEB_URL\",\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n] as const;\n\nconst WORKER_RUNTIME_ENV_KEYS = [\"PLATFORM_URL\", \"PLATFORM_WORKER_TOKEN\"] as const;\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];\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 for (const key of WORKER_RUNTIME_ENV_KEYS) {\n const value = source[key];\n if (value) {\n entries.set(key, value);\n }\n }\n\n const platformUrl = source.PLATFORM_URL ?? source.PUBLIC_WEB_URL;\n if (platformUrl) {\n entries.set(\"PLATFORM_URL\", platformUrl);\n }\n\n const workerToken = source.PLATFORM_WORKER_TOKEN ?? source.WORKER_INTERNAL_TOKEN;\n if (workerToken) {\n entries.set(\"WORKER_INTERNAL_TOKEN\", workerToken);\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\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\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 { 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\";\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 return {\n name: \"docker\",\n createRuntime: () => createDockerRuntime(options),\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 \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\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): 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];\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 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);\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.68",
3
+ "version": "0.0.74",
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.54",
40
+ "@keystrokehq/shared": "0.0.55",
41
41
  "@keystrokehq/tsconfig": "0.0.3",
42
42
  "@keystrokehq/tsdown-config": "0.0.3",
43
43
  "@keystrokehq/vitest-config": "0.0.3"