@fragments-sdk/cli 0.15.10 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/bin.js +896 -787
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-6SQPP47U.js → chunk-77AAP6R6.js} +532 -31
  4. package/dist/chunk-77AAP6R6.js.map +1 -0
  5. package/dist/{chunk-5JF26E55.js → chunk-ACFVKMVZ.js} +11 -11
  6. package/dist/{chunk-BJE3425I.js → chunk-ACX7YWZW.js} +2 -2
  7. package/dist/{chunk-ONUP6Z4W.js → chunk-G6UVWMFU.js} +8 -8
  8. package/dist/{chunk-2WXKALIG.js → chunk-OZZ4SVZX.js} +2 -2
  9. package/dist/{chunk-32LIWN2P.js → chunk-SJFSG7QF.js} +582 -261
  10. package/dist/chunk-SJFSG7QF.js.map +1 -0
  11. package/dist/{chunk-HQ6A6DTV.js → chunk-XRADMHMV.js} +315 -1089
  12. package/dist/chunk-XRADMHMV.js.map +1 -0
  13. package/dist/core/index.js +53 -1
  14. package/dist/{create-EXURTBKK.js → create-3ZFYQB3T.js} +2 -2
  15. package/dist/{doctor-BDPMYYE6.js → doctor-4IDUM7HI.js} +2 -2
  16. package/dist/{generate-PVOLUAAC.js → generate-VNUUWVWQ.js} +4 -4
  17. package/dist/{govern-scan-DW4QUAYD.js → govern-scan-HTACKYPF.js} +158 -120
  18. package/dist/govern-scan-HTACKYPF.js.map +1 -0
  19. package/dist/index.js +6 -7
  20. package/dist/index.js.map +1 -1
  21. package/dist/{init-SSGUSP7Z.js → init-PXFRAQ64.js} +5 -5
  22. package/dist/mcp-bin.js +2 -2
  23. package/dist/{scan-PKSYSTRR.js → scan-L4GWGEZX.js} +5 -6
  24. package/dist/{scan-generate-VY27PIOX.js → scan-generate-74EYSAGH.js} +4 -4
  25. package/dist/{service-QJGWUIVL.js → service-VELQHEWV.js} +12 -14
  26. package/dist/{snapshot-WIJMEIFT.js → snapshot-DT4B6DPR.js} +2 -2
  27. package/dist/{static-viewer-7QIBQZRC.js → static-viewer-E4OJWFDJ.js} +3 -3
  28. package/dist/{test-64Z5BKBA.js → test-QJY2QO4X.js} +3 -3
  29. package/dist/{token-normalizer-TEPOVBPV.js → token-normalizer-56H4242J.js} +2 -2
  30. package/dist/{tokens-NZWFQIAB.js → tokens-K6URXFPK.js} +7 -8
  31. package/dist/{tokens-NZWFQIAB.js.map → tokens-K6URXFPK.js.map} +1 -1
  32. package/dist/{tokens-generate-5JQSJ27E.js → tokens-generate-EL6IN536.js} +2 -2
  33. package/package.json +7 -6
  34. package/src/bin.ts +49 -88
  35. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
  36. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
  37. package/src/commands/__tests__/context-cloud.test.ts +291 -0
  38. package/src/commands/__tests__/govern-scan.test.ts +185 -0
  39. package/src/commands/__tests__/govern.test.ts +1 -0
  40. package/src/commands/context-cloud.ts +355 -0
  41. package/src/commands/govern-scan-report.ts +170 -0
  42. package/src/commands/govern-scan.ts +42 -147
  43. package/src/commands/govern.ts +0 -157
  44. package/dist/chunk-32LIWN2P.js.map +0 -1
  45. package/dist/chunk-6SQPP47U.js.map +0 -1
  46. package/dist/chunk-HQ6A6DTV.js.map +0 -1
  47. package/dist/chunk-MHIBEEW4.js +0 -511
  48. package/dist/chunk-MHIBEEW4.js.map +0 -1
  49. package/dist/govern-scan-DW4QUAYD.js.map +0 -1
  50. package/dist/init-cloud-3DNKPWFB.js +0 -304
  51. package/dist/init-cloud-3DNKPWFB.js.map +0 -1
  52. package/dist/node-37AUE74M.js +0 -65
  53. package/dist/push-contracts-WY32TFP6.js +0 -84
  54. package/dist/push-contracts-WY32TFP6.js.map +0 -1
  55. package/dist/static-viewer-7QIBQZRC.js.map +0 -1
  56. package/dist/token-parser-32KOIOFN.js +0 -22
  57. package/dist/token-parser-32KOIOFN.js.map +0 -1
  58. package/dist/tokens-push-HY3KO36V.js +0 -148
  59. package/dist/tokens-push-HY3KO36V.js.map +0 -1
  60. package/src/commands/init-cloud.ts +0 -382
  61. package/src/commands/push-contracts.ts +0 -112
  62. package/src/commands/tokens-push.ts +0 -199
  63. /package/dist/{chunk-5JF26E55.js.map → chunk-ACFVKMVZ.js.map} +0 -0
  64. /package/dist/{chunk-BJE3425I.js.map → chunk-ACX7YWZW.js.map} +0 -0
  65. /package/dist/{chunk-ONUP6Z4W.js.map → chunk-G6UVWMFU.js.map} +0 -0
  66. /package/dist/{chunk-2WXKALIG.js.map → chunk-OZZ4SVZX.js.map} +0 -0
  67. /package/dist/{create-EXURTBKK.js.map → create-3ZFYQB3T.js.map} +0 -0
  68. /package/dist/{doctor-BDPMYYE6.js.map → doctor-4IDUM7HI.js.map} +0 -0
  69. /package/dist/{generate-PVOLUAAC.js.map → generate-VNUUWVWQ.js.map} +0 -0
  70. /package/dist/{init-SSGUSP7Z.js.map → init-PXFRAQ64.js.map} +0 -0
  71. /package/dist/{node-37AUE74M.js.map → scan-L4GWGEZX.js.map} +0 -0
  72. /package/dist/{scan-generate-VY27PIOX.js.map → scan-generate-74EYSAGH.js.map} +0 -0
  73. /package/dist/{scan-PKSYSTRR.js.map → service-VELQHEWV.js.map} +0 -0
  74. /package/dist/{snapshot-WIJMEIFT.js.map → snapshot-DT4B6DPR.js.map} +0 -0
  75. /package/dist/{service-QJGWUIVL.js.map → static-viewer-E4OJWFDJ.js.map} +0 -0
  76. /package/dist/{test-64Z5BKBA.js.map → test-QJY2QO4X.js.map} +0 -0
  77. /package/dist/{token-normalizer-TEPOVBPV.js.map → token-normalizer-56H4242J.js.map} +0 -0
  78. /package/dist/{tokens-generate-5JQSJ27E.js.map → tokens-generate-EL6IN536.js.map} +0 -0
