@conceptcraft/mindframes 0.1.2 → 0.1.3
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 +1053 -453
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,84 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// src/lib/brand.ts
|
|
9
|
-
import path from "path";
|
|
10
|
-
var BRANDS = {
|
|
11
|
-
conceptcraft: {
|
|
12
|
-
id: "conceptcraft",
|
|
13
|
-
name: "conceptcraft",
|
|
14
|
-
displayName: "Conceptcraft",
|
|
15
|
-
description: "CLI tool for Conceptcraft presentation generation",
|
|
16
|
-
commands: ["cc", "conceptcraft"],
|
|
17
|
-
apiUrl: "https://conceptcraft.ai",
|
|
18
|
-
docsUrl: "https://docs.conceptcraft.ai"
|
|
19
|
-
},
|
|
20
|
-
mindframes: {
|
|
21
|
-
id: "mindframes",
|
|
22
|
-
name: "mindframes",
|
|
23
|
-
displayName: "Mindframes",
|
|
24
|
-
description: "CLI tool for Mindframes presentation generation",
|
|
25
|
-
commands: ["mf", "mindframes"],
|
|
26
|
-
apiUrl: "https://mindframes.app",
|
|
27
|
-
docsUrl: "https://docs.mindframes.app"
|
|
28
|
-
}
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
29
7
|
};
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
mf: "mindframes",
|
|
34
|
-
mindframes: "mindframes"
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
35
11
|
};
|
|
36
|
-
function detectBrand() {
|
|
37
|
-
const binaryPath = process.argv[1] || "";
|
|
38
|
-
const binaryName = path.basename(binaryPath).replace(/\.(js|ts)$/, "");
|
|
39
|
-
const brandId = COMMAND_TO_BRAND[binaryName];
|
|
40
|
-
if (brandId) {
|
|
41
|
-
return BRANDS[brandId];
|
|
42
|
-
}
|
|
43
|
-
for (const [cmd2, id] of Object.entries(COMMAND_TO_BRAND)) {
|
|
44
|
-
if (binaryPath.includes(`/${cmd2}`) || binaryPath.includes(`\\${cmd2}`)) {
|
|
45
|
-
return BRANDS[id];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return BRANDS.mindframes;
|
|
49
|
-
}
|
|
50
|
-
var brand = detectBrand();
|
|
51
|
-
|
|
52
|
-
// src/commands/config.ts
|
|
53
|
-
import { Command } from "commander";
|
|
54
|
-
import chalk3 from "chalk";
|
|
55
|
-
import { input, password, confirm, select } from "@inquirer/prompts";
|
|
56
|
-
import ora from "ora";
|
|
57
12
|
|
|
58
13
|
// src/lib/config.ts
|
|
59
14
|
import Conf from "conf";
|
|
60
|
-
var DEFAULT_API_URL = "https://www.mindframes.app";
|
|
61
|
-
var schema = {
|
|
62
|
-
apiKey: {
|
|
63
|
-
type: "string"
|
|
64
|
-
},
|
|
65
|
-
apiUrl: {
|
|
66
|
-
type: "string",
|
|
67
|
-
default: DEFAULT_API_URL
|
|
68
|
-
},
|
|
69
|
-
defaultTeamId: {
|
|
70
|
-
type: "string"
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
var config = new Conf({
|
|
74
|
-
projectName: "mindframes",
|
|
75
|
-
schema
|
|
76
|
-
});
|
|
77
15
|
function getConfig() {
|
|
78
16
|
return {
|
|
79
17
|
apiKey: getApiKey(),
|
|
80
18
|
apiUrl: getApiUrl(),
|
|
81
|
-
defaultTeamId: config.get("defaultTeamId")
|
|
19
|
+
defaultTeamId: config.get("defaultTeamId"),
|
|
20
|
+
accessToken: config.get("accessToken"),
|
|
21
|
+
refreshToken: config.get("refreshToken"),
|
|
22
|
+
tokenExpiresAt: config.get("tokenExpiresAt"),
|
|
23
|
+
clientId: config.get("clientId"),
|
|
24
|
+
clientSecret: config.get("clientSecret")
|
|
82
25
|
};
|
|
83
26
|
}
|
|
84
27
|
function getApiKey() {
|
|
@@ -116,38 +59,329 @@ function getConfigPath() {
|
|
|
116
59
|
function hasApiKey() {
|
|
117
60
|
return !!getApiKey();
|
|
118
61
|
}
|
|
62
|
+
function getAccessToken() {
|
|
63
|
+
return config.get("accessToken");
|
|
64
|
+
}
|
|
65
|
+
function getRefreshToken() {
|
|
66
|
+
return config.get("refreshToken");
|
|
67
|
+
}
|
|
68
|
+
function setOAuthTokens(accessToken, refreshToken, expiresIn) {
|
|
69
|
+
config.set("accessToken", accessToken);
|
|
70
|
+
config.set("refreshToken", refreshToken);
|
|
71
|
+
config.set("tokenExpiresAt", Date.now() + (expiresIn - 60) * 1e3);
|
|
72
|
+
}
|
|
73
|
+
function clearOAuthTokens() {
|
|
74
|
+
config.delete("accessToken");
|
|
75
|
+
config.delete("refreshToken");
|
|
76
|
+
config.delete("tokenExpiresAt");
|
|
77
|
+
}
|
|
78
|
+
function isTokenExpired() {
|
|
79
|
+
const expiresAt = config.get("tokenExpiresAt");
|
|
80
|
+
if (!expiresAt) return true;
|
|
81
|
+
return Date.now() >= expiresAt;
|
|
82
|
+
}
|
|
83
|
+
function hasOAuthTokens() {
|
|
84
|
+
return !!config.get("accessToken") && !!config.get("refreshToken");
|
|
85
|
+
}
|
|
86
|
+
function getClientId() {
|
|
87
|
+
return config.get("clientId");
|
|
88
|
+
}
|
|
89
|
+
function getClientSecret() {
|
|
90
|
+
return config.get("clientSecret");
|
|
91
|
+
}
|
|
92
|
+
function setOAuthClient(clientId, clientSecret) {
|
|
93
|
+
config.set("clientId", clientId);
|
|
94
|
+
config.set("clientSecret", clientSecret);
|
|
95
|
+
}
|
|
96
|
+
var DEFAULT_API_URL, schema, config;
|
|
97
|
+
var init_config = __esm({
|
|
98
|
+
"src/lib/config.ts"() {
|
|
99
|
+
"use strict";
|
|
100
|
+
DEFAULT_API_URL = "https://www.mindframes.app";
|
|
101
|
+
schema = {
|
|
102
|
+
apiKey: {
|
|
103
|
+
type: "string"
|
|
104
|
+
},
|
|
105
|
+
apiUrl: {
|
|
106
|
+
type: "string",
|
|
107
|
+
default: DEFAULT_API_URL
|
|
108
|
+
},
|
|
109
|
+
defaultTeamId: {
|
|
110
|
+
type: "string"
|
|
111
|
+
},
|
|
112
|
+
// OAuth tokens (preferred over API key)
|
|
113
|
+
accessToken: {
|
|
114
|
+
type: "string"
|
|
115
|
+
},
|
|
116
|
+
refreshToken: {
|
|
117
|
+
type: "string"
|
|
118
|
+
},
|
|
119
|
+
tokenExpiresAt: {
|
|
120
|
+
type: "number"
|
|
121
|
+
},
|
|
122
|
+
// OAuth client registration (cached per-server)
|
|
123
|
+
clientId: {
|
|
124
|
+
type: "string"
|
|
125
|
+
},
|
|
126
|
+
clientSecret: {
|
|
127
|
+
type: "string"
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
config = new Conf({
|
|
131
|
+
projectName: "mindframes",
|
|
132
|
+
schema
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
119
136
|
|
|
120
|
-
// src/lib/
|
|
137
|
+
// src/lib/output.ts
|
|
121
138
|
import chalk from "chalk";
|
|
139
|
+
import Table from "cli-table3";
|
|
140
|
+
function isTTY() {
|
|
141
|
+
return process.stdout.isTTY ?? false;
|
|
142
|
+
}
|
|
143
|
+
function success(message, format = "human") {
|
|
144
|
+
if (format === "quiet") return;
|
|
145
|
+
if (format === "json") return;
|
|
146
|
+
console.log(chalk.green("\u2713"), message);
|
|
147
|
+
}
|
|
148
|
+
function error(message, format = "human") {
|
|
149
|
+
if (format === "quiet") return;
|
|
150
|
+
if (format === "json") {
|
|
151
|
+
console.error(JSON.stringify({ error: message }));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.error(chalk.red("\u2717"), message);
|
|
155
|
+
}
|
|
156
|
+
function warn(message, format = "human") {
|
|
157
|
+
if (format === "quiet") return;
|
|
158
|
+
if (format === "json") return;
|
|
159
|
+
console.warn(chalk.yellow("\u26A0"), message);
|
|
160
|
+
}
|
|
161
|
+
function info(message, format = "human") {
|
|
162
|
+
if (format === "quiet") return;
|
|
163
|
+
if (format === "json") return;
|
|
164
|
+
console.log(chalk.blue("\u2139"), message);
|
|
165
|
+
}
|
|
166
|
+
function buildViewUrl(slug, language = "en") {
|
|
167
|
+
if (!slug) return "N/A";
|
|
168
|
+
const apiUrl = getApiUrl();
|
|
169
|
+
return `${apiUrl}/${language}/view/presentations/${slug}`;
|
|
170
|
+
}
|
|
171
|
+
function formatPresentationTable(presentations, options = {}) {
|
|
172
|
+
const { showLinks = false, language = "en" } = options;
|
|
173
|
+
const truncate = (str, len) => str.length > len ? str.slice(0, len - 1) + "\u2026" : str;
|
|
174
|
+
const shortDateTime = (dateStr) => {
|
|
175
|
+
try {
|
|
176
|
+
const d = new Date(dateStr);
|
|
177
|
+
return d.toLocaleString("en-US", {
|
|
178
|
+
month: "short",
|
|
179
|
+
day: "numeric",
|
|
180
|
+
hour: "numeric",
|
|
181
|
+
minute: "2-digit",
|
|
182
|
+
hour12: true
|
|
183
|
+
}).replace(" at ", ", ");
|
|
184
|
+
} catch {
|
|
185
|
+
return "-";
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
if (showLinks) {
|
|
189
|
+
const table2 = new Table({
|
|
190
|
+
head: [
|
|
191
|
+
chalk.cyan("Slug"),
|
|
192
|
+
chalk.cyan("Title"),
|
|
193
|
+
chalk.cyan("Slides"),
|
|
194
|
+
chalk.cyan("Created"),
|
|
195
|
+
chalk.cyan("URL")
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
for (const p of presentations) {
|
|
199
|
+
table2.push([
|
|
200
|
+
truncate(p.slug || p.id.slice(0, 8), 30),
|
|
201
|
+
truncate(p.title || "Untitled", 22),
|
|
202
|
+
String(p.numberOfSlides || "-"),
|
|
203
|
+
shortDateTime(p.createdAt),
|
|
204
|
+
buildViewUrl(p.slug, language)
|
|
205
|
+
]);
|
|
206
|
+
}
|
|
207
|
+
return table2.toString();
|
|
208
|
+
}
|
|
209
|
+
const table = new Table({
|
|
210
|
+
head: [
|
|
211
|
+
chalk.cyan("Slug"),
|
|
212
|
+
chalk.cyan("Title"),
|
|
213
|
+
chalk.cyan("Slides"),
|
|
214
|
+
chalk.cyan("Mode"),
|
|
215
|
+
chalk.cyan("Created")
|
|
216
|
+
],
|
|
217
|
+
colWidths: [32, 28, 8, 11, 18]
|
|
218
|
+
});
|
|
219
|
+
for (const p of presentations) {
|
|
220
|
+
table.push([
|
|
221
|
+
p.slug || p.id.slice(0, 8),
|
|
222
|
+
p.title || "Untitled",
|
|
223
|
+
String(p.numberOfSlides || "-"),
|
|
224
|
+
p.mode || "-",
|
|
225
|
+
shortDateTime(p.createdAt)
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
return table.toString();
|
|
229
|
+
}
|
|
230
|
+
function formatPresentationIds(presentations) {
|
|
231
|
+
return presentations.map((p) => p.slug || p.id).join("\n");
|
|
232
|
+
}
|
|
233
|
+
function formatBrandingTable(brandings) {
|
|
234
|
+
const sorted = [...brandings].sort((a, b) => {
|
|
235
|
+
if (a.isDefault && !b.isDefault) return -1;
|
|
236
|
+
if (!a.isDefault && b.isDefault) return 1;
|
|
237
|
+
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
238
|
+
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
239
|
+
return dateB - dateA;
|
|
240
|
+
});
|
|
241
|
+
const formatUrl = (url) => {
|
|
242
|
+
if (!url) return "-";
|
|
243
|
+
try {
|
|
244
|
+
const u = new URL(url);
|
|
245
|
+
return u.href;
|
|
246
|
+
} catch {
|
|
247
|
+
return url.startsWith("http") ? url : `https://${url}`;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
const table = new Table({
|
|
251
|
+
head: [
|
|
252
|
+
chalk.cyan("Name"),
|
|
253
|
+
chalk.cyan("Source"),
|
|
254
|
+
chalk.cyan("Color"),
|
|
255
|
+
chalk.cyan("Logo"),
|
|
256
|
+
chalk.cyan("Default"),
|
|
257
|
+
chalk.cyan("ID")
|
|
258
|
+
]
|
|
259
|
+
});
|
|
260
|
+
for (const b of sorted) {
|
|
261
|
+
const colorDisplay = b.primaryColor ? `${chalk.bgHex(b.primaryColor)(" ")} ${b.primaryColor}` : "-";
|
|
262
|
+
table.push([
|
|
263
|
+
b.name,
|
|
264
|
+
formatUrl(b.sourceUrl),
|
|
265
|
+
colorDisplay,
|
|
266
|
+
b.logoUrl ? chalk.green("\u2713") : chalk.gray("\u2717"),
|
|
267
|
+
b.isDefault ? chalk.green("\u2605") : "",
|
|
268
|
+
b.id
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
return table.toString();
|
|
272
|
+
}
|
|
273
|
+
function formatDate(dateStr) {
|
|
274
|
+
try {
|
|
275
|
+
const date = new Date(dateStr);
|
|
276
|
+
return date.toLocaleString();
|
|
277
|
+
} catch {
|
|
278
|
+
return dateStr;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function header(text) {
|
|
282
|
+
console.log();
|
|
283
|
+
console.log(chalk.bold(text));
|
|
284
|
+
console.log(chalk.gray("\u2500".repeat(text.length)));
|
|
285
|
+
}
|
|
286
|
+
function keyValue(key, value) {
|
|
287
|
+
console.log(` ${chalk.gray(key + ":")} ${value ?? "-"}`);
|
|
288
|
+
}
|
|
289
|
+
function progressBar(current, total, width = 30, showPercentage = true) {
|
|
290
|
+
const percentage = Math.min(100, Math.round(current / total * 100));
|
|
291
|
+
const filled = Math.round(width * current / total);
|
|
292
|
+
const empty = width - filled;
|
|
293
|
+
const bar = chalk.green("\u2588".repeat(filled)) + chalk.gray("\u2591".repeat(empty));
|
|
294
|
+
return showPercentage ? `[${bar}] ${percentage}%` : `[${bar}]`;
|
|
295
|
+
}
|
|
296
|
+
var init_output = __esm({
|
|
297
|
+
"src/lib/output.ts"() {
|
|
298
|
+
"use strict";
|
|
299
|
+
init_config();
|
|
300
|
+
}
|
|
301
|
+
});
|
|
122
302
|
|
|
123
303
|
// src/types/index.ts
|
|
124
|
-
var EXIT_CODES
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
304
|
+
var EXIT_CODES;
|
|
305
|
+
var init_types = __esm({
|
|
306
|
+
"src/types/index.ts"() {
|
|
307
|
+
"use strict";
|
|
308
|
+
EXIT_CODES = {
|
|
309
|
+
SUCCESS: 0,
|
|
310
|
+
GENERAL_ERROR: 1,
|
|
311
|
+
AUTH_ERROR: 2,
|
|
312
|
+
NOT_FOUND: 3,
|
|
313
|
+
RATE_LIMIT: 4,
|
|
314
|
+
NETWORK_ERROR: 5,
|
|
315
|
+
INVALID_INPUT: 6
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
});
|
|
133
319
|
|
|
134
320
|
// src/lib/auth.ts
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
321
|
+
import chalk2 from "chalk";
|
|
322
|
+
async function refreshAccessToken() {
|
|
323
|
+
const refreshToken = getRefreshToken();
|
|
324
|
+
const clientId = getClientId();
|
|
325
|
+
const apiUrl = getApiUrl();
|
|
326
|
+
if (!refreshToken || !clientId) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
const metaResponse = await fetch(`${apiUrl}/.well-known/oauth-authorization-server`);
|
|
331
|
+
if (!metaResponse.ok) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const metadata = await metaResponse.json();
|
|
335
|
+
const params = new URLSearchParams({
|
|
336
|
+
grant_type: "refresh_token",
|
|
337
|
+
refresh_token: refreshToken,
|
|
338
|
+
client_id: clientId
|
|
339
|
+
});
|
|
340
|
+
const response = await fetch(metadata.token_endpoint, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
343
|
+
body: params.toString()
|
|
344
|
+
});
|
|
345
|
+
if (!response.ok) {
|
|
346
|
+
clearOAuthTokens();
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const tokens = await response.json();
|
|
350
|
+
setOAuthTokens(tokens.access_token, tokens.refresh_token, tokens.expires_in);
|
|
351
|
+
return tokens.access_token;
|
|
352
|
+
} catch {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function getValidAccessToken() {
|
|
357
|
+
if (!hasOAuthTokens()) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
if (isTokenExpired()) {
|
|
361
|
+
return refreshAccessToken();
|
|
362
|
+
}
|
|
363
|
+
return getAccessToken() || null;
|
|
364
|
+
}
|
|
365
|
+
function hasAuth() {
|
|
366
|
+
return hasOAuthTokens() || hasApiKey();
|
|
367
|
+
}
|
|
368
|
+
async function requireAuth() {
|
|
369
|
+
if (!hasAuth()) {
|
|
370
|
+
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
371
|
+
try {
|
|
372
|
+
const shouldLogin = await confirm4({
|
|
373
|
+
message: chalk2.red("Not authenticated.") + " Log in now?",
|
|
374
|
+
default: true
|
|
375
|
+
});
|
|
376
|
+
if (!shouldLogin) {
|
|
377
|
+
console.log(chalk2.gray("\nTip: You can also set CC_MINDFRAMES_API_KEY environment variable."));
|
|
378
|
+
process.exit(EXIT_CODES.AUTH_ERROR);
|
|
379
|
+
}
|
|
380
|
+
const { runLoginFlow: runLoginFlow2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
381
|
+
await runLoginFlow2({ browser: true });
|
|
382
|
+
} catch {
|
|
383
|
+
process.exit(EXIT_CODES.AUTH_ERROR);
|
|
384
|
+
}
|
|
151
385
|
}
|
|
152
386
|
}
|
|
153
387
|
function maskApiKey(key) {
|
|
@@ -165,33 +399,43 @@ function isValidApiKeyFormat(key) {
|
|
|
165
399
|
const validPrefixes = ["cc_slides_", "mcp_", "cc_sk_", "sk_"];
|
|
166
400
|
return validPrefixes.some((prefix) => key.startsWith(prefix));
|
|
167
401
|
}
|
|
402
|
+
var init_auth = __esm({
|
|
403
|
+
"src/lib/auth.ts"() {
|
|
404
|
+
"use strict";
|
|
405
|
+
init_config();
|
|
406
|
+
init_types();
|
|
407
|
+
}
|
|
408
|
+
});
|
|
168
409
|
|
|
169
410
|
// src/lib/api.ts
|
|
170
411
|
import { readFileSync, statSync } from "fs";
|
|
171
412
|
import { basename } from "path";
|
|
172
413
|
import { randomUUID } from "crypto";
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.exitCode = exitCode;
|
|
178
|
-
this.name = "ApiError";
|
|
414
|
+
async function getAuthHeaders() {
|
|
415
|
+
const accessToken = await getValidAccessToken();
|
|
416
|
+
if (accessToken) {
|
|
417
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
179
418
|
}
|
|
180
|
-
};
|
|
181
|
-
async function request(endpoint, options = {}) {
|
|
182
419
|
const apiKey = getApiKey();
|
|
420
|
+
if (apiKey) {
|
|
421
|
+
return { "x-api-key": apiKey };
|
|
422
|
+
}
|
|
423
|
+
return {};
|
|
424
|
+
}
|
|
425
|
+
async function request(endpoint, options = {}) {
|
|
183
426
|
const apiUrl = getApiUrl();
|
|
184
|
-
if (!
|
|
427
|
+
if (!hasAuth()) {
|
|
185
428
|
throw new ApiError(
|
|
186
|
-
"
|
|
429
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
187
430
|
401,
|
|
188
431
|
2
|
|
189
432
|
// AUTH_ERROR
|
|
190
433
|
);
|
|
191
434
|
}
|
|
435
|
+
const authHeaders = await getAuthHeaders();
|
|
192
436
|
const url = `${apiUrl}${endpoint}`;
|
|
193
437
|
const headers = {
|
|
194
|
-
|
|
438
|
+
...authHeaders,
|
|
195
439
|
...options.headers
|
|
196
440
|
};
|
|
197
441
|
if (options.body && !options.stream) {
|
|
@@ -248,15 +492,15 @@ async function request(endpoint, options = {}) {
|
|
|
248
492
|
return response.text();
|
|
249
493
|
}
|
|
250
494
|
async function streamRequest(endpoint, body) {
|
|
251
|
-
const apiKey = getApiKey();
|
|
252
495
|
const apiUrl = getApiUrl();
|
|
253
|
-
if (!
|
|
496
|
+
if (!hasAuth()) {
|
|
254
497
|
throw new ApiError(
|
|
255
|
-
"
|
|
498
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
256
499
|
401,
|
|
257
500
|
2
|
|
258
501
|
);
|
|
259
502
|
}
|
|
503
|
+
const authHeaders = await getAuthHeaders();
|
|
260
504
|
const url = `${apiUrl}${endpoint}`;
|
|
261
505
|
let response;
|
|
262
506
|
try {
|
|
@@ -264,7 +508,7 @@ async function streamRequest(endpoint, body) {
|
|
|
264
508
|
method: "POST",
|
|
265
509
|
headers: {
|
|
266
510
|
"Content-Type": "application/json",
|
|
267
|
-
|
|
511
|
+
...authHeaders
|
|
268
512
|
},
|
|
269
513
|
body: JSON.stringify(body)
|
|
270
514
|
});
|
|
@@ -324,15 +568,15 @@ function getMimeType(filePath) {
|
|
|
324
568
|
return mimeTypes[ext || ""] || "application/octet-stream";
|
|
325
569
|
}
|
|
326
570
|
async function uploadFile(filePath) {
|
|
327
|
-
const apiKey = getApiKey();
|
|
328
571
|
const apiUrl = getApiUrl();
|
|
329
|
-
if (!
|
|
572
|
+
if (!hasAuth()) {
|
|
330
573
|
throw new ApiError(
|
|
331
|
-
"
|
|
574
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
332
575
|
401,
|
|
333
576
|
2
|
|
334
577
|
);
|
|
335
578
|
}
|
|
579
|
+
const authHeaders = await getAuthHeaders();
|
|
336
580
|
const stat = statSync(filePath);
|
|
337
581
|
const fileName = basename(filePath);
|
|
338
582
|
const mimeType = getMimeType(filePath);
|
|
@@ -340,7 +584,7 @@ async function uploadFile(filePath) {
|
|
|
340
584
|
method: "POST",
|
|
341
585
|
headers: {
|
|
342
586
|
"Content-Type": "application/json",
|
|
343
|
-
|
|
587
|
+
...authHeaders
|
|
344
588
|
},
|
|
345
589
|
body: JSON.stringify({
|
|
346
590
|
fileMetadata: {
|
|
@@ -461,6 +705,9 @@ async function createPresentation(options) {
|
|
|
461
705
|
if (options.teamId) {
|
|
462
706
|
body.teamId = options.teamId;
|
|
463
707
|
}
|
|
708
|
+
if (options.theme) {
|
|
709
|
+
body.theme = options.theme;
|
|
710
|
+
}
|
|
464
711
|
return streamRequest("/api/slides/skills/create-presentation", body);
|
|
465
712
|
}
|
|
466
713
|
async function listPresentations(teamId, limit = 20) {
|
|
@@ -481,20 +728,20 @@ async function deletePresentation(slugOrId) {
|
|
|
481
728
|
await request(`/api/cli/presentation/${slugOrId}`, { method: "DELETE" });
|
|
482
729
|
}
|
|
483
730
|
async function exportPresentation(presentationId, options = {}) {
|
|
484
|
-
const apiKey = getApiKey();
|
|
485
731
|
const apiUrl = getApiUrl();
|
|
486
|
-
if (!
|
|
732
|
+
if (!hasAuth()) {
|
|
487
733
|
throw new ApiError(
|
|
488
|
-
"
|
|
734
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
489
735
|
401,
|
|
490
736
|
2
|
|
491
737
|
);
|
|
492
738
|
}
|
|
739
|
+
const authHeaders = await getAuthHeaders();
|
|
493
740
|
const response = await fetch(`${apiUrl}/api/presentations/export`, {
|
|
494
741
|
method: "POST",
|
|
495
742
|
headers: {
|
|
496
743
|
"Content-Type": "application/json",
|
|
497
|
-
|
|
744
|
+
...authHeaders
|
|
498
745
|
},
|
|
499
746
|
body: JSON.stringify({
|
|
500
747
|
presentationId,
|
|
@@ -515,15 +762,15 @@ async function exportPresentation(presentationId, options = {}) {
|
|
|
515
762
|
return response.arrayBuffer();
|
|
516
763
|
}
|
|
517
764
|
async function importPresentation(fileBuffer, fileName, options = {}) {
|
|
518
|
-
const apiKey = getApiKey();
|
|
519
765
|
const apiUrl = getApiUrl();
|
|
520
|
-
if (!
|
|
766
|
+
if (!hasAuth()) {
|
|
521
767
|
throw new ApiError(
|
|
522
|
-
"
|
|
768
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
523
769
|
401,
|
|
524
770
|
2
|
|
525
771
|
);
|
|
526
772
|
}
|
|
773
|
+
const authHeaders = await getAuthHeaders();
|
|
527
774
|
const formData = new FormData();
|
|
528
775
|
const blob = new Blob([fileBuffer], { type: "application/zip" });
|
|
529
776
|
formData.append("file", blob, fileName);
|
|
@@ -536,9 +783,7 @@ async function importPresentation(fileBuffer, fileName, options = {}) {
|
|
|
536
783
|
);
|
|
537
784
|
const response = await fetch(`${apiUrl}/api/presentations/import`, {
|
|
538
785
|
method: "POST",
|
|
539
|
-
headers:
|
|
540
|
-
"x-api-key": apiKey
|
|
541
|
-
},
|
|
786
|
+
headers: authHeaders,
|
|
542
787
|
body: formData
|
|
543
788
|
});
|
|
544
789
|
const result = await response.json();
|
|
@@ -570,22 +815,22 @@ async function getFeatureFlags() {
|
|
|
570
815
|
return request("/api/cli/features");
|
|
571
816
|
}
|
|
572
817
|
async function generateBlog(presentationId, documentCreatedAt, options = {}) {
|
|
573
|
-
const apiKey = getApiKey();
|
|
574
818
|
const apiUrl = getApiUrl();
|
|
575
|
-
if (!
|
|
819
|
+
if (!hasAuth()) {
|
|
576
820
|
throw new ApiError(
|
|
577
|
-
"
|
|
821
|
+
"Not authenticated. Run 'mindframes login' or set CC_MINDFRAMES_API_KEY environment variable.",
|
|
578
822
|
401,
|
|
579
823
|
2
|
|
580
824
|
);
|
|
581
825
|
}
|
|
826
|
+
const authHeaders = await getAuthHeaders();
|
|
582
827
|
const response = await fetch(
|
|
583
828
|
`${apiUrl}/api/presentations/${presentationId}/generate-blog`,
|
|
584
829
|
{
|
|
585
830
|
method: "POST",
|
|
586
831
|
headers: {
|
|
587
832
|
"Content-Type": "application/json",
|
|
588
|
-
|
|
833
|
+
...authHeaders
|
|
589
834
|
},
|
|
590
835
|
body: JSON.stringify({
|
|
591
836
|
documentCreatedAt,
|
|
@@ -644,15 +889,25 @@ async function validateGeneration(mode, slideCount, teamId) {
|
|
|
644
889
|
}
|
|
645
890
|
return limits;
|
|
646
891
|
}
|
|
892
|
+
var ApiError;
|
|
893
|
+
var init_api = __esm({
|
|
894
|
+
"src/lib/api.ts"() {
|
|
895
|
+
"use strict";
|
|
896
|
+
init_config();
|
|
897
|
+
init_auth();
|
|
898
|
+
ApiError = class extends Error {
|
|
899
|
+
constructor(message, statusCode, exitCode = 1) {
|
|
900
|
+
super(message);
|
|
901
|
+
this.statusCode = statusCode;
|
|
902
|
+
this.exitCode = exitCode;
|
|
903
|
+
this.name = "ApiError";
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
});
|
|
647
908
|
|
|
648
909
|
// src/lib/feature-cache.ts
|
|
649
910
|
import Conf2 from "conf";
|
|
650
|
-
var cache = new Conf2({
|
|
651
|
-
projectName: "conceptcraft",
|
|
652
|
-
configName: "feature-cache"
|
|
653
|
-
});
|
|
654
|
-
var FRESH_TTL = 60 * 60 * 1e3;
|
|
655
|
-
var STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
656
911
|
function hashApiKey(key) {
|
|
657
912
|
let hash = 0;
|
|
658
913
|
for (let i = 0; i < key.length; i++) {
|
|
@@ -698,173 +953,406 @@ function invalidateCache() {
|
|
|
698
953
|
function getCachePath() {
|
|
699
954
|
return cache.path;
|
|
700
955
|
}
|
|
956
|
+
var cache, FRESH_TTL, STALE_TTL;
|
|
957
|
+
var init_feature_cache = __esm({
|
|
958
|
+
"src/lib/feature-cache.ts"() {
|
|
959
|
+
"use strict";
|
|
960
|
+
init_api();
|
|
961
|
+
init_config();
|
|
962
|
+
cache = new Conf2({
|
|
963
|
+
projectName: "conceptcraft",
|
|
964
|
+
configName: "feature-cache"
|
|
965
|
+
});
|
|
966
|
+
FRESH_TTL = 60 * 60 * 1e3;
|
|
967
|
+
STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
968
|
+
}
|
|
969
|
+
});
|
|
701
970
|
|
|
702
|
-
// src/
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
971
|
+
// src/commands/login.ts
|
|
972
|
+
var login_exports = {};
|
|
973
|
+
__export(login_exports, {
|
|
974
|
+
loginCommand: () => loginCommand,
|
|
975
|
+
runLoginFlow: () => runLoginFlow
|
|
976
|
+
});
|
|
977
|
+
import { Command } from "commander";
|
|
978
|
+
import chalk3 from "chalk";
|
|
979
|
+
import ora from "ora";
|
|
980
|
+
import http from "http";
|
|
981
|
+
import { randomBytes, createHash } from "crypto";
|
|
982
|
+
import open from "open";
|
|
983
|
+
function generateCodeVerifier() {
|
|
984
|
+
return randomBytes(32).toString("base64url");
|
|
707
985
|
}
|
|
708
|
-
function
|
|
709
|
-
|
|
710
|
-
if (format === "json") return;
|
|
711
|
-
console.log(chalk2.green("\u2713"), message);
|
|
986
|
+
function generateCodeChallenge(verifier) {
|
|
987
|
+
return createHash("sha256").update(verifier).digest("base64url");
|
|
712
988
|
}
|
|
713
|
-
function
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
989
|
+
function generateState() {
|
|
990
|
+
return randomBytes(16).toString("hex");
|
|
991
|
+
}
|
|
992
|
+
async function findAvailablePort(start, end) {
|
|
993
|
+
for (let port = start; port <= end; port++) {
|
|
994
|
+
try {
|
|
995
|
+
await new Promise((resolve4, reject) => {
|
|
996
|
+
const server = http.createServer();
|
|
997
|
+
server.listen(port, () => {
|
|
998
|
+
server.close(() => resolve4());
|
|
999
|
+
});
|
|
1000
|
+
server.on("error", reject);
|
|
1001
|
+
});
|
|
1002
|
+
return port;
|
|
1003
|
+
} catch {
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
718
1006
|
}
|
|
719
|
-
|
|
1007
|
+
throw new Error(`No available port found between ${start} and ${end}`);
|
|
720
1008
|
}
|
|
721
|
-
function
|
|
722
|
-
|
|
723
|
-
if (
|
|
724
|
-
|
|
1009
|
+
async function getAuthServerMetadata(apiUrl) {
|
|
1010
|
+
const response = await fetch(`${apiUrl}/.well-known/oauth-authorization-server`);
|
|
1011
|
+
if (!response.ok) {
|
|
1012
|
+
throw new Error(`Failed to fetch OAuth metadata: ${response.status}`);
|
|
1013
|
+
}
|
|
1014
|
+
return response.json();
|
|
725
1015
|
}
|
|
726
|
-
function
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
1016
|
+
async function registerClient(registrationEndpoint, redirectUri) {
|
|
1017
|
+
const response = await fetch(registrationEndpoint, {
|
|
1018
|
+
method: "POST",
|
|
1019
|
+
headers: { "Content-Type": "application/json" },
|
|
1020
|
+
body: JSON.stringify({
|
|
1021
|
+
client_name: CLI_CLIENT_NAME,
|
|
1022
|
+
redirect_uris: [redirectUri],
|
|
1023
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
1024
|
+
response_types: ["code"],
|
|
1025
|
+
token_endpoint_auth_method: "none",
|
|
1026
|
+
// Public client with PKCE
|
|
1027
|
+
scope: "presentations:read presentations:write"
|
|
1028
|
+
})
|
|
1029
|
+
});
|
|
1030
|
+
if (!response.ok) {
|
|
1031
|
+
const err = await response.text();
|
|
1032
|
+
throw new Error(`Client registration failed: ${err}`);
|
|
1033
|
+
}
|
|
1034
|
+
return response.json();
|
|
1035
|
+
}
|
|
1036
|
+
async function exchangeCodeForTokens(tokenEndpoint, code, codeVerifier, redirectUri, clientId) {
|
|
1037
|
+
const params = new URLSearchParams({
|
|
1038
|
+
grant_type: "authorization_code",
|
|
1039
|
+
code,
|
|
1040
|
+
redirect_uri: redirectUri,
|
|
1041
|
+
client_id: clientId,
|
|
1042
|
+
code_verifier: codeVerifier
|
|
1043
|
+
});
|
|
1044
|
+
const response = await fetch(tokenEndpoint, {
|
|
1045
|
+
method: "POST",
|
|
1046
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1047
|
+
body: params.toString()
|
|
1048
|
+
});
|
|
1049
|
+
if (!response.ok) {
|
|
1050
|
+
const err = await response.text();
|
|
1051
|
+
throw new Error(`Token exchange failed: ${err}`);
|
|
1052
|
+
}
|
|
1053
|
+
return response.json();
|
|
1054
|
+
}
|
|
1055
|
+
function startCallbackServer(port, expectedState) {
|
|
1056
|
+
return new Promise((resolve4, reject) => {
|
|
1057
|
+
const server = http.createServer((req, res) => {
|
|
1058
|
+
const url = new URL(req.url || "", `http://localhost:${port}`);
|
|
1059
|
+
if (url.pathname !== "/callback") {
|
|
1060
|
+
res.writeHead(404);
|
|
1061
|
+
res.end("Not found");
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const code = url.searchParams.get("code");
|
|
1065
|
+
const state = url.searchParams.get("state");
|
|
1066
|
+
const errorParam = url.searchParams.get("error");
|
|
1067
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
1068
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1069
|
+
if (errorParam) {
|
|
1070
|
+
res.end(`
|
|
1071
|
+
<!DOCTYPE html>
|
|
1072
|
+
<html>
|
|
1073
|
+
<head><title>Login Failed</title></head>
|
|
1074
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1075
|
+
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
1076
|
+
<p>${errorDescription || errorParam}</p>
|
|
1077
|
+
<p>You can close this window.</p>
|
|
1078
|
+
</body>
|
|
1079
|
+
</html>
|
|
1080
|
+
`);
|
|
1081
|
+
server.close();
|
|
1082
|
+
reject(new Error(errorDescription || errorParam));
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if (!code || !state) {
|
|
1086
|
+
res.end(`
|
|
1087
|
+
<!DOCTYPE html>
|
|
1088
|
+
<html>
|
|
1089
|
+
<head><title>Login Failed</title></head>
|
|
1090
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1091
|
+
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
1092
|
+
<p>Missing authorization code or state</p>
|
|
1093
|
+
<p>You can close this window.</p>
|
|
1094
|
+
</body>
|
|
1095
|
+
</html>
|
|
1096
|
+
`);
|
|
1097
|
+
server.close();
|
|
1098
|
+
reject(new Error("Missing authorization code or state"));
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
if (state !== expectedState) {
|
|
1102
|
+
res.end(`
|
|
1103
|
+
<!DOCTYPE html>
|
|
1104
|
+
<html>
|
|
1105
|
+
<head><title>Login Failed</title></head>
|
|
1106
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1107
|
+
<h1 style="color: #dc2626;">Login Failed</h1>
|
|
1108
|
+
<p>State mismatch - possible CSRF attack</p>
|
|
1109
|
+
<p>You can close this window.</p>
|
|
1110
|
+
</body>
|
|
1111
|
+
</html>
|
|
1112
|
+
`);
|
|
1113
|
+
server.close();
|
|
1114
|
+
reject(new Error("State mismatch"));
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
res.end(`
|
|
1118
|
+
<!DOCTYPE html>
|
|
1119
|
+
<html>
|
|
1120
|
+
<head><title>Login Successful</title></head>
|
|
1121
|
+
<body style="font-family: system-ui; text-align: center; padding: 50px;">
|
|
1122
|
+
<h1 style="color: #16a34a;">Login Successful!</h1>
|
|
1123
|
+
<p>You can close this window and return to the terminal.</p>
|
|
1124
|
+
</body>
|
|
1125
|
+
</html>
|
|
1126
|
+
`);
|
|
1127
|
+
server.close();
|
|
1128
|
+
resolve4({ code, state });
|
|
1129
|
+
});
|
|
1130
|
+
server.listen(port);
|
|
1131
|
+
const cleanup = () => {
|
|
1132
|
+
server.close();
|
|
1133
|
+
reject(new Error("Login cancelled"));
|
|
1134
|
+
};
|
|
1135
|
+
process.once("SIGINT", cleanup);
|
|
1136
|
+
process.once("SIGTERM", cleanup);
|
|
1137
|
+
setTimeout(() => {
|
|
1138
|
+
process.off("SIGINT", cleanup);
|
|
1139
|
+
process.off("SIGTERM", cleanup);
|
|
1140
|
+
server.close();
|
|
1141
|
+
reject(new Error("Login timed out - no callback received within 5 minutes"));
|
|
1142
|
+
}, 5 * 60 * 1e3);
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
async function fetchWhoami(apiUrl, accessToken) {
|
|
1146
|
+
const response = await fetch(`${apiUrl}/api/cli/whoami`, {
|
|
1147
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
1148
|
+
});
|
|
1149
|
+
if (!response.ok) {
|
|
1150
|
+
throw new Error(`Failed to fetch user info: ${response.status}`);
|
|
1151
|
+
}
|
|
1152
|
+
return response.json();
|
|
730
1153
|
}
|
|
731
|
-
function
|
|
732
|
-
if (!slug) return "N/A";
|
|
1154
|
+
async function runLoginFlow(options) {
|
|
733
1155
|
const apiUrl = getApiUrl();
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1156
|
+
let spinner = ora("Discovering OAuth server...").start();
|
|
1157
|
+
try {
|
|
1158
|
+
const metadata = await getAuthServerMetadata(apiUrl);
|
|
1159
|
+
spinner.succeed("Connecting to " + apiUrl);
|
|
1160
|
+
const port = await findAvailablePort(CALLBACK_PORT_START, CALLBACK_PORT_END);
|
|
1161
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
1162
|
+
let clientId = getClientId();
|
|
1163
|
+
let clientSecret = getClientSecret();
|
|
1164
|
+
if (!clientId) {
|
|
1165
|
+
const client = await registerClient(metadata.registration_endpoint, redirectUri);
|
|
1166
|
+
clientId = client.client_id;
|
|
1167
|
+
clientSecret = client.client_secret;
|
|
1168
|
+
setOAuthClient(clientId, clientSecret);
|
|
1169
|
+
}
|
|
1170
|
+
const codeVerifier = generateCodeVerifier();
|
|
1171
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
1172
|
+
const state = generateState();
|
|
1173
|
+
const authParams = new URLSearchParams({
|
|
1174
|
+
client_id: clientId,
|
|
1175
|
+
redirect_uri: redirectUri,
|
|
1176
|
+
response_type: "code",
|
|
1177
|
+
scope: "presentations:read presentations:write",
|
|
1178
|
+
state,
|
|
1179
|
+
code_challenge: codeChallenge,
|
|
1180
|
+
code_challenge_method: "S256"
|
|
1181
|
+
});
|
|
1182
|
+
const authUrl = `${metadata.authorization_endpoint}?${authParams}`;
|
|
1183
|
+
if (options.browser) {
|
|
1184
|
+
info("Opening browser...");
|
|
1185
|
+
await open(authUrl);
|
|
1186
|
+
} else {
|
|
1187
|
+
console.log(chalk3.bold("Open this URL in your browser:"));
|
|
1188
|
+
console.log(chalk3.cyan(authUrl));
|
|
1189
|
+
}
|
|
1190
|
+
const callbackPromise = startCallbackServer(port, state);
|
|
1191
|
+
const { code } = await callbackPromise;
|
|
1192
|
+
spinner = ora("Completing login...").start();
|
|
1193
|
+
const tokens = await exchangeCodeForTokens(
|
|
1194
|
+
metadata.token_endpoint,
|
|
1195
|
+
code,
|
|
1196
|
+
codeVerifier,
|
|
1197
|
+
redirectUri,
|
|
1198
|
+
clientId
|
|
1199
|
+
);
|
|
1200
|
+
setOAuthTokens(tokens.access_token, tokens.refresh_token, tokens.expires_in);
|
|
1201
|
+
const whoami2 = await fetchWhoami(apiUrl, tokens.access_token);
|
|
1202
|
+
spinner.succeed("Logged in!");
|
|
1203
|
+
console.log();
|
|
1204
|
+
keyValue("Logged in as", whoami2.user.email);
|
|
1205
|
+
if (whoami2.currentTeam) {
|
|
1206
|
+
setDefaultTeamId(whoami2.currentTeam.id);
|
|
1207
|
+
keyValue("Team", `${whoami2.currentTeam.name} (${whoami2.currentTeam.planName})`);
|
|
1208
|
+
}
|
|
740
1209
|
try {
|
|
741
|
-
|
|
742
|
-
return d.toLocaleString("en-US", {
|
|
743
|
-
month: "short",
|
|
744
|
-
day: "numeric",
|
|
745
|
-
hour: "numeric",
|
|
746
|
-
minute: "2-digit",
|
|
747
|
-
hour12: true
|
|
748
|
-
}).replace(" at ", ", ");
|
|
1210
|
+
await fetchAndCache();
|
|
749
1211
|
} catch {
|
|
750
|
-
return "-";
|
|
751
1212
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1213
|
+
console.log();
|
|
1214
|
+
success("You're all set!");
|
|
1215
|
+
console.log();
|
|
1216
|
+
} catch (err) {
|
|
1217
|
+
spinner.fail("Login failed");
|
|
1218
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1219
|
+
throw err;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
var CLI_CLIENT_NAME, CALLBACK_PORT_START, CALLBACK_PORT_END, loginCommand;
|
|
1223
|
+
var init_login = __esm({
|
|
1224
|
+
"src/commands/login.ts"() {
|
|
1225
|
+
"use strict";
|
|
1226
|
+
init_config();
|
|
1227
|
+
init_output();
|
|
1228
|
+
init_feature_cache();
|
|
1229
|
+
CLI_CLIENT_NAME = "ConceptCraft CLI";
|
|
1230
|
+
CALLBACK_PORT_START = 8765;
|
|
1231
|
+
CALLBACK_PORT_END = 8775;
|
|
1232
|
+
loginCommand = new Command("login").description("Authenticate with ConceptCraft (opens browser)").option("--no-browser", "Print URL instead of opening browser").action(async (options) => {
|
|
1233
|
+
console.log();
|
|
1234
|
+
if (hasOAuthTokens()) {
|
|
1235
|
+
warn("You are already logged in.");
|
|
1236
|
+
info("Run 'conceptcraft logout' to log out first, or continue to re-authenticate.");
|
|
1237
|
+
console.log();
|
|
1238
|
+
}
|
|
1239
|
+
try {
|
|
1240
|
+
await runLoginFlow(options);
|
|
1241
|
+
} catch {
|
|
1242
|
+
process.exit(1);
|
|
1243
|
+
}
|
|
762
1244
|
});
|
|
763
|
-
for (const p of presentations) {
|
|
764
|
-
table2.push([
|
|
765
|
-
truncate(p.slug || p.id.slice(0, 8), 30),
|
|
766
|
-
truncate(p.title || "Untitled", 22),
|
|
767
|
-
String(p.numberOfSlides || "-"),
|
|
768
|
-
shortDateTime(p.createdAt),
|
|
769
|
-
buildViewUrl(p.slug, language)
|
|
770
|
-
]);
|
|
771
|
-
}
|
|
772
|
-
return table2.toString();
|
|
773
1245
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
// src/index.ts
|
|
1249
|
+
import { Command as Command15 } from "commander";
|
|
1250
|
+
import chalk13 from "chalk";
|
|
1251
|
+
|
|
1252
|
+
// src/lib/brand.ts
|
|
1253
|
+
import path from "path";
|
|
1254
|
+
var BRANDS = {
|
|
1255
|
+
conceptcraft: {
|
|
1256
|
+
id: "conceptcraft",
|
|
1257
|
+
name: "conceptcraft",
|
|
1258
|
+
displayName: "Conceptcraft",
|
|
1259
|
+
description: "CLI tool for Conceptcraft presentation generation",
|
|
1260
|
+
commands: ["cc", "conceptcraft"],
|
|
1261
|
+
apiUrl: "https://conceptcraft.ai",
|
|
1262
|
+
docsUrl: "https://docs.conceptcraft.ai"
|
|
1263
|
+
},
|
|
1264
|
+
mindframes: {
|
|
1265
|
+
id: "mindframes",
|
|
1266
|
+
name: "mindframes",
|
|
1267
|
+
displayName: "Mindframes",
|
|
1268
|
+
description: "CLI tool for Mindframes presentation generation",
|
|
1269
|
+
commands: ["mf", "mindframes"],
|
|
1270
|
+
apiUrl: "https://mindframes.app",
|
|
1271
|
+
docsUrl: "https://docs.mindframes.app"
|
|
792
1272
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1273
|
+
};
|
|
1274
|
+
var COMMAND_TO_BRAND = {
|
|
1275
|
+
cc: "conceptcraft",
|
|
1276
|
+
conceptcraft: "conceptcraft",
|
|
1277
|
+
mf: "mindframes",
|
|
1278
|
+
mindframes: "mindframes"
|
|
1279
|
+
};
|
|
1280
|
+
function detectBrand() {
|
|
1281
|
+
const binaryPath = process.argv[1] || "";
|
|
1282
|
+
const binaryName = path.basename(binaryPath).replace(/\.(js|ts)$/, "");
|
|
1283
|
+
const brandId = COMMAND_TO_BRAND[binaryName];
|
|
1284
|
+
if (brandId) {
|
|
1285
|
+
return BRANDS[brandId];
|
|
1286
|
+
}
|
|
1287
|
+
for (const [cmd2, id] of Object.entries(COMMAND_TO_BRAND)) {
|
|
1288
|
+
if (binaryPath.includes(`/${cmd2}`) || binaryPath.includes(`\\${cmd2}`)) {
|
|
1289
|
+
return BRANDS[id];
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return BRANDS.mindframes;
|
|
797
1293
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1294
|
+
var brand = detectBrand();
|
|
1295
|
+
|
|
1296
|
+
// src/index.ts
|
|
1297
|
+
init_login();
|
|
1298
|
+
|
|
1299
|
+
// src/commands/logout.ts
|
|
1300
|
+
init_config();
|
|
1301
|
+
init_feature_cache();
|
|
1302
|
+
init_output();
|
|
1303
|
+
import { Command as Command2 } from "commander";
|
|
1304
|
+
import { confirm } from "@inquirer/prompts";
|
|
1305
|
+
var logoutCommand = new Command2("logout").description("Log out and clear authentication").option("--all", "Clear all config including API key").action(async (options) => {
|
|
1306
|
+
console.log();
|
|
1307
|
+
const hasTokens = hasOAuthTokens();
|
|
1308
|
+
const hasKey = hasApiKey();
|
|
1309
|
+
if (!hasTokens && !hasKey) {
|
|
1310
|
+
warn("You are not logged in.");
|
|
1311
|
+
console.log();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
if (options.all) {
|
|
808
1315
|
try {
|
|
809
|
-
const
|
|
810
|
-
|
|
1316
|
+
const confirmed = await confirm({
|
|
1317
|
+
message: "Clear all configuration including API key?",
|
|
1318
|
+
default: false
|
|
1319
|
+
});
|
|
1320
|
+
if (confirmed) {
|
|
1321
|
+
clearConfig();
|
|
1322
|
+
invalidateCache();
|
|
1323
|
+
success("All configuration cleared.");
|
|
1324
|
+
} else {
|
|
1325
|
+
info("Cancelled.");
|
|
1326
|
+
}
|
|
811
1327
|
} catch {
|
|
812
|
-
|
|
1328
|
+
info("Cancelled.");
|
|
1329
|
+
}
|
|
1330
|
+
} else {
|
|
1331
|
+
clearOAuthTokens();
|
|
1332
|
+
invalidateCache();
|
|
1333
|
+
success("Logged out successfully.");
|
|
1334
|
+
if (hasKey) {
|
|
1335
|
+
info("Note: Your API key is still configured. Use --all to clear everything.");
|
|
813
1336
|
}
|
|
814
|
-
};
|
|
815
|
-
const table = new Table({
|
|
816
|
-
head: [
|
|
817
|
-
chalk2.cyan("Name"),
|
|
818
|
-
chalk2.cyan("Source"),
|
|
819
|
-
chalk2.cyan("Color"),
|
|
820
|
-
chalk2.cyan("Logo"),
|
|
821
|
-
chalk2.cyan("Default"),
|
|
822
|
-
chalk2.cyan("ID")
|
|
823
|
-
]
|
|
824
|
-
});
|
|
825
|
-
for (const b of sorted) {
|
|
826
|
-
const colorDisplay = b.primaryColor ? `${chalk2.bgHex(b.primaryColor)(" ")} ${b.primaryColor}` : "-";
|
|
827
|
-
table.push([
|
|
828
|
-
b.name,
|
|
829
|
-
formatUrl(b.sourceUrl),
|
|
830
|
-
colorDisplay,
|
|
831
|
-
b.logoUrl ? chalk2.green("\u2713") : chalk2.gray("\u2717"),
|
|
832
|
-
b.isDefault ? chalk2.green("\u2605") : "",
|
|
833
|
-
b.id
|
|
834
|
-
]);
|
|
835
|
-
}
|
|
836
|
-
return table.toString();
|
|
837
|
-
}
|
|
838
|
-
function formatDate(dateStr) {
|
|
839
|
-
try {
|
|
840
|
-
const date = new Date(dateStr);
|
|
841
|
-
return date.toLocaleString();
|
|
842
|
-
} catch {
|
|
843
|
-
return dateStr;
|
|
844
1337
|
}
|
|
845
|
-
}
|
|
846
|
-
function header(text) {
|
|
847
1338
|
console.log();
|
|
848
|
-
|
|
849
|
-
console.log(chalk2.gray("\u2500".repeat(text.length)));
|
|
850
|
-
}
|
|
851
|
-
function keyValue(key, value) {
|
|
852
|
-
console.log(` ${chalk2.gray(key + ":")} ${value ?? "-"}`);
|
|
853
|
-
}
|
|
854
|
-
function progressBar(current, total, width = 30, showPercentage = true) {
|
|
855
|
-
const percentage = Math.min(100, Math.round(current / total * 100));
|
|
856
|
-
const filled = Math.round(width * current / total);
|
|
857
|
-
const empty = width - filled;
|
|
858
|
-
const bar = chalk2.green("\u2588".repeat(filled)) + chalk2.gray("\u2591".repeat(empty));
|
|
859
|
-
return showPercentage ? `[${bar}] ${percentage}%` : `[${bar}]`;
|
|
860
|
-
}
|
|
1339
|
+
});
|
|
861
1340
|
|
|
862
1341
|
// src/commands/config.ts
|
|
863
|
-
|
|
864
|
-
|
|
1342
|
+
init_config();
|
|
1343
|
+
init_auth();
|
|
1344
|
+
init_api();
|
|
1345
|
+
init_feature_cache();
|
|
1346
|
+
init_output();
|
|
1347
|
+
import { Command as Command3 } from "commander";
|
|
1348
|
+
import chalk4 from "chalk";
|
|
1349
|
+
import { input, password, confirm as confirm2, select } from "@inquirer/prompts";
|
|
1350
|
+
import ora2 from "ora";
|
|
1351
|
+
var configCommand = new Command3("config").description("Manage CLI configuration").addCommand(
|
|
1352
|
+
new Command3("init").description("Initialize configuration interactively").action(async () => {
|
|
865
1353
|
console.log();
|
|
866
|
-
console.log(
|
|
867
|
-
console.log(
|
|
1354
|
+
console.log(chalk4.bold("ConceptCraft CLI Configuration"));
|
|
1355
|
+
console.log(chalk4.gray("\u2500".repeat(35)));
|
|
868
1356
|
console.log();
|
|
869
1357
|
try {
|
|
870
1358
|
const apiKey = await password({
|
|
@@ -881,7 +1369,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
881
1369
|
}
|
|
882
1370
|
});
|
|
883
1371
|
setApiKey(apiKey.trim());
|
|
884
|
-
const useCustomUrl = await
|
|
1372
|
+
const useCustomUrl = await confirm2({
|
|
885
1373
|
message: "Use a custom API URL? (default: www.mindframes.app)",
|
|
886
1374
|
default: false
|
|
887
1375
|
});
|
|
@@ -901,7 +1389,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
901
1389
|
setApiUrl(customUrl);
|
|
902
1390
|
}
|
|
903
1391
|
console.log();
|
|
904
|
-
const spinner =
|
|
1392
|
+
const spinner = ora2("Verifying API key...").start();
|
|
905
1393
|
try {
|
|
906
1394
|
const result = await whoami();
|
|
907
1395
|
spinner.succeed("API key verified!");
|
|
@@ -953,18 +1441,18 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
953
1441
|
}
|
|
954
1442
|
})
|
|
955
1443
|
).addCommand(
|
|
956
|
-
new
|
|
1444
|
+
new Command3("show").description("Show current configuration").option("--verify", "Verify API key and show team details").action(async (options) => {
|
|
957
1445
|
const config2 = getConfig();
|
|
958
1446
|
header("Current Configuration");
|
|
959
1447
|
console.log();
|
|
960
|
-
keyValue("API Key", getMaskedApiKey() ??
|
|
1448
|
+
keyValue("API Key", getMaskedApiKey() ?? chalk4.red("Not set"));
|
|
961
1449
|
keyValue("API URL", config2.apiUrl);
|
|
962
|
-
keyValue("Default Team ID", config2.defaultTeamId ??
|
|
1450
|
+
keyValue("Default Team ID", config2.defaultTeamId ?? chalk4.gray("Not set"));
|
|
963
1451
|
console.log();
|
|
964
1452
|
keyValue("Config file", getConfigPath());
|
|
965
1453
|
if (options.verify && config2.apiKey) {
|
|
966
1454
|
console.log();
|
|
967
|
-
const spinner =
|
|
1455
|
+
const spinner = ora2("Verifying...").start();
|
|
968
1456
|
try {
|
|
969
1457
|
const result = await whoami();
|
|
970
1458
|
spinner.succeed("Verified");
|
|
@@ -984,7 +1472,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
984
1472
|
console.log();
|
|
985
1473
|
})
|
|
986
1474
|
).addCommand(
|
|
987
|
-
new
|
|
1475
|
+
new Command3("set").description("Set a configuration value").argument("<key>", "Configuration key (api-key, api-url, team-id)").argument("<value>", "Value to set").action(async (key, value) => {
|
|
988
1476
|
switch (key) {
|
|
989
1477
|
case "api-key":
|
|
990
1478
|
if (!isValidApiKeyFormat(value)) {
|
|
@@ -1013,14 +1501,14 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1013
1501
|
break;
|
|
1014
1502
|
default:
|
|
1015
1503
|
error(`Unknown config key: ${key}`);
|
|
1016
|
-
console.log(
|
|
1504
|
+
console.log(chalk4.gray("Valid keys: api-key, api-url, team-id"));
|
|
1017
1505
|
process.exit(6);
|
|
1018
1506
|
}
|
|
1019
1507
|
})
|
|
1020
1508
|
).addCommand(
|
|
1021
|
-
new
|
|
1509
|
+
new Command3("clear").description("Clear all configuration").action(async () => {
|
|
1022
1510
|
try {
|
|
1023
|
-
const confirmed = await
|
|
1511
|
+
const confirmed = await confirm2({
|
|
1024
1512
|
message: "Are you sure you want to clear all configuration?",
|
|
1025
1513
|
default: false
|
|
1026
1514
|
});
|
|
@@ -1036,8 +1524,8 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1036
1524
|
}
|
|
1037
1525
|
})
|
|
1038
1526
|
).addCommand(
|
|
1039
|
-
new
|
|
1040
|
-
const spinner =
|
|
1527
|
+
new Command3("refresh").description("Refresh cached feature flags").action(async () => {
|
|
1528
|
+
const spinner = ora2("Refreshing feature flags...").start();
|
|
1041
1529
|
try {
|
|
1042
1530
|
await fetchAndCache();
|
|
1043
1531
|
spinner.succeed("Feature flags refreshed");
|
|
@@ -1049,7 +1537,7 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1049
1537
|
}
|
|
1050
1538
|
})
|
|
1051
1539
|
).addCommand(
|
|
1052
|
-
new
|
|
1540
|
+
new Command3("path").description("Show configuration file path").option("--cache", "Show feature cache file path").action((options) => {
|
|
1053
1541
|
if (options.cache) {
|
|
1054
1542
|
console.log(getCachePath());
|
|
1055
1543
|
} else {
|
|
@@ -1059,13 +1547,16 @@ var configCommand = new Command("config").description("Manage CLI configuration"
|
|
|
1059
1547
|
);
|
|
1060
1548
|
|
|
1061
1549
|
// src/commands/create.ts
|
|
1062
|
-
|
|
1063
|
-
|
|
1550
|
+
init_api();
|
|
1551
|
+
init_auth();
|
|
1552
|
+
import { Command as Command4 } from "commander";
|
|
1553
|
+
import chalk6 from "chalk";
|
|
1064
1554
|
|
|
1065
1555
|
// src/lib/streaming.ts
|
|
1556
|
+
init_output();
|
|
1066
1557
|
import { createParser } from "eventsource-parser";
|
|
1067
|
-
import
|
|
1068
|
-
import
|
|
1558
|
+
import chalk5 from "chalk";
|
|
1559
|
+
import ora3 from "ora";
|
|
1069
1560
|
async function handleStream(response, callbacks, options = {}) {
|
|
1070
1561
|
const reader = response.body?.getReader();
|
|
1071
1562
|
if (!reader) {
|
|
@@ -1086,7 +1577,7 @@ async function handleStream(response, callbacks, options = {}) {
|
|
|
1086
1577
|
const parsed = JSON.parse(event.data);
|
|
1087
1578
|
const { type, data } = parsed;
|
|
1088
1579
|
if (options.debug) {
|
|
1089
|
-
console.log(
|
|
1580
|
+
console.log(chalk5.gray(`[SSE] ${type}:`), data);
|
|
1090
1581
|
}
|
|
1091
1582
|
callbacks.onData?.(type, data);
|
|
1092
1583
|
switch (type) {
|
|
@@ -1156,7 +1647,7 @@ async function handleStream(response, callbacks, options = {}) {
|
|
|
1156
1647
|
}
|
|
1157
1648
|
} catch (e) {
|
|
1158
1649
|
if (options.debug) {
|
|
1159
|
-
console.log(
|
|
1650
|
+
console.log(chalk5.gray("[SSE Raw]"), event.data);
|
|
1160
1651
|
}
|
|
1161
1652
|
}
|
|
1162
1653
|
}
|
|
@@ -1181,9 +1672,9 @@ async function streamWithProgress(response, topic, options) {
|
|
|
1181
1672
|
const useTTY = isTTY() && !options.noStream && !options.quiet && !options.json;
|
|
1182
1673
|
if (!options.json) {
|
|
1183
1674
|
console.log();
|
|
1184
|
-
console.log(
|
|
1675
|
+
console.log(chalk5.bold(`Creating presentation: "${topic}"`));
|
|
1185
1676
|
console.log(
|
|
1186
|
-
|
|
1677
|
+
chalk5.gray(
|
|
1187
1678
|
`Quality: ${options.mode} | Tone: ${options.tone ?? "professional"} | Slides: ${options.slideCount} | Language: ${options.language}`
|
|
1188
1679
|
)
|
|
1189
1680
|
);
|
|
@@ -1234,10 +1725,10 @@ async function streamWithProgress(response, topic, options) {
|
|
|
1234
1725
|
const tokens = formatTokens(currentTokens);
|
|
1235
1726
|
const elapsed = formatTime(getElapsed()).padStart(5, " ");
|
|
1236
1727
|
const remaining = getRemaining().padStart(5, " ");
|
|
1237
|
-
return `${bar} ${
|
|
1728
|
+
return `${bar} ${chalk5.bold(pct)}${chalk5.gray("%")} ${chalk5.cyan(phase)} ${chalk5.gray(slides)} ${chalk5.yellow(tokens)} ${chalk5.gray(elapsed)} ${chalk5.green(remaining + " left")}`;
|
|
1238
1729
|
};
|
|
1239
1730
|
if (useTTY) {
|
|
1240
|
-
spinner =
|
|
1731
|
+
spinner = ora3({
|
|
1241
1732
|
text: buildProgressLine(),
|
|
1242
1733
|
spinner: "dots"
|
|
1243
1734
|
}).start();
|
|
@@ -1273,9 +1764,9 @@ async function streamWithProgress(response, topic, options) {
|
|
|
1273
1764
|
onError: (error2) => {
|
|
1274
1765
|
if (timer) clearInterval(timer);
|
|
1275
1766
|
if (spinner) {
|
|
1276
|
-
spinner.fail(
|
|
1767
|
+
spinner.fail(chalk5.red(`Error: ${error2}`));
|
|
1277
1768
|
} else if (!options.json) {
|
|
1278
|
-
console.error(
|
|
1769
|
+
console.error(chalk5.red(`Error: ${error2}`));
|
|
1279
1770
|
}
|
|
1280
1771
|
},
|
|
1281
1772
|
onComplete: (data) => {
|
|
@@ -1324,6 +1815,7 @@ async function streamTextContent(response, options = {}) {
|
|
|
1324
1815
|
}
|
|
1325
1816
|
|
|
1326
1817
|
// src/commands/create.ts
|
|
1818
|
+
init_output();
|
|
1327
1819
|
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
1328
1820
|
import { resolve } from "path";
|
|
1329
1821
|
var VALID_GOALS = [
|
|
@@ -1334,7 +1826,7 @@ var VALID_GOALS = [
|
|
|
1334
1826
|
"entertain",
|
|
1335
1827
|
"report"
|
|
1336
1828
|
];
|
|
1337
|
-
var createCommand = new
|
|
1829
|
+
var createCommand = new Command4("create").description("Create a new presentation").argument("<topic>", "The topic or title for the presentation").option("-n, --slides <count>", "Number of slides (1-20)", "10").option(
|
|
1338
1830
|
"-m, --mode <mode>",
|
|
1339
1831
|
"Generation quality mode (best, balanced, fast, ultrafast, instant)",
|
|
1340
1832
|
"balanced"
|
|
@@ -1375,67 +1867,83 @@ var createCommand = new Command2("create").description("Create a new presentatio
|
|
|
1375
1867
|
"--thinking-depth <depth>",
|
|
1376
1868
|
"AI thinking depth (quick, moderate, deep, profound)",
|
|
1377
1869
|
"moderate"
|
|
1378
|
-
).option(
|
|
1870
|
+
).option(
|
|
1871
|
+
"--theme <preset>",
|
|
1872
|
+
"Color theme preset (blue, violet, rose, orange, green)"
|
|
1873
|
+
).option("--primary-color <hex>", "Primary color in hex (e.g., #0066CC)").option("--secondary-color <hex>", "Secondary color in hex").option("--accent-color <hex>", "Accent color in hex").option("--background-color <hex>", "Background color in hex").option("--foreground-color <hex>", "Foreground/text color in hex").option(
|
|
1874
|
+
"--decorations <style>",
|
|
1875
|
+
"Background decoration style (none, waves-bottom-left, waves-top-right, blob-corners, minimal)"
|
|
1876
|
+
).option("-o, --output <format>", "Output format (human, json, quiet)", "human").option("--no-stream", "Wait for completion without progress display").option("--debug", "Enable debug logging").option("--team-id <id>", "Team ID to create presentation for (switch teams)").option("--open", "Open the presentation in browser after creation").addHelpText(
|
|
1379
1877
|
"after",
|
|
1380
1878
|
`
|
|
1381
|
-
${
|
|
1879
|
+
${chalk6.bold("Recommended Usage:")}
|
|
1382
1880
|
Always specify slides (-n), mode (-m), audience, and context for best results.
|
|
1383
1881
|
|
|
1384
|
-
${
|
|
1385
|
-
${
|
|
1882
|
+
${chalk6.bold("Examples:")}
|
|
1883
|
+
${chalk6.gray("# Content from PDF + style from image reference")}
|
|
1386
1884
|
$ conceptcraft create "Quarterly Report" \\
|
|
1387
1885
|
--file ./report.pdf \\
|
|
1388
1886
|
--reference-url https://example.com/style-template.png \\
|
|
1389
1887
|
-n 12 -m best --audience "Executive team"
|
|
1390
1888
|
|
|
1391
|
-
${
|
|
1889
|
+
${chalk6.gray("# Upload content files (PDF, PPTX, DOCX)")}
|
|
1392
1890
|
$ conceptcraft create "Product Demo" \\
|
|
1393
1891
|
--file ./existing-deck.pptx --file ./specs.pdf \\
|
|
1394
1892
|
--goal persuade --audience "Enterprise buyers"
|
|
1395
1893
|
|
|
1396
|
-
${
|
|
1894
|
+
${chalk6.gray("# Inline context with custom styling")}
|
|
1397
1895
|
$ conceptcraft create "Q4 Business Review" \\
|
|
1398
1896
|
-n 12 -m best --goal inform \\
|
|
1399
1897
|
--context "Revenue: $50M (+25% YoY), EBITDA: $8M" \\
|
|
1400
1898
|
--reference-url https://example.com/brand-style.jpg
|
|
1401
1899
|
|
|
1402
|
-
${
|
|
1900
|
+
${chalk6.gray("# Research presentation from URLs")}
|
|
1403
1901
|
$ conceptcraft create "AI Industry Trends" \\
|
|
1404
1902
|
-n 10 -m best -t educational \\
|
|
1405
1903
|
--sources https://example.com/ai-report.pdf
|
|
1406
1904
|
|
|
1407
|
-
${
|
|
1905
|
+
${chalk6.gray("# Pipe content from another command")}
|
|
1408
1906
|
$ cat meeting-notes.md | conceptcraft create "Meeting Summary" \\
|
|
1409
1907
|
-n 6 -m balanced --goal inform
|
|
1410
1908
|
|
|
1411
|
-
${
|
|
1909
|
+
${chalk6.bold("Content Options (what to include in slides):")}
|
|
1412
1910
|
-f, --file <paths...> Upload files for content extraction (PDF, PPTX, DOCX)
|
|
1413
1911
|
-c, --context <text> Inline text (key facts, data points)
|
|
1414
1912
|
--context-file <path> Read content from file (markdown, text, JSON)
|
|
1415
1913
|
--sources <urls...> URLs to scrape for content
|
|
1416
1914
|
--stdin Pipe content from another command
|
|
1417
1915
|
|
|
1418
|
-
${
|
|
1419
|
-
--reference-url <url> ${
|
|
1916
|
+
${chalk6.bold("Styling Options (how slides look):")}
|
|
1917
|
+
--reference-url <url> ${chalk6.yellow("Image URL to guide visual style")} (colors, layout, feel)
|
|
1420
1918
|
--styling <mode> freeform, brand-only, style-only, no-styling
|
|
1421
1919
|
-b, --brand <id> Apply saved brand settings
|
|
1422
1920
|
|
|
1423
|
-
${
|
|
1424
|
-
${
|
|
1921
|
+
${chalk6.gray("NOTE: --file uploads provide CONTENT (data, text to include)")}
|
|
1922
|
+
${chalk6.gray(" --reference-url provides STYLE (visual design inspiration)")}
|
|
1425
1923
|
|
|
1426
|
-
${
|
|
1924
|
+
${chalk6.bold("Goal Options:")}
|
|
1427
1925
|
-g, --goal <type> inform, persuade, train, learn, entertain, report
|
|
1428
1926
|
--custom-goal <text> Custom goal description
|
|
1429
1927
|
|
|
1430
|
-
${
|
|
1928
|
+
${chalk6.bold("Theme Options:")}
|
|
1929
|
+
--theme <preset> Preset color scheme (blue, violet, rose, orange, green)
|
|
1930
|
+
--primary-color <hex> Primary brand color (e.g., #0066CC)
|
|
1931
|
+
--secondary-color <hex> Secondary color for accents
|
|
1932
|
+
--decorations <style> Background style (none, waves-bottom-left, waves-top-right, blob-corners, minimal)
|
|
1933
|
+
|
|
1934
|
+
${chalk6.gray("Example: Apply corporate colors")}
|
|
1935
|
+
$ conceptcraft create "Brand Deck" \\
|
|
1936
|
+
--theme blue --primary-color "#1E40AF" --decorations waves-bottom-left
|
|
1937
|
+
|
|
1938
|
+
${chalk6.bold("Mode Reference:")}
|
|
1431
1939
|
best Highest quality, thorough research (recommended for important decks)
|
|
1432
1940
|
balanced Good quality with reasonable speed (default)
|
|
1433
1941
|
fast Quick generation, less refinement
|
|
1434
1942
|
ultrafast Very quick, minimal processing
|
|
1435
|
-
instant Fastest, basic output
|
|
1943
|
+
instant Fastest, basic output (theme options work best with instant mode)
|
|
1436
1944
|
`
|
|
1437
1945
|
).action(async (topic, options) => {
|
|
1438
|
-
requireAuth();
|
|
1946
|
+
await requireAuth();
|
|
1439
1947
|
const slideCount = parseInt(options.slides, 10);
|
|
1440
1948
|
if (isNaN(slideCount) || slideCount < 1 || slideCount > 20) {
|
|
1441
1949
|
error("Slide count must be between 1 and 20");
|
|
@@ -1510,15 +2018,68 @@ ${chalk5.bold("Mode Reference:")}
|
|
|
1510
2018
|
);
|
|
1511
2019
|
process.exit(6);
|
|
1512
2020
|
}
|
|
2021
|
+
const validThemePresets = ["blue", "violet", "rose", "orange", "green"];
|
|
2022
|
+
if (options.theme && !validThemePresets.includes(options.theme)) {
|
|
2023
|
+
error(
|
|
2024
|
+
`Invalid theme: ${options.theme}. Valid themes: ${validThemePresets.join(", ")}`
|
|
2025
|
+
);
|
|
2026
|
+
process.exit(6);
|
|
2027
|
+
}
|
|
2028
|
+
const validDecorations = [
|
|
2029
|
+
"none",
|
|
2030
|
+
"waves-bottom-left",
|
|
2031
|
+
"waves-top-right",
|
|
2032
|
+
"blob-corners",
|
|
2033
|
+
"minimal"
|
|
2034
|
+
];
|
|
2035
|
+
if (options.decorations && !validDecorations.includes(options.decorations)) {
|
|
2036
|
+
error(
|
|
2037
|
+
`Invalid decorations: ${options.decorations}. Valid styles: ${validDecorations.join(", ")}`
|
|
2038
|
+
);
|
|
2039
|
+
process.exit(6);
|
|
2040
|
+
}
|
|
2041
|
+
const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
2042
|
+
const colorOptions = [
|
|
2043
|
+
{ name: "primary-color", value: options.primaryColor },
|
|
2044
|
+
{ name: "secondary-color", value: options.secondaryColor },
|
|
2045
|
+
{ name: "accent-color", value: options.accentColor },
|
|
2046
|
+
{ name: "background-color", value: options.backgroundColor },
|
|
2047
|
+
{ name: "foreground-color", value: options.foregroundColor }
|
|
2048
|
+
];
|
|
2049
|
+
for (const { name, value } of colorOptions) {
|
|
2050
|
+
if (value && !hexColorRegex.test(value)) {
|
|
2051
|
+
error(`Invalid ${name}: ${value}. Must be a hex color (e.g., #0066CC or #06C)`);
|
|
2052
|
+
process.exit(6);
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
let theme;
|
|
2056
|
+
const hasCustomColors = options.primaryColor || options.secondaryColor || options.accentColor || options.backgroundColor || options.foregroundColor;
|
|
2057
|
+
if (options.theme || hasCustomColors || options.decorations) {
|
|
2058
|
+
theme = {};
|
|
2059
|
+
if (options.theme) {
|
|
2060
|
+
theme.preset = options.theme;
|
|
2061
|
+
}
|
|
2062
|
+
if (hasCustomColors) {
|
|
2063
|
+
theme.custom = {};
|
|
2064
|
+
if (options.primaryColor) theme.custom.primary = options.primaryColor;
|
|
2065
|
+
if (options.secondaryColor) theme.custom.secondary = options.secondaryColor;
|
|
2066
|
+
if (options.accentColor) theme.custom.accent = options.accentColor;
|
|
2067
|
+
if (options.backgroundColor) theme.custom.background = options.backgroundColor;
|
|
2068
|
+
if (options.foregroundColor) theme.custom.foreground = options.foregroundColor;
|
|
2069
|
+
}
|
|
2070
|
+
if (options.decorations) {
|
|
2071
|
+
theme.decorations = options.decorations;
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
1513
2074
|
try {
|
|
1514
2075
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1515
|
-
process.stdout.write(
|
|
2076
|
+
process.stdout.write(chalk6.gray("Checking generation limits... "));
|
|
1516
2077
|
}
|
|
1517
2078
|
const limits = await validateGeneration(options.mode, slideCount, options.teamId);
|
|
1518
2079
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1519
|
-
console.log(
|
|
2080
|
+
console.log(chalk6.green("\u2713"));
|
|
1520
2081
|
console.log(
|
|
1521
|
-
|
|
2082
|
+
chalk6.gray(
|
|
1522
2083
|
` Plan: ${limits.planName} | ${options.mode}: ${limits.remaining[options.mode]}/${limits.limits[options.mode]} remaining`
|
|
1523
2084
|
)
|
|
1524
2085
|
);
|
|
@@ -1532,7 +2093,7 @@ ${chalk5.bold("Mode Reference:")}
|
|
|
1532
2093
|
})
|
|
1533
2094
|
);
|
|
1534
2095
|
} else {
|
|
1535
|
-
console.log(
|
|
2096
|
+
console.log(chalk6.red("\u2717"));
|
|
1536
2097
|
error(err instanceof Error ? err.message : String(err));
|
|
1537
2098
|
}
|
|
1538
2099
|
process.exit(err.exitCode ?? 1);
|
|
@@ -1547,7 +2108,7 @@ ${chalk5.bold("Mode Reference:")}
|
|
|
1547
2108
|
}
|
|
1548
2109
|
}
|
|
1549
2110
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1550
|
-
console.log(
|
|
2111
|
+
console.log(chalk6.gray(`
|
|
1551
2112
|
Uploading ${options.file.length} file(s)...`));
|
|
1552
2113
|
}
|
|
1553
2114
|
try {
|
|
@@ -1556,13 +2117,13 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1556
2117
|
(completed, total, fileName) => {
|
|
1557
2118
|
if (options.output !== "json" && options.output !== "quiet" && fileName) {
|
|
1558
2119
|
process.stdout.write(
|
|
1559
|
-
`\r ${
|
|
2120
|
+
`\r ${chalk6.cyan("\u2B06")} Uploading: ${fileName} (${completed + 1}/${total})`
|
|
1560
2121
|
);
|
|
1561
2122
|
}
|
|
1562
2123
|
}
|
|
1563
2124
|
);
|
|
1564
2125
|
if (options.output !== "json" && options.output !== "quiet") {
|
|
1565
|
-
console.log(`\r ${
|
|
2126
|
+
console.log(`\r ${chalk6.green("\u2713")} Uploaded ${uploadedFiles.length} file(s) `);
|
|
1566
2127
|
}
|
|
1567
2128
|
} catch (err) {
|
|
1568
2129
|
error(
|
|
@@ -1601,16 +2162,16 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1601
2162
|
if (!hasContext) {
|
|
1602
2163
|
error("Context is required to create a presentation.");
|
|
1603
2164
|
console.log();
|
|
1604
|
-
console.log(
|
|
1605
|
-
console.log(
|
|
1606
|
-
console.log(
|
|
1607
|
-
console.log(
|
|
1608
|
-
console.log(
|
|
1609
|
-
console.log(
|
|
2165
|
+
console.log(chalk6.gray("Provide context using one of these methods:"));
|
|
2166
|
+
console.log(chalk6.gray(" -f, --file <paths...> Upload files (PDF, PPTX, images)"));
|
|
2167
|
+
console.log(chalk6.gray(" -c, --context <text> Direct text context"));
|
|
2168
|
+
console.log(chalk6.gray(" --context-file <path> Read from a file"));
|
|
2169
|
+
console.log(chalk6.gray(" --sources <urls...> URLs to scrape"));
|
|
2170
|
+
console.log(chalk6.gray(" cat file | conceptcraft Pipe content"));
|
|
1610
2171
|
console.log();
|
|
1611
|
-
console.log(
|
|
1612
|
-
console.log(
|
|
1613
|
-
console.log(
|
|
2172
|
+
console.log(chalk6.gray("Example:"));
|
|
2173
|
+
console.log(chalk6.cyan(' conceptcraft create "Q4 Report" --file ./report.pdf'));
|
|
2174
|
+
console.log(chalk6.cyan(' conceptcraft create "Q4 Report" --context "Revenue: $10M, Growth: 25%"'));
|
|
1614
2175
|
process.exit(6);
|
|
1615
2176
|
}
|
|
1616
2177
|
try {
|
|
@@ -1633,7 +2194,8 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1633
2194
|
uploadedFiles: uploadedFiles.length > 0 ? uploadedFiles : void 0,
|
|
1634
2195
|
goal: options.goal,
|
|
1635
2196
|
customGoal: options.customGoal,
|
|
1636
|
-
teamId: options.teamId
|
|
2197
|
+
teamId: options.teamId,
|
|
2198
|
+
theme
|
|
1637
2199
|
});
|
|
1638
2200
|
const result = await streamWithProgress(response, topic, {
|
|
1639
2201
|
slideCount,
|
|
@@ -1684,7 +2246,11 @@ Uploading ${options.file.length} file(s)...`));
|
|
|
1684
2246
|
console.log();
|
|
1685
2247
|
const viewUrl = buildViewUrl(result.slug, options.language);
|
|
1686
2248
|
if (viewUrl !== "N/A") {
|
|
1687
|
-
console.log(
|
|
2249
|
+
console.log(chalk6.bold(" Open: ") + chalk6.cyan.underline(viewUrl));
|
|
2250
|
+
if (options.open) {
|
|
2251
|
+
const open2 = await import("open");
|
|
2252
|
+
await open2.default(viewUrl);
|
|
2253
|
+
}
|
|
1688
2254
|
}
|
|
1689
2255
|
console.log();
|
|
1690
2256
|
}
|
|
@@ -1731,13 +2297,17 @@ function isUrl(str) {
|
|
|
1731
2297
|
}
|
|
1732
2298
|
|
|
1733
2299
|
// src/commands/list.ts
|
|
1734
|
-
|
|
1735
|
-
|
|
2300
|
+
init_api();
|
|
2301
|
+
init_auth();
|
|
2302
|
+
init_config();
|
|
2303
|
+
init_output();
|
|
2304
|
+
import { Command as Command5 } from "commander";
|
|
2305
|
+
var listCommand = new Command5("list").description("List presentations").option("-n, --limit <count>", "Number of results to return", "20").option(
|
|
1736
2306
|
"-f, --format <format>",
|
|
1737
2307
|
"Output format (table, json, ids)",
|
|
1738
2308
|
"table"
|
|
1739
2309
|
).option("--sort <field>", "Sort by field (created, updated, title)").option("--team-id <id>", "Team ID (uses default if not specified)").option("-d, --detail", "Show detailed output including URLs").option("-l, --language <lang>", "Language for URLs", "en").action(async (options) => {
|
|
1740
|
-
requireAuth();
|
|
2310
|
+
await requireAuth();
|
|
1741
2311
|
const limit = parseInt(options.limit, 10);
|
|
1742
2312
|
if (isNaN(limit) || limit < 1) {
|
|
1743
2313
|
error("Invalid limit value");
|
|
@@ -1794,14 +2364,17 @@ var listCommand = new Command3("list").description("List presentations").option(
|
|
|
1794
2364
|
});
|
|
1795
2365
|
|
|
1796
2366
|
// src/commands/get.ts
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
2367
|
+
init_api();
|
|
2368
|
+
init_auth();
|
|
2369
|
+
init_output();
|
|
2370
|
+
import { Command as Command6 } from "commander";
|
|
2371
|
+
import chalk7 from "chalk";
|
|
2372
|
+
var getCommand = new Command6("get").description("Get presentation details").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option(
|
|
1800
2373
|
"-f, --format <format>",
|
|
1801
2374
|
"Output format (summary, full, json)",
|
|
1802
2375
|
"summary"
|
|
1803
2376
|
).option("--include <fields>", "Fields to include (comma-separated: slides,styling)").option("-l, --language <lang>", "Language for view URL", "en").action(async (slug, options) => {
|
|
1804
|
-
requireAuth();
|
|
2377
|
+
await requireAuth();
|
|
1805
2378
|
try {
|
|
1806
2379
|
const presentation = await getPresentation(slug);
|
|
1807
2380
|
if (options.format === "json") {
|
|
@@ -1813,7 +2386,7 @@ var getCommand = new Command4("get").description("Get presentation details").arg
|
|
|
1813
2386
|
console.log();
|
|
1814
2387
|
keyValue("Slug", presentation.slug);
|
|
1815
2388
|
keyValue("Title", presentation.title || "Untitled");
|
|
1816
|
-
keyValue("Description", presentation.description ||
|
|
2389
|
+
keyValue("Description", presentation.description || chalk7.gray("None"));
|
|
1817
2390
|
keyValue("Slides", String(presentation.numberOfSlides || "-"));
|
|
1818
2391
|
keyValue("Mode", presentation.mode || "-");
|
|
1819
2392
|
keyValue("Created", formatDate(presentation.createdAt));
|
|
@@ -1822,11 +2395,11 @@ var getCommand = new Command4("get").description("Get presentation details").arg
|
|
|
1822
2395
|
}
|
|
1823
2396
|
console.log();
|
|
1824
2397
|
if (viewUrl !== "N/A") {
|
|
1825
|
-
console.log(
|
|
2398
|
+
console.log(chalk7.bold(" Open: ") + chalk7.cyan.underline(viewUrl));
|
|
1826
2399
|
}
|
|
1827
2400
|
console.log();
|
|
1828
2401
|
if (options.format === "full" && options.include?.includes("slides")) {
|
|
1829
|
-
console.log(
|
|
2402
|
+
console.log(chalk7.gray("(Full slide content available in JSON format)"));
|
|
1830
2403
|
console.log();
|
|
1831
2404
|
}
|
|
1832
2405
|
} catch (err) {
|
|
@@ -1836,13 +2409,16 @@ var getCommand = new Command4("get").description("Get presentation details").arg
|
|
|
1836
2409
|
});
|
|
1837
2410
|
|
|
1838
2411
|
// src/commands/delete.ts
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2412
|
+
init_api();
|
|
2413
|
+
init_auth();
|
|
2414
|
+
init_output();
|
|
2415
|
+
import { Command as Command7 } from "commander";
|
|
2416
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
2417
|
+
var deleteCommand = new Command7("delete").description("Delete a presentation").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option("-f, --force", "Skip confirmation prompt").option("-q, --quiet", "Suppress output").action(async (slug, options) => {
|
|
2418
|
+
await requireAuth();
|
|
1843
2419
|
if (!options.force) {
|
|
1844
2420
|
try {
|
|
1845
|
-
const confirmed = await
|
|
2421
|
+
const confirmed = await confirm3({
|
|
1846
2422
|
message: `Are you sure you want to delete presentation "${slug}"?`,
|
|
1847
2423
|
default: false
|
|
1848
2424
|
});
|
|
@@ -1871,13 +2447,16 @@ var deleteCommand = new Command5("delete").description("Delete a presentation").
|
|
|
1871
2447
|
});
|
|
1872
2448
|
|
|
1873
2449
|
// src/commands/export.ts
|
|
1874
|
-
|
|
2450
|
+
init_api();
|
|
2451
|
+
init_auth();
|
|
2452
|
+
init_output();
|
|
2453
|
+
import { Command as Command8 } from "commander";
|
|
1875
2454
|
import { writeFile } from "fs/promises";
|
|
1876
2455
|
import { resolve as resolve2 } from "path";
|
|
1877
|
-
import
|
|
1878
|
-
var exportCommand = new
|
|
1879
|
-
requireAuth();
|
|
1880
|
-
const spinner =
|
|
2456
|
+
import ora4 from "ora";
|
|
2457
|
+
var exportCommand = new Command8("export").description("Export a presentation to ZIP").argument("<slug>", "Presentation slug (e.g., my-presentation-v1-abc123)").option("-o, --output <path>", "Output file path").option("--include-images", "Include images (default: true)", true).option("--include-branding", "Include branding (default: true)", true).option("--no-external", "Skip external image downloads").action(async (slug, options) => {
|
|
2458
|
+
await requireAuth();
|
|
2459
|
+
const spinner = ora4("Fetching presentation...").start();
|
|
1881
2460
|
try {
|
|
1882
2461
|
const presentation = await getPresentation(slug);
|
|
1883
2462
|
const title = presentation.title || slug;
|
|
@@ -1912,14 +2491,18 @@ function formatBytes(bytes) {
|
|
|
1912
2491
|
}
|
|
1913
2492
|
|
|
1914
2493
|
// src/commands/import.ts
|
|
1915
|
-
|
|
2494
|
+
init_api();
|
|
2495
|
+
init_auth();
|
|
2496
|
+
init_output();
|
|
2497
|
+
init_config();
|
|
2498
|
+
import { Command as Command9 } from "commander";
|
|
1916
2499
|
import { readFile } from "fs/promises";
|
|
1917
2500
|
import { resolve as resolve3, basename as basename2 } from "path";
|
|
1918
|
-
import
|
|
1919
|
-
var cmd = new
|
|
2501
|
+
import ora5 from "ora";
|
|
2502
|
+
var cmd = new Command9("import").description("Import a presentation from ZIP (admin only)").argument("<file>", "Path to ZIP file");
|
|
1920
2503
|
cmd._hidden = true;
|
|
1921
2504
|
var importCommand = cmd.option("--dry-run", "Validate without importing").option("--overwrite", "Overwrite existing presentations").option("--remap-ids", "Generate new IDs (default: true)", true).action(async (file, options) => {
|
|
1922
|
-
requireAuth();
|
|
2505
|
+
await requireAuth();
|
|
1923
2506
|
try {
|
|
1924
2507
|
const me = await whoami();
|
|
1925
2508
|
if (me.user.role !== "admin") {
|
|
@@ -1936,7 +2519,7 @@ var importCommand = cmd.option("--dry-run", "Validate without importing").option
|
|
|
1936
2519
|
error("File must be a ZIP archive");
|
|
1937
2520
|
process.exit(6);
|
|
1938
2521
|
}
|
|
1939
|
-
const spinner =
|
|
2522
|
+
const spinner = ora5("Reading file...").start();
|
|
1940
2523
|
try {
|
|
1941
2524
|
const fileBuffer = await readFile(filePath);
|
|
1942
2525
|
if (options.dryRun) {
|
|
@@ -2001,12 +2584,16 @@ var importCommand = cmd.option("--dry-run", "Validate without importing").option
|
|
|
2001
2584
|
});
|
|
2002
2585
|
|
|
2003
2586
|
// src/commands/branding.ts
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2587
|
+
init_api();
|
|
2588
|
+
init_auth();
|
|
2589
|
+
init_config();
|
|
2590
|
+
init_output();
|
|
2591
|
+
import { Command as Command10 } from "commander";
|
|
2592
|
+
import chalk8 from "chalk";
|
|
2593
|
+
import ora6 from "ora";
|
|
2594
|
+
var brandingCommand = new Command10("branding").description("Manage brand profiles").addCommand(
|
|
2595
|
+
new Command10("list").description("List brand profiles").option("-f, --format <format>", "Output format (table, json)", "table").action(async (options) => {
|
|
2596
|
+
await requireAuth();
|
|
2010
2597
|
try {
|
|
2011
2598
|
const result = await listBrandings();
|
|
2012
2599
|
if (result.brandings.length === 0) {
|
|
@@ -2016,7 +2603,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2016
2603
|
info("No brand profiles found");
|
|
2017
2604
|
console.log();
|
|
2018
2605
|
console.log(
|
|
2019
|
-
|
|
2606
|
+
chalk8.gray(
|
|
2020
2607
|
"Create one with: conceptcraft branding extract <url>"
|
|
2021
2608
|
)
|
|
2022
2609
|
);
|
|
@@ -2034,8 +2621,8 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2034
2621
|
}
|
|
2035
2622
|
})
|
|
2036
2623
|
).addCommand(
|
|
2037
|
-
new
|
|
2038
|
-
requireAuth();
|
|
2624
|
+
new Command10("get").description("Get brand profile details").argument("<id>", "Brand profile ID").option("-f, --format <format>", "Output format (summary, json)", "summary").action(async (id, options) => {
|
|
2625
|
+
await requireAuth();
|
|
2039
2626
|
try {
|
|
2040
2627
|
const brand2 = await getBranding(id);
|
|
2041
2628
|
if (options.format === "json") {
|
|
@@ -2045,36 +2632,36 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2045
2632
|
console.log();
|
|
2046
2633
|
keyValue("ID", brand2.id);
|
|
2047
2634
|
keyValue("Name", brand2.name);
|
|
2048
|
-
keyValue("Source URL", brand2.sourceUrl ??
|
|
2049
|
-
keyValue("Default", brand2.isDefault ?
|
|
2635
|
+
keyValue("Source URL", brand2.sourceUrl ?? chalk8.gray("None"));
|
|
2636
|
+
keyValue("Default", brand2.isDefault ? chalk8.green("Yes") : "No");
|
|
2050
2637
|
if (brand2.createdAt) {
|
|
2051
2638
|
keyValue("Created", new Date(brand2.createdAt).toLocaleString());
|
|
2052
2639
|
}
|
|
2053
2640
|
console.log();
|
|
2054
2641
|
if (brand2.primaryColor || brand2.colors.length > 0) {
|
|
2055
|
-
console.log(
|
|
2642
|
+
console.log(chalk8.bold(" Colors"));
|
|
2056
2643
|
if (brand2.primaryColor) {
|
|
2057
|
-
const swatch =
|
|
2644
|
+
const swatch = chalk8.bgHex(brand2.primaryColor)(" ");
|
|
2058
2645
|
console.log(` Primary: ${swatch} ${brand2.primaryColor}`);
|
|
2059
2646
|
}
|
|
2060
2647
|
if (brand2.colors.length > 0) {
|
|
2061
2648
|
console.log(" Palette:");
|
|
2062
2649
|
for (const c of brand2.colors.slice(0, 10)) {
|
|
2063
|
-
const swatch =
|
|
2064
|
-
const role = c.role ?
|
|
2650
|
+
const swatch = chalk8.bgHex(c.hex)(" ");
|
|
2651
|
+
const role = c.role ? chalk8.gray(` (${c.role})`) : "";
|
|
2065
2652
|
console.log(` ${swatch} ${c.hex}${role}`);
|
|
2066
2653
|
}
|
|
2067
2654
|
}
|
|
2068
2655
|
console.log();
|
|
2069
2656
|
}
|
|
2070
2657
|
if (brand2.logoUrl) {
|
|
2071
|
-
console.log(
|
|
2658
|
+
console.log(chalk8.bold(" Logo"));
|
|
2072
2659
|
console.log(` ${brand2.logoUrl}`);
|
|
2073
2660
|
console.log();
|
|
2074
2661
|
}
|
|
2075
2662
|
const typo = brand2.typography;
|
|
2076
2663
|
if (typo && Object.keys(typo).length > 0) {
|
|
2077
|
-
console.log(
|
|
2664
|
+
console.log(chalk8.bold(" Typography"));
|
|
2078
2665
|
if (typo.primary || typo.headings) {
|
|
2079
2666
|
console.log(` Headings: ${typo.primary || typo.headings || "-"}`);
|
|
2080
2667
|
}
|
|
@@ -2084,7 +2671,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2084
2671
|
console.log();
|
|
2085
2672
|
}
|
|
2086
2673
|
if (brand2.confidence || brand2.extractionMethod) {
|
|
2087
|
-
console.log(
|
|
2674
|
+
console.log(chalk8.bold(" Extraction"));
|
|
2088
2675
|
if (brand2.confidence) {
|
|
2089
2676
|
console.log(` Confidence: ${Math.round(brand2.confidence * 100)}%`);
|
|
2090
2677
|
}
|
|
@@ -2093,7 +2680,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2093
2680
|
}
|
|
2094
2681
|
console.log();
|
|
2095
2682
|
}
|
|
2096
|
-
console.log(
|
|
2683
|
+
console.log(chalk8.gray(" Use --format json for full details"));
|
|
2097
2684
|
console.log();
|
|
2098
2685
|
}
|
|
2099
2686
|
} catch (err) {
|
|
@@ -2102,8 +2689,8 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2102
2689
|
}
|
|
2103
2690
|
})
|
|
2104
2691
|
).addCommand(
|
|
2105
|
-
new
|
|
2106
|
-
requireAuth();
|
|
2692
|
+
new Command10("extract").description("Extract brand profile from a website").argument("<url>", "Website URL to extract branding from").option("--team-id <id>", "Team ID").option("--no-save", "Extract without saving").action(async (url, options) => {
|
|
2693
|
+
await requireAuth();
|
|
2107
2694
|
try {
|
|
2108
2695
|
new URL(url.startsWith("http") ? url : `https://${url}`);
|
|
2109
2696
|
} catch {
|
|
@@ -2112,7 +2699,7 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2112
2699
|
}
|
|
2113
2700
|
const fullUrl = url.startsWith("http") ? url : `https://${url}`;
|
|
2114
2701
|
const teamId = options.teamId ?? getDefaultTeamId();
|
|
2115
|
-
const spinner =
|
|
2702
|
+
const spinner = ora6({ text: `Extracting branding from ${fullUrl}...`, stream: process.stdout }).start();
|
|
2116
2703
|
try {
|
|
2117
2704
|
const result = await extractBranding(fullUrl, teamId);
|
|
2118
2705
|
const brand2 = await getBranding(result.id);
|
|
@@ -2125,28 +2712,28 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2125
2712
|
keyValue("Source URL", brand2.sourceUrl ?? fullUrl);
|
|
2126
2713
|
console.log();
|
|
2127
2714
|
if (brand2.primaryColor || brand2.colors.length > 0) {
|
|
2128
|
-
console.log(
|
|
2715
|
+
console.log(chalk8.bold(" Colors"));
|
|
2129
2716
|
if (brand2.primaryColor) {
|
|
2130
|
-
const swatch =
|
|
2717
|
+
const swatch = chalk8.bgHex(brand2.primaryColor)(" ");
|
|
2131
2718
|
console.log(` Primary: ${swatch} ${brand2.primaryColor}`);
|
|
2132
2719
|
}
|
|
2133
2720
|
if (brand2.colors.length > 0) {
|
|
2134
2721
|
console.log(" Palette:");
|
|
2135
2722
|
for (const c of brand2.colors.slice(0, 6)) {
|
|
2136
|
-
const swatch =
|
|
2137
|
-
const role = c.role ?
|
|
2723
|
+
const swatch = chalk8.bgHex(c.hex)(" ");
|
|
2724
|
+
const role = c.role ? chalk8.gray(` (${c.role})`) : "";
|
|
2138
2725
|
console.log(` ${swatch} ${c.hex}${role}`);
|
|
2139
2726
|
}
|
|
2140
2727
|
}
|
|
2141
2728
|
console.log();
|
|
2142
2729
|
}
|
|
2143
2730
|
if (brand2.logoUrl) {
|
|
2144
|
-
console.log(
|
|
2731
|
+
console.log(chalk8.bold(" Logo"));
|
|
2145
2732
|
console.log(` ${brand2.logoUrl}`);
|
|
2146
2733
|
console.log();
|
|
2147
2734
|
}
|
|
2148
2735
|
if (brand2.confidence) {
|
|
2149
|
-
console.log(
|
|
2736
|
+
console.log(chalk8.bold(" Extraction"));
|
|
2150
2737
|
console.log(` Confidence: ${Math.round(brand2.confidence * 100)}%`);
|
|
2151
2738
|
console.log();
|
|
2152
2739
|
}
|
|
@@ -2159,14 +2746,14 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2159
2746
|
}
|
|
2160
2747
|
})
|
|
2161
2748
|
).addCommand(
|
|
2162
|
-
new
|
|
2163
|
-
requireAuth();
|
|
2749
|
+
new Command10("set-default").description("Set a brand profile as default").argument("<id>", "Brand profile ID").action(async (id) => {
|
|
2750
|
+
await requireAuth();
|
|
2164
2751
|
info(
|
|
2165
2752
|
`To set brand ${id} as default, use the web dashboard.`
|
|
2166
2753
|
);
|
|
2167
2754
|
console.log();
|
|
2168
2755
|
console.log(
|
|
2169
|
-
|
|
2756
|
+
chalk8.gray(
|
|
2170
2757
|
"API support for setting default brand is coming soon."
|
|
2171
2758
|
)
|
|
2172
2759
|
);
|
|
@@ -2174,11 +2761,15 @@ var brandingCommand = new Command8("branding").description("Manage brand profile
|
|
|
2174
2761
|
);
|
|
2175
2762
|
|
|
2176
2763
|
// src/commands/derive.ts
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2764
|
+
init_api();
|
|
2765
|
+
init_auth();
|
|
2766
|
+
init_feature_cache();
|
|
2767
|
+
import { Command as Command11 } from "commander";
|
|
2768
|
+
import chalk9 from "chalk";
|
|
2769
|
+
import ora7 from "ora";
|
|
2770
|
+
init_output();
|
|
2180
2771
|
function createBlogCommand() {
|
|
2181
|
-
return new
|
|
2772
|
+
return new Command11("blog").description("Generate a blog post from a presentation").argument("<slug>", "Presentation slug").option("--words <count>", "Target word count (50-2000)", "300").option(
|
|
2182
2773
|
"--tone <tone>",
|
|
2183
2774
|
"Writing tone (professional, casual, educational)",
|
|
2184
2775
|
"professional"
|
|
@@ -2187,7 +2778,7 @@ function createBlogCommand() {
|
|
|
2187
2778
|
"Output format (human, json, markdown)",
|
|
2188
2779
|
"human"
|
|
2189
2780
|
).option("--instructions <text>", "Custom instructions for the blog").option("--no-images", "Exclude image references").option("--toc", "Include table of contents").action(async (presentationId, options) => {
|
|
2190
|
-
requireAuth();
|
|
2781
|
+
await requireAuth();
|
|
2191
2782
|
const wordCount = parseInt(options.words, 10);
|
|
2192
2783
|
if (isNaN(wordCount) || wordCount < 50 || wordCount > 2e3) {
|
|
2193
2784
|
error("Word count must be between 50 and 2000");
|
|
@@ -2198,7 +2789,7 @@ function createBlogCommand() {
|
|
|
2198
2789
|
error(`Invalid tone. Valid options: ${validTones.join(", ")}`);
|
|
2199
2790
|
process.exit(6);
|
|
2200
2791
|
}
|
|
2201
|
-
const spinner =
|
|
2792
|
+
const spinner = ora7("Fetching presentation...").start();
|
|
2202
2793
|
try {
|
|
2203
2794
|
const presentation = await getPresentation(presentationId);
|
|
2204
2795
|
spinner.text = "Generating blog post...";
|
|
@@ -2234,8 +2825,8 @@ function createBlogCommand() {
|
|
|
2234
2825
|
console.log(content);
|
|
2235
2826
|
} else {
|
|
2236
2827
|
console.log();
|
|
2237
|
-
console.log(
|
|
2238
|
-
console.log(
|
|
2828
|
+
console.log(chalk9.bold(`Blog: ${presentation.title}`));
|
|
2829
|
+
console.log(chalk9.gray("\u2500".repeat(40)));
|
|
2239
2830
|
console.log();
|
|
2240
2831
|
console.log(content);
|
|
2241
2832
|
console.log();
|
|
@@ -2248,35 +2839,35 @@ function createBlogCommand() {
|
|
|
2248
2839
|
});
|
|
2249
2840
|
}
|
|
2250
2841
|
function createTweetsCommand() {
|
|
2251
|
-
return new
|
|
2252
|
-
requireAuth();
|
|
2842
|
+
return new Command11("tweets").description("Generate tweets from a presentation").argument("<slug>", "Presentation slug").option("--count <n>", "Number of tweets to generate", "5").option("-f, --format <format>", "Output format (human, json)", "human").action(async (presentationId, options) => {
|
|
2843
|
+
await requireAuth();
|
|
2253
2844
|
info("Tweet generation coming soon.");
|
|
2254
2845
|
});
|
|
2255
2846
|
}
|
|
2256
2847
|
function createLinkedInCommand() {
|
|
2257
|
-
return new
|
|
2258
|
-
requireAuth();
|
|
2848
|
+
return new Command11("linkedin").description("Generate a LinkedIn carousel from a presentation").argument("<slug>", "Presentation slug").option("-f, --format <format>", "Output format (human, json)", "human").action(async (presentationId) => {
|
|
2849
|
+
await requireAuth();
|
|
2259
2850
|
info("LinkedIn carousel generation coming soon.");
|
|
2260
2851
|
});
|
|
2261
2852
|
}
|
|
2262
2853
|
function createQuestionsCommand() {
|
|
2263
|
-
return new
|
|
2264
|
-
requireAuth();
|
|
2854
|
+
return new Command11("questions").description("Generate questions for a presentation").argument("<slug>", "Presentation slug").option("--count <n>", "Number of questions", "10").option("-f, --format <format>", "Output format (human, json)", "human").action(async (presentationId, options) => {
|
|
2855
|
+
await requireAuth();
|
|
2265
2856
|
info("Question generation coming soon.");
|
|
2266
2857
|
});
|
|
2267
2858
|
}
|
|
2268
2859
|
function createCheatsheetCommand() {
|
|
2269
|
-
return new
|
|
2860
|
+
return new Command11("cheatsheet").description("Generate a presenter cheat sheet").argument("<slug>", "Presentation slug").option("--mode <mode>", "Cheat sheet mode (qa-prep, talking-points)", "qa-prep").option(
|
|
2270
2861
|
"-f, --format <format>",
|
|
2271
2862
|
"Output format (human, json, markdown)",
|
|
2272
2863
|
"human"
|
|
2273
2864
|
).action(async (presentationId, options) => {
|
|
2274
|
-
requireAuth();
|
|
2865
|
+
await requireAuth();
|
|
2275
2866
|
info("Cheat sheet generation coming soon.");
|
|
2276
2867
|
});
|
|
2277
2868
|
}
|
|
2278
2869
|
function buildDeriveCommand() {
|
|
2279
|
-
const derive = new
|
|
2870
|
+
const derive = new Command11("derive").description("Generate derivative content from a presentation");
|
|
2280
2871
|
const flags = getCachedFlags();
|
|
2281
2872
|
if (!flags) {
|
|
2282
2873
|
return derive;
|
|
@@ -2300,33 +2891,39 @@ function buildDeriveCommand() {
|
|
|
2300
2891
|
}
|
|
2301
2892
|
|
|
2302
2893
|
// src/commands/ideas.ts
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2894
|
+
init_auth();
|
|
2895
|
+
init_output();
|
|
2896
|
+
import { Command as Command12 } from "commander";
|
|
2897
|
+
import chalk10 from "chalk";
|
|
2898
|
+
var ideasCommand = new Command12("ideas").description("Generate presentation topic ideas").option("--context <text>", "Custom context for idea generation").option("--count <n>", "Number of ideas to generate", "5").option("-f, --format <format>", "Output format (human, json)", "human").action(async (options) => {
|
|
2899
|
+
await requireAuth();
|
|
2307
2900
|
info("Idea generation is available in the web dashboard.");
|
|
2308
2901
|
console.log();
|
|
2309
2902
|
console.log(
|
|
2310
|
-
|
|
2903
|
+
chalk10.gray("The CLI will support idea generation in a future release.")
|
|
2311
2904
|
);
|
|
2312
2905
|
console.log();
|
|
2313
2906
|
console.log("For now, try these approaches:");
|
|
2314
2907
|
console.log(
|
|
2315
|
-
|
|
2908
|
+
chalk10.gray(
|
|
2316
2909
|
" 1. Visit the ConceptCraft dashboard and use the idea generator"
|
|
2317
2910
|
)
|
|
2318
2911
|
);
|
|
2319
2912
|
console.log(
|
|
2320
|
-
|
|
2913
|
+
chalk10.gray(" 2. Create a presentation with a broad topic and refine it")
|
|
2321
2914
|
);
|
|
2322
2915
|
console.log();
|
|
2323
2916
|
});
|
|
2324
2917
|
|
|
2325
2918
|
// src/commands/whoami.ts
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2919
|
+
init_api();
|
|
2920
|
+
init_auth();
|
|
2921
|
+
init_feature_cache();
|
|
2922
|
+
init_output();
|
|
2923
|
+
import { Command as Command13 } from "commander";
|
|
2924
|
+
import chalk11 from "chalk";
|
|
2925
|
+
var whoamiCommand = new Command13("whoami").description("Show current user and team information").option("-f, --format <format>", "Output format (human, json)", "human").action(async (options) => {
|
|
2926
|
+
await requireAuth();
|
|
2330
2927
|
try {
|
|
2331
2928
|
const result = await whoami();
|
|
2332
2929
|
refreshInBackground();
|
|
@@ -2361,12 +2958,12 @@ var whoamiCommand = new Command11("whoami").description("Show current user and t
|
|
|
2361
2958
|
header("All Teams");
|
|
2362
2959
|
console.log();
|
|
2363
2960
|
for (const team of result.teams) {
|
|
2364
|
-
const current = team.isCurrent ?
|
|
2961
|
+
const current = team.isCurrent ? chalk11.green(" (current)") : "";
|
|
2365
2962
|
console.log(
|
|
2366
|
-
` ${
|
|
2963
|
+
` ${chalk11.bold(team.name)}${current}`
|
|
2367
2964
|
);
|
|
2368
2965
|
console.log(
|
|
2369
|
-
|
|
2966
|
+
chalk11.gray(` ID: ${team.id} | Plan: ${team.planName} | Role: ${team.role}`)
|
|
2370
2967
|
);
|
|
2371
2968
|
}
|
|
2372
2969
|
}
|
|
@@ -2378,8 +2975,9 @@ var whoamiCommand = new Command11("whoami").description("Show current user and t
|
|
|
2378
2975
|
});
|
|
2379
2976
|
|
|
2380
2977
|
// src/commands/skill.ts
|
|
2381
|
-
|
|
2382
|
-
import
|
|
2978
|
+
init_output();
|
|
2979
|
+
import { Command as Command14 } from "commander";
|
|
2980
|
+
import chalk12 from "chalk";
|
|
2383
2981
|
import { mkdirSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
2384
2982
|
import { join } from "path";
|
|
2385
2983
|
import { homedir } from "os";
|
|
@@ -2582,17 +3180,17 @@ var EDITORS = [
|
|
|
2582
3180
|
{ name: "Windsurf", dir: ".windsurf" },
|
|
2583
3181
|
{ name: "Agent", dir: ".agent" }
|
|
2584
3182
|
];
|
|
2585
|
-
var skillCommand = new
|
|
3183
|
+
var skillCommand = new Command14("skill").description("Manage ConceptCraft skill for AI coding assistants").addHelpText(
|
|
2586
3184
|
"after",
|
|
2587
3185
|
`
|
|
2588
|
-
${
|
|
2589
|
-
${
|
|
3186
|
+
${chalk12.bold("Examples:")}
|
|
3187
|
+
${chalk12.gray("# Install skill for all detected editors")}
|
|
2590
3188
|
$ conceptcraft skill install
|
|
2591
3189
|
|
|
2592
|
-
${
|
|
3190
|
+
${chalk12.gray("# Install to specific directory")}
|
|
2593
3191
|
$ conceptcraft skill install --dir ~/.claude
|
|
2594
3192
|
|
|
2595
|
-
${
|
|
3193
|
+
${chalk12.gray("# Show skill content")}
|
|
2596
3194
|
$ conceptcraft skill show
|
|
2597
3195
|
`
|
|
2598
3196
|
);
|
|
@@ -2638,7 +3236,7 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
|
|
|
2638
3236
|
if (skipped.length > 0) {
|
|
2639
3237
|
console.log();
|
|
2640
3238
|
info(`Skipped (already exists): ${skipped.join(", ")}`);
|
|
2641
|
-
console.log(
|
|
3239
|
+
console.log(chalk12.gray(" Use --force to overwrite"));
|
|
2642
3240
|
}
|
|
2643
3241
|
if (errors.length > 0) {
|
|
2644
3242
|
console.log();
|
|
@@ -2649,8 +3247,8 @@ skillCommand.command("install").description("Install the ConceptCraft skill for
|
|
|
2649
3247
|
if (installed.length === 0 && skipped.length === 0 && errors.length === 0) {
|
|
2650
3248
|
info("No supported AI coding assistants detected.");
|
|
2651
3249
|
console.log();
|
|
2652
|
-
console.log(
|
|
2653
|
-
console.log(
|
|
3250
|
+
console.log(chalk12.gray("Supported editors: " + EDITORS.map((e) => e.name).join(", ")));
|
|
3251
|
+
console.log(chalk12.gray("Use --dir <path> to install to a specific directory"));
|
|
2654
3252
|
}
|
|
2655
3253
|
console.log();
|
|
2656
3254
|
});
|
|
@@ -2687,14 +3285,16 @@ function installSkill(skillPath, force) {
|
|
|
2687
3285
|
}
|
|
2688
3286
|
|
|
2689
3287
|
// src/index.ts
|
|
2690
|
-
var VERSION = "0.1.
|
|
2691
|
-
var program = new
|
|
3288
|
+
var VERSION = "0.1.3";
|
|
3289
|
+
var program = new Command15();
|
|
2692
3290
|
var cmdName = brand.commands[0];
|
|
2693
3291
|
program.name(cmdName).description(brand.description).version(VERSION, "-v, --version", "Show version number").option("--debug", "Enable debug logging").option("--no-color", "Disable colored output").configureOutput({
|
|
2694
3292
|
outputError: (str, write) => {
|
|
2695
|
-
write(
|
|
3293
|
+
write(chalk13.red(str));
|
|
2696
3294
|
}
|
|
2697
3295
|
});
|
|
3296
|
+
program.addCommand(loginCommand);
|
|
3297
|
+
program.addCommand(logoutCommand);
|
|
2698
3298
|
program.addCommand(configCommand);
|
|
2699
3299
|
program.addCommand(createCommand);
|
|
2700
3300
|
program.addCommand(listCommand);
|
|
@@ -2711,7 +3311,7 @@ if (deriveCommand.commands.length > 0) {
|
|
|
2711
3311
|
program.addCommand(deriveCommand);
|
|
2712
3312
|
}
|
|
2713
3313
|
program.on("command:*", (operands) => {
|
|
2714
|
-
console.error(
|
|
3314
|
+
console.error(chalk13.red(`Error: Unknown command '${operands[0]}'`));
|
|
2715
3315
|
console.error();
|
|
2716
3316
|
console.error(`Run '${cmdName} --help' to see available commands.`);
|
|
2717
3317
|
process.exit(1);
|
|
@@ -2719,38 +3319,38 @@ program.on("command:*", (operands) => {
|
|
|
2719
3319
|
program.addHelpText(
|
|
2720
3320
|
"after",
|
|
2721
3321
|
`
|
|
2722
|
-
${
|
|
2723
|
-
${
|
|
2724
|
-
$ ${cmdName}
|
|
3322
|
+
${chalk13.bold("Examples:")}
|
|
3323
|
+
${chalk13.gray("# Log in (opens browser)")}
|
|
3324
|
+
$ ${cmdName} login
|
|
2725
3325
|
|
|
2726
|
-
${
|
|
3326
|
+
${chalk13.gray("# Create a presentation (recommended pattern)")}
|
|
2727
3327
|
$ ${cmdName} create "Q4 Business Review" \\
|
|
2728
3328
|
-n 12 -m best \\
|
|
2729
3329
|
--audience "Executive leadership team" \\
|
|
2730
3330
|
--context "Revenue: $50M (+25% YoY), New customers: 150"
|
|
2731
3331
|
|
|
2732
|
-
${
|
|
3332
|
+
${chalk13.gray("# Create from a file")}
|
|
2733
3333
|
$ ${cmdName} create "Product Launch" \\
|
|
2734
3334
|
-n 10 -m balanced \\
|
|
2735
3335
|
--audience "Sales team and partners" \\
|
|
2736
3336
|
--context-file ./launch-brief.md
|
|
2737
3337
|
|
|
2738
|
-
${
|
|
3338
|
+
${chalk13.gray("# Create from URLs")}
|
|
2739
3339
|
$ ${cmdName} create "Market Analysis" \\
|
|
2740
3340
|
-n 15 -m best \\
|
|
2741
3341
|
--audience "Strategy team" \\
|
|
2742
3342
|
--sources https://example.com/report.pdf
|
|
2743
3343
|
|
|
2744
|
-
${
|
|
3344
|
+
${chalk13.gray("# List and export")}
|
|
2745
3345
|
$ ${cmdName} list --format table
|
|
2746
3346
|
$ ${cmdName} export <slug> -o backup.zip
|
|
2747
3347
|
|
|
2748
|
-
${
|
|
2749
|
-
|
|
2750
|
-
|
|
3348
|
+
${chalk13.bold("Authentication:")}
|
|
3349
|
+
Run '${cmdName} login' to authenticate (recommended).
|
|
3350
|
+
Or set CC_MINDFRAMES_API_KEY environment variable for API key auth.
|
|
2751
3351
|
|
|
2752
|
-
${
|
|
2753
|
-
${
|
|
3352
|
+
${chalk13.bold("More Info:")}
|
|
3353
|
+
${chalk13.blue(brand.docsUrl)}
|
|
2754
3354
|
`
|
|
2755
3355
|
);
|
|
2756
3356
|
program.parse();
|