commet 1.10.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +989 -593
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_chalk16 = __toESM(require("chalk"));
28
- var import_commander13 = require("commander");
27
+ var import_chalk14 = __toESM(require("chalk"));
28
+ var import_commander10 = require("commander");
29
29
 
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "commet",
33
- version: "1.10.0",
33
+ version: "2.0.0",
34
34
  description: "Commet CLI - Manage your billing platform from the command line",
35
35
  bin: {
36
36
  commet: "./bin/commet"
@@ -87,16 +87,10 @@ var package_default = {
87
87
  }
88
88
  };
89
89
 
90
- // src/commands/create.ts
91
- var import_node_child_process = require("child_process");
92
- var fs2 = __toESM(require("fs"));
93
- var os2 = __toESM(require("os"));
94
- var path2 = __toESM(require("path"));
95
- var import_prompts = require("@inquirer/prompts");
96
- var import_chalk3 = __toESM(require("chalk"));
90
+ // src/commands/api-key.ts
91
+ var import_chalk2 = __toESM(require("chalk"));
97
92
  var import_commander = require("commander");
98
- var import_ora2 = __toESM(require("ora"));
99
- var import_tar = require("tar");
93
+ var import_ora = __toESM(require("ora"));
100
94
 
101
95
  // src/utils/config.ts
102
96
  var fs = __toESM(require("fs"));
@@ -165,55 +159,353 @@ function clearProjectConfig() {
165
159
  }
166
160
  }
167
161
 
162
+ // src/utils/telemetry.ts
163
+ var CLI_VERSION = true ? "2.0.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
+ }
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
+ };
192
+ }
193
+ function getClientInfoHeader() {
194
+ if (!cachedClientInfo) {
195
+ cachedClientInfo = JSON.stringify(collectClientInfo());
196
+ }
197
+ return cachedClientInfo;
198
+ }
199
+ function getUserAgent() {
200
+ if (!cachedUserAgent) {
201
+ const runtime = detectRuntime();
202
+ cachedUserAgent = `commet-cli/${CLI_VERSION} ${runtime.name}/${runtime.version} ${process.platform}/${process.arch}`;
203
+ }
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;
216
+ }
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()
237
+ },
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
+ }
299
+
168
300
  // src/utils/api.ts
169
301
  var BASE_URL = "https://commet.co";
170
302
  async function apiRequest(endpoint, options = {}) {
171
- const auth = loadAuth();
172
- if (!auth) {
173
- return { error: "Not authenticated. Run `commet login` first." };
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
+ };
174
312
  }
175
313
  try {
314
+ markApiRequest();
176
315
  const response = await fetch(endpoint, {
177
316
  ...options,
178
317
  headers: {
179
318
  ...options.headers,
180
- Authorization: `Bearer ${auth.token}`,
181
- "Content-Type": "application/json"
319
+ "Content-Type": "application/json",
320
+ "User-Agent": getUserAgent(),
321
+ "commet-client-info": getClientInfoHeader(),
322
+ ...apiKey ? { "x-api-key": apiKey } : { Authorization: `Bearer ${auth.token}` }
182
323
  }
183
324
  });
184
325
  if (!response.ok) {
185
326
  const errorData = await response.json().catch(() => ({}));
186
327
  return {
187
- error: errorData.message || errorData.error || `Request failed with status ${response.status}`
328
+ error: {
329
+ code: errorData.code ?? `http_${response.status}`,
330
+ message: errorData.message ?? errorData.error ?? `Request failed with status ${response.status}`
331
+ }
188
332
  };
189
333
  }
190
334
  const data = await response.json();
191
335
  return { data };
192
336
  } catch (error) {
193
337
  return {
194
- error: error instanceof Error ? error.message : "Unknown error occurred"
338
+ error: {
339
+ code: "network_error",
340
+ message: error instanceof Error ? error.message : "Unknown error occurred"
341
+ }
195
342
  };
196
343
  }
197
344
  }
198
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
+
199
491
  // src/utils/login-flow.ts
200
- var import_chalk2 = __toESM(require("chalk"));
492
+ var import_chalk4 = __toESM(require("chalk"));
201
493
  var import_open = __toESM(require("open"));
202
- var import_ora = __toESM(require("ora"));
494
+ var import_ora2 = __toESM(require("ora"));
203
495
 
204
496
  // src/utils/prompt-theme.ts
205
- var import_chalk = __toESM(require("chalk"));
206
- var commetColor = import_chalk.default.hex("#e8a07c");
497
+ var import_chalk3 = __toESM(require("chalk"));
498
+ var commetColor = import_chalk3.default.hex("#e8a07c");
207
499
  var promptTheme = {
208
500
  prefix: commetColor("\u276F"),
209
501
  style: {
210
502
  answer: commetColor,
211
- message: import_chalk.default.bold,
212
- error: import_chalk.default.red,
213
- help: import_chalk.default.dim,
503
+ message: import_chalk3.default.bold,
504
+ error: import_chalk3.default.red,
505
+ help: import_chalk3.default.dim,
214
506
  highlight: commetColor.bold,
215
- description: import_chalk.default.dim,
216
- defaultAnswer: import_chalk.default.dim
507
+ description: import_chalk3.default.dim,
508
+ defaultAnswer: import_chalk3.default.dim
217
509
  }
218
510
  };
219
511
 
@@ -222,7 +514,7 @@ function sleep(ms) {
222
514
  return new Promise((resolve4) => setTimeout(resolve4, ms));
223
515
  }