@@ -1,148 +0,0 @@
1
- import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
2
-
3
- // src/commands/tokens-push.ts
4
- import { resolve } from "path";
5
- import pc from "picocolors";
6
- async function tokensPush(options) {
7
- const startTime = Date.now();
8
- const apiKey = process.env.FRAGMENTS_API_KEY;
9
- const baseUrl = process.env.FRAGMENTS_URL || "https://app.usefragments.com";
10
- if (!apiKey && !options.dryRun) {
11
- console.error(pc.red("Error: FRAGMENTS_API_KEY environment variable is required"));
12
- console.error(pc.dim("Get your API key from https://app.usefragments.com/api-keys"));
13
- process.exit(1);
14
- }
15
- const flatMap = {};
16
- if (options.tailwindV4) {
17
- const { parseTailwindV4File } = await import("./token-parser-32KOIOFN.js");
18
- const filePath = resolve(process.cwd(), options.tailwindV4);
19
- const result = parseTailwindV4File(filePath);
20
- if (result.errors.length > 0) {
21
- for (const err of result.errors) {
22
- console.error(pc.red(` Error: ${err.message}`));
23
- }
24
- }
25
- if (options.verbose && result.warnings.length > 0) {
26
- for (const warn of result.warnings) {
27
- console.warn(pc.yellow(` Warning: ${warn}`));
28
- }
29
- }
30
- for (const token of result.tokens) {
31
- flatMap[token.name] = token.resolvedValue;
32
- }
33
- if (options.verbose) {
34
- console.log(
35
- pc.dim(` Parsed ${result.tokens.length} tokens from ${options.tailwindV4} (${result.parseTimeMs.toFixed(1)}ms)`)
36
- );
37
- }
38
- } else if (options.config) {
39
- try {
40
- const { loadConfig } = await import("./node-37AUE74M.js");
41
- const { parseTokenFiles } = await import("./service-QJGWUIVL.js");
42
- const { config, configDir } = await loadConfig(options.config);
43
- if (config.tokens?.include?.length) {
44
- const result = await parseTokenFiles(config.tokens, configDir);
45
- for (const token of result.tokens) {
46
- flatMap[token.name] = token.resolvedValue;
47
- }
48
- if (options.verbose) {
49
- console.log(
50
- pc.dim(` Parsed ${result.tokens.length} tokens from config (${result.parseTimeMs.toFixed(1)}ms)`)
51
- );
52
- }
53
- } else {
54
- console.error(pc.red("Error: Config file has no tokens.include patterns"));
55
- process.exit(1);
56
- }
57
- } catch (error) {
58
- console.error(pc.red("Error loading config:"), error instanceof Error ? error.message : error);
59
- process.exit(1);
60
- }
61
- } else {
62
- try {
63
- const { loadConfig } = await import("./node-37AUE74M.js");
64
- const { parseTokenFiles } = await import("./service-QJGWUIVL.js");
65
- const { config, configDir } = await loadConfig();
66
- if (config.tokens?.include?.length) {
67
- const result = await parseTokenFiles(config.tokens, configDir);
68
- for (const token of result.tokens) {
69
- flatMap[token.name] = token.resolvedValue;
70
- }
71
- if (options.verbose) {
72
- console.log(
73
- pc.dim(` Parsed ${result.tokens.length} tokens from auto-detected config (${result.parseTimeMs.toFixed(1)}ms)`)
74
- );
75
- }
76
- }
77
- } catch {
78
- }
79
- if (Object.keys(flatMap).length === 0) {
80
- console.error(pc.red("Error: No token source specified"));
81
- console.error(pc.dim("Provide one of:"));
82
- console.error(pc.dim(" --tailwind-v4 <path> Path to Tailwind v4 CSS file with @theme block"));
83
- console.error(pc.dim(" --config <path> Path to fragments config with tokens.include"));
84
- console.error(pc.dim("\nExample: fragments tokens push --tailwind-v4 ./app.css"));
85
- process.exit(1);
86
- }
87
- }
88
- const tokenCount = Object.keys(flatMap).length;
89
- if (tokenCount === 0) {
90
- console.log(pc.yellow("No tokens found."));
91
- return;
92
- }
93
- console.log(pc.cyan(`Found ${tokenCount} tokens`));
94
- if (options.dryRun) {
95
- console.log(pc.dim("\nDry run \u2014 tokens that would be pushed:\n"));
96
- const entries = Object.entries(flatMap);
97
- for (const [name, value] of entries.slice(0, 20)) {
98
- console.log(` ${pc.bold(name)}: ${value}`);
99
- }
100
- if (entries.length > 20) {
101
- console.log(pc.dim(` ... and ${entries.length - 20} more`));
102
- }
103
- return;
104
- }
105
- try {
106
- const response = await fetch(`${baseUrl}/api/ingest`, {
107
- method: "POST",
108
- headers: {
109
- "Content-Type": "application/json",
110
- "Authorization": `Bearer ${apiKey}`
111
- },
112
- body: JSON.stringify({
113
- codeTokens: JSON.stringify(flatMap)
114
- })
115
- });
116
- if (!response.ok) {
117
- const errorText = await response.text();
118
- console.error(pc.red(`Error: API returned ${response.status}`));
119
- console.error(pc.dim(errorText));
120
- process.exit(1);
121
- }
122
- const result = await response.json();
123
- console.log(pc.green(`
124
- Pushed ${tokenCount} tokens to Fragments Cloud`));
125
- if (result.tokenDrift) {
126
- const drift = result.tokenDrift;
127
- const summary = drift.summary;
128
- if (summary) {
129
- console.log(pc.cyan("\nDrift Summary:"));
130
- if (summary.total !== void 0) console.log(pc.dim(` Total issues: ${summary.total}`));
131
- if (summary.missingInCode > 0) console.log(pc.yellow(` Missing in code: ${summary.missingInCode}`));
132
- if (summary.missingInFigma > 0) console.log(pc.yellow(` Missing in Figma: ${summary.missingInFigma}`));
133
- if (summary.valueMismatch > 0) console.log(pc.yellow(` Value mismatches: ${summary.valueMismatch}`));
134
- if (summary.score !== void 0) console.log(` Health score: ${summary.score}%`);
135
- }
136
- }
137
- console.log(pc.dim(`
138
- View dashboard: ${baseUrl}/tokens`));
139
- console.log(pc.dim(`Completed in ${Date.now() - startTime}ms`));
140
- } catch (error) {
141
- console.error(pc.red("Error pushing tokens:"), error instanceof Error ? error.message : error);
142
- process.exit(1);
143
- }
144
- }
145
- export {
146
- tokensPush
147
- };
148
- //# sourceMappingURL=tokens-push-HY3KO36V.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/tokens-push.ts"],"sourcesContent":["/**\n * fragments tokens push — Push code tokens to Fragments Cloud for drift comparison.\n *\n * Extracts CSS custom properties from Tailwind v4 @theme blocks (or config-based\n * token files) and POSTs them to the Fragments Cloud /api/ingest endpoint.\n * Supports --dry-run for local inspection without pushing.\n */\n\nimport { resolve } from 'node:path';\nimport pc from 'picocolors';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface TokensPushOptions {\n /** Path to fragments config file */\n config?: string;\n /** Path to Tailwind v4 CSS file with @theme block */\n tailwindV4?: string;\n /** Parse and display tokens without pushing */\n dryRun?: boolean;\n /** Show detailed output */\n verbose?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// tokensPush\n// ---------------------------------------------------------------------------\n\nexport async function tokensPush(options: TokensPushOptions): Promise<void> {\n const startTime = Date.now();\n\n // 1. Resolve API credentials\n const apiKey = process.env.FRAGMENTS_API_KEY;\n const baseUrl = process.env.FRAGMENTS_URL || 'https://app.usefragments.com';\n\n if (!apiKey && !options.dryRun) {\n console.error(pc.red('Error: FRAGMENTS_API_KEY environment variable is required'));\n console.error(pc.dim('Get your API key from https://app.usefragments.com/api-keys'));\n process.exit(1);\n }\n\n // 2. Token extraction\n const flatMap: Record<string, string> = {};\n\n if (options.tailwindV4) {\n // Use @theme parser for Tailwind v4 CSS files\n const { parseTailwindV4File } = await import('../service/token-parser.js');\n\n const filePath = resolve(process.cwd(), options.tailwindV4);\n const result = parseTailwindV4File(filePath);\n\n if (result.errors.length > 0) {\n for (const err of result.errors) {\n console.error(pc.red(` Error: ${err.message}`));\n }\n }\n\n if (options.verbose && result.warnings.length > 0) {\n for (const warn of result.warnings) {\n console.warn(pc.yellow(` Warning: ${warn}`));\n }\n }\n\n for (const token of result.tokens) {\n flatMap[token.name] = token.resolvedValue;\n }\n\n if (options.verbose) {\n console.log(\n pc.dim(` Parsed ${result.tokens.length} tokens from ${options.tailwindV4} (${result.parseTimeMs.toFixed(1)}ms)`),\n );\n }\n } else if (options.config) {\n // Use config-based token extraction\n try {\n const { loadConfig } = await import('../core/node.js');\n const { parseTokenFiles } = await import('../service/index.js');\n\n const { config, configDir } = await loadConfig(options.config);\n if (config.tokens?.include?.length) {\n const result = await parseTokenFiles(config.tokens, configDir);\n for (const token of result.tokens) {\n flatMap[token.name] = token.resolvedValue;\n }\n\n if (options.verbose) {\n console.log(\n pc.dim(` Parsed ${result.tokens.length} tokens from config (${result.parseTimeMs.toFixed(1)}ms)`),\n );\n }\n } else {\n console.error(pc.red('Error: Config file has no tokens.include patterns'));\n process.exit(1);\n }\n } catch (error) {\n console.error(pc.red('Error loading config:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n } else {\n // No source specified — try auto-detection\n try {\n const { loadConfig } = await import('../core/node.js');\n const { parseTokenFiles } = await import('../service/index.js');\n\n const { config, configDir } = await loadConfig();\n if (config.tokens?.include?.length) {\n const result = await parseTokenFiles(config.tokens, configDir);\n for (const token of result.tokens) {\n flatMap[token.name] = token.resolvedValue;\n }\n\n if (options.verbose) {\n console.log(\n pc.dim(` Parsed ${result.tokens.length} tokens from auto-detected config (${result.parseTimeMs.toFixed(1)}ms)`),\n );\n }\n }\n } catch {\n // No config found — fall through to error\n }\n\n if (Object.keys(flatMap).length === 0) {\n console.error(pc.red('Error: No token source specified'));\n console.error(pc.dim('Provide one of:'));\n console.error(pc.dim(' --tailwind-v4 <path> Path to Tailwind v4 CSS file with @theme block'));\n console.error(pc.dim(' --config <path> Path to fragments config with tokens.include'));\n console.error(pc.dim('\\nExample: fragments tokens push --tailwind-v4 ./app.css'));\n process.exit(1);\n }\n }\n\n const tokenCount = Object.keys(flatMap).length;\n\n if (tokenCount === 0) {\n console.log(pc.yellow('No tokens found.'));\n return;\n }\n\n console.log(pc.cyan(`Found ${tokenCount} tokens`));\n\n // 3. Dry run — display tokens and exit\n if (options.dryRun) {\n console.log(pc.dim('\\nDry run — tokens that would be pushed:\\n'));\n const entries = Object.entries(flatMap);\n for (const [name, value] of entries.slice(0, 20)) {\n console.log(` ${pc.bold(name)}: ${value}`);\n }\n if (entries.length > 20) {\n console.log(pc.dim(` ... and ${entries.length - 20} more`));\n }\n return;\n }\n\n // 4. POST to Fragments Cloud\n try {\n const response = await fetch(`${baseUrl}/api/ingest`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n codeTokens: JSON.stringify(flatMap),\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(pc.red(`Error: API returned ${response.status}`));\n console.error(pc.dim(errorText));\n process.exit(1);\n }\n\n const result = await response.json() as Record<string, unknown>;\n\n console.log(pc.green(`\\nPushed ${tokenCount} tokens to Fragments Cloud`));\n\n if (result.tokenDrift) {\n const drift = result.tokenDrift as Record<string, unknown>;\n const summary = drift.summary as Record<string, number> | undefined;\n if (summary) {\n console.log(pc.cyan('\\nDrift Summary:'));\n if (summary.total !== undefined) console.log(pc.dim(` Total issues: ${summary.total}`));\n if (summary.missingInCode > 0) console.log(pc.yellow(` Missing in code: ${summary.missingInCode}`));\n if (summary.missingInFigma > 0) console.log(pc.yellow(` Missing in Figma: ${summary.missingInFigma}`));\n if (summary.valueMismatch > 0) console.log(pc.yellow(` Value mismatches: ${summary.valueMismatch}`));\n if (summary.score !== undefined) console.log(` Health score: ${summary.score}%`);\n }\n }\n\n console.log(pc.dim(`\\nView dashboard: ${baseUrl}/tokens`));\n console.log(pc.dim(`Completed in ${Date.now() - startTime}ms`));\n } catch (error) {\n console.error(pc.red('Error pushing tokens:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,382 +0,0 @@
1
- /**
2
- * fragments init --cloud
3
- *
4
- * Zero-config cloud setup: opens browser for auth, receives API key
5
- * via localhost callback, detects project, installs deps, creates config,
6
- * runs first check.
7
- */
8
-
9
- import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
10
- import { randomBytes } from 'node:crypto';
11
- import { execSync, exec } from 'node:child_process';
12
- import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'node:fs';
13
- import { resolve, join } from 'node:path';
14
- import { platform } from 'node:os';
15
- import pc from 'picocolors';
16
- import { BRAND } from '../core/index.js';
17
-
18
- // ─── Types ──────────────────────────────────────────────────────────────────
19
-
20
- interface AuthResult {
21
- apiKey: string;
22
- orgName: string;
23
- }
24
-
25
- export interface InitCloudOptions {
26
- /** Cloud dashboard URL */
27
- url?: string;
28
- /** Port for localhost callback server */
29
- port?: number;
30
- /** Timeout in ms for auth */
31
- timeout?: number;
32
- /** Skip project detection and setup */
33
- authOnly?: boolean;
34
- /** Skip the first governance check */
35
- skipCheck?: boolean;
36
- }
37
-
38
- // ─── Utilities ──────────────────────────────────────────────────────────────
39
-
40
- function detectPackageManager(): 'pnpm' | 'yarn' | 'bun' | 'npm' {
41
- // Check current dir and parent dirs (for monorepos)
42
- let dir = process.cwd();
43
- const root = resolve('/');
44
- while (dir !== root) {
45
- if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';
46
- if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';
47
- if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';
48
- const parent = resolve(dir, '..');
49
- if (parent === dir) break;
50
- dir = parent;
51
- }
52
- return 'npm';
53
- }
54
-
55
- function detectFramework(): string {
56
- try {
57
- const pkg = JSON.parse(readFileSync(resolve('package.json'), 'utf-8'));
58
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
59
-
60
- if (allDeps['next']) return 'Next.js';
61
- if (allDeps['nuxt']) return 'Nuxt';
62
- if (allDeps['@sveltejs/kit']) return 'SvelteKit';
63
- if (allDeps['svelte']) return 'Svelte';
64
- if (allDeps['vue']) return 'Vue';
65
- if (allDeps['astro']) return 'Astro';
66
- if (allDeps['react']) return 'React';
67
- return 'Unknown';
68
- } catch {
69
- return 'Unknown';
70
- }
71
- }
72
-
73
- function inferInputGlob(framework: string): string | string[] {
74
- switch (framework) {
75
- case 'Next.js':
76
- return ['./app/**/*.{tsx,jsx}', './components/**/*.{tsx,jsx}'];
77
- case 'Nuxt':
78
- case 'Vue':
79
- return ['./**/*.vue'];
80
- case 'SvelteKit':
81
- case 'Svelte':
82
- return ['./src/**/*.svelte'];
83
- case 'Astro':
84
- return ['./src/**/*.{astro,tsx,jsx}'];
85
- default:
86
- return './src/**/*.{tsx,jsx}';
87
- }
88
- }
89
-
90
- function openBrowser(url: string): void {
91
- const os = platform();
92
- const cmd =
93
- os === 'darwin' ? 'open' :
94
- os === 'win32' ? 'start ""' :
95
- 'xdg-open';
96
-
97
- exec(`${cmd} "${url}"`);
98
- }
99
-
100
- function installCommand(pm: string): string {
101
- switch (pm) {
102
- case 'pnpm': return 'pnpm add';
103
- case 'yarn': return 'yarn add';
104
- case 'bun': return 'bun add';
105
- default: return 'npm install';
106
- }
107
- }
108
-
109
- function isMonorepoWorkspaceDep(): boolean {
110
- try {
111
- const pkg = JSON.parse(readFileSync(resolve('package.json'), 'utf-8'));
112
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
113
- return (
114
- allDeps['@fragments-sdk/govern']?.startsWith('workspace:') ||
115
- allDeps['@fragments-sdk/cli']?.startsWith('workspace:')
116
- );
117
- } catch {
118
- return false;
119
- }
120
- }
121
-
122
- // ─── Localhost Auth Server ──────────────────────────────────────────────────
123
-
124
- function waitForAuth(
125
- cloudUrl: string,
126
- port: number,
127
- timeoutMs: number,
128
- ): Promise<AuthResult> {
129
- const nonce = randomBytes(16).toString('hex');
130
-
131
- return new Promise<AuthResult>((resolve, reject) => {
132
- const timeout = setTimeout(() => {
133
- server.close();
134
- reject(new Error('Authentication timed out. Please try again.'));
135
- }, timeoutMs);
136
-
137
- const server = createServer((req: IncomingMessage, res: ServerResponse) => {
138
- const url = new URL(req.url!, `http://localhost:${port}`);
139
-
140
- if (url.pathname === '/callback') {
141
- const key = url.searchParams.get('key');
142
- const org = url.searchParams.get('org');
143
- const returnedNonce = url.searchParams.get('nonce');
144
-
145
- if (returnedNonce !== nonce) {
146
- res.writeHead(400, { 'Content-Type': 'text/plain' });
147
- res.end('Nonce mismatch — please try again.');
148
- return;
149
- }
150
-
151
- if (!key) {
152
- res.writeHead(400, { 'Content-Type': 'text/plain' });
153
- res.end('Missing API key in callback.');
154
- return;
155
- }
156
-
157
- // Show "you can close this tab" page
158
- res.writeHead(200, { 'Content-Type': 'text/html' });
159
- res.end(`<!DOCTYPE html>
160
- <html><head><title>Fragments CLI</title>
161
- <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#0a0a0f;color:#e5e5e5}
162
- .box{text-align:center;padding:48px}.check{font-size:48px;margin-bottom:16px}p{color:#888;margin-top:8px}</style>
163
- </head><body><div class="box"><div class="check">&#10003;</div><h2>CLI Authorized</h2><p>You can close this tab and return to your terminal.</p></div>
164
- <script>setTimeout(()=>window.close(),3000)</script>
165
- </body></html>`);
166
-
167
- clearTimeout(timeout);
168
- server.close();
169
- resolve({ apiKey: key, orgName: org ?? 'your organization' });
170
- } else {
171
- res.writeHead(404);
172
- res.end();
173
- }
174
- });
175
-
176
- server.on('error', (err: NodeJS.ErrnoException) => {
177
- if (err.code === 'EADDRINUSE') {
178
- clearTimeout(timeout);
179
- reject(new Error(`Port ${port} is in use. Try: fragments init --cloud --port ${port + 1}`));
180
- } else {
181
- clearTimeout(timeout);
182
- reject(err);
183
- }
184
- });
185
-
186
- server.listen(port, '127.0.0.1', () => {
187
- const authUrl = `${cloudUrl}/cli-auth?port=${port}&nonce=${nonce}`;
188
- openBrowser(authUrl);
189
- });
190
- });
191
- }
192
-
193
- // ─── Save to .env ───────────────────────────────────────────────────────────
194
-
195
- function saveApiKey(apiKey: string, cloudUrl: string): void {
196
- const envPath = resolve('.env');
197
- const entry = `FRAGMENTS_API_KEY=${apiKey}`;
198
-
199
- if (existsSync(envPath)) {
200
- const content = readFileSync(envPath, 'utf-8');
201
- if (content.includes('FRAGMENTS_API_KEY=')) {
202
- const updated = content.replace(/^FRAGMENTS_API_KEY=.*$/m, entry);
203
- writeFileSync(envPath, updated, 'utf-8');
204
- } else {
205
- appendFileSync(envPath, `\n${entry}\n`, 'utf-8');
206
- }
207
- } else {
208
- writeFileSync(envPath, `${entry}\n`, 'utf-8');
209
- }
210
-
211
- // Add FRAGMENTS_URL if non-default
212
- if (cloudUrl !== 'https://app.usefragments.com') {
213
- const content = readFileSync(envPath, 'utf-8');
214
- if (!content.includes('FRAGMENTS_URL=')) {
215
- appendFileSync(envPath, `FRAGMENTS_URL=${cloudUrl}\n`, 'utf-8');
216
- }
217
- }
218
-
219
- // Ensure .env is in .gitignore
220
- const gitignorePath = resolve('.gitignore');
221
- if (existsSync(gitignorePath)) {
222
- const gitignore = readFileSync(gitignorePath, 'utf-8');
223
- if (!gitignore.split('\n').some((line) => line.trim() === '.env')) {
224
- appendFileSync(gitignorePath, '\n.env\n', 'utf-8');
225
- }
226
- } else {
227
- writeFileSync(gitignorePath, '.env\n', 'utf-8');
228
- }
229
- }
230
-
231
- // ─── Write governance config ────────────────────────────────────────────────
232
-
233
- function writeGovernConfig(input: string | string[]): void {
234
- const configPath = resolve(BRAND.configFile);
235
- if (existsSync(configPath)) return; // Don't overwrite
236
-
237
- const inputStr = Array.isArray(input)
238
- ? `[${input.map((p) => `'${p}'`).join(', ')}]`
239
- : `'${input}'`;
240
-
241
- const template = `import { defineConfig } from '@fragments-sdk/govern';
242
-
243
- export default defineConfig({
244
- cloud: true,
245
- checks: ['accessibility', 'consistency', 'responsive'],
246
- input: ${inputStr},
247
- });
248
- `;
249
-
250
- writeFileSync(configPath, template, 'utf-8');
251
- }
252
-
253
- // ─── Main ───────────────────────────────────────────────────────────────────
254
-
255
- export async function initCloud(options: InitCloudOptions = {}): Promise<void> {
256
- const cloudUrl = options.url ?? process.env.FRAGMENTS_URL ?? 'https://app.usefragments.com';
257
- const port = options.port ?? 9876;
258
- const timeoutMs = options.timeout ?? 120_000;
259
-
260
- console.log(pc.bold(`\n ${BRAND.name}\n`));
261
-
262
- // ── 1. Detect project ─────────────────────────────────────────────
263
- if (!options.authOnly) {
264
- if (!existsSync(resolve('package.json'))) {
265
- console.log(pc.red(' No package.json found. Run this from a project directory.\n'));
266
- process.exit(1);
267
- }
268
-
269
- const pm = detectPackageManager();
270
- const framework = detectFramework();
271
- console.log(pc.dim(` Project: ${framework}`));
272
- console.log(pc.dim(` Package manager: ${pm}\n`));
273
- }
274
-
275
- // ── 2. Authenticate ───────────────────────────────────────────────
276
- console.log(pc.dim(' Opening browser to sign in...\n'));
277
-
278
- let auth: AuthResult;
279
- try {
280
- auth = await waitForAuth(cloudUrl, port, timeoutMs);
281
- } catch (err) {
282
- console.log(pc.red(`\n ${err instanceof Error ? err.message : 'Auth failed'}\n`));
283
- process.exit(1);
284
- }
285
-
286
- console.log(pc.green(` ✓ Authenticated — ${auth.orgName}\n`));
287
-
288
- // ── 3. Save API key ──────────────────────────────────────────────
289
- saveApiKey(auth.apiKey, cloudUrl);
290
- console.log(pc.green(' ✓ API key saved to .env'));
291
-
292
- if (options.authOnly) {
293
- console.log(pc.green('\n ✓ All set!\n'));
294
- console.log(pc.dim(` Dashboard: ${cloudUrl}\n`));
295
- return;
296
- }
297
-
298
- // ── 4. Install dependencies ───────────────────────────────────────
299
- const pm = detectPackageManager();
300
-
301
- if (!isMonorepoWorkspaceDep()) {
302
- const deps = '@fragments-sdk/govern @fragments-sdk/cli';
303
- console.log(pc.dim(`\n Installing ${deps}...`));
304
- try {
305
- execSync(`${installCommand(pm)} ${deps}`, {
306
- stdio: 'pipe',
307
- cwd: process.cwd(),
308
- });
309
- console.log(pc.green(' ✓ Dependencies installed'));
310
- } catch (err) {
311
- console.log(pc.yellow(' ⚠ Install failed — you may need to install manually:'));
312
- console.log(pc.dim(` ${installCommand(pm)} ${deps}\n`));
313
- }
314
- } else {
315
- console.log(pc.dim('\n Workspace deps detected — skipping install'));
316
- }
317
-
318
- // ── 5. Create governance config ───────────────────────────────────
319
- const framework = detectFramework();
320
- const input = inferInputGlob(framework);
321
- const configPath = resolve(BRAND.configFile);
322
-
323
- if (existsSync(configPath)) {
324
- console.log(pc.dim(` Config already exists: ${BRAND.configFile}`));
325
- } else {
326
- writeGovernConfig(input);
327
- console.log(pc.green(` ✓ Created ${BRAND.configFile}`));
328
- }
329
-
330
- // ── 6. Push contracts if available ──────────────────────────────────
331
- const fragmentsJsonPath = resolve('fragments.json');
332
- if (existsSync(fragmentsJsonPath)) {
333
- console.log(pc.dim('\n Found fragments.json — pushing contracts to Cloud...'));
334
- try {
335
- const { pushContracts } = await import('@fragments-sdk/govern');
336
- const raw = readFileSync(fragmentsJsonPath, 'utf-8');
337
- const parsed = JSON.parse(raw);
338
- if (parsed.fragments && Array.isArray(parsed.fragments)) {
339
- const result = await pushContracts({
340
- contractRegistry: JSON.stringify({ fragments: parsed.fragments }),
341
- apiKey: auth.apiKey,
342
- url: cloudUrl,
343
- });
344
- if (result.ok) {
345
- console.log(pc.green(` ✓ Pushed ${parsed.fragments.length} component contracts`));
346
- } else {
347
- console.log(pc.yellow(` ⚠ Contract push failed: ${result.error}`));
348
- }
349
- }
350
- } catch {
351
- console.log(pc.yellow(' ⚠ Could not push contracts'));
352
- }
353
- } else {
354
- console.log(pc.dim('\n No fragments.json found — run `fragments scan` or `fragments build` to generate contracts'));
355
- }
356
-
357
- // ── 7. Run first check ────────────────────────────────────────────
358
- if (!options.skipCheck) {
359
- console.log(pc.dim('\n Running first governance check...\n'));
360
- try {
361
- const output = execSync('npx fragments govern check --cloud', {
362
- stdio: 'pipe',
363
- cwd: process.cwd(),
364
- env: { ...process.env, FRAGMENTS_API_KEY: auth.apiKey },
365
- });
366
- console.log(output.toString());
367
- } catch (err: any) {
368
- // Check may "fail" with violations — that's OK
369
- if (err.stdout) {
370
- console.log(err.stdout.toString());
371
- }
372
- console.log(pc.dim(' (check completed with violations — see dashboard for details)'));
373
- }
374
- }
375
-
376
- // ── 8. Done ───────────────────────────────────────────────────────
377
- console.log(pc.green('\n ✓ All set!') + ' Your project is connected to Fragments Cloud.\n');
378
- console.log(pc.dim(` Dashboard: ${cloudUrl}`));
379
- console.log(pc.dim(' Run checks: fragments govern scan'));
380
- console.log(pc.dim(' Push contracts: fragments govern push-contracts'));
381
- console.log(pc.dim(' View config: fragments.config.ts\n'));
382
- }
@@ -1,112 +0,0 @@
1
- /**
2
- * fragments push-contracts
3
- *
4
- * Push compiled component contracts to Fragments Cloud.
5
- * Reads fragments.json from the project root (or builds it from .contract.json files)
6
- * and posts the contract registry to the Cloud ingest endpoint.
7
- */
8
-
9
- import { readFileSync, existsSync } from 'node:fs';
10
- import { resolve } from 'node:path';
11
- import pc from 'picocolors';
12
- import { BRAND } from '../core/index.js';
13
-
14
- export interface PushContractsOptions {
15
- /** Path to fragments.json or directory containing .contract.json files */
16
- input?: string;
17
- /** Fragments Cloud URL */
18
- url?: string;
19
- /** API key (defaults to FRAGMENTS_API_KEY env var) */
20
- apiKey?: string;
21
- /** Suppress output */
22
- quiet?: boolean;
23
- }
24
-
25
- export async function pushContracts(
26
- options: PushContractsOptions = {},
27
- ): Promise<{ exitCode: number }> {
28
- const quiet = options.quiet ?? false;
29
- const apiKey = options.apiKey ?? process.env.FRAGMENTS_API_KEY;
30
-
31
- if (!apiKey) {
32
- console.error(
33
- pc.red('No API key found. Set FRAGMENTS_API_KEY or pass --api-key.'),
34
- );
35
- return { exitCode: 1 };
36
- }
37
-
38
- if (!quiet) {
39
- console.log(pc.cyan(`\n${BRAND.name} Push Contracts\n`));
40
- }
41
-
42
- // 1. Find contract registry
43
- const inputPath = options.input
44
- ? resolve(options.input)
45
- : resolve('fragments.json');
46
-
47
- if (!existsSync(inputPath)) {
48
- console.error(
49
- pc.red(`Contract registry not found at ${inputPath}`),
50
- );
51
- console.error(
52
- pc.dim('Run `fragments build` or `fragments scan` to generate fragments.json first.'),
53
- );
54
- return { exitCode: 1 };
55
- }
56
-
57
- // 2. Parse and validate
58
- let contractRegistry: string;
59
- let componentCount = 0;
60
-
61
- try {
62
- const raw = readFileSync(inputPath, 'utf-8');
63
- const parsed = JSON.parse(raw);
64
- const fragments = parsed.fragments ?? parsed;
65
-
66
- if (!Array.isArray(fragments)) {
67
- console.error(pc.red('Invalid contract registry: expected fragments array'));
68
- return { exitCode: 1 };
69
- }
70
-
71
- componentCount = fragments.length;
72
- contractRegistry = JSON.stringify({ fragments });
73
- } catch (error) {
74
- console.error(
75
- pc.red('Failed to parse contract registry:'),
76
- error instanceof Error ? error.message : 'unknown error',
77
- );
78
- return { exitCode: 1 };
79
- }
80
-
81
- if (componentCount === 0) {
82
- console.warn(pc.yellow('No components found in contract registry. Nothing to push.'));
83
- return { exitCode: 0 };
84
- }
85
-
86
- if (!quiet) {
87
- console.log(pc.dim(` Found ${componentCount} component contracts\n`));
88
- console.log(pc.dim(' Pushing to Fragments Cloud...\n'));
89
- }
90
-
91
- // 3. Push to Cloud
92
- const { pushContracts: push } = await import('@fragments-sdk/govern');
93
-
94
- const result = await push({
95
- contractRegistry,
96
- url: options.url,
97
- apiKey,
98
- });
99
-
100
- if (!result.ok) {
101
- console.error(pc.red(`Failed to push contracts: ${result.error}`));
102
- return { exitCode: 1 };
103
- }
104
-
105
- if (!quiet) {
106
- console.log(
107
- pc.green(` ✓ Pushed ${componentCount} component contracts to Fragments Cloud\n`),
108
- );
109
- }
110
-
111
- return { exitCode: 0 };
112
- }