@percepta/create 4.1.9 → 4.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,12 @@
1
- import { a as validateProjectName, d as detectMonorepo, f as toKebabCase, m as toTitleCase, p as toSnakeCase, t as readWorkspaceManifest } from "./index.js";
1
+ import { f as detectMonorepo, h as toTitleCase, m as toSnakeCase, n as readWorkspaceManifest, o as validateProjectName, p as toKebabCase } from "./index.js";
2
2
  import { a as createOrUpdateInfraPullRequestFiles, n as INFRA_REPOSITORY, o as resolveGitHubToken, r as createInfraGitHubApi, t as INFRA_BASE_BRANCH } from "./github-BOp8VQCY.js";
3
3
  import path from "node:path";
4
4
  import chalk from "chalk";
5
5
  import fs from "fs-extra";
6
6
  import { isMap, isSeq, parseDocument } from "yaml";
7
7
  //#region src/commands/infra/register-app.ts
8
- const OS_POSTGRESQL_TERRAFORM_ALIAS = "os-postgresql-terraform";
8
+ const OS_POSTGRESQL_TERRAFORM_SUFFIX = "postgresql-terraform";
9
+ const LEGACY_OS_POSTGRESQL_TERRAFORM_ALIAS = "os-postgresql-terraform";
9
10
  const OS_POSTGRESQL_TERRAFORM_SERVICES = new Set(["os-postgresql-terraform-aws", "os-postgresql-terraform-azure"]);
