commet 1.6.0 → 1.8.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 +309 -214
  2. package/package.json +2 -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_chalk13 = __toESM(require("chalk"));
28
- var import_commander11 = require("commander");
27
+ var import_chalk14 = __toESM(require("chalk"));
28
+ var import_commander12 = require("commander");
29
29
 
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "commet",
33
- version: "1.6.0",
33
+ version: "1.8.0",
34
34
  description: "Commet CLI - Manage your billing platform from the command line",
35
35
  bin: {
36
36
  commet: "./bin/commet"
@@ -58,6 +58,7 @@ var package_default = {
58
58
  license: "MIT",
59
59
  dependencies: {
60
60
  "@inquirer/prompts": "8.0.1",
61
+ ably: "^2.21.0",
61
62
  chalk: "5.6.2",
62
63
  commander: "14.0.2",
63
64
  "jsonc-parser": "3.3.1",
@@ -164,11 +165,12 @@ function clearProjectConfig() {
164
165
  }
165
166
 
166
167
  // src/utils/api.ts
167
- function getBaseURL(environment) {
168
- if (environment === "production") {
169
- return "https://commet.co";
170
- }
171
- return "https://sandbox.commet.co";
168
+ var BASE_URL = "https://beta.commet.co";
169
+ function bypassHeaders() {
170
+ return {
171
+ Cookie: "_vercel_jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJiZXRhLmNvbW1ldC5jbyIsImJ5cGFzcyI6IjFERUlTcngyczA3YXFjVmhBbVhDUnhjTEdvOHNZaGFTIiwiaWF0IjoxNzc4NjkxNzMxLCJzdWIiOiJwcm90ZWN0aW9uLWJ5cGFzcy1hdXRvbWF0aW9uIn0.ONjvMguLM-7wXfTppY1cQveu9IN05x4VSblB39YpW_A",
172
+ Origin: "https://beta.commet.co"
173
+ };
172
174
  }
173
175
  async function apiRequest(endpoint, options = {}) {
174
176
  const auth = loadAuth();
@@ -180,6 +182,7 @@ async function apiRequest(endpoint, options = {}) {
180
182
  ...options,
181
183
  headers: {
182
184
  ...options.headers,
185
+ ...bypassHeaders(),
183
186
  Authorization: `Bearer ${auth.token}`,
184
187
  "Content-Type": "application/json"
185
188
  }
@@ -200,19 +203,36 @@ async function apiRequest(endpoint, options = {}) {
200
203
  }
201
204
 
202
205
  // src/utils/login-flow.ts
203
- var import_chalk = __toESM(require("chalk"));
206
+ var import_chalk2 = __toESM(require("chalk"));
204
207
  var import_open = __toESM(require("open"));
205
208
  var import_ora = __toESM(require("ora"));
209
+
210
+ // src/utils/prompt-theme.ts
211
+ var import_chalk = __toESM(require("chalk"));
212
+ var commetColor = import_chalk.default.hex("#e8a07c");
213
+ var promptTheme = {
214
+ prefix: commetColor("\u276F"),
215
+ style: {
216
+ answer: commetColor,
217
+ message: import_chalk.default.bold,
218
+ error: import_chalk.default.red,
219
+ help: import_chalk.default.dim,
220
+ highlight: commetColor.bold,
221
+ description: import_chalk.default.dim,
222
+ defaultAnswer: import_chalk.default.dim
223
+ }
224
+ };
225
+
226
+ // src/utils/login-flow.ts
206
227
  function sleep(ms) {
207
228
  return new Promise((resolve3) => setTimeout(resolve3, ms));
208
229
  }
209
- async function performLogin(environment) {
210
- const baseURL = getBaseURL(environment);
230
+ async function performLogin() {
211
231
  const spinner = (0, import_ora.default)("Initiating login flow...").start();
212
232
  try {
213
- const deviceResponse = await fetch(`${baseURL}/api/auth/device/code`, {
233
+ const deviceResponse = await fetch(`${BASE_URL}/api/auth/device/code`, {
214
234
  method: "POST",
215
- headers: { "Content-Type": "application/json" },
235
+ headers: { "Content-Type": "application/json", ...bypassHeaders() },
216
236
  body: JSON.stringify({
217
237
  client_id: "commet-cli",
218
238
  scope: "openid profile email"
@@ -230,16 +250,16 @@ async function performLogin(environment) {
230
250
  interval = 5
231
251
  } = deviceData;
232
252
  spinner.stop();
233
- console.log(import_chalk.default.bold("\n\u{1F510} Commet CLI Login\n"));
253
+ console.log(import_chalk2.default.bold("\n\u{1F510} Commet CLI Login\n"));
234
254
  console.log("Visit the following URL in your browser:");
235
- console.log(import_chalk.default.cyan.underline(verification_uri_complete));
255
+ console.log(commetColor.underline(verification_uri_complete));
236
256
  console.log("\nOr enter this code manually:");
237
- console.log(import_chalk.default.bold.green(` ${user_code}`));
238
- console.log(import_chalk.default.dim("\nOpening browser...\n"));
257
+ console.log(import_chalk2.default.bold.green(` ${user_code}`));
258
+ console.log(import_chalk2.default.dim("\nOpening browser...\n"));
239
259
  try {
240
260
  await (0, import_open.default)(verification_uri_complete);
241
261
  } catch {
242
- console.log(import_chalk.default.yellow("\u26A0 Could not open browser automatically."));
262
+ console.log(import_chalk2.default.yellow("\u26A0 Could not open browser automatically."));
243
263
  }
244
264
  const pollSpinner = (0, import_ora.default)("Waiting for authorization...").start();
245
265
  let pollingInterval = interval;
@@ -249,9 +269,9 @@ async function performLogin(environment) {
249
269
  attempts++;
250
270
  await sleep(pollingInterval * 1e3);
251
271
  try {
252
- const tokenResponse = await fetch(`${baseURL}/api/auth/device/token`, {
272
+ const tokenResponse = await fetch(`${BASE_URL}/api/auth/device/token`, {
253
273
  method: "POST",
254
- headers: { "Content-Type": "application/json" },
274
+ headers: { "Content-Type": "application/json", ...bypassHeaders() },
255
275
  body: JSON.stringify({
256
276
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
257
277
  device_code,
@@ -261,8 +281,7 @@ async function performLogin(environment) {
261
281
  const tokenData = await tokenResponse.json();
262
282
  if (tokenData.access_token) {
263
283
  const authConfig = {
264
- token: tokenData.access_token,
265
- environment
284
+ token: tokenData.access_token
266
285
  };
267
286
  if (tokenData.refresh_token) {
268
287
  authConfig.refreshToken = tokenData.refresh_token;
@@ -304,23 +323,6 @@ async function performLogin(environment) {
304
323
  }
305
324
  }
306
325
 
307
- // src/utils/prompt-theme.ts
308
- var import_chalk2 = __toESM(require("chalk"));
309
- var commetColor = import_chalk2.default.hex("#5BC0B0");
310
- var promptTheme = {
311
- prefix: commetColor("\u276F"),
312
- style: {
313
- answer: commetColor,
314
- message: import_chalk2.default.bold,
315
- error: import_chalk2.default.red,
316
- help: import_chalk2.default.dim,
317
- highlight: commetColor.bold,
318
- // Selected option in mint
319
- description: import_chalk2.default.dim,
320
- defaultAnswer: import_chalk2.default.dim
321
- }
322
- };
323
-
324
326
  // src/commands/create.ts
325
327
  var GITHUB_REPO = "commet-labs/commet";
326
328
  var TEMPLATES = [
@@ -462,23 +464,19 @@ function copyEnvExample(dest) {
462
464
  fs2.copyFileSync(examplePath, envPath);
463
465
  }
464
466
  }
465
- function writeApiKeyToEnv(dest, apiKey, environment) {
467
+ function writeApiKeyToEnv(dest, apiKey) {
466
468
  const envPath = path2.join(dest, ".env");
467
469
  if (!fs2.existsSync(envPath)) return;
468
470
  let content = fs2.readFileSync(envPath, "utf-8");
469
471
  content = content.replace(/COMMET_API_KEY=.*/, `COMMET_API_KEY=${apiKey}`);
470
- content = content.replace(
471
- /COMMET_ENVIRONMENT=.*/,
472
- `COMMET_ENVIRONMENT=${environment}`
473
- );
474
472
  fs2.writeFileSync(envPath, content);
475
473
  }
476
- function linkProject(dest, orgId, orgName, environment) {
474
+ function linkProject(dest, orgId, orgName) {
477
475
  const commetDir = path2.join(dest, ".commet");
478
476
  fs2.mkdirSync(commetDir, { recursive: true });
479
477
  fs2.writeFileSync(
480
478
  path2.join(commetDir, "config.json"),
481
- JSON.stringify({ orgId, orgName, environment }, null, 2),
479
+ JSON.stringify({ orgId, orgName, mode: "sandbox" }, null, 2),
482
480
  "utf8"
483
481
  );
484
482
  }
@@ -562,53 +560,24 @@ var createCommand = new import_commander.Command("create").description("Create a
562
560
  console.log(import_chalk3.default.dim("Run `commet login` first"));
563
561
  return;
564
562
  }
565
- let environment;
566
- try {
567
- environment = await (0, import_prompts.select)({
568
- message: "Environment:",
569
- choices: [
570
- {
571
- name: `Sandbox ${import_chalk3.default.dim("(Development)")}`,
572
- value: "sandbox"
573
- },
574
- { name: "Production", value: "production" }
575
- ],
576
- default: "sandbox",
577
- theme: promptTheme
578
- });
579
- } catch {
580
- console.log(import_chalk3.default.yellow("\n\u26A0 Cancelled"));
581
- return;
582
- }
583
- const loggedIn = await performLogin(environment);
563
+ const loggedIn = await performLogin();
584
564
  if (!loggedIn) {
585
565
  return;
586
566
  }
587
567
  }
588
- const auth = loadAuth();
589
- const baseURL = getBaseURL(auth.environment);
590
- if (auth.environment !== "sandbox") {
591
- console.log(
592
- import_chalk3.default.red(
593
- "\u2717 `commet create` is only available in sandbox environment"
594
- )
595
- );
596
- console.log(
597
- import_chalk3.default.dim("Run `commet logout` and login to sandbox to use templates")
598
- );
599
- return;
600
- }
601
- const orgsSpinner = (0, import_ora2.default)("Fetching organizations...").start();
602
- const orgsResult = await apiRequest(`${baseURL}/api/cli/organizations`);
568
+ const orgsSpinner = (0, import_ora2.default)("Fetching sandbox organizations...").start();
569
+ const orgsResult = await apiRequest(`${BASE_URL}/api/cli/organizations`);
603
570
  if (orgsResult.error || !orgsResult.data) {
604
571
  orgsSpinner.fail("Failed to fetch organizations");
605
572
  console.log(import_chalk3.default.dim(orgsResult.error));
606
573
  return;
607
574
  }
608
- const { organizations } = orgsResult.data;
575
+ const organizations = orgsResult.data.organizations.filter(
576
+ (org) => org.mode === "sandbox"
577
+ );
609
578
  orgsSpinner.stop();
610
579
  if (organizations.length === 0) {
611
- console.log(import_chalk3.default.yellow("\u26A0 No organizations found"));
580
+ console.log(import_chalk3.default.yellow("\u26A0 No sandbox organizations found"));
612
581
  console.log(
613
582
  import_chalk3.default.dim("Create an organization at https://commet.co first")
614
583
  );
@@ -643,7 +612,7 @@ var createCommand = new import_commander.Command("create").description("Create a
643
612
  }
644
613
  try {
645
614
  const orgId = await (0, import_prompts.select)({
646
- message: "Organization:",
615
+ message: "Sandbox organization:",
647
616
  choices: organizations.map((org) => ({
648
617
  name: `${org.name} ${import_chalk3.default.dim(`(${org.slug})`)}`,
649
618
  value: org.id
@@ -725,7 +694,7 @@ var createCommand = new import_commander.Command("create").description("Create a
725
694
  return;
726
695
  }
727
696
  const planSpinner = (0, import_ora2.default)("Creating plans...").start();
728
- const templateResult = await apiRequest(`${baseURL}/api/cli/templates`, {
697
+ const templateResult = await apiRequest(`${BASE_URL}/api/cli/templates`, {
729
698
  method: "POST",
730
699
  body: JSON.stringify({
731
700
  templateId: template.templateId,
@@ -741,7 +710,7 @@ var createCommand = new import_commander.Command("create").description("Create a
741
710
  );
742
711
  }
743
712
  const keySpinner = (0, import_ora2.default)("Creating API key...").start();
744
- const keyResult = await apiRequest(`${baseURL}/api/cli/api-keys`, {
713
+ const keyResult = await apiRequest(`${BASE_URL}/api/cli/api-keys`, {
745
714
  method: "POST",
746
715
  body: JSON.stringify({
747
716
  organizationId: selectedOrg.id,
@@ -753,21 +722,21 @@ var createCommand = new import_commander.Command("create").description("Create a
753
722
  console.log(import_chalk3.default.dim(keyResult.error));
754
723
  console.log(import_chalk3.default.dim("You can create one manually in the dashboard"));
755
724
  } else {
756
- writeApiKeyToEnv(dest, keyResult.data.apiKey, auth.environment);
725
+ writeApiKeyToEnv(dest, keyResult.data.apiKey);
757
726
  keySpinner.succeed("API key created and saved to .env");
758
727
  }
759
- linkProject(dest, selectedOrg.id, selectedOrg.name, auth.environment);
728
+ linkProject(dest, selectedOrg.id, selectedOrg.name);
760
729
  if (shouldInstallSkills) {
761
730
  await installSkills(dest);
762
731
  }
763
732
  console.log(import_chalk3.default.green(`
764
733
  \u2713 Created ${projectName}`));
765
734
  console.log(import_chalk3.default.dim(` Template: ${template.name}`));
766
- console.log(import_chalk3.default.dim(` Organization: ${selectedOrg.name}`));
735
+ console.log(import_chalk3.default.dim(` Organization: ${selectedOrg.name} \xB7 sandbox`));
767
736
  console.log();
768
- console.log(` ${import_chalk3.default.cyan("cd")} ${projectName}`);
769
- console.log(` ${import_chalk3.default.cyan("npm install")}`);
770
- console.log(` ${import_chalk3.default.cyan("npm run dev")}`);
737
+ console.log(` ${commetColor("cd")} ${projectName}`);
738
+ console.log(` ${commetColor("npm install")}`);
739
+ console.log(` ${commetColor("npm run dev")}`);
771
740
  console.log();
772
741
  });
773
742
 
@@ -797,7 +766,7 @@ var infoCommand = new import_commander2.Command("info").description("Display inf
797
766
  console.log(import_chalk4.default.green("\nProject: Linked \u2713"));
798
767
  console.log(import_chalk4.default.dim(" Organization:"), projectConfig.orgName);
799
768
  console.log(import_chalk4.default.dim(" Organization ID:"), projectConfig.orgId);
800
- console.log(import_chalk4.default.dim(" Environment:"), projectConfig.environment);
769
+ console.log(import_chalk4.default.dim(" Mode:"), projectConfig.mode);
801
770
  console.log(
802
771
  import_chalk4.default.dim("\nRun `commet pull` to generate type definitions\n")
803
772
  );
@@ -853,7 +822,7 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
853
822
  const config = loadProjectConfig();
854
823
  console.log(import_chalk5.default.yellow("\u26A0 This project is already linked"));
855
824
  console.log(
856
- import_chalk5.default.dim(`Organization: ${config?.orgName} (${config?.environment})`)
825
+ import_chalk5.default.dim(`Organization: ${config?.orgName} \xB7 ${config?.mode}`)
857
826
  );
858
827
  console.log(
859
828
  import_chalk5.default.dim(
@@ -863,15 +832,8 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
863
832
  return;
864
833
  }
865
834
  const spinner = (0, import_ora3.default)("Fetching organizations...").start();
866
- const auth = loadAuth();
867
- if (!auth) {
868
- spinner.fail("Authentication error");
869
- console.log(import_chalk5.default.red("\u2717 Could not load authentication"));
870
- return;
871
- }
872
- const baseURL = getBaseURL(auth.environment);
873
835
  const result = await apiRequest(
874
- `${baseURL}/api/cli/organizations`
836
+ `${BASE_URL}/api/cli/organizations`
875
837
  );
876
838
  if (result.error || !result.data) {
877
839
  spinner.fail("Failed to fetch organizations");
@@ -893,7 +855,7 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
893
855
  orgId = await (0, import_prompts2.select)({
894
856
  message: "Select organization:",
895
857
  choices: organizations.map((org) => ({
896
- name: `${org.name} ${import_chalk5.default.dim(`(${org.slug})`)}`,
858
+ name: `${org.name} ${import_chalk5.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
897
859
  value: org.id
898
860
  })),
899
861
  theme: promptTheme
@@ -910,12 +872,13 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
910
872
  saveProjectConfig({
911
873
  orgId: selectedOrg.id,
912
874
  orgName: selectedOrg.name,
913
- environment: auth.environment
875
+ mode: selectedOrg.mode
914
876
  });
915
877
  const gitignoreResult = updateGitignore(".commet/");
916
878
  console.log(import_chalk5.default.green("\n\u2713 Project linked successfully"));
917
- console.log(import_chalk5.default.dim(`Organization: ${selectedOrg.name}`));
918
- console.log(import_chalk5.default.dim(`Environment: ${auth.environment}`));
879
+ console.log(
880
+ import_chalk5.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
881
+ );
919
882
  if (gitignoreResult.success) {
920
883
  console.log(import_chalk5.default.green("\u2713 Updated .gitignore"));
921
884
  } else {
@@ -959,9 +922,8 @@ var listCommand = new import_commander4.Command("list").description("List featur
959
922
  return;
960
923
  }
961
924
  const spinner = (0, import_ora4.default)(`Fetching ${type}...`).start();
962
- const baseURL = getBaseURL(projectConfig.environment);
963
925
  const result = await apiRequest(
964
- `${baseURL}/api/cli/types?orgId=${projectConfig.orgId}`
926
+ `${BASE_URL}/api/cli/types?orgId=${projectConfig.orgId}`
965
927
  );
966
928
  if (result.error || !result.data) {
967
929
  spinner.fail(`Failed to fetch ${type}`);
@@ -1032,46 +994,185 @@ var listCommand = new import_commander4.Command("list").description("List featur
1032
994
  }
1033
995
  });
1034
996
 
1035
- // src/commands/login.ts
1036
- var import_prompts3 = require("@inquirer/prompts");
997
+ // src/commands/listen.ts
998
+ var import_ably = __toESM(require("ably"));
1037
999
  var import_chalk7 = __toESM(require("chalk"));
1038
1000
  var import_commander5 = require("commander");
1039
- var loginCommand = new import_commander5.Command("login").description("Authenticate with Commet").action(async () => {
1040
- if (authExists()) {
1041
- console.log(import_chalk7.default.yellow("\u26A0 You are already logged in."));
1001
+ function printEventLine(line) {
1002
+ const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
1003
+ const eventName = import_chalk7.default.yellow(line.event.padEnd(28));
1004
+ const timing = import_chalk7.default.dim(`(${line.ms}ms)`);
1005
+ if ("error" in line) {
1042
1006
  console.log(
1043
- import_chalk7.default.dim(
1044
- "Run `commet logout` first if you want to login with a different account."
1045
- )
1007
+ ` ${import_chalk7.default.dim(time)} ${eventName} \u2192 ${import_chalk7.default.red("Error")} ${timing}`
1046
1008
  );
1009
+ console.log(` ${" ".repeat(12)}${import_chalk7.default.red(line.error)}`);
1047
1010
  return;
1048
1011
  }
1049
- let environment;
1050
- try {
1051
- environment = await (0, import_prompts3.select)({
1052
- message: "Select environment to login:",
1053
- choices: [
1054
- {
1055
- name: `Sandbox ${import_chalk7.default.dim("(Development)")}`,
1056
- value: "sandbox"
1057
- },
1058
- {
1059
- name: "Production",
1060
- value: "production"
1012
+ const status = line.statusCode < 400 ? import_chalk7.default.green(`${line.statusCode} OK`) : import_chalk7.default.red(`${line.statusCode} Error`);
1013
+ console.log(` ${import_chalk7.default.dim(time)} ${eventName} \u2192 ${status} ${timing}`);
1014
+ }
1015
+ function isListenMessage(data) {
1016
+ if (typeof data !== "object" || data === null) return false;
1017
+ const obj = data;
1018
+ return typeof obj.payload === "object" && obj.payload !== null && typeof obj.headers === "object" && obj.headers !== null;
1019
+ }
1020
+ var listenCommand = new import_commander5.Command("listen").description("Forward webhook events to your local server").requiredOption("--port <number>", "Local port to forward to").option("--events <types>", "Comma-separated event types to filter").option("--path <path>", "Path within localhost", "/").action(async (options) => {
1021
+ const auth = loadAuth();
1022
+ if (!auth) {
1023
+ console.log(import_chalk7.default.red("Not authenticated. Run: commet login"));
1024
+ process.exit(1);
1025
+ }
1026
+ const projectConfig = loadProjectConfig();
1027
+ if (!projectConfig) {
1028
+ console.log(import_chalk7.default.red("No project linked. Run: commet link"));
1029
+ process.exit(1);
1030
+ }
1031
+ const port = Number(options.port);
1032
+ if (Number.isNaN(port) || port <= 0 || port > 65535) {
1033
+ console.log(import_chalk7.default.red("Invalid port number"));
1034
+ process.exit(1);
1035
+ }
1036
+ async function fetchTokenRequest() {
1037
+ const result = await apiRequest(
1038
+ `${BASE_URL}/api/cli/listen/start`,
1039
+ {
1040
+ method: "POST",
1041
+ body: JSON.stringify({ organizationId: projectConfig.orgId })
1042
+ }
1043
+ );
1044
+ if (result.error || !result.data) {
1045
+ console.log(import_chalk7.default.red("Failed to start listen session"));
1046
+ if (result.error) {
1047
+ console.log(import_chalk7.default.dim(result.error));
1048
+ }
1049
+ process.exit(1);
1050
+ }
1051
+ return result.data;
1052
+ }
1053
+ const initialSession = await fetchTokenRequest();
1054
+ const { sessionId, channelName, signingSecret, tokenRequest } = initialSession;
1055
+ async function refreshToken() {
1056
+ const result = await apiRequest(
1057
+ `${BASE_URL}/api/cli/listen/refresh`,
1058
+ {
1059
+ method: "POST",
1060
+ body: JSON.stringify({
1061
+ sessionId,
1062
+ organizationId: projectConfig.orgId
1063
+ })
1064
+ }
1065
+ );
1066
+ if (result.error || !result.data) {
1067
+ throw new Error(result.error ?? "Failed to refresh token");
1068
+ }
1069
+ return result.data.tokenRequest;
1070
+ }
1071
+ let isFirstToken = true;
1072
+ const ably = new import_ably.default.Realtime({
1073
+ authCallback: async (_tokenParams, callback) => {
1074
+ try {
1075
+ if (isFirstToken) {
1076
+ isFirstToken = false;
1077
+ callback(null, tokenRequest);
1078
+ return;
1061
1079
  }
1062
- ],
1063
- default: "sandbox",
1064
- theme: promptTheme
1080
+ const refreshed = await refreshToken();
1081
+ callback(null, refreshed);
1082
+ } catch (error) {
1083
+ callback(
1084
+ error instanceof Error ? error.message : "Token refresh failed",
1085
+ null
1086
+ );
1087
+ }
1088
+ }
1089
+ });
1090
+ let wasConnected = false;
1091
+ ably.connection.on("connected", () => {
1092
+ if (wasConnected) {
1093
+ console.log(import_chalk7.default.green(" \u2713 Reconnected"));
1094
+ }
1095
+ wasConnected = true;
1096
+ });
1097
+ ably.connection.on("disconnected", () => {
1098
+ console.log(import_chalk7.default.yellow("\n \u26A0 Disconnected. Reconnecting..."));
1099
+ });
1100
+ ably.connection.on("failed", () => {
1101
+ console.log(
1102
+ import_chalk7.default.red("\n \u2717 Connection failed. Check your authentication.")
1103
+ );
1104
+ process.exit(1);
1105
+ });
1106
+ const channel = ably.channels.get(channelName);
1107
+ const targetUrl = `http://localhost:${port}${options.path}`;
1108
+ console.log("");
1109
+ console.log(
1110
+ import_chalk7.default.green(` \u2713 Authenticated (org: ${projectConfig.orgName})`)
1111
+ );
1112
+ console.log(import_chalk7.default.green(" \u2713 Connected to Commet webhook stream"));
1113
+ console.log(import_chalk7.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
1114
+ console.log(import_chalk7.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
1115
+ console.log("");
1116
+ console.log(" Ready! Listening for webhook events...");
1117
+ console.log("");
1118
+ const eventFilter = options.events?.split(",").map((e) => e.trim()).filter(Boolean);
1119
+ channel.subscribe((message) => {
1120
+ if (!message.name || !isListenMessage(message.data)) return;
1121
+ const event = message.name;
1122
+ const { payload, headers } = message.data;
1123
+ if (eventFilter && !eventFilter.includes(event)) return;
1124
+ const start = performance.now();
1125
+ fetch(targetUrl, {
1126
+ method: "POST",
1127
+ headers,
1128
+ body: JSON.stringify(payload)
1129
+ }).then((response) => {
1130
+ const ms = Math.round(performance.now() - start);
1131
+ printEventLine({ event, statusCode: response.status, ms });
1132
+ }).catch((error) => {
1133
+ const ms = Math.round(performance.now() - start);
1134
+ printEventLine({
1135
+ event,
1136
+ error: error instanceof Error ? error.message : "Unknown error",
1137
+ ms
1138
+ });
1065
1139
  });
1066
- } catch {
1067
- console.log(import_chalk7.default.yellow("\n\u26A0 Login cancelled"));
1140
+ });
1141
+ let isShuttingDown = false;
1142
+ process.on("SIGINT", async () => {
1143
+ if (isShuttingDown) return;
1144
+ isShuttingDown = true;
1145
+ console.log(import_chalk7.default.dim("\n Disconnecting..."));
1146
+ ably.close();
1147
+ await apiRequest(`${BASE_URL}/api/cli/listen/stop`, {
1148
+ method: "POST",
1149
+ body: JSON.stringify({
1150
+ sessionId,
1151
+ organizationId: projectConfig.orgId
1152
+ })
1153
+ });
1154
+ process.exit(0);
1155
+ });
1156
+ });
1157
+
1158
+ // src/commands/login.ts
1159
+ var import_chalk8 = __toESM(require("chalk"));
1160
+ var import_commander6 = require("commander");
1161
+ var loginCommand = new import_commander6.Command("login").description("Authenticate with Commet").action(async () => {
1162
+ if (authExists()) {
1163
+ console.log(import_chalk8.default.yellow("\u26A0 You are already logged in."));
1164
+ console.log(
1165
+ import_chalk8.default.dim(
1166
+ "Run `commet logout` first if you want to login with a different account."
1167
+ )
1168
+ );
1068
1169
  return;
1069
1170
  }
1070
- const success = await performLogin(environment);
1171
+ const success = await performLogin();
1071
1172
  if (success) {
1072
- console.log(import_chalk7.default.green("\n\u2713 Authentication complete"));
1173
+ console.log(import_chalk8.default.green("\n\u2713 Authentication complete"));
1073
1174
  console.log(
1074
- import_chalk7.default.dim(
1175
+ import_chalk8.default.dim(
1075
1176
  "\nNext steps:\n 1. Run `commet link` to connect a project\n 2. Run `commet pull` to generate types\n"
1076
1177
  )
1077
1178
  );
@@ -1079,22 +1180,22 @@ var loginCommand = new import_commander5.Command("login").description("Authentic
1079
1180
  });
1080
1181
 
1081
1182
  // src/commands/logout.ts
1082
- var import_chalk8 = __toESM(require("chalk"));
1083
- var import_commander6 = require("commander");
1084
- var logoutCommand = new import_commander6.Command("logout").description("Log out of Commet").action(async () => {
1183
+ var import_chalk9 = __toESM(require("chalk"));
1184
+ var import_commander7 = require("commander");
1185
+ var logoutCommand = new import_commander7.Command("logout").description("Log out of Commet").action(async () => {
1085
1186
  if (!authExists()) {
1086
- console.log(import_chalk8.default.yellow("\u26A0 You are not logged in."));
1187
+ console.log(import_chalk9.default.yellow("\u26A0 You are not logged in."));
1087
1188
  return;
1088
1189
  }
1089
1190
  clearAuth();
1090
- console.log(import_chalk8.default.green("\u2713 Successfully logged out"));
1191
+ console.log(import_chalk9.default.green("\u2713 Successfully logged out"));
1091
1192
  });
1092
1193
 
1093
1194
  // src/commands/pull.ts
1094
1195
  var fs6 = __toESM(require("fs"));
1095
1196
  var path6 = __toESM(require("path"));
1096
- var import_chalk9 = __toESM(require("chalk"));
1097
- var import_commander7 = require("commander");
1197
+ var import_chalk10 = __toESM(require("chalk"));
1198
+ var import_commander8 = require("commander");
1098
1199
  var import_ora5 = __toESM(require("ora"));
1099
1200
 
1100
1201
  // src/utils/environment-validator.ts
@@ -1199,33 +1300,32 @@ function updateTsConfig(entry) {
1199
1300
  }
1200
1301
 
1201
1302
  // src/commands/pull.ts
1202
- var pullCommand = new import_commander7.Command("pull").description("Pull type definitions from Commet").action(async () => {
1303
+ var pullCommand = new import_commander8.Command("pull").description("Pull type definitions from Commet").action(async () => {
1203
1304
  if (!authExists()) {
1204
- console.log(import_chalk9.default.red("\u2717 Not authenticated"));
1205
- console.log(import_chalk9.default.dim("Run `commet login` first"));
1305
+ console.log(import_chalk10.default.red("\u2717 Not authenticated"));
1306
+ console.log(import_chalk10.default.dim("Run `commet login` first"));
1206
1307
  return;
1207
1308
  }
1208
1309
  const hasTsConfig = validateTypeScriptProject();
1209
1310
  if (!projectConfigExists()) {
1210
- console.log(import_chalk9.default.red("\u2717 Project not linked"));
1311
+ console.log(import_chalk10.default.red("\u2717 Project not linked"));
1211
1312
  console.log(
1212
- import_chalk9.default.dim("Run `commet link` first to connect to an organization")
1313
+ import_chalk10.default.dim("Run `commet link` first to connect to an organization")
1213
1314
  );
1214
1315
  return;
1215
1316
  }
1216
1317
  const projectConfig = loadProjectConfig();
1217
1318
  if (!projectConfig) {
1218
- console.log(import_chalk9.default.red("\u2717 Invalid project configuration"));
1319
+ console.log(import_chalk10.default.red("\u2717 Invalid project configuration"));
1219
1320
  return;
1220
1321
  }
1221
1322
  const spinner = (0, import_ora5.default)("Fetching type definitions...").start();
1222
- const baseURL = getBaseURL(projectConfig.environment);
1223
1323
  const result = await apiRequest(
1224
- `${baseURL}/api/cli/types?orgId=${projectConfig.orgId}`
1324
+ `${BASE_URL}/api/cli/types?orgId=${projectConfig.orgId}`
1225
1325
  );
1226
1326
  if (result.error || !result.data) {
1227
1327
  spinner.fail("Failed to fetch types");
1228
- console.error(import_chalk9.default.red("Error:"), result.error);
1328
+ console.error(import_chalk10.default.red("Error:"), result.error);
1229
1329
  return;
1230
1330
  }
1231
1331
  const { seatTypes, features, plans } = result.data;
@@ -1238,52 +1338,52 @@ var pullCommand = new import_commander7.Command("pull").description("Pull type d
1238
1338
  if (hasTsConfig) {
1239
1339
  const tsconfigResult = updateTsConfig(".commet/types.d.ts");
1240
1340
  if (tsconfigResult.success) {
1241
- console.log(import_chalk9.default.green("\u2713 Updated tsconfig.json"));
1341
+ console.log(import_chalk10.default.green("\u2713 Updated tsconfig.json"));
1242
1342
  } else {
1243
- console.log(import_chalk9.default.yellow("\u26A0 Could not update tsconfig.json"));
1343
+ console.log(import_chalk10.default.yellow("\u26A0 Could not update tsconfig.json"));
1244
1344
  console.log(
1245
- import_chalk9.default.dim(
1345
+ import_chalk10.default.dim(
1246
1346
  'Add ".commet/types.d.ts" to your tsconfig.json include array'
1247
1347
  )
1248
1348
  );
1249
1349
  }
1250
1350
  } else {
1251
- console.log(import_chalk9.default.yellow("\u26A0 No tsconfig.json found"));
1351
+ console.log(import_chalk10.default.yellow("\u26A0 No tsconfig.json found"));
1252
1352
  console.log(
1253
- import_chalk9.default.dim(
1353
+ import_chalk10.default.dim(
1254
1354
  'Add ".commet/types.d.ts" to your tsconfig.json to enable types'
1255
1355
  )
1256
1356
  );
1257
1357
  }
1258
1358
  const gitignoreResult = updateGitignore(".commet/");
1259
1359
  if (gitignoreResult.success) {
1260
- console.log(import_chalk9.default.green("\u2713 Updated .gitignore"));
1360
+ console.log(import_chalk10.default.green("\u2713 Updated .gitignore"));
1261
1361
  } else {
1262
- console.log(import_chalk9.default.yellow("\u26A0 No .gitignore found"));
1263
- console.log(import_chalk9.default.dim("Add .commet/ to your .gitignore file"));
1362
+ console.log(import_chalk10.default.yellow("\u26A0 No .gitignore found"));
1363
+ console.log(import_chalk10.default.dim("Add .commet/ to your .gitignore file"));
1264
1364
  }
1265
- console.log(import_chalk9.default.green("\nSuccess!"));
1266
- console.log(import_chalk9.default.dim("\nGenerated types:"));
1365
+ console.log(import_chalk10.default.green("\nSuccess!"));
1366
+ console.log(import_chalk10.default.dim("\nGenerated types:"));
1267
1367
  console.log(
1268
- import_chalk9.default.dim(
1368
+ import_chalk10.default.dim(
1269
1369
  ` Features: ${features.length > 0 ? features.map((f) => f.code).join(", ") : "none"}`
1270
1370
  )
1271
1371
  );
1272
1372
  console.log(
1273
- import_chalk9.default.dim(
1373
+ import_chalk10.default.dim(
1274
1374
  ` Seat types: ${seatTypes.length > 0 ? seatTypes.map((s) => s.code).join(", ") : "none"}`
1275
1375
  )
1276
1376
  );
1277
1377
  console.log(
1278
- import_chalk9.default.dim(
1378
+ import_chalk10.default.dim(
1279
1379
  ` Plans: ${plans.length > 0 ? plans.map((p) => p.code).join(", ") : "none"}`
1280
1380
  )
1281
1381
  );
1282
- console.log(import_chalk9.default.dim(`
1382
+ console.log(import_chalk10.default.dim(`
1283
1383
  Output: ${outputPath}`));
1284
1384
  if (seatTypes.length === 0 && plans.length === 0 && features.length === 0) {
1285
1385
  console.log(
1286
- import_chalk9.default.yellow(
1386
+ import_chalk10.default.yellow(
1287
1387
  "\n\u26A0 No types found. Create features, seat types, and plans in your Commet dashboard."
1288
1388
  )
1289
1389
  );
@@ -1291,118 +1391,112 @@ Output: ${outputPath}`));
1291
1391
  });
1292
1392
 
1293
1393
  // src/commands/switch.ts
1294
- var import_prompts4 = require("@inquirer/prompts");
1295
- var import_chalk10 = __toESM(require("chalk"));
1296
- var import_commander8 = require("commander");
1394
+ var import_prompts3 = require("@inquirer/prompts");
1395
+ var import_chalk11 = __toESM(require("chalk"));
1396
+ var import_commander9 = require("commander");
1297
1397
  var import_ora6 = __toESM(require("ora"));
1298
- var switchCommand = new import_commander8.Command("switch").description("Switch to a different organization").action(async () => {
1398
+ var switchCommand = new import_commander9.Command("switch").description("Switch to a different organization").action(async () => {
1299
1399
  if (!authExists()) {
1300
- console.log(import_chalk10.default.red("\u2717 Not authenticated"));
1301
- console.log(import_chalk10.default.dim("Run `commet login` first"));
1400
+ console.log(import_chalk11.default.red("\u2717 Not authenticated"));
1401
+ console.log(import_chalk11.default.dim("Run `commet login` first"));
1302
1402
  return;
1303
1403
  }
1304
1404
  if (!projectConfigExists()) {
1305
- console.log(import_chalk10.default.yellow("\u26A0 Project not linked"));
1405
+ console.log(import_chalk11.default.yellow("\u26A0 Project not linked"));
1306
1406
  console.log(
1307
- import_chalk10.default.dim("Run `commet link` first to connect to an organization")
1407
+ import_chalk11.default.dim("Run `commet link` first to connect to an organization")
1308
1408
  );
1309
1409
  return;
1310
1410
  }
1311
1411
  const spinner = (0, import_ora6.default)("Fetching organizations...").start();
1312
- const auth = loadAuth();
1313
- if (!auth) {
1314
- spinner.fail("Authentication error");
1315
- console.log(import_chalk10.default.red("\u2717 Could not load authentication"));
1316
- return;
1317
- }
1318
- const baseURL = getBaseURL(auth.environment);
1319
1412
  const result = await apiRequest(
1320
- `${baseURL}/api/cli/organizations`
1413
+ `${BASE_URL}/api/cli/organizations`
1321
1414
  );
1322
1415
  if (result.error || !result.data) {
1323
1416
  spinner.fail("Failed to fetch organizations");
1324
- console.error(import_chalk10.default.red("Error:"), result.error);
1417
+ console.error(import_chalk11.default.red("Error:"), result.error);
1325
1418
  return;
1326
1419
  }
1327
1420
  const { organizations } = result.data;
1328
1421
  if (organizations.length === 0) {
1329
1422
  spinner.stop();
1330
- console.log(import_chalk10.default.yellow("\u26A0 No organizations found"));
1423
+ console.log(import_chalk11.default.yellow("\u26A0 No organizations found"));
1331
1424
  return;
1332
1425
  }
1333
1426
  spinner.stop();
1334
1427
  let orgId;
1335
1428
  try {
1336
- orgId = await (0, import_prompts4.select)({
1429
+ orgId = await (0, import_prompts3.select)({
1337
1430
  message: "Select organization:",
1338
1431
  choices: organizations.map((org) => ({
1339
- name: `${org.name} ${import_chalk10.default.dim(`(${org.slug})`)}`,
1432
+ name: `${org.name} ${import_chalk11.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
1340
1433
  value: org.id
1341
1434
  })),
1342
1435
  theme: promptTheme
1343
1436
  });
1344
1437
  } catch (_error) {
1345
- console.log(import_chalk10.default.yellow("\n\u26A0 Switch cancelled"));
1438
+ console.log(import_chalk11.default.yellow("\n\u26A0 Switch cancelled"));
1346
1439
  return;
1347
1440
  }
1348
1441
  const selectedOrg = organizations.find((org) => org.id === orgId);
1349
1442
  if (!selectedOrg) {
1350
- console.log(import_chalk10.default.red("\u2717 Organization not found"));
1443
+ console.log(import_chalk11.default.red("\u2717 Organization not found"));
1351
1444
  return;
1352
1445
  }
1353
1446
  saveProjectConfig({
1354
1447
  orgId: selectedOrg.id,
1355
1448
  orgName: selectedOrg.name,
1356
- environment: auth.environment
1449
+ mode: selectedOrg.mode
1357
1450
  });
1358
- console.log(import_chalk10.default.green("\n\u2713 Switched organization successfully!"));
1359
- console.log(import_chalk10.default.dim("\nNew configuration:"));
1360
- console.log(import_chalk10.default.dim(` Organization: ${selectedOrg.name}`));
1361
- console.log(import_chalk10.default.dim(` Environment: ${auth.environment}`));
1451
+ console.log(import_chalk11.default.green("\n\u2713 Switched organization successfully!"));
1362
1452
  console.log(
1363
- import_chalk10.default.dim(
1453
+ import_chalk11.default.dim(`
1454
+ Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
1455
+ );
1456
+ console.log(
1457
+ import_chalk11.default.dim(
1364
1458
  "\nRun `commet pull` to update TypeScript types for this organization"
1365
1459
  )
1366
1460
  );
1367
1461
  });
1368
1462
 
1369
1463
  // src/commands/unlink.ts
1370
- var import_chalk11 = __toESM(require("chalk"));
1371
- var import_commander9 = require("commander");
1372
- var unlinkCommand = new import_commander9.Command("unlink").description("Unlink this project from Commet").action(async () => {
1464
+ var import_chalk12 = __toESM(require("chalk"));
1465
+ var import_commander10 = require("commander");
1466
+ var unlinkCommand = new import_commander10.Command("unlink").description("Unlink this project from Commet").action(async () => {
1373
1467
  if (!projectConfigExists()) {
1374
1468
  console.log(
1375
- import_chalk11.default.yellow("\u26A0 This project is not linked to any organization")
1469
+ import_chalk12.default.yellow("\u26A0 This project is not linked to any organization")
1376
1470
  );
1377
1471
  return;
1378
1472
  }
1379
1473
  clearProjectConfig();
1380
- console.log(import_chalk11.default.green("\u2713 Project unlinked successfully"));
1381
- console.log(import_chalk11.default.dim("\u2713 Removed .commet/ directory"));
1474
+ console.log(import_chalk12.default.green("\u2713 Project unlinked successfully"));
1475
+ console.log(import_chalk12.default.dim("\u2713 Removed .commet/ directory"));
1382
1476
  console.log(
1383
- import_chalk11.default.dim("\nRun `commet link` to connect to a different organization")
1477
+ import_chalk12.default.dim("\nRun `commet link` to connect to a different organization")
1384
1478
  );
1385
1479
  });
1386
1480
 
1387
1481
  // src/commands/whoami.ts
1388
- var import_chalk12 = __toESM(require("chalk"));
1389
- var import_commander10 = require("commander");
1390
- var whoamiCommand = new import_commander10.Command("whoami").description("Display current authentication and project status").action(async () => {
1482
+ var import_chalk13 = __toESM(require("chalk"));
1483
+ var import_commander11 = require("commander");
1484
+ var whoamiCommand = new import_commander11.Command("whoami").description("Display current authentication and project status").action(async () => {
1391
1485
  if (!authExists()) {
1392
- console.log(import_chalk12.default.yellow("\u26A0 Not logged in"));
1393
- console.log(import_chalk12.default.dim("Run `commet login` to authenticate"));
1486
+ console.log(import_chalk13.default.yellow("\u26A0 Not logged in"));
1487
+ console.log(import_chalk13.default.dim("Run `commet login` to authenticate"));
1394
1488
  return;
1395
1489
  }
1396
- console.log(import_chalk12.default.green("\u2713 Logged in"));
1490
+ console.log(import_chalk13.default.green("\u2713 Logged in"));
1397
1491
  const projectConfig = loadProjectConfig();
1398
1492
  if (projectConfig) {
1399
- console.log(import_chalk12.default.bold("\nProject:"));
1400
- console.log(import_chalk12.default.dim("Organization:"), projectConfig.orgName);
1401
- console.log(import_chalk12.default.dim("Environment:"), projectConfig.environment);
1493
+ console.log(import_chalk13.default.bold("\nProject:"));
1494
+ console.log(import_chalk13.default.dim("Organization:"), projectConfig.orgName);
1495
+ console.log(import_chalk13.default.dim("Mode:"), projectConfig.mode);
1402
1496
  } else {
1403
- console.log(import_chalk12.default.yellow("\n\u26A0 No project linked"));
1497
+ console.log(import_chalk13.default.yellow("\n\u26A0 No project linked"));
1404
1498
  console.log(
1405
- import_chalk12.default.dim(
1499
+ import_chalk13.default.dim(
1406
1500
  "Run `commet link` to connect this directory to an organization"
1407
1501
  )
1408
1502
  );
@@ -1410,7 +1504,7 @@ var whoamiCommand = new import_commander10.Command("whoami").description("Displa
1410
1504
  });
1411
1505
 
1412
1506
  // src/index.ts
1413
- var program = new import_commander11.Command();
1507
+ var program = new import_commander12.Command();
1414
1508
  program.name("commet").description(
1415
1509
  "Commet CLI - Manage your billing platform from the command line"
1416
1510
  ).version(package_default.version);
@@ -1424,6 +1518,7 @@ program.addCommand(switchCommand);
1424
1518
  program.addCommand(infoCommand);
1425
1519
  program.addCommand(pullCommand);
1426
1520
  program.addCommand(listCommand);
1521
+ program.addCommand(listenCommand);
1427
1522
  program.exitOverride();
1428
1523
  try {
1429
1524
  program.parse(process.argv);
@@ -1433,7 +1528,7 @@ try {
1433
1528
  if (code === "commander.version" || code === "commander.help" || code === "commander.helpDisplayed") {
1434
1529
  process.exit(0);
1435
1530
  }
1436
- console.error(import_chalk13.default.red("Error:"), error.message);
1531
+ console.error(import_chalk14.default.red("Error:"), error.message);
1437
1532
  }
1438
1533
  process.exit(1);
1439
1534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commet",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Commet CLI - Manage your billing platform from the command line",
5
5
  "bin": {
6
6
  "commet": "./bin/commet"
@@ -20,6 +20,7 @@
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
22
  "@inquirer/prompts": "8.0.1",
23
+ "ably": "^2.21.0",
23
24
  "chalk": "5.6.2",
24
25
  "commander": "14.0.2",
25
26
  "jsonc-parser": "3.3.1",