everything-dev 1.51.7 → 1.53.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/api-contract.cjs +66 -16
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs +65 -17
- package/dist/api-contract.mjs.map +1 -1
- package/dist/app.cjs +1 -1
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +1 -1
- package/dist/app.mjs.map +1 -1
- package/dist/cli/infra.cjs +0 -67
- package/dist/cli/infra.cjs.map +1 -1
- package/dist/cli/infra.mjs +1 -65
- package/dist/cli/infra.mjs.map +1 -1
- package/dist/cli/init.cjs +76 -52
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +15 -7
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +15 -7
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +70 -47
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/sync.cjs +34 -5
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +35 -6
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli/upgrade.cjs +151 -93
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +138 -80
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/cli/utils/helpers.cjs +25 -0
- package/dist/cli/utils/helpers.cjs.map +1 -0
- package/dist/cli/utils/helpers.mjs +23 -0
- package/dist/cli/utils/helpers.mjs.map +1 -0
- package/dist/cli.cjs +84 -72
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +84 -72
- package/dist/cli.mjs.map +1 -1
- package/dist/code-artifacts.cjs +1 -1
- package/dist/code-artifacts.mjs +1 -1
- package/dist/config.cjs +47 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +47 -5
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +9 -4
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +42 -24
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +42 -24
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.meta.cjs +2 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +3 -0
- package/dist/contract.meta.d.mts +3 -0
- package/dist/contract.meta.mjs +2 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +9 -4
- package/dist/contract.mjs.map +1 -1
- package/dist/dag.cjs +177 -0
- package/dist/dag.cjs.map +1 -0
- package/dist/dag.d.cts +24 -0
- package/dist/dag.d.cts.map +1 -0
- package/dist/dag.d.mts +24 -0
- package/dist/dag.d.mts.map +1 -0
- package/dist/dag.mjs +168 -0
- package/dist/dag.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/infra/planner.cjs +1 -1
- package/dist/infra/planner.mjs +1 -1
- package/dist/merge.cjs +1 -1
- package/dist/merge.cjs.map +1 -1
- package/dist/merge.mjs +1 -1
- package/dist/merge.mjs.map +1 -1
- package/dist/near-cli.cjs +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/orchestrator.cjs +1 -1
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.mjs +1 -1
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs +28 -30
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +67 -11
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +67 -11
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +30 -32
- package/dist/plugin.mjs.map +1 -1
- package/dist/types.cjs +52 -9
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +131 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +131 -3
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +51 -10
- package/dist/types.mjs.map +1 -1
- package/package.json +8 -3
package/dist/cli/infra.mjs
CHANGED
|
@@ -254,70 +254,6 @@ function renderDockerCompose(databases, redisConfigs, projectName) {
|
|
|
254
254
|
}
|
|
255
255
|
return `${lines.join("\n")}\n`;
|
|
256
256
|
}
|
|
257
|
-
function renderEnvFileFromPlan(env, devHostPort) {
|
|
258
|
-
const lines = [
|
|
259
|
-
"# Generated from bos dev infra plan",
|
|
260
|
-
"# Update values as needed for your local environment",
|
|
261
|
-
""
|
|
262
|
-
];
|
|
263
|
-
const sortedKeys = Object.keys(env).sort();
|
|
264
|
-
for (const key of sortedKeys) {
|
|
265
|
-
let value = env[key];
|
|
266
|
-
if (key === "CORS_ORIGIN" && devHostPort && !value) value = `http://localhost:${devHostPort}`;
|
|
267
|
-
lines.push(`${key}=${value}`);
|
|
268
|
-
}
|
|
269
|
-
lines.push("");
|
|
270
|
-
return `${lines.join("\n")}\n`;
|
|
271
|
-
}
|
|
272
|
-
function renderDockerComposeFromPlan(databases, redis, projectName) {
|
|
273
|
-
const lines = [`name: ${projectName}`, ""];
|
|
274
|
-
if (databases.length > 0) lines.push("x-pg-common: &pg-common", " image: postgres:17-alpine", " environment: &pg-env", ` POSTGRES_USER: ${POSTGRES_USER}`, ` POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}`, " healthcheck:", " test: [\"CMD-SHELL\", \"pg_isready -U everythingdev\"]", " interval: 3s", " timeout: 3s", " retries: 5", "");
|
|
275
|
-
if (redis.length > 0) lines.push("x-redis-common: &redis-common", " image: redis:7-alpine", " command: redis-server --appendonly yes", " healthcheck:", " test: [\"CMD\", \"redis-cli\", \"ping\"]", " interval: 3s", " timeout: 3s", " retries: 5", "");
|
|
276
|
-
lines.push("services:");
|
|
277
|
-
for (const db of databases) {
|
|
278
|
-
lines.push(` ${db.serviceName}:`);
|
|
279
|
-
lines.push(" <<: *pg-common");
|
|
280
|
-
lines.push(` container_name: ${db.containerName}`);
|
|
281
|
-
lines.push(" environment:");
|
|
282
|
-
lines.push(" <<: *pg-env");
|
|
283
|
-
lines.push(` POSTGRES_DB: ${db.databaseName}`);
|
|
284
|
-
lines.push(" ports:");
|
|
285
|
-
lines.push(` - "${db.port}:5432"`);
|
|
286
|
-
lines.push(" volumes:");
|
|
287
|
-
lines.push(` - ${db.volumeName}:/var/lib/postgresql/data`);
|
|
288
|
-
lines.push("");
|
|
289
|
-
}
|
|
290
|
-
for (const r of redis) {
|
|
291
|
-
lines.push(` ${r.serviceName}:`);
|
|
292
|
-
lines.push(" <<: *redis-common");
|
|
293
|
-
lines.push(` container_name: ${r.containerName}`);
|
|
294
|
-
lines.push(" ports:");
|
|
295
|
-
lines.push(` - "${r.port}:6379"`);
|
|
296
|
-
lines.push(" volumes:");
|
|
297
|
-
lines.push(` - ${r.volumeName}:/data`);
|
|
298
|
-
lines.push("");
|
|
299
|
-
}
|
|
300
|
-
lines.push("volumes:");
|
|
301
|
-
for (const db of databases) {
|
|
302
|
-
lines.push(` ${db.volumeName}:`);
|
|
303
|
-
lines.push(` name: ${db.volumeName}`);
|
|
304
|
-
}
|
|
305
|
-
for (const r of redis) {
|
|
306
|
-
lines.push(` ${r.volumeName}:`);
|
|
307
|
-
lines.push(` name: ${r.volumeName}`);
|
|
308
|
-
}
|
|
309
|
-
return `${lines.join("\n")}\n`;
|
|
310
|
-
}
|
|
311
|
-
function materializeInfraPlan(configDir, planEnv, planDatabases, planRedis, projectName, devHostPort) {
|
|
312
|
-
const envContent = renderEnvFileFromPlan(planEnv, devHostPort);
|
|
313
|
-
const dockerContent = renderDockerComposeFromPlan(planDatabases, planRedis, projectName);
|
|
314
|
-
const envExamplePath = join(configDir, ".env.example");
|
|
315
|
-
const dockerComposePath = join(configDir, "docker-compose.yml");
|
|
316
|
-
return {
|
|
317
|
-
envExampleChanged: syncTextFile(envExamplePath, envContent),
|
|
318
|
-
dockerComposeChanged: syncTextFile(dockerComposePath, dockerContent)
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
257
|
function syncTextFile(filePath, nextContent) {
|
|
322
258
|
if (existsSync(filePath) && readFileSync(filePath, "utf-8") === nextContent) return false;
|
|
323
259
|
writeFileSync(filePath, nextContent);
|
|
@@ -401,5 +337,5 @@ function loadProjectEnv(configDir) {
|
|
|
401
337
|
}
|
|
402
338
|
|
|
403
339
|
//#endregion
|
|
404
|
-
export { buildDatabaseConfigs, buildOriginMap, buildRedisConfigs, ensureEnvFile, getSecretGroups, loadPortState, loadProjectEnv,
|
|
340
|
+
export { buildDatabaseConfigs, buildOriginMap, buildRedisConfigs, ensureEnvFile, getSecretGroups, loadPortState, loadProjectEnv, normalizeDatabaseSlug, normalizeRedisSlug, savePortState, syncGeneratedInfra, writeGeneratedInfra };
|
|
405
341
|
//# sourceMappingURL=infra.mjs.map
|
package/dist/cli/infra.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infra.mjs","names":[],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { config as loadDotenv } from \"dotenv\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst API_DATABASE_SECRET = \"API_DATABASE_URL\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst BASE_POSTGRES_PORT = 5434;\nconst BASE_REDIS_PORT = 6379;\n\nexport interface DatabaseSecretConfig {\n secret: string;\n slug: string;\n fromKey: string;\n port: number;\n serviceName: string;\n containerName: string;\n databaseName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface RedisSecretConfig {\n secret: string;\n slug: string;\n fromKey: string;\n port: number;\n serviceName: string;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport interface PortState {\n postgresPorts: Record<string, number>;\n redisPorts: Record<string, number>;\n devPorts?: DevPortState;\n}\n\nexport interface GeneratedInfraSpec {\n groups: SecretGroup[];\n databases: DatabaseSecretConfig[];\n redis: RedisSecretConfig[];\n}\n\ninterface SyncGeneratedInfraResult {\n secrets: string[];\n envExampleChanged: boolean;\n dockerComposeChanged: boolean;\n staleEnvWarnings: string[];\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return { postgresPorts: {}, redisPorts: {} };\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return { postgresPorts: {}, redisPorts: {} };\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return {\n postgresPorts: raw.postgresPorts ?? {},\n redisPorts: raw.redisPorts ?? {},\n devPorts: raw.devPorts,\n };\n } catch {\n return { postgresPorts: {}, redisPorts: {} };\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport type { DevPortState };\n\nfunction resolvePort(slug: string, portMap: Record<string, number>, basePort: number): number {\n if (portMap[slug] !== undefined) return portMap[slug];\n const assigned = Object.values(portMap);\n const maxAssigned = assigned.length > 0 ? Math.max(...assigned) : basePort - 1;\n const next = Math.max(maxAssigned + 1, basePort);\n portMap[slug] = next;\n return next;\n}\n\nexport function normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction buildGeneratedInfraSpec(\n runtimeConfig: RuntimeConfig,\n configDir?: string,\n): { spec: GeneratedInfraSpec; portState: PortState } {\n const groups = getSecretGroups(runtimeConfig);\n const allSecrets = groups.flatMap((group) => group.secrets);\n const originMap = configDir ? buildOriginMap(configDir, runtimeConfig) : new Map();\n const portState = loadPortState(configDir);\n\n const databases = buildDatabaseConfigs(allSecrets, originMap, portState.postgresPorts);\n const redis = buildRedisConfigs(allSecrets, originMap, portState.redisPorts);\n\n return { spec: { groups, databases, redis }, portState };\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nexport function buildOriginMap(\n configDir: string,\n runtimeConfig: RuntimeConfig,\n): Map<string, string> {\n const configPath = join(configDir, \"bos.config.json\");\n\n const originMap = new Map<string, string>();\n const account = runtimeConfig.account;\n\n const resolveOrigin = (extendsRef: unknown): string | null => {\n if (typeof extendsRef === \"string\") {\n const match = extendsRef.match(/^bos:\\/\\/([^/]+)\\//);\n return match?.[1] ?? null;\n }\n return null;\n };\n\n const rawConfig = existsSync(configPath)\n ? (JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>)\n : null;\n const rawPlugins = rawConfig?.plugins as Record<string, unknown> | undefined;\n\n for (const secret of runtimeConfig.api.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, account);\n }\n\n const rawApp = rawConfig?.app as Record<string, unknown> | undefined;\n const authExtends = (rawApp?.auth as Record<string, unknown> | undefined)?.extends;\n const authOrigin = resolveOrigin(authExtends) ?? account;\n for (const secret of runtimeConfig.auth?.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, authOrigin);\n }\n\n for (const [pluginKey, pluginEntry] of Object.entries(runtimeConfig.plugins ?? {})) {\n const rawPlugin = rawPlugins?.[pluginKey];\n let pluginOrigin: string;\n if (typeof rawPlugin === \"string\") {\n pluginOrigin = resolveOrigin(rawPlugin) ?? account;\n } else if (rawPlugin && typeof rawPlugin === \"object\") {\n pluginOrigin = resolveOrigin((rawPlugin as Record<string, unknown>).extends) ?? account;\n } else {\n pluginOrigin = account;\n }\n for (const secret of pluginEntry.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, pluginOrigin);\n }\n }\n\n for (const secret of runtimeConfig.host.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, account);\n }\n\n return originMap;\n}\n\nexport function buildDatabaseConfigs(\n secrets: string[],\n originMap: Map<string, string>,\n portMap: Record<string, number>,\n): DatabaseSecretConfig[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n const orderedSecrets = [...databaseSecrets];\n\n // Prune stale entries from removed plugins\n const currentSlugs = new Set(orderedSecrets.map(normalizeDatabaseSlug));\n for (const slug of Object.keys(portMap)) {\n if (!currentSlugs.has(slug)) delete portMap[slug];\n }\n\n // Sort by slug for deterministic assignment order\n orderedSecrets.sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)));\n\n for (const secret of orderedSecrets) {\n const slug = normalizeDatabaseSlug(secret);\n if (secret === API_DATABASE_SECRET) {\n portMap[slug] = 5432;\n } else if (secret === AUTH_DATABASE_SECRET) {\n portMap[slug] = 5433;\n } else {\n resolvePort(slug, portMap, BASE_POSTGRES_PORT);\n }\n }\n\n return orderedSecrets.map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const fromKey = originMap.get(secret) ?? \"\";\n const port = portMap[slug];\n\n const volumeName = fromKey\n ? `${fromKey.replace(/\\./g, \"_\")}_postgres_${slug}_data`\n : `postgres_${slug}_data`;\n\n const containerName = fromKey ? `${fromKey}-postgres-${slug}` : `postgres-${slug}`;\n\n return {\n secret,\n slug,\n fromKey,\n port,\n serviceName: `postgres-${slug.replace(/_/g, \"-\")}`,\n containerName,\n databaseName: `${slug}_db`,\n volumeName,\n url: `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${slug}_db`,\n };\n });\n}\n\nexport function buildRedisConfigs(\n secrets: string[],\n originMap: Map<string, string>,\n portMap: Record<string, number>,\n): RedisSecretConfig[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n const orderedSecrets = [...redisSecrets];\n\n // Prune stale entries from removed plugins\n const currentSlugs = new Set(orderedSecrets.map(normalizeRedisSlug));\n for (const slug of Object.keys(portMap)) {\n if (!currentSlugs.has(slug)) delete portMap[slug];\n }\n\n // Sort by slug for deterministic assignment order\n orderedSecrets.sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)));\n\n for (const secret of orderedSecrets) {\n const slug = normalizeRedisSlug(secret);\n resolvePort(slug, portMap, BASE_REDIS_PORT);\n }\n\n return orderedSecrets.map((secret) => {\n const slug = normalizeRedisSlug(secret);\n const fromKey = originMap.get(secret) ?? \"\";\n const port = portMap[slug];\n\n const volumeName = fromKey\n ? `${fromKey.replace(/\\./g, \"_\")}_redis_${slug}_data`\n : `redis_${slug}_data`;\n\n const containerName = fromKey ? `${fromKey}-redis-${slug}` : `redis-${slug}`;\n\n return {\n secret,\n slug,\n fromKey,\n port,\n serviceName: `redis-${slug.replace(/_/g, \"-\")}`,\n containerName,\n volumeName,\n url: `redis://localhost:${port}`,\n };\n });\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseSecretConfig>,\n redisConfigs: Map<string, RedisSecretConfig>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n databases: DatabaseSecretConfig[],\n redisConfigs: RedisSecretConfig[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const databaseMap = new Map(databases.map((entry) => [entry.secret, entry]));\n const redisMap = new Map(redisConfigs.map((entry) => [entry.secret, entry]));\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of groups) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction renderDockerCompose(\n databases: DatabaseSecretConfig[],\n redisConfigs: RedisSecretConfig[],\n projectName: string,\n): string {\n const lines = [`name: ${projectName}`, \"\"];\n\n if (databases.length > 0) {\n lines.push(\n \"x-pg-common: &pg-common\",\n \" image: postgres:17-alpine\",\n \" environment: &pg-env\",\n ` POSTGRES_USER: ${POSTGRES_USER}`,\n ` POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}`,\n \" healthcheck:\",\n ' test: [\"CMD-SHELL\", \"pg_isready -U everythingdev\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n if (redisConfigs.length > 0) {\n lines.push(\n \"x-redis-common: &redis-common\",\n \" image: redis:7-alpine\",\n \" command: redis-server --appendonly yes\",\n \" healthcheck:\",\n ' test: [\"CMD\", \"redis-cli\", \"ping\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n lines.push(\"services:\");\n\n for (const database of databases) {\n lines.push(` ${database.serviceName}:`);\n lines.push(\" <<: *pg-common\");\n lines.push(` container_name: ${database.containerName}`);\n lines.push(\" environment:\");\n lines.push(\" <<: *pg-env\");\n lines.push(` POSTGRES_DB: ${database.databaseName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${database.port}:5432\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${database.volumeName}:/var/lib/postgresql/data`);\n lines.push(\"\");\n }\n\n for (const redis of redisConfigs) {\n lines.push(` ${redis.serviceName}:`);\n lines.push(\" <<: *redis-common\");\n lines.push(` container_name: ${redis.containerName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${redis.port}:6379\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${redis.volumeName}:/data`);\n lines.push(\"\");\n }\n\n lines.push(\"volumes:\");\n for (const database of databases) {\n lines.push(` ${database.volumeName}:`);\n lines.push(` name: ${database.volumeName}`);\n }\n for (const redis of redisConfigs) {\n lines.push(` ${redis.volumeName}:`);\n lines.push(` name: ${redis.volumeName}`);\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function renderEnvFileFromPlan(env: Record<string, string>, devHostPort?: number): string {\n const lines: string[] = [\n \"# Generated from bos dev infra plan\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n const sortedKeys = Object.keys(env).sort();\n for (const key of sortedKeys) {\n let value = env[key];\n if (key === \"CORS_ORIGIN\" && devHostPort && !value) {\n value = `http://localhost:${devHostPort}`;\n }\n lines.push(`${key}=${value}`);\n }\n lines.push(\"\");\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function renderDockerComposeFromPlan(\n databases: {\n serviceName: string;\n containerName: string;\n port: number;\n volumeName: string;\n databaseName: string;\n }[],\n redis: { serviceName: string; containerName: string; port: number; volumeName: string }[],\n projectName: string,\n): string {\n const lines = [`name: ${projectName}`, \"\"];\n\n if (databases.length > 0) {\n lines.push(\n \"x-pg-common: &pg-common\",\n \" image: postgres:17-alpine\",\n \" environment: &pg-env\",\n ` POSTGRES_USER: ${POSTGRES_USER}`,\n ` POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}`,\n \" healthcheck:\",\n ' test: [\"CMD-SHELL\", \"pg_isready -U everythingdev\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n if (redis.length > 0) {\n lines.push(\n \"x-redis-common: &redis-common\",\n \" image: redis:7-alpine\",\n \" command: redis-server --appendonly yes\",\n \" healthcheck:\",\n ' test: [\"CMD\", \"redis-cli\", \"ping\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n lines.push(\"services:\");\n\n for (const db of databases) {\n lines.push(` ${db.serviceName}:`);\n lines.push(\" <<: *pg-common\");\n lines.push(` container_name: ${db.containerName}`);\n lines.push(\" environment:\");\n lines.push(\" <<: *pg-env\");\n lines.push(` POSTGRES_DB: ${db.databaseName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${db.port}:5432\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${db.volumeName}:/var/lib/postgresql/data`);\n lines.push(\"\");\n }\n\n for (const r of redis) {\n lines.push(` ${r.serviceName}:`);\n lines.push(\" <<: *redis-common\");\n lines.push(` container_name: ${r.containerName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${r.port}:6379\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${r.volumeName}:/data`);\n lines.push(\"\");\n }\n\n lines.push(\"volumes:\");\n for (const db of databases) {\n lines.push(` ${db.volumeName}:`);\n lines.push(` name: ${db.volumeName}`);\n }\n for (const r of redis) {\n lines.push(` ${r.volumeName}:`);\n lines.push(` name: ${r.volumeName}`);\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function materializeInfraPlan(\n configDir: string,\n planEnv: Record<string, string>,\n planDatabases: {\n serviceName: string;\n containerName: string;\n port: number;\n volumeName: string;\n databaseName: string;\n }[],\n planRedis: { serviceName: string; containerName: string; port: number; volumeName: string }[],\n projectName: string,\n devHostPort?: number,\n): { envExampleChanged: boolean; dockerComposeChanged: boolean } {\n const envContent = renderEnvFileFromPlan(planEnv, devHostPort);\n const dockerContent = renderDockerComposeFromPlan(planDatabases, planRedis, projectName);\n\n const envExamplePath = join(configDir, \".env.example\");\n const dockerComposePath = join(configDir, \"docker-compose.yml\");\n\n return {\n envExampleChanged: syncTextFile(envExamplePath, envContent),\n dockerComposeChanged: syncTextFile(dockerComposePath, dockerContent),\n };\n}\n\nfunction syncTextFile(filePath: string, nextContent: string): boolean {\n if (existsSync(filePath) && readFileSync(filePath, \"utf-8\") === nextContent) {\n return false;\n }\n\n writeFileSync(filePath, nextContent);\n return true;\n}\n\nexport function writeGeneratedInfra(configDir: string, runtimeConfig: RuntimeConfig): string[] {\n const result = syncGeneratedInfra(configDir, runtimeConfig);\n\n if (result.staleEnvWarnings.length > 0) {\n p.log.warn(\n `.env has ${result.staleEnvWarnings.length} stale value(s) compared to .env.example:`,\n );\n for (const warning of result.staleEnvWarnings) {\n p.log.message(` ${warning}`);\n }\n }\n\n return result.secrets;\n}\n\nexport function syncGeneratedInfra(\n configDir: string,\n runtimeConfig: RuntimeConfig,\n): SyncGeneratedInfraResult {\n const { spec, portState } = buildGeneratedInfraSpec(runtimeConfig, configDir);\n const secrets = spec.groups.flatMap((group) => group.secrets);\n const envOptions: { forExample: true; devHostPort?: number } = { forExample: true };\n if (runtimeConfig.env === \"development\") {\n envOptions.devHostPort = resolveDevHostPort(runtimeConfig);\n }\n const newEnvContent = renderEnvFile(spec.groups, spec.databases, spec.redis, envOptions);\n const newDockerContent = renderDockerCompose(spec.databases, spec.redis, runtimeConfig.account);\n\n const envExamplePath = join(configDir, \".env.example\");\n const dockerComposePath = join(configDir, \"docker-compose.yml\");\n\n const staleWarnings = checkEnvStaleness(configDir, spec.databases, spec.redis);\n\n if (configDir) {\n savePortState(configDir, portState);\n }\n\n return {\n secrets,\n envExampleChanged: syncTextFile(envExamplePath, newEnvContent),\n dockerComposeChanged: syncTextFile(dockerComposePath, newDockerContent),\n staleEnvWarnings: staleWarnings,\n };\n}\n\nfunction checkEnvStaleness(\n configDir: string,\n databases: DatabaseSecretConfig[],\n redisConfigs: RedisSecretConfig[],\n): string[] {\n const envPath = join(configDir, \".env\");\n if (!existsSync(envPath)) return [];\n\n const existingEnv = readFileSync(envPath, \"utf-8\");\n const envMap = new Map<string, string>();\n for (const line of existingEnv.split(\"\\n\")) {\n const match = line.match(/^([A-Z_]+)=(.*)$/);\n if (match) envMap.set(match[1], match[2]);\n }\n\n const stale: string[] = [];\n\n for (const db of databases) {\n const existing = envMap.get(db.secret);\n if (existing && existing !== db.url) {\n const oldPort = extractPortFromUrl(existing) ?? \"?\";\n stale.push(`${db.secret}: port ${oldPort} → ${db.port}`);\n }\n }\n\n for (const redis of redisConfigs) {\n const existing = envMap.get(redis.secret);\n if (existing && existing !== redis.url) {\n const oldPort = extractPortFromUrl(existing) ?? \"?\";\n stale.push(`${redis.secret}: port ${oldPort} → ${redis.port}`);\n }\n }\n\n return stale;\n}\n\nexport function ensureEnvFile(configDir: string): void {\n const envPath = join(configDir, \".env\");\n const examplePath = join(configDir, \".env.example\");\n\n if (existsSync(envPath) || !existsSync(examplePath)) return;\n\n const content = readFileSync(examplePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const secret = randomBytes(32).toString(\"base64url\");\n const updated = lines\n .map((line) => {\n if (/^BETTER_AUTH_SECRET=/.test(line)) {\n return `BETTER_AUTH_SECRET=${secret}`;\n }\n return line;\n })\n .join(\"\\n\");\n\n writeFileSync(envPath, updated);\n p.log.info(\"Created .env from generated .env.example with generated BETTER_AUTH_SECRET\");\n}\n\nlet envLoadedDir: string | null = null;\n\nexport function loadProjectEnv(configDir: string): void {\n if (envLoadedDir === configDir) return;\n const envPath = join(configDir, \".env\");\n if (!existsSync(envPath)) return;\n\n loadDotenv({ path: envPath, processEnv: process.env, quiet: true });\n envLoadedDir = configDir;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"infra.mjs","names":[],"sources":["../../src/cli/infra.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { config as loadDotenv } from \"dotenv\";\nimport type { RuntimeConfig } from \"../types\";\n\nconst POSTGRES_USER = \"everythingdev\";\nconst POSTGRES_PASSWORD = \"everythingdev\";\nconst API_DATABASE_SECRET = \"API_DATABASE_URL\";\nconst AUTH_DATABASE_SECRET = \"AUTH_DATABASE_URL\";\nconst HOST_SECRET = \"CORS_ORIGIN\";\nconst BASE_POSTGRES_PORT = 5434;\nconst BASE_REDIS_PORT = 6379;\n\nexport interface DatabaseSecretConfig {\n secret: string;\n slug: string;\n fromKey: string;\n port: number;\n serviceName: string;\n containerName: string;\n databaseName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface RedisSecretConfig {\n secret: string;\n slug: string;\n fromKey: string;\n port: number;\n serviceName: string;\n containerName: string;\n volumeName: string;\n url: string;\n}\n\nexport interface SecretGroup {\n section: string;\n secrets: string[];\n}\n\ninterface DevPortState {\n host?: number;\n api?: number;\n ui?: number;\n auth?: number;\n pluginPortStart?: number;\n}\n\nexport interface PortState {\n postgresPorts: Record<string, number>;\n redisPorts: Record<string, number>;\n devPorts?: DevPortState;\n}\n\nexport interface GeneratedInfraSpec {\n groups: SecretGroup[];\n databases: DatabaseSecretConfig[];\n redis: RedisSecretConfig[];\n}\n\ninterface SyncGeneratedInfraResult {\n secrets: string[];\n envExampleChanged: boolean;\n dockerComposeChanged: boolean;\n staleEnvWarnings: string[];\n}\n\nfunction uniqueSecrets(values: Array<string | undefined>): string[] {\n const secrets: string[] = [];\n const seen = new Set<string>();\n\n for (const value of values) {\n if (!value || seen.has(value)) continue;\n seen.add(value);\n secrets.push(value);\n }\n\n return secrets;\n}\n\nexport function loadPortState(configDir?: string): PortState {\n if (!configDir) return { postgresPorts: {}, redisPorts: {} };\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n if (!existsSync(statePath)) return { postgresPorts: {}, redisPorts: {} };\n try {\n const raw = JSON.parse(readFileSync(statePath, \"utf-8\")) as Partial<PortState>;\n return {\n postgresPorts: raw.postgresPorts ?? {},\n redisPorts: raw.redisPorts ?? {},\n devPorts: raw.devPorts,\n };\n } catch {\n return { postgresPorts: {}, redisPorts: {} };\n }\n}\n\nexport function savePortState(configDir: string, state: PortState): void {\n const statePath = join(configDir, \".bos\", \"infra-state.json\");\n mkdirSync(dirname(statePath), { recursive: true });\n writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\nexport type { DevPortState };\n\nfunction resolvePort(slug: string, portMap: Record<string, number>, basePort: number): number {\n if (portMap[slug] !== undefined) return portMap[slug];\n const assigned = Object.values(portMap);\n const maxAssigned = assigned.length > 0 ? Math.max(...assigned) : basePort - 1;\n const next = Math.max(maxAssigned + 1, basePort);\n portMap[slug] = next;\n return next;\n}\n\nexport function normalizeRedisSlug(secret: string): string {\n return secret.replace(/_REDIS_URL$/, \"\").toLowerCase();\n}\n\nexport function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] {\n const groups: SecretGroup[] = [];\n const seen = new Set<string>();\n\n const addGroup = (section: string, secrets: string[]) => {\n const filtered = secrets.filter((s) => {\n if (seen.has(s)) return false;\n seen.add(s);\n return true;\n });\n if (filtered.length > 0) {\n groups.push({ section, secrets: filtered });\n }\n };\n\n addGroup(\"app.host\", uniqueSecrets([...(runtimeConfig.host.secrets ?? []), HOST_SECRET]));\n\n addGroup(\"app.api\", uniqueSecrets(runtimeConfig.api.secrets ?? []));\n\n if (runtimeConfig.auth) {\n addGroup(\"app.auth\", uniqueSecrets(runtimeConfig.auth.secrets ?? []));\n }\n\n if (runtimeConfig.plugins) {\n for (const [pluginKey, plugin] of Object.entries(runtimeConfig.plugins)) {\n if (plugin.secrets && plugin.secrets.length > 0) {\n addGroup(`plugins.${pluginKey}`, plugin.secrets);\n }\n }\n }\n\n return groups;\n}\n\nfunction buildGeneratedInfraSpec(\n runtimeConfig: RuntimeConfig,\n configDir?: string,\n): { spec: GeneratedInfraSpec; portState: PortState } {\n const groups = getSecretGroups(runtimeConfig);\n const allSecrets = groups.flatMap((group) => group.secrets);\n const originMap = configDir ? buildOriginMap(configDir, runtimeConfig) : new Map();\n const portState = loadPortState(configDir);\n\n const databases = buildDatabaseConfigs(allSecrets, originMap, portState.postgresPorts);\n const redis = buildRedisConfigs(allSecrets, originMap, portState.redisPorts);\n\n return { spec: { groups, databases, redis }, portState };\n}\n\nexport function normalizeDatabaseSlug(secret: string): string {\n return secret.replace(/_DATABASE_URL$/, \"\").toLowerCase();\n}\n\nexport function buildOriginMap(\n configDir: string,\n runtimeConfig: RuntimeConfig,\n): Map<string, string> {\n const configPath = join(configDir, \"bos.config.json\");\n\n const originMap = new Map<string, string>();\n const account = runtimeConfig.account;\n\n const resolveOrigin = (extendsRef: unknown): string | null => {\n if (typeof extendsRef === \"string\") {\n const match = extendsRef.match(/^bos:\\/\\/([^/]+)\\//);\n return match?.[1] ?? null;\n }\n return null;\n };\n\n const rawConfig = existsSync(configPath)\n ? (JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>)\n : null;\n const rawPlugins = rawConfig?.plugins as Record<string, unknown> | undefined;\n\n for (const secret of runtimeConfig.api.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, account);\n }\n\n const rawApp = rawConfig?.app as Record<string, unknown> | undefined;\n const authExtends = (rawApp?.auth as Record<string, unknown> | undefined)?.extends;\n const authOrigin = resolveOrigin(authExtends) ?? account;\n for (const secret of runtimeConfig.auth?.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, authOrigin);\n }\n\n for (const [pluginKey, pluginEntry] of Object.entries(runtimeConfig.plugins ?? {})) {\n const rawPlugin = rawPlugins?.[pluginKey];\n let pluginOrigin: string;\n if (typeof rawPlugin === \"string\") {\n pluginOrigin = resolveOrigin(rawPlugin) ?? account;\n } else if (rawPlugin && typeof rawPlugin === \"object\") {\n pluginOrigin = resolveOrigin((rawPlugin as Record<string, unknown>).extends) ?? account;\n } else {\n pluginOrigin = account;\n }\n for (const secret of pluginEntry.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, pluginOrigin);\n }\n }\n\n for (const secret of runtimeConfig.host.secrets ?? []) {\n if (!originMap.has(secret)) originMap.set(secret, account);\n }\n\n return originMap;\n}\n\nexport function buildDatabaseConfigs(\n secrets: string[],\n originMap: Map<string, string>,\n portMap: Record<string, number>,\n): DatabaseSecretConfig[] {\n const databaseSecrets = uniqueSecrets(\n secrets.filter((secret) => secret.endsWith(\"_DATABASE_URL\")),\n );\n\n const orderedSecrets = [...databaseSecrets];\n\n // Prune stale entries from removed plugins\n const currentSlugs = new Set(orderedSecrets.map(normalizeDatabaseSlug));\n for (const slug of Object.keys(portMap)) {\n if (!currentSlugs.has(slug)) delete portMap[slug];\n }\n\n // Sort by slug for deterministic assignment order\n orderedSecrets.sort((a, b) => normalizeDatabaseSlug(a).localeCompare(normalizeDatabaseSlug(b)));\n\n for (const secret of orderedSecrets) {\n const slug = normalizeDatabaseSlug(secret);\n if (secret === API_DATABASE_SECRET) {\n portMap[slug] = 5432;\n } else if (secret === AUTH_DATABASE_SECRET) {\n portMap[slug] = 5433;\n } else {\n resolvePort(slug, portMap, BASE_POSTGRES_PORT);\n }\n }\n\n return orderedSecrets.map((secret) => {\n const slug = normalizeDatabaseSlug(secret);\n const fromKey = originMap.get(secret) ?? \"\";\n const port = portMap[slug];\n\n const volumeName = fromKey\n ? `${fromKey.replace(/\\./g, \"_\")}_postgres_${slug}_data`\n : `postgres_${slug}_data`;\n\n const containerName = fromKey ? `${fromKey}-postgres-${slug}` : `postgres-${slug}`;\n\n return {\n secret,\n slug,\n fromKey,\n port,\n serviceName: `postgres-${slug.replace(/_/g, \"-\")}`,\n containerName,\n databaseName: `${slug}_db`,\n volumeName,\n url: `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${port}/${slug}_db`,\n };\n });\n}\n\nexport function buildRedisConfigs(\n secrets: string[],\n originMap: Map<string, string>,\n portMap: Record<string, number>,\n): RedisSecretConfig[] {\n const redisSecrets = uniqueSecrets(secrets.filter((secret) => secret.endsWith(\"_REDIS_URL\")));\n const orderedSecrets = [...redisSecrets];\n\n // Prune stale entries from removed plugins\n const currentSlugs = new Set(orderedSecrets.map(normalizeRedisSlug));\n for (const slug of Object.keys(portMap)) {\n if (!currentSlugs.has(slug)) delete portMap[slug];\n }\n\n // Sort by slug for deterministic assignment order\n orderedSecrets.sort((a, b) => normalizeRedisSlug(a).localeCompare(normalizeRedisSlug(b)));\n\n for (const secret of orderedSecrets) {\n const slug = normalizeRedisSlug(secret);\n resolvePort(slug, portMap, BASE_REDIS_PORT);\n }\n\n return orderedSecrets.map((secret) => {\n const slug = normalizeRedisSlug(secret);\n const fromKey = originMap.get(secret) ?? \"\";\n const port = portMap[slug];\n\n const volumeName = fromKey\n ? `${fromKey.replace(/\\./g, \"_\")}_redis_${slug}_data`\n : `redis_${slug}_data`;\n\n const containerName = fromKey ? `${fromKey}-redis-${slug}` : `redis-${slug}`;\n\n return {\n secret,\n slug,\n fromKey,\n port,\n serviceName: `redis-${slug.replace(/_/g, \"-\")}`,\n containerName,\n volumeName,\n url: `redis://localhost:${port}`,\n };\n });\n}\n\nfunction extractPortFromUrl(url: string): string | null {\n const match = url.match(/:(\\d{4,5})(?:\\/|$)/);\n return match?.[1] ?? null;\n}\n\nfunction resolveDevHostPort(runtimeConfig: RuntimeConfig): number {\n if (typeof runtimeConfig.host?.port === \"number\") return runtimeConfig.host.port;\n const fromUrl = runtimeConfig.host?.url ? extractPortFromUrl(runtimeConfig.host.url) : null;\n if (fromUrl) {\n const parsed = Number.parseInt(fromUrl, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 3000;\n}\n\nfunction defaultSecretValue(\n secret: string,\n databases: Map<string, DatabaseSecretConfig>,\n redisConfigs: Map<string, RedisSecretConfig>,\n options: { forExample: boolean; devHostPort?: number },\n): string {\n if (secret === \"BETTER_AUTH_SECRET\") {\n return options.forExample ? \"\" : randomBytes(32).toString(\"base64url\");\n }\n\n if (secret === \"CORS_ORIGIN\") {\n if (typeof options.devHostPort === \"number\") {\n return `http://localhost:${options.devHostPort}`;\n }\n return \"http://localhost:3000\";\n }\n\n return databases.get(secret)?.url ?? redisConfigs.get(secret)?.url ?? \"\";\n}\n\nfunction renderEnvFile(\n groups: SecretGroup[],\n databases: DatabaseSecretConfig[],\n redisConfigs: RedisSecretConfig[],\n options: { forExample: boolean; devHostPort?: number },\n): string {\n const databaseMap = new Map(databases.map((entry) => [entry.secret, entry]));\n const redisMap = new Map(redisConfigs.map((entry) => [entry.secret, entry]));\n const lines: string[] = [\n \"# Generated from configured bos secrets\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n\n for (const group of groups) {\n lines.push(`# ${group.section}`);\n for (const secret of group.secrets) {\n lines.push(`${secret}=${defaultSecretValue(secret, databaseMap, redisMap, options)}`);\n }\n lines.push(\"\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction renderDockerCompose(\n databases: DatabaseSecretConfig[],\n redisConfigs: RedisSecretConfig[],\n projectName: string,\n): string {\n const lines = [`name: ${projectName}`, \"\"];\n\n if (databases.length > 0) {\n lines.push(\n \"x-pg-common: &pg-common\",\n \" image: postgres:17-alpine\",\n \" environment: &pg-env\",\n ` POSTGRES_USER: ${POSTGRES_USER}`,\n ` POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}`,\n \" healthcheck:\",\n ' test: [\"CMD-SHELL\", \"pg_isready -U everythingdev\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n if (redisConfigs.length > 0) {\n lines.push(\n \"x-redis-common: &redis-common\",\n \" image: redis:7-alpine\",\n \" command: redis-server --appendonly yes\",\n \" healthcheck:\",\n ' test: [\"CMD\", \"redis-cli\", \"ping\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n lines.push(\"services:\");\n\n for (const database of databases) {\n lines.push(` ${database.serviceName}:`);\n lines.push(\" <<: *pg-common\");\n lines.push(` container_name: ${database.containerName}`);\n lines.push(\" environment:\");\n lines.push(\" <<: *pg-env\");\n lines.push(` POSTGRES_DB: ${database.databaseName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${database.port}:5432\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${database.volumeName}:/var/lib/postgresql/data`);\n lines.push(\"\");\n }\n\n for (const redis of redisConfigs) {\n lines.push(` ${redis.serviceName}:`);\n lines.push(\" <<: *redis-common\");\n lines.push(` container_name: ${redis.containerName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${redis.port}:6379\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${redis.volumeName}:/data`);\n lines.push(\"\");\n }\n\n lines.push(\"volumes:\");\n for (const database of databases) {\n lines.push(` ${database.volumeName}:`);\n lines.push(` name: ${database.volumeName}`);\n }\n for (const redis of redisConfigs) {\n lines.push(` ${redis.volumeName}:`);\n lines.push(` name: ${redis.volumeName}`);\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function renderEnvFileFromPlan(env: Record<string, string>, devHostPort?: number): string {\n const lines: string[] = [\n \"# Generated from bos dev infra plan\",\n \"# Update values as needed for your local environment\",\n \"\",\n ];\n const sortedKeys = Object.keys(env).sort();\n for (const key of sortedKeys) {\n let value = env[key];\n if (key === \"CORS_ORIGIN\" && devHostPort && !value) {\n value = `http://localhost:${devHostPort}`;\n }\n lines.push(`${key}=${value}`);\n }\n lines.push(\"\");\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function renderDockerComposeFromPlan(\n databases: {\n serviceName: string;\n containerName: string;\n port: number;\n volumeName: string;\n databaseName: string;\n }[],\n redis: { serviceName: string; containerName: string; port: number; volumeName: string }[],\n projectName: string,\n): string {\n const lines = [`name: ${projectName}`, \"\"];\n\n if (databases.length > 0) {\n lines.push(\n \"x-pg-common: &pg-common\",\n \" image: postgres:17-alpine\",\n \" environment: &pg-env\",\n ` POSTGRES_USER: ${POSTGRES_USER}`,\n ` POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}`,\n \" healthcheck:\",\n ' test: [\"CMD-SHELL\", \"pg_isready -U everythingdev\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n if (redis.length > 0) {\n lines.push(\n \"x-redis-common: &redis-common\",\n \" image: redis:7-alpine\",\n \" command: redis-server --appendonly yes\",\n \" healthcheck:\",\n ' test: [\"CMD\", \"redis-cli\", \"ping\"]',\n \" interval: 3s\",\n \" timeout: 3s\",\n \" retries: 5\",\n \"\",\n );\n }\n\n lines.push(\"services:\");\n\n for (const db of databases) {\n lines.push(` ${db.serviceName}:`);\n lines.push(\" <<: *pg-common\");\n lines.push(` container_name: ${db.containerName}`);\n lines.push(\" environment:\");\n lines.push(\" <<: *pg-env\");\n lines.push(` POSTGRES_DB: ${db.databaseName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${db.port}:5432\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${db.volumeName}:/var/lib/postgresql/data`);\n lines.push(\"\");\n }\n\n for (const r of redis) {\n lines.push(` ${r.serviceName}:`);\n lines.push(\" <<: *redis-common\");\n lines.push(` container_name: ${r.containerName}`);\n lines.push(\" ports:\");\n lines.push(` - \"${r.port}:6379\"`);\n lines.push(\" volumes:\");\n lines.push(` - ${r.volumeName}:/data`);\n lines.push(\"\");\n }\n\n lines.push(\"volumes:\");\n for (const db of databases) {\n lines.push(` ${db.volumeName}:`);\n lines.push(` name: ${db.volumeName}`);\n }\n for (const r of redis) {\n lines.push(` ${r.volumeName}:`);\n lines.push(` name: ${r.volumeName}`);\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function materializeInfraPlan(\n configDir: string,\n planEnv: Record<string, string>,\n planDatabases: {\n serviceName: string;\n containerName: string;\n port: number;\n volumeName: string;\n databaseName: string;\n }[],\n planRedis: { serviceName: string; containerName: string; port: number; volumeName: string }[],\n projectName: string,\n devHostPort?: number,\n): { envExampleChanged: boolean; dockerComposeChanged: boolean } {\n const envContent = renderEnvFileFromPlan(planEnv, devHostPort);\n const dockerContent = renderDockerComposeFromPlan(planDatabases, planRedis, projectName);\n\n const envExamplePath = join(configDir, \".env.example\");\n const dockerComposePath = join(configDir, \"docker-compose.yml\");\n\n return {\n envExampleChanged: syncTextFile(envExamplePath, envContent),\n dockerComposeChanged: syncTextFile(dockerComposePath, dockerContent),\n };\n}\n\nfunction syncTextFile(filePath: string, nextContent: string): boolean {\n if (existsSync(filePath) && readFileSync(filePath, \"utf-8\") === nextContent) {\n return false;\n }\n\n writeFileSync(filePath, nextContent);\n return true;\n}\n\nexport function writeGeneratedInfra(configDir: string, runtimeConfig: RuntimeConfig): string[] {\n const result = syncGeneratedInfra(configDir, runtimeConfig);\n\n if (result.staleEnvWarnings.length > 0) {\n p.log.warn(\n `.env has ${result.staleEnvWarnings.length} stale value(s) compared to .env.example:`,\n );\n for (const warning of result.staleEnvWarnings) {\n p.log.message(` ${warning}`);\n }\n }\n\n return result.secrets;\n}\n\nexport function syncGeneratedInfra(\n configDir: string,\n runtimeConfig: RuntimeConfig,\n): SyncGeneratedInfraResult {\n const { spec, portState } = buildGeneratedInfraSpec(runtimeConfig, configDir);\n const secrets = spec.groups.flatMap((group) => group.secrets);\n const envOptions: { forExample: true; devHostPort?: number } = { forExample: true };\n if (runtimeConfig.env === \"development\") {\n envOptions.devHostPort = resolveDevHostPort(runtimeConfig);\n }\n const newEnvContent = renderEnvFile(spec.groups, spec.databases, spec.redis, envOptions);\n const newDockerContent = renderDockerCompose(spec.databases, spec.redis, runtimeConfig.account);\n\n const envExamplePath = join(configDir, \".env.example\");\n const dockerComposePath = join(configDir, \"docker-compose.yml\");\n\n const staleWarnings = checkEnvStaleness(configDir, spec.databases, spec.redis);\n\n if (configDir) {\n savePortState(configDir, portState);\n }\n\n return {\n secrets,\n envExampleChanged: syncTextFile(envExamplePath, newEnvContent),\n dockerComposeChanged: syncTextFile(dockerComposePath, newDockerContent),\n staleEnvWarnings: staleWarnings,\n };\n}\n\nfunction checkEnvStaleness(\n configDir: string,\n databases: DatabaseSecretConfig[],\n redisConfigs: RedisSecretConfig[],\n): string[] {\n const envPath = join(configDir, \".env\");\n if (!existsSync(envPath)) return [];\n\n const existingEnv = readFileSync(envPath, \"utf-8\");\n const envMap = new Map<string, string>();\n for (const line of existingEnv.split(\"\\n\")) {\n const match = line.match(/^([A-Z_]+)=(.*)$/);\n if (match) envMap.set(match[1], match[2]);\n }\n\n const stale: string[] = [];\n\n for (const db of databases) {\n const existing = envMap.get(db.secret);\n if (existing && existing !== db.url) {\n const oldPort = extractPortFromUrl(existing) ?? \"?\";\n stale.push(`${db.secret}: port ${oldPort} → ${db.port}`);\n }\n }\n\n for (const redis of redisConfigs) {\n const existing = envMap.get(redis.secret);\n if (existing && existing !== redis.url) {\n const oldPort = extractPortFromUrl(existing) ?? \"?\";\n stale.push(`${redis.secret}: port ${oldPort} → ${redis.port}`);\n }\n }\n\n return stale;\n}\n\nexport function ensureEnvFile(configDir: string): void {\n const envPath = join(configDir, \".env\");\n const examplePath = join(configDir, \".env.example\");\n\n if (existsSync(envPath) || !existsSync(examplePath)) return;\n\n const content = readFileSync(examplePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const secret = randomBytes(32).toString(\"base64url\");\n const updated = lines\n .map((line) => {\n if (/^BETTER_AUTH_SECRET=/.test(line)) {\n return `BETTER_AUTH_SECRET=${secret}`;\n }\n return line;\n })\n .join(\"\\n\");\n\n writeFileSync(envPath, updated);\n p.log.info(\"Created .env from generated .env.example with generated BETTER_AUTH_SECRET\");\n}\n\nlet envLoadedDir: string | null = null;\n\nexport function loadProjectEnv(configDir: string): void {\n if (envLoadedDir === configDir) return;\n const envPath = join(configDir, \".env\");\n if (!existsSync(envPath)) return;\n\n loadDotenv({ path: envPath, processEnv: process.env, quiet: true });\n envLoadedDir = configDir;\n}\n"],"mappings":"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"}
|
package/dist/cli/init.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_http_client = require('../http-client.cjs');
|
|
3
3
|
const require_fastkv = require('../fastkv.cjs');
|
|
4
|
-
const require_merge = require('../merge.cjs');
|
|
5
4
|
const require_manifest_normalizer = require('../internal/manifest-normalizer.cjs');
|
|
6
5
|
const require_save_config = require('../utils/save-config.cjs');
|
|
7
6
|
const require_snapshot = require('./snapshot.cjs');
|
|
7
|
+
const require_helpers = require('./utils/helpers.cjs');
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
let node_path = require("node:path");
|
|
10
10
|
let node_crypto = require("node:crypto");
|
|
@@ -39,22 +39,10 @@ const OVERRIDE_WORKSPACE_MAP = {
|
|
|
39
39
|
host: ["host"],
|
|
40
40
|
plugins: []
|
|
41
41
|
};
|
|
42
|
-
function getExtendsRef(config) {
|
|
43
|
-
if (typeof config.extends === "string") return config.extends;
|
|
44
|
-
if (config.extends && typeof config.extends === "object") return require_merge.resolveExtendsRef(config.extends, "production");
|
|
45
|
-
}
|
|
46
|
-
function parseBosRef(ref) {
|
|
47
|
-
const match = ref.match(/^bos:\/\/([^/]+)\/(.+)$/);
|
|
48
|
-
if (!match?.[1] || !match[2]) return null;
|
|
49
|
-
return {
|
|
50
|
-
account: match[1],
|
|
51
|
-
gateway: match[2]
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
42
|
function readWorkspaceCatalog(sourceDir) {
|
|
55
43
|
const pkgPath = (0, node_path.join)(sourceDir, "package.json");
|
|
56
44
|
if (!(0, node_fs.existsSync)(pkgPath)) return {};
|
|
57
|
-
return { ...readJsonFile(pkgPath).workspaces?.catalog ?? {} };
|
|
45
|
+
return { ...require_helpers.readJsonFile(pkgPath).workspaces?.catalog ?? {} };
|
|
58
46
|
}
|
|
59
47
|
async function resolveCatalogChainSource(opts) {
|
|
60
48
|
const catalogs = [];
|
|
@@ -74,10 +62,10 @@ async function resolveCatalogChainSource(opts) {
|
|
|
74
62
|
let currentSourceDir = sourceDir;
|
|
75
63
|
let cleanup = async () => {};
|
|
76
64
|
if (configPath) {
|
|
77
|
-
config = readJsonFile(configPath);
|
|
65
|
+
config = require_helpers.readJsonFile(configPath);
|
|
78
66
|
currentSourceDir = (0, node_path.dirname)(configPath);
|
|
79
67
|
} else {
|
|
80
|
-
const parsed = parseBosRef(currentRef);
|
|
68
|
+
const parsed = require_helpers.parseBosRef(currentRef);
|
|
81
69
|
if (!parsed) break;
|
|
82
70
|
const sourceResult = await resolveSourceDir({
|
|
83
71
|
extendsAccount: parsed.account,
|
|
@@ -90,7 +78,7 @@ async function resolveCatalogChainSource(opts) {
|
|
|
90
78
|
cleanups.push(cleanup);
|
|
91
79
|
catalogs.push(currentSourceDir ? readWorkspaceCatalog(currentSourceDir) : {});
|
|
92
80
|
if (typeof config.repository === "string") repository = config.repository;
|
|
93
|
-
const nextExtendsRef = getExtendsRef(config);
|
|
81
|
+
const nextExtendsRef = require_helpers.getExtendsRef(config);
|
|
94
82
|
if (!nextExtendsRef) break;
|
|
95
83
|
if (nextExtendsRef.startsWith("bos://")) {
|
|
96
84
|
currentRef = nextExtendsRef;
|
|
@@ -195,9 +183,9 @@ async function resolveRepositoryViaExtendsChain(extendsAccount, extendsGateway,
|
|
|
195
183
|
repository: config.repository,
|
|
196
184
|
config
|
|
197
185
|
};
|
|
198
|
-
const extendsRef = getExtendsRef(config);
|
|
186
|
+
const extendsRef = require_helpers.getExtendsRef(config);
|
|
199
187
|
if (extendsRef) {
|
|
200
|
-
const parsed = parseBosRef(extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`);
|
|
188
|
+
const parsed = require_helpers.parseBosRef(extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`);
|
|
201
189
|
if (parsed) {
|
|
202
190
|
const result = await resolveRepositoryViaExtendsChain(parsed.account, parsed.gateway, visited);
|
|
203
191
|
if (result) return result;
|
|
@@ -345,14 +333,31 @@ function buildRootTypecheckScript(sections) {
|
|
|
345
333
|
if (sections.plugins) commands.push("if [ -d plugins ]; then for dir in plugins/*; do if [ -f \"$dir/package.json\" ]; then bun run --cwd \"$dir\" typecheck; fi; done; fi");
|
|
346
334
|
return commands.join(" && ");
|
|
347
335
|
}
|
|
336
|
+
function getParentOnlyScriptKeys(projectDir) {
|
|
337
|
+
const parentPkgPath = (0, node_path.join)(projectDir, "node_modules", "everything-dev", "package.json");
|
|
338
|
+
if (!(0, node_fs.existsSync)(parentPkgPath)) return [];
|
|
339
|
+
try {
|
|
340
|
+
const parentScripts = JSON.parse((0, node_fs.readFileSync)(parentPkgPath, "utf-8")).scripts ?? {};
|
|
341
|
+
const allChildKeys = new Set(Object.keys(buildChildRootScripts({
|
|
342
|
+
ui: true,
|
|
343
|
+
api: true,
|
|
344
|
+
host: true,
|
|
345
|
+
plugins: true
|
|
346
|
+
})));
|
|
347
|
+
allChildKeys.add("test:integration");
|
|
348
|
+
return Object.keys(parentScripts).filter((key) => !allChildKeys.has(key));
|
|
349
|
+
} catch {
|
|
350
|
+
return [];
|
|
351
|
+
}
|
|
352
|
+
}
|
|
348
353
|
function buildChildRootScripts(sections) {
|
|
349
354
|
const scripts = {
|
|
350
|
-
dev: "
|
|
351
|
-
"dev:proxy": "
|
|
352
|
-
build: "
|
|
353
|
-
deploy: "
|
|
354
|
-
publish: "
|
|
355
|
-
start: "
|
|
355
|
+
dev: "bos dev",
|
|
356
|
+
"dev:proxy": "bos dev --proxy",
|
|
357
|
+
build: "bos build",
|
|
358
|
+
deploy: "bos build --deploy",
|
|
359
|
+
publish: "bos publish",
|
|
360
|
+
start: "bos start",
|
|
356
361
|
typecheck: buildRootTypecheckScript(sections),
|
|
357
362
|
lint: "biome check .",
|
|
358
363
|
"lint:fix": "biome check --write .",
|
|
@@ -361,31 +366,34 @@ function buildChildRootScripts(sections) {
|
|
|
361
366
|
changeset: "changeset",
|
|
362
367
|
version: "changeset version",
|
|
363
368
|
release: "echo 'Packages versioned - app release handled by workflow'",
|
|
364
|
-
postinstall: "node_modules/.bin/bos types gen || true",
|
|
365
|
-
"types:gen": "node_modules/.bin/bos types gen",
|
|
366
|
-
bos: "
|
|
369
|
+
postinstall: "node node_modules/.bin/bos types gen || true",
|
|
370
|
+
"types:gen": "node node_modules/.bin/bos types gen",
|
|
371
|
+
bos: "bos"
|
|
367
372
|
};
|
|
368
373
|
if (sections.api) {
|
|
369
374
|
scripts["db:push"] = "bun run --cwd api drizzle-kit push";
|
|
370
|
-
scripts["db:studio"] = "
|
|
371
|
-
scripts["db:doctor"] = "
|
|
372
|
-
scripts["db:repair"] = "
|
|
375
|
+
scripts["db:studio"] = "bos db:studio";
|
|
376
|
+
scripts["db:doctor"] = "bos db:doctor";
|
|
377
|
+
scripts["db:repair"] = "bos db:repair";
|
|
373
378
|
scripts["db:generate"] = "bun run --cwd api drizzle-kit generate";
|
|
374
379
|
scripts["db:migrate"] = "bun run --cwd api drizzle-kit migrate";
|
|
375
|
-
scripts["test:api"] = "
|
|
376
|
-
scripts["test:integration"] = "
|
|
380
|
+
scripts["test:api"] = "bun run --cwd api test --if-present";
|
|
381
|
+
scripts["test:integration"] = "bun run --cwd api test tests/integration/ --if-present";
|
|
377
382
|
}
|
|
378
|
-
if (sections.host) scripts["test:e2e"] = "bun run --cwd host test
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
383
|
+
if (sections.host) scripts["test:e2e"] = "bun run --cwd host test --if-present";
|
|
384
|
+
const testTargets = [];
|
|
385
|
+
if (sections.api) testTargets.push("bun run --cwd api test --if-present");
|
|
386
|
+
if (sections.host) testTargets.push("bun run --cwd host test --if-present");
|
|
387
|
+
if (sections.ui) testTargets.push("bun run --cwd ui test --if-present");
|
|
388
|
+
if (sections.plugins) testTargets.push("for d in plugins/*; do [ -f \"$d/package.json\" ] && bun run --cwd \"$d\" test --if-present; done");
|
|
389
|
+
scripts.test = testTargets.length > 0 ? testTargets.join(" && ") : "echo \"No workspace directories configured\"";
|
|
382
390
|
if (sections.api || sections.host) {
|
|
383
391
|
scripts["dev:postgres"] = "docker compose up -d --wait && bun run dev";
|
|
384
392
|
scripts["dev:postgres:down"] = "docker compose down";
|
|
385
393
|
scripts["dev:postgres:reset"] = "docker compose down -v && docker compose up -d --wait";
|
|
386
394
|
}
|
|
387
|
-
if (sections.ui) scripts["dev:ui"] = "
|
|
388
|
-
if (sections.api) scripts["dev:api"] = "
|
|
395
|
+
if (sections.ui) scripts["dev:ui"] = "bos dev --ui local --api remote";
|
|
396
|
+
if (sections.api) scripts["dev:api"] = "bos dev --ui remote --api local";
|
|
389
397
|
return scripts;
|
|
390
398
|
}
|
|
391
399
|
async function personalizeConfig(destination, opts) {
|
|
@@ -471,6 +479,12 @@ async function personalizeConfig(destination, opts) {
|
|
|
471
479
|
}
|
|
472
480
|
await require_save_config.saveBosConfig(destination, config);
|
|
473
481
|
}
|
|
482
|
+
for (const relPath of ["ui/src/lib/api-types.gen.ts", "api/src/lib/plugins-types.gen.ts"]) {
|
|
483
|
+
const absolutePath = (0, node_path.join)(destination, relPath);
|
|
484
|
+
try {
|
|
485
|
+
(0, node_fs.rmSync)(absolutePath, { force: true });
|
|
486
|
+
} catch {}
|
|
487
|
+
}
|
|
474
488
|
const pkgPath = (0, node_path.join)(destination, "package.json");
|
|
475
489
|
if ((0, node_fs.existsSync)(pkgPath)) {
|
|
476
490
|
const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8"));
|
|
@@ -505,6 +519,7 @@ async function personalizeConfig(destination, opts) {
|
|
|
505
519
|
const scripts = pkg.scripts;
|
|
506
520
|
for (const [key, value] of Object.entries(childScripts)) scripts[key] = value;
|
|
507
521
|
for (const obsoleteScript of [
|
|
522
|
+
...getParentOnlyScriptKeys(destination),
|
|
508
523
|
"init",
|
|
509
524
|
"sync-catalog",
|
|
510
525
|
"db:push",
|
|
@@ -613,9 +628,9 @@ export type InferOutput<_TRoute extends string> = any;
|
|
|
613
628
|
if (has("plugins")) for (const plugin of opts.plugins ?? []) {
|
|
614
629
|
const pluginSrcDir = (0, node_path.join)(destination, "plugins", plugin, "src");
|
|
615
630
|
const pluginIndexPath = (0, node_path.join)(pluginSrcDir, "index.ts");
|
|
616
|
-
const pluginClientGenPath = (0, node_path.join)(pluginSrcDir, "plugins-client.gen.ts");
|
|
631
|
+
const pluginClientGenPath = (0, node_path.join)(pluginSrcDir, "lib", "plugins-client.gen.ts");
|
|
617
632
|
if (!(0, node_fs.existsSync)(pluginIndexPath) || (0, node_fs.existsSync)(pluginClientGenPath)) continue;
|
|
618
|
-
if (!(0, node_fs.readFileSync)(pluginIndexPath, "utf-8").includes("./plugins-client.gen")) continue;
|
|
633
|
+
if (!(0, node_fs.readFileSync)(pluginIndexPath, "utf-8").includes("./lib/plugins-client.gen")) continue;
|
|
619
634
|
(0, node_fs.writeFileSync)(pluginClientGenPath, "export type PluginsClient = Record<string, never>;\n");
|
|
620
635
|
}
|
|
621
636
|
}
|
|
@@ -664,15 +679,22 @@ function toRelativeImportPath(fromPath, toPath) {
|
|
|
664
679
|
const rel = (0, node_path.relative)((0, node_path.dirname)(toPath), fromPath);
|
|
665
680
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
666
681
|
}
|
|
667
|
-
async function runBunInstall(destination,
|
|
668
|
-
await runWithProgress("bun", ["install", "--ignore-scripts"], destination, spinner, "Installing dependencies");
|
|
682
|
+
async function runBunInstall(destination, opts) {
|
|
683
|
+
await runWithProgress("bun", ["install", "--ignore-scripts"], destination, opts?.spinner, "Installing dependencies");
|
|
669
684
|
}
|
|
670
|
-
async function runBunInstallForUpgrade(destination,
|
|
671
|
-
await runWithProgress("bun", ["install", "--force"], destination, spinner, "Installing dependencies");
|
|
685
|
+
async function runBunInstallForUpgrade(destination, opts) {
|
|
686
|
+
await runWithProgress("bun", ["install", "--force"], destination, opts?.spinner, "Installing dependencies");
|
|
672
687
|
}
|
|
673
|
-
async function runTypesGen(destination,
|
|
674
|
-
|
|
675
|
-
|
|
688
|
+
async function runTypesGen(destination, opts) {
|
|
689
|
+
const bosModule = (0, node_path.join)(destination, "node_modules", "everything-dev", "dist", "cli.mjs");
|
|
690
|
+
if ((0, node_fs.existsSync)(bosModule)) {
|
|
691
|
+
const args = [
|
|
692
|
+
bosModule,
|
|
693
|
+
"types",
|
|
694
|
+
"gen"
|
|
695
|
+
];
|
|
696
|
+
if (opts?.remotePlugins && opts.remotePlugins.length > 0) args.push("--remote-plugins", opts.remotePlugins.join(","));
|
|
697
|
+
await runWithProgress(process.execPath, args, destination, opts?.spinner, "Generating types");
|
|
676
698
|
return;
|
|
677
699
|
}
|
|
678
700
|
throw new Error("Unable to locate bos CLI for types generation");
|
|
@@ -689,7 +711,11 @@ async function runWithProgress(command, args, cwd, spinner, label) {
|
|
|
689
711
|
const child = (0, execa.execa)(command, args, {
|
|
690
712
|
cwd,
|
|
691
713
|
stdio: "inherit",
|
|
692
|
-
timeout: COMMAND_TIMEOUTS[command] ?? 2 * 6e4
|
|
714
|
+
timeout: COMMAND_TIMEOUTS[command] ?? 2 * 6e4,
|
|
715
|
+
env: {
|
|
716
|
+
...process.env,
|
|
717
|
+
BOS_NO_BANNER: "1"
|
|
718
|
+
}
|
|
693
719
|
});
|
|
694
720
|
if (spinner) {
|
|
695
721
|
const start = Date.now();
|
|
@@ -731,9 +757,6 @@ function removeInitLockfile(lockfilePath) {
|
|
|
731
757
|
if (!(0, node_fs.existsSync)(lockfilePath)) return;
|
|
732
758
|
(0, node_fs.rmSync)(lockfilePath, { force: true });
|
|
733
759
|
}
|
|
734
|
-
function readJsonFile(filePath) {
|
|
735
|
-
return JSON.parse((0, node_fs.readFileSync)(filePath, "utf-8"));
|
|
736
|
-
}
|
|
737
760
|
async function scaffoldMinimalProject(destination, parentConfig, opts) {
|
|
738
761
|
(0, node_fs.mkdirSync)(destination, { recursive: true });
|
|
739
762
|
const has = (section) => opts.overrides.includes(section);
|
|
@@ -1068,6 +1091,7 @@ exports.execCommand = execCommand;
|
|
|
1068
1091
|
exports.extractSkillsBlock = extractSkillsBlock;
|
|
1069
1092
|
exports.fetchParentConfig = fetchParentConfig;
|
|
1070
1093
|
exports.generateDatabaseMigrations = generateDatabaseMigrations;
|
|
1094
|
+
exports.getParentOnlyScriptKeys = getParentOnlyScriptKeys;
|
|
1071
1095
|
exports.personalizeAgentsMd = personalizeAgentsMd;
|
|
1072
1096
|
exports.personalizeConfig = personalizeConfig;
|
|
1073
1097
|
exports.removeInitLockfile = removeInitLockfile;
|