10
11
  const OS_BLUEPRINT_INPUT_GROUPS = [
11
12
  {
@@ -148,8 +149,10 @@ function updateBlueprint(blueprintContent, appName, options) {
148
149
  if (options.appInstallation) {
149
150
  const installations = spec.get("installations", true);
150
151
  if (!isSeq(installations)) throw new Error("OS blueprint spec.installations must be a sequence.");
151
- changed = ensureOsPostgresqlInstallationAlias(installations) || changed;
152
- changed = addAppInstallation(document, installations, appName) || changed;
152
+ const postgresqlInstallationName = getOsPostgresqlInstallationName(getBlueprintName(document));
153
+ changed = ensureOsPostgresqlInstallationAlias(installations, postgresqlInstallationName) || changed;
154
+ changed = ensureAppInstallationPostgresqlOutputRefs(installations, postgresqlInstallationName) || changed;
155
+ changed = addAppInstallation(document, installations, appName, postgresqlInstallationName) || changed;
153
156
  }
154
157
  return changed ? document.toString() : blueprintContent;
155
158
  }
@@ -168,18 +171,52 @@ function ensureInputGroups(document, spec) {
168
171
  }
169
172
  return changed;
170
173
  }
171
- function ensureOsPostgresqlInstallationAlias(installations) {
174
+ function ensureOsPostgresqlInstallationAlias(installations, postgresqlInstallationName) {
172
175
  let changed = false;
173
176
  for (const installation of installations.items) {
174
177
  if (!isMap(installation)) continue;
175
178
  const service = installation.get("service");
176
179
  if (typeof service !== "string" || !OS_POSTGRESQL_TERRAFORM_SERVICES.has(service)) continue;
177
- if (installation.get("name") === OS_POSTGRESQL_TERRAFORM_ALIAS) continue;
178
- installation.set("name", OS_POSTGRESQL_TERRAFORM_ALIAS);
180
+ if (installation.get("name") === postgresqlInstallationName) continue;
181
+ installation.set("name", postgresqlInstallationName);
179
182
  changed = true;
180
183
  }
181
184
  return changed;
182
185
  }
186
+ function ensureAppInstallationPostgresqlOutputRefs(installations, postgresqlInstallationName) {
187
+ let changed = false;
188
+ for (const installation of installations.items) {
189
+ if (!isMap(installation)) continue;
190
+ if (isOsPostgresqlInstallation(installation)) continue;
191
+ const env = installation.get("env", true);
192
+ if (!isSeq(env)) continue;
193
+ for (const envVar of env.items) {
194
+ if (!isMap(envVar)) continue;
195
+ const valueFromOutput = envVar.get("valueFromOutput", true);
196
+ if (!isMap(valueFromOutput)) continue;
197
+ if (valueFromOutput.get("serviceInstallation") !== LEGACY_OS_POSTGRESQL_TERRAFORM_ALIAS) continue;
198
+ const outputName = valueFromOutput.get("name");
199
+ if (typeof outputName !== "string" || outputName !== "auth_database_url" && !outputName.startsWith("app_database_urls.")) continue;
200
+ valueFromOutput.set("serviceInstallation", postgresqlInstallationName);
201
+ changed = true;
202
+ }
203
+ }
204
+ return changed;
205
+ }
206
+ function isOsPostgresqlInstallation(installation) {
207
+ const service = installation.get("service");
208
+ return typeof service === "string" && OS_POSTGRESQL_TERRAFORM_SERVICES.has(service);
209
+ }
210
+ function getBlueprintName(document) {
211
+ const metadata = document.get("metadata", true);
212
+ if (!isMap(metadata)) throw new Error("OS blueprint must include a metadata map.");
213
+ const name = metadata.get("name");
214
+ if (typeof name !== "string" || name.length === 0) throw new Error("OS blueprint metadata.name must be a non-empty string.");
215
+ return name;
216
+ }
217
+ function getOsPostgresqlInstallationName(blueprintName) {
218
+ return `${blueprintName}-${OS_POSTGRESQL_TERRAFORM_SUFFIX}`;
219
+ }
183
220
  function addAppInput(document, inputs, input) {
184
221
  if (inputs.items.some((item) => isMap(item) && item.get("name") === input.name)) return false;
185
222
  inputs.add(document.createNode(input));
@@ -196,11 +233,11 @@ function addAppDatabase(document, inputs, appName) {
196
233
  defaultValue.set(appName, appDatabaseValue);
197
234
  return true;
198
235
  }
199
- function addAppInstallation(document, installations, appName) {
236
+ function addAppInstallation(document, installations, appName, postgresqlInstallationName) {
200
237
  if (installations.items.some((item) => isMap(item) && item.get("service") === appName)) return false;
201
238
  installations.add(document.createNode({
202
239
  service: appName,
203
- env: renderAppInstallationEnv(appName),
240
+ env: renderAppInstallationEnv(appName, postgresqlInstallationName),
204
241
  config: renderAppInstallationConfig(appName)
205
242
  }));
206
243
  return true;
@@ -271,14 +308,14 @@ function renderInngestSigningKeyInput() {
271
308
  description: "Shared Inngest signing key for generated OS webapps."
272
309
  };
273
310
  }
274
- function renderAppInstallationEnv(appName) {
311
+ function renderAppInstallationEnv(appName, postgresqlInstallationName) {
275
312
  const appInternalEndpoint = `http://${appName}-web-server.{{ .ryvn.env.name }}.svc.cluster.local:3000/api/inngest`;
276
313
  return [
277
314
  {
278
315
  key: "DATABASE_URL",
279
316
  isSecret: true,
280
317
  valueFromOutput: {
281
- serviceInstallation: "os-postgresql-terraform",
318
+ serviceInstallation: postgresqlInstallationName,
282
319
  name: `app_database_urls.${appName}`
283
320
  }
284
321
  },
@@ -286,7 +323,7 @@ function renderAppInstallationEnv(appName) {
286
323
  key: "AUTH_DATABASE_URL",
287
324
  isSecret: true,
288
325
  valueFromOutput: {
289
- serviceInstallation: "os-postgresql-terraform",
326
+ serviceInstallation: postgresqlInstallationName,
290
327
  name: "auth_database_url"
291
328
  }
292
329
  },
@@ -465,4 +502,4 @@ function normalizeAppName(appNameInput) {
465
502
  //#endregion
466
503
  export { registerAppCommand };
467
504
 
468
- //# sourceMappingURL=register-app-B9vKTkoI.js.map
505
+ //# sourceMappingURL=register-app-BeSQEsel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-app-BeSQEsel.js","names":[],"sources":["../src/commands/infra/register-app.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { isMap, isSeq, parseDocument } from \"yaml\";\nimport {\n toKebabCase,\n toSnakeCase,\n toTitleCase,\n} from \"../../utils/case-converters.js\";\nimport { detectMonorepo } from \"../../utils/detect-monorepo.js\";\nimport { validateProjectName } from \"../../utils/validate.js\";\nimport { readWorkspaceManifest } from \"../../utils/workspace-manifest.js\";\nimport {\n createInfraGitHubApi,\n createOrUpdateInfraPullRequestFiles,\n INFRA_BASE_BRANCH,\n INFRA_REPOSITORY,\n type InfraGitHubApi,\n type InfraPullRequestFile,\n resolveGitHubToken,\n} from \"./github.js\";\n\nconst OS_POSTGRESQL_TERRAFORM_SUFFIX = \"postgresql-terraform\";\nconst LEGACY_OS_POSTGRESQL_TERRAFORM_ALIAS = \"os-postgresql-terraform\";\nconst OS_POSTGRESQL_TERRAFORM_SERVICES = new Set([\n \"os-postgresql-terraform-aws\",\n \"os-postgresql-terraform-azure\",\n]);\nconst OS_BLUEPRINT_INPUT_GROUPS = [\n {\n name: \"general\",\n displayName: \"General\",\n description: \"Shared OS infrastructure settings.\",\n },\n {\n name: \"applications\",\n displayName: \"Applications\",\n description: \"Generated OS webapp settings.\",\n },\n {\n name: \"aws_postgresql\",\n displayName: \"AWS PostgreSQL\",\n description: \"AWS Aurora PostgreSQL settings.\",\n condition: '{{ eq EnvironmentProviderType \"aws\" }}',\n },\n {\n name: \"azure_postgresql\",\n displayName: \"Azure PostgreSQL\",\n description: \"Azure PostgreSQL Flexible Server settings.\",\n condition: '{{ eq EnvironmentProviderType \"azure\" }}',\n },\n];\n\nexport interface RegisterAppResult {\n appName: string;\n blueprintName: string;\n blueprintPath: string;\n branchName: string;\n customerSlug: string;\n pullRequestUrl: string | null;\n repository: typeof INFRA_REPOSITORY;\n status: \"already_registered\" | \"created_pr\" | \"updated_pr\";\n servicePath: string;\n targetPath: string;\n}\n\nexport async function registerApp(\n appNameInput: string,\n args: {\n cwd?: string;\n github?: InfraGitHubApi;\n } = {},\n): Promise<RegisterAppResult> {\n const appName = normalizeAppName(appNameInput);\n const cwd = args.cwd ?? process.cwd();\n const monorepoContext = await detectMonorepo(cwd);\n if (!monorepoContext.found || !monorepoContext.rootDir) {\n throw new Error(\n \"Run this command from a Mosaic customer monorepo with a .mosaic-workspace.json file.\",\n );\n }\n\n const workspaceManifest = await readWorkspaceManifest(\n monorepoContext.rootDir,\n );\n const customerSlug = workspaceManifest?.customerSlug;\n if (!customerSlug) {\n throw new Error(\n \".mosaic-workspace.json is missing customerSlug. Recreate the monorepo with a current @percepta/create.\",\n );\n }\n\n const github = args.github ?? createInfraGitHubApi(resolveGitHubToken());\n const blueprintName = `${customerSlug}-os`;\n const branchName = `blueberry/register-${customerSlug}-${appName}`;\n const blueprintPath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"blueprints\",\n `${blueprintName}.blueprint.yaml`,\n ].join(\"/\");\n const servicePath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"services\",\n `${appName}.service.yaml`,\n ].join(\"/\");\n\n const mainBlueprintFile = await github.getFile(\n blueprintPath,\n INFRA_BASE_BRANCH,\n );\n if (!mainBlueprintFile) {\n throw new Error(\n `${blueprintPath} does not exist in ${INFRA_REPOSITORY}. Run \\`pnpm mosaic infra register-os-blueprint\\` and merge that infra PR first.`,\n );\n }\n\n const mainServiceFile = await github.getFile(servicePath, INFRA_BASE_BRANCH);\n const serviceContent =\n mainServiceFile == null\n ? await readLocalServiceDefinition(monorepoContext.rootDir, appName)\n : null;\n const blueprintContent = registerAppInBlueprint(\n mainBlueprintFile.content,\n appName,\n );\n\n const files: InfraPullRequestFile[] = [];\n if (blueprintContent !== mainBlueprintFile.content) {\n files.push({\n baseFileSha: mainBlueprintFile.sha,\n content: blueprintContent,\n message: `Register ${appName} in ${blueprintName}`,\n path: blueprintPath,\n });\n }\n if (serviceContent != null) {\n files.push({\n content: serviceContent,\n message: `Register ${appName} service`,\n path: servicePath,\n });\n }\n\n if (files.length === 0) {\n return {\n appName,\n blueprintName,\n blueprintPath,\n branchName,\n customerSlug,\n pullRequestUrl: null,\n repository: INFRA_REPOSITORY,\n status: \"already_registered\",\n servicePath,\n targetPath: blueprintPath,\n };\n }\n\n const pullRequest = await createOrUpdateInfraPullRequestFiles({\n branchName,\n github,\n files,\n title: `Register ${appName} app`,\n body: [\n `Registers the ${appName} service and deployment in ${blueprintName}.`,\n \"\",\n \"Generated by `mosaic infra register-app`.\",\n ].join(\"\\n\"),\n });\n\n return {\n appName,\n blueprintName,\n blueprintPath,\n branchName,\n customerSlug,\n pullRequestUrl: pullRequest.pullRequestUrl,\n repository: INFRA_REPOSITORY,\n status: pullRequest.status,\n servicePath,\n targetPath: blueprintPath,\n };\n}\n\nexport async function registerAppCommand(appName: string): Promise<void> {\n try {\n const result = await registerApp(appName);\n\n if (result.status === \"already_registered\") {\n console.log(\n chalk.green(\"✔\"),\n `${result.appName} is already registered in ${result.repository} at`,\n chalk.cyan(result.targetPath),\n );\n return;\n }\n\n const verb =\n result.status === \"created_pr\" ? \"Created\" : \"Updated existing\";\n console.log(\n chalk.green(\"✔\"),\n `${verb} infra PR for ${result.appName}:`,\n chalk.cyan(result.pullRequestUrl),\n );\n } catch (error) {\n console.error(chalk.red(\"Error:\"), (error as Error).message);\n process.exit(1);\n }\n}\n\nexport function addAppDatabaseToBlueprint(\n blueprintContent: string,\n appName: string,\n): string {\n return updateBlueprint(blueprintContent, appName, {\n appDatabase: true,\n appInstallation: false,\n appInputs: false,\n });\n}\n\nexport function registerAppInBlueprint(\n blueprintContent: string,\n appName: string,\n): string {\n return updateBlueprint(blueprintContent, appName, {\n appDatabase: true,\n appInstallation: true,\n appInputs: true,\n });\n}\n\nfunction updateBlueprint(\n blueprintContent: string,\n appName: string,\n options: {\n appDatabase: boolean;\n appInstallation: boolean;\n appInputs: boolean;\n },\n): string {\n const document = parseDocument(blueprintContent);\n if (document.errors.length > 0) {\n throw new Error(\n `Invalid OS blueprint YAML: ${document.errors.map((error) => error.message).join(\"; \")}`,\n );\n }\n\n const spec = document.get(\"spec\", true);\n if (!isMap(spec)) {\n throw new Error(\"OS blueprint must include a spec map.\");\n }\n\n let changed = false;\n const inputs = spec.get(\"inputs\", true);\n if (!isSeq(inputs)) {\n throw new Error(\"OS blueprint spec.inputs must be a sequence.\");\n }\n\n changed = ensureInputGroups(document, spec) || changed;\n\n if (options.appInputs) {\n changed =\n addAppInput(document, inputs, renderIngressDomainInput()) || changed;\n changed =\n addAppInput(document, inputs, renderBetterAuthSecretInput(appName)) ||\n changed;\n changed =\n addAppInput(document, inputs, renderLangfusePublicKeyInput()) || changed;\n changed =\n addAppInput(document, inputs, renderLangfuseSecretKeyInput()) || changed;\n changed =\n addAppInput(document, inputs, renderInngestEventKeyInput()) || changed;\n changed =\n addAppInput(document, inputs, renderInngestSigningKeyInput()) || changed;\n }\n\n if (options.appDatabase) {\n changed = addAppDatabase(document, inputs, appName) || changed;\n }\n\n if (options.appInstallation) {\n const installations = spec.get(\"installations\", true);\n if (!isSeq(installations)) {\n throw new Error(\"OS blueprint spec.installations must be a sequence.\");\n }\n const postgresqlInstallationName = getOsPostgresqlInstallationName(\n getBlueprintName(document),\n );\n changed =\n ensureOsPostgresqlInstallationAlias(\n installations,\n postgresqlInstallationName,\n ) || changed;\n changed =\n ensureAppInstallationPostgresqlOutputRefs(\n installations,\n postgresqlInstallationName,\n ) || changed;\n changed =\n addAppInstallation(\n document,\n installations,\n appName,\n postgresqlInstallationName,\n ) || changed;\n }\n\n return changed ? document.toString() : blueprintContent;\n}\n\nfunction ensureInputGroups(\n document: ReturnType<typeof parseDocument>,\n spec: {\n get(key: string, keepScalar?: true): unknown;\n set(key: string, value: unknown): void;\n },\n): boolean {\n let changed = false;\n const inputGroups = spec.get(\"inputGroups\", true);\n\n if (inputGroups == null) {\n spec.set(\"inputGroups\", document.createNode(OS_BLUEPRINT_INPUT_GROUPS));\n return true;\n }\n\n if (!isSeq(inputGroups)) {\n throw new Error(\"OS blueprint spec.inputGroups must be a sequence.\");\n }\n\n for (const group of OS_BLUEPRINT_INPUT_GROUPS) {\n const exists = inputGroups.items.some(\n (item) => isMap(item) && item.get(\"name\") === group.name,\n );\n if (exists) continue;\n\n inputGroups.add(document.createNode(group));\n changed = true;\n }\n\n return changed;\n}\n\nfunction ensureOsPostgresqlInstallationAlias(\n installations: { items: unknown[] },\n postgresqlInstallationName: string,\n): boolean {\n let changed = false;\n\n for (const installation of installations.items) {\n if (!isMap(installation)) continue;\n\n const service = installation.get(\"service\");\n if (\n typeof service !== \"string\" ||\n !OS_POSTGRESQL_TERRAFORM_SERVICES.has(service)\n ) {\n continue;\n }\n\n if (installation.get(\"name\") === postgresqlInstallationName) continue;\n\n installation.set(\"name\", postgresqlInstallationName);\n changed = true;\n }\n\n return changed;\n}\n\nfunction ensureAppInstallationPostgresqlOutputRefs(\n installations: { items: unknown[] },\n postgresqlInstallationName: string,\n): boolean {\n let changed = false;\n\n for (const installation of installations.items) {\n if (!isMap(installation)) continue;\n if (isOsPostgresqlInstallation(installation)) continue;\n\n const env = installation.get(\"env\", true);\n if (!isSeq(env)) continue;\n\n for (const envVar of env.items) {\n if (!isMap(envVar)) continue;\n\n const valueFromOutput = envVar.get(\"valueFromOutput\", true);\n if (!isMap(valueFromOutput)) continue;\n if (\n valueFromOutput.get(\"serviceInstallation\") !==\n LEGACY_OS_POSTGRESQL_TERRAFORM_ALIAS\n ) {\n continue;\n }\n\n const outputName = valueFromOutput.get(\"name\");\n if (\n typeof outputName !== \"string\" ||\n (outputName !== \"auth_database_url\" &&\n !outputName.startsWith(\"app_database_urls.\"))\n ) {\n continue;\n }\n\n valueFromOutput.set(\"serviceInstallation\", postgresqlInstallationName);\n changed = true;\n }\n }\n\n return changed;\n}\n\nfunction isOsPostgresqlInstallation(installation: {\n get(key: string, keepScalar?: true): unknown;\n}): boolean {\n const service = installation.get(\"service\");\n return (\n typeof service === \"string\" && OS_POSTGRESQL_TERRAFORM_SERVICES.has(service)\n );\n}\n\nfunction getBlueprintName(document: ReturnType<typeof parseDocument>): string {\n const metadata = document.get(\"metadata\", true);\n if (!isMap(metadata)) {\n throw new Error(\"OS blueprint must include a metadata map.\");\n }\n\n const name = metadata.get(\"name\");\n if (typeof name !== \"string\" || name.length === 0) {\n throw new Error(\"OS blueprint metadata.name must be a non-empty string.\");\n }\n\n return name;\n}\n\nfunction getOsPostgresqlInstallationName(blueprintName: string): string {\n return `${blueprintName}-${OS_POSTGRESQL_TERRAFORM_SUFFIX}`;\n}\n\nfunction addAppInput(\n document: ReturnType<typeof parseDocument>,\n inputs: { add(value: unknown): void; items: unknown[] },\n input: Record<string, unknown> & { name: string },\n): boolean {\n if (\n inputs.items.some((item) => isMap(item) && item.get(\"name\") === input.name)\n ) {\n return false;\n }\n\n inputs.add(document.createNode(input));\n return true;\n}\n\nfunction addAppDatabase(\n document: ReturnType<typeof parseDocument>,\n inputs: { items: unknown[] },\n appName: string,\n): boolean {\n const appDatabasesInput = inputs.items.find(\n (item) => isMap(item) && item.get(\"name\") === \"app_databases\",\n );\n if (!isMap(appDatabasesInput)) {\n throw new Error(\"OS blueprint must include an app_databases input.\");\n }\n\n const defaultValue = appDatabasesInput.get(\"default\", true);\n if (!isMap(defaultValue)) {\n throw new Error(\"OS blueprint app_databases default must be a map.\");\n }\n\n if (defaultValue.has(appName)) return false;\n\n defaultValue.flow = false;\n const appDatabaseValue = document.createNode({\n schema_name: toSnakeCase(appName),\n });\n defaultValue.set(appName, appDatabaseValue);\n return true;\n}\n\nfunction addAppInstallation(\n document: ReturnType<typeof parseDocument>,\n installations: { add(value: unknown): void; items: unknown[] },\n appName: string,\n postgresqlInstallationName: string,\n): boolean {\n if (\n installations.items.some(\n (item) => isMap(item) && item.get(\"service\") === appName,\n )\n ) {\n return false;\n }\n\n installations.add(\n document.createNode({\n service: appName,\n env: renderAppInstallationEnv(appName, postgresqlInstallationName),\n config: renderAppInstallationConfig(appName),\n }),\n );\n return true;\n}\n\nfunction renderIngressDomainInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: ingressDomainInputName(),\n type: \"string\",\n group: \"applications\",\n displayName: \"Ingress Domain\",\n description: \"Shared ingress domain for generated OS webapps.\",\n default: '{{ default \"example.local\" .ryvn.env.state.public_domain.name }}',\n };\n}\n\nfunction renderBetterAuthSecretInput(\n appName: string,\n): Record<string, unknown> & { name: string } {\n return {\n name: betterAuthSecretInputName(appName),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: `${toTitleCase(appName)} Better Auth Secret`,\n description: `Generated Better Auth signing secret for ${appName}.`,\n hidden: true,\n generated: {\n type: \"random-bytes\",\n length: 32,\n },\n };\n}\n\nfunction renderLangfusePublicKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: langfusePublicKeyInputName(),\n type: \"string\",\n group: \"applications\",\n displayName: \"Langfuse Public Key\",\n description:\n \"Shared Langfuse public key for generated OS webapps. Leave empty to disable Langfuse export.\",\n default: \"\",\n };\n}\n\nfunction renderLangfuseSecretKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: langfuseSecretKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Langfuse Secret Key\",\n description:\n \"Shared Langfuse secret key for generated OS webapps. Leave unset to disable Langfuse export.\",\n condition: `{{ ne (input \"${langfusePublicKeyInputName()}\") \"\" }}`,\n };\n}\n\nfunction renderInngestEventKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: inngestEventKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Inngest Event Key\",\n description: \"Shared Inngest event key for generated OS webapps.\",\n };\n}\n\nfunction renderInngestSigningKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: inngestSigningKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Inngest Signing Key\",\n description: \"Shared Inngest signing key for generated OS webapps.\",\n };\n}\n\nfunction renderAppInstallationEnv(\n appName: string,\n postgresqlInstallationName: string,\n): Array<Record<string, unknown>> {\n const appInternalEndpoint = `http://${appName}-web-server.{{ .ryvn.env.name }}.svc.cluster.local:3000/api/inngest`;\n\n return [\n {\n key: \"DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: postgresqlInstallationName,\n name: `app_database_urls.${appName}`,\n },\n },\n {\n key: \"AUTH_DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: postgresqlInstallationName,\n name: \"auth_database_url\",\n },\n },\n {\n key: \"DATABASE_SCHEMA\",\n value: toSnakeCase(appName),\n },\n {\n key: \"INGRESS_DOMAIN\",\n valueFromInput: {\n name: ingressDomainInputName(),\n },\n },\n {\n key: \"APP_BASE_URL\",\n value: `https://${appName}.$(INGRESS_DOMAIN)`,\n },\n {\n key: \"BETTER_AUTH_URL\",\n value: `https://${appName}.$(INGRESS_DOMAIN)`,\n },\n {\n key: \"DEPLOYMENT_ENVIRONMENT\",\n value: \"{{ .ryvn.env.name }}\",\n },\n {\n key: \"BETTER_AUTH_SECRET\",\n isSecret: true,\n valueFromInput: {\n name: betterAuthSecretInputName(appName),\n },\n },\n {\n key: \"INNGEST_BASE_URL\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"inngest_base_url\",\n },\n },\n {\n key: \"INNGEST_EVENT_KEY\",\n isSecret: true,\n valueFromInput: {\n name: inngestEventKeyInputName(),\n },\n },\n {\n key: \"INNGEST_SIGNING_KEY\",\n isSecret: true,\n valueFromInput: {\n name: inngestSigningKeyInputName(),\n },\n },\n {\n key: \"INNGEST_APP_URL\",\n value: appInternalEndpoint,\n },\n {\n key: \"INNGEST_SERVE_HOST\",\n value: appInternalEndpoint,\n },\n {\n key: \"LANGFUSE_BASE_URL\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"langfuse_internal_url\",\n },\n },\n {\n key: \"LANGFUSE_PUBLIC_KEY\",\n valueFromInput: {\n name: langfusePublicKeyInputName(),\n },\n },\n {\n key: \"LANGFUSE_SECRET_KEY\",\n isSecret: true,\n valueFromInput: {\n name: langfuseSecretKeyInputName(),\n },\n },\n {\n key: \"OTEL_EXPORTER_OTLP_ENDPOINT\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"otel_exporter_otlp_endpoint\",\n },\n },\n {\n key: \"SPICEDB_ENDPOINT\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"spicedb_endpoint\",\n },\n },\n {\n key: \"SPICEDB_PRESHARED_KEY\",\n isSecret: true,\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"spicedb_preshared_key\",\n },\n },\n {\n key: \"SPICEDB_INSECURE\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"spicedb_insecure\",\n },\n },\n ];\n}\n\nfunction renderAppInstallationConfig(appName: string): string {\n const appHost = `${appName}.{{ input \"${ingressDomainInputName()}\" }}`;\n\n return [\n \"replicaCount: 1\",\n \"\",\n \"service:\",\n \" port: 3000\",\n \"\",\n \"livenessEnabled: true\",\n \"readinessEnabled: true\",\n \"startupEnabled: true\",\n \"\",\n \"resources:\",\n \" requests:\",\n ' cpu: \"100m\"',\n \" memory: 256Mi\",\n \" limits:\",\n ' cpu: \"500m\"',\n \" memory: 512Mi\",\n \"\",\n \"ingress:\",\n \" enabled: true\",\n \" className: external-nginx\",\n \" annotations:\",\n \" cert-manager.io/cluster-issuer: external-issuer\",\n ' nginx.ingress.kubernetes.io/ssl-redirect: \"true\"',\n \" hosts:\",\n ` - host: '${appHost}'`,\n \" paths:\",\n \" - path: /\",\n \" pathType: Prefix\",\n \" tls:\",\n ` - secretName: ${appName}-tls`,\n \" hosts:\",\n ` - '${appHost}'`,\n \"\",\n ].join(\"\\n\");\n}\n\nasync function readLocalServiceDefinition(\n monorepoRoot: string,\n appName: string,\n): Promise<string> {\n const serviceDefinitionPath = path.join(\n monorepoRoot,\n \"packages\",\n appName,\n \"deploy\",\n \"ryvn\",\n `${appName}.service.yaml`,\n );\n if (!(await fs.pathExists(serviceDefinitionPath))) {\n throw new Error(\n `${serviceDefinitionPath} does not exist. Add the app's Ryvn service definition before registering it in infra.`,\n );\n }\n\n const content = await fs.readFile(serviceDefinitionPath, \"utf-8\");\n validateLocalServiceDefinition(content, appName, serviceDefinitionPath);\n return content.endsWith(\"\\n\") ? content : `${content}\\n`;\n}\n\nfunction validateLocalServiceDefinition(\n content: string,\n appName: string,\n serviceDefinitionPath: string,\n): void {\n const document = parseDocument(content);\n if (document.errors.length > 0) {\n throw new Error(\n `Invalid Ryvn service YAML at ${serviceDefinitionPath}: ${document.errors.map((error) => error.message).join(\"; \")}`,\n );\n }\n\n const service = document.toJS() as {\n kind?: unknown;\n metadata?: { name?: unknown };\n };\n if (service.kind !== \"Service\" || service.metadata?.name !== appName) {\n throw new Error(\n `${serviceDefinitionPath} must define kind: Service with metadata.name: ${appName}.`,\n );\n }\n}\n\nfunction ingressDomainInputName(): string {\n return \"ingress_domain\";\n}\n\nfunction betterAuthSecretInputName(appName: string): string {\n return `${toSnakeCase(appName)}_better_auth_secret`;\n}\n\nfunction langfusePublicKeyInputName(): string {\n return \"langfuse_public_key\";\n}\n\nfunction langfuseSecretKeyInputName(): string {\n return \"langfuse_secret_key\";\n}\n\nfunction inngestEventKeyInputName(): string {\n return \"inngest_event_key\";\n}\n\nfunction inngestSigningKeyInputName(): string {\n return \"inngest_signing_key\";\n}\n\nfunction normalizeAppName(appNameInput: string): string {\n const appName = toKebabCase(appNameInput);\n const validation = validateProjectName(appName);\n if (!validation.valid) {\n throw new Error(`Invalid app name: ${validation.error}`);\n }\n return appName;\n}\n"],"mappings":"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ACE;EACF,SAAS;CACX;AACF;AAEA,SAAS,+BAEP;CACA,OAAO;EACL,MAAM,2BAA2B;EACjC,MAAM;EACN,UAAU;EACV,OAAO;EACP,aAAa;EACb,aACE;EACF,WAAW,iBAAiB,2BAA2B,EAAE;CAC3D;AACF;AAEA,SAAS,6BAEP;CACA,OAAO;EACL,MAAM,yBAAyB;EAC/B,MAAM;EACN,UAAU;EACV,OAAO;EACP,aAAa;EACb,aAAa;CACf;AACF;AAEA,SAAS,+BAEP;CACA,OAAO;EACL,MAAM,2BAA2B;EACjC,MAAM;EACN,UAAU;EACV,OAAO;EACP,aAAa;EACb,aAAa;CACf;AACF;AAEA,SAAS,yBACP,SACA,4BACgC;CAChC,MAAM,sBAAsB,UAAU,QAAQ;CAE9C,OAAO;EACL;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,qBAAqB;IACrB,MAAM,qBAAqB;GAC7B;EACF;EACA;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,qBAAqB;IACrB,MAAM;GACR;EACF;EACA;GACE,KAAK;GACL,OAAO,YAAY,OAAO;EAC5B;EACA;GACE,KAAK;GACL,gBAAgB,EACd,MAAM,uBAAuB,EAC/B;EACF;EACA;GACE,KAAK;GACL,OAAO,WAAW,QAAQ;EAC5B;EACA;GACE,KAAK;GACL,OAAO,WAAW,QAAQ;EAC5B;EACA;GACE,KAAK;GACL,OAAO;EACT;EACA;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,0BAA0B,OAAO,EACzC;EACF;EACA;GACE,KAAK;GACL,iBAAiB;IACf,uBAAuB;IACvB,MAAM;GACR;EACF;EACA;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,yBAAyB,EACjC;EACF;EACA;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,2BAA2B,EACnC;EACF;EACA;GACE,KAAK;GACL,OAAO;EACT;EACA;GACE,KAAK;GACL,OAAO;EACT;EACA;GACE,KAAK;GACL,iBAAiB;IACf,uBAAuB;IACvB,MAAM;GACR;EACF;EACA;GACE,KAAK;GACL,gBAAgB,EACd,MAAM,2BAA2B,EACnC;EACF;EACA;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,2BAA2B,EACnC;EACF;EACA;GACE,KAAK;GACL,iBAAiB;IACf,uBAAuB;IACvB,MAAM;GACR;EACF;EACA;GACE,KAAK;GACL,iBAAiB;IACf,uBAAuB;IACvB,MAAM;GACR;EACF;EACA;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,uBAAuB;IACvB,MAAM;GACR;EACF;EACA;GACE,KAAK;GACL,iBAAiB;IACf,uBAAuB;IACvB,MAAM;GACR;EACF;CACF;AACF;AAEA,SAAS,4BAA4B,SAAyB;CAC5D,MAAM,UAAU,GAAG,QAAQ,aAAa,uBAAuB,EAAE;CAEjE,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB,QAAQ;EACxB;EACA;EACA;EACA;EACA,qBAAqB,QAAQ;EAC7B;EACA,cAAc,QAAQ;EACtB;CACF,EAAE,KAAK,IAAI;AACb;AAEA,eAAe,2BACb,cACA,SACiB;CACjB,MAAM,wBAAwB,KAAK,KACjC,cACA,YACA,SACA,UACA,QACA,GAAG,QAAQ,cACb;CACA,IAAI,CAAE,MAAM,GAAG,WAAW,qBAAqB,GAC7C,MAAM,IAAI,MACR,GAAG,sBAAsB,uFAC3B;CAGF,MAAM,UAAU,MAAM,GAAG,SAAS,uBAAuB,OAAO;CAChE,+BAA+B,SAAS,SAAS,qBAAqB;CACtE,OAAO,QAAQ,SAAS,IAAI,IAAI,UAAU,GAAG,QAAQ;AACvD;AAEA,SAAS,+BACP,SACA,SACA,uBACM;CACN,MAAM,WAAW,cAAc,OAAO;CACtC,IAAI,SAAS,OAAO,SAAS,GAC3B,MAAM,IAAI,MACR,gCAAgC,sBAAsB,IAAI,SAAS,OAAO,KAAK,UAAU,MAAM,OAAO,EAAE,KAAK,IAAI,GACnH;CAGF,MAAM,UAAU,SAAS,KAAK;CAI9B,IAAI,QAAQ,SAAS,aAAa,QAAQ,UAAU,SAAS,SAC3D,MAAM,IAAI,MACR,GAAG,sBAAsB,iDAAiD,QAAQ,EACpF;AAEJ;AAEA,SAAS,yBAAiC;CACxC,OAAO;AACT;AAEA,SAAS,0BAA0B,SAAyB;CAC1D,OAAO,GAAG,YAAY,OAAO,EAAE;AACjC;AAEA,SAAS,6BAAqC;CAC5C,OAAO;AACT;AAEA,SAAS,6BAAqC;CAC5C,OAAO;AACT;AAEA,SAAS,2BAAmC;CAC1C,OAAO;AACT;AAEA,SAAS,6BAAqC;CAC5C,OAAO;AACT;AAEA,SAAS,iBAAiB,cAA8B;CACtD,MAAM,UAAU,YAAY,YAAY;CACxC,MAAM,aAAa,oBAAoB,OAAO;CAC9C,IAAI,CAAC,WAAW,OACd,MAAM,IAAI,MAAM,qBAAqB,WAAW,OAAO;CAEzD,OAAO;AACT"}
@@ -1,4 +1,4 @@
1
- import { d as detectMonorepo, m as toTitleCase, t as readWorkspaceManifest } from "./index.js";
1
+ import { f as detectMonorepo, h as toTitleCase, n as readWorkspaceManifest } from "./index.js";
2
2
  import { i as createOrUpdateInfraPullRequest, n as INFRA_REPOSITORY, o as resolveGitHubToken, r as createInfraGitHubApi, t as INFRA_BASE_BRANCH } from "./github-BOp8VQCY.js";
3
3
  import path from "node:path";
4
4
  import chalk from "chalk";
@@ -45,7 +45,8 @@ async function registerOsBlueprint(args = {}) {
45
45
  body: [
46
46
  `Registers the ${blueprintName} OS blueprint for ${customerSlug}.`,
47
47
  "",
48
- "Generated by `mosaic infra register-os-blueprint`."
48
+ "Generated by `mosaic infra register-os-blueprint`.",
49
+ "Rerunning the command before this PR is merged updates this generated branch with the current OS blueprint template."
49
50
  ].join("\n")
50
51
  });
51
52
  return {
@@ -86,4 +87,4 @@ function getOsBlueprintTemplatePath() {
86
87
  //#endregion
87
88
  export { registerOsBlueprintCommand };
88
89
 
89
- //# sourceMappingURL=register-os-blueprint-Gdyn0pN1.js.map
90
+ //# sourceMappingURL=register-os-blueprint-dfBmxSRc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-os-blueprint-dfBmxSRc.js","names":[],"sources":["../src/commands/infra/register-os-blueprint.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { toTitleCase } from \"../../utils/case-converters.js\";\nimport { detectMonorepo } from \"../../utils/detect-monorepo.js\";\nimport { readWorkspaceManifest } from \"../../utils/workspace-manifest.js\";\nimport {\n createOrUpdateInfraPullRequest,\n createInfraGitHubApi,\n INFRA_BASE_BRANCH,\n INFRA_REPOSITORY,\n type InfraGitHubApi,\n resolveGitHubToken,\n} from \"./github.js\";\n\nconst OS_BLUEPRINT_TEMPLATE = \"os.blueprint.yaml.template\";\n\nexport interface RegisterOsBlueprintResult {\n blueprintName: string;\n branchName: string;\n customerSlug: string;\n pullRequestUrl: string | null;\n repository: typeof INFRA_REPOSITORY;\n status: \"already_registered\" | \"created_pr\" | \"updated_pr\";\n targetPath: string;\n}\n\nexport async function registerOsBlueprint(\n args: {\n cwd?: string;\n github?: InfraGitHubApi;\n } = {},\n): Promise<RegisterOsBlueprintResult> {\n const cwd = args.cwd ?? process.cwd();\n const monorepoContext = await detectMonorepo(cwd);\n if (!monorepoContext.found || !monorepoContext.rootDir) {\n throw new Error(\n \"Run this command from a Mosaic customer monorepo with a .mosaic-workspace.json file.\",\n );\n }\n\n const workspaceManifest = await readWorkspaceManifest(\n monorepoContext.rootDir,\n );\n const customerSlug = workspaceManifest?.customerSlug;\n if (!customerSlug) {\n throw new Error(\n \".mosaic-workspace.json is missing customerSlug. Recreate the monorepo with a current @percepta/create.\",\n );\n }\n\n const github = args.github ?? createInfraGitHubApi(resolveGitHubToken());\n const blueprintName = `${customerSlug}-os`;\n const branchName = `blueberry/register-${blueprintName}-blueprint`;\n const targetPath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"blueprints\",\n `${blueprintName}.blueprint.yaml`,\n ].join(\"/\");\n const content = await renderOsBlueprint(customerSlug);\n\n const mainFile = await github.getFile(targetPath, INFRA_BASE_BRANCH);\n if (mainFile) {\n if (mainFile.content === content) {\n return {\n blueprintName,\n branchName,\n customerSlug,\n pullRequestUrl: null,\n repository: INFRA_REPOSITORY,\n status: \"already_registered\",\n targetPath,\n };\n }\n\n throw new Error(\n `${targetPath} already exists in ${INFRA_REPOSITORY}. Not overwriting an existing customer OS blueprint.`,\n );\n }\n\n const pullRequest = await createOrUpdateInfraPullRequest({\n branchName,\n content,\n github,\n message: `Register ${blueprintName} blueprint`,\n targetPath,\n title: `Register ${blueprintName} blueprint`,\n body: [\n `Registers the ${blueprintName} OS blueprint for ${customerSlug}.`,\n \"\",\n \"Generated by `mosaic infra register-os-blueprint`.\",\n \"Rerunning the command before this PR is merged updates this generated branch with the current OS blueprint template.\",\n ].join(\"\\n\"),\n });\n\n return {\n blueprintName,\n branchName,\n customerSlug,\n pullRequestUrl: pullRequest.pullRequestUrl,\n repository: INFRA_REPOSITORY,\n status: pullRequest.status,\n targetPath,\n };\n}\n\nexport async function registerOsBlueprintCommand(): Promise<void> {\n try {\n const result = await registerOsBlueprint();\n\n if (result.status === \"already_registered\") {\n console.log(\n chalk.green(\"✔\"),\n `${result.blueprintName} is already registered in ${result.repository} at`,\n chalk.cyan(result.targetPath),\n );\n return;\n }\n\n const verb =\n result.status === \"created_pr\" ? \"Created\" : \"Updated existing\";\n console.log(\n chalk.green(\"✔\"),\n `${verb} infra PR for ${result.blueprintName}:`,\n chalk.cyan(result.pullRequestUrl),\n );\n } catch (error) {\n console.error(chalk.red(\"Error:\"), (error as Error).message);\n process.exit(1);\n }\n}\n\nasync function renderOsBlueprint(customerSlug: string): Promise<string> {\n const template = await fs.readFile(getOsBlueprintTemplatePath(), \"utf-8\");\n const customerTitle = toTitleCase(customerSlug);\n\n return template\n .replaceAll(\"__CUSTOMER_SLUG__\", customerSlug)\n .replaceAll(\"__CUSTOMER_TITLE__\", customerTitle);\n}\n\nfunction getOsBlueprintTemplatePath(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../templates/infra\", OS_BLUEPRINT_TEMPLATE),\n path.resolve(currentDir, \"../../../templates/infra\", OS_BLUEPRINT_TEMPLATE),\n ];\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) return candidate;\n }\n\n throw new Error(\n `OS blueprint template not found. Checked: ${candidates.join(\", \")}`,\n );\n}\n"],"mappings":";;;;;;;AAgBA,MAAM,wBAAwB;AAY9B,eAAsB,oBACpB,OAGI,CAAC,GAC+B;CAEpC,MAAM,kBAAkB,MAAM,eADlB,KAAK,OAAO,QAAQ,IAAI,CACY;CAChD,IAAI,CAAC,gBAAgB,SAAS,CAAC,gBAAgB,SAC7C,MAAM,IAAI,MACR,sFACF;CAMF,MAAM,gBAAe,MAHW,sBAC9B,gBAAgB,OAClB,IACwC;CACxC,IAAI,CAAC,cACH,MAAM,IAAI,MACR,wGACF;CAGF,MAAM,SAAS,KAAK,UAAU,qBAAqB,mBAAmB,CAAC;CACvE,MAAM,gBAAgB,GAAG,aAAa;CACtC,MAAM,aAAa,sBAAsB,cAAc;CACvD,MAAM,aAAa;EACjB;EACA;EACA;EACA;EACA,GAAG,cAAc;CACnB,EAAE,KAAK,GAAG;CACV,MAAM,UAAU,MAAM,kBAAkB,YAAY;CAEpD,MAAM,WAAW,MAAM,OAAO,QAAQ,YAAY,iBAAiB;CACnE,IAAI,UAAU;EACZ,IAAI,SAAS,YAAY,SACvB,OAAO;GACL;GACA;GACA;GACA,gBAAgB;GAChB,YAAY;GACZ,QAAQ;GACR;EACF;EAGF,MAAM,IAAI,MACR,GAAG,WAAW,qBAAqB,iBAAiB,qDACtD;CACF;CAEA,MAAM,cAAc,MAAM,+BAA+B;EACvD;EACA;EACA;EACA,SAAS,YAAY,cAAc;EACnC;EACA,OAAO,YAAY,cAAc;EACjC,MAAM;GACJ,iBAAiB,cAAc,oBAAoB,aAAa;GAChE;GACA;GACA;EACF,EAAE,KAAK,IAAI;CACb,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA,gBAAgB,YAAY;EAC5B,YAAY;EACZ,QAAQ,YAAY;EACpB;CACF;AACF;AAEA,eAAsB,6BAA4C;CAChE,IAAI;EACF,MAAM,SAAS,MAAM,oBAAoB;EAEzC,IAAI,OAAO,WAAW,sBAAsB;GAC1C,QAAQ,IACN,MAAM,MAAM,GAAG,GACf,GAAG,OAAO,cAAc,4BAA4B,OAAO,WAAW,MACtE,MAAM,KAAK,OAAO,UAAU,CAC9B;GACA;EACF;EAEA,MAAM,OACJ,OAAO,WAAW,eAAe,YAAY;EAC/C,QAAQ,IACN,MAAM,MAAM,GAAG,GACf,GAAG,KAAK,gBAAgB,OAAO,cAAc,IAC7C,MAAM,KAAK,OAAO,cAAc,CAClC;CACF,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAI,QAAQ,GAAI,MAAgB,OAAO;EAC3D,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,eAAe,kBAAkB,cAAuC;CACtE,MAAM,WAAW,MAAM,GAAG,SAAS,2BAA2B,GAAG,OAAO;CACxE,MAAM,gBAAgB,YAAY,YAAY;CAE9C,OAAO,SACJ,WAAW,qBAAqB,YAAY,EAC5C,WAAW,sBAAsB,aAAa;AACnD;AAEA,SAAS,6BAAqC;CAC5C,MAAM,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;CAC9D,MAAM,aAAa,CACjB,KAAK,QAAQ,YAAY,sBAAsB,qBAAqB,GACpE,KAAK,QAAQ,YAAY,4BAA4B,qBAAqB,CAC5E;CAEA,KAAK,MAAM,aAAa,YACtB,IAAI,GAAG,WAAW,SAAS,GAAG,OAAO;CAGvC,MAAM,IAAI,MACR,6CAA6C,WAAW,KAAK,IAAI,GACnE;AACF"}
@@ -0,0 +1,27 @@
1
+ import { l as readManifest } from "./index.js";
2
+ import chalk from "chalk";
3
+ //#region src/commands/status.ts
4
+ async function statusCommand() {
5
+ const cwd = process.cwd();
6
+ try {
7
+ const manifest = await readManifest(cwd);
8
+ console.log();
9
+ console.log(chalk.bold("Mosaic Template Status"));
10
+ console.log();
11
+ console.log(chalk.dim(" Template type:"), manifest.templateType);
12
+ console.log(chalk.dim(" Current version:"), manifest.templateVersion);
13
+ console.log(chalk.dim(" Template commit:"), manifest.templateCommit);
14
+ console.log(chalk.dim(" Created:"), manifest.createdAt);
15
+ if (manifest.lastSyncedAt) console.log(chalk.dim(" Last synced:"), manifest.lastSyncedAt);
16
+ console.log();
17
+ console.log(chalk.dim(" Run from monorepo root:"), `pnpm mosaic sync`);
18
+ console.log();
19
+ } catch (error) {
20
+ console.error(chalk.red(error.message));
21
+ process.exit(1);
22
+ }
23
+ }
24
+ //#endregion
25
+ export { statusCommand };
26
+
27
+ //# sourceMappingURL=status-C8SBzB-K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-C8SBzB-K.js","names":[],"sources":["../src/commands/status.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { readManifest } from \"../utils/manifest.js\";\n\nexport async function statusCommand(): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n\n console.log();\n console.log(chalk.bold(\"Mosaic Template Status\"));\n console.log();\n console.log(chalk.dim(\" Template type:\"), manifest.templateType);\n console.log(chalk.dim(\" Current version:\"), manifest.templateVersion);\n console.log(chalk.dim(\" Template commit:\"), manifest.templateCommit);\n console.log(chalk.dim(\" Created:\"), manifest.createdAt);\n if (manifest.lastSyncedAt) {\n console.log(chalk.dim(\" Last synced:\"), manifest.lastSyncedAt);\n }\n\n console.log();\n console.log(chalk.dim(\" Run from monorepo root:\"), `pnpm mosaic sync`);\n\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;AAGA,eAAsB,gBAA+B;CACnD,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI;EACF,MAAM,WAAW,MAAM,aAAa,GAAG;EAEvC,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,KAAK,wBAAwB,CAAC;EAChD,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,kBAAkB,GAAG,SAAS,YAAY;EAChE,QAAQ,IAAI,MAAM,IAAI,oBAAoB,GAAG,SAAS,eAAe;EACrE,QAAQ,IAAI,MAAM,IAAI,oBAAoB,GAAG,SAAS,cAAc;EACpE,QAAQ,IAAI,MAAM,IAAI,YAAY,GAAG,SAAS,SAAS;EACvD,IAAI,SAAS,cACX,QAAQ,IAAI,MAAM,IAAI,gBAAgB,GAAG,SAAS,YAAY;EAGhE,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,2BAA2B,GAAG,kBAAkB;EAEtE,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}
@@ -0,0 +1,280 @@
1
+ import { l as readManifest, n as readWorkspaceManifest, s as derivePlaceholders, t as getWorkspaceManifestPath } from "./index.js";
2
+ import { i as getTemplateVersionFromTag, n as getLatestTemplateTag, r as getTemplateDiff } from "./git-ops-BNpQnEc1.js";
3
+ import { execFileSync } from "node:child_process";
4
+ import path from "node:path";
5
+ import chalk from "chalk";
6
+ import fs from "fs-extra";
7
+ import os from "node:os";
8
+ //#region src/utils/mosaic-source.ts
9
+ const DEFAULT_MOSAIC_REPO_URL = "git@github.com:Percepta-Core/mosaic.git";
10
+ const DEFAULT_MOSAIC_REPO_REF = "main";
11
+ async function resolveMosaicSource() {
12
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "mosaic-sync-"));
13
+ const checkoutPath = path.join(tempRoot, "mosaic");
14
+ console.log(chalk.dim(" Checking out Mosaic template source:"), chalk.cyan(`${DEFAULT_MOSAIC_REPO_URL}#${DEFAULT_MOSAIC_REPO_REF}`));
15
+ try {
16
+ execFileSync("git", [
17
+ "clone",
18
+ "--filter=blob:none",
19
+ "--branch",
20
+ DEFAULT_MOSAIC_REPO_REF,
21
+ DEFAULT_MOSAIC_REPO_URL,
22
+ checkoutPath
23
+ ], {
24
+ encoding: "utf-8",
25
+ stdio: [
26
+ "ignore",
27
+ "pipe",
28
+ "pipe"
29
+ ]
30
+ });
31
+ } catch (error) {
32
+ await fs.remove(tempRoot);
33
+ throw new Error(`Failed to check out Mosaic from ${DEFAULT_MOSAIC_REPO_URL}#${DEFAULT_MOSAIC_REPO_REF}: ${error.message}`);
34
+ }
35
+ let commit;
36
+ try {
37
+ commit = execFileSync("git", [
38
+ "rev-parse",
39
+ "--short",
40
+ "HEAD"
41
+ ], {
42
+ cwd: checkoutPath,
43
+ encoding: "utf-8"
44
+ }).trim();
45
+ } catch (error) {
46
+ await fs.remove(tempRoot);
47
+ throw new Error(`Failed to read commit from Mosaic checkout: ${error.message}`);
48
+ }
49
+ return {
50
+ path: checkoutPath,
51
+ label: `${DEFAULT_MOSAIC_REPO_URL}#${DEFAULT_MOSAIC_REPO_REF} (${commit})`,
52
+ cleanup: () => fs.remove(tempRoot)
53
+ };
54
+ }
55
+ //#endregion
56
+ //#region src/commands/sync.ts
57
+ const CONTEXT_FILENAME = ".mosaic-sync-context.md";
58
+ const MONOREPO_TEMPLATE_TYPE = "monorepo";
59
+ const MONOREPO_TEMPLATE_PATH = "packages/blueberry/templates/monorepo";
60
+ const PACKAGE_MANIFEST_FILENAME = ".mosaic-template.json";
61
+ const DISCOVERY_SKIP_DIRS = new Set([
62
+ ".git",
63
+ ".next",
64
+ ".turbo",
65
+ ".vercel",
66
+ "coverage",
67
+ "dist",
68
+ "node_modules"
69
+ ]);
70
+ function generateSyncContext(target, toVersion, diff, notes, sourceLabel) {
71
+ let content = `# Mosaic Sync Context
72
+
73
+ ## Template Info
74
+ - **Target:** ${target.label}
75
+ - **Template:** ${target.templateType}
76
+ - **Current version:** ${target.currentVersion}
77
+ - **Target version:** ${toVersion}
78
+ - **Mosaic source:** ${sourceLabel}
79
+
80
+ ## Placeholder Mappings
81
+
82
+ When applying template changes, replace these placeholder tokens with the actual values:
83
+
84
+ | Placeholder | Value |
85
+ |------------|-------|
86
+ ${Object.entries(target.placeholders).map(([k, v]) => `| \`${k}\` | \`${v}\` |`).join("\n")}
87
+
88
+ ## Template Changes (${target.currentVersion} → ${toVersion})
89
+
90
+ \`\`\`diff
91
+ ${diff}
92
+ \`\`\`
93
+ `;
94
+ if (notes.trim()) content += `
95
+ ## Divergence Notes (from mosaic-template-notes.md)
96
+
97
+ ${notes}
98
+ `;
99
+ content += `
100
+ ## Instructions
101
+
102
+ 1. Apply the template changes above to this workspace or package
103
+ 2. When you see placeholder tokens (e.g. \`__APP_NAME__\`), replace them with the actual values from the mapping table
104
+ 3. Check the divergence notes — preserve intentional divergences
105
+ 4. For files not modified locally: apply changes directly
106
+ 5. For files modified locally: merge intelligently, preserving local customizations
107
+ 6. After applying all changes, run: \`pnpm install && pnpm build && pnpm lint\`
108
+ 7. ${target.manifestUpdateInstruction(toVersion)}
109
+ 8. If you made decisions about merge conflicts and this target has \`mosaic-template-notes.md\`, add notes there
110
+ 9. Delete this file (\`.mosaic-sync-context.md\`) when done
111
+ `;
112
+ return content;
113
+ }
114
+ function targetFromManifest(dir, manifest) {
115
+ return {
116
+ dir,
117
+ label: path.relative(process.cwd(), dir) || ".",
118
+ templateType: manifest.templateType,
119
+ currentVersion: manifest.templateVersion,
120
+ templatePath: manifest.source.templatePath,
121
+ placeholders: manifest.placeholders,
122
+ contextFileName: CONTEXT_FILENAME,
123
+ notesFileName: "mosaic-template-notes.md",
124
+ manifestUpdateInstruction: (toVersion) => `Update \`.mosaic-template.json\`: set \`templateVersion\` to \`"${toVersion}"\` and update \`templateCommit\` to the Mosaic source used for this sync`
125
+ };
126
+ }
127
+ async function readRootPackageJson(rootDir) {
128
+ const packageJsonPath = path.join(rootDir, "package.json");
129
+ if (!await fs.pathExists(packageJsonPath)) return {};
130
+ return JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
131
+ }
132
+ async function targetFromWorkspaceManifest(rootDir, manifest) {
133
+ const packageJson = await readRootPackageJson(rootDir);
134
+ const repoName = packageJson.name ?? path.basename(rootDir);
135
+ const title = packageJson.description ?? repoName;
136
+ return {
137
+ dir: rootDir,
138
+ label: ".",
139
+ templateType: MONOREPO_TEMPLATE_TYPE,
140
+ currentVersion: manifest.monorepoTemplateVersion,
141
+ templatePath: MONOREPO_TEMPLATE_PATH,
142
+ placeholders: {
143
+ ...derivePlaceholders(repoName, title, repoName, manifest.customerSlug),
144
+ __CREATE_PACKAGE__: manifest.createPackage,
145
+ __CREATE_VERSION__: manifest.createVersion
146
+ },
147
+ contextFileName: CONTEXT_FILENAME,
148
+ manifestUpdateInstruction: (toVersion) => `Update \`.mosaic-workspace.json\`: set \`monorepoTemplateVersion\` to \`"${toVersion}"\`; when package contexts are applied, keep \`compatibleTemplates\` aligned with the synced package template versions`
149
+ };
150
+ }
151
+ async function findPackageManifestDirs(rootDir) {
152
+ const dirs = [];
153
+ const resolvedRootDir = path.resolve(rootDir);
154
+ async function walk(dir) {
155
+ const entries = await fs.readdir(dir, { withFileTypes: true });
156
+ if (path.resolve(dir) !== resolvedRootDir && entries.some((entry) => entry.name === PACKAGE_MANIFEST_FILENAME)) {
157
+ dirs.push(dir);
158
+ return;
159
+ }
160
+ for (const entry of entries) {
161
+ if (!entry.isDirectory() || DISCOVERY_SKIP_DIRS.has(entry.name)) continue;
162
+ await walk(path.join(dir, entry.name));
163
+ }
164
+ }
165
+ await walk(rootDir);
166
+ return dirs.sort((a, b) => a.localeCompare(b));
167
+ }
168
+ function getTargetTag(args) {
169
+ const latest = getLatestTemplateTag(args.target.templateType, args.mosaicRepoPath);
170
+ if (!latest) throw new Error(`No template tags found for "${args.target.templateType}". Run 'pnpm template:tag' in the mosaic repo first.`);
171
+ return latest;
172
+ }
173
+ async function syncTarget(args) {
174
+ const { target, mosaicRepoPath, sourceLabel } = args;
175
+ const fromTag = `template/${target.templateType}/${target.currentVersion}`;
176
+ const toTag = getTargetTag({
177
+ target,
178
+ mosaicRepoPath
179
+ });
180
+ const toVersion = getTemplateVersionFromTag(toTag);
181
+ if (toVersion === target.currentVersion) return {
182
+ target,
183
+ status: "up-to-date",
184
+ toVersion
185
+ };
186
+ const diff = getTemplateDiff(mosaicRepoPath, target.templatePath, fromTag, toTag);
187
+ if (!diff.trim()) return {
188
+ target,
189
+ status: "no-template-changes",
190
+ toVersion
191
+ };
192
+ let notes = "";
193
+ if (target.notesFileName) {
194
+ const notesPath = path.join(target.dir, target.notesFileName);
195
+ if (await fs.pathExists(notesPath)) notes = await fs.readFile(notesPath, "utf-8");
196
+ }
197
+ const context = generateSyncContext(target, toVersion, diff, notes, sourceLabel);
198
+ return {
199
+ target,
200
+ status: "generated",
201
+ toVersion,
202
+ contextPath: path.join(target.dir, target.contextFileName),
203
+ context
204
+ };
205
+ }
206
+ async function writeGeneratedContexts(results) {
207
+ for (const result of results) {
208
+ if (result.status !== "generated") continue;
209
+ if (!result.contextPath || result.context === void 0) throw new Error(`Missing generated sync context for ${result.target.label}.`);
210
+ await fs.writeFile(result.contextPath, result.context);
211
+ }
212
+ }
213
+ function printWorkspaceSummary(rootDir, results) {
214
+ const generated = results.filter((result) => result.status === "generated");
215
+ const skipped = results.filter((result) => result.status !== "generated");
216
+ if (generated.length === 0) {
217
+ const noTemplateChanges = results.filter((result) => result.status === "no-template-changes");
218
+ if (noTemplateChanges.length === 0) {
219
+ console.log(chalk.green("Already up to date."));
220
+ return;
221
+ }
222
+ console.log(chalk.green("No template file changes between versions."));
223
+ for (const result of noTemplateChanges) console.log(chalk.dim(" "), `${result.target.label}: ${result.target.currentVersion} → ${result.toVersion}`);
224
+ console.log(chalk.dim(" Update manifests manually to record these target versions."));
225
+ console.log(chalk.dim(" No sync contexts generated."));
226
+ return;
227
+ }
228
+ console.log();
229
+ console.log(chalk.bold("Workspace Sync Contexts Generated"));
230
+ console.log();
231
+ for (const result of generated) {
232
+ console.log(chalk.dim(" Context:"), path.relative(rootDir, result.contextPath ?? result.target.dir) || ".");
233
+ console.log(chalk.dim(" "), `${result.target.templateType}: ${result.target.currentVersion} → ${result.toVersion}`);
234
+ }
235
+ if (skipped.length > 0) console.log(chalk.dim(` ${skipped.length} target${skipped.length === 1 ? "" : "s"} already up to date or unchanged.`));
236
+ console.log();
237
+ console.log("Next steps:");
238
+ console.log(chalk.dim(" 1."), "Open Claude Code at the monorepo root");
239
+ console.log(chalk.dim(" 2."), `Tell Claude: "Read every ${CONTEXT_FILENAME} and apply the template changes"`);
240
+ console.log(chalk.dim(" 3."), `Review Claude's changes, then delete the generated ${CONTEXT_FILENAME} files`);
241
+ console.log();
242
+ }
243
+ async function syncWorkspace(args) {
244
+ const workspaceManifest = await readWorkspaceManifest(args.cwd);
245
+ if (!workspaceManifest) throw new Error(`No .mosaic-workspace.json found in ${args.cwd}. Run sync from the customer monorepo root.`);
246
+ const packageDirs = await findPackageManifestDirs(args.cwd);
247
+ const targets = [await targetFromWorkspaceManifest(args.cwd, workspaceManifest), ...await Promise.all(packageDirs.map(async (packageDir) => targetFromManifest(packageDir, await readManifest(packageDir))))];
248
+ const results = [];
249
+ for (const target of targets) results.push(await syncTarget({
250
+ target,
251
+ mosaicRepoPath: args.mosaicRepoPath,
252
+ sourceLabel: args.sourceLabel
253
+ }));
254
+ await writeGeneratedContexts(results);
255
+ printWorkspaceSummary(args.cwd, results);
256
+ }
257
+ async function syncCommand() {
258
+ const cwd = process.cwd();
259
+ let source;
260
+ let failed = false;
261
+ try {
262
+ if (!await fs.pathExists(getWorkspaceManifestPath(cwd))) throw new Error(`No .mosaic-workspace.json found in ${cwd}. Run sync from the customer monorepo root.`);
263
+ source = await resolveMosaicSource();
264
+ await syncWorkspace({
265
+ cwd,
266
+ mosaicRepoPath: source.path,
267
+ sourceLabel: source.label
268
+ });
269
+ } catch (error) {
270
+ console.error(chalk.red(error.message));
271
+ failed = true;
272
+ } finally {
273
+ await source?.cleanup?.();
274
+ }
275
+ if (failed) process.exitCode = 1;
276
+ }
277
+ //#endregion
278
+ export { syncCommand };
279
+
280
+ //# sourceMappingURL=sync-Lsfz8ZH4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-Lsfz8ZH4.js","names":[],"sources":["../src/utils/mosaic-source.ts","../src/commands/sync.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\n\nexport const DEFAULT_MOSAIC_REPO_URL =\n \"git@github.com:Percepta-Core/mosaic.git\";\nexport const DEFAULT_MOSAIC_REPO_REF = \"main\";\n\nexport interface ResolvedMosaicSource {\n path: string;\n label: string;\n cleanup?: () => Promise<void>;\n}\n\nexport async function resolveMosaicSource(): Promise<ResolvedMosaicSource> {\n const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"mosaic-sync-\"));\n const checkoutPath = path.join(tempRoot, \"mosaic\");\n\n console.log(\n chalk.dim(\" Checking out Mosaic template source:\"),\n chalk.cyan(`${DEFAULT_MOSAIC_REPO_URL}#${DEFAULT_MOSAIC_REPO_REF}`),\n );\n\n try {\n execFileSync(\n \"git\",\n [\n \"clone\",\n \"--filter=blob:none\",\n \"--branch\",\n DEFAULT_MOSAIC_REPO_REF,\n DEFAULT_MOSAIC_REPO_URL,\n checkoutPath,\n ],\n {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n );\n } catch (error) {\n await fs.remove(tempRoot);\n throw new Error(\n `Failed to check out Mosaic from ${DEFAULT_MOSAIC_REPO_URL}#${DEFAULT_MOSAIC_REPO_REF}: ${\n (error as Error).message\n }`,\n );\n }\n\n let commit: string;\n try {\n commit = execFileSync(\"git\", [\"rev-parse\", \"--short\", \"HEAD\"], {\n cwd: checkoutPath,\n encoding: \"utf-8\",\n }).trim();\n } catch (error) {\n await fs.remove(tempRoot);\n throw new Error(\n `Failed to read commit from Mosaic checkout: ${(error as Error).message}`,\n );\n }\n\n return {\n path: checkoutPath,\n label: `${DEFAULT_MOSAIC_REPO_URL}#${DEFAULT_MOSAIC_REPO_REF} (${commit})`,\n cleanup: () => fs.remove(tempRoot),\n };\n}\n","import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport {\n getLatestTemplateTag,\n getTemplateVersionFromTag,\n getTemplateDiff,\n} from \"../utils/git-ops.js\";\nimport {\n derivePlaceholders,\n readManifest,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\nimport {\n resolveMosaicSource,\n type ResolvedMosaicSource,\n} from \"../utils/mosaic-source.js\";\nimport {\n getWorkspaceManifestPath,\n readWorkspaceManifest,\n type MosaicWorkspaceManifest,\n} from \"../utils/workspace-manifest.js\";\n\ninterface SyncTarget {\n dir: string;\n label: string;\n templateType: string;\n currentVersion: string;\n templatePath: string;\n placeholders: Record<string, string>;\n contextFileName: string;\n notesFileName?: string;\n manifestUpdateInstruction: (toVersion: string) => string;\n}\n\ninterface SyncResult {\n target: SyncTarget;\n status: \"generated\" | \"up-to-date\" | \"no-template-changes\";\n toVersion: string;\n contextPath?: string;\n context?: string;\n}\n\nconst CONTEXT_FILENAME = \".mosaic-sync-context.md\";\nconst MONOREPO_TEMPLATE_TYPE = \"monorepo\";\nconst MONOREPO_TEMPLATE_PATH = \"packages/blueberry/templates/monorepo\";\nconst PACKAGE_MANIFEST_FILENAME = \".mosaic-template.json\";\n\nconst DISCOVERY_SKIP_DIRS = new Set([\n \".git\",\n \".next\",\n \".turbo\",\n \".vercel\",\n \"coverage\",\n \"dist\",\n \"node_modules\",\n]);\n\nfunction generateSyncContext(\n target: SyncTarget,\n toVersion: string,\n diff: string,\n notes: string,\n sourceLabel: string,\n): string {\n let content = `# Mosaic Sync Context\n\n## Template Info\n- **Target:** ${target.label}\n- **Template:** ${target.templateType}\n- **Current version:** ${target.currentVersion}\n- **Target version:** ${toVersion}\n- **Mosaic source:** ${sourceLabel}\n\n## Placeholder Mappings\n\nWhen applying template changes, replace these placeholder tokens with the actual values:\n\n| Placeholder | Value |\n|------------|-------|\n${Object.entries(target.placeholders)\n .map(([k, v]) => `| \\`${k}\\` | \\`${v}\\` |`)\n .join(\"\\n\")}\n\n## Template Changes (${target.currentVersion} → ${toVersion})\n\n\\`\\`\\`diff\n${diff}\n\\`\\`\\`\n`;\n\n if (notes.trim()) {\n content += `\n## Divergence Notes (from mosaic-template-notes.md)\n\n${notes}\n`;\n }\n\n content += `\n## Instructions\n\n1. Apply the template changes above to this workspace or package\n2. When you see placeholder tokens (e.g. \\`__APP_NAME__\\`), replace them with the actual values from the mapping table\n3. Check the divergence notes — preserve intentional divergences\n4. For files not modified locally: apply changes directly\n5. For files modified locally: merge intelligently, preserving local customizations\n6. After applying all changes, run: \\`pnpm install && pnpm build && pnpm lint\\`\n7. ${target.manifestUpdateInstruction(toVersion)}\n8. If you made decisions about merge conflicts and this target has \\`mosaic-template-notes.md\\`, add notes there\n9. Delete this file (\\`.mosaic-sync-context.md\\`) when done\n`;\n\n return content;\n}\n\nfunction targetFromManifest(dir: string, manifest: MosaicManifest): SyncTarget {\n return {\n dir,\n label: path.relative(process.cwd(), dir) || \".\",\n templateType: manifest.templateType,\n currentVersion: manifest.templateVersion,\n templatePath: manifest.source.templatePath,\n placeholders: manifest.placeholders,\n contextFileName: CONTEXT_FILENAME,\n notesFileName: \"mosaic-template-notes.md\",\n manifestUpdateInstruction: (toVersion) =>\n `Update \\`.mosaic-template.json\\`: set \\`templateVersion\\` to \\`\"${toVersion}\"\\` and update \\`templateCommit\\` to the Mosaic source used for this sync`,\n };\n}\n\nasync function readRootPackageJson(rootDir: string): Promise<{\n name?: string;\n description?: string;\n}> {\n const packageJsonPath = path.join(rootDir, \"package.json\");\n if (!(await fs.pathExists(packageJsonPath))) return {};\n return JSON.parse(await fs.readFile(packageJsonPath, \"utf-8\")) as {\n name?: string;\n description?: string;\n };\n}\n\nasync function targetFromWorkspaceManifest(\n rootDir: string,\n manifest: MosaicWorkspaceManifest,\n): Promise<SyncTarget> {\n const packageJson = await readRootPackageJson(rootDir);\n const repoName = packageJson.name ?? path.basename(rootDir);\n const title = packageJson.description ?? repoName;\n return {\n dir: rootDir,\n label: \".\",\n templateType: MONOREPO_TEMPLATE_TYPE,\n currentVersion: manifest.monorepoTemplateVersion,\n templatePath: MONOREPO_TEMPLATE_PATH,\n placeholders: {\n ...derivePlaceholders(repoName, title, repoName, manifest.customerSlug),\n __CREATE_PACKAGE__: manifest.createPackage,\n __CREATE_VERSION__: manifest.createVersion,\n },\n contextFileName: CONTEXT_FILENAME,\n manifestUpdateInstruction: (toVersion) =>\n `Update \\`.mosaic-workspace.json\\`: set \\`monorepoTemplateVersion\\` to \\`\"${toVersion}\"\\`; when package contexts are applied, keep \\`compatibleTemplates\\` aligned with the synced package template versions`,\n };\n}\n\nasync function findPackageManifestDirs(rootDir: string): Promise<string[]> {\n const dirs: string[] = [];\n const resolvedRootDir = path.resolve(rootDir);\n\n async function walk(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n if (\n path.resolve(dir) !== resolvedRootDir &&\n entries.some((entry) => entry.name === PACKAGE_MANIFEST_FILENAME)\n ) {\n dirs.push(dir);\n return;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory() || DISCOVERY_SKIP_DIRS.has(entry.name)) {\n continue;\n }\n await walk(path.join(dir, entry.name));\n }\n }\n\n await walk(rootDir);\n return dirs.sort((a, b) => a.localeCompare(b));\n}\n\nfunction getTargetTag(args: {\n target: SyncTarget;\n mosaicRepoPath: string;\n}): string {\n const latest = getLatestTemplateTag(\n args.target.templateType,\n args.mosaicRepoPath,\n );\n if (!latest) {\n throw new Error(\n `No template tags found for \"${args.target.templateType}\". Run 'pnpm template:tag' in the mosaic repo first.`,\n );\n }\n return latest;\n}\n\nasync function syncTarget(args: {\n target: SyncTarget;\n mosaicRepoPath: string;\n sourceLabel: string;\n}): Promise<SyncResult> {\n const { target, mosaicRepoPath, sourceLabel } = args;\n const fromTag = `template/${target.templateType}/${target.currentVersion}`;\n const toTag = getTargetTag({ target, mosaicRepoPath });\n const toVersion = getTemplateVersionFromTag(toTag);\n\n if (toVersion === target.currentVersion) {\n return { target, status: \"up-to-date\", toVersion };\n }\n\n const diff = getTemplateDiff(\n mosaicRepoPath,\n target.templatePath,\n fromTag,\n toTag,\n );\n\n if (!diff.trim()) {\n return { target, status: \"no-template-changes\", toVersion };\n }\n\n let notes = \"\";\n if (target.notesFileName) {\n const notesPath = path.join(target.dir, target.notesFileName);\n if (await fs.pathExists(notesPath)) {\n notes = await fs.readFile(notesPath, \"utf-8\");\n }\n }\n\n const context = generateSyncContext(\n target,\n toVersion,\n diff,\n notes,\n sourceLabel,\n );\n const contextPath = path.join(target.dir, target.contextFileName);\n\n return { target, status: \"generated\", toVersion, contextPath, context };\n}\n\nasync function writeGeneratedContexts(results: SyncResult[]): Promise<void> {\n for (const result of results) {\n if (result.status !== \"generated\") continue;\n if (!result.contextPath || result.context === undefined) {\n throw new Error(\n `Missing generated sync context for ${result.target.label}.`,\n );\n }\n await fs.writeFile(result.contextPath, result.context);\n }\n}\n\nfunction printWorkspaceSummary(rootDir: string, results: SyncResult[]): void {\n const generated = results.filter((result) => result.status === \"generated\");\n const skipped = results.filter((result) => result.status !== \"generated\");\n\n if (generated.length === 0) {\n const noTemplateChanges = results.filter(\n (result) => result.status === \"no-template-changes\",\n );\n if (noTemplateChanges.length === 0) {\n console.log(chalk.green(\"Already up to date.\"));\n return;\n }\n\n console.log(chalk.green(\"No template file changes between versions.\"));\n for (const result of noTemplateChanges) {\n console.log(\n chalk.dim(\" \"),\n `${result.target.label}: ${result.target.currentVersion} → ${result.toVersion}`,\n );\n }\n console.log(\n chalk.dim(\" Update manifests manually to record these target versions.\"),\n );\n console.log(chalk.dim(\" No sync contexts generated.\"));\n return;\n }\n\n console.log();\n console.log(chalk.bold(\"Workspace Sync Contexts Generated\"));\n console.log();\n\n for (const result of generated) {\n console.log(\n chalk.dim(\" Context:\"),\n path.relative(rootDir, result.contextPath ?? result.target.dir) || \".\",\n );\n console.log(\n chalk.dim(\" \"),\n `${result.target.templateType}: ${result.target.currentVersion} → ${result.toVersion}`,\n );\n }\n\n if (skipped.length > 0) {\n console.log(\n chalk.dim(\n ` ${skipped.length} target${skipped.length === 1 ? \"\" : \"s\"} already up to date or unchanged.`,\n ),\n );\n }\n\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.dim(\" 1.\"), \"Open Claude Code at the monorepo root\");\n console.log(\n chalk.dim(\" 2.\"),\n `Tell Claude: \"Read every ${CONTEXT_FILENAME} and apply the template changes\"`,\n );\n console.log(\n chalk.dim(\" 3.\"),\n `Review Claude's changes, then delete the generated ${CONTEXT_FILENAME} files`,\n );\n console.log();\n}\n\nasync function syncWorkspace(args: {\n cwd: string;\n mosaicRepoPath: string;\n sourceLabel: string;\n}): Promise<void> {\n const workspaceManifest = await readWorkspaceManifest(args.cwd);\n if (!workspaceManifest) {\n throw new Error(\n `No .mosaic-workspace.json found in ${args.cwd}. Run sync from the customer monorepo root.`,\n );\n }\n\n const packageDirs = await findPackageManifestDirs(args.cwd);\n const targets: SyncTarget[] = [\n await targetFromWorkspaceManifest(args.cwd, workspaceManifest),\n ...(await Promise.all(\n packageDirs.map(async (packageDir) =>\n targetFromManifest(packageDir, await readManifest(packageDir)),\n ),\n )),\n ];\n\n const results: SyncResult[] = [];\n for (const target of targets) {\n results.push(\n await syncTarget({\n target,\n mosaicRepoPath: args.mosaicRepoPath,\n sourceLabel: args.sourceLabel,\n }),\n );\n }\n\n await writeGeneratedContexts(results);\n printWorkspaceSummary(args.cwd, results);\n}\n\nexport async function syncCommand(): Promise<void> {\n const cwd = process.cwd();\n let source: ResolvedMosaicSource | undefined;\n let failed = false;\n\n try {\n const hasWorkspaceManifest = await fs.pathExists(\n getWorkspaceManifestPath(cwd),\n );\n\n if (!hasWorkspaceManifest) {\n throw new Error(\n `No .mosaic-workspace.json found in ${cwd}. Run sync from the customer monorepo root.`,\n );\n }\n\n source = await resolveMosaicSource();\n\n await syncWorkspace({\n cwd,\n mosaicRepoPath: source.path,\n sourceLabel: source.label,\n });\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n failed = true;\n } finally {\n await source?.cleanup?.();\n }\n\n if (failed) {\n process.exitCode = 1;\n }\n}\n"],"mappings":"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"}