@cmssy/cli 0.22.4 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +435 -0
- package/package.json +25 -86
- package/LICENSE +0 -21
- package/README.md +0 -692
- package/config.d.ts +0 -3
- package/config.js +0 -4
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -340
- package/dist/cli.js.map +0 -1
- package/dist/commands/add-source.d.ts +0 -13
- package/dist/commands/add-source.d.ts.map +0 -1
- package/dist/commands/add-source.js +0 -273
- package/dist/commands/add-source.js.map +0 -1
- package/dist/commands/build.d.ts +0 -7
- package/dist/commands/build.d.ts.map +0 -1
- package/dist/commands/build.js +0 -127
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/codegen.d.ts +0 -7
- package/dist/commands/codegen.d.ts.map +0 -1
- package/dist/commands/codegen.js +0 -150
- package/dist/commands/codegen.js.map +0 -1
- package/dist/commands/configure.d.ts +0 -6
- package/dist/commands/configure.d.ts.map +0 -1
- package/dist/commands/configure.js +0 -39
- package/dist/commands/configure.js.map +0 -1
- package/dist/commands/create.d.ts +0 -18
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -403
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/dev.d.ts +0 -6
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -144
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -230
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.d.ts +0 -6
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -414
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/lib.d.ts +0 -17
- package/dist/commands/lib.d.ts.map +0 -1
- package/dist/commands/lib.js +0 -132
- package/dist/commands/lib.js.map +0 -1
- package/dist/commands/link.d.ts +0 -8
- package/dist/commands/link.d.ts.map +0 -1
- package/dist/commands/link.js +0 -116
- package/dist/commands/link.js.map +0 -1
- package/dist/commands/migrate.d.ts +0 -2
- package/dist/commands/migrate.d.ts.map +0 -1
- package/dist/commands/migrate.js +0 -231
- package/dist/commands/migrate.js.map +0 -1
- package/dist/commands/package.d.ts +0 -14
- package/dist/commands/package.d.ts.map +0 -1
- package/dist/commands/package.js +0 -137
- package/dist/commands/package.js.map +0 -1
- package/dist/commands/publish-block-buildtime.d.ts +0 -8
- package/dist/commands/publish-block-buildtime.d.ts.map +0 -1
- package/dist/commands/publish-block-buildtime.js +0 -290
- package/dist/commands/publish-block-buildtime.js.map +0 -1
- package/dist/commands/publish-template.d.ts +0 -22
- package/dist/commands/publish-template.d.ts.map +0 -1
- package/dist/commands/publish-template.js +0 -345
- package/dist/commands/publish-template.js.map +0 -1
- package/dist/commands/publish.d.ts +0 -15
- package/dist/commands/publish.d.ts.map +0 -1
- package/dist/commands/publish.js +0 -1003
- package/dist/commands/publish.js.map +0 -1
- package/dist/commands/skills.d.ts +0 -11
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js +0 -178
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/sync.d.ts +0 -6
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js +0 -208
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/test.d.ts +0 -8
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js +0 -60
- package/dist/commands/test.js.map +0 -1
- package/dist/commands/upload.d.ts +0 -11
- package/dist/commands/upload.d.ts.map +0 -1
- package/dist/commands/upload.js +0 -128
- package/dist/commands/upload.js.map +0 -1
- package/dist/commands/workspaces.d.ts +0 -2
- package/dist/commands/workspaces.d.ts.map +0 -1
- package/dist/commands/workspaces.js +0 -62
- package/dist/commands/workspaces.js.map +0 -1
- package/dist/dev-ui/app.js +0 -1284
- package/dist/dev-ui/index.html +0 -1511
- package/dist/dev-ui-react/App.tsx +0 -164
- package/dist/dev-ui-react/__tests__/previewData.test.ts +0 -193
- package/dist/dev-ui-react/components/BlocksList.tsx +0 -232
- package/dist/dev-ui-react/components/Editor.tsx +0 -469
- package/dist/dev-ui-react/components/Preview.tsx +0 -146
- package/dist/dev-ui-react/hooks/useBlocks.ts +0 -80
- package/dist/dev-ui-react/index.html +0 -13
- package/dist/dev-ui-react/main.tsx +0 -8
- package/dist/dev-ui-react/styles.css +0 -856
- package/dist/dev-ui-react/types.ts +0 -45
- package/dist/skills/claude/cmssy-block.md +0 -454
- package/dist/skills/claude/cmssy-mcp-content.md +0 -235
- package/dist/test-helpers/index.d.ts +0 -23
- package/dist/test-helpers/index.d.ts.map +0 -1
- package/dist/test-helpers/index.js +0 -50
- package/dist/test-helpers/index.js.map +0 -1
- package/dist/types/block-config.d.ts +0 -6
- package/dist/types/block-config.d.ts.map +0 -1
- package/dist/types/block-config.js +0 -12
- package/dist/types/block-config.js.map +0 -1
- package/dist/types/publish-job.d.ts +0 -61
- package/dist/types/publish-job.d.ts.map +0 -1
- package/dist/types/publish-job.js +0 -13
- package/dist/types/publish-job.js.map +0 -1
- package/dist/utils/api-error.d.ts +0 -10
- package/dist/utils/api-error.d.ts.map +0 -1
- package/dist/utils/api-error.js +0 -55
- package/dist/utils/api-error.js.map +0 -1
- package/dist/utils/block-config.d.ts +0 -87
- package/dist/utils/block-config.d.ts.map +0 -1
- package/dist/utils/block-config.js +0 -214
- package/dist/utils/block-config.js.map +0 -1
- package/dist/utils/blocks-meta-cache.d.ts +0 -28
- package/dist/utils/blocks-meta-cache.d.ts.map +0 -1
- package/dist/utils/blocks-meta-cache.js +0 -72
- package/dist/utils/blocks-meta-cache.js.map +0 -1
- package/dist/utils/builder.d.ts +0 -34
- package/dist/utils/builder.d.ts.map +0 -1
- package/dist/utils/builder.js +0 -140
- package/dist/utils/builder.js.map +0 -1
- package/dist/utils/cmssy-config.d.ts +0 -16
- package/dist/utils/cmssy-config.d.ts.map +0 -1
- package/dist/utils/cmssy-config.js +0 -19
- package/dist/utils/cmssy-config.js.map +0 -1
- package/dist/utils/config.d.ts +0 -9
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js +0 -51
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/dependency-check.d.ts +0 -21
- package/dist/utils/dependency-check.d.ts.map +0 -1
- package/dist/utils/dependency-check.js +0 -56
- package/dist/utils/dependency-check.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/blocks.d.ts +0 -3
- package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/blocks.js +0 -194
- package/dist/utils/dev-generator/api-routes/blocks.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/config.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/config.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/config.js +0 -107
- package/dist/utils/dev-generator/api-routes/config.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/preview.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/preview.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/preview.js +0 -50
- package/dist/utils/dev-generator/api-routes/preview.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/workspaces.js +0 -128
- package/dist/utils/dev-generator/api-routes/workspaces.js.map +0 -1
- package/dist/utils/dev-generator/helpers.d.ts +0 -23
- package/dist/utils/dev-generator/helpers.d.ts.map +0 -1
- package/dist/utils/dev-generator/helpers.js +0 -128
- package/dist/utils/dev-generator/helpers.js.map +0 -1
- package/dist/utils/dev-generator/home-page.d.ts +0 -2
- package/dist/utils/dev-generator/home-page.d.ts.map +0 -1
- package/dist/utils/dev-generator/home-page.js +0 -643
- package/dist/utils/dev-generator/home-page.js.map +0 -1
- package/dist/utils/dev-generator/index.d.ts +0 -11
- package/dist/utils/dev-generator/index.d.ts.map +0 -1
- package/dist/utils/dev-generator/index.js +0 -85
- package/dist/utils/dev-generator/index.js.map +0 -1
- package/dist/utils/dev-generator/layout.d.ts +0 -3
- package/dist/utils/dev-generator/layout.d.ts.map +0 -1
- package/dist/utils/dev-generator/layout.js +0 -63
- package/dist/utils/dev-generator/layout.js.map +0 -1
- package/dist/utils/dev-generator/next-config.d.ts +0 -3
- package/dist/utils/dev-generator/next-config.d.ts.map +0 -1
- package/dist/utils/dev-generator/next-config.js +0 -111
- package/dist/utils/dev-generator/next-config.js.map +0 -1
- package/dist/utils/dev-generator/preview-pages.d.ts +0 -3
- package/dist/utils/dev-generator/preview-pages.d.ts.map +0 -1
- package/dist/utils/dev-generator/preview-pages.js +0 -833
- package/dist/utils/dev-generator/preview-pages.js.map +0 -1
- package/dist/utils/dev-generator.d.ts +0 -12
- package/dist/utils/dev-generator.d.ts.map +0 -1
- package/dist/utils/dev-generator.js +0 -1752
- package/dist/utils/dev-generator.js.map +0 -1
- package/dist/utils/field-schema.d.ts +0 -13
- package/dist/utils/field-schema.d.ts.map +0 -1
- package/dist/utils/field-schema.js +0 -182
- package/dist/utils/field-schema.js.map +0 -1
- package/dist/utils/graphql.d.ts +0 -39
- package/dist/utils/graphql.d.ts.map +0 -1
- package/dist/utils/graphql.js +0 -138
- package/dist/utils/graphql.js.map +0 -1
- package/dist/utils/lib-manifest.d.ts +0 -7
- package/dist/utils/lib-manifest.d.ts.map +0 -1
- package/dist/utils/lib-manifest.js +0 -59
- package/dist/utils/lib-manifest.js.map +0 -1
- package/dist/utils/publish-helpers.d.ts +0 -43
- package/dist/utils/publish-helpers.d.ts.map +0 -1
- package/dist/utils/publish-helpers.js +0 -215
- package/dist/utils/publish-helpers.js.map +0 -1
- package/dist/utils/publish-helpers.test.d.ts +0 -2
- package/dist/utils/publish-helpers.test.d.ts.map +0 -1
- package/dist/utils/publish-helpers.test.js +0 -24
- package/dist/utils/publish-helpers.test.js.map +0 -1
- package/dist/utils/resolve-workspace.d.ts +0 -6
- package/dist/utils/resolve-workspace.d.ts.map +0 -1
- package/dist/utils/resolve-workspace.js +0 -32
- package/dist/utils/resolve-workspace.js.map +0 -1
- package/dist/utils/scanner.d.ts +0 -42
- package/dist/utils/scanner.d.ts.map +0 -1
- package/dist/utils/scanner.js +0 -156
- package/dist/utils/scanner.js.map +0 -1
- package/dist/utils/schema-diff.d.ts +0 -22
- package/dist/utils/schema-diff.d.ts.map +0 -1
- package/dist/utils/schema-diff.js +0 -107
- package/dist/utils/schema-diff.js.map +0 -1
- package/dist/utils/source-collector.d.ts +0 -19
- package/dist/utils/source-collector.d.ts.map +0 -1
- package/dist/utils/source-collector.js +0 -565
- package/dist/utils/source-collector.js.map +0 -1
- package/dist/utils/theme-builder.d.ts +0 -12
- package/dist/utils/theme-builder.d.ts.map +0 -1
- package/dist/utils/theme-builder.js +0 -117
- package/dist/utils/theme-builder.js.map +0 -1
- package/dist/utils/type-generator.d.ts +0 -9
- package/dist/utils/type-generator.d.ts.map +0 -1
- package/dist/utils/type-generator.js +0 -86
- package/dist/utils/type-generator.js.map +0 -1
- package/dist/utils/version.d.ts +0 -10
- package/dist/utils/version.d.ts.map +0 -1
- package/dist/utils/version.js +0 -30
- package/dist/utils/version.js.map +0 -1
- package/dist/utils/versions.d.ts.map +0 -1
- package/dist/utils/versions.js.map +0 -1
- package/src/dev-app/app/api/blocks/[name]/config/route.ts +0 -140
- package/src/dev-app/app/api/blocks/route.ts +0 -61
- package/src/dev-app/app/api/config/route.ts +0 -98
- package/src/dev-app/app/api/context/route.ts +0 -126
- package/src/dev-app/app/api/preview/[name]/route.ts +0 -137
- package/src/dev-app/app/api/workspaces/route.ts +0 -124
- package/src/dev-app/app/layout.tsx +0 -18
- package/src/dev-app/app/page.tsx +0 -1785
- package/test.d.ts +0 -1
- package/test.js +0 -2
package/dist/index.js
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { createInterface } from "readline/promises";
|
|
5
|
+
|
|
6
|
+
// src/link.ts
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import {
|
|
10
|
+
buildEditorUrl,
|
|
11
|
+
checkDraftSecret,
|
|
12
|
+
checkWorkspaceReachable
|
|
13
|
+
} from "@cmssy/core/preflight";
|
|
14
|
+
|
|
15
|
+
// src/admin-client.ts
|
|
16
|
+
var DEFAULT_ADMIN_API_URL = "https://api.cmssy.io/graphql";
|
|
17
|
+
var BAD_TOKEN_HINT = {
|
|
18
|
+
message: "the cmssy API rejected the token",
|
|
19
|
+
fix: "create an API token in the cmssy dashboard (Settings \u2192 API Tokens) and pass it via --token or CMSSY_API_TOKEN"
|
|
20
|
+
};
|
|
21
|
+
var NEEDS_PAGES_EDIT_HINT = {
|
|
22
|
+
message: "the token's user cannot read this workspace's settings",
|
|
23
|
+
fix: "the token's user needs the PAGES_EDIT permission in the selected workspace"
|
|
24
|
+
};
|
|
25
|
+
var CliError = class extends Error {
|
|
26
|
+
fix;
|
|
27
|
+
constructor(message, fix) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "CliError";
|
|
30
|
+
this.fix = fix;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function isDenied(errors, status) {
|
|
34
|
+
return status === 401 || status === 403 || errors.some(
|
|
35
|
+
(error) => error.extensions?.code === "UNAUTHENTICATED" || error.extensions?.code === "FORBIDDEN" || /unauthenticated|unauthorized|not authorized|not authenticated|invalid token|permission|forbidden|not allowed|access denied/i.test(
|
|
36
|
+
error.message ?? ""
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
async function adminGraphql(query, variables, options) {
|
|
41
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
42
|
+
const url = options.apiUrl?.trim() || DEFAULT_ADMIN_API_URL;
|
|
43
|
+
let response;
|
|
44
|
+
try {
|
|
45
|
+
response = await doFetch(url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
authorization: `Bearer ${options.token}`,
|
|
50
|
+
...options.workspaceId ? { "x-workspace-id": options.workspaceId } : {}
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({ query, variables })
|
|
53
|
+
});
|
|
54
|
+
} catch {
|
|
55
|
+
throw new CliError(
|
|
56
|
+
`cannot reach the cmssy API at ${url}`,
|
|
57
|
+
"check your network connection and CMSSY_API_URL (leave it unset for cmssy cloud)"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
let envelope = null;
|
|
61
|
+
try {
|
|
62
|
+
envelope = await response.json();
|
|
63
|
+
} catch {
|
|
64
|
+
envelope = null;
|
|
65
|
+
}
|
|
66
|
+
const errors = Array.isArray(envelope?.errors) ? envelope.errors : [];
|
|
67
|
+
if (isDenied(errors, response.status)) {
|
|
68
|
+
const denied = options.denied ?? BAD_TOKEN_HINT;
|
|
69
|
+
throw new CliError(denied.message, denied.fix);
|
|
70
|
+
}
|
|
71
|
+
if (errors.length > 0) {
|
|
72
|
+
throw new CliError(
|
|
73
|
+
`the cmssy API rejected the request - ${errors.map((error) => error.message ?? "GraphQL error").join("; ")}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (!response.ok || envelope?.data === void 0) {
|
|
77
|
+
throw new CliError(`the cmssy API responded with HTTP ${response.status}`);
|
|
78
|
+
}
|
|
79
|
+
return envelope.data;
|
|
80
|
+
}
|
|
81
|
+
var WORKSPACES_MINE_QUERY = `query CliWorkspacesMine {
|
|
82
|
+
workspace {
|
|
83
|
+
mine {
|
|
84
|
+
id
|
|
85
|
+
slug
|
|
86
|
+
name
|
|
87
|
+
organizationSlug
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}`;
|
|
91
|
+
var DRAFT_SECRET_QUERY = `query CliDraftSecret {
|
|
92
|
+
workspace {
|
|
93
|
+
draftSecret
|
|
94
|
+
}
|
|
95
|
+
}`;
|
|
96
|
+
var UPDATE_PREVIEW_URL_MUTATION = `mutation CliSetPreviewUrl($input: UpdateSiteConfigInput!) {
|
|
97
|
+
siteConfig {
|
|
98
|
+
update(input: $input) {
|
|
99
|
+
previewUrl
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}`;
|
|
103
|
+
async function fetchMyWorkspaces(options) {
|
|
104
|
+
const data = await adminGraphql(WORKSPACES_MINE_QUERY, {}, options);
|
|
105
|
+
return data.workspace.mine;
|
|
106
|
+
}
|
|
107
|
+
async function fetchDraftSecret(options) {
|
|
108
|
+
const data = await adminGraphql(
|
|
109
|
+
DRAFT_SECRET_QUERY,
|
|
110
|
+
{},
|
|
111
|
+
{ ...options, denied: NEEDS_PAGES_EDIT_HINT }
|
|
112
|
+
);
|
|
113
|
+
return data.workspace.draftSecret;
|
|
114
|
+
}
|
|
115
|
+
async function setPreviewUrl(previewUrl, options) {
|
|
116
|
+
await adminGraphql(
|
|
117
|
+
UPDATE_PREVIEW_URL_MUTATION,
|
|
118
|
+
{ input: { previewUrl } },
|
|
119
|
+
{ ...options, denied: NEEDS_PAGES_EDIT_HINT }
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/env-file.ts
|
|
124
|
+
function parseEnvFile(content) {
|
|
125
|
+
const vars = {};
|
|
126
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
127
|
+
const line = rawLine.trim();
|
|
128
|
+
if (!line || line.startsWith("#")) continue;
|
|
129
|
+
const withoutExport = line.replace(/^export\s+/, "");
|
|
130
|
+
const separator = withoutExport.indexOf("=");
|
|
131
|
+
if (separator <= 0) continue;
|
|
132
|
+
const key = withoutExport.slice(0, separator).trim();
|
|
133
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
134
|
+
let value = withoutExport.slice(separator + 1).trim();
|
|
135
|
+
const quoted = value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"));
|
|
136
|
+
if (quoted) {
|
|
137
|
+
value = value.slice(1, -1);
|
|
138
|
+
} else {
|
|
139
|
+
const comment = value.indexOf(" #");
|
|
140
|
+
if (comment !== -1) value = value.slice(0, comment).trim();
|
|
141
|
+
}
|
|
142
|
+
vars[key] = value;
|
|
143
|
+
}
|
|
144
|
+
return vars;
|
|
145
|
+
}
|
|
146
|
+
function applyEnv(vars, env) {
|
|
147
|
+
const applied = [];
|
|
148
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
149
|
+
if (env[key] !== void 0) continue;
|
|
150
|
+
env[key] = value;
|
|
151
|
+
applied.push(key);
|
|
152
|
+
}
|
|
153
|
+
return applied;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/env-write.ts
|
|
157
|
+
function needsQuoting(value) {
|
|
158
|
+
return /[\s#"']/.test(value) || value === "";
|
|
159
|
+
}
|
|
160
|
+
function serialize(key, value) {
|
|
161
|
+
return needsQuoting(value) ? `${key}="${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : `${key}=${value}`;
|
|
162
|
+
}
|
|
163
|
+
function mergeEnvContent(existing, updates) {
|
|
164
|
+
const pending = new Map(Object.entries(updates));
|
|
165
|
+
const lines = existing ? existing.split(/\r?\n/) : [];
|
|
166
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
167
|
+
const merged = lines.map((line) => {
|
|
168
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
|
|
169
|
+
const key = match?.[1];
|
|
170
|
+
if (!key || !pending.has(key)) return line;
|
|
171
|
+
const value = pending.get(key);
|
|
172
|
+
pending.delete(key);
|
|
173
|
+
return serialize(key, value);
|
|
174
|
+
});
|
|
175
|
+
for (const [key, value] of pending) {
|
|
176
|
+
merged.push(serialize(key, value));
|
|
177
|
+
}
|
|
178
|
+
return `${merged.join("\n")}
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/format.ts
|
|
183
|
+
var GREEN = "\x1B[32m";
|
|
184
|
+
var RED = "\x1B[31m";
|
|
185
|
+
var YELLOW = "\x1B[33m";
|
|
186
|
+
var CYAN = "\x1B[36m";
|
|
187
|
+
var RESET = "\x1B[0m";
|
|
188
|
+
function useColor(env = process.env, isTty = Boolean(process.stdout.isTTY)) {
|
|
189
|
+
return isTty && env.NO_COLOR === void 0;
|
|
190
|
+
}
|
|
191
|
+
function paint(text, color, colored) {
|
|
192
|
+
return colored ? `${color}${text}${RESET}` : text;
|
|
193
|
+
}
|
|
194
|
+
function formatResult(result, colored = useColor()) {
|
|
195
|
+
if (result.status === "ok") {
|
|
196
|
+
return `${paint("\u2713", GREEN, colored)} ${result.message}`;
|
|
197
|
+
}
|
|
198
|
+
if (result.status === "unknown") {
|
|
199
|
+
return `${paint("?", YELLOW, colored)} ${result.message}`;
|
|
200
|
+
}
|
|
201
|
+
const fix = result.fix ? `
|
|
202
|
+
fix: ${result.fix}` : "";
|
|
203
|
+
return `${paint("\u2717", RED, colored)} ${result.message}${fix}`;
|
|
204
|
+
}
|
|
205
|
+
function formatEditorLink(url, colored = useColor()) {
|
|
206
|
+
return `
|
|
207
|
+
Edit this site visually:
|
|
208
|
+
${paint(url, CYAN, colored)}
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/link.ts
|
|
213
|
+
var ENV_FILES = [".env.local", ".env"];
|
|
214
|
+
var DEFAULT_PREVIEW_URL = "http://localhost:3000";
|
|
215
|
+
function loadEnvFiles(cwd, env) {
|
|
216
|
+
for (const file of ENV_FILES) {
|
|
217
|
+
const path = join(cwd, file);
|
|
218
|
+
if (!existsSync(path)) continue;
|
|
219
|
+
applyEnv(parseEnvFile(readFileSync(path, "utf8")), env);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function resolveToken(options, deps) {
|
|
223
|
+
const token = options.token?.trim() || deps.env.CMSSY_API_TOKEN?.trim();
|
|
224
|
+
if (!token) {
|
|
225
|
+
throw new CliError(
|
|
226
|
+
"no API token given",
|
|
227
|
+
"create an API token in the cmssy dashboard (Settings \u2192 API Tokens), then run cmssy link --token cs_... or set CMSSY_API_TOKEN"
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
return token;
|
|
231
|
+
}
|
|
232
|
+
function describeWorkspace(workspace) {
|
|
233
|
+
const org = workspace.organizationSlug ?? "?";
|
|
234
|
+
return `${workspace.name} (${org}/${workspace.slug})`;
|
|
235
|
+
}
|
|
236
|
+
async function selectWorkspace(workspaces, options, deps) {
|
|
237
|
+
if (workspaces.length === 0) {
|
|
238
|
+
throw new CliError(
|
|
239
|
+
"the token's user has no workspaces",
|
|
240
|
+
"create a workspace in the cmssy dashboard, or use a token from a user who is a member of one"
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
const slugs = workspaces.map((workspace) => `${workspace.organizationSlug}/${workspace.slug}`).join(", ");
|
|
244
|
+
if (options.workspace) {
|
|
245
|
+
const wanted = options.workspace.trim();
|
|
246
|
+
const match = workspaces.find(
|
|
247
|
+
(workspace) => workspace.slug === wanted || `${workspace.organizationSlug}/${workspace.slug}` === wanted
|
|
248
|
+
);
|
|
249
|
+
if (!match) {
|
|
250
|
+
throw new CliError(
|
|
251
|
+
`no workspace named "${wanted}"`,
|
|
252
|
+
`pass one of: ${slugs}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return match;
|
|
256
|
+
}
|
|
257
|
+
if (workspaces.length === 1) {
|
|
258
|
+
return workspaces[0];
|
|
259
|
+
}
|
|
260
|
+
if (!deps.isTty) {
|
|
261
|
+
throw new CliError(
|
|
262
|
+
"several workspaces are available and there is no terminal to ask in",
|
|
263
|
+
`pass --workspace <slug> - one of: ${slugs}`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
deps.log("Which workspace should this app be linked to?");
|
|
267
|
+
workspaces.forEach((workspace, index) => {
|
|
268
|
+
deps.log(` ${index + 1}. ${describeWorkspace(workspace)}`);
|
|
269
|
+
});
|
|
270
|
+
const answer = await deps.ask(`Workspace [1-${workspaces.length}]: `);
|
|
271
|
+
const choice = Number(answer.trim());
|
|
272
|
+
const selected = Number.isInteger(choice) && choice >= 1 && choice <= workspaces.length ? workspaces[choice - 1] : void 0;
|
|
273
|
+
if (!selected) {
|
|
274
|
+
throw new CliError(
|
|
275
|
+
`"${answer.trim()}" is not a number between 1 and ${workspaces.length}`,
|
|
276
|
+
"run cmssy link again, or pass --workspace <slug>"
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
return selected;
|
|
280
|
+
}
|
|
281
|
+
async function resolvePreviewUrl(options, deps) {
|
|
282
|
+
if (options.previewUrl) {
|
|
283
|
+
try {
|
|
284
|
+
return new URL(options.previewUrl).origin;
|
|
285
|
+
} catch {
|
|
286
|
+
throw new CliError(
|
|
287
|
+
`"${options.previewUrl}" is not a valid URL`,
|
|
288
|
+
"pass --preview-url with a full origin, e.g. --preview-url http://localhost:3000"
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!deps.isTty) return null;
|
|
293
|
+
const answer = await deps.ask(
|
|
294
|
+
`Set the workspace preview URL to ${DEFAULT_PREVIEW_URL}? (Y/n) `
|
|
295
|
+
);
|
|
296
|
+
return /^(y(es)?)?$/i.test(answer.trim()) ? DEFAULT_PREVIEW_URL : null;
|
|
297
|
+
}
|
|
298
|
+
function writeEnvLocal(cwd, updates) {
|
|
299
|
+
const path = join(cwd, ".env.local");
|
|
300
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
301
|
+
writeFileSync(path, mergeEnvContent(existing, updates));
|
|
302
|
+
}
|
|
303
|
+
async function runLink(options, deps) {
|
|
304
|
+
const { cwd, env, log } = deps;
|
|
305
|
+
try {
|
|
306
|
+
loadEnvFiles(cwd, env);
|
|
307
|
+
const token = resolveToken(options, deps);
|
|
308
|
+
const admin = {
|
|
309
|
+
token,
|
|
310
|
+
apiUrl: env.CMSSY_API_URL,
|
|
311
|
+
fetch: deps.fetch
|
|
312
|
+
};
|
|
313
|
+
const workspaces = await fetchMyWorkspaces(admin);
|
|
314
|
+
const workspace = await selectWorkspace(workspaces, options, deps);
|
|
315
|
+
const orgSlug = workspace.organizationSlug;
|
|
316
|
+
if (!orgSlug) {
|
|
317
|
+
throw new CliError(
|
|
318
|
+
`workspace ${workspace.slug} has no organization slug`,
|
|
319
|
+
"open the workspace in the cmssy dashboard once, then run cmssy link again"
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
log(
|
|
323
|
+
formatResult({
|
|
324
|
+
status: "ok",
|
|
325
|
+
message: `linking to ${describeWorkspace(workspace)}`
|
|
326
|
+
})
|
|
327
|
+
);
|
|
328
|
+
const draftSecret = await fetchDraftSecret({
|
|
329
|
+
...admin,
|
|
330
|
+
workspaceId: workspace.id
|
|
331
|
+
});
|
|
332
|
+
log(formatResult({ status: "ok", message: "fetched the draft secret" }));
|
|
333
|
+
const previewUrl = await resolvePreviewUrl(options, deps);
|
|
334
|
+
if (previewUrl) {
|
|
335
|
+
await setPreviewUrl(previewUrl, { ...admin, workspaceId: workspace.id });
|
|
336
|
+
log(
|
|
337
|
+
formatResult({
|
|
338
|
+
status: "ok",
|
|
339
|
+
message: `set the workspace preview URL to ${previewUrl}`
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
} else {
|
|
343
|
+
log(
|
|
344
|
+
formatResult({
|
|
345
|
+
status: "unknown",
|
|
346
|
+
message: `preview URL left unchanged - pass --preview-url to set it`
|
|
347
|
+
})
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
writeEnvLocal(cwd, {
|
|
351
|
+
CMSSY_ORG_SLUG: orgSlug,
|
|
352
|
+
CMSSY_WORKSPACE_SLUG: workspace.slug,
|
|
353
|
+
CMSSY_DRAFT_SECRET: draftSecret
|
|
354
|
+
});
|
|
355
|
+
log(
|
|
356
|
+
formatResult({
|
|
357
|
+
status: "ok",
|
|
358
|
+
message: "wrote CMSSY_ORG_SLUG, CMSSY_WORKSPACE_SLUG and CMSSY_DRAFT_SECRET to .env.local"
|
|
359
|
+
})
|
|
360
|
+
);
|
|
361
|
+
const preflight = {
|
|
362
|
+
apiUrl: env.CMSSY_API_URL,
|
|
363
|
+
org: orgSlug,
|
|
364
|
+
workspaceSlug: workspace.slug,
|
|
365
|
+
draftSecret,
|
|
366
|
+
fetch: deps.fetch
|
|
367
|
+
};
|
|
368
|
+
const reachable = await checkWorkspaceReachable(preflight);
|
|
369
|
+
log(formatResult(reachable));
|
|
370
|
+
const secretResult = await checkDraftSecret(preflight);
|
|
371
|
+
log(formatResult(secretResult));
|
|
372
|
+
log(formatEditorLink(buildEditorUrl(preflight)));
|
|
373
|
+
return reachable.status === "fail" || secretResult.status === "fail" ? 1 : 0;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof CliError) {
|
|
376
|
+
log(
|
|
377
|
+
formatResult({
|
|
378
|
+
status: "fail",
|
|
379
|
+
message: error.message,
|
|
380
|
+
fix: error.fix
|
|
381
|
+
})
|
|
382
|
+
);
|
|
383
|
+
return 1;
|
|
384
|
+
}
|
|
385
|
+
log(
|
|
386
|
+
formatResult({
|
|
387
|
+
status: "fail",
|
|
388
|
+
message: error instanceof Error ? error.message : String(error)
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
return 1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/index.ts
|
|
396
|
+
var USAGE = "usage: cmssy link [--token <cs_...>] [--workspace <slug>] [--preview-url <url>]";
|
|
397
|
+
function flagValue(args, name) {
|
|
398
|
+
const index = args.findIndex((arg) => arg === name);
|
|
399
|
+
if (index !== -1) return args[index + 1];
|
|
400
|
+
return args.find((arg) => arg.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
401
|
+
}
|
|
402
|
+
async function main() {
|
|
403
|
+
const [command, ...args] = process.argv.slice(2);
|
|
404
|
+
if (command !== "link") {
|
|
405
|
+
console.error(USAGE);
|
|
406
|
+
process.exitCode = command === void 0 || command === "--help" ? 0 : 1;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const rl = process.stdin.isTTY ? createInterface({ input: process.stdin, output: process.stdout }) : null;
|
|
410
|
+
try {
|
|
411
|
+
process.exitCode = await runLink(
|
|
412
|
+
{
|
|
413
|
+
token: flagValue(args, "--token"),
|
|
414
|
+
workspace: flagValue(args, "--workspace"),
|
|
415
|
+
previewUrl: flagValue(args, "--preview-url")
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
cwd: process.cwd(),
|
|
419
|
+
env: process.env,
|
|
420
|
+
log: (line) => console.log(line),
|
|
421
|
+
fetch: globalThis.fetch,
|
|
422
|
+
isTty: rl !== null,
|
|
423
|
+
ask: (question) => rl ? rl.question(question) : Promise.resolve("")
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
} finally {
|
|
427
|
+
rl?.close();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
main().catch((error) => {
|
|
431
|
+
console.error(
|
|
432
|
+
`cmssy: ${error instanceof Error ? error.message : String(error)}`
|
|
433
|
+
);
|
|
434
|
+
process.exitCode = 1;
|
|
435
|
+
});
|
package/package.json
CHANGED
|
@@ -1,102 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"cmssy": "./dist/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"exports": {
|
|
10
|
-
".": "./dist/cli.js",
|
|
11
|
-
"./config": "./config.js",
|
|
12
|
-
"./test": "./test.js"
|
|
13
|
-
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc && tsc config.ts --module esnext --target esnext --moduleResolution bundler && tsc config.ts --declaration --emitDeclarationOnly --module esnext --target esnext --moduleResolution bundler && node scripts/copy-assets.mjs",
|
|
16
|
-
"dev": "tsc --watch",
|
|
17
|
-
"test": "vitest run",
|
|
18
|
-
"test:watch": "vitest",
|
|
19
|
-
"test:coverage": "vitest run --coverage",
|
|
20
|
-
"prepare": "npm run build"
|
|
21
|
-
},
|
|
3
|
+
"version": "9.1.0",
|
|
4
|
+
"description": "One command to connect an app to a cmssy workspace: `cmssy link` writes .env.local, sets the preview URL and verifies the editor wiring.",
|
|
22
5
|
"keywords": [
|
|
23
6
|
"cmssy",
|
|
24
|
-
"design-library",
|
|
25
7
|
"cli",
|
|
26
|
-
"
|
|
8
|
+
"link"
|
|
27
9
|
],
|
|
28
|
-
"
|
|
29
|
-
"license": "MIT",
|
|
10
|
+
"homepage": "https://cmssy.com",
|
|
30
11
|
"repository": {
|
|
31
12
|
"type": "git",
|
|
32
|
-
"url": "https://github.com/cmssy-io/cmssy-
|
|
33
|
-
|
|
34
|
-
"homepage": "https://github.com/cmssy-io/cmssy-cli#readme",
|
|
35
|
-
"bugs": {
|
|
36
|
-
"url": "https://github.com/cmssy-io/cmssy-cli/issues"
|
|
37
|
-
},
|
|
38
|
-
"files": [
|
|
39
|
-
"dist",
|
|
40
|
-
"src/dev-app",
|
|
41
|
-
"config.js",
|
|
42
|
-
"config.d.ts",
|
|
43
|
-
"test.js",
|
|
44
|
-
"test.d.ts",
|
|
45
|
-
"README.md",
|
|
46
|
-
"LICENSE"
|
|
47
|
-
],
|
|
48
|
-
"engines": {
|
|
49
|
-
"node": ">=18.0.0"
|
|
13
|
+
"url": "git+https://github.com/cmssy-io/cmssy-sdk.git",
|
|
14
|
+
"directory": "packages/cli"
|
|
50
15
|
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"license": "MIT",
|
|
51
18
|
"publishConfig": {
|
|
52
19
|
"access": "public"
|
|
53
20
|
},
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"@tailwindcss/postcss": "^4.1.18",
|
|
57
|
-
"archiver": "^7.0.1",
|
|
58
|
-
"chalk": "^5.3.0",
|
|
59
|
-
"chokidar": "^4.0.1",
|
|
60
|
-
"commander": "^12.1.0",
|
|
61
|
-
"dotenv": "^16.4.7",
|
|
62
|
-
"esbuild": "^0.24.0",
|
|
63
|
-
"form-data": "^4.0.1",
|
|
64
|
-
"fs-extra": "^11.2.0",
|
|
65
|
-
"glob": "^11.0.0",
|
|
66
|
-
"graphql": "^16.9.0",
|
|
67
|
-
"graphql-request": "^7.1.2",
|
|
68
|
-
"inquirer": "^10.0.0",
|
|
69
|
-
"node-fetch": "^3.3.2",
|
|
70
|
-
"ora": "^8.0.1",
|
|
71
|
-
"postcss": "^8.4.49",
|
|
72
|
-
"postcss-import": "^16.1.1",
|
|
73
|
-
"semver": "^7.6.3",
|
|
74
|
-
"tsx": "^4.21.0"
|
|
75
|
-
},
|
|
76
|
-
"peerDependencies": {
|
|
77
|
-
"next": ">=15.0.0",
|
|
78
|
-
"react": ">=19.0.0",
|
|
79
|
-
"react-dom": ">=19.0.0"
|
|
21
|
+
"bin": {
|
|
22
|
+
"cmssy": "./dist/index.js"
|
|
80
23
|
},
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@cmssy/core": "9.1.0"
|
|
85
29
|
},
|
|
86
30
|
"devDependencies": {
|
|
87
|
-
"@
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"@types/react-dom": "^19",
|
|
97
|
-
"@types/semver": "^7.7.1",
|
|
98
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
99
|
-
"typescript": "^5.7.2",
|
|
100
|
-
"vitest": "^3.2.4"
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"tsup": "^8.3.0",
|
|
33
|
+
"typescript": "^5.6.0",
|
|
34
|
+
"vitest": "^2.1.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"typecheck": "tsc --noEmit"
|
|
101
40
|
}
|
|
102
|
-
}
|
|
41
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Cmssy Team
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|