224
516
  async function performLogin() {
225
- const spinner = (0, import_ora.default)("Initiating login flow...").start();
517
+ const spinner = (0, import_ora2.default)("Initiating login flow...").start();
226
518
  try {
227
519
  const deviceResponse = await fetch(`${BASE_URL}/api/auth/device/code`, {
228
520
  method: "POST",
@@ -244,18 +536,18 @@ async function performLogin() {
244
536
  interval = 5
245
537
  } = deviceData;
246
538
  spinner.stop();
247
- console.log(import_chalk2.default.bold("\n\u{1F510} Commet CLI Login\n"));
539
+ console.log(import_chalk4.default.bold("\n\u{1F510} Commet CLI Login\n"));
248
540
  console.log("Visit the following URL in your browser:");
249
541
  console.log(commetColor.underline(verification_uri_complete));
250
542
  console.log("\nOr enter this code manually:");
251
- console.log(import_chalk2.default.bold.green(` ${user_code}`));
252
- console.log(import_chalk2.default.dim("\nOpening browser...\n"));
543
+ console.log(import_chalk4.default.bold.green(` ${user_code}`));
544
+ console.log(import_chalk4.default.dim("\nOpening browser...\n"));
253
545
  try {
254
546
  await (0, import_open.default)(verification_uri_complete);
255
547
  } catch {
256
- console.log(import_chalk2.default.yellow("\u26A0 Could not open browser automatically."));
548
+ console.log(import_chalk4.default.yellow("\u26A0 Could not open browser automatically."));
257
549
  }
258
- const pollSpinner = (0, import_ora.default)("Waiting for authorization...").start();
550
+ const pollSpinner = (0, import_ora2.default)("Waiting for authorization...").start();
259
551
  let pollingInterval = interval;
260
552
  let attempts = 0;
261
553
  const maxAttempts = Math.floor(180 / pollingInterval);
@@ -501,22 +793,22 @@ async function installSkills(projectRoot) {
501
793
  );
502
794
  child.on("close", (code) => {
503
795
  if (code !== 0) {
504
- console.log(import_chalk3.default.dim(" You can install them manually by running:"));
505
- console.log(import_chalk3.default.dim(" npx skills add commet-labs/commet-skills"));
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"));
506
798
  }
507
799
  resolve4();
508
800
  });
509
801
  });
510
802
  }
511
- var createCommand = new import_commander.Command("create").description("Create a new Commet app from a template").argument("[name]", "Project name").option(
803
+ var createCommand = new import_commander2.Command("create").description("Create a new Commet app from a template").argument("[name]", "Project name").option(
512
804
  "-t, --template <template>",
513
805
  "Template to use (fixed, seats, metered, credits, balance-ai, balance-fixed)"
514
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) => {
515
807
  if (opts.list) {
516
- console.log(import_chalk3.default.bold("\nAvailable templates:\n"));
808
+ console.log(import_chalk5.default.bold("\nAvailable templates:\n"));
517
809
  for (const t of TEMPLATES) {
518
810
  console.log(
519
- ` ${commetColor(t.name.padEnd(14))}${import_chalk3.default.dim(t.description)}`
811
+ ` ${commetColor(t.name.padEnd(14))}${import_chalk5.default.dim(t.description)}`
520
812
  );
521
813
  }
522
814
  console.log();
@@ -525,9 +817,9 @@ var createCommand = new import_commander.Command("create").description("Create a
525
817
  let projectName = argName;
526
818
  if (!projectName) {
527
819
  if (!isInteractive()) {
528
- console.log(import_chalk3.default.red("\u2717 Project name is required"));
820
+ console.log(import_chalk5.default.red("\u2717 Project name is required"));
529
821
  console.log(
530
- import_chalk3.default.dim("Pass as positional argument: commet create <name>")
822
+ import_chalk5.default.dim("Pass as positional argument: commet create <name>")
531
823
  );
532
824
  return;
533
825
  }
@@ -537,21 +829,21 @@ var createCommand = new import_commander.Command("create").description("Create a
537
829
  theme: promptTheme
538
830
  });
539
831
  } catch {
540
- console.log(import_chalk3.default.yellow("\n\u26A0 Cancelled"));
832
+ console.log(import_chalk5.default.yellow("\n\u26A0 Cancelled"));
541
833
  return;
542
834
  }
543
835
  }
544
836
  const dest = path2.resolve(projectName);
545
837
  if (fs2.existsSync(dest)) {
546
838
  console.log(
547
- import_chalk3.default.red(`\u2717 Directory "${projectName}" already exists`)
839
+ import_chalk5.default.red(`\u2717 Directory "${projectName}" already exists`)
548
840
  );
549
841
  return;
550
842
  }
551
843
  if (!authExists()) {
552
844
  if (!isInteractive()) {
553
- console.log(import_chalk3.default.red("\u2717 Not authenticated"));
554
- console.log(import_chalk3.default.dim("Run `commet login` first"));
845
+ console.log(import_chalk5.default.red("\u2717 Not authenticated"));
846
+ console.log(import_chalk5.default.dim("Run `commet login` first"));
555
847
  return;
556
848
  }
557
849
  const loggedIn = await performLogin();
@@ -559,11 +851,11 @@ var createCommand = new import_commander.Command("create").description("Create a
559
851
  return;
560
852
  }
561
853
  }
562
- const orgsSpinner = (0, import_ora2.default)("Fetching sandbox organizations...").start();
854
+ const orgsSpinner = (0, import_ora3.default)("Fetching sandbox organizations...").start();
563
855
  const orgsResult = await apiRequest(`${BASE_URL}/api/cli/organizations`);
564
856
  if (orgsResult.error || !orgsResult.data) {
565
857
  orgsSpinner.fail("Failed to fetch organizations");
566
- console.log(import_chalk3.default.dim(orgsResult.error));
858
+ console.log(import_chalk5.default.dim(orgsResult.error));
567
859
  return;
568
860
  }
569
861
  const organizations = orgsResult.data.organizations.filter(
@@ -571,9 +863,9 @@ var createCommand = new import_commander.Command("create").description("Create a
571
863
  );
572
864
  orgsSpinner.stop();
573
865
  if (organizations.length === 0) {
574
- console.log(import_chalk3.default.yellow("\u26A0 No sandbox organizations found"));
866
+ console.log(import_chalk5.default.yellow("\u26A0 No sandbox organizations found"));
575
867
  console.log(
576
- import_chalk3.default.dim("Create an organization at https://commet.co first")
868
+ import_chalk5.default.dim("Create an organization at https://commet.co first")
577
869
  );
578
870
  return;
579
871
  }
@@ -583,9 +875,9 @@ var createCommand = new import_commander.Command("create").description("Create a
583
875
  (org) => org.slug === opts.org || org.id === opts.org
584
876
  );
585
877
  if (!match) {
586
- console.log(import_chalk3.default.red(`\u2717 Organization "${opts.org}" not found`));
878
+ console.log(import_chalk5.default.red(`\u2717 Organization "${opts.org}" not found`));
587
879
  console.log(
588
- import_chalk3.default.dim(
880
+ import_chalk5.default.dim(
589
881
  `Available: ${organizations.map((o) => o.slug).join(", ")}`
590
882
  )
591
883
  );
@@ -596,9 +888,9 @@ var createCommand = new import_commander.Command("create").description("Create a
596
888
  selectedOrg = organizations[0];
597
889
  } else {
598
890
  if (!isInteractive()) {
599
- console.log(import_chalk3.default.red("\u2717 Organization is required"));
891
+ console.log(import_chalk5.default.red("\u2717 Organization is required"));
600
892
  console.log(
601
- import_chalk3.default.dim(
893
+ import_chalk5.default.dim(
602
894
  `Pass --org=<slug>. Available: ${organizations.map((o) => o.slug).join(", ")}`
603
895
  )
604
896
  );
@@ -608,30 +900,30 @@ var createCommand = new import_commander.Command("create").description("Create a
608
900
  const orgId = await (0, import_prompts.select)({
609
901
  message: "Sandbox organization:",
610
902
  choices: organizations.map((org) => ({
611
- name: `${org.name} ${import_chalk3.default.dim(`(${org.slug})`)}`,
903
+ name: `${org.name} ${import_chalk5.default.dim(`(${org.slug})`)}`,
612
904
  value: org.id
613
905
  })),
614
906
  theme: promptTheme
615
907
  });
616
908
  selectedOrg = organizations.find((org) => org.id === orgId);
617
909
  } catch {
618
- console.log(import_chalk3.default.yellow("\n\u26A0 Cancelled"));
910
+ console.log(import_chalk5.default.yellow("\n\u26A0 Cancelled"));
619
911
  return;
620
912
  }
621
913
  }
622
914
  let template = TEMPLATES.find((t) => t.name === opts.template);
623
915
  if (opts.template && !template) {
624
- console.log(import_chalk3.default.red(`\u2717 Unknown template "${opts.template}"`));
916
+ console.log(import_chalk5.default.red(`\u2717 Unknown template "${opts.template}"`));
625
917
  console.log(
626
- import_chalk3.default.dim("Run `commet create --list` to see available templates")
918
+ import_chalk5.default.dim("Run `commet create --list` to see available templates")
627
919
  );
628
920
  return;
629
921
  }
630
922
  if (!template) {
631
923
  if (!isInteractive()) {
632
- console.log(import_chalk3.default.red("\u2717 Template is required"));
924
+ console.log(import_chalk5.default.red("\u2717 Template is required"));
633
925
  console.log(
634
- import_chalk3.default.dim(
926
+ import_chalk5.default.dim(
635
927
  `Pass --template=<name>. Available: ${TEMPLATES.map((t) => t.name).join(", ")}`
636
928
  )
637
929
  );
@@ -641,26 +933,26 @@ var createCommand = new import_commander.Command("create").description("Create a
641
933
  const selected = await (0, import_prompts.select)({
642
934
  message: "Billing model:",
643
935
  choices: TEMPLATES.map((t) => ({
644
- name: `${t.name.padEnd(14)} ${import_chalk3.default.dim(t.description)}`,
936
+ name: `${t.name.padEnd(14)} ${import_chalk5.default.dim(t.description)}`,
645
937
  value: t.name
646
938
  })),
647
939
  theme: promptTheme
648
940
  });
649
941
  template = TEMPLATES.find((t) => t.name === selected);
650
942
  } catch {
651
- console.log(import_chalk3.default.yellow("\n\u26A0 Cancelled"));
943
+ console.log(import_chalk5.default.yellow("\n\u26A0 Cancelled"));
652
944
  return;
653
945
  }
654
946
  }
655
947
  const shouldInstallSkills = await resolveSkills(opts);
656
- const downloadSpinner = (0, import_ora2.default)("Downloading template...").start();
948
+ const downloadSpinner = (0, import_ora3.default)("Downloading template...").start();
657
949
  try {
658
950
  await downloadTemplate(template.dir, dest, opts.ref);
659
951
  downloadSpinner.succeed("Template downloaded");
660
952
  } catch (error) {
661
953
  downloadSpinner.fail("Failed to download template");
662
954
  if (error instanceof Error) {
663
- console.error(import_chalk3.default.red(error.message));
955
+ console.error(import_chalk5.default.red(error.message));
664
956
  }
665
957
  if (fs2.existsSync(dest)) {
666
958
  fs2.rmSync(dest, { recursive: true, force: true });
@@ -669,7 +961,7 @@ var createCommand = new import_commander.Command("create").description("Create a
669
961
  }
670
962
  updatePackageJson(dest, projectName);
671
963
  copyEnvExample(dest);
672
- const resolveSpinner = (0, import_ora2.default)("Resolving package versions...").start();
964
+ const resolveSpinner = (0, import_ora3.default)("Resolving package versions...").start();
673
965
  try {
674
966
  const count = await resolveWorkspaceDeps(dest);
675
967
  if (count > 0) {
@@ -680,14 +972,14 @@ var createCommand = new import_commander.Command("create").description("Create a
680
972
  } catch (error) {
681
973
  resolveSpinner.fail("Failed to resolve package versions");
682
974
  if (error instanceof Error) {
683
- console.error(import_chalk3.default.red(error.message));
975
+ console.error(import_chalk5.default.red(error.message));
684
976
  }
685
977
  if (fs2.existsSync(dest)) {
686
978
  fs2.rmSync(dest, { recursive: true, force: true });
687
979
  }
688
980
  return;
689
981
  }
690
- const planSpinner = (0, import_ora2.default)("Creating plans...").start();
982
+ const planSpinner = (0, import_ora3.default)("Creating plans...").start();
691
983
  const templateResult = await apiRequest(`${BASE_URL}/api/cli/templates`, {
692
984
  method: "POST",
693
985
  body: JSON.stringify({
@@ -697,13 +989,13 @@ var createCommand = new import_commander.Command("create").description("Create a
697
989
  });
698
990
  if (templateResult.error || !templateResult.data) {
699
991
  planSpinner.fail("Failed to create plans");
700
- console.log(import_chalk3.default.dim(templateResult.error));
992
+ console.log(import_chalk5.default.dim(templateResult.error));
701
993
  } else {
702
994
  planSpinner.succeed(
703
995
  `Created ${templateResult.data.plansCreated} plans and ${templateResult.data.featuresCreated} features`
704
996
  );
705
997
  }
706
- const keySpinner = (0, import_ora2.default)("Creating API key...").start();
998
+ const keySpinner = (0, import_ora3.default)("Creating API key...").start();
707
999
  const keyResult = await apiRequest(`${BASE_URL}/api/cli/api-keys`, {
708
1000
  method: "POST",
709
1001
  body: JSON.stringify({
@@ -713,8 +1005,8 @@ var createCommand = new import_commander.Command("create").description("Create a
713
1005
  });
714
1006
  if (keyResult.error || !keyResult.data) {
715
1007
  keySpinner.fail("Failed to create API key");
716
- console.log(import_chalk3.default.dim(keyResult.error));
717
- console.log(import_chalk3.default.dim("You can create one manually in the dashboard"));
1008
+ console.log(import_chalk5.default.dim(keyResult.error));
1009
+ console.log(import_chalk5.default.dim("You can create one manually in the dashboard"));
718
1010
  } else {
719
1011
  writeApiKeyToEnv(dest, keyResult.data.apiKey);
720
1012
  keySpinner.succeed("API key created and saved to .env");
@@ -723,10 +1015,10 @@ var createCommand = new import_commander.Command("create").description("Create a
723
1015
  if (shouldInstallSkills) {
724
1016
  await installSkills(dest);
725
1017
  }
726
- console.log(import_chalk3.default.green(`
1018
+ console.log(import_chalk5.default.green(`
727
1019
  \u2713 Created ${projectName}`));
728
- console.log(import_chalk3.default.dim(` Template: ${template.name}`));
729
- console.log(import_chalk3.default.dim(` Organization: ${selectedOrg.name} \xB7 sandbox`));
1020
+ console.log(import_chalk5.default.dim(` Template: ${template.name}`));
1021
+ console.log(import_chalk5.default.dim(` Organization: ${selectedOrg.name} \xB7 sandbox`));
730
1022
  console.log();
731
1023
  console.log(` ${commetColor("cd")} ${projectName}`);
732
1024
  console.log(` ${commetColor("npm install")}`);
@@ -734,43 +1026,11 @@ var createCommand = new import_commander.Command("create").description("Create a
734
1026
  console.log();
735
1027
  });
736
1028
 
737
- // src/commands/info.ts
738
- var import_chalk4 = __toESM(require("chalk"));
739
- var import_commander2 = require("commander");
740
- var infoCommand = new import_commander2.Command("info").description("Display information about the current project").action(async () => {
741
- console.log(import_chalk4.default.bold("\n\u{1F4E6} Project Information\n"));
742
- if (!authExists()) {
743
- console.log(import_chalk4.default.yellow("Authentication: Not logged in"));
744
- console.log(import_chalk4.default.dim("Run `commet login` to authenticate\n"));
745
- return;
746
- }
747
- console.log(import_chalk4.default.green("Authentication: Logged in \u2713"));
748
- if (!projectConfigExists()) {
749
- console.log(import_chalk4.default.yellow("\nProject: Not linked"));
750
- console.log(
751
- import_chalk4.default.dim("Run `commet link` to connect to an organization\n")
752
- );
753
- return;
754
- }
755
- const projectConfig = loadProjectConfig();
756
- if (!projectConfig) {
757
- console.log(import_chalk4.default.red("\nProject: Invalid configuration"));
758
- return;
759
- }
760
- console.log(import_chalk4.default.green("\nProject: Linked \u2713"));
761
- console.log(import_chalk4.default.dim(" Organization:"), projectConfig.orgName);
762
- console.log(import_chalk4.default.dim(" Organization ID:"), projectConfig.orgId);
763
- console.log(import_chalk4.default.dim(" Mode:"), projectConfig.mode);
764
- console.log(
765
- import_chalk4.default.dim("\nRun `commet pull` to generate type definitions\n")
766
- );
767
- });
768
-
769
1029
  // src/commands/link.ts
770
1030
  var import_prompts2 = require("@inquirer/prompts");
771
- var import_chalk5 = __toESM(require("chalk"));
1031
+ var import_chalk6 = __toESM(require("chalk"));
772
1032
  var import_commander3 = require("commander");
773
- var import_ora3 = __toESM(require("ora"));
1033
+ var import_ora4 = __toESM(require("ora"));
774
1034
 
775
1035
  // src/utils/gitignore-updater.ts
776
1036
  var fs3 = __toESM(require("fs"));
@@ -806,192 +1066,199 @@ function updateGitignore(entry) {
806
1066
  }
807
1067
 
808
1068
  // src/commands/link.ts
809
- var linkCommand = new import_commander3.Command("link").description("Link this project to a Commet organization").action(async () => {
810
- if (!authExists()) {
811
- console.log(import_chalk5.default.red("\u2717 Not authenticated"));
812
- console.log(import_chalk5.default.dim("Run `commet login` first"));
1069
+ var linkCommand = new import_commander3.Command("link").description(
1070
+ "Link this project to a Commet organization. Re-run to switch orgs."
1071
+ ).option(
1072
+ "--org <slug-or-id>",
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"
1078
+ ).addHelpText(
1079
+ "after",
1080
+ `
1081
+ Examples:
1082
+ $ commet link Interactive \u2014 choose from a list
1083
+ $ commet link --org acme Non-interactive \u2014 match by slug or ID
1084
+ $ commet link --org other Re-run to switch organization
1085
+ $ commet link --clear Unlink project
1086
+ `
1087
+ ).action(async (options) => {
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
+ );
1097
+ }
1098
+ return;
1099
+ }
1100
+ clearProjectConfig();
1101
+ if (agentMode) {
1102
+ console.log(JSON.stringify({ success: true, action: "unlinked" }));
1103
+ } else {
1104
+ console.log(import_chalk6.default.green("\u2713 Project unlinked"));
1105
+ }
813
1106
  return;
814
1107
  }
815
- if (projectConfigExists()) {
816
- const config = loadProjectConfig();
817
- console.log(import_chalk5.default.yellow("\u26A0 This project is already linked"));
818
- console.log(
819
- import_chalk5.default.dim(`Organization: ${config?.orgName} \xB7 ${config?.mode}`)
820
- );
821
- console.log(
822
- import_chalk5.default.dim(
823
- "\nRun `commet unlink` first if you want to change the organization"
824
- )
825
- );
826
- return;
1108
+ if (!authExists()) {
1109
+ exitWithError({
1110
+ code: "auth_required",
1111
+ message: "Not authenticated",
1112
+ action: "commet login"
1113
+ });
827
1114
  }
828
- const spinner = (0, import_ora3.default)("Fetching organizations...").start();
1115
+ const currentConfig = projectConfigExists() ? loadProjectConfig() : null;
1116
+ const spinner = agentMode ? null : (0, import_ora4.default)("Fetching organizations...").start();
829
1117
  const result = await apiRequest(
830
1118
  `${BASE_URL}/api/cli/organizations`
831
1119
  );
832
1120
  if (result.error || !result.data) {
833
- spinner.fail("Failed to fetch organizations");
834
- console.error(import_chalk5.default.red("Error:"), result.error);
835
- return;
1121
+ if (agentMode) {
1122
+ console.log(JSON.stringify({ error: result.error }));
1123
+ } else {
1124
+ spinner?.fail("Failed to fetch organizations");
1125
+ console.error(import_chalk6.default.red("Error:"), result.error?.message);
1126
+ }
1127
+ process.exit(1);
836
1128
  }
837
1129
  const { organizations } = result.data;
838
1130
  if (organizations.length === 0) {
839
- spinner.stop();
840
- console.log(import_chalk5.default.yellow("\u26A0 No organizations found"));
841
- console.log(
842
- import_chalk5.default.dim("Create an organization at https://commet.co first")
843
- );
1131
+ spinner?.stop();
1132
+ if (agentMode) {
1133
+ console.log(
1134
+ JSON.stringify({
1135
+ error: {
1136
+ code: "no_organizations",
1137
+ message: "No organizations found"
1138
+ }
1139
+ })
1140
+ );
1141
+ } else {
1142
+ console.log(import_chalk6.default.yellow("\u26A0 No organizations found"));
1143
+ console.log(
1144
+ import_chalk6.default.dim("Create an organization at https://commet.co first")
1145
+ );
1146
+ }
844
1147
  return;
845
1148
  }
846
- spinner.stop();
847
- let orgId;
848
- try {
849
- orgId = await (0, import_prompts2.select)({
850
- message: "Select organization:",
851
- choices: organizations.map((org) => ({
852
- name: `${org.name} ${import_chalk5.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
853
- value: org.id
854
- })),
855
- theme: promptTheme
856
- });
857
- } catch (_error) {
858
- console.log(import_chalk5.default.yellow("\n\u26A0 Link cancelled"));
859
- return;
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
+ }
1175
+ }
1176
+ process.exit(1);
1177
+ }
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"));
1192
+ return;
1193
+ }
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
+ }
860
1201
  }
861
- const selectedOrg = organizations.find((org) => org.id === orgId);
862
- if (!selectedOrg) {
863
- console.log(import_chalk5.default.red("\u2717 Organization not found"));
1202
+ if (currentConfig?.orgId === selectedOrg.id) {
1203
+ if (agentMode) {
1204
+ console.log(
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
+ })
1215
+ );
1216
+ } else {
1217
+ console.log(
1218
+ import_chalk6.default.green(
1219
+ `\u2713 Already linked to ${selectedOrg.name} (${selectedOrg.mode})`
1220
+ )
1221
+ );
1222
+ }
864
1223
  return;
865
1224
  }
1225
+ const action = currentConfig ? "switched" : "linked";
866
1226
  saveProjectConfig({
867
1227
  orgId: selectedOrg.id,
868
1228
  orgName: selectedOrg.name,
869
1229
  mode: selectedOrg.mode
870
1230
  });
871
1231
  const gitignoreResult = updateGitignore(".commet/");
872
- console.log(import_chalk5.default.green("\n\u2713 Project linked successfully"));
873
- console.log(
874
- import_chalk5.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
875
- );
876
- if (gitignoreResult.success) {
877
- console.log(import_chalk5.default.green("\u2713 Updated .gitignore"));
878
- } else {
879
- console.log(import_chalk5.default.yellow("\u26A0 No .gitignore found"));
880
- console.log(import_chalk5.default.dim("Add .commet/ to your .gitignore file"));
881
- }
882
- console.log(import_chalk5.default.dim("\nRun 'commet pull' to generate TypeScript types"));
883
- });
884
-
885
- // src/commands/list.ts
886
- var import_chalk6 = __toESM(require("chalk"));
887
- var import_commander4 = require("commander");
888
- var import_ora4 = __toESM(require("ora"));
889
- var validTypes = ["features", "seats", "plans"];
890
- var listCommand = new import_commander4.Command("list").description("List features, seat types, or plans").argument("<type>", "Type to list (features, seats, or plans)").action(async (type) => {
891
- if (!validTypes.includes(type)) {
1232
+ if (agentMode) {
892
1233
  console.log(
893
- import_chalk6.default.red('\u2717 Invalid type. Use "features", "seats", or "plans"')
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
+ })
894
1244
  );
895
- console.log(import_chalk6.default.dim("\nExamples:"));
896
- console.log(import_chalk6.default.dim(" commet list features"));
897
- console.log(import_chalk6.default.dim(" commet list seats"));
898
- console.log(import_chalk6.default.dim(" commet list plans"));
899
- return;
900
- }
901
- if (!authExists()) {
902
- console.log(import_chalk6.default.red("\u2717 Not authenticated"));
903
- console.log(import_chalk6.default.dim("Run `commet login` first"));
904
- return;
905
- }
906
- if (!projectConfigExists()) {
907
- console.log(import_chalk6.default.red("\u2717 Project not linked"));
1245
+ } else {
1246
+ const verb = action === "switched" ? "Switched to" : "Linked to";
908
1247
  console.log(
909
- import_chalk6.default.dim("Run `commet link` first to connect to an organization")
1248
+ import_chalk6.default.green(`
1249
+ \u2713 ${verb} ${selectedOrg.name} (${selectedOrg.mode})`)
910
1250
  );
911
- return;
912
- }
913
- const projectConfig = loadProjectConfig();
914
- if (!projectConfig) {
915
- console.log(import_chalk6.default.red("\u2717 Invalid project configuration"));
916
- return;
917
- }
918
- const spinner = (0, import_ora4.default)(`Fetching ${type}...`).start();
919
- const result = await apiRequest(
920
- `${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
921
- );
922
- if (result.error || !result.data) {
923
- spinner.fail(`Failed to fetch ${type}`);
924
- console.error(import_chalk6.default.red("Error:"), result.error);
925
- return;
926
- }
927
- spinner.stop();
928
- if (type === "features") {
929
- const { features } = result.data;
930
- if (features.length === 0) {
931
- console.log(import_chalk6.default.yellow("\u26A0 No features found"));
932
- console.log(
933
- import_chalk6.default.dim("Create features in your Commet dashboard first")
934
- );
935
- return;
936
- }
937
- console.log(import_chalk6.default.bold(`
938
- \u{1F4CA} Features (${features.length})
939
- `));
940
- for (const feature of features) {
941
- console.log(import_chalk6.default.green(`\u2022 ${feature.code} (${feature.type})`));
942
- console.log(import_chalk6.default.dim(` ${feature.name}`));
943
- if (feature.description) {
944
- console.log(import_chalk6.default.dim(` ${feature.description}`));
945
- }
946
- console.log("");
947
- }
948
- } else if (type === "seats") {
949
- const { seatTypes } = result.data;
950
- if (seatTypes.length === 0) {
951
- console.log(import_chalk6.default.yellow("\u26A0 No seat types found"));
952
- console.log(
953
- import_chalk6.default.dim("Create seat types in your Commet dashboard first")
954
- );
955
- return;
956
- }
957
- console.log(import_chalk6.default.bold(`
958
- \u{1F4BA} Seat Types (${seatTypes.length})
959
- `));
960
- for (const seatType of seatTypes) {
961
- console.log(
962
- import_chalk6.default.green(`\u2022 ${seatType.code}${seatType.isFree ? " (Free)" : ""}`)
963
- );
964
- console.log(import_chalk6.default.dim(` ${seatType.name}`));
965
- if (seatType.description) {
966
- console.log(import_chalk6.default.dim(` ${seatType.description}`));
967
- }
968
- console.log("");
969
- }
970
- } else {
971
- const { plans } = result.data;
972
- if (plans.length === 0) {
973
- console.log(import_chalk6.default.yellow("\u26A0 No plans found"));
974
- console.log(import_chalk6.default.dim("Create plans in your Commet dashboard first"));
975
- return;
976
- }
977
- console.log(import_chalk6.default.bold(`
978
- \u{1F4CB} Plans (${plans.length})
979
- `));
980
- for (const plan of plans) {
981
- console.log(import_chalk6.default.green(`\u2022 ${plan.code}`));
982
- console.log(import_chalk6.default.dim(` ${plan.name}`));
983
- if (plan.description) {
984
- console.log(import_chalk6.default.dim(` ${plan.description}`));
985
- }
986
- console.log("");
1251
+ if (gitignoreResult.success && action === "linked") {
1252
+ console.log(import_chalk6.default.green("\u2713 Updated .gitignore"));
987
1253
  }
1254
+ console.log(import_chalk6.default.dim("\nRun `commet pull` to sync your config"));
988
1255
  }
989
1256
  });
990
1257
 
991
1258
  // src/commands/listen.ts
992
1259
  var import_ably = __toESM(require("ably"));
993
1260
  var import_chalk7 = __toESM(require("chalk"));
994
- var import_commander5 = require("commander");
1261
+ var import_commander4 = require("commander");
995
1262
  function printEventLine(line) {
996
1263
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
997
1264
  const eventName = import_chalk7.default.yellow(line.event.padEnd(28));
@@ -1027,64 +1294,52 @@ function resolveTargetUrl(input2) {
1027
1294
  }
1028
1295
  return parsed.toString();
1029
1296
  }
1030
- var listenCommand = new import_commander5.Command("listen").description("Forward webhook events to your local server").argument(
1297
+ var listenCommand = new import_commander4.Command("listen").description(
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."
1299
+ ).argument(
1031
1300
  "<url>",
1032
- "URL to forward to (e.g. localhost:3000, local.commet.co:3010/webhooks, or just a port)"
1033
- ).option("--events <types>", "Comma-separated event types to filter").action(async (url, options) => {
1034
- const auth = loadAuth();
1035
- if (!auth) {
1036
- console.log(import_chalk7.default.red("Not authenticated. Run: commet login"));
1037
- process.exit(1);
1038
- }
1039
- const projectConfig = loadProjectConfig();
1040
- if (!projectConfig) {
1041
- console.log(import_chalk7.default.red("No project linked. Run: commet link"));
1042
- process.exit(1);
1043
- }
1301
+ "Target URL \u2014 a port (3000), host:port (localhost:3000), or full URL"
1302
+ ).option(
1303
+ "--events <types>",
1304
+ "Only forward these event types (comma-separated)"
1305
+ ).addHelpText(
1306
+ "after",
1307
+ `
1308
+ Examples:
1309
+ $ commet listen 3000 Forward to http://localhost:3000/
1310
+ $ commet listen localhost:3000/webhooks Forward to a specific path
1311
+ $ commet listen 3000 --events invoice.paid Only forward invoice.paid events
1312
+ `
1313
+ ).action(async (url, options) => {
1314
+ const { orgId } = requireOrgContext();
1044
1315
  let targetUrl;
1045
1316
  try {
1046
1317
  targetUrl = resolveTargetUrl(url);
1047
1318
  } catch (error) {
1048
- console.log(
1049
- import_chalk7.default.red(error instanceof Error ? error.message : "Invalid URL")
1050
- );
1051
- process.exit(1);
1319
+ exitWithError({
1320
+ code: "invalid_url",
1321
+ message: error instanceof Error ? error.message : "Invalid URL"
1322
+ });
1052
1323
  }
1053
- async function fetchTokenRequest() {
1054
- const result = await apiRequest(
1055
- `${BASE_URL}/api/cli/listen/start`,
1056
- {
1057
- method: "POST",
1058
- body: JSON.stringify({ organizationId: projectConfig.orgId })
1059
- }
1060
- );
1061
- if (result.error || !result.data) {
1062
- console.log(import_chalk7.default.red("Failed to start listen session"));
1063
- if (result.error) {
1064
- console.log(import_chalk7.default.dim(result.error));
1065
- }
1066
- process.exit(1);
1067
- }
1068
- return result.data;
1069
- }
1070
- const initialSession = await fetchTokenRequest();
1071
- const { sessionId, channelName, signingSecret, tokenRequest } = initialSession;
1072
- async function refreshToken() {
1073
- const result = await apiRequest(
1074
- `${BASE_URL}/api/cli/listen/refresh`,
1075
- {
1076
- method: "POST",
1077
- body: JSON.stringify({
1078
- sessionId,
1079
- organizationId: projectConfig.orgId
1080
- })
1081
- }
1082
- );
1083
- if (result.error || !result.data) {
1084
- 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
+ })
1085
1334
  }
1086
- return result.data.tokenRequest;
1335
+ );
1336
+ if (startResult.error || !startResult.data) {
1337
+ exitWithError({
1338
+ code: "listen_failed",
1339
+ message: "Failed to start listen session"
1340
+ });
1087
1341
  }
1342
+ const { sessionId, channelName, signingSecret, tokenRequest } = startResult.data;
1088
1343
  let isFirstToken = true;
1089
1344
  const ably = new import_ably.default.Realtime({
1090
1345
  authCallback: async (_tokenParams, callback) => {
@@ -1094,8 +1349,20 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1094
1349
  callback(null, tokenRequest);
1095
1350
  return;
1096
1351
  }
1097
- const refreshed = await refreshToken();
1098
- callback(null, refreshed);
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);
1099
1366
  } catch (error) {
1100
1367
  callback(
1101
1368
  error instanceof Error ? error.message : "Token refresh failed",
@@ -1122,9 +1389,7 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1122
1389
  });
1123
1390
  const channel = ably.channels.get(channelName);
1124
1391
  console.log("");
1125
- console.log(
1126
- import_chalk7.default.green(` \u2713 Authenticated (org: ${projectConfig.orgName})`)
1127
- );
1392
+ console.log(import_chalk7.default.green(` \u2713 Authenticated (org: ${orgName})`));
1128
1393
  console.log(import_chalk7.default.green(" \u2713 Connected to Commet webhook stream"));
1129
1394
  console.log(import_chalk7.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
1130
1395
  console.log(import_chalk7.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
@@ -1144,7 +1409,11 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1144
1409
  body: JSON.stringify(payload)
1145
1410
  }).then((response) => {
1146
1411
  const ms = Math.round(performance.now() - start);
1147
- printEventLine({ event, statusCode: response.status, ms });
1412
+ printEventLine({
1413
+ event,
1414
+ statusCode: response.status,
1415
+ ms
1416
+ });
1148
1417
  }).catch((error) => {
1149
1418
  const ms = Math.round(performance.now() - start);
1150
1419
  printEventLine({
@@ -1164,7 +1433,7 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1164
1433
  method: "POST",
1165
1434
  body: JSON.stringify({
1166
1435
  sessionId,
1167
- organizationId: projectConfig.orgId
1436
+ ...organizationId ? { organizationId } : {}
1168
1437
  })
1169
1438
  });
1170
1439
  process.exit(0);
@@ -1173,8 +1442,23 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1173
1442
 
1174
1443
  // src/commands/login.ts
1175
1444
  var import_chalk8 = __toESM(require("chalk"));
1176
- var import_commander6 = require("commander");
1177
- var loginCommand = new import_commander6.Command("login").description("Authenticate with Commet").action(async () => {
1445
+ var import_commander5 = require("commander");
1446
+ var loginCommand = new import_commander5.Command("login").description(
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."
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
+ }
1178
1462
  if (authExists()) {
1179
1463
  console.log(import_chalk8.default.yellow("\u26A0 You are already logged in."));
1180
1464
  console.log(
@@ -1197,8 +1481,25 @@ var loginCommand = new import_commander6.Command("login").description("Authentic
1197
1481
 
1198
1482
  // src/commands/logout.ts
1199
1483
  var import_chalk9 = __toESM(require("chalk"));
1200
- var import_commander7 = require("commander");
1201
- var logoutCommand = new import_commander7.Command("logout").description("Log out of Commet").action(async () => {
1484
+ var import_commander6 = require("commander");
1485
+ var logoutCommand = new import_commander6.Command("logout").description(
1486
+ "Log out and remove stored credentials from ~/.commet/auth.json."
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
+ }
1202
1503
  if (!authExists()) {
1203
1504
  console.log(import_chalk9.default.yellow("\u26A0 You are not logged in."));
1204
1505
  return;
@@ -1207,13 +1508,76 @@ var logoutCommand = new import_commander7.Command("logout").description("Log out
1207
1508
  console.log(import_chalk9.default.green("\u2713 Successfully logged out"));
1208
1509
  });
1209
1510
 
1511
+ // src/commands/orgs.ts
1512
+ var import_chalk10 = __toESM(require("chalk"));
1513
+ var import_commander7 = require("commander");
1514
+ var import_ora5 = __toESM(require("ora"));
1515
+ var orgsCommand = new import_commander7.Command("orgs").description(
1516
+ "List all organizations you have access to. Shows name, slug, mode (live/sandbox), and which one is currently linked."
1517
+ ).option(
1518
+ "--output <format>",
1519
+ "Output format: human (default) or agent",
1520
+ "human"
1521
+ ).addHelpText(
1522
+ "after",
1523
+ `
1524
+ Examples:
1525
+ $ commet orgs Show orgs with current selection marked
1526
+ $ commet orgs --json JSON array for agent/CI use
1527
+
1528
+ The slug shown here is what you pass to 'commet link --org <slug>'.
1529
+ `
1530
+ ).action(async (options) => {
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 }));
1540
+ } else {
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
+
1210
1574
  // src/commands/pull.ts
1211
1575
  var fs5 = __toESM(require("fs"));
1212
1576
  var path5 = __toESM(require("path"));
1213
1577
  var import_prompts3 = require("@inquirer/prompts");
1214
- var import_chalk11 = __toESM(require("chalk"));
1578
+ var import_chalk12 = __toESM(require("chalk"));
1215
1579
  var import_commander8 = require("commander");
1216
- var import_ora5 = __toESM(require("ora"));
1580
+ var import_ora6 = __toESM(require("ora"));
1217
1581
 
1218
1582
  // src/utils/config-loader.ts
1219
1583
  var fs4 = __toESM(require("fs"));
@@ -1326,7 +1690,7 @@ function validateConfig(config, configPath) {
1326
1690
  }
1327
1691
 
1328
1692
  // src/utils/diff.ts
1329
- var import_chalk10 = __toESM(require("chalk"));
1693
+ var import_chalk11 = __toESM(require("chalk"));
1330
1694
  function computeDiff(config, remote) {
1331
1695
  const remoteFeatureMap = new Map(remote.features.map((f) => [f.code, f]));
1332
1696
  const remotePlanMap = new Map(remote.plans.map((p) => [p.code, p]));
@@ -1413,42 +1777,42 @@ function computeDiff(config, remote) {
1413
1777
  }
1414
1778
  function formatDiff(diff) {
1415
1779
  const lines = [];
1416
- lines.push(import_chalk10.default.bold("\nFeatures:"));
1780
+ lines.push(import_chalk11.default.bold("\nFeatures:"));
1417
1781
  for (const change of diff.features.changes) {
1418
1782
  if (change.action === "create") {
1419
- lines.push(import_chalk10.default.green(` + ${change.code}`));
1783
+ lines.push(import_chalk11.default.green(` + ${change.code}`));
1420
1784
  } else if (change.action === "update") {
1421
- lines.push(import_chalk10.default.yellow(` ~ ${change.code}`));
1785
+ lines.push(import_chalk11.default.yellow(` ~ ${change.code}`));
1422
1786
  for (const c of change.changes ?? []) {
1423
- lines.push(import_chalk10.default.dim(` ${c}`));
1787
+ lines.push(import_chalk11.default.dim(` ${c}`));
1424
1788
  }
1425
1789
  } else {
1426
- lines.push(import_chalk10.default.dim(` ${change.code}`));
1790
+ lines.push(import_chalk11.default.dim(` ${change.code}`));
1427
1791
  }
1428
1792
  }
1429
1793
  if (diff.features.unmanaged.length > 0) {
1430
1794
  lines.push(
1431
- import_chalk10.default.dim(
1795
+ import_chalk11.default.dim(
1432
1796
  ` ? unmanaged: ${diff.features.unmanaged.join(", ")} (not in config, left as-is)`
1433
1797
  )
1434
1798
  );
1435
1799
  }
1436
- lines.push(import_chalk10.default.bold("\nPlans:"));
1800
+ lines.push(import_chalk11.default.bold("\nPlans:"));
1437
1801
  for (const change of diff.plans.changes) {
1438
1802
  if (change.action === "create") {
1439
- lines.push(import_chalk10.default.green(` + ${change.code}`));
1803
+ lines.push(import_chalk11.default.green(` + ${change.code}`));
1440
1804
  } else if (change.action === "update") {
1441
- lines.push(import_chalk10.default.yellow(` ~ ${change.code}`));
1805
+ lines.push(import_chalk11.default.yellow(` ~ ${change.code}`));
1442
1806
  for (const c of change.changes ?? []) {
1443
- lines.push(import_chalk10.default.dim(` ${c}`));
1807
+ lines.push(import_chalk11.default.dim(` ${c}`));
1444
1808
  }
1445
1809
  } else {
1446
- lines.push(import_chalk10.default.dim(` ${change.code}`));
1810
+ lines.push(import_chalk11.default.dim(` ${change.code}`));
1447
1811
  }
1448
1812
  }
1449
1813
  if (diff.plans.unmanaged.length > 0) {
1450
1814
  lines.push(
1451
- import_chalk10.default.dim(
1815
+ import_chalk11.default.dim(
1452
1816
  ` ? unmanaged: ${diff.plans.unmanaged.join(", ")} (not in config, left as-is)`
1453
1817
  )
1454
1818
  );
@@ -1532,47 +1896,36 @@ function generateConfigFile(features, plans) {
1532
1896
  }
1533
1897
 
1534
1898
  // src/commands/pull.ts
1535
- var pullCommand = new import_commander8.Command("pull").description("Pull config from Commet and generate commet.config.ts").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show diff without applying changes").option("--json", "Output structured JSON (no colors, no prompts)").action(async (options) => {
1536
- const jsonMode = options.json;
1537
- if (!authExists()) {
1538
- if (jsonMode) {
1539
- console.log(JSON.stringify({ error: "Not authenticated" }));
1540
- } else {
1541
- console.log(import_chalk11.default.red("\u2717 Not authenticated"));
1542
- console.log(import_chalk11.default.dim("Run `commet login` first"));
1543
- }
1544
- process.exit(1);
1545
- }
1546
- if (!projectConfigExists()) {
1547
- if (jsonMode) {
1548
- console.log(JSON.stringify({ error: "Project not linked" }));
1549
- } else {
1550
- console.log(import_chalk11.default.red("\u2717 Project not linked"));
1551
- console.log(
1552
- import_chalk11.default.dim("Run `commet link` first to connect to an organization")
1553
- );
1554
- }
1555
- process.exit(1);
1556
- }
1557
- const projectConfig = loadProjectConfig();
1558
- if (!projectConfig) {
1559
- if (jsonMode) {
1560
- console.log(JSON.stringify({ error: "Invalid project configuration" }));
1561
- } else {
1562
- console.log(import_chalk11.default.red("\u2717 Invalid project configuration"));
1563
- }
1564
- process.exit(1);
1565
- }
1566
- const spinner = jsonMode ? null : (0, import_ora5.default)("Fetching config from remote...").start();
1899
+ var pullCommand = new import_commander8.Command("pull").description(
1900
+ "Fetch your billing config from Commet and generate (or update) commet.config.ts with features and plans."
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(
1906
+ "after",
1907
+ `
1908
+ Examples:
1909
+ $ commet pull Interactive \u2014 shows diff, asks to confirm
1910
+ $ commet pull --dry-run Preview changes without applying
1911
+ $ commet pull --yes Apply without confirmation
1912
+ $ commet pull --output agent --yes Agent/CI \u2014 structured JSON, no prompts
1913
+ $ COMMET_API_KEY=sk_... commet pull --yes CI pipeline
1914
+ `
1915
+ ).action(async (options) => {
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}`;
1567
1920
  const result = await apiRequest(
1568
- `${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
1921
+ `${BASE_URL}/api/cli/pull${orgQuery}`
1569
1922
  );
1570
1923
  if (result.error || !result.data) {
1571
- if (jsonMode) {
1924
+ if (agentMode) {
1572
1925
  console.log(JSON.stringify({ error: result.error }));
1573
1926
  } else {
1574
1927
  spinner?.fail("Failed to fetch config");
1575
- console.error(import_chalk11.default.red("Error:"), result.error);
1928
+ console.error(import_chalk12.default.red("Error:"), result.error?.message);
1576
1929
  }
1577
1930
  process.exit(1);
1578
1931
  }
@@ -1583,7 +1936,7 @@ var pullCommand = new import_commander8.Command("pull").description("Pull config
1583
1936
  const existingConfigPath = findConfigFile(process.cwd());
1584
1937
  if (!existingConfigPath) {
1585
1938
  if (options.dryRun) {
1586
- if (jsonMode) {
1939
+ if (agentMode) {
1587
1940
  console.log(
1588
1941
  JSON.stringify({
1589
1942
  action: "create",
@@ -1594,7 +1947,7 @@ var pullCommand = new import_commander8.Command("pull").description("Pull config
1594
1947
  );
1595
1948
  } else {
1596
1949
  console.log(
1597
- import_chalk11.default.green(
1950
+ import_chalk12.default.green(
1598
1951
  `
1599
1952
  Would create commet.config.ts (${features.length} features, ${plans.length} plans)`
1600
1953
  )
@@ -1603,7 +1956,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1603
1956
  return;
1604
1957
  }
1605
1958
  fs5.writeFileSync(outputPath, configContent, "utf8");
1606
- if (jsonMode) {
1959
+ if (agentMode) {
1607
1960
  console.log(
1608
1961
  JSON.stringify({
1609
1962
  action: "create",
@@ -1613,10 +1966,9 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1613
1966
  })
1614
1967
  );
1615
1968
  } else {
1616
- console.log(import_chalk11.default.green(`
1617
- \u2713 Created commet.config.ts`));
1969
+ console.log(import_chalk12.default.green("\n\u2713 Created commet.config.ts"));
1618
1970
  console.log(
1619
- import_chalk11.default.dim(` ${features.length} features, ${plans.length} plans`)
1971
+ import_chalk12.default.dim(` ${features.length} features, ${plans.length} plans`)
1620
1972
  );
1621
1973
  }
1622
1974
  return;
@@ -1628,7 +1980,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1628
1980
  );
1629
1981
  if ("parseError" in localLoaded) {
1630
1982
  if (options.dryRun) {
1631
- if (jsonMode) {
1983
+ if (agentMode) {
1632
1984
  console.log(
1633
1985
  JSON.stringify({
1634
1986
  action: "overwrite",
@@ -1638,7 +1990,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1638
1990
  );
1639
1991
  } else {
1640
1992
  console.log(
1641
- import_chalk11.default.yellow(
1993
+ import_chalk12.default.yellow(
1642
1994
  `
1643
1995
  \u26A0 Local config is invalid: ${localLoaded.parseError}`
1644
1996
  )
@@ -1646,23 +1998,23 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1646
1998
  }
1647
1999
  return;
1648
2000
  }
1649
- if (!options.yes && !jsonMode) {
1650
- console.log(import_chalk11.default.yellow(`
2001
+ if (!options.yes && !agentMode) {
2002
+ console.log(import_chalk12.default.yellow(`
1651
2003
  \u26A0 ${localLoaded.parseError}`));
1652
2004
  const shouldProceed = await (0, import_prompts3.confirm)({
1653
2005
  message: "Overwrite with remote?",
1654
2006
  default: true
1655
2007
  });
1656
2008
  if (!shouldProceed) {
1657
- console.log(import_chalk11.default.dim("Pull cancelled"));
2009
+ console.log(import_chalk12.default.dim("Pull cancelled"));
1658
2010
  return;
1659
2011
  }
1660
2012
  }
1661
2013
  fs5.writeFileSync(outputPath, configContent, "utf8");
1662
- if (jsonMode) {
2014
+ if (agentMode) {
1663
2015
  console.log(JSON.stringify({ action: "overwrite", applied: true }));
1664
2016
  } else {
1665
- console.log(import_chalk11.default.green("\n\u2713 Overwritten commet.config.ts"));
2017
+ console.log(import_chalk12.default.green("\n\u2713 Overwritten commet.config.ts"));
1666
2018
  }
1667
2019
  return;
1668
2020
  }
@@ -1685,9 +2037,7 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1685
2037
  {
1686
2038
  name: p.name,
1687
2039
  ...p.description ? { description: p.description } : {},
1688
- ...p.consumptionModel ? {
1689
- consumptionModel: p.consumptionModel
1690
- } : {},
2040
+ ...p.consumptionModel ? { consumptionModel: p.consumptionModel } : {},
1691
2041
  ...p.isFree ? { isFree: true } : {},
1692
2042
  ...p.isPublic === false ? { isPublic: false } : {},
1693
2043
  ...p.sortOrder ? { sortOrder: p.sortOrder } : {},
@@ -1733,14 +2083,14 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1733
2083
  };
1734
2084
  const diff = computeDiff(remoteAsConfig, localAsRemote);
1735
2085
  if (!diff.hasChanges && diff.features.unmanaged.length === 0 && diff.plans.unmanaged.length === 0) {
1736
- if (jsonMode) {
2086
+ if (agentMode) {
1737
2087
  console.log(JSON.stringify({ diff, applied: false, upToDate: true }));
1738
2088
  } else {
1739
- console.log(import_chalk11.default.green("\n\u2713 Already up to date"));
2089
+ console.log(import_chalk12.default.green("\n\u2713 Already up to date"));
1740
2090
  }
1741
2091
  return;
1742
2092
  }
1743
- if (jsonMode) {
2093
+ if (agentMode) {
1744
2094
  if (options.dryRun) {
1745
2095
  console.log(JSON.stringify({ diff, applied: false }));
1746
2096
  return;
@@ -1749,77 +2099,69 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1749
2099
  console.log(formatDiff(diff));
1750
2100
  }
1751
2101
  if (options.dryRun) {
1752
- if (!jsonMode) {
1753
- console.log(import_chalk11.default.dim("\n(dry run \u2014 no changes applied)"));
2102
+ if (!agentMode) {
2103
+ console.log(import_chalk12.default.dim("\n(dry run \u2014 no changes applied)"));
1754
2104
  }
1755
2105
  return;
1756
2106
  }
1757
- if (!options.yes && !jsonMode) {
2107
+ if (!options.yes && !agentMode) {
1758
2108
  const shouldProceed = await (0, import_prompts3.confirm)({
1759
2109
  message: "Overwrite commet.config.ts with remote state?",
1760
2110
  default: true
1761
2111
  });
1762
2112
  if (!shouldProceed) {
1763
- console.log(import_chalk11.default.dim("Pull cancelled"));
2113
+ console.log(import_chalk12.default.dim("Pull cancelled"));
1764
2114
  return;
1765
2115
  }
1766
2116
  }
1767
2117
  fs5.writeFileSync(outputPath, configContent, "utf8");
1768
- if (jsonMode) {
2118
+ if (agentMode) {
1769
2119
  console.log(JSON.stringify({ diff, applied: true }));
1770
2120
  } else {
1771
- console.log(import_chalk11.default.green("\n\u2713 Updated commet.config.ts"));
2121
+ console.log(import_chalk12.default.green("\n\u2713 Updated commet.config.ts"));
1772
2122
  console.log(
1773
- import_chalk11.default.dim(` ${features.length} features, ${plans.length} plans`)
2123
+ import_chalk12.default.dim(` ${features.length} features, ${plans.length} plans`)
1774
2124
  );
1775
2125
  }
1776
2126
  });
1777
2127
 
1778
2128
  // src/commands/push.ts
1779
2129
  var import_prompts4 = require("@inquirer/prompts");
1780
- var import_chalk12 = __toESM(require("chalk"));
2130
+ var import_chalk13 = __toESM(require("chalk"));
1781
2131
  var import_commander9 = require("commander");
1782
- var import_ora6 = __toESM(require("ora"));
1783
- var pushCommand = new import_commander9.Command("push").description("Push commet.config.ts to your Commet organization").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show diff without applying changes").option("--json", "Output structured JSON (no colors, no prompts)").action(async (options) => {
1784
- const jsonMode = options.json;
1785
- if (!authExists()) {
1786
- if (jsonMode) {
1787
- console.log(JSON.stringify({ error: "Not authenticated" }));
1788
- } else {
1789
- console.log(import_chalk12.default.red("\u2717 Not authenticated"));
1790
- console.log(import_chalk12.default.dim("Run `commet login` first"));
1791
- }
1792
- process.exit(1);
1793
- }
1794
- if (!projectConfigExists()) {
1795
- if (jsonMode) {
1796
- console.log(JSON.stringify({ error: "Project not linked" }));
1797
- } else {
1798
- console.log(import_chalk12.default.red("\u2717 Project not linked"));
1799
- console.log(
1800
- import_chalk12.default.dim("Run `commet link` first to connect to an organization")
1801
- );
1802
- }
1803
- process.exit(1);
1804
- }
1805
- const projectConfig = loadProjectConfig();
1806
- if (!projectConfig) {
1807
- if (jsonMode) {
1808
- console.log(JSON.stringify({ error: "Invalid project configuration" }));
1809
- } else {
1810
- console.log(import_chalk12.default.red("\u2717 Invalid project configuration"));
1811
- }
1812
- process.exit(1);
1813
- }
1814
- const loadSpinner = jsonMode ? null : (0, import_ora6.default)("Loading commet.config.ts...").start();
2132
+ var import_ora7 = __toESM(require("ora"));
2133
+ var pushCommand = new import_commander9.Command("push").description(
2134
+ "Push your local commet.config.ts to Commet. Creates or updates features and plans to match your config file."
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(
2140
+ "after",
2141
+ `
2142
+ Examples:
2143
+ $ commet push Interactive \u2014 shows diff, asks to confirm
2144
+ $ commet push --dry-run Preview what would change on remote
2145
+ $ commet push --yes Push without confirmation
2146
+ $ commet push --output agent --yes Agent/CI \u2014 structured JSON, no prompts
2147
+ $ COMMET_API_KEY=sk_... commet push --yes CI pipeline
2148
+ `
2149
+ ).action(async (options) => {
2150
+ const agentMode = isAgentMode(options);
2151
+ const { orgId } = requireOrgContext();
2152
+ const loadSpinner = agentMode ? null : (0, import_ora7.default)("Loading commet.config.ts...").start();
1815
2153
  const loaded = await loadBillingConfig(process.cwd()).catch(
1816
2154
  (error) => {
1817
2155
  const message = error instanceof Error ? error.message : String(error);
1818
- if (jsonMode) {
1819
- console.log(JSON.stringify({ error: message }));
2156
+ if (agentMode) {
2157
+ console.log(
2158
+ JSON.stringify({
2159
+ error: { code: "config_invalid", message }
2160
+ })
2161
+ );
1820
2162
  } else {
1821
2163
  loadSpinner?.fail("Failed to load config");
1822
- console.error(import_chalk12.default.red(message));
2164
+ console.error(import_chalk13.default.red(message));
1823
2165
  }
1824
2166
  return null;
1825
2167
  }
@@ -1827,16 +2169,17 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1827
2169
  if (!loaded) process.exit(1);
1828
2170
  const { config, configPath } = loaded;
1829
2171
  loadSpinner?.succeed(`Loaded ${configPath}`);
1830
- const fetchSpinner = jsonMode ? null : (0, import_ora6.default)("Fetching remote state...").start();
2172
+ const fetchSpinner = agentMode ? null : (0, import_ora7.default)("Fetching remote state...").start();
2173
+ const orgQuery = orgId === "__from_api_key__" ? "" : `?orgId=${orgId}`;
1831
2174
  const remoteResult = await apiRequest(
1832
- `${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
2175
+ `${BASE_URL}/api/cli/pull${orgQuery}`
1833
2176
  );
1834
2177
  if (remoteResult.error || !remoteResult.data) {
1835
- if (jsonMode) {
2178
+ if (agentMode) {
1836
2179
  console.log(JSON.stringify({ error: remoteResult.error }));
1837
2180
  } else {
1838
2181
  fetchSpinner?.fail("Failed to fetch remote state");
1839
- console.error(import_chalk12.default.red("Error:"), remoteResult.error);
2182
+ console.error(import_chalk13.default.red("Error:"), remoteResult.error?.message);
1840
2183
  }
1841
2184
  process.exit(1);
1842
2185
  }
@@ -1846,7 +2189,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1846
2189
  plans: remoteResult.data.plans
1847
2190
  };
1848
2191
  const diff = computeDiff(config, remote);
1849
- if (jsonMode) {
2192
+ if (agentMode) {
1850
2193
  if (options.dryRun) {
1851
2194
  console.log(JSON.stringify({ diff, applied: false }));
1852
2195
  return;
@@ -1855,10 +2198,10 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1855
2198
  console.log(formatDiff(diff));
1856
2199
  }
1857
2200
  if (!diff.hasChanges) {
1858
- if (jsonMode) {
2201
+ if (agentMode) {
1859
2202
  console.log(JSON.stringify({ diff, applied: false, upToDate: true }));
1860
2203
  } else {
1861
- console.log(import_chalk12.default.green("\n\u2713 Everything is up to date"));
2204
+ console.log(import_chalk13.default.green("\n\u2713 Everything is up to date"));
1862
2205
  }
1863
2206
  return;
1864
2207
  }
@@ -1867,67 +2210,67 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1867
2210
  );
1868
2211
  if (typeChanges.length > 0) {
1869
2212
  const blockedCodes = typeChanges.map((c) => c.code);
1870
- if (jsonMode) {
2213
+ if (agentMode) {
1871
2214
  console.log(
1872
2215
  JSON.stringify({
1873
- error: "Feature type changes blocked",
2216
+ error: {
2217
+ code: "type_change_blocked",
2218
+ message: "Feature type changes must be done in the dashboard"
2219
+ },
1874
2220
  blockedCodes,
1875
2221
  diff
1876
2222
  })
1877
2223
  );
1878
2224
  } else {
1879
2225
  console.log(
1880
- import_chalk12.default.red(
2226
+ import_chalk13.default.red(
1881
2227
  "\n\u2717 Cannot change feature types. Update them in the dashboard:"
1882
2228
  )
1883
2229
  );
1884
2230
  for (const change of typeChanges) {
1885
- console.log(import_chalk12.default.red(` - ${change.code}`));
2231
+ console.log(import_chalk13.default.red(` - ${change.code}`));
1886
2232
  }
1887
2233
  }
1888
2234
  process.exit(1);
1889
2235
  }
1890
2236
  if (options.dryRun) {
1891
- if (!jsonMode) {
1892
- console.log(import_chalk12.default.dim("\n(dry run \u2014 no changes applied)"));
2237
+ if (!agentMode) {
2238
+ console.log(import_chalk13.default.dim("\n(dry run \u2014 no changes applied)"));
1893
2239
  }
1894
2240
  return;
1895
2241
  }
1896
- if (!options.yes && !jsonMode) {
2242
+ if (!options.yes && !agentMode) {
1897
2243
  const shouldProceed = await (0, import_prompts4.confirm)({
1898
2244
  message: "Apply these changes?",
1899
2245
  default: true
1900
2246
  });
1901
2247
  if (!shouldProceed) {
1902
- console.log(import_chalk12.default.dim("Push cancelled"));
2248
+ console.log(import_chalk13.default.dim("Push cancelled"));
1903
2249
  return;
1904
2250
  }
1905
2251
  }
1906
- const pushSpinner = jsonMode ? null : (0, import_ora6.default)("Pushing config...").start();
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
+ }
1907
2259
  const pushResult = await apiRequest(
1908
2260
  `${BASE_URL}/api/cli/push`,
1909
- {
1910
- method: "POST",
1911
- body: JSON.stringify({
1912
- orgId: projectConfig.orgId,
1913
- config: {
1914
- features: config.features,
1915
- plans: config.plans
1916
- }
1917
- })
1918
- }
2261
+ { method: "POST", body: JSON.stringify(pushBody) }
1919
2262
  );
1920
2263
  if (pushResult.error || !pushResult.data) {
1921
- if (jsonMode) {
2264
+ if (agentMode) {
1922
2265
  console.log(JSON.stringify({ error: pushResult.error }));
1923
2266
  } else {
1924
2267
  pushSpinner?.fail("Push failed");
1925
- console.error(import_chalk12.default.red("Error:"), pushResult.error);
2268
+ console.error(import_chalk13.default.red("Error:"), pushResult.error?.message);
1926
2269
  }
1927
2270
  process.exit(1);
1928
2271
  }
1929
2272
  const pushOutcome = pushResult.data;
1930
- if (jsonMode) {
2273
+ if (agentMode) {
1931
2274
  console.log(JSON.stringify({ diff, applied: true, result: pushOutcome }));
1932
2275
  return;
1933
2276
  }
@@ -1938,170 +2281,88 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1938
2281
  if (errors.length > 0) {
1939
2282
  pushSpinner?.warn("Push completed with errors");
1940
2283
  for (const error of errors) {
1941
- console.log(import_chalk12.default.red(` \u2717 ${error.code}: ${error.message}`));
2284
+ console.log(import_chalk13.default.red(` \u2717 ${error.code}: ${error.message}`));
1942
2285
  }
1943
2286
  } else {
1944
2287
  pushSpinner?.succeed("Push complete");
1945
2288
  }
1946
2289
  if (pushOutcome.features.created.length > 0) {
1947
2290
  console.log(
1948
- import_chalk12.default.green(
2291
+ import_chalk13.default.green(
1949
2292
  ` Created features: ${pushOutcome.features.created.join(", ")}`
1950
2293
  )
1951
2294
  );
1952
2295
  }
1953
2296
  if (pushOutcome.features.updated.length > 0) {
1954
2297
  console.log(
1955
- import_chalk12.default.yellow(
2298
+ import_chalk13.default.yellow(
1956
2299
  ` Updated features: ${pushOutcome.features.updated.join(", ")}`
1957
2300
  )
1958
2301
  );
1959
2302
  }
1960
2303
  if (pushOutcome.plans.created.length > 0) {
1961
2304
  console.log(
1962
- import_chalk12.default.green(` Created plans: ${pushOutcome.plans.created.join(", ")}`)
2305
+ import_chalk13.default.green(` Created plans: ${pushOutcome.plans.created.join(", ")}`)
1963
2306
  );
1964
2307
  }
1965
2308
  if (pushOutcome.plans.updated.length > 0) {
1966
2309
  console.log(
1967
- import_chalk12.default.yellow(
2310
+ import_chalk13.default.yellow(
1968
2311
  ` Updated plans: ${pushOutcome.plans.updated.join(", ")}`
1969
2312
  )
1970
2313
  );
1971
2314
  }
1972
2315
  });
1973
2316
 
1974
- // src/commands/switch.ts
1975
- var import_prompts5 = require("@inquirer/prompts");
1976
- var import_chalk13 = __toESM(require("chalk"));
1977
- var import_commander10 = require("commander");
1978
- var import_ora7 = __toESM(require("ora"));
1979
- var switchCommand = new import_commander10.Command("switch").description("Switch to a different organization").action(async () => {
1980
- if (!authExists()) {
1981
- console.log(import_chalk13.default.red("\u2717 Not authenticated"));
1982
- console.log(import_chalk13.default.dim("Run `commet login` first"));
1983
- return;
1984
- }
1985
- if (!projectConfigExists()) {
1986
- console.log(import_chalk13.default.yellow("\u26A0 Project not linked"));
1987
- console.log(
1988
- import_chalk13.default.dim("Run `commet link` first to connect to an organization")
1989
- );
1990
- return;
1991
- }
1992
- const spinner = (0, import_ora7.default)("Fetching organizations...").start();
1993
- const result = await apiRequest(
1994
- `${BASE_URL}/api/cli/organizations`
1995
- );
1996
- if (result.error || !result.data) {
1997
- spinner.fail("Failed to fetch organizations");
1998
- console.error(import_chalk13.default.red("Error:"), result.error);
1999
- return;
2000
- }
2001
- const { organizations } = result.data;
2002
- if (organizations.length === 0) {
2003
- spinner.stop();
2004
- console.log(import_chalk13.default.yellow("\u26A0 No organizations found"));
2005
- return;
2006
- }
2007
- spinner.stop();
2008
- let orgId;
2009
- try {
2010
- orgId = await (0, import_prompts5.select)({
2011
- message: "Select organization:",
2012
- choices: organizations.map((org) => ({
2013
- name: `${org.name} ${import_chalk13.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
2014
- value: org.id
2015
- })),
2016
- theme: promptTheme
2017
- });
2018
- } catch (_error) {
2019
- console.log(import_chalk13.default.yellow("\n\u26A0 Switch cancelled"));
2020
- return;
2021
- }
2022
- const selectedOrg = organizations.find((org) => org.id === orgId);
2023
- if (!selectedOrg) {
2024
- console.log(import_chalk13.default.red("\u2717 Organization not found"));
2025
- return;
2026
- }
2027
- saveProjectConfig({
2028
- orgId: selectedOrg.id,
2029
- orgName: selectedOrg.name,
2030
- mode: selectedOrg.mode
2031
- });
2032
- console.log(import_chalk13.default.green("\n\u2713 Switched organization successfully!"));
2033
- console.log(
2034
- import_chalk13.default.dim(`
2035
- Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
2036
- );
2037
- console.log(
2038
- import_chalk13.default.dim(
2039
- "\nRun `commet pull` to update TypeScript types for this organization"
2040
- )
2041
- );
2042
- });
2043
-
2044
- // src/commands/unlink.ts
2045
- var import_chalk14 = __toESM(require("chalk"));
2046
- var import_commander11 = require("commander");
2047
- var unlinkCommand = new import_commander11.Command("unlink").description("Unlink this project from Commet").action(async () => {
2048
- if (!projectConfigExists()) {
2049
- console.log(
2050
- import_chalk14.default.yellow("\u26A0 This project is not linked to any organization")
2051
- );
2052
- return;
2053
- }
2054
- clearProjectConfig();
2055
- console.log(import_chalk14.default.green("\u2713 Project unlinked successfully"));
2056
- console.log(import_chalk14.default.dim("\u2713 Removed .commet/ directory"));
2057
- console.log(
2058
- import_chalk14.default.dim("\nRun `commet link` to connect to a different organization")
2059
- );
2060
- });
2061
-
2062
- // src/commands/whoami.ts
2063
- var import_chalk15 = __toESM(require("chalk"));
2064
- var import_commander12 = require("commander");
2065
- var whoamiCommand = new import_commander12.Command("whoami").description("Display current authentication and project status").action(async () => {
2066
- if (!authExists()) {
2067
- console.log(import_chalk15.default.yellow("\u26A0 Not logged in"));
2068
- console.log(import_chalk15.default.dim("Run `commet login` to authenticate"));
2069
- return;
2070
- }
2071
- console.log(import_chalk15.default.green("\u2713 Logged in"));
2072
- const projectConfig = loadProjectConfig();
2073
- if (projectConfig) {
2074
- console.log(import_chalk15.default.bold("\nProject:"));
2075
- console.log(import_chalk15.default.dim("Organization:"), projectConfig.orgName);
2076
- console.log(import_chalk15.default.dim("Mode:"), projectConfig.mode);
2077
- } else {
2078
- console.log(import_chalk15.default.yellow("\n\u26A0 No project linked"));
2079
- console.log(
2080
- import_chalk15.default.dim(
2081
- "Run `commet link` to connect this directory to an organization"
2082
- )
2083
- );
2084
- }
2085
- });
2086
-
2087
2317
  // src/index.ts
2088
- var program = new import_commander13.Command();
2318
+ var program = new import_commander10.Command();
2089
2319
  program.name("commet").description(
2090
- "Commet CLI - Manage your billing platform from the command line"
2091
- ).version(package_default.version);
2320
+ "Commet CLI \u2014 billing infrastructure as code.\nManage features, plans, and billing config from the command line."
2321
+ ).version(package_default.version).addHelpText(
2322
+ "after",
2323
+ `
2324
+ Workflow:
2325
+ $ commet pull Sync remote \u2192 commet.config.ts
2326
+ $ commet push Push local changes \u2192 remote
2327
+ $ commet pull --dry-run See what's configured
2328
+
2329
+ For agents and CI:
2330
+ $ commet JSON capabilities when piped (no args)
2331
+ $ commet pull --output agent --yes Structured output, no prompts
2332
+ $ COMMET_API_KEY=sk_... commet push --yes CI pipeline
2333
+
2334
+ Run commet <command> --help for detailed usage and examples.
2335
+ `
2336
+ );
2092
2337
  program.addCommand(createCommand);
2093
2338
  program.addCommand(loginCommand);
2094
2339
  program.addCommand(logoutCommand);
2095
- program.addCommand(whoamiCommand);
2096
2340
  program.addCommand(linkCommand);
2097
- program.addCommand(unlinkCommand);
2098
- program.addCommand(switchCommand);
2099
- program.addCommand(infoCommand);
2341
+ program.addCommand(orgsCommand);
2100
2342
  program.addCommand(pushCommand);
2101
2343
  program.addCommand(pullCommand);
2102
- program.addCommand(listCommand);
2103
2344
  program.addCommand(listenCommand);
2345
+ program.addCommand(apiKeyCommand);
2346
+ program.enablePositionalOptions().passThroughOptions().option(
2347
+ "--output <format>",
2348
+ "Output format: human (default) or agent",
2349
+ "human"
2350
+ );
2351
+ program.action((options) => {
2352
+ if (options.output === "agent") {
2353
+ printAgentInfo();
2354
+ } else {
2355
+ printDefaultScreen();
2356
+ }
2357
+ });
2358
+ program.showSuggestionAfterError();
2104
2359
  program.exitOverride();
2360
+ installCrashHandler();
2361
+ markCommandStart();
2362
+ var commandName = process.argv[2] || "(default)";
2363
+ program.hook("postAction", () => {
2364
+ reportCommand(commandName, "success");
2365
+ });
2105
2366
  try {
2106
2367
  program.parse(process.argv);
2107
2368
  } catch (error) {
@@ -2110,10 +2371,145 @@ try {
2110
2371
  if (code === "commander.version" || code === "commander.help" || code === "commander.helpDisplayed") {
2111
2372
  process.exit(0);
2112
2373
  }
2113
- console.error(import_chalk16.default.red("Error:"), error.message);
2374
+ reportCommand(commandName, "error", code);
2375
+ console.error(import_chalk14.default.red("Error:"), error.message);
2114
2376
  }
2115
2377
  process.exit(1);
2116
2378
  }
2117
- if (!process.argv.slice(2).length) {
2118
- program.outputHelp();
2379
+ function printAgentInfo() {
2380
+ const authenticated = authExists() || !!process.env.COMMET_API_KEY;
2381
+ const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
2382
+ const configPath = findConfigFile(process.cwd());
2383
+ const setup = [];
2384
+ if (!authenticated) {
2385
+ setup.push(
2386
+ "Not authenticated. Run 'commet login' (interactive) or set COMMET_API_KEY env var."
2387
+ );
2388
+ }
2389
+ if (authenticated && !projectConfig && !process.env.COMMET_API_KEY) {
2390
+ setup.push(
2391
+ "No project linked. Run 'commet link --org <slug>' or 'commet orgs --json' to find organizations."
2392
+ );
2393
+ }
2394
+ const output = {
2395
+ version: package_default.version,
2396
+ authenticated,
2397
+ ...setup.length > 0 ? { setup } : {},
2398
+ project: projectConfig ? {
2399
+ linked: true,
2400
+ orgId: projectConfig.orgId,
2401
+ orgName: projectConfig.orgName,
2402
+ mode: projectConfig.mode
2403
+ } : { linked: false },
2404
+ config: {
2405
+ exists: configPath !== null,
2406
+ path: configPath?.split("/").pop() ?? null
2407
+ },
2408
+ mcp: {
2409
+ url: "https://commet.co/mcp",
2410
+ sandbox: "https://sandbox.commet.co/mcp",
2411
+ hint: "For full billing CRUD (plans, features, customers, subscriptions), connect to the MCP server."
2412
+ },
2413
+ auth: {
2414
+ interactive: "commet login",
2415
+ ci: "Set COMMET_API_KEY environment variable"
2416
+ },
2417
+ commands: {
2418
+ pull: {
2419
+ description: "Pull remote config and generate commet.config.ts",
2420
+ usage: "commet pull --output agent --yes",
2421
+ preview: "commet pull --output agent --dry-run"
2422
+ },
2423
+ push: {
2424
+ description: "Push commet.config.ts to remote",
2425
+ usage: "commet push --output agent --yes",
2426
+ preview: "commet push --output agent --dry-run",
2427
+ ci: "COMMET_API_KEY=sk_... commet push --yes"
2428
+ },
2429
+ orgs: {
2430
+ description: "List available organizations",
2431
+ usage: "commet orgs --output agent"
2432
+ },
2433
+ link: {
2434
+ description: "Link/switch/unlink organization",
2435
+ usage: "commet link --org <slug-or-id>",
2436
+ clear: "commet link --clear"
2437
+ },
2438
+ listen: {
2439
+ description: "Forward webhook events to local server. Long-running streaming process.",
2440
+ usage: "commet listen <url> [--events <types>]"
2441
+ },
2442
+ create: {
2443
+ description: "Scaffold a new Commet app from template",
2444
+ usage: "commet create [name] -t <template> --org <slug> -y"
2445
+ },
2446
+ "api-key": {
2447
+ description: "Generate API key for CI",
2448
+ usage: "commet api-key --output agent"
2449
+ },
2450
+ login: {
2451
+ description: "Authenticate via browser. Requires a human \u2014 opens a device-code flow.",
2452
+ usage: "commet login"
2453
+ },
2454
+ logout: {
2455
+ description: "Log out of Commet",
2456
+ usage: "commet logout"
2457
+ }
2458
+ }
2459
+ };
2460
+ console.log(JSON.stringify(output, null, 2));
2461
+ }
2462
+ function printDefaultScreen() {
2463
+ const version = import_chalk14.default.dim(`v${package_default.version}`);
2464
+ console.log(`
2465
+ ${commetColor.bold("Commet")} ${version}`);
2466
+ console.log(import_chalk14.default.dim(" Billing infrastructure as code\n"));
2467
+ const authenticated = authExists();
2468
+ const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
2469
+ const configFile = findConfigFile(process.cwd());
2470
+ if (authenticated) {
2471
+ console.log(import_chalk14.default.green(" \u2713 Authenticated"));
2472
+ } else {
2473
+ console.log(
2474
+ ` ${import_chalk14.default.yellow("\u26A0")} Not logged in ${import_chalk14.default.dim("\u2192 commet login")}`
2475
+ );
2476
+ }
2477
+ if (projectConfig) {
2478
+ console.log(
2479
+ import_chalk14.default.green(
2480
+ ` \u2713 ${projectConfig.orgName} ${import_chalk14.default.dim(`(${projectConfig.mode})`)}`
2481
+ )
2482
+ );
2483
+ console.log(import_chalk14.default.dim(` ${projectConfig.orgId}`));
2484
+ } else if (authenticated) {
2485
+ console.log(
2486
+ ` ${import_chalk14.default.yellow("\u26A0")} No project linked ${import_chalk14.default.dim("\u2192 commet link")}`
2487
+ );
2488
+ }
2489
+ if (configFile) {
2490
+ const fileName = configFile.split("/").pop();
2491
+ console.log(import_chalk14.default.green(` \u2713 ${fileName}`));
2492
+ } else if (projectConfig) {
2493
+ console.log(
2494
+ ` ${import_chalk14.default.yellow("\u26A0")} No config file ${import_chalk14.default.dim("\u2192 commet pull")}`
2495
+ );
2496
+ }
2497
+ const cmd = (name) => commetColor(name.padEnd(22));
2498
+ const dim = import_chalk14.default.dim;
2499
+ console.log(dim("\n Config"));
2500
+ console.log(` ${cmd("pull")}${dim("Sync remote \u2192 commet.config.ts")}`);
2501
+ console.log(` ${cmd("push")}${dim("Sync commet.config.ts \u2192 remote")}`);
2502
+ console.log(dim("\n Development"));
2503
+ console.log(` ${cmd("listen <url>")}${dim("Forward webhooks locally")}`);
2504
+ console.log(dim("\n Project"));
2505
+ console.log(` ${cmd("link")}${dim("Link / switch organization")}`);
2506
+ console.log(` ${cmd("orgs")}${dim("List organizations")}`);
2507
+ console.log(dim("\n Setup"));
2508
+ console.log(` ${cmd("create")}${dim("Scaffold a new Commet app")}`);
2509
+ console.log(` ${cmd("api-key")}${dim("Generate API key for CI")}`);
2510
+ console.log(` ${cmd("login")}${dim("Authenticate")}`);
2511
+ console.log(` ${cmd("logout")}${dim("Log out")}`);
2512
+ console.log(
2513
+ dim("\n commet --help for full reference \xB7 COMMET_API_KEY for CI\n")
2514
+ );
2119
2515
  }