commet 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,11 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
3
+ var __create = Object.create;
2
4
  var __defProp = Object.defineProperty;
3
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
5
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
9
  var __copyProps = (to, from, except, desc) => {
11
10
  if (from && typeof from === "object" || typeof from === "function") {
12
11
  for (let key of __getOwnPropNames(from))
@@ -15,431 +14,695 @@ var __copyProps = (to, from, except, desc) => {
15
14
  }
16
15
  return to;
17
16
  };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
19
25
 
20
26
  // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- Commet: () => Commet,
24
- CommetAPIError: () => CommetAPIError,
25
- CommetError: () => CommetError,
26
- CommetValidationError: () => CommetValidationError,
27
- default: () => index_default,
28
- isProduction: () => isProduction,
29
- isSandbox: () => isSandbox
30
- });
31
- module.exports = __toCommonJS(index_exports);
27
+ var import_commander10 = require("commander");
28
+ var import_chalk10 = __toESM(require("chalk"));
32
29
 
33
- // src/resources/customers.ts
34
- var CustomersResource = class {
35
- constructor(httpClient) {
36
- this.httpClient = httpClient;
37
- }
38
- async create(params, options) {
39
- return this.httpClient.post("/customers", params, options);
40
- }
41
- async retrieve(customerId, options) {
42
- const params = options?.expand ? { expand: options.expand.join(",") } : void 0;
43
- return this.httpClient.get(`/customers/${customerId}`, params);
44
- }
45
- async update(customerId, params, options) {
46
- return this.httpClient.put(`/customers/${customerId}`, params, options);
47
- }
48
- async list(params) {
49
- return this.httpClient.get("/customers", params);
50
- }
51
- /**
52
- * Deactivate a customer (soft delete)
53
- */
54
- async deactivate(customerId, options) {
55
- return this.httpClient.put(
56
- `/customers/${customerId}`,
57
- { isActive: false },
58
- options
59
- );
60
- }
61
- };
62
-
63
- // src/resources/seats.ts
64
- var SeatsResource = class {
65
- constructor(httpClient) {
66
- this.httpClient = httpClient;
67
- }
68
- async add(customerId, seatType, count, options) {
69
- return this.httpClient.post(
70
- `/customers/${customerId}/seats/${seatType}/add`,
71
- { count },
72
- options
73
- );
74
- }
75
- async remove(customerId, seatType, count, options) {
76
- return this.httpClient.post(
77
- `/customers/${customerId}/seats/${seatType}/remove`,
78
- { count },
79
- options
80
- );
81
- }
82
- async set(customerId, seatType, count, options) {
83
- return this.httpClient.post(
84
- `/customers/${customerId}/seats/${seatType}/set`,
85
- { count },
86
- options
87
- );
88
- }
89
- async bulkUpdate(customerId, seats, options) {
90
- return this.httpClient.post(
91
- `/customers/${customerId}/seats/bulk-update`,
92
- { seats },
93
- options
94
- );
95
- }
96
- async getBalance(customerId, seatType) {
97
- return this.httpClient.get(
98
- `/customers/${customerId}/seats/${seatType}/balance`
99
- );
100
- }
101
- async getAllBalances(customerId) {
102
- return this.httpClient.get(`/customers/${customerId}/seats/balances`);
103
- }
104
- async getHistory(customerId, seatType, params) {
105
- const queryParams = {
106
- ...params,
107
- customerId,
108
- seatType
109
- };
110
- return this.httpClient.get(
111
- `/customers/${customerId}/seats/history`,
112
- queryParams
113
- );
114
- }
115
- async listEvents(params) {
116
- return this.httpClient.get("/seats/events", params);
117
- }
118
- };
30
+ // src/commands/login.ts
31
+ var import_commander = require("commander");
32
+ var import_chalk = __toESM(require("chalk"));
33
+ var import_ora = __toESM(require("ora"));
34
+ var import_open = __toESM(require("open"));
119
35
 
