commet 1.11.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1077 -942
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25,12 +25,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
27
|
var import_chalk15 = __toESM(require("chalk"));
|
|
28
|
-
var
|
|
28
|
+
var import_commander10 = require("commander");
|
|
29
29
|
|
|
30
30
|
// package.json
|
|
31
31
|
var package_default = {
|
|
32
32
|
name: "commet",
|
|
33
|
-
version: "1.
|
|
33
|
+
version: "2.1.0",
|
|
34
34
|
description: "Commet CLI - Manage your billing platform from the command line",
|
|
35
35
|
bin: {
|
|
36
36
|
commet: "./bin/commet"
|
|
@@ -87,8 +87,10 @@ var package_default = {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
// src/commands/
|
|
90
|
+
// src/commands/api-key.ts
|
|
91
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
91
92
|
var import_commander = require("commander");
|
|
93
|
+
var import_ora = __toESM(require("ora"));
|
|
92
94
|
|
|
93
95
|
// src/utils/config.ts
|
|
94
96
|
var fs = __toESM(require("fs"));
|
|
@@ -157,260 +159,353 @@ function clearProjectConfig() {
|
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
|
|
160
|
-
// src/utils/
|
|
161
|
-
var
|
|
162
|
-
var
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
return null;
|
|
162
|
+
// src/utils/telemetry.ts
|
|
163
|
+
var CLI_VERSION = true ? "2.1.0" : "0.0.0";
|
|
164
|
+
var TELEMETRY_URL = "https://commet.co/api/cli/telemetry";
|
|
165
|
+
function detectRuntime() {
|
|
166
|
+
if ("Bun" in globalThis) {
|
|
167
|
+
const bun = globalThis.Bun;
|
|
168
|
+
if (bun && typeof bun.version === "string")
|
|
169
|
+
return { name: "bun", version: bun.version };
|
|
170
|
+
}
|
|
171
|
+
if ("Deno" in globalThis) {
|
|
172
|
+
const deno = globalThis.Deno;
|
|
173
|
+
if (deno?.version?.deno)
|
|
174
|
+
return { name: "deno", version: deno.version.deno };
|
|
175
|
+
}
|
|
176
|
+
return { name: "node", version: process.versions.node };
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
throw new Error(
|
|
193
|
-
`${configPath}: must use \`export default defineConfig({...})\``
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
const config = moduleRecord.default;
|
|
197
|
-
validateConfig(config, configPath);
|
|
198
|
-
return { config, configPath };
|
|
178
|
+
var cachedClientInfo = null;
|
|
179
|
+
var cachedUserAgent = null;
|
|
180
|
+
function collectClientInfo() {
|
|
181
|
+
const runtime = detectRuntime();
|
|
182
|
+
return {
|
|
183
|
+
sdk: "commet-cli",
|
|
184
|
+
sdkVersion: CLI_VERSION,
|
|
185
|
+
lang: "node",
|
|
186
|
+
langVersion: process.versions.node,
|
|
187
|
+
platform: process.platform,
|
|
188
|
+
arch: process.arch,
|
|
189
|
+
runtime: runtime.name,
|
|
190
|
+
runtimeVersion: runtime.version
|
|
191
|
+
};
|
|
199
192
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"monthly",
|
|
204
|
-
"quarterly",
|
|
205
|
-
"yearly",
|
|
206
|
-
"one_time"
|
|
207
|
-
]);
|
|
208
|
-
function validateConfig(config, configPath) {
|
|
209
|
-
if (!config || typeof config !== "object") {
|
|
210
|
-
throw new Error(`${configPath}: config must be an object`);
|
|
211
|
-
}
|
|
212
|
-
if (!config.features || typeof config.features !== "object") {
|
|
213
|
-
throw new Error(`${configPath}: config.features must be an object`);
|
|
214
|
-
}
|
|
215
|
-
if (!config.plans || typeof config.plans !== "object") {
|
|
216
|
-
throw new Error(`${configPath}: config.plans must be an object`);
|
|
217
|
-
}
|
|
218
|
-
for (const [code, feature] of Object.entries(config.features)) {
|
|
219
|
-
if (!feature.name || typeof feature.name !== "string") {
|
|
220
|
-
throw new Error(`Feature "${code}": name is required`);
|
|
221
|
-
}
|
|
222
|
-
if (!VALID_FEATURE_TYPES.has(feature.type)) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`Feature "${code}": type must be one of: boolean, usage, seats`
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
for (const [code, plan] of Object.entries(config.plans)) {
|
|
229
|
-
if (!plan.name || typeof plan.name !== "string") {
|
|
230
|
-
throw new Error(`Plan "${code}": name is required`);
|
|
231
|
-
}
|
|
232
|
-
if (!Array.isArray(plan.prices)) {
|
|
233
|
-
throw new Error(`Plan "${code}": prices must be an array`);
|
|
234
|
-
}
|
|
235
|
-
for (const price of plan.prices) {
|
|
236
|
-
if (!VALID_INTERVALS.has(price.interval)) {
|
|
237
|
-
throw new Error(
|
|
238
|
-
`Plan "${code}": price interval "${price.interval}" is not valid`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
if (typeof price.amount !== "number") {
|
|
242
|
-
throw new Error(`Plan "${code}": price amount must be a number`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
if (plan.prices.length > 0) {
|
|
246
|
-
if (!plan.defaultInterval) {
|
|
247
|
-
throw new Error(
|
|
248
|
-
`Plan "${code}": defaultInterval is required when prices are defined`
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
const priceIntervals = new Set(plan.prices.map((p) => p.interval));
|
|
252
|
-
if (!priceIntervals.has(plan.defaultInterval)) {
|
|
253
|
-
throw new Error(
|
|
254
|
-
`Plan "${code}": defaultInterval "${plan.defaultInterval}" does not match any price interval (${[...priceIntervals].join(", ")})`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (plan.features) {
|
|
259
|
-
for (const featureCode of Object.keys(plan.features)) {
|
|
260
|
-
if (!config.features[featureCode]) {
|
|
261
|
-
throw new Error(
|
|
262
|
-
`Plan "${code}": references feature "${featureCode}" which is not defined in config.features`
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
193
|
+
function getClientInfoHeader() {
|
|
194
|
+
if (!cachedClientInfo) {
|
|
195
|
+
cachedClientInfo = JSON.stringify(collectClientInfo());
|
|
267
196
|
}
|
|
197
|
+
return cachedClientInfo;
|
|
268
198
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
).action(() => {
|
|
274
|
-
const authenticated = authExists();
|
|
275
|
-
const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
|
|
276
|
-
const configPath = findConfigFile(process.cwd());
|
|
277
|
-
const setup = [];
|
|
278
|
-
if (!authenticated) {
|
|
279
|
-
setup.push(
|
|
280
|
-
"Not authenticated. A human must run 'commet login' in a terminal with browser access \u2014 this is the only interactive step."
|
|
281
|
-
);
|
|
199
|
+
function getUserAgent() {
|
|
200
|
+
if (!cachedUserAgent) {
|
|
201
|
+
const runtime = detectRuntime();
|
|
202
|
+
cachedUserAgent = `commet-cli/${CLI_VERSION} ${runtime.name}/${runtime.version} ${process.platform}/${process.arch}`;
|
|
282
203
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
204
|
+
return cachedUserAgent;
|
|
205
|
+
}
|
|
206
|
+
function isTelemetryDisabled() {
|
|
207
|
+
return process.env.COMMET_TELEMETRY_DISABLED === "1" || process.env.DO_NOT_TRACK === "1";
|
|
208
|
+
}
|
|
209
|
+
function resolveOrgId() {
|
|
210
|
+
if (process.env.COMMET_API_KEY) return null;
|
|
211
|
+
try {
|
|
212
|
+
const config = loadProjectConfig();
|
|
213
|
+
return config?.orgId ?? null;
|
|
214
|
+
} catch {
|
|
215
|
+
return null;
|
|
287
216
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
217
|
+
}
|
|
218
|
+
var commandStartTime = null;
|
|
219
|
+
var apiRequestMade = false;
|
|
220
|
+
var commandReported = false;
|
|
221
|
+
function markCommandStart() {
|
|
222
|
+
commandStartTime = Date.now();
|
|
223
|
+
apiRequestMade = false;
|
|
224
|
+
commandReported = false;
|
|
225
|
+
}
|
|
226
|
+
function markApiRequest() {
|
|
227
|
+
apiRequestMade = true;
|
|
228
|
+
}
|
|
229
|
+
function sendTelemetry(payload) {
|
|
230
|
+
const controller = new AbortController();
|
|
231
|
+
const timeout = setTimeout(() => controller.abort(), 750);
|
|
232
|
+
fetch(TELEMETRY_URL, {
|
|
233
|
+
method: "POST",
|
|
234
|
+
headers: {
|
|
235
|
+
"Content-Type": "application/json",
|
|
236
|
+
"User-Agent": getUserAgent()
|
|
301
237
|
},
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
var import_tar = require("tar");
|
|
238
|
+
body: JSON.stringify(payload),
|
|
239
|
+
signal: controller.signal
|
|
240
|
+
}).catch(() => {
|
|
241
|
+
}).finally(() => clearTimeout(timeout));
|
|
242
|
+
}
|
|
243
|
+
function reportCommand(command, outcome, errorCode) {
|
|
244
|
+
if (commandReported) return;
|
|
245
|
+
if (isTelemetryDisabled()) return;
|
|
246
|
+
if (apiRequestMade && outcome === "success") return;
|
|
247
|
+
commandReported = true;
|
|
248
|
+
const durationMs = commandStartTime ? Date.now() - commandStartTime : null;
|
|
249
|
+
const runtime = detectRuntime();
|
|
250
|
+
const orgId = resolveOrgId();
|
|
251
|
+
sendTelemetry({
|
|
252
|
+
type: "command",
|
|
253
|
+
command,
|
|
254
|
+
outcome,
|
|
255
|
+
...errorCode ? { errorCode } : {},
|
|
256
|
+
...orgId ? { orgId } : {},
|
|
257
|
+
cliVersion: CLI_VERSION,
|
|
258
|
+
runtime: runtime.name,
|
|
259
|
+
runtimeVersion: runtime.version,
|
|
260
|
+
platform: process.platform,
|
|
261
|
+
arch: process.arch,
|
|
262
|
+
...durationMs !== null ? { durationMs } : {},
|
|
263
|
+
authMode: process.env.COMMET_API_KEY ? "api_key" : "login"
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function reportCrash(error) {
|
|
267
|
+
if (commandReported) return;
|
|
268
|
+
if (isTelemetryDisabled()) return;
|
|
269
|
+
commandReported = true;
|
|
270
|
+
const command = process.argv[2] || "(default)";
|
|
271
|
+
const orgId = resolveOrgId();
|
|
272
|
+
const errorName = error instanceof Error ? error.constructor.name : "UnknownError";
|
|
273
|
+
sendTelemetry({
|
|
274
|
+
type: "crash",
|
|
275
|
+
command,
|
|
276
|
+
errorName,
|
|
277
|
+
...orgId ? { orgId } : {},
|
|
278
|
+
cliVersion: CLI_VERSION,
|
|
279
|
+
platform: process.platform,
|
|
280
|
+
arch: process.arch,
|
|
281
|
+
authMode: process.env.COMMET_API_KEY ? "api_key" : "login"
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function installCrashHandler() {
|
|
285
|
+
process.on("uncaughtException", (error) => {
|
|
286
|
+
reportCrash(error);
|
|
287
|
+
console.error("Fatal:", error.message);
|
|
288
|
+
setTimeout(() => process.exit(1), 800);
|
|
289
|
+
});
|
|
290
|
+
process.on("unhandledRejection", (reason) => {
|
|
291
|
+
reportCrash(reason);
|
|
292
|
+
console.error(
|
|
293
|
+
"Fatal:",
|
|
294
|
+
reason instanceof Error ? reason.message : String(reason)
|
|
295
|
+
);
|
|
296
|
+
setTimeout(() => process.exit(1), 800);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
364
299
|
|
|
365
300
|
// src/utils/api.ts
|
|
366
301
|
var BASE_URL = "https://commet.co";
|
|
367
302
|
async function apiRequest(endpoint, options = {}) {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
303
|
+
const apiKey = process.env.COMMET_API_KEY;
|
|
304
|
+
const auth = apiKey ? null : loadAuth();
|
|
305
|
+
if (!apiKey && !auth) {
|
|
306
|
+
return {
|
|
307
|
+
error: {
|
|
308
|
+
code: "auth_required",
|
|
309
|
+
message: "Not authenticated. Run `commet login` first."
|
|
310
|
+
}
|
|
311
|
+
};
|
|
371
312
|
}
|
|
372
313
|
try {
|
|
314
|
+
markApiRequest();
|
|
373
315
|
const response = await fetch(endpoint, {
|
|
374
316
|
...options,
|
|
375
317
|
headers: {
|
|
376
318
|
...options.headers,
|
|
377
|
-
|
|
378
|
-
"
|
|
319
|
+
"Content-Type": "application/json",
|
|
320
|
+
"User-Agent": getUserAgent(),
|
|
321
|
+
"commet-client-info": getClientInfoHeader(),
|
|
322
|
+
...apiKey ? { "x-api-key": apiKey } : { Authorization: `Bearer ${auth.token}` }
|
|
379
323
|
}
|
|
380
324
|
});
|
|
381
325
|
if (!response.ok) {
|
|
382
326
|
const errorData = await response.json().catch(() => ({}));
|
|
383
327
|
return {
|
|
384
|
-
error:
|
|
328
|
+
error: {
|
|
329
|
+
code: errorData.code ?? `http_${response.status}`,
|
|
330
|
+
message: errorData.message ?? errorData.error ?? `Request failed with status ${response.status}`
|
|
331
|
+
}
|
|
385
332
|
};
|
|
386
333
|
}
|
|
387
334
|
const data = await response.json();
|
|
388
335
|
return { data };
|
|
389
336
|
} catch (error) {
|
|
390
337
|
return {
|
|
391
|
-
error:
|
|
338
|
+
error: {
|
|
339
|
+
code: "network_error",
|
|
340
|
+
message: error instanceof Error ? error.message : "Unknown error occurred"
|
|
341
|
+
}
|
|
392
342
|
};
|
|
393
343
|
}
|
|
394
344
|
}
|
|
395
345
|
|
|
346
|
+
// src/utils/output.ts
|
|
347
|
+
var import_chalk = __toESM(require("chalk"));
|
|
348
|
+
function isAgentMode(options) {
|
|
349
|
+
if (options?.output === "agent") return true;
|
|
350
|
+
const idx = process.argv.indexOf("--output");
|
|
351
|
+
return idx !== -1 && process.argv[idx + 1] === "agent";
|
|
352
|
+
}
|
|
353
|
+
function exitWithError(error) {
|
|
354
|
+
const command = process.argv[2] || "(default)";
|
|
355
|
+
reportCommand(command, "error", error.code);
|
|
356
|
+
if (isAgentMode()) {
|
|
357
|
+
console.log(JSON.stringify({ error }));
|
|
358
|
+
} else {
|
|
359
|
+
console.log(import_chalk.default.red(`\u2717 ${error.message}`));
|
|
360
|
+
if (error.action) {
|
|
361
|
+
console.log(import_chalk.default.dim(`Run \`${error.action}\``));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
function requireAuth() {
|
|
367
|
+
if (process.env.COMMET_API_KEY) return;
|
|
368
|
+
if (!authExists()) {
|
|
369
|
+
exitWithError({
|
|
370
|
+
code: "auth_required",
|
|
371
|
+
message: "Not authenticated",
|
|
372
|
+
action: "commet login"
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function requireOrgContext() {
|
|
377
|
+
if (process.env.COMMET_API_KEY) {
|
|
378
|
+
return { orgId: "__from_api_key__" };
|
|
379
|
+
}
|
|
380
|
+
requireAuth();
|
|
381
|
+
const projectConfig = loadProjectConfig();
|
|
382
|
+
if (!projectConfig) {
|
|
383
|
+
exitWithError({
|
|
384
|
+
code: "project_not_linked",
|
|
385
|
+
message: "No organization linked",
|
|
386
|
+
action: "commet link"
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return { orgId: projectConfig.orgId };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/commands/api-key.ts
|
|
393
|
+
var apiKeyCommand = new import_commander.Command("api-key").description(
|
|
394
|
+
"Generate an API key for the linked organization. Use it in CI with COMMET_API_KEY env var."
|
|
395
|
+
).option("--name <name>", "Name for the API key", "CLI").option(
|
|
396
|
+
"--output <format>",
|
|
397
|
+
"Output format: human (default) or agent",
|
|
398
|
+
"human"
|
|
399
|
+
).addHelpText(
|
|
400
|
+
"after",
|
|
401
|
+
`
|
|
402
|
+
Examples:
|
|
403
|
+
$ commet api-key Generate a key for the linked org
|
|
404
|
+
$ commet api-key --name "GitHub CI" Name it for easy identification
|
|
405
|
+
$ commet api-key --output agent JSON output with the key
|
|
406
|
+
|
|
407
|
+
Then use it in CI:
|
|
408
|
+
$ COMMET_API_KEY=sk_... commet push --yes
|
|
409
|
+
`
|
|
410
|
+
).action(async (options) => {
|
|
411
|
+
const agentMode = isAgentMode(options);
|
|
412
|
+
if (process.env.COMMET_API_KEY) {
|
|
413
|
+
exitWithError({
|
|
414
|
+
code: "invalid_context",
|
|
415
|
+
message: "Cannot create API keys while using COMMET_API_KEY",
|
|
416
|
+
action: "commet login"
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
requireAuth();
|
|
420
|
+
const projectConfig = loadProjectConfig();
|
|
421
|
+
if (!projectConfig) {
|
|
422
|
+
exitWithError({
|
|
423
|
+
code: "project_not_linked",
|
|
424
|
+
message: "No organization linked",
|
|
425
|
+
action: "commet link"
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const spinner = agentMode ? null : (0, import_ora.default)("Generating API key...").start();
|
|
429
|
+
const result = await apiRequest(
|
|
430
|
+
`${BASE_URL}/api/cli/api-keys`,
|
|
431
|
+
{
|
|
432
|
+
method: "POST",
|
|
433
|
+
body: JSON.stringify({
|
|
434
|
+
organizationId: projectConfig.orgId,
|
|
435
|
+
name: options.name
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
if (result.error || !result.data) {
|
|
440
|
+
if (agentMode) {
|
|
441
|
+
console.log(JSON.stringify({ error: result.error }));
|
|
442
|
+
} else {
|
|
443
|
+
spinner?.fail("Failed to create API key");
|
|
444
|
+
console.error(import_chalk2.default.red("Error:"), result.error?.message);
|
|
445
|
+
}
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
const { apiKey } = result.data;
|
|
449
|
+
const isLive = projectConfig.mode === "live";
|
|
450
|
+
if (agentMode) {
|
|
451
|
+
console.log(
|
|
452
|
+
JSON.stringify({
|
|
453
|
+
success: true,
|
|
454
|
+
apiKey,
|
|
455
|
+
mode: projectConfig.mode,
|
|
456
|
+
...isLive ? {
|
|
457
|
+
warning: "This is a live API key. Rotate it before using in production if generated by an agent."
|
|
458
|
+
} : {}
|
|
459
|
+
})
|
|
460
|
+
);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
spinner?.succeed("API key created");
|
|
464
|
+
console.log("");
|
|
465
|
+
console.log(` ${import_chalk2.default.bold(apiKey)}`);
|
|
466
|
+
console.log("");
|
|
467
|
+
console.log(import_chalk2.default.yellow(" \u26A0 This key is shown only once \u2014 copy it now."));
|
|
468
|
+
if (isLive) {
|
|
469
|
+
console.log(
|
|
470
|
+
import_chalk2.default.yellow(
|
|
471
|
+
" \u26A0 This is a live key. Rotate it from the dashboard before production use."
|
|
472
|
+
)
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
console.log(
|
|
476
|
+
import_chalk2.default.dim("\n Use in CI: COMMET_API_KEY=<key> commet push --yes")
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// src/commands/create.ts
|
|
481
|
+
var import_node_child_process = require("child_process");
|
|
482
|
+
var fs2 = __toESM(require("fs"));
|
|
483
|
+
var os2 = __toESM(require("os"));
|
|
484
|
+
var path2 = __toESM(require("path"));
|
|
485
|
+
var import_prompts = require("@inquirer/prompts");
|
|
486
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
487
|
+
var import_commander2 = require("commander");
|
|
488
|
+
var import_ora3 = __toESM(require("ora"));
|
|
489
|
+
var import_tar = require("tar");
|
|
490
|
+
|
|
396
491
|
// src/utils/login-flow.ts
|
|
397
|
-
var
|
|
492
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
398
493
|
var import_open = __toESM(require("open"));
|
|
399
|
-
var
|
|
494
|
+
var import_ora2 = __toESM(require("ora"));
|
|
400
495
|
|
|
401
496
|
// src/utils/prompt-theme.ts
|
|
402
|
-
var
|
|
403
|
-
var commetColor =
|
|
497
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
498
|
+
var commetColor = import_chalk3.default.hex("#e8a07c");
|
|
404
499
|
var promptTheme = {
|
|
405
500
|
prefix: commetColor("\u276F"),
|
|
406
501
|
style: {
|
|
407
502
|
answer: commetColor,
|
|
408
|
-
message:
|
|
409
|
-
error:
|
|
410
|
-
help:
|
|
503
|
+
message: import_chalk3.default.bold,
|
|
504
|
+
error: import_chalk3.default.red,
|
|
505
|
+
help: import_chalk3.default.dim,
|
|
411
506
|
highlight: commetColor.bold,
|
|
412
|
-
description:
|
|
413
|
-
defaultAnswer:
|
|
507
|
+
description: import_chalk3.default.dim,
|
|
508
|
+
defaultAnswer: import_chalk3.default.dim
|
|
414
509
|
}
|
|
415
510
|
};
|
|
416
511
|
|
|
@@ -419,7 +514,7 @@ function sleep(ms) {
|
|
|
419
514
|
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
420
515
|
}
|
|
421
516
|
async function performLogin() {
|
|
422
|
-
const spinner = (0,
|
|
517
|
+
const spinner = (0, import_ora2.default)("Initiating login flow...").start();
|
|
423
518
|
try {
|
|
424
519
|
const deviceResponse = await fetch(`${BASE_URL}/api/auth/device/code`, {
|
|
425
520
|
method: "POST",
|
|
@@ -441,18 +536,18 @@ async function performLogin() {
|
|
|
441
536
|
interval = 5
|
|
442
537
|
} = deviceData;
|
|
443
538
|
spinner.stop();
|
|
444
|
-
console.log(
|
|
539
|
+
console.log(import_chalk4.default.bold("\n\u{1F510} Commet CLI Login\n"));
|
|
445
540
|
console.log("Visit the following URL in your browser:");
|
|
446
541
|
console.log(commetColor.underline(verification_uri_complete));
|
|
447
542
|
console.log("\nOr enter this code manually:");
|
|
448
|
-
console.log(
|
|
449
|
-
console.log(
|
|
543
|
+
console.log(import_chalk4.default.bold.green(` ${user_code}`));
|
|
544
|
+
console.log(import_chalk4.default.dim("\nOpening browser...\n"));
|
|
450
545
|
try {
|
|
451
546
|
await (0, import_open.default)(verification_uri_complete);
|
|
452
547
|
} catch {
|
|
453
|
-
console.log(
|
|
548
|
+
console.log(import_chalk4.default.yellow("\u26A0 Could not open browser automatically."));
|
|
454
549
|
}
|
|
455
|
-
const pollSpinner = (0,
|
|
550
|
+
const pollSpinner = (0, import_ora2.default)("Waiting for authorization...").start();
|
|
456
551
|
let pollingInterval = interval;
|
|
457
552
|
let attempts = 0;
|
|
458
553
|
const maxAttempts = Math.floor(180 / pollingInterval);
|
|
@@ -560,11 +655,11 @@ async function downloadTemplate(templateDir, dest, ref) {
|
|
|
560
655
|
if (!response.ok) {
|
|
561
656
|
throw new Error(`Failed to download (HTTP ${response.status})`);
|
|
562
657
|
}
|
|
563
|
-
const tempFile =
|
|
658
|
+
const tempFile = path2.join(os2.tmpdir(), `commet-${Date.now()}.tar.gz`);
|
|
564
659
|
try {
|
|
565
660
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
566
|
-
|
|
567
|
-
|
|
661
|
+
fs2.writeFileSync(tempFile, buffer);
|
|
662
|
+
fs2.mkdirSync(dest, { recursive: true });
|
|
568
663
|
await (0, import_tar.extract)({
|
|
569
664
|
file: tempFile,
|
|
570
665
|
cwd: dest,
|
|
@@ -575,22 +670,22 @@ async function downloadTemplate(templateDir, dest, ref) {
|
|
|
575
670
|
}
|
|
576
671
|
});
|
|
577
672
|
} finally {
|
|
578
|
-
if (
|
|
579
|
-
|
|
673
|
+
if (fs2.existsSync(tempFile)) {
|
|
674
|
+
fs2.unlinkSync(tempFile);
|
|
580
675
|
}
|
|
581
676
|
}
|
|
582
|
-
const files =
|
|
677
|
+
const files = fs2.readdirSync(dest);
|
|
583
678
|
if (files.length === 0) {
|
|
584
679
|
throw new Error(`Template "${templateDir}" not found in repository`);
|
|
585
680
|
}
|
|
586
681
|
}
|
|
587
682
|
function updatePackageJson(dest, projectName) {
|
|
588
|
-
const pkgPath =
|
|
589
|
-
const pkg = JSON.parse(
|
|
683
|
+
const pkgPath = path2.join(dest, "package.json");
|
|
684
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
590
685
|
pkg.name = projectName;
|
|
591
686
|
pkg.version = "0.0.1";
|
|
592
687
|
pkg.private = true;
|
|
593
|
-
|
|
688
|
+
fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
594
689
|
`);
|
|
595
690
|
}
|
|
596
691
|
async function fetchLatestVersion(packageName) {
|
|
@@ -606,8 +701,8 @@ async function fetchLatestVersion(packageName) {
|
|
|
606
701
|
return data.version;
|
|
607
702
|
}
|
|
608
703
|
async function resolveWorkspaceDeps(dest) {
|
|
609
|
-
const pkgPath =
|
|
610
|
-
const pkg = JSON.parse(
|
|
704
|
+
const pkgPath = path2.join(dest, "package.json");
|
|
705
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
611
706
|
const depSections = [
|
|
612
707
|
"dependencies",
|
|
613
708
|
"devDependencies",
|
|
@@ -644,29 +739,29 @@ async function resolveWorkspaceDeps(dest) {
|
|
|
644
739
|
pkg[section][name] = version;
|
|
645
740
|
}
|
|
646
741
|
}
|
|
647
|
-
|
|
742
|
+
fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
648
743
|
`);
|
|
649
744
|
return workspaceDeps.size;
|
|
650
745
|
}
|
|
651
746
|
function copyEnvExample(dest) {
|
|
652
|
-
const examplePath =
|
|
653
|
-
const envPath =
|
|
654
|
-
if (
|
|
655
|
-
|
|
747
|
+
const examplePath = path2.join(dest, ".env.example");
|
|
748
|
+
const envPath = path2.join(dest, ".env");
|
|
749
|
+
if (fs2.existsSync(examplePath)) {
|
|
750
|
+
fs2.copyFileSync(examplePath, envPath);
|
|
656
751
|
}
|
|
657
752
|
}
|
|
658
753
|
function writeApiKeyToEnv(dest, apiKey) {
|
|
659
|
-
const envPath =
|
|
660
|
-
if (!
|
|
661
|
-
let content =
|
|
754
|
+
const envPath = path2.join(dest, ".env");
|
|
755
|
+
if (!fs2.existsSync(envPath)) return;
|
|
756
|
+
let content = fs2.readFileSync(envPath, "utf-8");
|
|
662
757
|
content = content.replace(/COMMET_API_KEY=.*/, `COMMET_API_KEY=${apiKey}`);
|
|
663
|
-
|
|
758
|
+
fs2.writeFileSync(envPath, content);
|
|
664
759
|
}
|
|
665
760
|
function linkProject(dest, orgId, orgName) {
|
|
666
|
-
const commetDir =
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
761
|
+
const commetDir = path2.join(dest, ".commet");
|
|
762
|
+
fs2.mkdirSync(commetDir, { recursive: true });
|
|
763
|
+
fs2.writeFileSync(
|
|
764
|
+
path2.join(commetDir, "config.json"),
|
|
670
765
|
JSON.stringify({ orgId, orgName, mode: "sandbox" }, null, 2),
|
|
671
766
|
"utf8"
|
|
672
767
|
);
|
|
@@ -698,8 +793,8 @@ async function installSkills(projectRoot) {
|
|
|
698
793
|
);
|
|
699
794
|
child.on("close", (code) => {
|
|
700
795
|
if (code !== 0) {
|
|
701
|
-
console.log(
|
|
702
|
-
console.log(
|
|
796
|
+
console.log(import_chalk5.default.dim(" You can install them manually by running:"));
|
|
797
|
+
console.log(import_chalk5.default.dim(" npx skills add commet-labs/commet-skills"));
|
|
703
798
|
}
|
|
704
799
|
resolve4();
|
|
705
800
|
});
|
|
@@ -710,10 +805,10 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
710
805
|
"Template to use (fixed, seats, metered, credits, balance-ai, balance-fixed)"
|
|
711
806
|
).option("--org <slug>", "Organization slug or ID (skips selection)").option("--skills", "Install agent skills").option("--no-skills", "Skip agent skills installation").option("-y, --yes", "Accept defaults for optional prompts").option("--ref <ref>", "Git ref to fetch templates from", "main").option("--list", "List available templates").action(async (argName, opts) => {
|
|
712
807
|
if (opts.list) {
|
|
713
|
-
console.log(
|
|
808
|
+
console.log(import_chalk5.default.bold("\nAvailable templates:\n"));
|
|
714
809
|
for (const t of TEMPLATES) {
|
|
715
810
|
console.log(
|
|
716
|
-
` ${commetColor(t.name.padEnd(14))}${
|
|
811
|
+
` ${commetColor(t.name.padEnd(14))}${import_chalk5.default.dim(t.description)}`
|
|
717
812
|
);
|
|
718
813
|
}
|
|
719
814
|
console.log();
|
|
@@ -722,9 +817,9 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
722
817
|
let projectName = argName;
|
|
723
818
|
if (!projectName) {
|
|
724
819
|
if (!isInteractive()) {
|
|
725
|
-
console.log(
|
|
820
|
+
console.log(import_chalk5.default.red("\u2717 Project name is required"));
|
|
726
821
|
console.log(
|
|
727
|
-
|
|
822
|
+
import_chalk5.default.dim("Pass as positional argument: commet create <name>")
|
|
728
823
|
);
|
|
729
824
|
return;
|
|
730
825
|
}
|
|
@@ -734,21 +829,21 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
734
829
|
theme: promptTheme
|
|
735
830
|
});
|
|
736
831
|
} catch {
|
|
737
|
-
console.log(
|
|
832
|
+
console.log(import_chalk5.default.yellow("\n\u26A0 Cancelled"));
|
|
738
833
|
return;
|
|
739
834
|
}
|
|
740
835
|
}
|
|
741
|
-
const dest =
|
|
742
|
-
if (
|
|
836
|
+
const dest = path2.resolve(projectName);
|
|
837
|
+
if (fs2.existsSync(dest)) {
|
|
743
838
|
console.log(
|
|
744
|
-
|
|
839
|
+
import_chalk5.default.red(`\u2717 Directory "${projectName}" already exists`)
|
|
745
840
|
);
|
|
746
841
|
return;
|
|
747
842
|
}
|
|
748
843
|
if (!authExists()) {
|
|
749
844
|
if (!isInteractive()) {
|
|
750
|
-
console.log(
|
|
751
|
-
console.log(
|
|
845
|
+
console.log(import_chalk5.default.red("\u2717 Not authenticated"));
|
|
846
|
+
console.log(import_chalk5.default.dim("Run `commet login` first"));
|
|
752
847
|
return;
|
|
753
848
|
}
|
|
754
849
|
const loggedIn = await performLogin();
|
|
@@ -756,11 +851,11 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
756
851
|
return;
|
|
757
852
|
}
|
|
758
853
|
}
|
|
759
|
-
const orgsSpinner = (0,
|
|
854
|
+
const orgsSpinner = (0, import_ora3.default)("Fetching sandbox organizations...").start();
|
|
760
855
|
const orgsResult = await apiRequest(`${BASE_URL}/api/cli/organizations`);
|
|
761
856
|
if (orgsResult.error || !orgsResult.data) {
|
|
762
857
|
orgsSpinner.fail("Failed to fetch organizations");
|
|
763
|
-
console.log(
|
|
858
|
+
console.log(import_chalk5.default.dim(orgsResult.error));
|
|
764
859
|
return;
|
|
765
860
|
}
|
|
766
861
|
const organizations = orgsResult.data.organizations.filter(
|
|
@@ -768,9 +863,9 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
768
863
|
);
|
|
769
864
|
orgsSpinner.stop();
|
|
770
865
|
if (organizations.length === 0) {
|
|
771
|
-
console.log(
|
|
866
|
+
console.log(import_chalk5.default.yellow("\u26A0 No sandbox organizations found"));
|
|
772
867
|
console.log(
|
|
773
|
-
|
|
868
|
+
import_chalk5.default.dim("Create an organization at https://commet.co first")
|
|
774
869
|
);
|
|
775
870
|
return;
|
|
776
871
|
}
|
|
@@ -780,9 +875,9 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
780
875
|
(org) => org.slug === opts.org || org.id === opts.org
|
|
781
876
|
);
|
|
782
877
|
if (!match) {
|
|
783
|
-
console.log(
|
|
878
|
+
console.log(import_chalk5.default.red(`\u2717 Organization "${opts.org}" not found`));
|
|
784
879
|
console.log(
|
|
785
|
-
|
|
880
|
+
import_chalk5.default.dim(
|
|
786
881
|
`Available: ${organizations.map((o) => o.slug).join(", ")}`
|
|
787
882
|
)
|
|
788
883
|
);
|
|
@@ -793,9 +888,9 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
793
888
|
selectedOrg = organizations[0];
|
|
794
889
|
} else {
|
|
795
890
|
if (!isInteractive()) {
|
|
796
|
-
console.log(
|
|
891
|
+
console.log(import_chalk5.default.red("\u2717 Organization is required"));
|
|
797
892
|
console.log(
|
|
798
|
-
|
|
893
|
+
import_chalk5.default.dim(
|
|
799
894
|
`Pass --org=<slug>. Available: ${organizations.map((o) => o.slug).join(", ")}`
|
|
800
895
|
)
|
|
801
896
|
);
|
|
@@ -805,30 +900,30 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
805
900
|
const orgId = await (0, import_prompts.select)({
|
|
806
901
|
message: "Sandbox organization:",
|
|
807
902
|
choices: organizations.map((org) => ({
|
|
808
|
-
name: `${org.name} ${
|
|
903
|
+
name: `${org.name} ${import_chalk5.default.dim(`(${org.slug})`)}`,
|
|
809
904
|
value: org.id
|
|
810
905
|
})),
|
|
811
906
|
theme: promptTheme
|
|
812
907
|
});
|
|
813
908
|
selectedOrg = organizations.find((org) => org.id === orgId);
|
|
814
909
|
} catch {
|
|
815
|
-
console.log(
|
|
910
|
+
console.log(import_chalk5.default.yellow("\n\u26A0 Cancelled"));
|
|
816
911
|
return;
|
|
817
912
|
}
|
|
818
913
|
}
|
|
819
914
|
let template = TEMPLATES.find((t) => t.name === opts.template);
|
|
820
915
|
if (opts.template && !template) {
|
|
821
|
-
console.log(
|
|
916
|
+
console.log(import_chalk5.default.red(`\u2717 Unknown template "${opts.template}"`));
|
|
822
917
|
console.log(
|
|
823
|
-
|
|
918
|
+
import_chalk5.default.dim("Run `commet create --list` to see available templates")
|
|
824
919
|
);
|
|
825
920
|
return;
|
|
826
921
|
}
|
|
827
922
|
if (!template) {
|
|
828
923
|
if (!isInteractive()) {
|
|
829
|
-
console.log(
|
|
924
|
+
console.log(import_chalk5.default.red("\u2717 Template is required"));
|
|
830
925
|
console.log(
|
|
831
|
-
|
|
926
|
+
import_chalk5.default.dim(
|
|
832
927
|
`Pass --template=<name>. Available: ${TEMPLATES.map((t) => t.name).join(", ")}`
|
|
833
928
|
)
|
|
834
929
|
);
|
|
@@ -838,35 +933,35 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
838
933
|
const selected = await (0, import_prompts.select)({
|
|
839
934
|
message: "Billing model:",
|
|
840
935
|
choices: TEMPLATES.map((t) => ({
|
|
841
|
-
name: `${t.name.padEnd(14)} ${
|
|
936
|
+
name: `${t.name.padEnd(14)} ${import_chalk5.default.dim(t.description)}`,
|
|
842
937
|
value: t.name
|
|
843
938
|
})),
|
|
844
939
|
theme: promptTheme
|
|
845
940
|
});
|
|
846
941
|
template = TEMPLATES.find((t) => t.name === selected);
|
|
847
942
|
} catch {
|
|
848
|
-
console.log(
|
|
943
|
+
console.log(import_chalk5.default.yellow("\n\u26A0 Cancelled"));
|
|
849
944
|
return;
|
|
850
945
|
}
|
|
851
946
|
}
|
|
852
947
|
const shouldInstallSkills = await resolveSkills(opts);
|
|
853
|
-
const downloadSpinner = (0,
|
|
948
|
+
const downloadSpinner = (0, import_ora3.default)("Downloading template...").start();
|
|
854
949
|
try {
|
|
855
950
|
await downloadTemplate(template.dir, dest, opts.ref);
|
|
856
951
|
downloadSpinner.succeed("Template downloaded");
|
|
857
952
|
} catch (error) {
|
|
858
953
|
downloadSpinner.fail("Failed to download template");
|
|
859
954
|
if (error instanceof Error) {
|
|
860
|
-
console.error(
|
|
955
|
+
console.error(import_chalk5.default.red(error.message));
|
|
861
956
|
}
|
|
862
|
-
if (
|
|
863
|
-
|
|
957
|
+
if (fs2.existsSync(dest)) {
|
|
958
|
+
fs2.rmSync(dest, { recursive: true, force: true });
|
|
864
959
|
}
|
|
865
960
|
return;
|
|
866
961
|
}
|
|
867
962
|
updatePackageJson(dest, projectName);
|
|
868
963
|
copyEnvExample(dest);
|
|
869
|
-
const resolveSpinner = (0,
|
|
964
|
+
const resolveSpinner = (0, import_ora3.default)("Resolving package versions...").start();
|
|
870
965
|
try {
|
|
871
966
|
const count = await resolveWorkspaceDeps(dest);
|
|
872
967
|
if (count > 0) {
|
|
@@ -877,14 +972,14 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
877
972
|
} catch (error) {
|
|
878
973
|
resolveSpinner.fail("Failed to resolve package versions");
|
|
879
974
|
if (error instanceof Error) {
|
|
880
|
-
console.error(
|
|
975
|
+
console.error(import_chalk5.default.red(error.message));
|
|
881
976
|
}
|
|
882
|
-
if (
|
|
883
|
-
|
|
977
|
+
if (fs2.existsSync(dest)) {
|
|
978
|
+
fs2.rmSync(dest, { recursive: true, force: true });
|
|
884
979
|
}
|
|
885
980
|
return;
|
|
886
981
|
}
|
|
887
|
-
const planSpinner = (0,
|
|
982
|
+
const planSpinner = (0, import_ora3.default)("Creating plans...").start();
|
|
888
983
|
const templateResult = await apiRequest(`${BASE_URL}/api/cli/templates`, {
|
|
889
984
|
method: "POST",
|
|
890
985
|
body: JSON.stringify({
|
|
@@ -894,13 +989,13 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
894
989
|
});
|
|
895
990
|
if (templateResult.error || !templateResult.data) {
|
|
896
991
|
planSpinner.fail("Failed to create plans");
|
|
897
|
-
console.log(
|
|
992
|
+
console.log(import_chalk5.default.dim(templateResult.error));
|
|
898
993
|
} else {
|
|
899
994
|
planSpinner.succeed(
|
|
900
995
|
`Created ${templateResult.data.plansCreated} plans and ${templateResult.data.featuresCreated} features`
|
|
901
996
|
);
|
|
902
997
|
}
|
|
903
|
-
const keySpinner = (0,
|
|
998
|
+
const keySpinner = (0, import_ora3.default)("Creating API key...").start();
|
|
904
999
|
const keyResult = await apiRequest(`${BASE_URL}/api/cli/api-keys`, {
|
|
905
1000
|
method: "POST",
|
|
906
1001
|
body: JSON.stringify({
|
|
@@ -910,8 +1005,8 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
910
1005
|
});
|
|
911
1006
|
if (keyResult.error || !keyResult.data) {
|
|
912
1007
|
keySpinner.fail("Failed to create API key");
|
|
913
|
-
console.log(
|
|
914
|
-
console.log(
|
|
1008
|
+
console.log(import_chalk5.default.dim(keyResult.error));
|
|
1009
|
+
console.log(import_chalk5.default.dim("You can create one manually in the dashboard"));
|
|
915
1010
|
} else {
|
|
916
1011
|
writeApiKeyToEnv(dest, keyResult.data.apiKey);
|
|
917
1012
|
keySpinner.succeed("API key created and saved to .env");
|
|
@@ -920,10 +1015,10 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
920
1015
|
if (shouldInstallSkills) {
|
|
921
1016
|
await installSkills(dest);
|
|
922
1017
|
}
|
|
923
|
-
console.log(
|
|
1018
|
+
console.log(import_chalk5.default.green(`
|
|
924
1019
|
\u2713 Created ${projectName}`));
|
|
925
|
-
console.log(
|
|
926
|
-
console.log(
|
|
1020
|
+
console.log(import_chalk5.default.dim(` Template: ${template.name}`));
|
|
1021
|
+
console.log(import_chalk5.default.dim(` Organization: ${selectedOrg.name} \xB7 sandbox`));
|
|
927
1022
|
console.log();
|
|
928
1023
|
console.log(` ${commetColor("cd")} ${projectName}`);
|
|
929
1024
|
console.log(` ${commetColor("npm install")}`);
|
|
@@ -933,20 +1028,20 @@ var createCommand = new import_commander2.Command("create").description("Create
|
|
|
933
1028
|
|
|
934
1029
|
// src/commands/link.ts
|
|
935
1030
|
var import_prompts2 = require("@inquirer/prompts");
|
|
936
|
-
var
|
|
1031
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
937
1032
|
var import_commander3 = require("commander");
|
|
938
|
-
var
|
|
1033
|
+
var import_ora4 = __toESM(require("ora"));
|
|
939
1034
|
|
|
940
1035
|
// src/utils/gitignore-updater.ts
|
|
941
|
-
var
|
|
942
|
-
var
|
|
1036
|
+
var fs3 = __toESM(require("fs"));
|
|
1037
|
+
var path3 = __toESM(require("path"));
|
|
943
1038
|
function updateGitignore(entry) {
|
|
944
1039
|
const cwd = process.cwd();
|
|
945
|
-
const gitignorePath =
|
|
1040
|
+
const gitignorePath = path3.join(cwd, ".gitignore");
|
|
946
1041
|
try {
|
|
947
1042
|
let content = "";
|
|
948
|
-
if (
|
|
949
|
-
content =
|
|
1043
|
+
if (fs3.existsSync(gitignorePath)) {
|
|
1044
|
+
content = fs3.readFileSync(gitignorePath, "utf8");
|
|
950
1045
|
const lines = content.split("\n");
|
|
951
1046
|
for (const line of lines) {
|
|
952
1047
|
const trimmed = line.trim();
|
|
@@ -960,7 +1055,7 @@ function updateGitignore(entry) {
|
|
|
960
1055
|
}
|
|
961
1056
|
content += `${entry}
|
|
962
1057
|
`;
|
|
963
|
-
|
|
1058
|
+
fs3.writeFileSync(gitignorePath, content, "utf8");
|
|
964
1059
|
return { success: true };
|
|
965
1060
|
} catch (error) {
|
|
966
1061
|
return {
|
|
@@ -972,285 +1067,211 @@ function updateGitignore(entry) {
|
|
|
972
1067
|
|
|
973
1068
|
// src/commands/link.ts
|
|
974
1069
|
var linkCommand = new import_commander3.Command("link").description(
|
|
975
|
-
"Link this project
|
|
1070
|
+
"Link this project to a Commet organization. Re-run to switch orgs."
|
|
976
1071
|
).option(
|
|
977
1072
|
"--org <slug-or-id>",
|
|
978
1073
|
"Organization slug or ID \u2014 skips interactive selection"
|
|
1074
|
+
).option("--clear", "Unlink project from its organization").option(
|
|
1075
|
+
"--output <format>",
|
|
1076
|
+
"Output format: human (default) or agent",
|
|
1077
|
+
"human"
|
|
979
1078
|
).addHelpText(
|
|
980
1079
|
"after",
|
|
981
1080
|
`
|
|
982
1081
|
Examples:
|
|
983
1082
|
$ commet link Interactive \u2014 choose from a list
|
|
984
1083
|
$ commet link --org acme Non-interactive \u2014 match by slug or ID
|
|
985
|
-
|
|
986
|
-
|
|
1084
|
+
$ commet link --org other Re-run to switch organization
|
|
1085
|
+
$ commet link --clear Unlink project
|
|
987
1086
|
`
|
|
988
1087
|
).action(async (options) => {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
import_chalk4.default.dim(`Organization: ${config?.orgName} \xB7 ${config?.mode}`)
|
|
999
|
-
);
|
|
1000
|
-
console.log(
|
|
1001
|
-
import_chalk4.default.dim(
|
|
1002
|
-
"\nRun `commet unlink` first if you want to change the organization"
|
|
1003
|
-
)
|
|
1004
|
-
);
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
|
-
const spinner = (0, import_ora3.default)("Fetching organizations...").start();
|
|
1008
|
-
const result = await apiRequest(
|
|
1009
|
-
`${BASE_URL}/api/cli/organizations`
|
|
1010
|
-
);
|
|
1011
|
-
if (result.error || !result.data) {
|
|
1012
|
-
spinner.fail("Failed to fetch organizations");
|
|
1013
|
-
console.error(import_chalk4.default.red("Error:"), result.error);
|
|
1014
|
-
process.exit(1);
|
|
1015
|
-
}
|
|
1016
|
-
const { organizations } = result.data;
|
|
1017
|
-
if (organizations.length === 0) {
|
|
1018
|
-
spinner.stop();
|
|
1019
|
-
console.log(import_chalk4.default.yellow("\u26A0 No organizations found"));
|
|
1020
|
-
console.log(
|
|
1021
|
-
import_chalk4.default.dim("Create an organization at https://commet.co first")
|
|
1022
|
-
);
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
spinner.stop();
|
|
1026
|
-
let selectedOrg;
|
|
1027
|
-
if (options.org) {
|
|
1028
|
-
selectedOrg = organizations.find(
|
|
1029
|
-
(org) => org.slug === options.org || org.id === options.org
|
|
1030
|
-
);
|
|
1031
|
-
if (!selectedOrg) {
|
|
1032
|
-
console.log(import_chalk4.default.red(`\u2717 Organization "${options.org}" not found`));
|
|
1033
|
-
console.log(import_chalk4.default.dim("\nAvailable organizations:"));
|
|
1034
|
-
for (const org of organizations) {
|
|
1035
|
-
console.log(import_chalk4.default.dim(` ${org.slug} (${org.mode})`));
|
|
1088
|
+
const agentMode = isAgentMode(options);
|
|
1089
|
+
if (options.clear) {
|
|
1090
|
+
if (!projectConfigExists()) {
|
|
1091
|
+
if (agentMode) {
|
|
1092
|
+
console.log(JSON.stringify({ success: true, message: "Not linked" }));
|
|
1093
|
+
} else {
|
|
1094
|
+
console.log(
|
|
1095
|
+
import_chalk6.default.yellow("\u26A0 This project is not linked to any organization")
|
|
1096
|
+
);
|
|
1036
1097
|
}
|
|
1037
|
-
process.exit(1);
|
|
1038
|
-
}
|
|
1039
|
-
} else {
|
|
1040
|
-
let orgId;
|
|
1041
|
-
try {
|
|
1042
|
-
orgId = await (0, import_prompts2.select)({
|
|
1043
|
-
message: "Select organization:",
|
|
1044
|
-
choices: organizations.map((org) => ({
|
|
1045
|
-
name: `${org.name} ${import_chalk4.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
|
|
1046
|
-
value: org.id
|
|
1047
|
-
})),
|
|
1048
|
-
theme: promptTheme
|
|
1049
|
-
});
|
|
1050
|
-
} catch (_error) {
|
|
1051
|
-
console.log(import_chalk4.default.yellow("\n\u26A0 Link cancelled"));
|
|
1052
1098
|
return;
|
|
1053
1099
|
}
|
|
1054
|
-
|
|
1055
|
-
if (
|
|
1056
|
-
console.log(
|
|
1057
|
-
process.exit(1);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
saveProjectConfig({
|
|
1061
|
-
orgId: selectedOrg.id,
|
|
1062
|
-
orgName: selectedOrg.name,
|
|
1063
|
-
mode: selectedOrg.mode
|
|
1064
|
-
});
|
|
1065
|
-
const gitignoreResult = updateGitignore(".commet/");
|
|
1066
|
-
console.log(import_chalk4.default.green("\n\u2713 Project linked successfully"));
|
|
1067
|
-
console.log(
|
|
1068
|
-
import_chalk4.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
1069
|
-
);
|
|
1070
|
-
if (gitignoreResult.success) {
|
|
1071
|
-
console.log(import_chalk4.default.green("\u2713 Updated .gitignore"));
|
|
1072
|
-
} else {
|
|
1073
|
-
console.log(import_chalk4.default.yellow("\u26A0 No .gitignore found"));
|
|
1074
|
-
console.log(import_chalk4.default.dim("Add .commet/ to your .gitignore file"));
|
|
1075
|
-
}
|
|
1076
|
-
console.log(import_chalk4.default.dim("\nRun 'commet pull' to generate TypeScript types"));
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
// src/commands/list.ts
|
|
1080
|
-
var import_chalk5 = __toESM(require("chalk"));
|
|
1081
|
-
var import_commander4 = require("commander");
|
|
1082
|
-
var import_ora4 = __toESM(require("ora"));
|
|
1083
|
-
var validTypes = ["features", "seats", "plans"];
|
|
1084
|
-
var listCommand = new import_commander4.Command("list").description(
|
|
1085
|
-
"List resources from your linked organization. Shows features, seat types, or plans currently configured on remote."
|
|
1086
|
-
).argument("<type>", "What to list: features, seats, or plans").option("--json", "Output as JSON array").addHelpText(
|
|
1087
|
-
"after",
|
|
1088
|
-
`
|
|
1089
|
-
Examples:
|
|
1090
|
-
$ commet list features Show all features with type and description
|
|
1091
|
-
$ commet list plans Show all plans
|
|
1092
|
-
$ commet list seats Show all seat types
|
|
1093
|
-
$ commet list features --json JSON array for agent/CI use
|
|
1094
|
-
`
|
|
1095
|
-
).action(async (type, options) => {
|
|
1096
|
-
const jsonMode = options.json;
|
|
1097
|
-
if (!validTypes.includes(type)) {
|
|
1098
|
-
if (jsonMode) {
|
|
1099
|
-
console.log(
|
|
1100
|
-
JSON.stringify({
|
|
1101
|
-
error: `Invalid type "${type}". Use: features, seats, or plans`
|
|
1102
|
-
})
|
|
1103
|
-
);
|
|
1100
|
+
clearProjectConfig();
|
|
1101
|
+
if (agentMode) {
|
|
1102
|
+
console.log(JSON.stringify({ success: true, action: "unlinked" }));
|
|
1104
1103
|
} else {
|
|
1105
|
-
console.log(
|
|
1106
|
-
import_chalk5.default.red('\u2717 Invalid type. Use "features", "seats", or "plans"')
|
|
1107
|
-
);
|
|
1108
|
-
console.log(import_chalk5.default.dim("\nExamples:"));
|
|
1109
|
-
console.log(import_chalk5.default.dim(" commet list features"));
|
|
1110
|
-
console.log(import_chalk5.default.dim(" commet list seats"));
|
|
1111
|
-
console.log(import_chalk5.default.dim(" commet list plans"));
|
|
1104
|
+
console.log(import_chalk6.default.green("\u2713 Project unlinked"));
|
|
1112
1105
|
}
|
|
1113
|
-
|
|
1106
|
+
return;
|
|
1114
1107
|
}
|
|
1115
1108
|
if (!authExists()) {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1122
|
-
process.exit(1);
|
|
1123
|
-
}
|
|
1124
|
-
if (!projectConfigExists()) {
|
|
1125
|
-
if (jsonMode) {
|
|
1126
|
-
console.log(JSON.stringify({ error: "Project not linked" }));
|
|
1127
|
-
} else {
|
|
1128
|
-
console.log(import_chalk5.default.red("\u2717 Project not linked"));
|
|
1129
|
-
console.log(
|
|
1130
|
-
import_chalk5.default.dim("Run `commet link` first to connect to an organization")
|
|
1131
|
-
);
|
|
1132
|
-
}
|
|
1133
|
-
process.exit(1);
|
|
1134
|
-
}
|
|
1135
|
-
const projectConfig = loadProjectConfig();
|
|
1136
|
-
if (!projectConfig) {
|
|
1137
|
-
if (jsonMode) {
|
|
1138
|
-
console.log(JSON.stringify({ error: "Invalid project configuration" }));
|
|
1139
|
-
} else {
|
|
1140
|
-
console.log(import_chalk5.default.red("\u2717 Invalid project configuration"));
|
|
1141
|
-
}
|
|
1142
|
-
process.exit(1);
|
|
1109
|
+
exitWithError({
|
|
1110
|
+
code: "auth_required",
|
|
1111
|
+
message: "Not authenticated",
|
|
1112
|
+
action: "commet login"
|
|
1113
|
+
});
|
|
1143
1114
|
}
|
|
1144
|
-
const
|
|
1115
|
+
const currentConfig = projectConfigExists() ? loadProjectConfig() : null;
|
|
1116
|
+
const spinner = agentMode ? null : (0, import_ora4.default)("Fetching organizations...").start();
|
|
1145
1117
|
const result = await apiRequest(
|
|
1146
|
-
`${BASE_URL}/api/cli/
|
|
1118
|
+
`${BASE_URL}/api/cli/organizations`
|
|
1147
1119
|
);
|
|
1148
1120
|
if (result.error || !result.data) {
|
|
1149
|
-
if (
|
|
1121
|
+
if (agentMode) {
|
|
1150
1122
|
console.log(JSON.stringify({ error: result.error }));
|
|
1151
1123
|
} else {
|
|
1152
|
-
spinner?.fail(
|
|
1153
|
-
console.error(
|
|
1124
|
+
spinner?.fail("Failed to fetch organizations");
|
|
1125
|
+
console.error(import_chalk6.default.red("Error:"), result.error?.message);
|
|
1154
1126
|
}
|
|
1155
1127
|
process.exit(1);
|
|
1156
1128
|
}
|
|
1157
|
-
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1160
|
-
if (
|
|
1161
|
-
console.log(JSON.stringify(features));
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1164
|
-
if (features.length === 0) {
|
|
1165
|
-
console.log(import_chalk5.default.yellow("\u26A0 No features found"));
|
|
1129
|
+
const { organizations } = result.data;
|
|
1130
|
+
if (organizations.length === 0) {
|
|
1131
|
+
spinner?.stop();
|
|
1132
|
+
if (agentMode) {
|
|
1166
1133
|
console.log(
|
|
1167
|
-
|
|
1134
|
+
JSON.stringify({
|
|
1135
|
+
error: {
|
|
1136
|
+
code: "no_organizations",
|
|
1137
|
+
message: "No organizations found"
|
|
1138
|
+
}
|
|
1139
|
+
})
|
|
1168
1140
|
);
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
console.log(import_chalk5.default.bold(`
|
|
1172
|
-
Features (${features.length})
|
|
1173
|
-
`));
|
|
1174
|
-
for (const feature of features) {
|
|
1141
|
+
} else {
|
|
1142
|
+
console.log(import_chalk6.default.yellow("\u26A0 No organizations found"));
|
|
1175
1143
|
console.log(
|
|
1176
|
-
|
|
1144
|
+
import_chalk6.default.dim("Create an organization at https://commet.co first")
|
|
1177
1145
|
);
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1146
|
+
}
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
spinner?.stop();
|
|
1150
|
+
let selectedOrg;
|
|
1151
|
+
if (options.org) {
|
|
1152
|
+
selectedOrg = organizations.find(
|
|
1153
|
+
(org) => org.slug === options.org || org.id === options.org
|
|
1154
|
+
);
|
|
1155
|
+
if (!selectedOrg) {
|
|
1156
|
+
if (agentMode) {
|
|
1157
|
+
console.log(
|
|
1158
|
+
JSON.stringify({
|
|
1159
|
+
error: {
|
|
1160
|
+
code: "org_not_found",
|
|
1161
|
+
message: `Organization "${options.org}" not found`
|
|
1162
|
+
},
|
|
1163
|
+
organizations: organizations.map((o) => ({
|
|
1164
|
+
slug: o.slug,
|
|
1165
|
+
mode: o.mode
|
|
1166
|
+
}))
|
|
1167
|
+
})
|
|
1168
|
+
);
|
|
1169
|
+
} else {
|
|
1170
|
+
console.log(import_chalk6.default.red(`\u2717 Organization "${options.org}" not found`));
|
|
1171
|
+
console.log(import_chalk6.default.dim("\nAvailable organizations:"));
|
|
1172
|
+
for (const org of organizations) {
|
|
1173
|
+
console.log(import_chalk6.default.dim(` ${org.slug} (${org.mode})`));
|
|
1174
|
+
}
|
|
1181
1175
|
}
|
|
1182
|
-
|
|
1176
|
+
process.exit(1);
|
|
1183
1177
|
}
|
|
1184
|
-
} else
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1178
|
+
} else {
|
|
1179
|
+
let orgId;
|
|
1180
|
+
try {
|
|
1181
|
+
orgId = await (0, import_prompts2.select)({
|
|
1182
|
+
message: currentConfig ? `Switch from ${currentConfig.orgName}? Select new org:` : "Select organization:",
|
|
1183
|
+
choices: organizations.map((org) => {
|
|
1184
|
+
const isCurrent = currentConfig?.orgId === org.id;
|
|
1185
|
+
const label = isCurrent ? `${org.name} ${import_chalk6.default.dim(`(${org.slug}) \xB7 ${org.mode}`)} ${import_chalk6.default.green("\u2190 current")}` : `${org.name} ${import_chalk6.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`;
|
|
1186
|
+
return { name: label, value: org.id };
|
|
1187
|
+
}),
|
|
1188
|
+
theme: promptTheme
|
|
1189
|
+
});
|
|
1190
|
+
} catch (_error) {
|
|
1191
|
+
console.log(import_chalk6.default.yellow("\n\u26A0 Cancelled"));
|
|
1188
1192
|
return;
|
|
1189
1193
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1194
|
+
selectedOrg = organizations.find((org) => org.id === orgId);
|
|
1195
|
+
if (!selectedOrg) {
|
|
1196
|
+
exitWithError({
|
|
1197
|
+
code: "org_not_found",
|
|
1198
|
+
message: "Organization not found"
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (currentConfig?.orgId === selectedOrg.id) {
|
|
1203
|
+
if (agentMode) {
|
|
1192
1204
|
console.log(
|
|
1193
|
-
|
|
1205
|
+
JSON.stringify({
|
|
1206
|
+
success: true,
|
|
1207
|
+
action: "unchanged",
|
|
1208
|
+
organization: {
|
|
1209
|
+
id: selectedOrg.id,
|
|
1210
|
+
name: selectedOrg.name,
|
|
1211
|
+
slug: selectedOrg.slug,
|
|
1212
|
+
mode: selectedOrg.mode
|
|
1213
|
+
}
|
|
1214
|
+
})
|
|
1194
1215
|
);
|
|
1195
|
-
|
|
1196
|
-
}
|
|
1197
|
-
console.log(import_chalk5.default.bold(`
|
|
1198
|
-
Seat Types (${seatTypes.length})
|
|
1199
|
-
`));
|
|
1200
|
-
for (const seatType of seatTypes) {
|
|
1216
|
+
} else {
|
|
1201
1217
|
console.log(
|
|
1202
|
-
|
|
1203
|
-
|
|
1218
|
+
import_chalk6.default.green(
|
|
1219
|
+
`\u2713 Already linked to ${selectedOrg.name} (${selectedOrg.mode})`
|
|
1204
1220
|
)
|
|
1205
1221
|
);
|
|
1206
|
-
console.log(import_chalk5.default.dim(` ${seatType.name}`));
|
|
1207
|
-
if (seatType.description) {
|
|
1208
|
-
console.log(import_chalk5.default.dim(` ${seatType.description}`));
|
|
1209
|
-
}
|
|
1210
|
-
console.log("");
|
|
1211
1222
|
}
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
const action = currentConfig ? "switched" : "linked";
|
|
1226
|
+
saveProjectConfig({
|
|
1227
|
+
orgId: selectedOrg.id,
|
|
1228
|
+
orgName: selectedOrg.name,
|
|
1229
|
+
mode: selectedOrg.mode
|
|
1230
|
+
});
|
|
1231
|
+
const gitignoreResult = updateGitignore(".commet/");
|
|
1232
|
+
if (agentMode) {
|
|
1233
|
+
console.log(
|
|
1234
|
+
JSON.stringify({
|
|
1235
|
+
success: true,
|
|
1236
|
+
action,
|
|
1237
|
+
organization: {
|
|
1238
|
+
id: selectedOrg.id,
|
|
1239
|
+
name: selectedOrg.name,
|
|
1240
|
+
slug: selectedOrg.slug,
|
|
1241
|
+
mode: selectedOrg.mode
|
|
1242
|
+
}
|
|
1243
|
+
})
|
|
1244
|
+
);
|
|
1212
1245
|
} else {
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
if (
|
|
1219
|
-
console.log(
|
|
1220
|
-
console.log(import_chalk5.default.dim("Create plans in your Commet dashboard first"));
|
|
1221
|
-
return;
|
|
1222
|
-
}
|
|
1223
|
-
console.log(import_chalk5.default.bold(`
|
|
1224
|
-
Plans (${plans.length})
|
|
1225
|
-
`));
|
|
1226
|
-
for (const plan of plans) {
|
|
1227
|
-
console.log(import_chalk5.default.green(` ${plan.code}`));
|
|
1228
|
-
console.log(import_chalk5.default.dim(` ${plan.name}`));
|
|
1229
|
-
if (plan.description) {
|
|
1230
|
-
console.log(import_chalk5.default.dim(` ${plan.description}`));
|
|
1231
|
-
}
|
|
1232
|
-
console.log("");
|
|
1246
|
+
const verb = action === "switched" ? "Switched to" : "Linked to";
|
|
1247
|
+
console.log(
|
|
1248
|
+
import_chalk6.default.green(`
|
|
1249
|
+
\u2713 ${verb} ${selectedOrg.name} (${selectedOrg.mode})`)
|
|
1250
|
+
);
|
|
1251
|
+
if (gitignoreResult.success && action === "linked") {
|
|
1252
|
+
console.log(import_chalk6.default.green("\u2713 Updated .gitignore"));
|
|
1233
1253
|
}
|
|
1254
|
+
console.log(import_chalk6.default.dim("\nRun `commet pull` to sync your config"));
|
|
1234
1255
|
}
|
|
1235
1256
|
});
|
|
1236
1257
|
|
|
1237
1258
|
// src/commands/listen.ts
|
|
1238
1259
|
var import_ably = __toESM(require("ably"));
|
|
1239
|
-
var
|
|
1240
|
-
var
|
|
1260
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
1261
|
+
var import_commander4 = require("commander");
|
|
1241
1262
|
function printEventLine(line) {
|
|
1242
1263
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
1243
|
-
const eventName =
|
|
1244
|
-
const timing =
|
|
1264
|
+
const eventName = import_chalk7.default.yellow(line.event.padEnd(28));
|
|
1265
|
+
const timing = import_chalk7.default.dim(`(${line.ms}ms)`);
|
|
1245
1266
|
if ("error" in line) {
|
|
1246
1267
|
console.log(
|
|
1247
|
-
` ${
|
|
1268
|
+
` ${import_chalk7.default.dim(time)} ${eventName} \u2192 ${import_chalk7.default.red("Error")} ${timing}`
|
|
1248
1269
|
);
|
|
1249
|
-
console.log(` ${" ".repeat(12)}${
|
|
1270
|
+
console.log(` ${" ".repeat(12)}${import_chalk7.default.red(line.error)}`);
|
|
1250
1271
|
return;
|
|
1251
1272
|
}
|
|
1252
|
-
const status = line.statusCode < 400 ?
|
|
1253
|
-
console.log(` ${
|
|
1273
|
+
const status = line.statusCode < 400 ? import_chalk7.default.green(`${line.statusCode} OK`) : import_chalk7.default.red(`${line.statusCode} Error`);
|
|
1274
|
+
console.log(` ${import_chalk7.default.dim(time)} ${eventName} \u2192 ${status} ${timing}`);
|
|
1254
1275
|
}
|
|
1255
1276
|
function isListenMessage(data) {
|
|
1256
1277
|
if (typeof data !== "object" || data === null) return false;
|
|
@@ -1273,7 +1294,7 @@ function resolveTargetUrl(input2) {
|
|
|
1273
1294
|
}
|
|
1274
1295
|
return parsed.toString();
|
|
1275
1296
|
}
|
|
1276
|
-
var listenCommand = new
|
|
1297
|
+
var listenCommand = new import_commander4.Command("listen").description(
|
|
1277
1298
|
"Forward webhook events from Commet to your local server in real time. Opens a persistent connection and replays every event as an HTTP POST to your URL."
|
|
1278
1299
|
).argument(
|
|
1279
1300
|
"<url>",
|
|
@@ -1290,60 +1311,35 @@ Examples:
|
|
|
1290
1311
|
$ commet listen 3000 --events invoice.paid Only forward invoice.paid events
|
|
1291
1312
|
`
|
|
1292
1313
|
).action(async (url, options) => {
|
|
1293
|
-
const
|
|
1294
|
-
if (!auth) {
|
|
1295
|
-
console.log(import_chalk6.default.red("Not authenticated. Run: commet login"));
|
|
1296
|
-
process.exit(1);
|
|
1297
|
-
}
|
|
1298
|
-
const projectConfig = loadProjectConfig();
|
|
1299
|
-
if (!projectConfig) {
|
|
1300
|
-
console.log(import_chalk6.default.red("No project linked. Run: commet link"));
|
|
1301
|
-
process.exit(1);
|
|
1302
|
-
}
|
|
1314
|
+
const { orgId } = requireOrgContext();
|
|
1303
1315
|
let targetUrl;
|
|
1304
1316
|
try {
|
|
1305
1317
|
targetUrl = resolveTargetUrl(url);
|
|
1306
1318
|
} catch (error) {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1319
|
+
exitWithError({
|
|
1320
|
+
code: "invalid_url",
|
|
1321
|
+
message: error instanceof Error ? error.message : "Invalid URL"
|
|
1322
|
+
});
|
|
1311
1323
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
if (result.error) {
|
|
1323
|
-
console.log(import_chalk6.default.dim(result.error));
|
|
1324
|
-
}
|
|
1325
|
-
process.exit(1);
|
|
1326
|
-
}
|
|
1327
|
-
return result.data;
|
|
1328
|
-
}
|
|
1329
|
-
const initialSession = await fetchTokenRequest();
|
|
1330
|
-
const { sessionId, channelName, signingSecret, tokenRequest } = initialSession;
|
|
1331
|
-
async function refreshToken() {
|
|
1332
|
-
const result = await apiRequest(
|
|
1333
|
-
`${BASE_URL}/api/cli/listen/refresh`,
|
|
1334
|
-
{
|
|
1335
|
-
method: "POST",
|
|
1336
|
-
body: JSON.stringify({
|
|
1337
|
-
sessionId,
|
|
1338
|
-
organizationId: projectConfig.orgId
|
|
1339
|
-
})
|
|
1340
|
-
}
|
|
1341
|
-
);
|
|
1342
|
-
if (result.error || !result.data) {
|
|
1343
|
-
throw new Error(result.error ?? "Failed to refresh token");
|
|
1324
|
+
const projectConfig = loadProjectConfig();
|
|
1325
|
+
const organizationId = orgId === "__from_api_key__" ? void 0 : orgId;
|
|
1326
|
+
const orgName = projectConfig?.orgName ?? "API key";
|
|
1327
|
+
const startResult = await apiRequest(
|
|
1328
|
+
`${BASE_URL}/api/cli/listen/start`,
|
|
1329
|
+
{
|
|
1330
|
+
method: "POST",
|
|
1331
|
+
body: JSON.stringify({
|
|
1332
|
+
...organizationId ? { organizationId } : {}
|
|
1333
|
+
})
|
|
1344
1334
|
}
|
|
1345
|
-
|
|
1335
|
+
);
|
|
1336
|
+
if (startResult.error || !startResult.data) {
|
|
1337
|
+
exitWithError({
|
|
1338
|
+
code: "listen_failed",
|
|
1339
|
+
message: "Failed to start listen session"
|
|
1340
|
+
});
|
|
1346
1341
|
}
|
|
1342
|
+
const { sessionId, channelName, signingSecret, tokenRequest } = startResult.data;
|
|
1347
1343
|
let isFirstToken = true;
|
|
1348
1344
|
const ably = new import_ably.default.Realtime({
|
|
1349
1345
|
authCallback: async (_tokenParams, callback) => {
|
|
@@ -1353,8 +1349,20 @@ Examples:
|
|
|
1353
1349
|
callback(null, tokenRequest);
|
|
1354
1350
|
return;
|
|
1355
1351
|
}
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1352
|
+
const refreshResult = await apiRequest(
|
|
1353
|
+
`${BASE_URL}/api/cli/listen/refresh`,
|
|
1354
|
+
{
|
|
1355
|
+
method: "POST",
|
|
1356
|
+
body: JSON.stringify({
|
|
1357
|
+
sessionId,
|
|
1358
|
+
...organizationId ? { organizationId } : {}
|
|
1359
|
+
})
|
|
1360
|
+
}
|
|
1361
|
+
);
|
|
1362
|
+
if (refreshResult.error || !refreshResult.data) {
|
|
1363
|
+
throw new Error("Failed to refresh token");
|
|
1364
|
+
}
|
|
1365
|
+
callback(null, refreshResult.data.tokenRequest);
|
|
1358
1366
|
} catch (error) {
|
|
1359
1367
|
callback(
|
|
1360
1368
|
error instanceof Error ? error.message : "Token refresh failed",
|
|
@@ -1366,27 +1374,25 @@ Examples:
|
|
|
1366
1374
|
let wasConnected = false;
|
|
1367
1375
|
ably.connection.on("connected", () => {
|
|
1368
1376
|
if (wasConnected) {
|
|
1369
|
-
console.log(
|
|
1377
|
+
console.log(import_chalk7.default.green(" \u2713 Reconnected"));
|
|
1370
1378
|
}
|
|
1371
1379
|
wasConnected = true;
|
|
1372
1380
|
});
|
|
1373
1381
|
ably.connection.on("disconnected", () => {
|
|
1374
|
-
console.log(
|
|
1382
|
+
console.log(import_chalk7.default.yellow("\n \u26A0 Disconnected. Reconnecting..."));
|
|
1375
1383
|
});
|
|
1376
1384
|
ably.connection.on("failed", () => {
|
|
1377
1385
|
console.log(
|
|
1378
|
-
|
|
1386
|
+
import_chalk7.default.red("\n \u2717 Connection failed. Check your authentication.")
|
|
1379
1387
|
);
|
|
1380
1388
|
process.exit(1);
|
|
1381
1389
|
});
|
|
1382
1390
|
const channel = ably.channels.get(channelName);
|
|
1383
1391
|
console.log("");
|
|
1384
|
-
console.log(
|
|
1385
|
-
|
|
1386
|
-
);
|
|
1387
|
-
console.log(
|
|
1388
|
-
console.log(import_chalk6.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
|
|
1389
|
-
console.log(import_chalk6.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
|
|
1392
|
+
console.log(import_chalk7.default.green(` \u2713 Authenticated (org: ${orgName})`));
|
|
1393
|
+
console.log(import_chalk7.default.green(" \u2713 Connected to Commet webhook stream"));
|
|
1394
|
+
console.log(import_chalk7.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
|
|
1395
|
+
console.log(import_chalk7.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
|
|
1390
1396
|
console.log("");
|
|
1391
1397
|
console.log(" Ready! Listening for webhook events...");
|
|
1392
1398
|
console.log("");
|
|
@@ -1403,7 +1409,11 @@ Examples:
|
|
|
1403
1409
|
body: JSON.stringify(payload)
|
|
1404
1410
|
}).then((response) => {
|
|
1405
1411
|
const ms = Math.round(performance.now() - start);
|
|
1406
|
-
printEventLine({
|
|
1412
|
+
printEventLine({
|
|
1413
|
+
event,
|
|
1414
|
+
statusCode: response.status,
|
|
1415
|
+
ms
|
|
1416
|
+
});
|
|
1407
1417
|
}).catch((error) => {
|
|
1408
1418
|
const ms = Math.round(performance.now() - start);
|
|
1409
1419
|
printEventLine({
|
|
@@ -1417,13 +1427,13 @@ Examples:
|
|
|
1417
1427
|
process.on("SIGINT", async () => {
|
|
1418
1428
|
if (isShuttingDown) return;
|
|
1419
1429
|
isShuttingDown = true;
|
|
1420
|
-
console.log(
|
|
1430
|
+
console.log(import_chalk7.default.dim("\n Disconnecting..."));
|
|
1421
1431
|
ably.close();
|
|
1422
1432
|
await apiRequest(`${BASE_URL}/api/cli/listen/stop`, {
|
|
1423
1433
|
method: "POST",
|
|
1424
1434
|
body: JSON.stringify({
|
|
1425
1435
|
sessionId,
|
|
1426
|
-
organizationId:
|
|
1436
|
+
...organizationId ? { organizationId } : {}
|
|
1427
1437
|
})
|
|
1428
1438
|
});
|
|
1429
1439
|
process.exit(0);
|
|
@@ -1431,15 +1441,28 @@ Examples:
|
|
|
1431
1441
|
});
|
|
1432
1442
|
|
|
1433
1443
|
// src/commands/login.ts
|
|
1434
|
-
var
|
|
1435
|
-
var
|
|
1436
|
-
var loginCommand = new
|
|
1444
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
1445
|
+
var import_commander5 = require("commander");
|
|
1446
|
+
var loginCommand = new import_commander5.Command("login").description(
|
|
1437
1447
|
"Authenticate with Commet via browser. Opens a device-code flow \u2014 you confirm in the browser and the CLI stores your token locally at ~/.commet/auth.json."
|
|
1438
1448
|
).action(async () => {
|
|
1449
|
+
if (process.env.COMMET_API_KEY) {
|
|
1450
|
+
if (isAgentMode()) {
|
|
1451
|
+
console.log(
|
|
1452
|
+
JSON.stringify({
|
|
1453
|
+
success: true,
|
|
1454
|
+
message: "Using COMMET_API_KEY \u2014 login not needed"
|
|
1455
|
+
})
|
|
1456
|
+
);
|
|
1457
|
+
} else {
|
|
1458
|
+
console.log(import_chalk8.default.green("\u2713 Using COMMET_API_KEY \u2014 login not needed"));
|
|
1459
|
+
}
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1439
1462
|
if (authExists()) {
|
|
1440
|
-
console.log(
|
|
1463
|
+
console.log(import_chalk8.default.yellow("\u26A0 You are already logged in."));
|
|
1441
1464
|
console.log(
|
|
1442
|
-
|
|
1465
|
+
import_chalk8.default.dim(
|
|
1443
1466
|
"Run `commet logout` first if you want to login with a different account."
|
|
1444
1467
|
)
|
|
1445
1468
|
);
|
|
@@ -1447,9 +1470,9 @@ var loginCommand = new import_commander6.Command("login").description(
|
|
|
1447
1470
|
}
|
|
1448
1471
|
const success = await performLogin();
|
|
1449
1472
|
if (success) {
|
|
1450
|
-
console.log(
|
|
1473
|
+
console.log(import_chalk8.default.green("\n\u2713 Authentication complete"));
|
|
1451
1474
|
console.log(
|
|
1452
|
-
|
|
1475
|
+
import_chalk8.default.dim(
|
|
1453
1476
|
"\nNext steps:\n 1. Run `commet link` to connect a project\n 2. Run `commet pull` to generate types\n"
|
|
1454
1477
|
)
|
|
1455
1478
|
);
|
|
@@ -1457,96 +1480,217 @@ var loginCommand = new import_commander6.Command("login").description(
|
|
|
1457
1480
|
});
|
|
1458
1481
|
|
|
1459
1482
|
// src/commands/logout.ts
|
|
1460
|
-
var
|
|
1461
|
-
var
|
|
1462
|
-
var logoutCommand = new
|
|
1483
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
1484
|
+
var import_commander6 = require("commander");
|
|
1485
|
+
var logoutCommand = new import_commander6.Command("logout").description(
|
|
1463
1486
|
"Log out and remove stored credentials from ~/.commet/auth.json."
|
|
1464
1487
|
).action(async () => {
|
|
1488
|
+
if (process.env.COMMET_API_KEY) {
|
|
1489
|
+
if (isAgentMode()) {
|
|
1490
|
+
console.log(
|
|
1491
|
+
JSON.stringify({
|
|
1492
|
+
success: true,
|
|
1493
|
+
message: "Using COMMET_API_KEY \u2014 no session to clear"
|
|
1494
|
+
})
|
|
1495
|
+
);
|
|
1496
|
+
} else {
|
|
1497
|
+
console.log(
|
|
1498
|
+
import_chalk9.default.green("\u2713 Using COMMET_API_KEY \u2014 no session to clear")
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1465
1503
|
if (!authExists()) {
|
|
1466
|
-
console.log(
|
|
1504
|
+
console.log(import_chalk9.default.yellow("\u26A0 You are not logged in."));
|
|
1467
1505
|
return;
|
|
1468
1506
|
}
|
|
1469
1507
|
clearAuth();
|
|
1470
|
-
console.log(
|
|
1508
|
+
console.log(import_chalk9.default.green("\u2713 Successfully logged out"));
|
|
1471
1509
|
});
|
|
1472
1510
|
|
|
1473
1511
|
// src/commands/orgs.ts
|
|
1474
|
-
var
|
|
1475
|
-
var
|
|
1512
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
1513
|
+
var import_commander7 = require("commander");
|
|
1476
1514
|
var import_ora5 = __toESM(require("ora"));
|
|
1477
|
-
var orgsCommand = new
|
|
1515
|
+
var orgsCommand = new import_commander7.Command("orgs").description(
|
|
1478
1516
|
"List all organizations you have access to. Shows name, slug, mode (live/sandbox), and which one is currently linked."
|
|
1479
|
-
).option(
|
|
1517
|
+
).option(
|
|
1518
|
+
"--output <format>",
|
|
1519
|
+
"Output format: human (default) or agent",
|
|
1520
|
+
"human"
|
|
1521
|
+
).addHelpText(
|
|
1480
1522
|
"after",
|
|
1481
1523
|
`
|
|
1482
1524
|
Examples:
|
|
1483
1525
|
$ commet orgs Show orgs with current selection marked
|
|
1484
|
-
$ commet orgs --
|
|
1526
|
+
$ commet orgs --output agent JSON array for agent/CI use
|
|
1485
1527
|
|
|
1486
1528
|
The slug shown here is what you pass to 'commet link --org <slug>'.
|
|
1487
1529
|
`
|
|
1488
1530
|
).action(async (options) => {
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1531
|
+
const agentMode = isAgentMode(options);
|
|
1532
|
+
requireAuth();
|
|
1533
|
+
const spinner = agentMode ? null : (0, import_ora5.default)("Fetching organizations...").start();
|
|
1534
|
+
const result = await apiRequest(
|
|
1535
|
+
`${BASE_URL}/api/cli/organizations`
|
|
1536
|
+
);
|
|
1537
|
+
if (result.error || !result.data) {
|
|
1538
|
+
if (agentMode) {
|
|
1539
|
+
console.log(JSON.stringify({ error: result.error }));
|
|
1493
1540
|
} else {
|
|
1494
|
-
|
|
1495
|
-
console.
|
|
1541
|
+
spinner?.fail("Failed to fetch organizations");
|
|
1542
|
+
console.error(import_chalk10.default.red("Error:"), result.error?.message);
|
|
1543
|
+
}
|
|
1544
|
+
process.exit(1);
|
|
1545
|
+
}
|
|
1546
|
+
spinner?.stop();
|
|
1547
|
+
const { organizations } = result.data;
|
|
1548
|
+
if (agentMode) {
|
|
1549
|
+
console.log(JSON.stringify(organizations));
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
if (organizations.length === 0) {
|
|
1553
|
+
console.log(import_chalk10.default.yellow("\u26A0 No organizations found"));
|
|
1554
|
+
console.log(
|
|
1555
|
+
import_chalk10.default.dim("Create an organization at https://commet.co first")
|
|
1556
|
+
);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const currentProject = loadProjectConfig();
|
|
1560
|
+
console.log(import_chalk10.default.bold(`
|
|
1561
|
+
Organizations (${organizations.length})
|
|
1562
|
+
`));
|
|
1563
|
+
for (const org of organizations) {
|
|
1564
|
+
const isCurrent = currentProject?.orgId === org.id;
|
|
1565
|
+
const marker = isCurrent ? import_chalk10.default.green("\u25CF") : import_chalk10.default.dim("\u25CB");
|
|
1566
|
+
const mode = org.mode === "live" ? import_chalk10.default.green(org.mode) : import_chalk10.default.yellow(org.mode);
|
|
1567
|
+
console.log(
|
|
1568
|
+
` ${marker} ${org.name} ${import_chalk10.default.dim(`(${org.slug})`)} ${mode}`
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
console.log("");
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
// src/commands/pull.ts
|
|
1575
|
+
var fs5 = __toESM(require("fs"));
|
|
1576
|
+
var path5 = __toESM(require("path"));
|
|
1577
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
1578
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
1579
|
+
var import_commander8 = require("commander");
|
|
1580
|
+
var import_ora6 = __toESM(require("ora"));
|
|
1581
|
+
|
|
1582
|
+
// src/utils/config-loader.ts
|
|
1583
|
+
var fs4 = __toESM(require("fs"));
|
|
1584
|
+
var path4 = __toESM(require("path"));
|
|
1585
|
+
var import_jiti = require("jiti");
|
|
1586
|
+
var CONFIG_NAMES = [
|
|
1587
|
+
"commet.config.ts",
|
|
1588
|
+
"commet.config.js",
|
|
1589
|
+
"commet.config.mjs"
|
|
1590
|
+
];
|
|
1591
|
+
function findConfigFile(cwd) {
|
|
1592
|
+
for (const name of CONFIG_NAMES) {
|
|
1593
|
+
const fullPath = path4.resolve(cwd, name);
|
|
1594
|
+
if (fs4.existsSync(fullPath)) {
|
|
1595
|
+
return fullPath;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
async function loadBillingConfig(cwd) {
|
|
1601
|
+
const configPath = findConfigFile(cwd);
|
|
1602
|
+
if (!configPath) {
|
|
1603
|
+
throw new Error(
|
|
1604
|
+
`No commet.config.ts found in ${cwd}. Create one with defineConfig() or run 'commet pull' to generate it.`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
const jiti = (0, import_jiti.createJiti)(configPath, { interopDefault: true });
|
|
1608
|
+
const mod = await jiti.import(configPath);
|
|
1609
|
+
if (!mod || typeof mod !== "object") {
|
|
1610
|
+
throw new Error(`${configPath}: failed to load config module`);
|
|
1611
|
+
}
|
|
1612
|
+
const moduleRecord = mod;
|
|
1613
|
+
if (!moduleRecord.default) {
|
|
1614
|
+
throw new Error(
|
|
1615
|
+
`${configPath}: must use \`export default defineConfig({...})\``
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
const config = moduleRecord.default;
|
|
1619
|
+
validateConfig(config, configPath);
|
|
1620
|
+
return { config, configPath };
|
|
1621
|
+
}
|
|
1622
|
+
var VALID_FEATURE_TYPES = /* @__PURE__ */ new Set(["boolean", "usage", "seats"]);
|
|
1623
|
+
var VALID_INTERVALS = /* @__PURE__ */ new Set([
|
|
1624
|
+
"weekly",
|
|
1625
|
+
"monthly",
|
|
1626
|
+
"quarterly",
|
|
1627
|
+
"yearly",
|
|
1628
|
+
"one_time"
|
|
1629
|
+
]);
|
|
1630
|
+
function validateConfig(config, configPath) {
|
|
1631
|
+
if (!config || typeof config !== "object") {
|
|
1632
|
+
throw new Error(`${configPath}: config must be an object`);
|
|
1633
|
+
}
|
|
1634
|
+
if (!config.features || typeof config.features !== "object") {
|
|
1635
|
+
throw new Error(`${configPath}: config.features must be an object`);
|
|
1636
|
+
}
|
|
1637
|
+
if (!config.plans || typeof config.plans !== "object") {
|
|
1638
|
+
throw new Error(`${configPath}: config.plans must be an object`);
|
|
1639
|
+
}
|
|
1640
|
+
for (const [code, feature] of Object.entries(config.features)) {
|
|
1641
|
+
if (!feature.name || typeof feature.name !== "string") {
|
|
1642
|
+
throw new Error(`Feature "${code}": name is required`);
|
|
1643
|
+
}
|
|
1644
|
+
if (!VALID_FEATURE_TYPES.has(feature.type)) {
|
|
1645
|
+
throw new Error(
|
|
1646
|
+
`Feature "${code}": type must be one of: boolean, usage, seats`
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
for (const [code, plan] of Object.entries(config.plans)) {
|
|
1651
|
+
if (!plan.name || typeof plan.name !== "string") {
|
|
1652
|
+
throw new Error(`Plan "${code}": name is required`);
|
|
1653
|
+
}
|
|
1654
|
+
if (!Array.isArray(plan.prices)) {
|
|
1655
|
+
throw new Error(`Plan "${code}": prices must be an array`);
|
|
1656
|
+
}
|
|
1657
|
+
for (const price of plan.prices) {
|
|
1658
|
+
if (!VALID_INTERVALS.has(price.interval)) {
|
|
1659
|
+
throw new Error(
|
|
1660
|
+
`Plan "${code}": price interval "${price.interval}" is not valid`
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
if (typeof price.amount !== "number") {
|
|
1664
|
+
throw new Error(`Plan "${code}": price amount must be a number`);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
if (plan.prices.length > 0) {
|
|
1668
|
+
if (!plan.defaultInterval) {
|
|
1669
|
+
throw new Error(
|
|
1670
|
+
`Plan "${code}": defaultInterval is required when prices are defined`
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
const priceIntervals = new Set(plan.prices.map((p) => p.interval));
|
|
1674
|
+
if (!priceIntervals.has(plan.defaultInterval)) {
|
|
1675
|
+
throw new Error(
|
|
1676
|
+
`Plan "${code}": defaultInterval "${plan.defaultInterval}" does not match any price interval (${[...priceIntervals].join(", ")})`
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1496
1679
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
console.log(JSON.stringify({ error: result.error }));
|
|
1506
|
-
} else {
|
|
1507
|
-
spinner?.fail("Failed to fetch organizations");
|
|
1508
|
-
console.error(import_chalk9.default.red("Error:"), result.error);
|
|
1680
|
+
if (plan.features) {
|
|
1681
|
+
for (const featureCode of Object.keys(plan.features)) {
|
|
1682
|
+
if (!config.features[featureCode]) {
|
|
1683
|
+
throw new Error(
|
|
1684
|
+
`Plan "${code}": references feature "${featureCode}" which is not defined in config.features`
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1509
1688
|
}
|
|
1510
|
-
process.exit(1);
|
|
1511
|
-
}
|
|
1512
|
-
spinner?.stop();
|
|
1513
|
-
const { organizations } = result.data;
|
|
1514
|
-
if (jsonMode) {
|
|
1515
|
-
console.log(JSON.stringify(organizations));
|
|
1516
|
-
return;
|
|
1517
|
-
}
|
|
1518
|
-
if (organizations.length === 0) {
|
|
1519
|
-
console.log(import_chalk9.default.yellow("\u26A0 No organizations found"));
|
|
1520
|
-
console.log(
|
|
1521
|
-
import_chalk9.default.dim("Create an organization at https://commet.co first")
|
|
1522
|
-
);
|
|
1523
|
-
return;
|
|
1524
1689
|
}
|
|
1525
|
-
|
|
1526
|
-
console.log(import_chalk9.default.bold(`
|
|
1527
|
-
Organizations (${organizations.length})
|
|
1528
|
-
`));
|
|
1529
|
-
for (const org of organizations) {
|
|
1530
|
-
const isCurrent = currentProject?.orgId === org.id;
|
|
1531
|
-
const marker = isCurrent ? import_chalk9.default.green("\u25CF") : import_chalk9.default.dim("\u25CB");
|
|
1532
|
-
const mode = org.mode === "live" ? import_chalk9.default.green(org.mode) : import_chalk9.default.yellow(org.mode);
|
|
1533
|
-
console.log(
|
|
1534
|
-
` ${marker} ${org.name} ${import_chalk9.default.dim(`(${org.slug})`)} ${mode}`
|
|
1535
|
-
);
|
|
1536
|
-
}
|
|
1537
|
-
console.log("");
|
|
1538
|
-
});
|
|
1539
|
-
|
|
1540
|
-
// src/commands/pull.ts
|
|
1541
|
-
var fs5 = __toESM(require("fs"));
|
|
1542
|
-
var path5 = __toESM(require("path"));
|
|
1543
|
-
var import_prompts3 = require("@inquirer/prompts");
|
|
1544
|
-
var import_chalk11 = __toESM(require("chalk"));
|
|
1545
|
-
var import_commander9 = require("commander");
|
|
1546
|
-
var import_ora6 = __toESM(require("ora"));
|
|
1690
|
+
}
|
|
1547
1691
|
|
|
1548
1692
|
// src/utils/diff.ts
|
|
1549
|
-
var
|
|
1693
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1550
1694
|
function computeDiff(config, remote) {
|
|
1551
1695
|
const remoteFeatureMap = new Map(remote.features.map((f) => [f.code, f]));
|
|
1552
1696
|
const remotePlanMap = new Map(remote.plans.map((p) => [p.code, p]));
|
|
@@ -1633,42 +1777,42 @@ function computeDiff(config, remote) {
|
|
|
1633
1777
|
}
|
|
1634
1778
|
function formatDiff(diff) {
|
|
1635
1779
|
const lines = [];
|
|
1636
|
-
lines.push(
|
|
1780
|
+
lines.push(import_chalk11.default.bold("\nFeatures:"));
|
|
1637
1781
|
for (const change of diff.features.changes) {
|
|
1638
1782
|
if (change.action === "create") {
|
|
1639
|
-
lines.push(
|
|
1783
|
+
lines.push(import_chalk11.default.green(` + ${change.code}`));
|
|
1640
1784
|
} else if (change.action === "update") {
|
|
1641
|
-
lines.push(
|
|
1785
|
+
lines.push(import_chalk11.default.yellow(` ~ ${change.code}`));
|
|
1642
1786
|
for (const c of change.changes ?? []) {
|
|
1643
|
-
lines.push(
|
|
1787
|
+
lines.push(import_chalk11.default.dim(` ${c}`));
|
|
1644
1788
|
}
|
|
1645
1789
|
} else {
|
|
1646
|
-
lines.push(
|
|
1790
|
+
lines.push(import_chalk11.default.dim(` ${change.code}`));
|
|
1647
1791
|
}
|
|
1648
1792
|
}
|
|
1649
1793
|
if (diff.features.unmanaged.length > 0) {
|
|
1650
1794
|
lines.push(
|
|
1651
|
-
|
|
1795
|
+
import_chalk11.default.dim(
|
|
1652
1796
|
` ? unmanaged: ${diff.features.unmanaged.join(", ")} (not in config, left as-is)`
|
|
1653
1797
|
)
|
|
1654
1798
|
);
|
|
1655
1799
|
}
|
|
1656
|
-
lines.push(
|
|
1800
|
+
lines.push(import_chalk11.default.bold("\nPlans:"));
|
|
1657
1801
|
for (const change of diff.plans.changes) {
|
|
1658
1802
|
if (change.action === "create") {
|
|
1659
|
-
lines.push(
|
|
1803
|
+
lines.push(import_chalk11.default.green(` + ${change.code}`));
|
|
1660
1804
|
} else if (change.action === "update") {
|
|
1661
|
-
lines.push(
|
|
1805
|
+
lines.push(import_chalk11.default.yellow(` ~ ${change.code}`));
|
|
1662
1806
|
for (const c of change.changes ?? []) {
|
|
1663
|
-
lines.push(
|
|
1807
|
+
lines.push(import_chalk11.default.dim(` ${c}`));
|
|
1664
1808
|
}
|
|
1665
1809
|
} else {
|
|
1666
|
-
lines.push(
|
|
1810
|
+
lines.push(import_chalk11.default.dim(` ${change.code}`));
|
|
1667
1811
|
}
|
|
1668
1812
|
}
|
|
1669
1813
|
if (diff.plans.unmanaged.length > 0) {
|
|
1670
1814
|
lines.push(
|
|
1671
|
-
|
|
1815
|
+
import_chalk11.default.dim(
|
|
1672
1816
|
` ? unmanaged: ${diff.plans.unmanaged.join(", ")} (not in config, left as-is)`
|
|
1673
1817
|
)
|
|
1674
1818
|
);
|
|
@@ -1752,58 +1896,36 @@ function generateConfigFile(features, plans) {
|
|
|
1752
1896
|
}
|
|
1753
1897
|
|
|
1754
1898
|
// src/commands/pull.ts
|
|
1755
|
-
var pullCommand = new
|
|
1899
|
+
var pullCommand = new import_commander8.Command("pull").description(
|
|
1756
1900
|
"Fetch your billing config from Commet and generate (or update) commet.config.ts with features and plans."
|
|
1757
|
-
).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without writing any files").option(
|
|
1901
|
+
).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without writing any files").option(
|
|
1902
|
+
"--output <format>",
|
|
1903
|
+
"Output format: human (default) or agent",
|
|
1904
|
+
"human"
|
|
1905
|
+
).addHelpText(
|
|
1758
1906
|
"after",
|
|
1759
1907
|
`
|
|
1760
1908
|
Examples:
|
|
1761
1909
|
$ commet pull Interactive \u2014 shows diff, asks to confirm
|
|
1762
1910
|
$ commet pull --dry-run Preview changes without applying
|
|
1763
1911
|
$ commet pull --yes Apply without confirmation
|
|
1764
|
-
$ commet pull --
|
|
1912
|
+
$ commet pull --output agent --yes Agent/CI \u2014 structured JSON, no prompts
|
|
1913
|
+
$ COMMET_API_KEY=sk_... commet pull --yes CI pipeline
|
|
1765
1914
|
`
|
|
1766
1915
|
).action(async (options) => {
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
} else {
|
|
1772
|
-
console.log(import_chalk11.default.red("\u2717 Not authenticated"));
|
|
1773
|
-
console.log(import_chalk11.default.dim("Run `commet login` first"));
|
|
1774
|
-
}
|
|
1775
|
-
process.exit(1);
|
|
1776
|
-
}
|
|
1777
|
-
if (!projectConfigExists()) {
|
|
1778
|
-
if (jsonMode) {
|
|
1779
|
-
console.log(JSON.stringify({ error: "Project not linked" }));
|
|
1780
|
-
} else {
|
|
1781
|
-
console.log(import_chalk11.default.red("\u2717 Project not linked"));
|
|
1782
|
-
console.log(
|
|
1783
|
-
import_chalk11.default.dim("Run `commet link` first to connect to an organization")
|
|
1784
|
-
);
|
|
1785
|
-
}
|
|
1786
|
-
process.exit(1);
|
|
1787
|
-
}
|
|
1788
|
-
const projectConfig = loadProjectConfig();
|
|
1789
|
-
if (!projectConfig) {
|
|
1790
|
-
if (jsonMode) {
|
|
1791
|
-
console.log(JSON.stringify({ error: "Invalid project configuration" }));
|
|
1792
|
-
} else {
|
|
1793
|
-
console.log(import_chalk11.default.red("\u2717 Invalid project configuration"));
|
|
1794
|
-
}
|
|
1795
|
-
process.exit(1);
|
|
1796
|
-
}
|
|
1797
|
-
const spinner = jsonMode ? null : (0, import_ora6.default)("Fetching config from remote...").start();
|
|
1916
|
+
const agentMode = isAgentMode(options);
|
|
1917
|
+
const { orgId } = requireOrgContext();
|
|
1918
|
+
const spinner = agentMode ? null : (0, import_ora6.default)("Fetching config from remote...").start();
|
|
1919
|
+
const orgQuery = orgId === "__from_api_key__" ? "" : `?orgId=${orgId}`;
|
|
1798
1920
|
const result = await apiRequest(
|
|
1799
|
-
`${BASE_URL}/api/cli/pull
|
|
1921
|
+
`${BASE_URL}/api/cli/pull${orgQuery}`
|
|
1800
1922
|
);
|
|
1801
1923
|
if (result.error || !result.data) {
|
|
1802
|
-
if (
|
|
1924
|
+
if (agentMode) {
|
|
1803
1925
|
console.log(JSON.stringify({ error: result.error }));
|
|
1804
1926
|
} else {
|
|
1805
1927
|
spinner?.fail("Failed to fetch config");
|
|
1806
|
-
console.error(
|
|
1928
|
+
console.error(import_chalk12.default.red("Error:"), result.error?.message);
|
|
1807
1929
|
}
|
|
1808
1930
|
process.exit(1);
|
|
1809
1931
|
}
|
|
@@ -1814,7 +1936,7 @@ Examples:
|
|
|
1814
1936
|
const existingConfigPath = findConfigFile(process.cwd());
|
|
1815
1937
|
if (!existingConfigPath) {
|
|
1816
1938
|
if (options.dryRun) {
|
|
1817
|
-
if (
|
|
1939
|
+
if (agentMode) {
|
|
1818
1940
|
console.log(
|
|
1819
1941
|
JSON.stringify({
|
|
1820
1942
|
action: "create",
|
|
@@ -1825,7 +1947,7 @@ Examples:
|
|
|
1825
1947
|
);
|
|
1826
1948
|
} else {
|
|
1827
1949
|
console.log(
|
|
1828
|
-
|
|
1950
|
+
import_chalk12.default.green(
|
|
1829
1951
|
`
|
|
1830
1952
|
Would create commet.config.ts (${features.length} features, ${plans.length} plans)`
|
|
1831
1953
|
)
|
|
@@ -1834,7 +1956,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1834
1956
|
return;
|
|
1835
1957
|
}
|
|
1836
1958
|
fs5.writeFileSync(outputPath, configContent, "utf8");
|
|
1837
|
-
if (
|
|
1959
|
+
if (agentMode) {
|
|
1838
1960
|
console.log(
|
|
1839
1961
|
JSON.stringify({
|
|
1840
1962
|
action: "create",
|
|
@@ -1844,10 +1966,9 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1844
1966
|
})
|
|
1845
1967
|
);
|
|
1846
1968
|
} else {
|
|
1847
|
-
console.log(
|
|
1848
|
-
\u2713 Created commet.config.ts`));
|
|
1969
|
+
console.log(import_chalk12.default.green("\n\u2713 Created commet.config.ts"));
|
|
1849
1970
|
console.log(
|
|
1850
|
-
|
|
1971
|
+
import_chalk12.default.dim(` ${features.length} features, ${plans.length} plans`)
|
|
1851
1972
|
);
|
|
1852
1973
|
}
|
|
1853
1974
|
return;
|
|
@@ -1859,7 +1980,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1859
1980
|
);
|
|
1860
1981
|
if ("parseError" in localLoaded) {
|
|
1861
1982
|
if (options.dryRun) {
|
|
1862
|
-
if (
|
|
1983
|
+
if (agentMode) {
|
|
1863
1984
|
console.log(
|
|
1864
1985
|
JSON.stringify({
|
|
1865
1986
|
action: "overwrite",
|
|
@@ -1869,7 +1990,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1869
1990
|
);
|
|
1870
1991
|
} else {
|
|
1871
1992
|
console.log(
|
|
1872
|
-
|
|
1993
|
+
import_chalk12.default.yellow(
|
|
1873
1994
|
`
|
|
1874
1995
|
\u26A0 Local config is invalid: ${localLoaded.parseError}`
|
|
1875
1996
|
)
|
|
@@ -1877,23 +1998,23 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1877
1998
|
}
|
|
1878
1999
|
return;
|
|
1879
2000
|
}
|
|
1880
|
-
if (!options.yes && !
|
|
1881
|
-
console.log(
|
|
2001
|
+
if (!options.yes && !agentMode) {
|
|
2002
|
+
console.log(import_chalk12.default.yellow(`
|
|
1882
2003
|
\u26A0 ${localLoaded.parseError}`));
|
|
1883
2004
|
const shouldProceed = await (0, import_prompts3.confirm)({
|
|
1884
2005
|
message: "Overwrite with remote?",
|
|
1885
2006
|
default: true
|
|
1886
2007
|
});
|
|
1887
2008
|
if (!shouldProceed) {
|
|
1888
|
-
console.log(
|
|
2009
|
+
console.log(import_chalk12.default.dim("Pull cancelled"));
|
|
1889
2010
|
return;
|
|
1890
2011
|
}
|
|
1891
2012
|
}
|
|
1892
2013
|
fs5.writeFileSync(outputPath, configContent, "utf8");
|
|
1893
|
-
if (
|
|
2014
|
+
if (agentMode) {
|
|
1894
2015
|
console.log(JSON.stringify({ action: "overwrite", applied: true }));
|
|
1895
2016
|
} else {
|
|
1896
|
-
console.log(
|
|
2017
|
+
console.log(import_chalk12.default.green("\n\u2713 Overwritten commet.config.ts"));
|
|
1897
2018
|
}
|
|
1898
2019
|
return;
|
|
1899
2020
|
}
|
|
@@ -1916,9 +2037,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1916
2037
|
{
|
|
1917
2038
|
name: p.name,
|
|
1918
2039
|
...p.description ? { description: p.description } : {},
|
|
1919
|
-
...p.consumptionModel ? {
|
|
1920
|
-
consumptionModel: p.consumptionModel
|
|
1921
|
-
} : {},
|
|
2040
|
+
...p.consumptionModel ? { consumptionModel: p.consumptionModel } : {},
|
|
1922
2041
|
...p.isFree ? { isFree: true } : {},
|
|
1923
2042
|
...p.isPublic === false ? { isPublic: false } : {},
|
|
1924
2043
|
...p.sortOrder ? { sortOrder: p.sortOrder } : {},
|
|
@@ -1964,14 +2083,14 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1964
2083
|
};
|
|
1965
2084
|
const diff = computeDiff(remoteAsConfig, localAsRemote);
|
|
1966
2085
|
if (!diff.hasChanges && diff.features.unmanaged.length === 0 && diff.plans.unmanaged.length === 0) {
|
|
1967
|
-
if (
|
|
2086
|
+
if (agentMode) {
|
|
1968
2087
|
console.log(JSON.stringify({ diff, applied: false, upToDate: true }));
|
|
1969
2088
|
} else {
|
|
1970
|
-
console.log(
|
|
2089
|
+
console.log(import_chalk12.default.green("\n\u2713 Already up to date"));
|
|
1971
2090
|
}
|
|
1972
2091
|
return;
|
|
1973
2092
|
}
|
|
1974
|
-
if (
|
|
2093
|
+
if (agentMode) {
|
|
1975
2094
|
if (options.dryRun) {
|
|
1976
2095
|
console.log(JSON.stringify({ diff, applied: false }));
|
|
1977
2096
|
return;
|
|
@@ -1980,92 +2099,69 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1980
2099
|
console.log(formatDiff(diff));
|
|
1981
2100
|
}
|
|
1982
2101
|
if (options.dryRun) {
|
|
1983
|
-
if (!
|
|
1984
|
-
console.log(
|
|
2102
|
+
if (!agentMode) {
|
|
2103
|
+
console.log(import_chalk12.default.dim("\n(dry run \u2014 no changes applied)"));
|
|
1985
2104
|
}
|
|
1986
2105
|
return;
|
|
1987
2106
|
}
|
|
1988
|
-
if (!options.yes && !
|
|
2107
|
+
if (!options.yes && !agentMode) {
|
|
1989
2108
|
const shouldProceed = await (0, import_prompts3.confirm)({
|
|
1990
2109
|
message: "Overwrite commet.config.ts with remote state?",
|
|
1991
2110
|
default: true
|
|
1992
2111
|
});
|
|
1993
2112
|
if (!shouldProceed) {
|
|
1994
|
-
console.log(
|
|
2113
|
+
console.log(import_chalk12.default.dim("Pull cancelled"));
|
|
1995
2114
|
return;
|
|
1996
2115
|
}
|
|
1997
2116
|
}
|
|
1998
2117
|
fs5.writeFileSync(outputPath, configContent, "utf8");
|
|
1999
|
-
if (
|
|
2118
|
+
if (agentMode) {
|
|
2000
2119
|
console.log(JSON.stringify({ diff, applied: true }));
|
|
2001
2120
|
} else {
|
|
2002
|
-
console.log(
|
|
2121
|
+
console.log(import_chalk12.default.green("\n\u2713 Updated commet.config.ts"));
|
|
2003
2122
|
console.log(
|
|
2004
|
-
|
|
2123
|
+
import_chalk12.default.dim(` ${features.length} features, ${plans.length} plans`)
|
|
2005
2124
|
);
|
|
2006
2125
|
}
|
|
2007
2126
|
});
|
|
2008
2127
|
|
|
2009
2128
|
// src/commands/push.ts
|
|
2010
2129
|
var import_prompts4 = require("@inquirer/prompts");
|
|
2011
|
-
var
|
|
2012
|
-
var
|
|
2130
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
2131
|
+
var import_commander9 = require("commander");
|
|
2013
2132
|
var import_ora7 = __toESM(require("ora"));
|
|
2014
|
-
var pushCommand = new
|
|
2133
|
+
var pushCommand = new import_commander9.Command("push").description(
|
|
2015
2134
|
"Push your local commet.config.ts to Commet. Creates or updates features and plans to match your config file."
|
|
2016
|
-
).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without pushing").option(
|
|
2135
|
+
).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without pushing").option(
|
|
2136
|
+
"--output <format>",
|
|
2137
|
+
"Output format: human (default) or agent",
|
|
2138
|
+
"human"
|
|
2139
|
+
).addHelpText(
|
|
2017
2140
|
"after",
|
|
2018
2141
|
`
|
|
2019
2142
|
Examples:
|
|
2020
2143
|
$ commet push Interactive \u2014 shows diff, asks to confirm
|
|
2021
2144
|
$ commet push --dry-run Preview what would change on remote
|
|
2022
2145
|
$ commet push --yes Push without confirmation
|
|
2023
|
-
$ commet push --
|
|
2024
|
-
|
|
2025
|
-
Notes:
|
|
2026
|
-
Feature types (boolean, usage, seats) cannot be changed via push.
|
|
2027
|
-
Resources in remote but not in your config are left as-is (not deleted).
|
|
2146
|
+
$ commet push --output agent --yes Agent/CI \u2014 structured JSON, no prompts
|
|
2147
|
+
$ COMMET_API_KEY=sk_... commet push --yes CI pipeline
|
|
2028
2148
|
`
|
|
2029
2149
|
).action(async (options) => {
|
|
2030
|
-
const
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
console.log(JSON.stringify({ error: "Not authenticated" }));
|
|
2034
|
-
} else {
|
|
2035
|
-
console.log(import_chalk12.default.red("\u2717 Not authenticated"));
|
|
2036
|
-
console.log(import_chalk12.default.dim("Run `commet login` first"));
|
|
2037
|
-
}
|
|
2038
|
-
process.exit(1);
|
|
2039
|
-
}
|
|
2040
|
-
if (!projectConfigExists()) {
|
|
2041
|
-
if (jsonMode) {
|
|
2042
|
-
console.log(JSON.stringify({ error: "Project not linked" }));
|
|
2043
|
-
} else {
|
|
2044
|
-
console.log(import_chalk12.default.red("\u2717 Project not linked"));
|
|
2045
|
-
console.log(
|
|
2046
|
-
import_chalk12.default.dim("Run `commet link` first to connect to an organization")
|
|
2047
|
-
);
|
|
2048
|
-
}
|
|
2049
|
-
process.exit(1);
|
|
2050
|
-
}
|
|
2051
|
-
const projectConfig = loadProjectConfig();
|
|
2052
|
-
if (!projectConfig) {
|
|
2053
|
-
if (jsonMode) {
|
|
2054
|
-
console.log(JSON.stringify({ error: "Invalid project configuration" }));
|
|
2055
|
-
} else {
|
|
2056
|
-
console.log(import_chalk12.default.red("\u2717 Invalid project configuration"));
|
|
2057
|
-
}
|
|
2058
|
-
process.exit(1);
|
|
2059
|
-
}
|
|
2060
|
-
const loadSpinner = jsonMode ? null : (0, import_ora7.default)("Loading commet.config.ts...").start();
|
|
2150
|
+
const agentMode = isAgentMode(options);
|
|
2151
|
+
const { orgId } = requireOrgContext();
|
|
2152
|
+
const loadSpinner = agentMode ? null : (0, import_ora7.default)("Loading commet.config.ts...").start();
|
|
2061
2153
|
const loaded = await loadBillingConfig(process.cwd()).catch(
|
|
2062
2154
|
(error) => {
|
|
2063
2155
|
const message = error instanceof Error ? error.message : String(error);
|
|
2064
|
-
if (
|
|
2065
|
-
console.log(
|
|
2156
|
+
if (agentMode) {
|
|
2157
|
+
console.log(
|
|
2158
|
+
JSON.stringify({
|
|
2159
|
+
error: { code: "config_invalid", message }
|
|
2160
|
+
})
|
|
2161
|
+
);
|
|
2066
2162
|
} else {
|
|
2067
2163
|
loadSpinner?.fail("Failed to load config");
|
|
2068
|
-
console.error(
|
|
2164
|
+
console.error(import_chalk13.default.red(message));
|
|
2069
2165
|
}
|
|
2070
2166
|
return null;
|
|
2071
2167
|
}
|
|
@@ -2073,16 +2169,17 @@ Notes:
|
|
|
2073
2169
|
if (!loaded) process.exit(1);
|
|
2074
2170
|
const { config, configPath } = loaded;
|
|
2075
2171
|
loadSpinner?.succeed(`Loaded ${configPath}`);
|
|
2076
|
-
const fetchSpinner =
|
|
2172
|
+
const fetchSpinner = agentMode ? null : (0, import_ora7.default)("Fetching remote state...").start();
|
|
2173
|
+
const orgQuery = orgId === "__from_api_key__" ? "" : `?orgId=${orgId}`;
|
|
2077
2174
|
const remoteResult = await apiRequest(
|
|
2078
|
-
`${BASE_URL}/api/cli/pull
|
|
2175
|
+
`${BASE_URL}/api/cli/pull${orgQuery}`
|
|
2079
2176
|
);
|
|
2080
2177
|
if (remoteResult.error || !remoteResult.data) {
|
|
2081
|
-
if (
|
|
2178
|
+
if (agentMode) {
|
|
2082
2179
|
console.log(JSON.stringify({ error: remoteResult.error }));
|
|
2083
2180
|
} else {
|
|
2084
2181
|
fetchSpinner?.fail("Failed to fetch remote state");
|
|
2085
|
-
console.error(
|
|
2182
|
+
console.error(import_chalk13.default.red("Error:"), remoteResult.error?.message);
|
|
2086
2183
|
}
|
|
2087
2184
|
process.exit(1);
|
|
2088
2185
|
}
|
|
@@ -2092,7 +2189,7 @@ Notes:
|
|
|
2092
2189
|
plans: remoteResult.data.plans
|
|
2093
2190
|
};
|
|
2094
2191
|
const diff = computeDiff(config, remote);
|
|
2095
|
-
if (
|
|
2192
|
+
if (agentMode) {
|
|
2096
2193
|
if (options.dryRun) {
|
|
2097
2194
|
console.log(JSON.stringify({ diff, applied: false }));
|
|
2098
2195
|
return;
|
|
@@ -2101,10 +2198,10 @@ Notes:
|
|
|
2101
2198
|
console.log(formatDiff(diff));
|
|
2102
2199
|
}
|
|
2103
2200
|
if (!diff.hasChanges) {
|
|
2104
|
-
if (
|
|
2201
|
+
if (agentMode) {
|
|
2105
2202
|
console.log(JSON.stringify({ diff, applied: false, upToDate: true }));
|
|
2106
2203
|
} else {
|
|
2107
|
-
console.log(
|
|
2204
|
+
console.log(import_chalk13.default.green("\n\u2713 Everything is up to date"));
|
|
2108
2205
|
}
|
|
2109
2206
|
return;
|
|
2110
2207
|
}
|
|
@@ -2113,67 +2210,67 @@ Notes:
|
|
|
2113
2210
|
);
|
|
2114
2211
|
if (typeChanges.length > 0) {
|
|
2115
2212
|
const blockedCodes = typeChanges.map((c) => c.code);
|
|
2116
|
-
if (
|
|
2213
|
+
if (agentMode) {
|
|
2117
2214
|
console.log(
|
|
2118
2215
|
JSON.stringify({
|
|
2119
|
-
error:
|
|
2216
|
+
error: {
|
|
2217
|
+
code: "type_change_blocked",
|
|
2218
|
+
message: "Feature type changes must be done in the dashboard"
|
|
2219
|
+
},
|
|
2120
2220
|
blockedCodes,
|
|
2121
2221
|
diff
|
|
2122
2222
|
})
|
|
2123
2223
|
);
|
|
2124
2224
|
} else {
|
|
2125
2225
|
console.log(
|
|
2126
|
-
|
|
2226
|
+
import_chalk13.default.red(
|
|
2127
2227
|
"\n\u2717 Cannot change feature types. Update them in the dashboard:"
|
|
2128
2228
|
)
|
|
2129
2229
|
);
|
|
2130
2230
|
for (const change of typeChanges) {
|
|
2131
|
-
console.log(
|
|
2231
|
+
console.log(import_chalk13.default.red(` - ${change.code}`));
|
|
2132
2232
|
}
|
|
2133
2233
|
}
|
|
2134
2234
|
process.exit(1);
|
|
2135
2235
|
}
|
|
2136
2236
|
if (options.dryRun) {
|
|
2137
|
-
if (!
|
|
2138
|
-
console.log(
|
|
2237
|
+
if (!agentMode) {
|
|
2238
|
+
console.log(import_chalk13.default.dim("\n(dry run \u2014 no changes applied)"));
|
|
2139
2239
|
}
|
|
2140
2240
|
return;
|
|
2141
2241
|
}
|
|
2142
|
-
if (!options.yes && !
|
|
2242
|
+
if (!options.yes && !agentMode) {
|
|
2143
2243
|
const shouldProceed = await (0, import_prompts4.confirm)({
|
|
2144
2244
|
message: "Apply these changes?",
|
|
2145
2245
|
default: true
|
|
2146
2246
|
});
|
|
2147
2247
|
if (!shouldProceed) {
|
|
2148
|
-
console.log(
|
|
2248
|
+
console.log(import_chalk13.default.dim("Push cancelled"));
|
|
2149
2249
|
return;
|
|
2150
2250
|
}
|
|
2151
2251
|
}
|
|
2152
|
-
const pushSpinner =
|
|
2252
|
+
const pushSpinner = agentMode ? null : (0, import_ora7.default)("Pushing config...").start();
|
|
2253
|
+
const pushBody = {
|
|
2254
|
+
config: { features: config.features, plans: config.plans }
|
|
2255
|
+
};
|
|
2256
|
+
if (orgId !== "__from_api_key__") {
|
|
2257
|
+
pushBody.orgId = orgId;
|
|
2258
|
+
}
|
|
2153
2259
|
const pushResult = await apiRequest(
|
|
2154
2260
|
`${BASE_URL}/api/cli/push`,
|
|
2155
|
-
{
|
|
2156
|
-
method: "POST",
|
|
2157
|
-
body: JSON.stringify({
|
|
2158
|
-
orgId: projectConfig.orgId,
|
|
2159
|
-
config: {
|
|
2160
|
-
features: config.features,
|
|
2161
|
-
plans: config.plans
|
|
2162
|
-
}
|
|
2163
|
-
})
|
|
2164
|
-
}
|
|
2261
|
+
{ method: "POST", body: JSON.stringify(pushBody) }
|
|
2165
2262
|
);
|
|
2166
2263
|
if (pushResult.error || !pushResult.data) {
|
|
2167
|
-
if (
|
|
2264
|
+
if (agentMode) {
|
|
2168
2265
|
console.log(JSON.stringify({ error: pushResult.error }));
|
|
2169
2266
|
} else {
|
|
2170
2267
|
pushSpinner?.fail("Push failed");
|
|
2171
|
-
console.error(
|
|
2268
|
+
console.error(import_chalk13.default.red("Error:"), pushResult.error?.message);
|
|
2172
2269
|
}
|
|
2173
2270
|
process.exit(1);
|
|
2174
2271
|
}
|
|
2175
2272
|
const pushOutcome = pushResult.data;
|
|
2176
|
-
if (
|
|
2273
|
+
if (agentMode) {
|
|
2177
2274
|
console.log(JSON.stringify({ diff, applied: true, result: pushOutcome }));
|
|
2178
2275
|
return;
|
|
2179
2276
|
}
|
|
@@ -2184,160 +2281,106 @@ Notes:
|
|
|
2184
2281
|
if (errors.length > 0) {
|
|
2185
2282
|
pushSpinner?.warn("Push completed with errors");
|
|
2186
2283
|
for (const error of errors) {
|
|
2187
|
-
console.log(
|
|
2284
|
+
console.log(import_chalk13.default.red(` \u2717 ${error.code}: ${error.message}`));
|
|
2188
2285
|
}
|
|
2189
2286
|
} else {
|
|
2190
2287
|
pushSpinner?.succeed("Push complete");
|
|
2191
2288
|
}
|
|
2192
2289
|
if (pushOutcome.features.created.length > 0) {
|
|
2193
2290
|
console.log(
|
|
2194
|
-
|
|
2291
|
+
import_chalk13.default.green(
|
|
2195
2292
|
` Created features: ${pushOutcome.features.created.join(", ")}`
|
|
2196
2293
|
)
|
|
2197
2294
|
);
|
|
2198
2295
|
}
|
|
2199
2296
|
if (pushOutcome.features.updated.length > 0) {
|
|
2200
2297
|
console.log(
|
|
2201
|
-
|
|
2298
|
+
import_chalk13.default.yellow(
|
|
2202
2299
|
` Updated features: ${pushOutcome.features.updated.join(", ")}`
|
|
2203
2300
|
)
|
|
2204
2301
|
);
|
|
2205
2302
|
}
|
|
2206
2303
|
if (pushOutcome.plans.created.length > 0) {
|
|
2207
2304
|
console.log(
|
|
2208
|
-
|
|
2305
|
+
import_chalk13.default.green(` Created plans: ${pushOutcome.plans.created.join(", ")}`)
|
|
2209
2306
|
);
|
|
2210
2307
|
}
|
|
2211
2308
|
if (pushOutcome.plans.updated.length > 0) {
|
|
2212
2309
|
console.log(
|
|
2213
|
-
|
|
2310
|
+
import_chalk13.default.yellow(
|
|
2214
2311
|
` Updated plans: ${pushOutcome.plans.updated.join(", ")}`
|
|
2215
2312
|
)
|
|
2216
2313
|
);
|
|
2217
2314
|
}
|
|
2218
2315
|
});
|
|
2219
2316
|
|
|
2220
|
-
// src/
|
|
2221
|
-
var
|
|
2222
|
-
var
|
|
2223
|
-
var
|
|
2224
|
-
var import_ora8 = __toESM(require("ora"));
|
|
2225
|
-
var switchCommand = new import_commander11.Command("switch").description(
|
|
2226
|
-
"Switch this project to a different organization. Updates .commet/config.json with the new org."
|
|
2227
|
-
).option(
|
|
2228
|
-
"--org <slug-or-id>",
|
|
2229
|
-
"Organization slug or ID \u2014 skips interactive selection"
|
|
2230
|
-
).addHelpText(
|
|
2231
|
-
"after",
|
|
2232
|
-
`
|
|
2233
|
-
Examples:
|
|
2234
|
-
$ commet switch Interactive \u2014 choose from a list
|
|
2235
|
-
$ commet switch --org acme Non-interactive \u2014 match by slug or ID
|
|
2236
|
-
|
|
2237
|
-
Use 'commet orgs' to see available organizations and their slugs.
|
|
2238
|
-
After switching, run 'commet pull' to update your config file.
|
|
2239
|
-
`
|
|
2240
|
-
).action(async (options) => {
|
|
2241
|
-
if (!authExists()) {
|
|
2242
|
-
console.log(import_chalk13.default.red("\u2717 Not authenticated"));
|
|
2243
|
-
console.log(import_chalk13.default.dim("Run `commet login` first"));
|
|
2244
|
-
process.exit(1);
|
|
2245
|
-
}
|
|
2246
|
-
if (!projectConfigExists()) {
|
|
2247
|
-
console.log(import_chalk13.default.yellow("\u26A0 Project not linked"));
|
|
2248
|
-
console.log(
|
|
2249
|
-
import_chalk13.default.dim("Run `commet link` first to connect to an organization")
|
|
2250
|
-
);
|
|
2251
|
-
process.exit(1);
|
|
2252
|
-
}
|
|
2253
|
-
const spinner = (0, import_ora8.default)("Fetching organizations...").start();
|
|
2254
|
-
const result = await apiRequest(
|
|
2255
|
-
`${BASE_URL}/api/cli/organizations`
|
|
2256
|
-
);
|
|
2257
|
-
if (result.error || !result.data) {
|
|
2258
|
-
spinner.fail("Failed to fetch organizations");
|
|
2259
|
-
console.error(import_chalk13.default.red("Error:"), result.error);
|
|
2260
|
-
process.exit(1);
|
|
2261
|
-
}
|
|
2262
|
-
const { organizations } = result.data;
|
|
2263
|
-
if (organizations.length === 0) {
|
|
2264
|
-
spinner.stop();
|
|
2265
|
-
console.log(import_chalk13.default.yellow("\u26A0 No organizations found"));
|
|
2266
|
-
process.exit(1);
|
|
2267
|
-
}
|
|
2268
|
-
spinner.stop();
|
|
2269
|
-
let selectedOrg;
|
|
2270
|
-
if (options.org) {
|
|
2271
|
-
selectedOrg = organizations.find(
|
|
2272
|
-
(org) => org.slug === options.org || org.id === options.org
|
|
2273
|
-
);
|
|
2274
|
-
if (!selectedOrg) {
|
|
2275
|
-
console.log(import_chalk13.default.red(`\u2717 Organization "${options.org}" not found`));
|
|
2276
|
-
console.log(import_chalk13.default.dim("\nAvailable organizations:"));
|
|
2277
|
-
for (const org of organizations) {
|
|
2278
|
-
console.log(import_chalk13.default.dim(` ${org.slug} (${org.mode})`));
|
|
2279
|
-
}
|
|
2280
|
-
process.exit(1);
|
|
2281
|
-
}
|
|
2282
|
-
} else {
|
|
2283
|
-
let orgId;
|
|
2284
|
-
try {
|
|
2285
|
-
orgId = await (0, import_prompts5.select)({
|
|
2286
|
-
message: "Select organization:",
|
|
2287
|
-
choices: organizations.map((org) => ({
|
|
2288
|
-
name: `${org.name} ${import_chalk13.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
|
|
2289
|
-
value: org.id
|
|
2290
|
-
})),
|
|
2291
|
-
theme: promptTheme
|
|
2292
|
-
});
|
|
2293
|
-
} catch (_error) {
|
|
2294
|
-
console.log(import_chalk13.default.yellow("\n\u26A0 Switch cancelled"));
|
|
2295
|
-
return;
|
|
2296
|
-
}
|
|
2297
|
-
selectedOrg = organizations.find((org) => org.id === orgId);
|
|
2298
|
-
if (!selectedOrg) {
|
|
2299
|
-
console.log(import_chalk13.default.red("\u2717 Organization not found"));
|
|
2300
|
-
process.exit(1);
|
|
2301
|
-
}
|
|
2302
|
-
}
|
|
2303
|
-
saveProjectConfig({
|
|
2304
|
-
orgId: selectedOrg.id,
|
|
2305
|
-
orgName: selectedOrg.name,
|
|
2306
|
-
mode: selectedOrg.mode
|
|
2307
|
-
});
|
|
2308
|
-
console.log(import_chalk13.default.green("\n\u2713 Switched organization"));
|
|
2309
|
-
console.log(
|
|
2310
|
-
import_chalk13.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
2311
|
-
);
|
|
2312
|
-
console.log(
|
|
2313
|
-
import_chalk13.default.dim(
|
|
2314
|
-
"\nRun `commet pull` to update TypeScript types for this organization"
|
|
2315
|
-
)
|
|
2316
|
-
);
|
|
2317
|
-
});
|
|
2318
|
-
|
|
2319
|
-
// src/commands/unlink.ts
|
|
2317
|
+
// src/utils/update-check.ts
|
|
2318
|
+
var fs6 = __toESM(require("fs"));
|
|
2319
|
+
var os3 = __toESM(require("os"));
|
|
2320
|
+
var path6 = __toESM(require("path"));
|
|
2320
2321
|
var import_chalk14 = __toESM(require("chalk"));
|
|
2321
|
-
var
|
|
2322
|
-
var
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
);
|
|
2329
|
-
return;
|
|
2322
|
+
var CACHE_FILE = path6.join(os3.homedir(), ".commet", ".update-check");
|
|
2323
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2324
|
+
var REGISTRY_URL = "https://registry.npmjs.org/commet/latest";
|
|
2325
|
+
function isNewerVersion(latest, current) {
|
|
2326
|
+
const l = latest.split(".").map(Number);
|
|
2327
|
+
const c = current.split(".").map(Number);
|
|
2328
|
+
for (let i = 0; i < 3; i++) {
|
|
2329
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
2330
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
2331
|
+
}
|
|
2332
|
+
return false;
|
|
2333
|
+
}
|
|
2334
|
+
function readCache() {
|
|
2335
|
+
try {
|
|
2336
|
+
return JSON.parse(fs6.readFileSync(CACHE_FILE, "utf8"));
|
|
2337
|
+
} catch {
|
|
2338
|
+
return null;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
function writeCache(cache) {
|
|
2342
|
+
try {
|
|
2343
|
+
fs6.writeFileSync(CACHE_FILE, JSON.stringify(cache), "utf8");
|
|
2344
|
+
} catch {
|
|
2330
2345
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2346
|
+
}
|
|
2347
|
+
function shouldSkip() {
|
|
2348
|
+
if (process.env.COMMET_NO_UPDATE_CHECK === "1") return true;
|
|
2349
|
+
if (process.env.CI) return true;
|
|
2350
|
+
const idx = process.argv.indexOf("--output");
|
|
2351
|
+
if (idx !== -1 && process.argv[idx + 1] === "agent") return true;
|
|
2352
|
+
return false;
|
|
2353
|
+
}
|
|
2354
|
+
var updateAvailable = null;
|
|
2355
|
+
function scheduleUpdateCheck(currentVersion) {
|
|
2356
|
+
if (shouldSkip()) return;
|
|
2357
|
+
const cache = readCache();
|
|
2358
|
+
if (cache && isNewerVersion(cache.latestVersion, currentVersion)) {
|
|
2359
|
+
updateAvailable = cache.latestVersion;
|
|
2360
|
+
}
|
|
2361
|
+
const needsFetch = !cache || Date.now() - cache.checkedAt > CHECK_INTERVAL_MS;
|
|
2362
|
+
if (!needsFetch) return;
|
|
2363
|
+
const controller = new AbortController();
|
|
2364
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
2365
|
+
fetch(REGISTRY_URL, { signal: controller.signal }).then((r) => r.json()).then((data) => {
|
|
2366
|
+
writeCache({ latestVersion: data.version, checkedAt: Date.now() });
|
|
2367
|
+
if (!updateAvailable && isNewerVersion(data.version, currentVersion)) {
|
|
2368
|
+
updateAvailable = data.version;
|
|
2369
|
+
}
|
|
2370
|
+
}).catch(() => {
|
|
2371
|
+
}).finally(() => clearTimeout(timeout));
|
|
2372
|
+
}
|
|
2373
|
+
function printUpdateNotification(currentVersion) {
|
|
2374
|
+
if (!updateAvailable) return;
|
|
2375
|
+
console.log("");
|
|
2334
2376
|
console.log(
|
|
2335
|
-
import_chalk14.default.dim(
|
|
2377
|
+
` Update available: ${import_chalk14.default.dim(currentVersion)} \u2192 ${import_chalk14.default.green(updateAvailable)}`
|
|
2336
2378
|
);
|
|
2337
|
-
|
|
2379
|
+
console.log(import_chalk14.default.dim(` Run: npm i -g commet@latest`));
|
|
2380
|
+
}
|
|
2338
2381
|
|
|
2339
2382
|
// src/index.ts
|
|
2340
|
-
var program = new
|
|
2383
|
+
var program = new import_commander10.Command();
|
|
2341
2384
|
program.name("commet").description(
|
|
2342
2385
|
"Commet CLI \u2014 billing infrastructure as code.\nManage features, plans, and billing config from the command line."
|
|
2343
2386
|
).version(package_default.version).addHelpText(
|
|
@@ -2346,13 +2389,12 @@ program.name("commet").description(
|
|
|
2346
2389
|
Workflow:
|
|
2347
2390
|
$ commet pull Sync remote \u2192 commet.config.ts
|
|
2348
2391
|
$ commet push Push local changes \u2192 remote
|
|
2349
|
-
$ commet
|
|
2392
|
+
$ commet pull --dry-run See what's configured
|
|
2350
2393
|
|
|
2351
2394
|
For agents and CI:
|
|
2352
|
-
$ commet
|
|
2353
|
-
$ commet pull --
|
|
2354
|
-
$ commet
|
|
2355
|
-
$ commet link --org <slug> Link without interactive selection
|
|
2395
|
+
$ commet JSON capabilities when piped (no args)
|
|
2396
|
+
$ commet pull --output agent --yes Structured output, no prompts
|
|
2397
|
+
$ COMMET_API_KEY=sk_... commet push --yes CI pipeline
|
|
2356
2398
|
|
|
2357
2399
|
Run commet <command> --help for detailed usage and examples.
|
|
2358
2400
|
`
|
|
@@ -2361,16 +2403,33 @@ program.addCommand(createCommand);
|
|
|
2361
2403
|
program.addCommand(loginCommand);
|
|
2362
2404
|
program.addCommand(logoutCommand);
|
|
2363
2405
|
program.addCommand(linkCommand);
|
|
2364
|
-
program.addCommand(unlinkCommand);
|
|
2365
|
-
program.addCommand(switchCommand);
|
|
2366
|
-
program.addCommand(agentInfoCommand);
|
|
2367
2406
|
program.addCommand(orgsCommand);
|
|
2368
2407
|
program.addCommand(pushCommand);
|
|
2369
2408
|
program.addCommand(pullCommand);
|
|
2370
|
-
program.addCommand(listCommand);
|
|
2371
2409
|
program.addCommand(listenCommand);
|
|
2410
|
+
program.addCommand(apiKeyCommand);
|
|
2411
|
+
program.enablePositionalOptions().passThroughOptions().option(
|
|
2412
|
+
"--output <format>",
|
|
2413
|
+
"Output format: human (default) or agent",
|
|
2414
|
+
"human"
|
|
2415
|
+
);
|
|
2416
|
+
program.action((options) => {
|
|
2417
|
+
if (options.output === "agent") {
|
|
2418
|
+
printAgentInfo();
|
|
2419
|
+
} else {
|
|
2420
|
+
printDefaultScreen();
|
|
2421
|
+
}
|
|
2422
|
+
});
|
|
2372
2423
|
program.showSuggestionAfterError();
|
|
2373
2424
|
program.exitOverride();
|
|
2425
|
+
installCrashHandler();
|
|
2426
|
+
markCommandStart();
|
|
2427
|
+
scheduleUpdateCheck(package_default.version);
|
|
2428
|
+
var commandName = process.argv[2] || "(default)";
|
|
2429
|
+
program.hook("postAction", () => {
|
|
2430
|
+
reportCommand(commandName, "success");
|
|
2431
|
+
printUpdateNotification(package_default.version);
|
|
2432
|
+
});
|
|
2374
2433
|
try {
|
|
2375
2434
|
program.parse(process.argv);
|
|
2376
2435
|
} catch (error) {
|
|
@@ -2379,12 +2438,93 @@ try {
|
|
|
2379
2438
|
if (code === "commander.version" || code === "commander.help" || code === "commander.helpDisplayed") {
|
|
2380
2439
|
process.exit(0);
|
|
2381
2440
|
}
|
|
2441
|
+
reportCommand(commandName, "error", code);
|
|
2382
2442
|
console.error(import_chalk15.default.red("Error:"), error.message);
|
|
2383
2443
|
}
|
|
2384
2444
|
process.exit(1);
|
|
2385
2445
|
}
|
|
2386
|
-
|
|
2387
|
-
|
|
2446
|
+
function printAgentInfo() {
|
|
2447
|
+
const authenticated = authExists() || !!process.env.COMMET_API_KEY;
|
|
2448
|
+
const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
|
|
2449
|
+
const configPath = findConfigFile(process.cwd());
|
|
2450
|
+
const setup = [];
|
|
2451
|
+
if (!authenticated) {
|
|
2452
|
+
setup.push(
|
|
2453
|
+
"Not authenticated. Run 'commet login' (interactive) or set COMMET_API_KEY env var."
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2456
|
+
if (authenticated && !projectConfig && !process.env.COMMET_API_KEY) {
|
|
2457
|
+
setup.push(
|
|
2458
|
+
"No project linked. Run 'commet link --org <slug>' or 'commet orgs --json' to find organizations."
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
const output = {
|
|
2462
|
+
version: package_default.version,
|
|
2463
|
+
authenticated,
|
|
2464
|
+
...setup.length > 0 ? { setup } : {},
|
|
2465
|
+
project: projectConfig ? {
|
|
2466
|
+
linked: true,
|
|
2467
|
+
orgId: projectConfig.orgId,
|
|
2468
|
+
orgName: projectConfig.orgName,
|
|
2469
|
+
mode: projectConfig.mode
|
|
2470
|
+
} : { linked: false },
|
|
2471
|
+
config: {
|
|
2472
|
+
exists: configPath !== null,
|
|
2473
|
+
path: configPath?.split("/").pop() ?? null
|
|
2474
|
+
},
|
|
2475
|
+
mcp: {
|
|
2476
|
+
url: "https://commet.co/mcp",
|
|
2477
|
+
sandbox: "https://sandbox.commet.co/mcp",
|
|
2478
|
+
hint: "For full billing CRUD (plans, features, customers, subscriptions), connect to the MCP server."
|
|
2479
|
+
},
|
|
2480
|
+
auth: {
|
|
2481
|
+
interactive: "commet login",
|
|
2482
|
+
ci: "Set COMMET_API_KEY environment variable"
|
|
2483
|
+
},
|
|
2484
|
+
commands: {
|
|
2485
|
+
pull: {
|
|
2486
|
+
description: "Pull remote config and generate commet.config.ts",
|
|
2487
|
+
usage: "commet pull --output agent --yes",
|
|
2488
|
+
preview: "commet pull --output agent --dry-run"
|
|
2489
|
+
},
|
|
2490
|
+
push: {
|
|
2491
|
+
description: "Push commet.config.ts to remote",
|
|
2492
|
+
usage: "commet push --output agent --yes",
|
|
2493
|
+
preview: "commet push --output agent --dry-run",
|
|
2494
|
+
ci: "COMMET_API_KEY=sk_... commet push --yes"
|
|
2495
|
+
},
|
|
2496
|
+
orgs: {
|
|
2497
|
+
description: "List available organizations",
|
|
2498
|
+
usage: "commet orgs --output agent"
|
|
2499
|
+
},
|
|
2500
|
+
link: {
|
|
2501
|
+
description: "Link/switch/unlink organization",
|
|
2502
|
+
usage: "commet link --org <slug-or-id>",
|
|
2503
|
+
clear: "commet link --clear"
|
|
2504
|
+
},
|
|
2505
|
+
listen: {
|
|
2506
|
+
description: "Forward webhook events to local server. Long-running streaming process.",
|
|
2507
|
+
usage: "commet listen <url> [--events <types>]"
|
|
2508
|
+
},
|
|
2509
|
+
create: {
|
|
2510
|
+
description: "Scaffold a new Commet app from template",
|
|
2511
|
+
usage: "commet create [name] -t <template> --org <slug> -y"
|
|
2512
|
+
},
|
|
2513
|
+
"api-key": {
|
|
2514
|
+
description: "Generate API key for CI",
|
|
2515
|
+
usage: "commet api-key --output agent"
|
|
2516
|
+
},
|
|
2517
|
+
login: {
|
|
2518
|
+
description: "Authenticate via browser. Requires a human \u2014 opens a device-code flow.",
|
|
2519
|
+
usage: "commet login"
|
|
2520
|
+
},
|
|
2521
|
+
logout: {
|
|
2522
|
+
description: "Log out of Commet",
|
|
2523
|
+
usage: "commet logout"
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
};
|
|
2527
|
+
console.log(JSON.stringify(output, null, 2));
|
|
2388
2528
|
}
|
|
2389
2529
|
function printDefaultScreen() {
|
|
2390
2530
|
const version = import_chalk15.default.dim(`v${package_default.version}`);
|
|
@@ -2426,22 +2566,17 @@ function printDefaultScreen() {
|
|
|
2426
2566
|
console.log(dim("\n Config"));
|
|
2427
2567
|
console.log(` ${cmd("pull")}${dim("Sync remote \u2192 commet.config.ts")}`);
|
|
2428
2568
|
console.log(` ${cmd("push")}${dim("Sync commet.config.ts \u2192 remote")}`);
|
|
2429
|
-
console.log(
|
|
2430
|
-
` ${cmd("list <type>")}${dim("List features, plans, or seats")}`
|
|
2431
|
-
);
|
|
2432
2569
|
console.log(dim("\n Development"));
|
|
2433
2570
|
console.log(` ${cmd("listen <url>")}${dim("Forward webhooks locally")}`);
|
|
2434
2571
|
console.log(dim("\n Project"));
|
|
2435
|
-
console.log(` ${cmd("link")}${dim("Link
|
|
2436
|
-
console.log(` ${cmd("switch")}${dim("Switch organization")}`);
|
|
2572
|
+
console.log(` ${cmd("link")}${dim("Link / switch organization")}`);
|
|
2437
2573
|
console.log(` ${cmd("orgs")}${dim("List organizations")}`);
|
|
2438
2574
|
console.log(dim("\n Setup"));
|
|
2439
2575
|
console.log(` ${cmd("create")}${dim("Scaffold a new Commet app")}`);
|
|
2576
|
+
console.log(` ${cmd("api-key")}${dim("Generate API key for CI")}`);
|
|
2440
2577
|
console.log(` ${cmd("login")}${dim("Authenticate")}`);
|
|
2441
2578
|
console.log(` ${cmd("logout")}${dim("Log out")}`);
|
|
2442
2579
|
console.log(
|
|
2443
|
-
dim(
|
|
2444
|
-
"\n commet --help for full reference \xB7 commet agent-info for agents/CI\n"
|
|
2445
|
-
)
|
|
2580
|
+
dim("\n commet --help for full reference \xB7 COMMET_API_KEY for CI\n")
|
|
2446
2581
|
);
|
|
2447
2582
|
}
|