@fragments-sdk/cli 0.13.0 → 0.13.1
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/bin.js +152 -16
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-3SOAPJDX.js → chunk-55KERLWL.js} +2 -2
- package/dist/{chunk-4K7EAQ5L.js → chunk-7K3VROEP.js} +2 -2
- package/dist/{chunk-RF3C6LGA.js → chunk-FZLPVN32.js} +5 -5
- package/dist/{chunk-QM7SVOGF.js → chunk-I34BC3CU.js} +10 -1
- package/dist/chunk-I34BC3CU.js.map +1 -0
- package/dist/{chunk-DXX6HADE.js → chunk-PJT5IZ37.js} +2 -2
- package/dist/{chunk-UV5JQV3R.js → chunk-TXFCEDOC.js} +2 -2
- package/dist/{chunk-FO6EBJWP.js → chunk-Z5BUXIFJ.js} +5 -5
- package/dist/{chunk-SM674YAS.js → chunk-ZKTFKHWN.js} +2 -2
- package/dist/core/index.js +1 -1
- package/dist/{discovery-VSGC76JN.js → discovery-VDANZAJ2.js} +3 -3
- package/dist/{generate-QZXOXYFW.js → generate-RYWIPDN2.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{init-XK6PRUE5.js → init-U6534EMZ.js} +5 -5
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-CHQHXWVD.js → scan-LE2JEIJ4.js} +6 -6
- package/dist/{scan-generate-U3RFVDTX.js → scan-generate-TFZVL3BT.js} +4 -4
- package/dist/{service-MMEKG4MZ.js → service-S5LXPKV4.js} +3 -3
- package/dist/{snapshot-53TUR3HW.js → snapshot-C5DYIGIV.js} +2 -2
- package/dist/{static-viewer-KKCR4KXR.js → static-viewer-DUVC4UIM.js} +3 -3
- package/dist/{test-5UCKXYSC.js → test-JW7JIDFG.js} +4 -4
- package/dist/{tokens-L46MK5AW.js → tokens-OPVTVITP.js} +5 -5
- package/dist/{viewer-M2EQQSGE.js → viewer-OBTEPVY7.js} +13 -13
- package/package.json +6 -6
- package/src/bin.ts +15 -3
- package/src/commands/govern.ts +158 -1
- package/dist/chunk-QM7SVOGF.js.map +0 -1
- /package/dist/{chunk-3SOAPJDX.js.map → chunk-55KERLWL.js.map} +0 -0
- /package/dist/{chunk-4K7EAQ5L.js.map → chunk-7K3VROEP.js.map} +0 -0
- /package/dist/{chunk-RF3C6LGA.js.map → chunk-FZLPVN32.js.map} +0 -0
- /package/dist/{chunk-DXX6HADE.js.map → chunk-PJT5IZ37.js.map} +0 -0
- /package/dist/{chunk-UV5JQV3R.js.map → chunk-TXFCEDOC.js.map} +0 -0
- /package/dist/{chunk-FO6EBJWP.js.map → chunk-Z5BUXIFJ.js.map} +0 -0
- /package/dist/{chunk-SM674YAS.js.map → chunk-ZKTFKHWN.js.map} +0 -0
- /package/dist/{discovery-VSGC76JN.js.map → discovery-VDANZAJ2.js.map} +0 -0
- /package/dist/{generate-QZXOXYFW.js.map → generate-RYWIPDN2.js.map} +0 -0
- /package/dist/{init-XK6PRUE5.js.map → init-U6534EMZ.js.map} +0 -0
- /package/dist/{scan-CHQHXWVD.js.map → scan-LE2JEIJ4.js.map} +0 -0
- /package/dist/{scan-generate-U3RFVDTX.js.map → scan-generate-TFZVL3BT.js.map} +0 -0
- /package/dist/{service-MMEKG4MZ.js.map → service-S5LXPKV4.js.map} +0 -0
- /package/dist/{snapshot-53TUR3HW.js.map → snapshot-C5DYIGIV.js.map} +0 -0
- /package/dist/{static-viewer-KKCR4KXR.js.map → static-viewer-DUVC4UIM.js.map} +0 -0
- /package/dist/{test-5UCKXYSC.js.map → test-JW7JIDFG.js.map} +0 -0
- /package/dist/{tokens-L46MK5AW.js.map → tokens-OPVTVITP.js.map} +0 -0
- /package/dist/{viewer-M2EQQSGE.js.map → viewer-OBTEPVY7.js.map} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
scan
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-Z5BUXIFJ.js";
|
|
5
|
+
import "./chunk-55KERLWL.js";
|
|
6
|
+
import "./chunk-ZKTFKHWN.js";
|
|
7
|
+
import "./chunk-7K3VROEP.js";
|
|
8
8
|
import "./chunk-D2CDBRNU.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-I34BC3CU.js";
|
|
10
10
|
import "./chunk-Z7EY4VHE.js";
|
|
11
11
|
export {
|
|
12
12
|
scan
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=scan-
|
|
14
|
+
//# sourceMappingURL=scan-LE2JEIJ4.js.map
|
|
@@ -2,14 +2,14 @@ import { createRequire as __banner_createRequire } from 'module'; const require
|
|
|
2
2
|
import {
|
|
3
3
|
createComponentExtractor
|
|
4
4
|
} from "./chunk-EYXVAMEX.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-55KERLWL.js";
|
|
6
6
|
import {
|
|
7
7
|
discoverAllComponents
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZKTFKHWN.js";
|
|
9
9
|
import "./chunk-D2CDBRNU.js";
|
|
10
10
|
import {
|
|
11
11
|
BRAND
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-I34BC3CU.js";
|
|
13
13
|
import "./chunk-Z7EY4VHE.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/scan-generate.ts
|
|
@@ -1112,4 +1112,4 @@ export {
|
|
|
1112
1112
|
parseEnrichmentResponse,
|
|
1113
1113
|
scanGenerate
|
|
1114
1114
|
};
|
|
1115
|
-
//# sourceMappingURL=scan-generate-
|
|
1115
|
+
//# sourceMappingURL=scan-generate-TFZVL3BT.js.map
|
|
@@ -97,12 +97,12 @@ import {
|
|
|
97
97
|
shutdownSharedPool,
|
|
98
98
|
sleep,
|
|
99
99
|
summarizePatternsForPrompt
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-7K3VROEP.js";
|
|
101
101
|
import "./chunk-D2CDBRNU.js";
|
|
102
102
|
import {
|
|
103
103
|
BRAND,
|
|
104
104
|
DEFAULTS
|
|
105
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-I34BC3CU.js";
|
|
106
106
|
import "./chunk-Z7EY4VHE.js";
|
|
107
107
|
export {
|
|
108
108
|
BRAND,
|
|
@@ -206,4 +206,4 @@ export {
|
|
|
206
206
|
sleep,
|
|
207
207
|
summarizePatternsForPrompt
|
|
208
208
|
};
|
|
209
|
-
//# sourceMappingURL=service-
|
|
209
|
+
//# sourceMappingURL=service-S5LXPKV4.js.map
|
|
@@ -2,7 +2,7 @@ import { createRequire as __banner_createRequire } from 'module'; const require
|
|
|
2
2
|
import "./chunk-D2CDBRNU.js";
|
|
3
3
|
import {
|
|
4
4
|
BRAND
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-I34BC3CU.js";
|
|
6
6
|
import "./chunk-Z7EY4VHE.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/snapshot.ts
|
|
@@ -136,4 +136,4 @@ ${BRAND.name} Visual Snapshots
|
|
|
136
136
|
export {
|
|
137
137
|
snapshot
|
|
138
138
|
};
|
|
139
|
-
//# sourceMappingURL=snapshot-
|
|
139
|
+
//# sourceMappingURL=snapshot-C5DYIGIV.js.map
|
|
@@ -2,12 +2,12 @@ import { createRequire as __banner_createRequire } from 'module'; const require
|
|
|
2
2
|
import {
|
|
3
3
|
generateStaticViewer,
|
|
4
4
|
generateViewerFromJson
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TXFCEDOC.js";
|
|
6
6
|
import "./chunk-D2CDBRNU.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-I34BC3CU.js";
|
|
8
8
|
import "./chunk-Z7EY4VHE.js";
|
|
9
9
|
export {
|
|
10
10
|
generateStaticViewer,
|
|
11
11
|
generateViewerFromJson
|
|
12
12
|
};
|
|
13
|
-
//# sourceMappingURL=static-viewer-
|
|
13
|
+
//# sourceMappingURL=static-viewer-DUVC4UIM.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
parseFragmentFile
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-55KERLWL.js";
|
|
5
5
|
import {
|
|
6
6
|
discoverFragmentFiles
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-ZKTFKHWN.js";
|
|
8
|
+
import "./chunk-I34BC3CU.js";
|
|
9
9
|
import "./chunk-Z7EY4VHE.js";
|
|
10
10
|
|
|
11
11
|
// src/test/index.ts
|
|
@@ -1071,4 +1071,4 @@ export {
|
|
|
1071
1071
|
listTests,
|
|
1072
1072
|
runTestCommand
|
|
1073
1073
|
};
|
|
1074
|
-
//# sourceMappingURL=test-
|
|
1074
|
+
//# sourceMappingURL=test-JW7JIDFG.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
loadConfig
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-55KERLWL.js";
|
|
5
|
+
import "./chunk-ZKTFKHWN.js";
|
|
6
6
|
import {
|
|
7
7
|
parseTokenFiles
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7K3VROEP.js";
|
|
9
9
|
import "./chunk-D2CDBRNU.js";
|
|
10
10
|
import {
|
|
11
11
|
BRAND
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-I34BC3CU.js";
|
|
13
13
|
import "./chunk-Z7EY4VHE.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/tokens.ts
|
|
@@ -172,4 +172,4 @@ export {
|
|
|
172
172
|
tokens_default as default,
|
|
173
173
|
tokens
|
|
174
174
|
};
|
|
175
|
-
//# sourceMappingURL=tokens-
|
|
175
|
+
//# sourceMappingURL=tokens-OPVTVITP.js.map
|
|
@@ -5,16 +5,16 @@ import {
|
|
|
5
5
|
generatePreviewModule,
|
|
6
6
|
loadConfig,
|
|
7
7
|
parseFragmentFile
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-55KERLWL.js";
|
|
9
9
|
import {
|
|
10
10
|
discoverFragmentFiles,
|
|
11
11
|
discoverInstalledFragments
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZKTFKHWN.js";
|
|
13
13
|
import "./chunk-D2CDBRNU.js";
|
|
14
14
|
import {
|
|
15
15
|
BRAND,
|
|
16
16
|
generateContext
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-I34BC3CU.js";
|
|
18
18
|
import "./chunk-Z7EY4VHE.js";
|
|
19
19
|
|
|
20
20
|
// src/viewer/server.ts
|
|
@@ -373,7 +373,7 @@ var sharedRenderPool = null;
|
|
|
373
373
|
var browserPoolModule = null;
|
|
374
374
|
async function getSharedRenderPool() {
|
|
375
375
|
if (!browserPoolModule) {
|
|
376
|
-
browserPoolModule = await import("./service-
|
|
376
|
+
browserPoolModule = await import("./service-S5LXPKV4.js");
|
|
377
377
|
}
|
|
378
378
|
if (!sharedRenderPool) {
|
|
379
379
|
sharedRenderPool = new browserPoolModule.BrowserPool({
|
|
@@ -616,7 +616,7 @@ function fragmentsPlugin(options) {
|
|
|
616
616
|
const address = _server.httpServer?.address();
|
|
617
617
|
const port = typeof address === "object" && address ? address.port : 6006;
|
|
618
618
|
const renderViewport = viewport || { width: 800, height: 600 };
|
|
619
|
-
const { FigmaClient, bufferToBase64Url } = await import("./service-
|
|
619
|
+
const { FigmaClient, bufferToBase64Url } = await import("./service-S5LXPKV4.js");
|
|
620
620
|
const figmaClient = new FigmaClient({
|
|
621
621
|
accessToken: figmaToken
|
|
622
622
|
});
|
|
@@ -707,7 +707,7 @@ function fragmentsPlugin(options) {
|
|
|
707
707
|
);
|
|
708
708
|
return;
|
|
709
709
|
}
|
|
710
|
-
const { FigmaClient } = await import("./service-
|
|
710
|
+
const { FigmaClient } = await import("./service-S5LXPKV4.js");
|
|
711
711
|
const figmaClient = new FigmaClient({ accessToken: figmaToken });
|
|
712
712
|
const { fileKey, nodeId } = figmaClient.parseUrl(figmaUrl);
|
|
713
713
|
const figmaDesignProps = await figmaClient.getNodeProperties(
|
|
@@ -748,7 +748,7 @@ function fragmentsPlugin(options) {
|
|
|
748
748
|
}));
|
|
749
749
|
return;
|
|
750
750
|
}
|
|
751
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
751
|
+
const { getSharedTokenRegistry } = await import("./service-S5LXPKV4.js");
|
|
752
752
|
const registry = getSharedTokenRegistry();
|
|
753
753
|
if (!registry.isInitialized()) {
|
|
754
754
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -808,7 +808,7 @@ function fragmentsPlugin(options) {
|
|
|
808
808
|
}));
|
|
809
809
|
return;
|
|
810
810
|
}
|
|
811
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
811
|
+
const { getSharedTokenRegistry } = await import("./service-S5LXPKV4.js");
|
|
812
812
|
const registry = getSharedTokenRegistry();
|
|
813
813
|
if (!registry.isInitialized()) {
|
|
814
814
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -870,7 +870,7 @@ function fragmentsPlugin(options) {
|
|
|
870
870
|
res.end(JSON.stringify({ error: "Could not resolve fragment file path" }));
|
|
871
871
|
return;
|
|
872
872
|
}
|
|
873
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
873
|
+
const { getSharedTokenRegistry } = await import("./service-S5LXPKV4.js");
|
|
874
874
|
const registry = getSharedTokenRegistry();
|
|
875
875
|
if (!registry.isInitialized()) {
|
|
876
876
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -1048,7 +1048,7 @@ function fragmentsPlugin(options) {
|
|
|
1048
1048
|
}
|
|
1049
1049
|
if (!config.tokens || !config.tokens.include || config.tokens.include.length === 0) {
|
|
1050
1050
|
try {
|
|
1051
|
-
const { discoverTokenFiles } = await import("./discovery-
|
|
1051
|
+
const { discoverTokenFiles } = await import("./discovery-VDANZAJ2.js");
|
|
1052
1052
|
const discovered = await discoverTokenFiles(projectRoot);
|
|
1053
1053
|
if (discovered.length > 0) {
|
|
1054
1054
|
config.tokens = {
|
|
@@ -1085,7 +1085,7 @@ function fragmentsPlugin(options) {
|
|
|
1085
1085
|
const {
|
|
1086
1086
|
getSharedTokenRegistry,
|
|
1087
1087
|
generateTokenPatches
|
|
1088
|
-
} = await import("./service-
|
|
1088
|
+
} = await import("./service-S5LXPKV4.js");
|
|
1089
1089
|
const registry = getSharedTokenRegistry();
|
|
1090
1090
|
if (!registry.isInitialized()) {
|
|
1091
1091
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -2194,7 +2194,7 @@ async function loadFullFragmentForCompare(_server, _fragmentFiles, componentName
|
|
|
2194
2194
|
}
|
|
2195
2195
|
}
|
|
2196
2196
|
async function compareImages(image1Base64, image2Base64, threshold) {
|
|
2197
|
-
const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-
|
|
2197
|
+
const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-S5LXPKV4.js");
|
|
2198
2198
|
const { PNG } = await import("pngjs");
|
|
2199
2199
|
const buffer1 = base64UrlToBuffer(image1Base64);
|
|
2200
2200
|
const buffer2 = base64UrlToBuffer(image2Base64);
|
|
@@ -2727,4 +2727,4 @@ export {
|
|
|
2727
2727
|
createDevServer,
|
|
2728
2728
|
fragmentsPlugin
|
|
2729
2729
|
};
|
|
2730
|
-
//# sourceMappingURL=viewer-
|
|
2730
|
+
//# sourceMappingURL=viewer-OBTEPVY7.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragments-sdk/cli",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"license": "FSL-1.1-MIT",
|
|
5
5
|
"description": "CLI, MCP server, and dev tools for Fragments design system",
|
|
6
6
|
"author": "Conan McNicholl",
|
|
@@ -80,11 +80,11 @@
|
|
|
80
80
|
"vite": "^6.0.0",
|
|
81
81
|
"vite-plugin-svgr": "^4.5.0",
|
|
82
82
|
"zod": "^3.24.1",
|
|
83
|
-
"@fragments-sdk/context": "0.5.
|
|
84
|
-
"@fragments-sdk/core": "1.0.
|
|
85
|
-
"@fragments-sdk/
|
|
86
|
-
"@fragments-sdk/
|
|
87
|
-
"@fragments-sdk/
|
|
83
|
+
"@fragments-sdk/context": "0.5.1",
|
|
84
|
+
"@fragments-sdk/core": "1.0.1",
|
|
85
|
+
"@fragments-sdk/govern": "^0.2.1",
|
|
86
|
+
"@fragments-sdk/viewer": "0.2.5",
|
|
87
|
+
"@fragments-sdk/webmcp": "2.0.1"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
90
|
"@types/babel__generator": "^7.6.8",
|
package/src/bin.ts
CHANGED
|
@@ -40,7 +40,7 @@ import { perf } from './commands/perf.js';
|
|
|
40
40
|
import { doctor } from './commands/doctor.js';
|
|
41
41
|
import { setup } from './commands/setup.js';
|
|
42
42
|
import { sync } from './commands/sync.js';
|
|
43
|
-
import { governCheck, governInit, governReport } from './commands/govern.js';
|
|
43
|
+
import { governCheck, governInit, governReport, governConnect } from './commands/govern.js';
|
|
44
44
|
|
|
45
45
|
// Import existing commands that were already extracted
|
|
46
46
|
import { runScreenshotCommand } from './screenshot.js';
|
|
@@ -1147,8 +1147,8 @@ governCmd
|
|
|
1147
1147
|
|
|
1148
1148
|
governCmd
|
|
1149
1149
|
.command('init')
|
|
1150
|
-
.description('Generate a
|
|
1151
|
-
.option('-o, --output <path>', 'Output path', '
|
|
1150
|
+
.description('Generate a fragments.config.ts with govern section')
|
|
1151
|
+
.option('-o, --output <path>', 'Output path', 'fragments.config.ts')
|
|
1152
1152
|
.action(async (options) => {
|
|
1153
1153
|
try {
|
|
1154
1154
|
await governInit({ output: options.output });
|
|
@@ -1170,5 +1170,17 @@ governCmd
|
|
|
1170
1170
|
}
|
|
1171
1171
|
});
|
|
1172
1172
|
|
|
1173
|
+
governCmd
|
|
1174
|
+
.command('connect')
|
|
1175
|
+
.description('Connect your project to the Fragments Govern cloud dashboard')
|
|
1176
|
+
.action(async () => {
|
|
1177
|
+
try {
|
|
1178
|
+
await governConnect();
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
1181
|
+
process.exit(1);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1173
1185
|
// Parse command line arguments
|
|
1174
1186
|
program.parse();
|
package/src/commands/govern.ts
CHANGED
|
@@ -65,13 +65,170 @@ export async function governInit(options: GovernInitOptions = {}): Promise<void>
|
|
|
65
65
|
const { resolve } = await import('node:path');
|
|
66
66
|
const { generateConfigTemplate } = await import('@fragments-sdk/govern');
|
|
67
67
|
|
|
68
|
-
const outputPath = resolve(options.output ?? '
|
|
68
|
+
const outputPath = resolve(options.output ?? 'fragments.config.ts');
|
|
69
69
|
const template = generateConfigTemplate();
|
|
70
70
|
|
|
71
71
|
await writeFile(outputPath, template, 'utf-8');
|
|
72
72
|
console.log(pc.green(`✓ Created ${outputPath}\n`));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// connect
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
export async function governConnect(): Promise<void> {
|
|
80
|
+
const { readFile, writeFile, appendFile } = await import('node:fs/promises');
|
|
81
|
+
const { existsSync } = await import('node:fs');
|
|
82
|
+
const { resolve } = await import('node:path');
|
|
83
|
+
const { platform } = await import('node:os');
|
|
84
|
+
const { exec } = await import('node:child_process');
|
|
85
|
+
const { password, confirm } = await import('@inquirer/prompts');
|
|
86
|
+
|
|
87
|
+
const cloudUrl = process.env.FRAGMENTS_URL ?? 'https://app.usefragments.com';
|
|
88
|
+
|
|
89
|
+
console.log(pc.cyan(`\n ${BRAND.name} — Connect to Cloud\n`));
|
|
90
|
+
console.log(
|
|
91
|
+
pc.dim(' This will connect your project to the Fragments dashboard\n') +
|
|
92
|
+
pc.dim(' for centralized audit tracking and team visibility.\n'),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// ── Step 1: Get API key ──────────────────────────────────────────────────
|
|
96
|
+
console.log(pc.bold(' Step 1 of 3: Get your API key\n'));
|
|
97
|
+
|
|
98
|
+
const dashboardUrl = `${cloudUrl}/dashboard/settings`;
|
|
99
|
+
console.log(pc.dim(` → Opening the dashboard in your browser...`));
|
|
100
|
+
console.log(pc.dim(` Copy your API key from Settings → API Keys\n`));
|
|
101
|
+
|
|
102
|
+
// Open browser (best-effort)
|
|
103
|
+
const os = platform();
|
|
104
|
+
const openCmd = os === 'darwin'
|
|
105
|
+
? `open "${dashboardUrl}"`
|
|
106
|
+
: os === 'win32'
|
|
107
|
+
? `start "" "${dashboardUrl}"`
|
|
108
|
+
: `xdg-open "${dashboardUrl}"`;
|
|
109
|
+
exec(openCmd);
|
|
110
|
+
|
|
111
|
+
let apiKey: string;
|
|
112
|
+
let orgName: string;
|
|
113
|
+
|
|
114
|
+
// eslint-disable-next-line no-constant-condition
|
|
115
|
+
while (true) {
|
|
116
|
+
apiKey = await password({
|
|
117
|
+
message: 'Paste your API key:',
|
|
118
|
+
mask: '*',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (!apiKey.trim()) {
|
|
122
|
+
console.log(pc.yellow('\n API key cannot be empty. Please try again.\n'));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Verify key against cloud
|
|
127
|
+
console.log(pc.dim('\n Verifying...'));
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch(`${cloudUrl}/api/verify`, {
|
|
130
|
+
headers: { Authorization: `Bearer ${apiKey.trim()}` },
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
console.log(pc.red(`\n ✗ Invalid API key (HTTP ${response.status}). Please try again.\n`));
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const data = (await response.json()) as { valid: boolean; orgName?: string };
|
|
139
|
+
if (!data.valid) {
|
|
140
|
+
console.log(pc.red('\n ✗ API key not recognized. Please try again.\n'));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
orgName = data.orgName ?? 'your organization';
|
|
145
|
+
console.log(pc.green(`\n ✓ Connected to "${orgName}" (verified)\n`));
|
|
146
|
+
break;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.log(
|
|
149
|
+
pc.red('\n ✗ Could not reach the dashboard.'),
|
|
150
|
+
);
|
|
151
|
+
console.log(
|
|
152
|
+
pc.dim(` ${error instanceof Error ? error.message : 'Network error'}\n`),
|
|
153
|
+
);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── Step 2: Save configuration ──────────────────────────────────────────
|
|
159
|
+
console.log(pc.bold(' Step 2 of 3: Save configuration\n'));
|
|
160
|
+
|
|
161
|
+
const saveToEnv = await confirm({
|
|
162
|
+
message: 'Save API key to .env file?',
|
|
163
|
+
default: true,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (saveToEnv) {
|
|
167
|
+
const envPath = resolve('.env');
|
|
168
|
+
const envEntry = `FRAGMENTS_API_KEY=${apiKey.trim()}`;
|
|
169
|
+
|
|
170
|
+
if (existsSync(envPath)) {
|
|
171
|
+
const envContent = await readFile(envPath, 'utf-8');
|
|
172
|
+
if (envContent.includes('FRAGMENTS_API_KEY=')) {
|
|
173
|
+
// Replace existing entry
|
|
174
|
+
const updated = envContent.replace(
|
|
175
|
+
/^FRAGMENTS_API_KEY=.*$/m,
|
|
176
|
+
envEntry,
|
|
177
|
+
);
|
|
178
|
+
await writeFile(envPath, updated, 'utf-8');
|
|
179
|
+
console.log(pc.green(' ✓ Updated FRAGMENTS_API_KEY in .env'));
|
|
180
|
+
} else {
|
|
181
|
+
await appendFile(envPath, `\n${envEntry}\n`, 'utf-8');
|
|
182
|
+
console.log(pc.green(' ✓ Added FRAGMENTS_API_KEY to .env'));
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
await writeFile(envPath, `${envEntry}\n`, 'utf-8');
|
|
186
|
+
console.log(pc.green(' ✓ Created .env with FRAGMENTS_API_KEY'));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Write FRAGMENTS_URL only if non-default
|
|
190
|
+
if (cloudUrl !== 'https://app.usefragments.com') {
|
|
191
|
+
const envContent = await readFile(envPath, 'utf-8');
|
|
192
|
+
if (!envContent.includes('FRAGMENTS_URL=')) {
|
|
193
|
+
await appendFile(envPath, `FRAGMENTS_URL=${cloudUrl}\n`, 'utf-8');
|
|
194
|
+
console.log(pc.green(` ✓ Added FRAGMENTS_URL to .env`));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Ensure .env is in .gitignore
|
|
199
|
+
const gitignorePath = resolve('.gitignore');
|
|
200
|
+
if (existsSync(gitignorePath)) {
|
|
201
|
+
const gitignore = await readFile(gitignorePath, 'utf-8');
|
|
202
|
+
if (!gitignore.split('\n').some((line) => line.trim() === '.env')) {
|
|
203
|
+
await appendFile(gitignorePath, '\n.env\n', 'utf-8');
|
|
204
|
+
console.log(pc.green(' ✓ Added .env to .gitignore'));
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
await writeFile(gitignorePath, '.env\n', 'utf-8');
|
|
208
|
+
console.log(pc.green(' ✓ Created .gitignore with .env entry'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Step 3: Config check ────────────────────────────────────────────────
|
|
213
|
+
console.log(pc.bold('\n Step 3 of 3: Config check\n'));
|
|
214
|
+
|
|
215
|
+
const { findGovernConfig } = await import('@fragments-sdk/govern');
|
|
216
|
+
const configPath = findGovernConfig();
|
|
217
|
+
|
|
218
|
+
if (configPath) {
|
|
219
|
+
console.log(pc.green(` ✓ Found govern config: ${configPath}`));
|
|
220
|
+
} else {
|
|
221
|
+
console.log(
|
|
222
|
+
pc.yellow(' No govern config found — run `fragments govern init` to create one'),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── Done ────────────────────────────────────────────────────────────────
|
|
227
|
+
console.log(pc.dim('\n ─────────────────────────────────────\n'));
|
|
228
|
+
console.log(pc.green(' ✓ All set!') + ' Run `fragments govern check` to send your first audit.\n');
|
|
229
|
+
console.log(pc.dim(` Dashboard: ${cloudUrl}/dashboard\n`));
|
|
230
|
+
}
|
|
231
|
+
|
|
75
232
|
// ---------------------------------------------------------------------------
|
|
76
233
|
// report
|
|
77
234
|
// ---------------------------------------------------------------------------
|