120
- // src/resources/usage.ts
121
- var UsageEventsResource = class {
122
- constructor(httpClient) {
123
- this.httpClient = httpClient;
124
- }
125
- async create(params, options) {
126
- const eventData = {
127
- ...params,
128
- ts: params.timestamp || (/* @__PURE__ */ new Date()).toISOString()
129
- };
130
- return this.httpClient.post("/usage/events", eventData, options);
131
- }
132
- async createBatch(params, options) {
133
- return this.httpClient.post("/usage/events/batch", params, options);
134
- }
135
- async retrieve(eventId) {
136
- return this.httpClient.get(`/usage/events/${eventId}`);
137
- }
138
- async list(params) {
139
- return this.httpClient.get("/usage/events", params);
140
- }
141
- async delete(eventId, options) {
142
- return this.httpClient.delete(`/usage/events/${eventId}`, options);
143
- }
144
- };
145
- var UsageMetricsResource = class {
146
- constructor(httpClient) {
147
- this.httpClient = httpClient;
36
+ // src/utils/config.ts
37
+ var fs = __toESM(require("fs"));
38
+ var os = __toESM(require("os"));
39
+ var path = __toESM(require("path"));
40
+ function getAuthPath() {
41
+ return path.join(os.homedir(), ".commet", "auth.json");
42
+ }
43
+ function getProjectConfigPath() {
44
+ return path.join(process.cwd(), ".commet");
45
+ }
46
+ function authExists() {
47
+ return fs.existsSync(getAuthPath());
48
+ }
49
+ function loadAuth() {
50
+ try {
51
+ const authPath = getAuthPath();
52
+ if (!fs.existsSync(authPath)) {
53
+ return null;
54
+ }
55
+ const data = fs.readFileSync(authPath, "utf8");
56
+ return JSON.parse(data);
57
+ } catch {
58
+ return null;
148
59
  }
149
- async list() {
150
- return this.httpClient.get("/usage/metrics");
60
+ }
61
+ function saveAuth(data) {
62
+ const authPath = getAuthPath();
63
+ fs.mkdirSync(path.dirname(authPath), { recursive: true });
64
+ fs.writeFileSync(authPath, JSON.stringify(data, null, 2), "utf8");
65
+ }
66
+ function clearAuth() {
67
+ const authPath = getAuthPath();
68
+ if (fs.existsSync(authPath)) {
69
+ fs.unlinkSync(authPath);
151
70
  }
152
- async retrieve(metricId) {
153
- return this.httpClient.get(`/usage/metrics/${metricId}`);
71
+ }
72
+ function projectConfigExists() {
73
+ return fs.existsSync(getProjectConfigPath());
74
+ }
75
+ function loadProjectConfig() {
76
+ try {
77
+ const configPath = getProjectConfigPath();
78
+ if (!fs.existsSync(configPath)) {
79
+ return null;
80
+ }
81
+ const data = fs.readFileSync(configPath, "utf8");
82
+ return JSON.parse(data);
83
+ } catch {
84
+ return null;
154
85
  }
155
- };
156
- var UsageResource = class {
157
- constructor(httpClient) {
158
- this.events = new UsageEventsResource(httpClient);
159
- this.metrics = new UsageMetricsResource(httpClient);
86
+ }
87
+ function saveProjectConfig(data) {
88
+ const configPath = getProjectConfigPath();
89
+ fs.writeFileSync(configPath, JSON.stringify(data, null, 2), "utf8");
90
+ }
91
+ function clearProjectConfig() {
92
+ const configPath = getProjectConfigPath();
93
+ if (fs.existsSync(configPath)) {
94
+ fs.unlinkSync(configPath);
160
95
  }
161
- };
96
+ }
162
97
 
163
- // src/types/common.ts
164
- var CommetError = class extends Error {
165
- constructor(message, code, statusCode, details) {
166
- super(message);
167
- this.code = code;
168
- this.statusCode = statusCode;
169
- this.details = details;
170
- this.name = "CommetError";
171
- }
172
- };
173
- var CommetAPIError = class extends CommetError {
174
- constructor(message, statusCode, code, details) {
175
- super(message, code, statusCode, details);
176
- this.statusCode = statusCode;
177
- this.code = code;
178
- this.details = details;
179
- this.name = "CommetAPIError";
98
+ // src/utils/api.ts
99
+ function getBaseURL(environment) {
100
+ if (environment === "production") {
101
+ return "https://billing.commet.co";
180
102
  }
181
- };
182
- var CommetValidationError = class extends CommetError {
183
- constructor(message, validationErrors) {
184
- super(message);
185
- this.validationErrors = validationErrors;
186
- this.name = "CommetValidationError";
187
- }
188
- };
189
-
190
- // src/utils/http.ts
191
- var DEFAULT_RETRY_CONFIG = {
192
- maxRetries: 3,
193
- baseDelay: 1e3,
194
- // 1s
195
- maxDelay: 8e3,
196
- // 8s
197
- retryableStatusCodes: [408, 429, 500, 502, 503, 504]
198
- };
199
- var CommetHTTPClient = class {
200
- constructor(config, environment) {
201
- this.config = config;
202
- this.environment = environment;
203
- this.retryConfig = {
204
- ...DEFAULT_RETRY_CONFIG,
205
- maxRetries: config.retries ?? DEFAULT_RETRY_CONFIG.maxRetries
103
+ return "https://billing.commet.co";
104
+ }
105
+ async function apiRequest(endpoint, options = {}) {
106
+ const auth = loadAuth();
107
+ if (!auth) {
108
+ return { error: "Not authenticated. Run `commet login` first." };
109
+ }
110
+ try {
111
+ const response = await fetch(endpoint, {
112
+ ...options,
113
+ headers: {
114
+ ...options.headers,
115
+ Authorization: `Bearer ${auth.token}`,
116
+ "Content-Type": "application/json"
117
+ }
118
+ });
119
+ if (!response.ok) {
120
+ const errorData = await response.json().catch(() => ({}));
121
+ return {
122
+ error: errorData.message || errorData.error || `Request failed with status ${response.status}`
123
+ };
124
+ }
125
+ const data = await response.json();
126
+ return { data };
127
+ } catch (error) {
128
+ return {
129
+ error: error instanceof Error ? error.message : "Unknown error occurred"
206
130
  };
207
131
  }
208
- async get(endpoint, params, options) {
209
- return this.request("GET", endpoint, void 0, options, params);
210
- }
211
- async post(endpoint, data, options) {
212
- return this.request("POST", endpoint, data, options);
213
- }
214
- async put(endpoint, data, options) {
215
- return this.request("PUT", endpoint, data, options);
216
- }
217
- async delete(endpoint, options) {
218
- return this.request("DELETE", endpoint, void 0, options);
219
- }
220
- /**
221
- * Core request method with retry logic
222
- */
223
- async request(method, endpoint, data, options, params) {
224
- const url = this.buildURL(endpoint, params);
225
- return this.executeRequest(method, url, data, options);
226
- }
227
- /**
228
- * Execute real API request with retry logic
229
- */
230
- async executeRequest(method, url, data, options, attempt = 1) {
132
+ }
133
+
134
+ // src/commands/login.ts
135
+ var loginCommand = new import_commander.Command("login").description("Authenticate with Commet").action(async () => {
136
+ if (authExists()) {
137
+ const auth = loadAuth();
138
+ console.log(import_chalk.default.yellow("\u26A0 You are already logged in."));
139
+ console.log(
140
+ import_chalk.default.dim("Run `commet logout` first if you want to login with a different account.")
141
+ );
142
+ return;
143
+ }
144
+ const spinner = (0, import_ora.default)("Initiating login flow...").start();
145
+ const baseURL = getBaseURL("sandbox");
146
+ try {
147
+ const deviceResponse = await fetch(`${baseURL}/api/auth/device/code`, {
148
+ method: "POST",
149
+ headers: { "Content-Type": "application/json" },
150
+ body: JSON.stringify({
151
+ client_id: "commet-cli",
152
+ scope: "openid profile email"
153
+ })
154
+ });
155
+ if (!deviceResponse.ok) {
156
+ spinner.fail("Failed to initiate login");
157
+ console.error(import_chalk.default.red("Error:"), await deviceResponse.text());
158
+ return;
159
+ }
160
+ const deviceData = await deviceResponse.json();
161
+ const {
162
+ device_code,
163
+ user_code,
164
+ verification_uri_complete,
165
+ interval = 5
166
+ } = deviceData;
167
+ spinner.stop();
168
+ console.log(import_chalk.default.bold("\n\u{1F510} Commet CLI Login\n"));
169
+ console.log("Visit the following URL in your browser:");
170
+ console.log(import_chalk.default.cyan.underline(verification_uri_complete));
171
+ console.log("\nOr enter this code manually:");
172
+ console.log(import_chalk.default.bold.green(` ${user_code}`));
173
+ console.log(import_chalk.default.dim("\nOpening browser...\n"));
231
174
  try {
232
- const headers = {
233
- "x-api-key": this.config.apiKey,
234
- "Content-Type": "application/json",
235
- "User-Agent": "commet/0.1.0"
236
- };
237
- if (options?.idempotencyKey) {
238
- headers["Idempotency-Key"] = options.idempotencyKey;
239
- } else if (method === "POST" && data) {
240
- headers["Idempotency-Key"] = this.generateIdempotencyKey();
241
- }
242
- const requestConfig = {
243
- method,
244
- headers,
245
- signal: AbortSignal.timeout(
246
- options?.timeout ?? this.config.timeout ?? 3e4
247
- )
248
- };
249
- if (data) {
250
- requestConfig.body = JSON.stringify(data);
251
- }
252
- if (this.config.debug) {
253
- console.log(`[Commet SDK] ${method} ${url}`);
254
- if (data) {
255
- console.log("Request data:", JSON.stringify(data, null, 2));
256
- }
257
- }
258
- const response = await fetch(url, requestConfig);
259
- if (this.config.debug) {
260
- console.log(
261
- `[Commet SDK] Response status: ${response.status} ${response.statusText}`
262
- );
175
+ await (0, import_open.default)(verification_uri_complete);
176
+ } catch {
177
+ console.log(import_chalk.default.yellow("\u26A0 Could not open browser automatically."));
178
+ }
179
+ const pollSpinner = (0, import_ora.default)("Waiting for authorization...").start();
180
+ let pollingInterval = interval;
181
+ let attempts = 0;
182
+ const maxAttempts = 180 / pollingInterval;
183
+ const poll = async () => {
184
+ if (attempts >= maxAttempts) {
185
+ pollSpinner.fail("Login timed out");
186
+ console.log(import_chalk.default.red("Authorization timed out. Please try again."));
187
+ return;
263
188
  }
264
- let responseData;
265
- let responseText;
189
+ attempts++;
266
190
  try {
267
- const responseClone = response.clone();
268
- responseData = await response.json();
269
- responseText = "";
270
- } catch (jsonError) {
271
- try {
272
- responseText = await response.text();
273
- } catch (textError) {
274
- responseText = "Failed to read response body";
275
- }
276
- if (this.config.debug) {
277
- console.log(
278
- "[Commet SDK] Failed to parse JSON response:",
279
- responseText
280
- );
281
- }
282
- throw new CommetAPIError(
283
- `Invalid JSON response: ${response.status} ${response.statusText}`,
284
- response.status,
285
- "INVALID_JSON",
286
- { responseText }
287
- );
288
- }
289
- if (!response.ok) {
290
- if (attempt <= this.retryConfig.maxRetries && this.retryConfig.retryableStatusCodes.includes(response.status)) {
291
- const delay = Math.min(
292
- this.retryConfig.baseDelay * 2 ** (attempt - 1),
293
- this.retryConfig.maxDelay
294
- );
295
- if (this.config.debug) {
296
- console.log(
297
- `[Commet SDK] Retrying in ${delay}ms (attempt ${attempt}/${this.retryConfig.maxRetries})`
298
- );
191
+ const tokenResponse = await fetch(`${baseURL}/api/auth/device/token`, {
192
+ method: "POST",
193
+ headers: { "Content-Type": "application/json" },
194
+ body: JSON.stringify({
195
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
196
+ device_code,
197
+ client_id: "commet-cli"
198
+ })
199
+ });
200
+ const tokenData = await tokenResponse.json();
201
+ if (tokenData.access_token) {
202
+ const authConfig = {
203
+ token: tokenData.access_token
204
+ };
205
+ if (tokenData.refresh_token) {
206
+ authConfig.refreshToken = tokenData.refresh_token;
299
207
  }
300
- await this.sleep(delay);
301
- return this.executeRequest(method, url, data, options, attempt + 1);
302
- }
303
- if (this.config.debug) {
208
+ if (tokenData.expires_in) {
209
+ authConfig.expiresAt = Date.now() + tokenData.expires_in * 1e3;
210
+ }
211
+ saveAuth(authConfig);
212
+ pollSpinner.succeed("Successfully logged in!");
213
+ console.log(import_chalk.default.green("\n\u2713 Authentication complete"));
304
214
  console.log(
305
- "[Commet SDK] Error response:",
306
- JSON.stringify(responseData, null, 2)
215
+ import_chalk.default.dim("\nNext steps:\n 1. Run `commet link` to connect a project\n 2. Run `commet pull` to generate types\n")
307
216
  );
217
+ return;
308
218
  }
309
- const isErrorResponse = (data2) => {
310
- return typeof data2 === "object" && data2 !== null;
311
- };
312
- const errorData = isErrorResponse(responseData) ? responseData : {};
313
- if (response.status === 400 && errorData.errors) {
314
- throw new CommetValidationError(
315
- errorData.message || "Validation failed",
316
- errorData.errors
219
+ if (tokenData.error === "authorization_pending") {
220
+ setTimeout(() => poll(), pollingInterval * 1e3);
221
+ } else if (tokenData.error === "slow_down") {
222
+ pollingInterval += 5;
223
+ setTimeout(() => poll(), pollingInterval * 1e3);
224
+ } else if (tokenData.error === "access_denied") {
225
+ pollSpinner.fail("Authorization denied");
226
+ console.log(
227
+ import_chalk.default.red("\n\u2717 You denied the authorization request.")
317
228
  );
229
+ return;
230
+ } else if (tokenData.error === "expired_token") {
231
+ pollSpinner.fail("Code expired");
232
+ console.log(import_chalk.default.red("\n\u2717 The authorization code expired. Please try again."));
233
+ return;
234
+ } else {
235
+ pollSpinner.fail("Authorization failed");
236
+ console.log(import_chalk.default.red("\n\u2717 Error:"), tokenData.error || "Unknown error");
237
+ return;
318
238
  }
319
- throw new CommetAPIError(
320
- errorData.message || `Request failed with status ${response.status}`,
321
- response.status,
322
- errorData.code,
323
- errorData.details
239
+ } catch (error) {
240
+ pollSpinner.fail("Network error");
241
+ console.error(
242
+ import_chalk.default.red("Error:"),
243
+ error instanceof Error ? error.message : "Unknown error"
324
244
  );
325
245
  }
326
- if (this.config.debug) {
327
- console.log("[Commet SDK] Response:", responseData);
328
- }
329
- return responseData;
330
- } catch (error) {
331
- if (error instanceof TypeError && error.message.includes("fetch")) {
332
- if (attempt <= this.retryConfig.maxRetries) {
333
- const delay = Math.min(
334
- this.retryConfig.baseDelay * 2 ** (attempt - 1),
335
- this.retryConfig.maxDelay
336
- );
337
- if (this.config.debug) {
338
- console.log(`[Commet SDK] Network error, retrying in ${delay}ms`);
339
- }
340
- await this.sleep(delay);
341
- return this.executeRequest(method, url, data, options, attempt + 1);
342
- }
343
- }
344
- throw error;
246
+ };
247
+ poll();
248
+ } catch (error) {
249
+ spinner.fail("Login failed");
250
+ console.error(
251
+ import_chalk.default.red("Error:"),
252
+ error instanceof Error ? error.message : "Unknown error"
253
+ );
254
+ }
255
+ });
256
+
257
+ // src/commands/logout.ts
258
+ var import_commander2 = require("commander");
259
+ var import_chalk2 = __toESM(require("chalk"));
260
+ var logoutCommand = new import_commander2.Command("logout").description("Log out of Commet").action(async () => {
261
+ if (!authExists()) {
262
+ console.log(import_chalk2.default.yellow("\u26A0 You are not logged in."));
263
+ return;
264
+ }
265
+ clearAuth();
266
+ console.log(import_chalk2.default.green("\u2713 Successfully logged out"));
267
+ });
268
+
269
+ // src/commands/whoami.ts
270
+ var import_commander3 = require("commander");
271
+ var import_chalk3 = __toESM(require("chalk"));
272
+ var whoamiCommand = new import_commander3.Command("whoami").description("Display current authentication and project status").action(async () => {
273
+ if (!authExists()) {
274
+ console.log(import_chalk3.default.yellow("\u26A0 Not logged in"));
275
+ console.log(import_chalk3.default.dim("Run `commet login` to authenticate"));
276
+ return;
277
+ }
278
+ console.log(import_chalk3.default.green("\u2713 Logged in"));
279
+ const projectConfig = loadProjectConfig();
280
+ if (projectConfig) {
281
+ console.log(import_chalk3.default.bold("\nProject:"));
282
+ console.log(import_chalk3.default.dim("Organization:"), projectConfig.orgName);
283
+ console.log(import_chalk3.default.dim("Environment:"), projectConfig.environment);
284
+ } else {
285
+ console.log(import_chalk3.default.yellow("\n\u26A0 No project linked"));
286
+ console.log(import_chalk3.default.dim("Run `commet link` to connect this directory to an organization"));
287
+ }
288
+ });
289
+
290
+ // src/commands/link.ts
291
+ var import_commander4 = require("commander");
292
+ var import_chalk4 = __toESM(require("chalk"));
293
+ var import_inquirer = __toESM(require("inquirer"));
294
+ var import_ora2 = __toESM(require("ora"));
295
+ var linkCommand = new import_commander4.Command("link").description("Link this project to a Commet organization").action(async () => {
296
+ if (!authExists()) {
297
+ console.log(import_chalk4.default.red("\u2717 Not authenticated"));
298
+ console.log(import_chalk4.default.dim("Run `commet login` first"));
299
+ return;
300
+ }
301
+ if (projectConfigExists()) {
302
+ const config = loadProjectConfig();
303
+ console.log(import_chalk4.default.yellow("\u26A0 This project is already linked"));
304
+ console.log(
305
+ import_chalk4.default.dim(`Organization: ${config?.orgName} (${config?.environment})`)
306
+ );
307
+ console.log(
308
+ import_chalk4.default.dim("\nRun `commet unlink` first if you want to change the organization")
309
+ );
310
+ return;
311
+ }
312
+ const spinner = (0, import_ora2.default)("Fetching organizations...").start();
313
+ const baseURL = getBaseURL("sandbox");
314
+ const result = await apiRequest(
315
+ `${baseURL}/api/cli/organizations`
316
+ );
317
+ if (result.error || !result.data) {
318
+ spinner.fail("Failed to fetch organizations");
319
+ console.error(import_chalk4.default.red("Error:"), result.error);
320
+ return;
321
+ }
322
+ const { organizations } = result.data;
323
+ if (organizations.length === 0) {
324
+ spinner.stop();
325
+ console.log(import_chalk4.default.yellow("\u26A0 No organizations found"));
326
+ console.log(
327
+ import_chalk4.default.dim("Create an organization at https://billing.commet.co first")
328
+ );
329
+ return;
330
+ }
331
+ spinner.stop();
332
+ const answers = await import_inquirer.default.prompt([
333
+ {
334
+ type: "list",
335
+ name: "orgId",
336
+ message: "Select organization:",
337
+ choices: organizations.map((org) => ({
338
+ name: `${org.name} (${org.slug})`,
339
+ value: org.id
340
+ }))
341
+ },
342
+ {
343
+ type: "list",
344
+ name: "environment",
345
+ message: "Select environment:",
346
+ choices: [
347
+ { name: "Sandbox (Development)", value: "sandbox" },
348
+ { name: "Production", value: "production" }
349
+ ],
350
+ default: "sandbox"
351
+ }
352
+ ]);
353
+ const selectedOrg = organizations.find((org) => org.id === answers.orgId);
354
+ if (!selectedOrg) {
355
+ console.log(import_chalk4.default.red("\u2717 Organization not found"));
356
+ return;
357
+ }
358
+ saveProjectConfig({
359
+ orgId: selectedOrg.id,
360
+ orgName: selectedOrg.name,
361
+ environment: answers.environment
362
+ });
363
+ console.log(import_chalk4.default.green("\n\u2713 Project linked successfully!"));
364
+ console.log(import_chalk4.default.dim("\nProject configuration:"));
365
+ console.log(import_chalk4.default.dim(` Organization: ${selectedOrg.name}`));
366
+ console.log(import_chalk4.default.dim(` Environment: ${answers.environment}`));
367
+ console.log(
368
+ import_chalk4.default.dim("\nNext step:\n Run `commet pull` to generate TypeScript types")
369
+ );
370
+ });
371
+
372
+ // src/commands/unlink.ts
373
+ var import_commander5 = require("commander");
374
+ var import_chalk5 = __toESM(require("chalk"));
375
+ var unlinkCommand = new import_commander5.Command("unlink").description("Unlink this project from Commet").action(async () => {
376
+ if (!projectConfigExists()) {
377
+ console.log(import_chalk5.default.yellow("\u26A0 This project is not linked to any organization"));
378
+ return;
379
+ }
380
+ clearProjectConfig();
381
+ console.log(import_chalk5.default.green("\u2713 Project unlinked successfully"));
382
+ console.log(import_chalk5.default.dim("Run `commet link` to connect to an organization"));
383
+ });
384
+
385
+ // src/commands/switch.ts
386
+ var import_commander6 = require("commander");
387
+ var import_chalk6 = __toESM(require("chalk"));
388
+ var import_inquirer2 = __toESM(require("inquirer"));
389
+ var import_ora3 = __toESM(require("ora"));
390
+ var switchCommand = new import_commander6.Command("switch").description("Switch to a different organization").action(async () => {
391
+ if (!authExists()) {
392
+ console.log(import_chalk6.default.red("\u2717 Not authenticated"));
393
+ console.log(import_chalk6.default.dim("Run `commet login` first"));
394
+ return;
395
+ }
396
+ if (!projectConfigExists()) {
397
+ console.log(import_chalk6.default.yellow("\u26A0 Project not linked"));
398
+ console.log(
399
+ import_chalk6.default.dim("Run `commet link` first to connect to an organization")
400
+ );
401
+ return;
402
+ }
403
+ const spinner = (0, import_ora3.default)("Fetching organizations...").start();
404
+ const baseURL = getBaseURL("sandbox");
405
+ const result = await apiRequest(
406
+ `${baseURL}/api/cli/organizations`
407
+ );
408
+ if (result.error || !result.data) {
409
+ spinner.fail("Failed to fetch organizations");
410
+ console.error(import_chalk6.default.red("Error:"), result.error);
411
+ return;
412
+ }
413
+ const { organizations } = result.data;
414
+ if (organizations.length === 0) {
415
+ spinner.stop();
416
+ console.log(import_chalk6.default.yellow("\u26A0 No organizations found"));
417
+ return;
418
+ }
419
+ spinner.stop();
420
+ const answers = await import_inquirer2.default.prompt([
421
+ {
422
+ type: "list",
423
+ name: "orgId",
424
+ message: "Select organization:",
425
+ choices: organizations.map((org) => ({
426
+ name: `${org.name} (${org.slug})`,
427
+ value: org.id
428
+ }))
429
+ },
430
+ {
431
+ type: "list",
432
+ name: "environment",
433
+ message: "Select environment:",
434
+ choices: [
435
+ { name: "Sandbox (Development)", value: "sandbox" },
436
+ { name: "Production", value: "production" }
437
+ ],
438
+ default: "sandbox"
345
439
  }
440
+ ]);
441
+ const selectedOrg = organizations.find((org) => org.id === answers.orgId);
442
+ if (!selectedOrg) {
443
+ console.log(import_chalk6.default.red("\u2717 Organization not found"));
444
+ return;
445
+ }
446
+ saveProjectConfig({
447
+ orgId: selectedOrg.id,
448
+ orgName: selectedOrg.name,
449
+ environment: answers.environment
450
+ });
451
+ console.log(import_chalk6.default.green("\n\u2713 Switched organization successfully!"));
452
+ console.log(import_chalk6.default.dim("\nNew configuration:"));
453
+ console.log(import_chalk6.default.dim(` Organization: ${selectedOrg.name}`));
454
+ console.log(import_chalk6.default.dim(` Environment: ${answers.environment}`));
455
+ console.log(
456
+ import_chalk6.default.dim("\nRun `commet pull` to update TypeScript types for this organization")
457
+ );
458
+ });
459
+
460
+ // src/commands/pull.ts
461
+ var fs2 = __toESM(require("fs"));
462
+ var path2 = __toESM(require("path"));
463
+ var import_commander7 = require("commander");
464
+ var import_chalk7 = __toESM(require("chalk"));
465
+ var import_ora4 = __toESM(require("ora"));
466
+
467
+ // src/utils/generator.ts
468
+ function generateTypes(eventTypes, seatTypes) {
469
+ const eventTypeUnion = eventTypes.length > 0 ? eventTypes.map((e) => `"${e.code}"`).join(" | ") : "string";
470
+ const seatTypeUnion = seatTypes.length > 0 ? seatTypes.map((s) => `"${s.code}"`).join(" | ") : "string";
471
+ const eventComments = eventTypes.map((e) => ` * - "${e.code}": ${e.name}${e.description ? ` - ${e.description}` : ""}`).join("\n");
472
+ const seatComments = seatTypes.map(
473
+ (s) => ` * - "${s.code}": ${s.name}${s.description ? ` - ${s.description}` : ""} ${s.isFree ? "(Free)" : ""}`
474
+ ).join("\n");
475
+ return `// Auto-generated by Commet CLI
476
+ // Do not edit this file manually - run 'commet pull' to update
477
+
478
+ /**
479
+ * Event types available in your organization
480
+ ${eventComments}
481
+ */
482
+ export type CommetEventType = ${eventTypeUnion};
483
+
484
+ /**
485
+ * Seat types available in your organization
486
+ ${seatComments}
487
+ */
488
+ export type CommetSeatType = ${seatTypeUnion};
489
+
490
+ /**
491
+ * Use these types with the Commet SDK for type-safe event and seat tracking
492
+ *
493
+ * @example
494
+ * import { Commet } from 'commet';
495
+ * import type { CommetEventType, CommetSeatType } from './.commet';
496
+ *
497
+ * const commet = new Commet({ apiKey: 'your-api-key' });
498
+ *
499
+ * // Type-safe event tracking
500
+ * await commet.usage.sendEvent<CommetEventType>({
501
+ * customerId: 'cust_123',
502
+ * eventType: 'api_call', // Autocomplete works!
503
+ * timestamp: new Date(),
504
+ * });
505
+ *
506
+ * // Type-safe seat management
507
+ * await commet.seats.updateSeats<CommetSeatType>({
508
+ * customerId: 'cust_123',
509
+ * seatType: 'admin_seat', // Autocomplete works!
510
+ * totalSeats: 5,
511
+ * });
512
+ */
513
+ `;
514
+ }
515
+
516
+ // src/commands/pull.ts
517
+ var pullCommand = new import_commander7.Command("pull").description("Pull type definitions from Commet").option(
518
+ "-o, --output <file>",
519
+ "Output file path",
520
+ ".commet.d.ts"
521
+ ).action(async (options) => {
522
+ if (!authExists()) {
523
+ console.log(import_chalk7.default.red("\u2717 Not authenticated"));
524
+ console.log(import_chalk7.default.dim("Run `commet login` first"));
525
+ return;
526
+ }
527
+ if (!projectConfigExists()) {
528
+ console.log(import_chalk7.default.red("\u2717 Project not linked"));
529
+ console.log(import_chalk7.default.dim("Run `commet link` first to connect to an organization"));
530
+ return;
531
+ }
532
+ const projectConfig = loadProjectConfig();
533
+ if (!projectConfig) {
534
+ console.log(import_chalk7.default.red("\u2717 Invalid project configuration"));
535
+ return;
536
+ }
537
+ const spinner = (0, import_ora4.default)("Fetching type definitions...").start();
538
+ const baseURL = getBaseURL(projectConfig.environment);
539
+ const result = await apiRequest(
540
+ `${baseURL}/api/cli/types?orgId=${projectConfig.orgId}`
541
+ );
542
+ if (result.error || !result.data) {
543
+ spinner.fail("Failed to fetch types");
544
+ console.error(import_chalk7.default.red("Error:"), result.error);
545
+ return;
546
+ }
547
+ const { eventTypes, seatTypes } = result.data;
548
+ const typeDefinitions = generateTypes(eventTypes, seatTypes);
549
+ const outputPath = path2.resolve(process.cwd(), options.output);
550
+ fs2.writeFileSync(outputPath, typeDefinitions, "utf8");
551
+ spinner.succeed("Type definitions generated!");
552
+ console.log(import_chalk7.default.green("\n\u2713 Success"));
553
+ console.log(import_chalk7.default.dim("\nGenerated types:"));
554
+ console.log(
555
+ import_chalk7.default.dim(` Event types: ${eventTypes.length > 0 ? eventTypes.map((e) => e.code).join(", ") : "none"}`)
556
+ );
557
+ console.log(
558
+ import_chalk7.default.dim(` Seat types: ${seatTypes.length > 0 ? seatTypes.map((s) => s.code).join(", ") : "none"}`)
559
+ );
560
+ console.log(import_chalk7.default.dim(`
561
+ Output file: ${outputPath}`));
562
+ if (eventTypes.length === 0 && seatTypes.length === 0) {
563
+ console.log(
564
+ import_chalk7.default.yellow("\n\u26A0 No types found. Create event types and seat types in your Commet dashboard.")
565
+ );
346
566
  }
347
- /**
348
- * Get base URL based on environment
349
- */
350
- getBaseURL() {
351
- return this.environment === "production" ? "https://billing.commet.co" : "https://sandbox.commet.co";
352
- }
353
- /**
354
- * Build full URL from endpoint and params
355
- */
356
- buildURL(endpoint, params) {
357
- const baseURL = this.getBaseURL();
358
- const fullPath = `/api${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`;
359
- if (this.config.debug) {
567
+ });
568
+
569
+ // src/commands/list.ts
570
+ var import_commander8 = require("commander");
571
+ var import_chalk8 = __toESM(require("chalk"));
572
+ var import_ora5 = __toESM(require("ora"));
573
+ var listCommand = new import_commander8.Command("list").description("List event types or seat types").argument("<type>", "Type to list (events or seats)").action(async (type) => {
574
+ if (type !== "events" && type !== "seats") {
575
+ console.log(import_chalk8.default.red('\u2717 Invalid type. Use "events" or "seats"'));
576
+ console.log(import_chalk8.default.dim("\nExamples:"));
577
+ console.log(import_chalk8.default.dim(" commet list events"));
578
+ console.log(import_chalk8.default.dim(" commet list seats"));
579
+ return;
580
+ }
581
+ if (!authExists()) {
582
+ console.log(import_chalk8.default.red("\u2717 Not authenticated"));
583
+ console.log(import_chalk8.default.dim("Run `commet login` first"));
584
+ return;
585
+ }
586
+ if (!projectConfigExists()) {
587
+ console.log(import_chalk8.default.red("\u2717 Project not linked"));
588
+ console.log(import_chalk8.default.dim("Run `commet link` first to connect to an organization"));
589
+ return;
590
+ }
591
+ const projectConfig = loadProjectConfig();
592
+ if (!projectConfig) {
593
+ console.log(import_chalk8.default.red("\u2717 Invalid project configuration"));
594
+ return;
595
+ }
596
+ const spinner = (0, import_ora5.default)(`Fetching ${type}...`).start();
597
+ const baseURL = getBaseURL(projectConfig.environment);
598
+ const result = await apiRequest(
599
+ `${baseURL}/api/cli/types?orgId=${projectConfig.orgId}`
600
+ );
601
+ if (result.error || !result.data) {
602
+ spinner.fail(`Failed to fetch ${type}`);
603
+ console.error(import_chalk8.default.red("Error:"), result.error);
604
+ return;
605
+ }
606
+ spinner.stop();
607
+ if (type === "events") {
608
+ const { eventTypes } = result.data;
609
+ if (eventTypes.length === 0) {
610
+ console.log(import_chalk8.default.yellow("\u26A0 No event types found"));
360
611
  console.log(
361
- `[Commet SDK] Building URL - baseURL: ${baseURL}, endpoint: ${endpoint}, fullPath: ${fullPath}`
612
+ import_chalk8.default.dim("Create event types in your Commet dashboard first")
362
613
  );
614
+ return;
363
615
  }
364
- const url = new URL(fullPath, baseURL);
365
- if (params) {
366
- for (const [key, value] of Object.entries(params)) {
367
- if (value !== void 0 && value !== null) {
368
- url.searchParams.append(key, String(value));
369
- }
616
+ console.log(import_chalk8.default.bold(`
617
+ \u{1F4CA} Event Types (${eventTypes.length})
618
+ `));
619
+ for (const eventType of eventTypes) {
620
+ console.log(import_chalk8.default.green(`\u2022 ${eventType.code}`));
621
+ console.log(import_chalk8.default.dim(` ${eventType.name}`));
622
+ if (eventType.description) {
623
+ console.log(import_chalk8.default.dim(` ${eventType.description}`));
370
624
  }
625
+ console.log("");
371
626
  }
372
- const finalUrl = url.toString();
373
- if (this.config.debug) {
374
- console.log(`[Commet SDK] Final URL: ${finalUrl}`);
375
- }
376
- return finalUrl;
377
- }
378
- /**
379
- * Generate idempotency key
380
- */
381
- generateIdempotencyKey() {
382
- return `sdk_${Date.now()}_${Math.random().toString(36).substring(2)}`;
383
- }
384
- /**
385
- * Sleep for specified milliseconds
386
- */
387
- sleep(ms) {
388
- return new Promise((resolve) => setTimeout(resolve, ms));
389
- }
390
- };
391
-
392
- // src/client.ts
393
- var Commet = class {
394
- constructor(config) {
395
- if (!config.apiKey) {
396
- throw new Error("Commet SDK: API key is required");
397
- }
398
- if (!config.apiKey.startsWith("ck_")) {
399
- throw new Error(
400
- "Commet SDK: Invalid API key format. Expected format: ck_xxx..."
627
+ } else {
628
+ const { seatTypes } = result.data;
629
+ if (seatTypes.length === 0) {
630
+ console.log(import_chalk8.default.yellow("\u26A0 No seat types found"));
631
+ console.log(
632
+ import_chalk8.default.dim("Create seat types in your Commet dashboard first")
401
633
  );
634
+ return;
402
635
  }
403
- this.environment = config.environment || "sandbox";
404
- this.httpClient = new CommetHTTPClient(config, this.environment);
405
- this.customers = new CustomersResource(this.httpClient);
406
- this.usage = new UsageResource(this.httpClient);
407
- this.seats = new SeatsResource(this.httpClient);
408
- if (config.debug) {
409
- console.log(`[Commet SDK] Initialized in ${this.environment} mode`);
410
- console.log("API Key:", `${config.apiKey.substring(0, 12)}...`);
411
- const baseURL = this.environment === "production" ? "https://billing.commet.co" : "https://sandbox.commet.co";
412
- console.log("Base URL:", baseURL);
636
+ console.log(import_chalk8.default.bold(`
637
+ \u{1F4BA} Seat Types (${seatTypes.length})
638
+ `));
639
+ for (const seatType of seatTypes) {
640
+ console.log(
641
+ import_chalk8.default.green(`\u2022 ${seatType.code}${seatType.isFree ? " (Free)" : ""}`)
642
+ );
643
+ console.log(import_chalk8.default.dim(` ${seatType.name}`));
644
+ if (seatType.description) {
645
+ console.log(import_chalk8.default.dim(` ${seatType.description}`));
646
+ }
647
+ console.log("");
413
648
  }
414
649
  }
415
- getEnvironment() {
416
- return this.environment;
417
- }
418
- isSandbox() {
419
- return this.environment === "sandbox";
420
- }
421
- isProduction() {
422
- return this.environment === "production";
423
- }
424
- };
650
+ });
425
651
 
426
- // src/utils/environment.ts
427
- function isSandbox(environment) {
428
- return environment === "sandbox";
429
- }
430
- function isProduction(environment) {
431
- return environment === "production";
432
- }
652
+ // src/commands/info.ts
653
+ var import_commander9 = require("commander");
654
+ var import_chalk9 = __toESM(require("chalk"));
655
+ var infoCommand = new import_commander9.Command("info").description("Display information about the current project").action(async () => {
656
+ console.log(import_chalk9.default.bold("\n\u{1F4E6} Project Information\n"));
657
+ if (!authExists()) {
658
+ console.log(import_chalk9.default.yellow("Authentication: Not logged in"));
659
+ console.log(import_chalk9.default.dim("Run `commet login` to authenticate\n"));
660
+ return;
661
+ }
662
+ console.log(import_chalk9.default.green("Authentication: Logged in \u2713"));
663
+ if (!projectConfigExists()) {
664
+ console.log(import_chalk9.default.yellow("\nProject: Not linked"));
665
+ console.log(import_chalk9.default.dim("Run `commet link` to connect to an organization\n"));
666
+ return;
667
+ }
668
+ const projectConfig = loadProjectConfig();
669
+ if (!projectConfig) {
670
+ console.log(import_chalk9.default.red("\nProject: Invalid configuration"));
671
+ return;
672
+ }
673
+ console.log(import_chalk9.default.green("\nProject: Linked \u2713"));
674
+ console.log(import_chalk9.default.dim(" Organization:"), projectConfig.orgName);
675
+ console.log(import_chalk9.default.dim(" Organization ID:"), projectConfig.orgId);
676
+ console.log(import_chalk9.default.dim(" Environment:"), projectConfig.environment);
677
+ console.log(
678
+ import_chalk9.default.dim("\nRun `commet pull` to generate type definitions\n")
679
+ );
680
+ });
433
681
 
434
682
  // src/index.ts
435
- var index_default = Commet;
436
- // Annotate the CommonJS export names for ESM import in node:
437
- 0 && (module.exports = {
438
- Commet,
439
- CommetAPIError,
440
- CommetError,
441
- CommetValidationError,
442
- isProduction,
443
- isSandbox
444
- });
445
- //# sourceMappingURL=index.js.map
683
+ var program = new import_commander10.Command();
684
+ program.name("commet").description("Commet CLI - Manage your billing platform from the command line").version("0.3.0");
685
+ program.addCommand(loginCommand);
686
+ program.addCommand(logoutCommand);
687
+ program.addCommand(whoamiCommand);
688
+ program.addCommand(linkCommand);
689
+ program.addCommand(unlinkCommand);
690
+ program.addCommand(switchCommand);
691
+ program.addCommand(infoCommand);
692
+ program.addCommand(pullCommand);
693
+ program.addCommand(listCommand);
694
+ program.exitOverride();
695
+ try {
696
+ program.parse(process.argv);
697
+ } catch (error) {
698
+ if (error instanceof Error) {
699
+ if (error.message.includes("outputHelp")) {
700
+ process.exit(0);
701
+ }
702
+ console.error(import_chalk10.default.red("Error:"), error.message);
703
+ }
704
+ process.exit(1);
705
+ }
706
+ if (!process.argv.slice(2).length) {
707
+ program.outputHelp();
708
+ }