@percepta/create 4.1.8 → 4.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +94 -30
- package/dist/index.js.map +1 -1
- package/dist/{init-CsuO_mu2.js → init-CP3IzRa6.js} +2 -2
- package/dist/{init-CsuO_mu2.js.map → init-CP3IzRa6.js.map} +1 -1
- package/dist/{register-app-mNc1oYVK.js → register-app-Dmnocuuy.js} +88 -34
- package/dist/register-app-Dmnocuuy.js.map +1 -0
- package/dist/{register-os-blueprint-Gdyn0pN1.js → register-os-blueprint-Byf69wrl.js} +2 -2
- package/dist/{register-os-blueprint-Gdyn0pN1.js.map → register-os-blueprint-Byf69wrl.js.map} +1 -1
- package/dist/status-C8SBzB-K.js +27 -0
- package/dist/status-C8SBzB-K.js.map +1 -0
- package/dist/sync-Lsfz8ZH4.js +280 -0
- package/dist/sync-Lsfz8ZH4.js.map +1 -0
- package/dist/{upstream-PNL6DGtl.js → upstream-R8YDvUue.js} +6 -6
- package/dist/upstream-R8YDvUue.js.map +1 -0
- package/package.json +1 -1
- package/templates/infra/os.blueprint.yaml.template +13 -0
- package/templates/monorepo/README.md +13 -0
- package/templates/monorepo/auth/README.md +2 -2
- package/templates/monorepo/auth/package.json +1 -1
- package/templates/monorepo/pnpm-workspace.yaml +8 -0
- package/templates/webapp/.claude/commands/sync.md +11 -10
- package/templates/webapp/.claude/commands/upstream.md +2 -2
- package/templates/webapp/AGENTS.md +4 -4
- package/templates/webapp/README.md +2 -1
- package/templates/webapp/agent-skills/access-control.md +3 -3
- package/templates/webapp/agent-skills/database.md +2 -1
- package/templates/webapp/next.config.ts +1 -1
- package/templates/webapp/package.json.template +2 -2
- package/templates/webapp/scripts/seed.ts +3 -3
- package/templates/webapp/src/app/(settings)/settings/page.tsx +1 -1
- package/templates/webapp/src/drizzle/schema/index.ts +1 -1
- package/templates/webapp/src/lib/auth/index.ts +2 -2
- package/templates/webapp/src/startup-checks.ts +1 -1
- package/dist/register-app-mNc1oYVK.js.map +0 -1
- package/dist/status-BrK9v1yb.js +0 -48
- package/dist/status-BrK9v1yb.js.map +0 -1
- package/dist/sync-DC5DhIBT.js +0 -101
- package/dist/sync-DC5DhIBT.js.map +0 -1
- package/dist/upstream-PNL6DGtl.js.map +0 -1
|
@@ -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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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as readManifest, u as resolveMosaicRepoPath } from "./index.js";
|
|
2
2
|
import { t as getFileAtTag } from "./git-ops-BNpQnEc1.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import fs from "fs-extra";
|
|
6
6
|
//#region src/commands/upstream.ts
|
|
7
|
-
async function generateUpstreamContext(manifest,
|
|
7
|
+
async function generateUpstreamContext(manifest, mosaicRepoPath, tag, appDir, files) {
|
|
8
8
|
let content = `# Mosaic Upstream Context
|
|
9
9
|
|
|
10
10
|
## App Info
|
|
@@ -27,7 +27,7 @@ ${Object.entries(manifest.placeholders).sort((a, b) => b[1].length - a[1].length
|
|
|
27
27
|
const appFilePath = path.resolve(appDir, file);
|
|
28
28
|
const templateRelPath = `${manifest.source.templatePath}/${file}`;
|
|
29
29
|
const appContent = await fs.pathExists(appFilePath) ? await fs.readFile(appFilePath, "utf-8") : null;
|
|
30
|
-
const templateContent = getFileAtTag(
|
|
30
|
+
const templateContent = getFileAtTag(mosaicRepoPath, tag, templateRelPath);
|
|
31
31
|
content += `### ${file}\n\n`;
|
|
32
32
|
if (!templateContent && appContent) {
|
|
33
33
|
content += `**New file** (not in template at ${manifest.templateVersion})\n\n`;
|
|
@@ -54,13 +54,13 @@ async function upstreamCommand(options) {
|
|
|
54
54
|
const cwd = process.cwd();
|
|
55
55
|
try {
|
|
56
56
|
const manifest = await readManifest(cwd);
|
|
57
|
-
const
|
|
57
|
+
const mosaicRepoPath = resolveMosaicRepoPath(options);
|
|
58
58
|
if (!options.files || options.files.length === 0) {
|
|
59
59
|
console.error(chalk.red("Specify files with --files <file1> <file2> ..."));
|
|
60
60
|
console.log(chalk.dim(" Example: create upstream --files src/config/getEnvConfig.ts"));
|
|
61
61
|
process.exit(1);
|
|
62
62
|
}
|
|
63
|
-
const context = await generateUpstreamContext(manifest,
|
|
63
|
+
const context = await generateUpstreamContext(manifest, mosaicRepoPath, `template/${manifest.templateType}/${manifest.templateVersion}`, cwd, options.files);
|
|
64
64
|
const contextPath = path.join(cwd, ".mosaic-upstream-context.md");
|
|
65
65
|
await fs.writeFile(contextPath, context);
|
|
66
66
|
console.log();
|
|
@@ -82,4 +82,4 @@ async function upstreamCommand(options) {
|
|
|
82
82
|
//#endregion
|
|
83
83
|
export { upstreamCommand };
|
|
84
84
|
|
|
85
|
-
//# sourceMappingURL=upstream-
|
|
85
|
+
//# sourceMappingURL=upstream-R8YDvUue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upstream-R8YDvUue.js","names":[],"sources":["../src/commands/upstream.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { getFileAtTag } from \"../utils/git-ops.js\";\nimport {\n readManifest,\n resolveMosaicRepoPath,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\n\nexport interface UpstreamOptions {\n mosaicRepoPath?: string;\n files?: string[];\n}\n\nasync function generateUpstreamContext(\n manifest: MosaicManifest,\n mosaicRepoPath: string,\n tag: string,\n appDir: string,\n files: string[],\n): Promise<string> {\n let content = `# Mosaic Upstream Context\n\n## App Info\n- **App name:** ${manifest.placeholders.__APP_NAME__ || \"unknown\"}\n- **Template:** ${manifest.templateType}\n- **Template version:** ${manifest.templateVersion}\n\n## Placeholder Mappings\n\nWhen generalizing app code back to template, replace these values with placeholder tokens:\n\n| Value | Placeholder |\n|-------|------------|\n${Object.entries(manifest.placeholders)\n .sort((a, b) => b[1].length - a[1].length) // longest first to avoid partial matches\n .map(([k, v]) => `| \\`${v}\\` | \\`${k}\\` |`)\n .join(\"\\n\")}\n\n## Files to Review\n\n`;\n\n for (const file of files) {\n const appFilePath = path.resolve(appDir, file);\n const templateRelPath = `${manifest.source.templatePath}/${file}`;\n\n const appContent = (await fs.pathExists(appFilePath))\n ? await fs.readFile(appFilePath, \"utf-8\")\n : null;\n const templateContent = getFileAtTag(mosaicRepoPath, tag, templateRelPath);\n\n content += `### ${file}\\n\\n`;\n\n if (!templateContent && appContent) {\n content += `**New file** (not in template at ${manifest.templateVersion})\\n\\n`;\n content += `\\`\\`\\`\\n${appContent}\\n\\`\\`\\`\\n\\n`;\n } else if (templateContent && !appContent) {\n content += `**Deleted** (exists in template but not in app)\\n\\n`;\n } else if (appContent && templateContent) {\n content += `**App version:**\\n\\`\\`\\`\\n${appContent}\\n\\`\\`\\`\\n\\n`;\n content += `**Template version (at ${manifest.templateVersion}):**\\n\\`\\`\\`\\n${templateContent}\\n\\`\\`\\`\\n\\n`;\n } else {\n content += `**Not found** (file does not exist in app or template)\\n\\n`;\n }\n }\n\n content += `## Instructions\n\n1. Review each file above\n2. Determine which changes are generalizable (useful for all apps) vs app-specific\n3. For generalizable changes: apply them to the template at \\`${manifest.source.templatePath}/\\`\n4. When applying, replace app-specific values with placeholders using the mapping table above (replace longest values first)\n5. After applying, bump the version in \\`packages/blueberry/template-versions.json\\`\n6. Run \\`pnpm template:tag\\` to create the new version tag\n7. Delete this file (\\`.mosaic-upstream-context.md\\`) when done\n`;\n\n return content;\n}\n\nexport async function upstreamCommand(options: UpstreamOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n const mosaicRepoPath = resolveMosaicRepoPath(options);\n\n if (!options.files || options.files.length === 0) {\n console.error(\n chalk.red(\"Specify files with --files <file1> <file2> ...\"),\n );\n console.log(\n chalk.dim(\n \" Example: create upstream --files src/config/getEnvConfig.ts\",\n ),\n );\n process.exit(1);\n }\n\n const tag = `template/${manifest.templateType}/${manifest.templateVersion}`;\n\n const context = await generateUpstreamContext(\n manifest,\n mosaicRepoPath,\n tag,\n cwd,\n options.files,\n );\n const contextPath = path.join(cwd, \".mosaic-upstream-context.md\");\n await fs.writeFile(contextPath, context);\n\n console.log();\n console.log(chalk.bold(\"Upstream Context Generated\"));\n console.log();\n console.log(chalk.dim(\" Files:\"), options.files.join(\", \"));\n console.log(chalk.dim(\" Context file:\"), \".mosaic-upstream-context.md\");\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.dim(\" 1.\"), \"Open Claude Code in the mosaic repo\");\n console.log(\n chalk.dim(\" 2.\"),\n `Tell Claude: \"Read ${path.resolve(cwd, \".mosaic-upstream-context.md\")} and apply generalizable changes to the template\"`,\n );\n console.log(chalk.dim(\" 3.\"), \"Review Claude's changes to the template\");\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;AAeA,eAAe,wBACb,UACA,gBACA,KACA,QACA,OACiB;CACjB,IAAI,UAAU;;;kBAGE,SAAS,aAAa,gBAAgB,UAAU;kBAChD,SAAS,aAAa;0BACd,SAAS,gBAAgB;;;;;;;;EAQjD,OAAO,QAAQ,SAAS,YAAY,EACnC,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,MAAM,EACxC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,KAAK,EACzC,KAAK,IAAI,EAAE;;;;;CAMZ,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,KAAK,QAAQ,QAAQ,IAAI;EAC7C,MAAM,kBAAkB,GAAG,SAAS,OAAO,aAAa,GAAG;EAE3D,MAAM,aAAc,MAAM,GAAG,WAAW,WAAW,IAC/C,MAAM,GAAG,SAAS,aAAa,OAAO,IACtC;EACJ,MAAM,kBAAkB,aAAa,gBAAgB,KAAK,eAAe;EAEzE,WAAW,OAAO,KAAK;EAEvB,IAAI,CAAC,mBAAmB,YAAY;GAClC,WAAW,oCAAoC,SAAS,gBAAgB;GACxE,WAAW,WAAW,WAAW;EACnC,OAAO,IAAI,mBAAmB,CAAC,YAC7B,WAAW;OACN,IAAI,cAAc,iBAAiB;GACxC,WAAW,6BAA6B,WAAW;GACnD,WAAW,0BAA0B,SAAS,gBAAgB,gBAAgB,gBAAgB;EAChG,OACE,WAAW;CAEf;CAEA,WAAW;;;;gEAImD,SAAS,OAAO,aAAa;;;;;;CAO3F,OAAO;AACT;AAEA,eAAsB,gBAAgB,SAAyC;CAC7E,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI;EACF,MAAM,WAAW,MAAM,aAAa,GAAG;EACvC,MAAM,iBAAiB,sBAAsB,OAAO;EAEpD,IAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,WAAW,GAAG;GAChD,QAAQ,MACN,MAAM,IAAI,gDAAgD,CAC5D;GACA,QAAQ,IACN,MAAM,IACJ,+DACF,CACF;GACA,QAAQ,KAAK,CAAC;EAChB;EAIA,MAAM,UAAU,MAAM,wBACpB,UACA,gBACA,YALsB,SAAS,aAAa,GAAG,SAAS,mBAMxD,KACA,QAAQ,KACV;EACA,MAAM,cAAc,KAAK,KAAK,KAAK,6BAA6B;EAChE,MAAM,GAAG,UAAU,aAAa,OAAO;EAEvC,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,KAAK,4BAA4B,CAAC;EACpD,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,UAAU,GAAG,QAAQ,MAAM,KAAK,IAAI,CAAC;EAC3D,QAAQ,IAAI,MAAM,IAAI,iBAAiB,GAAG,6BAA6B;EACvE,QAAQ,IAAI;EACZ,QAAQ,IAAI,aAAa;EACzB,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,qCAAqC;EACpE,QAAQ,IACN,MAAM,IAAI,MAAM,GAChB,sBAAsB,KAAK,QAAQ,KAAK,6BAA6B,EAAE,kDACzE;EACA,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,yCAAyC;EACxE,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}
|
package/package.json
CHANGED
|
@@ -48,6 +48,18 @@ spec:
|
|
|
48
48
|
displayName: "Langfuse Secret Key"
|
|
49
49
|
description: "Shared Langfuse secret key for generated OS webapps. Leave unset to disable Langfuse export."
|
|
50
50
|
condition: '{{ ne (input "langfuse_public_key") "" }}'
|
|
51
|
+
- name: inngest_event_key
|
|
52
|
+
type: string
|
|
53
|
+
isSecret: true
|
|
54
|
+
group: applications
|
|
55
|
+
displayName: "Inngest Event Key"
|
|
56
|
+
description: "Shared Inngest event key for generated OS webapps."
|
|
57
|
+
- name: inngest_signing_key
|
|
58
|
+
type: string
|
|
59
|
+
isSecret: true
|
|
60
|
+
group: applications
|
|
61
|
+
displayName: "Inngest Signing Key"
|
|
62
|
+
description: "Shared Inngest signing key for generated OS webapps."
|
|
51
63
|
- name: auth_secret_name
|
|
52
64
|
type: string
|
|
53
65
|
group: general
|
|
@@ -131,6 +143,7 @@ spec:
|
|
|
131
143
|
|
|
132
144
|
region: {{ .ryvn.env.state.cluster_region }}
|
|
133
145
|
vpc_id: {{ .ryvn.env.state.vpc.id }}
|
|
146
|
+
sslmode: require
|
|
134
147
|
subnet_ids:
|
|
135
148
|
{{ .ryvn.env.state.vpc.private_subnet_ids | toYaml | nindent 2 }}
|
|
136
149
|
{{ if ne (input "aws_postgresql_cluster_name") "" }}
|
|
@@ -98,6 +98,19 @@ pnpm mosaic add library my-lib
|
|
|
98
98
|
|
|
99
99
|
The customer slug and compatibility metadata live in `.mosaic-workspace.json`.
|
|
100
100
|
|
|
101
|
+
## Syncing from Mosaic
|
|
102
|
+
|
|
103
|
+
Run sync from the monorepo root to pull both workspace-level template updates
|
|
104
|
+
and package-level template updates:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pnpm mosaic sync
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
By default, the command checks out `git@github.com:Percepta-Core/mosaic.git` at
|
|
111
|
+
`main` into a temporary local directory, generates `.mosaic-sync-context.md`
|
|
112
|
+
files for changed targets, and leaves the merge for your coding agent to apply.
|
|
113
|
+
|
|
101
114
|
## Registering the customer OS blueprint
|
|
102
115
|
|
|
103
116
|
To register this customer monorepo's OS blueprint in `Percepta-Core/infra`,
|
|
@@ -5,8 +5,8 @@ This workspace wires the customer-global Better Auth database schema from
|
|
|
5
5
|
session validation and user / group table references instead of creating
|
|
6
6
|
app-local auth tables.
|
|
7
7
|
|
|
8
|
-
Import auth as `@
|
|
9
|
-
`@
|
|
8
|
+
Import auth as `@__REPO_NAME__/auth`, the database handle as
|
|
9
|
+
`@__REPO_NAME__/auth/db`, and table definitions as `@__REPO_NAME__/auth/schema`
|
|
10
10
|
from app packages.
|
|
11
11
|
|
|
12
12
|
The important identity invariant is:
|
|
@@ -6,6 +6,14 @@ packages:
|
|
|
6
6
|
# user's pnpm config enforces minimumReleaseAge.
|
|
7
7
|
minimumReleaseAgeExclude:
|
|
8
8
|
- "@percepta/*"
|
|
9
|
+
- "better-auth"
|
|
10
|
+
- "@better-auth/*"
|
|
11
|
+
- "oxfmt"
|
|
12
|
+
- "@oxfmt/*"
|
|
13
|
+
- "oxlint"
|
|
14
|
+
- "@oxlint/*"
|
|
15
|
+
- "oxlint-tsgolint"
|
|
16
|
+
- "@oxlint-tsgolint/*"
|
|
9
17
|
|
|
10
18
|
overrides:
|
|
11
19
|
fast-xml-parser@<=5.5.6: 5.5.6
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
Sync downstream template changes into this app.
|
|
1
|
+
Sync downstream template changes into this app or its containing Mosaic monorepo.
|
|
2
2
|
|
|
3
3
|
## Steps
|
|
4
4
|
|
|
5
|
-
1.
|
|
6
|
-
2.
|
|
7
|
-
3. Run: `
|
|
5
|
+
1. Prefer running from the monorepo root so workspace-level template changes and package-level template changes are discovered together.
|
|
6
|
+
2. Read `.mosaic-workspace.json` at the monorepo root and any package `.mosaic-template.json` files to understand template versions and placeholder mappings.
|
|
7
|
+
3. Run: `pnpm mosaic sync`
|
|
8
|
+
- The CLI checks out `git@github.com:Percepta-Core/mosaic.git` at `main` into a temporary local directory.
|
|
8
9
|
4. If the command reports "Already up to date" or "No template file changes", inform the user and stop.
|
|
9
|
-
5. Read
|
|
10
|
-
6. Apply the template changes described in the context
|
|
10
|
+
5. Read every generated `.mosaic-sync-context.md` file.
|
|
11
|
+
6. Apply the template changes described in the context files:
|
|
11
12
|
- Replace placeholder tokens (e.g. `__APP_NAME__`) with actual values from the mapping table
|
|
12
|
-
- Check `mosaic-template-notes.md` for intentional divergences — preserve them
|
|
13
|
+
- Check package `mosaic-template-notes.md` files for intentional divergences — preserve them
|
|
13
14
|
- For files not modified locally, apply changes directly
|
|
14
15
|
- For files modified locally, merge intelligently, preserving local customizations
|
|
15
16
|
7. Run `pnpm install && pnpm build && pnpm lint` to verify the changes.
|
|
16
|
-
8. Update `.mosaic-
|
|
17
|
-
9. If you made decisions about merge conflicts, add notes to `mosaic-template-notes.md`.
|
|
18
|
-
10. Delete `.mosaic-sync-context.md
|
|
17
|
+
8. Update `.mosaic-workspace.json` and package `.mosaic-template.json` files as instructed by the sync contexts.
|
|
18
|
+
9. If you made decisions about merge conflicts, add notes to the relevant `mosaic-template-notes.md`.
|
|
19
|
+
10. Delete generated `.mosaic-sync-context.md` files.
|
|
19
20
|
11. Summarize what changed and ask the user to review before committing.
|
|
@@ -4,8 +4,8 @@ Propose app improvements upstream to the mosaic template.
|
|
|
4
4
|
|
|
5
5
|
1. Read `.mosaic-template.json` in the current package directory to get the template type, current version, and placeholder mappings.
|
|
6
6
|
2. Determine which files to propose upstream: use `$ARGUMENTS` if provided (space-separated file paths), otherwise ask the user which files contain improvements they want to contribute back to the template.
|
|
7
|
-
3.
|
|
8
|
-
4. Run: `npx @percepta/create upstream --mosaic-
|
|
7
|
+
3. Ask the user for the path to the local mosaic repo checkout they want to edit.
|
|
8
|
+
4. Run: `npx @percepta/create upstream --mosaic-repo-path <path> --files <file1> <file2> ...`
|
|
9
9
|
5. Read the generated `.mosaic-upstream-context.md` file.
|
|
10
10
|
6. For each file listed in the context:
|
|
11
11
|
- Compare the app version against the template version
|
|
@@ -241,7 +241,7 @@ Client-side usage via `src/lib/trpc.ts`.
|
|
|
241
241
|
|
|
242
242
|
### Authentication
|
|
243
243
|
|
|
244
|
-
Better Auth is configured in the customer monorepo's shared `@
|
|
244
|
+
Better Auth is configured in the customer monorepo's shared `@__REPO_NAME__/auth` package. The app imports it through `src/lib/auth/` for local session validation. `DATABASE_URL` is this app's database only; deployed auth should use `AUTH_DATABASE_URL` from the monorepo auth Secret.
|
|
245
245
|
|
|
246
246
|
- **Server-side**: `auth.api.getSession({ headers: await headers() })` — get session in server components or tRPC context
|
|
247
247
|
- **Client-side**: `authClient.useSession()` — React hook from `src/lib/auth-client.ts`
|
|
@@ -276,9 +276,9 @@ use Ryvn releases.
|
|
|
276
276
|
|
|
277
277
|
## Template Sync
|
|
278
278
|
|
|
279
|
-
This app tracks its template origin in `.mosaic-template.json`. Two Claude commands are available:
|
|
279
|
+
This app tracks its template origin in `.mosaic-template.json`. The monorepo root tracks workspace template compatibility in `.mosaic-workspace.json`. Two Claude commands are available:
|
|
280
280
|
|
|
281
|
-
- **`/sync`** — pull downstream changes from the
|
|
281
|
+
- **`/sync`** — pull downstream changes from the Mosaic monorepo template and package templates
|
|
282
282
|
- **`/upstream`** — propose app improvements back to the mosaic template
|
|
283
283
|
|
|
284
|
-
Both commands use `@percepta/create` CLI under the hood. Check `mosaic-template-notes.md` for documented intentional divergences from the template.
|
|
284
|
+
Both commands use `@percepta/create` CLI under the hood. `sync` uses a temporary checkout of `git@github.com:Percepta-Core/mosaic.git` at `main`. Check `mosaic-template-notes.md` for documented intentional divergences from the template.
|
|
@@ -145,7 +145,7 @@ logger.error({ safe: { documentId } }, "Processing failed", error);
|
|
|
145
145
|
|
|
146
146
|
## Authentication
|
|
147
147
|
|
|
148
|
-
This app consumes the customer monorepo's shared [Better Auth](https://better-auth.com) package, `@
|
|
148
|
+
This app consumes the customer monorepo's shared [Better Auth](https://better-auth.com) package, `@__REPO_NAME__/auth`. The app still serves local development auth routes, but the users, sessions, accounts, groups, and group memberships live in the shared customer auth database. Deployed apps should receive that shared database through `AUTH_DATABASE_URL` from the monorepo auth Secret; `DATABASE_URL` is reserved for this app's own database.
|
|
149
149
|
|
|
150
150
|
Required auth environment variables:
|
|
151
151
|
|
|
@@ -186,6 +186,7 @@ App permissions are authored in `src/access/schema.zed`; `src/access/access.mani
|
|
|
186
186
|
|
|
187
187
|
### App Database
|
|
188
188
|
|
|
189
|
+
<!-- prettier-ignore -->
|
|
189
190
|
| Variable | Description | Default |
|
|
190
191
|
| -------------- | ----------------------------- | ------------------------------------------------------------- |
|
|
191
192
|
| `DATABASE_URL` | App PostgreSQL connection URL | `postgresql://postgres:postgres@localhost:5434/__DB_NAME__` |
|
|
@@ -257,13 +257,13 @@ await getAccessControl().app.assignAppRole(
|
|
|
257
257
|
|
|
258
258
|
## Shared Auth Boundary
|
|
259
259
|
|
|
260
|
-
This app imports Better Auth from `@
|
|
260
|
+
This app imports Better Auth from `@__REPO_NAME__/auth` via `src/lib/auth/index.ts`. The app does not own `users`, `groups`, `group_members`, sessions, accounts, or verification tables.
|
|
261
261
|
|
|
262
262
|
When app code needs users for display, import from the shared auth package:
|
|
263
263
|
|
|
264
264
|
```ts
|
|
265
|
-
import { db as authDb } from "@
|
|
266
|
-
import { users } from "@
|
|
265
|
+
import { db as authDb } from "@__REPO_NAME__/auth/db";
|
|
266
|
+
import { users } from "@__REPO_NAME__/auth/schema";
|
|
267
267
|
```
|
|
268
268
|
|
|
269
269
|
Use `users.id` in SpiceDB refs. `users.external_id`, email, and group external IDs are ingestion lookup keys only.
|
|
@@ -10,7 +10,7 @@ Create a new schema file alongside the existing ones:
|
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
12
|
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
13
|
-
import { users } from "@
|
|
13
|
+
import { users } from "@__REPO_NAME__/auth/schema";
|
|
14
14
|
|
|
15
15
|
export const documents = pgTable("documents", {
|
|
16
16
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
@@ -135,6 +135,7 @@ pnpm --dir ../.. run docker:down
|
|
|
135
135
|
|
|
136
136
|
## Environment Variables
|
|
137
137
|
|
|
138
|
+
<!-- prettier-ignore -->
|
|
138
139
|
| Variable | Default | Description |
|
|
139
140
|
| -------------- | ------------------------------------------------------------- | ----------------------------- |
|
|
140
141
|
| `DATABASE_URL` | `postgresql://postgres:postgres@localhost:5434/__DB_NAME__` | App PostgreSQL connection URL |
|
|
@@ -9,7 +9,7 @@ const nextConfig: NextConfig = {
|
|
|
9
9
|
// Enable standalone output for Docker:
|
|
10
10
|
output: "standalone",
|
|
11
11
|
outputFileTracingRoot: monorepoRoot,
|
|
12
|
-
transpilePackages: ["@
|
|
12
|
+
transpilePackages: ["@__REPO_NAME__/auth"],
|
|
13
13
|
turbopack: {
|
|
14
14
|
root: monorepoRoot,
|
|
15
15
|
},
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
"@opentelemetry/auto-instrumentations-node": "^0.75.0",
|
|
44
44
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.217.0",
|
|
45
45
|
"@opentelemetry/sdk-node": "^0.217.0",
|
|
46
|
+
"@__REPO_NAME__/auth": "workspace:*",
|
|
46
47
|
"@percepta/access-control": "^1.0.0",
|
|
47
48
|
"@percepta/ai": "^0.1.0",
|
|
48
|
-
"@
|
|
49
|
-
"@percepta/database": "^0.1.2",
|
|
49
|
+
"@percepta/database": "0.1.3",
|
|
50
50
|
"@percepta/design": "^0.4.1",
|
|
51
51
|
"@percepta/inngest": "^0.1.0",
|
|
52
52
|
"@percepta/logger": "^0.1.0",
|
|
@@ -52,9 +52,9 @@ async function main(): Promise<void> {
|
|
|
52
52
|
// oxlint-disable-next-line typescript/no-explicit-any
|
|
53
53
|
(globalThis as any).AsyncLocalStorage = AsyncLocalStorage;
|
|
54
54
|
|
|
55
|
-
const { auth } = await import("@
|
|
56
|
-
const { db: authDb } = await import("@
|
|
57
|
-
const { users } = await import("@
|
|
55
|
+
const { auth } = await import("@__REPO_NAME__/auth");
|
|
56
|
+
const { db: authDb } = await import("@__REPO_NAME__/auth/db");
|
|
57
|
+
const { users } = await import("@__REPO_NAME__/auth/schema");
|
|
58
58
|
const { getAccessControl, toUserSubject } =
|
|
59
59
|
await import("../src/services/access/AppAccessControl");
|
|
60
60
|
const { getEnvConfig } = await import("../src/config/getEnvConfig");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { listPrincipals } from "@__REPO_NAME__/auth/principals";
|
|
1
2
|
import {
|
|
2
3
|
type AccessRoleDefinition,
|
|
3
4
|
type ApplicationGrant,
|
|
@@ -8,7 +9,6 @@ import {
|
|
|
8
9
|
PrincipalMultiInput,
|
|
9
10
|
type PrincipalOption,
|
|
10
11
|
} from "@percepta/access-control/react";
|
|
11
|
-
import { listPrincipals } from "@__CUSTOMER_SLUG__/auth/principals";
|
|
12
12
|
import {
|
|
13
13
|
Badge,
|
|
14
14
|
Table,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { auth } from "@
|
|
1
|
+
import { auth } from "@__REPO_NAME__/auth";
|
|
2
2
|
import { headers } from "next/headers";
|
|
3
3
|
|
|
4
|
-
export { auth, type BetterAuthSession } from "@
|
|
4
|
+
export { auth, type BetterAuthSession } from "@__REPO_NAME__/auth";
|
|
5
5
|
|
|
6
6
|
export async function getServerSession() {
|
|
7
7
|
return auth.api.getSession({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { client as authClient } from "@
|
|
1
|
+
import { client as authClient } from "@__REPO_NAME__/auth/db";
|
|
2
2
|
import { getEnvConfig } from "./config/getEnvConfig";
|
|
3
3
|
import { client } from "./drizzle/db";
|
|
4
4
|
import { getLogger } from "./services/logger/AppLogger";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"register-app-mNc1oYVK.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_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 }\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 changed = ensureOsPostgresqlInstallationAlias(installations) || changed;\n changed = addAppInstallation(document, installations, appName) || 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(installations: {\n items: unknown[];\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\") === OS_POSTGRESQL_TERRAFORM_ALIAS) continue;\n\n installation.set(\"name\", OS_POSTGRESQL_TERRAFORM_ALIAS);\n changed = true;\n }\n\n return changed;\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 if (isMap(appDatabaseValue)) appDatabaseValue.flow = true;\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): 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),\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 renderAppInstallationEnv(\n appName: string,\n): Array<Record<string, unknown>> {\n const appHost = `${appName}.{{ input \"${ingressDomainInputName()}\" }}`;\n\n return [\n {\n key: \"DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: \"os-postgresql-terraform\",\n name: `app_database_urls.${appName}`,\n },\n },\n {\n key: \"AUTH_DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: \"os-postgresql-terraform\",\n name: \"auth_database_url\",\n },\n },\n {\n key: \"APP_BASE_URL\",\n value: `https://${appHost}`,\n },\n {\n key: \"DEPLOYMENT_ENVIRONMENT\",\n value: \"{{ EnvironmentName }}\",\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 value: '{{ (blueprintInstallation \"mosaic\").outputs.inngest_base_url }}',\n },\n {\n key: \"LANGFUSE_BASE_URL\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.langfuse_base_url }}',\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 value:\n '{{ (blueprintInstallation \"mosaic\").outputs.otel_exporter_otlp_endpoint }}',\n },\n {\n key: \"SPICEDB_ENDPOINT\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_endpoint }}',\n },\n {\n key: \"SPICEDB_PRESHARED_KEY\",\n isSecret: true,\n value:\n '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_preshared_key }}',\n },\n {\n key: \"SPICEDB_INSECURE\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_insecure }}',\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 \"env:\",\n \" - name: INNGEST_EVENT_KEY\",\n \" valueFrom:\",\n \" secretKeyRef:\",\n ` name: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,\n ` key: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestEventKeySecretKeyOutput()} }}'`,\n \" - name: INNGEST_SIGNING_KEY\",\n \" valueFrom:\",\n \" secretKeyRef:\",\n ` name: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,\n ` key: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestSigningKeySecretKeyOutput()} }}'`,\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 mosaicInngestKeysSecretNameOutput(): string {\n return \"inngest_keys_secret_name\";\n}\n\nfunction mosaicInngestEventKeySecretKeyOutput(): string {\n return \"inngest_event_key_secret_key\";\n}\n\nfunction mosaicInngestSigningKeySecretKeyOutput(): string {\n return \"inngest_signing_key_secret_key\";\n}\n\nfunction langfusePublicKeyInputName(): string {\n return \"langfuse_public_key\";\n}\n\nfunction langfuseSecretKeyInputName(): string {\n return \"langfuse_secret_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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EACV;CACF;AACF;AAEA,SAAS,+BAEP;CACA,OAAO;EACL,MAAM,2BAA2B;EACjC,MAAM;EACN,OAAO;EACP,aAAa;EACb,aACE;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,yBACP,SACgC;CAChC,MAAM,UAAU,GAAG,QAAQ,aAAa,uBAAuB,EAAE;CAEjE,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,WAAW;EACpB;EACA;GACE,KAAK;GACL,OAAO;EACT;EACA;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,0BAA0B,OAAO,EACzC;EACF;EACA;GACE,KAAK;GACL,OAAO;EACT;EACA;GACE,KAAK;GACL,OAAO;EACT;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,OACE;EACJ;EACA;GACE,KAAK;GACL,OAAO;EACT;EACA;GACE,KAAK;GACL,UAAU;GACV,OACE;EACJ;EACA;GACE,KAAK;GACL,OAAO;EACT;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,8DAA8D,kCAAkC,EAAE;EAClG,6DAA6D,qCAAqC,EAAE;EACpG;EACA;EACA;EACA,8DAA8D,kCAAkC,EAAE;EAClG,6DAA6D,uCAAuC,EAAE;EACtG;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,oCAA4C;CACnD,OAAO;AACT;AAEA,SAAS,uCAA+C;CACtD,OAAO;AACT;AAEA,SAAS,yCAAiD;CACxD,OAAO;AACT;AAEA,SAAS,6BAAqC;CAC5C,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"}
|
package/dist/status-BrK9v1yb.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { c as readManifest } from "./index.js";
|
|
2
|
-
import { i as getTemplateVersionFromTag, n as getLatestTemplateTag } from "./git-ops-BNpQnEc1.js";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import chalk from "chalk";
|
|
5
|
-
//#region src/commands/status.ts
|
|
6
|
-
async function statusCommand(options) {
|
|
7
|
-
const cwd = process.cwd();
|
|
8
|
-
try {
|
|
9
|
-
const manifest = await readManifest(cwd);
|
|
10
|
-
console.log();
|
|
11
|
-
console.log(chalk.bold("Mosaic Template Status"));
|
|
12
|
-
console.log();
|
|
13
|
-
console.log(chalk.dim(" Template type:"), manifest.templateType);
|
|
14
|
-
console.log(chalk.dim(" Current version:"), manifest.templateVersion);
|
|
15
|
-
console.log(chalk.dim(" Template commit:"), manifest.templateCommit);
|
|
16
|
-
console.log(chalk.dim(" Created:"), manifest.createdAt);
|
|
17
|
-
if (manifest.lastSyncedAt) console.log(chalk.dim(" Last synced:"), manifest.lastSyncedAt);
|
|
18
|
-
const rawPath = options.mosaicTemplatePath || process.env.MOSAIC_TEMPLATE_PATH;
|
|
19
|
-
const mosaicTemplatePath = rawPath ? path.resolve(rawPath) : void 0;
|
|
20
|
-
if (mosaicTemplatePath) {
|
|
21
|
-
const latestTag = getLatestTemplateTag(manifest.templateType, mosaicTemplatePath);
|
|
22
|
-
if (latestTag) {
|
|
23
|
-
const latestVersion = getTemplateVersionFromTag(latestTag);
|
|
24
|
-
console.log(chalk.dim(" Latest version:"), latestVersion);
|
|
25
|
-
console.log();
|
|
26
|
-
if (latestVersion !== manifest.templateVersion) {
|
|
27
|
-
console.log(chalk.yellow(` Update available: ${manifest.templateVersion} → ${latestVersion}`));
|
|
28
|
-
console.log(chalk.dim(" Run:"), `create sync --mosaic-template-path ${mosaicTemplatePath}`);
|
|
29
|
-
} else console.log(chalk.green(" Up to date"));
|
|
30
|
-
} else {
|
|
31
|
-
console.log();
|
|
32
|
-
console.log(chalk.yellow(" No template tags found in mosaic repo."));
|
|
33
|
-
console.log(chalk.dim(" Run:"), `cd ${mosaicTemplatePath} && pnpm template:tag`);
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
console.log();
|
|
37
|
-
console.log(chalk.dim(" Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH to check for updates"));
|
|
38
|
-
}
|
|
39
|
-
console.log();
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error(chalk.red(error.message));
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
export { statusCommand };
|
|
47
|
-
|
|
48
|
-
//# sourceMappingURL=status-BrK9v1yb.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"status-BrK9v1yb.js","names":[],"sources":["../src/commands/status.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n getLatestTemplateTag,\n getTemplateVersionFromTag,\n} from \"../utils/git-ops.js\";\nimport { readManifest } from \"../utils/manifest.js\";\n\nexport interface StatusOptions {\n mosaicTemplatePath?: string;\n}\n\nexport async function statusCommand(options: StatusOptions): 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 const rawPath =\n options.mosaicTemplatePath || process.env.MOSAIC_TEMPLATE_PATH;\n const mosaicTemplatePath = rawPath ? path.resolve(rawPath) : undefined;\n\n if (mosaicTemplatePath) {\n const latestTag = getLatestTemplateTag(\n manifest.templateType,\n mosaicTemplatePath,\n );\n if (latestTag) {\n const latestVersion = getTemplateVersionFromTag(latestTag);\n console.log(chalk.dim(\" Latest version:\"), latestVersion);\n console.log();\n\n if (latestVersion !== manifest.templateVersion) {\n console.log(\n chalk.yellow(\n ` Update available: ${manifest.templateVersion} → ${latestVersion}`,\n ),\n );\n console.log(\n chalk.dim(\" Run:\"),\n `create sync --mosaic-template-path ${mosaicTemplatePath}`,\n );\n } else {\n console.log(chalk.green(\" Up to date\"));\n }\n } else {\n console.log();\n console.log(chalk.yellow(\" No template tags found in mosaic repo.\"));\n console.log(\n chalk.dim(\" Run:\"),\n `cd ${mosaicTemplatePath} && pnpm template:tag`,\n );\n }\n } else {\n console.log();\n console.log(\n chalk.dim(\n \" Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH to check for updates\",\n ),\n );\n }\n\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;AAYA,eAAsB,cAAc,SAAuC;CACzE,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,MAAM,UACJ,QAAQ,sBAAsB,QAAQ,IAAI;EAC5C,MAAM,qBAAqB,UAAU,KAAK,QAAQ,OAAO,IAAI,KAAA;EAE7D,IAAI,oBAAoB;GACtB,MAAM,YAAY,qBAChB,SAAS,cACT,kBACF;GACA,IAAI,WAAW;IACb,MAAM,gBAAgB,0BAA0B,SAAS;IACzD,QAAQ,IAAI,MAAM,IAAI,mBAAmB,GAAG,aAAa;IACzD,QAAQ,IAAI;IAEZ,IAAI,kBAAkB,SAAS,iBAAiB;KAC9C,QAAQ,IACN,MAAM,OACJ,uBAAuB,SAAS,gBAAgB,KAAK,eACvD,CACF;KACA,QAAQ,IACN,MAAM,IAAI,QAAQ,GAClB,sCAAsC,oBACxC;IACF,OACE,QAAQ,IAAI,MAAM,MAAM,cAAc,CAAC;GAE3C,OAAO;IACL,QAAQ,IAAI;IACZ,QAAQ,IAAI,MAAM,OAAO,0CAA0C,CAAC;IACpE,QAAQ,IACN,MAAM,IAAI,QAAQ,GAClB,MAAM,mBAAmB,sBAC3B;GACF;EACF,OAAO;GACL,QAAQ,IAAI;GACZ,QAAQ,IACN,MAAM,IACJ,+EACF,CACF;EACF;EAEA,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}
|