commet 1.10.0 → 1.11.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 +700 -372
- 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
|
|
27
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
28
28
|
var import_commander13 = require("commander");
|
|
29
29
|
|
|
30
30
|
// package.json
|
|
31
31
|
var package_default = {
|
|
32
32
|
name: "commet",
|
|
33
|
-
version: "1.
|
|
33
|
+
version: "1.11.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,8 @@ var package_default = {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
// src/commands/
|
|
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/agent-info.ts
|
|
97
91
|
var import_commander = require("commander");
|
|
98
|
-
var import_ora2 = __toESM(require("ora"));
|
|
99
|
-
var import_tar = require("tar");
|
|
100
92
|
|
|
101
93
|
// src/utils/config.ts
|
|
102
94
|
var fs = __toESM(require("fs"));
|
|
@@ -165,6 +157,211 @@ function clearProjectConfig() {
|
|
|
165
157
|
}
|
|
166
158
|
}
|
|
167
159
|
|
|
160
|
+
// src/utils/config-loader.ts
|
|
161
|
+
var fs2 = __toESM(require("fs"));
|
|
162
|
+
var path2 = __toESM(require("path"));
|
|
163
|
+
var import_jiti = require("jiti");
|
|
164
|
+
var CONFIG_NAMES = [
|
|
165
|
+
"commet.config.ts",
|
|
166
|
+
"commet.config.js",
|
|
167
|
+
"commet.config.mjs"
|
|
168
|
+
];
|
|
169
|
+
function findConfigFile(cwd) {
|
|
170
|
+
for (const name of CONFIG_NAMES) {
|
|
171
|
+
const fullPath = path2.resolve(cwd, name);
|
|
172
|
+
if (fs2.existsSync(fullPath)) {
|
|
173
|
+
return fullPath;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
async function loadBillingConfig(cwd) {
|
|
179
|
+
const configPath = findConfigFile(cwd);
|
|
180
|
+
if (!configPath) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`No commet.config.ts found in ${cwd}. Create one with defineConfig() or run 'commet pull' to generate it.`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
const jiti = (0, import_jiti.createJiti)(configPath, { interopDefault: true });
|
|
186
|
+
const mod = await jiti.import(configPath);
|
|
187
|
+
if (!mod || typeof mod !== "object") {
|
|
188
|
+
throw new Error(`${configPath}: failed to load config module`);
|
|
189
|
+
}
|
|
190
|
+
const moduleRecord = mod;
|
|
191
|
+
if (!moduleRecord.default) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`${configPath}: must use \`export default defineConfig({...})\``
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const config = moduleRecord.default;
|
|
197
|
+
validateConfig(config, configPath);
|
|
198
|
+
return { config, configPath };
|
|
199
|
+
}
|
|
200
|
+
var VALID_FEATURE_TYPES = /* @__PURE__ */ new Set(["boolean", "usage", "seats"]);
|
|
201
|
+
var VALID_INTERVALS = /* @__PURE__ */ new Set([
|
|
202
|
+
"weekly",
|
|
203
|
+
"monthly",
|
|
204
|
+
"quarterly",
|
|
205
|
+
"yearly",
|
|
206
|
+
"one_time"
|
|
207
|
+
]);
|
|
208
|
+
function validateConfig(config, configPath) {
|
|
209
|
+
if (!config || typeof config !== "object") {
|
|
210
|
+
throw new Error(`${configPath}: config must be an object`);
|
|
211
|
+
}
|
|
212
|
+
if (!config.features || typeof config.features !== "object") {
|
|
213
|
+
throw new Error(`${configPath}: config.features must be an object`);
|
|
214
|
+
}
|
|
215
|
+
if (!config.plans || typeof config.plans !== "object") {
|
|
216
|
+
throw new Error(`${configPath}: config.plans must be an object`);
|
|
217
|
+
}
|
|
218
|
+
for (const [code, feature] of Object.entries(config.features)) {
|
|
219
|
+
if (!feature.name || typeof feature.name !== "string") {
|
|
220
|
+
throw new Error(`Feature "${code}": name is required`);
|
|
221
|
+
}
|
|
222
|
+
if (!VALID_FEATURE_TYPES.has(feature.type)) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`Feature "${code}": type must be one of: boolean, usage, seats`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (const [code, plan] of Object.entries(config.plans)) {
|
|
229
|
+
if (!plan.name || typeof plan.name !== "string") {
|
|
230
|
+
throw new Error(`Plan "${code}": name is required`);
|
|
231
|
+
}
|
|
232
|
+
if (!Array.isArray(plan.prices)) {
|
|
233
|
+
throw new Error(`Plan "${code}": prices must be an array`);
|
|
234
|
+
}
|
|
235
|
+
for (const price of plan.prices) {
|
|
236
|
+
if (!VALID_INTERVALS.has(price.interval)) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
`Plan "${code}": price interval "${price.interval}" is not valid`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
if (typeof price.amount !== "number") {
|
|
242
|
+
throw new Error(`Plan "${code}": price amount must be a number`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (plan.prices.length > 0) {
|
|
246
|
+
if (!plan.defaultInterval) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Plan "${code}": defaultInterval is required when prices are defined`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const priceIntervals = new Set(plan.prices.map((p) => p.interval));
|
|
252
|
+
if (!priceIntervals.has(plan.defaultInterval)) {
|
|
253
|
+
throw new Error(
|
|
254
|
+
`Plan "${code}": defaultInterval "${plan.defaultInterval}" does not match any price interval (${[...priceIntervals].join(", ")})`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (plan.features) {
|
|
259
|
+
for (const featureCode of Object.keys(plan.features)) {
|
|
260
|
+
if (!config.features[featureCode]) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Plan "${code}": references feature "${featureCode}" which is not defined in config.features`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/commands/agent-info.ts
|
|
271
|
+
var agentInfoCommand = new import_commander.Command("agent-info").description(
|
|
272
|
+
"Print project status and CLI capabilities as JSON. Designed for AI agents and CI pipelines \u2014 run this first to discover what commands are available and how to call them non-interactively."
|
|
273
|
+
).action(() => {
|
|
274
|
+
const authenticated = authExists();
|
|
275
|
+
const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
|
|
276
|
+
const configPath = findConfigFile(process.cwd());
|
|
277
|
+
const setup = [];
|
|
278
|
+
if (!authenticated) {
|
|
279
|
+
setup.push(
|
|
280
|
+
"Not authenticated. A human must run 'commet login' in a terminal with browser access \u2014 this is the only interactive step."
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (authenticated && !projectConfig) {
|
|
284
|
+
setup.push(
|
|
285
|
+
"No project linked. Run 'commet link --org <slug>' or 'commet orgs --json' to find available organizations."
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
const output = {
|
|
289
|
+
version: package_default.version,
|
|
290
|
+
authenticated,
|
|
291
|
+
...setup.length > 0 ? { setup } : {},
|
|
292
|
+
project: projectConfig ? {
|
|
293
|
+
linked: true,
|
|
294
|
+
orgId: projectConfig.orgId,
|
|
295
|
+
orgName: projectConfig.orgName,
|
|
296
|
+
mode: projectConfig.mode
|
|
297
|
+
} : { linked: false },
|
|
298
|
+
config: {
|
|
299
|
+
exists: configPath !== null,
|
|
300
|
+
path: configPath?.split("/").pop() ?? null
|
|
301
|
+
},
|
|
302
|
+
commands: {
|
|
303
|
+
pull: {
|
|
304
|
+
description: "Pull remote config and generate commet.config.ts",
|
|
305
|
+
usage: "commet pull --json --yes",
|
|
306
|
+
preview: "commet pull --json --dry-run"
|
|
307
|
+
},
|
|
308
|
+
push: {
|
|
309
|
+
description: "Push commet.config.ts to remote",
|
|
310
|
+
usage: "commet push --json --yes",
|
|
311
|
+
preview: "commet push --json --dry-run"
|
|
312
|
+
},
|
|
313
|
+
list: {
|
|
314
|
+
description: "List resources from remote",
|
|
315
|
+
usage: "commet list <features|plans|seats> --json"
|
|
316
|
+
},
|
|
317
|
+
orgs: {
|
|
318
|
+
description: "List available organizations",
|
|
319
|
+
usage: "commet orgs --json"
|
|
320
|
+
},
|
|
321
|
+
link: {
|
|
322
|
+
description: "Link project to an organization",
|
|
323
|
+
usage: "commet link --org <slug-or-id>"
|
|
324
|
+
},
|
|
325
|
+
switch: {
|
|
326
|
+
description: "Switch to a different organization",
|
|
327
|
+
usage: "commet switch --org <slug-or-id>"
|
|
328
|
+
},
|
|
329
|
+
listen: {
|
|
330
|
+
description: "Forward webhook events to local server. Long-running streaming process \u2014 run in background.",
|
|
331
|
+
usage: "commet listen <url> [--events <types>]"
|
|
332
|
+
},
|
|
333
|
+
create: {
|
|
334
|
+
description: "Scaffold a new Commet app from template",
|
|
335
|
+
usage: "commet create [name] -t <template> --org <slug> -y"
|
|
336
|
+
},
|
|
337
|
+
unlink: {
|
|
338
|
+
description: "Unlink project from organization",
|
|
339
|
+
usage: "commet unlink"
|
|
340
|
+
},
|
|
341
|
+
login: {
|
|
342
|
+
description: "Authenticate via browser. Requires a human \u2014 opens a device-code flow that must be confirmed in a browser.",
|
|
343
|
+
usage: "commet login"
|
|
344
|
+
},
|
|
345
|
+
logout: {
|
|
346
|
+
description: "Log out of Commet",
|
|
347
|
+
usage: "commet logout"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
console.log(JSON.stringify(output, null, 2));
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// src/commands/create.ts
|
|
355
|
+
var import_node_child_process = require("child_process");
|
|
356
|
+
var fs3 = __toESM(require("fs"));
|
|
357
|
+
var os2 = __toESM(require("os"));
|
|
358
|
+
var path3 = __toESM(require("path"));
|
|
359
|
+
var import_prompts = require("@inquirer/prompts");
|
|
360
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
361
|
+
var import_commander2 = require("commander");
|
|
362
|
+
var import_ora2 = __toESM(require("ora"));
|
|
363
|
+
var import_tar = require("tar");
|
|
364
|
+
|
|
168
365
|
// src/utils/api.ts
|
|
169
366
|
var BASE_URL = "https://commet.co";
|
|
170
367
|
async function apiRequest(endpoint, options = {}) {
|
|
@@ -363,11 +560,11 @@ async function downloadTemplate(templateDir, dest, ref) {
|
|
|
363
560
|
if (!response.ok) {
|
|
364
561
|
throw new Error(`Failed to download (HTTP ${response.status})`);
|
|
365
562
|
}
|
|
366
|
-
const tempFile =
|
|
563
|
+
const tempFile = path3.join(os2.tmpdir(), `commet-${Date.now()}.tar.gz`);
|
|
367
564
|
try {
|
|
368
565
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
369
|
-
|
|
370
|
-
|
|
566
|
+
fs3.writeFileSync(tempFile, buffer);
|
|
567
|
+
fs3.mkdirSync(dest, { recursive: true });
|
|
371
568
|
await (0, import_tar.extract)({
|
|
372
569
|
file: tempFile,
|
|
373
570
|
cwd: dest,
|
|
@@ -378,22 +575,22 @@ async function downloadTemplate(templateDir, dest, ref) {
|
|
|
378
575
|
}
|
|
379
576
|
});
|
|
380
577
|
} finally {
|
|
381
|
-
if (
|
|
382
|
-
|
|
578
|
+
if (fs3.existsSync(tempFile)) {
|
|
579
|
+
fs3.unlinkSync(tempFile);
|
|
383
580
|
}
|
|
384
581
|
}
|
|
385
|
-
const files =
|
|
582
|
+
const files = fs3.readdirSync(dest);
|
|
386
583
|
if (files.length === 0) {
|
|
387
584
|
throw new Error(`Template "${templateDir}" not found in repository`);
|
|
388
585
|
}
|
|
389
586
|
}
|
|
390
587
|
function updatePackageJson(dest, projectName) {
|
|
391
|
-
const pkgPath =
|
|
392
|
-
const pkg = JSON.parse(
|
|
588
|
+
const pkgPath = path3.join(dest, "package.json");
|
|
589
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
393
590
|
pkg.name = projectName;
|
|
394
591
|
pkg.version = "0.0.1";
|
|
395
592
|
pkg.private = true;
|
|
396
|
-
|
|
593
|
+
fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
397
594
|
`);
|
|
398
595
|
}
|
|
399
596
|
async function fetchLatestVersion(packageName) {
|
|
@@ -409,8 +606,8 @@ async function fetchLatestVersion(packageName) {
|
|
|
409
606
|
return data.version;
|
|
410
607
|
}
|
|
411
608
|
async function resolveWorkspaceDeps(dest) {
|
|
412
|
-
const pkgPath =
|
|
413
|
-
const pkg = JSON.parse(
|
|
609
|
+
const pkgPath = path3.join(dest, "package.json");
|
|
610
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
414
611
|
const depSections = [
|
|
415
612
|
"dependencies",
|
|
416
613
|
"devDependencies",
|
|
@@ -447,29 +644,29 @@ async function resolveWorkspaceDeps(dest) {
|
|
|
447
644
|
pkg[section][name] = version;
|
|
448
645
|
}
|
|
449
646
|
}
|
|
450
|
-
|
|
647
|
+
fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
451
648
|
`);
|
|
452
649
|
return workspaceDeps.size;
|
|
453
650
|
}
|
|
454
651
|
function copyEnvExample(dest) {
|
|
455
|
-
const examplePath =
|
|
456
|
-
const envPath =
|
|
457
|
-
if (
|
|
458
|
-
|
|
652
|
+
const examplePath = path3.join(dest, ".env.example");
|
|
653
|
+
const envPath = path3.join(dest, ".env");
|
|
654
|
+
if (fs3.existsSync(examplePath)) {
|
|
655
|
+
fs3.copyFileSync(examplePath, envPath);
|
|
459
656
|
}
|
|
460
657
|
}
|
|
461
658
|
function writeApiKeyToEnv(dest, apiKey) {
|
|
462
|
-
const envPath =
|
|
463
|
-
if (!
|
|
464
|
-
let content =
|
|
659
|
+
const envPath = path3.join(dest, ".env");
|
|
660
|
+
if (!fs3.existsSync(envPath)) return;
|
|
661
|
+
let content = fs3.readFileSync(envPath, "utf-8");
|
|
465
662
|
content = content.replace(/COMMET_API_KEY=.*/, `COMMET_API_KEY=${apiKey}`);
|
|
466
|
-
|
|
663
|
+
fs3.writeFileSync(envPath, content);
|
|
467
664
|
}
|
|
468
665
|
function linkProject(dest, orgId, orgName) {
|
|
469
|
-
const commetDir =
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
666
|
+
const commetDir = path3.join(dest, ".commet");
|
|
667
|
+
fs3.mkdirSync(commetDir, { recursive: true });
|
|
668
|
+
fs3.writeFileSync(
|
|
669
|
+
path3.join(commetDir, "config.json"),
|
|
473
670
|
JSON.stringify({ orgId, orgName, mode: "sandbox" }, null, 2),
|
|
474
671
|
"utf8"
|
|
475
672
|
);
|
|
@@ -508,7 +705,7 @@ async function installSkills(projectRoot) {
|
|
|
508
705
|
});
|
|
509
706
|
});
|
|
510
707
|
}
|
|
511
|
-
var createCommand = new
|
|
708
|
+
var createCommand = new import_commander2.Command("create").description("Create a new Commet app from a template").argument("[name]", "Project name").option(
|
|
512
709
|
"-t, --template <template>",
|
|
513
710
|
"Template to use (fixed, seats, metered, credits, balance-ai, balance-fixed)"
|
|
514
711
|
).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) => {
|
|
@@ -541,8 +738,8 @@ var createCommand = new import_commander.Command("create").description("Create a
|
|
|
541
738
|
return;
|
|
542
739
|
}
|
|
543
740
|
}
|
|
544
|
-
const dest =
|
|
545
|
-
if (
|
|
741
|
+
const dest = path3.resolve(projectName);
|
|
742
|
+
if (fs3.existsSync(dest)) {
|
|
546
743
|
console.log(
|
|
547
744
|
import_chalk3.default.red(`\u2717 Directory "${projectName}" already exists`)
|
|
548
745
|
);
|
|
@@ -662,8 +859,8 @@ var createCommand = new import_commander.Command("create").description("Create a
|
|
|
662
859
|
if (error instanceof Error) {
|
|
663
860
|
console.error(import_chalk3.default.red(error.message));
|
|
664
861
|
}
|
|
665
|
-
if (
|
|
666
|
-
|
|
862
|
+
if (fs3.existsSync(dest)) {
|
|
863
|
+
fs3.rmSync(dest, { recursive: true, force: true });
|
|
667
864
|
}
|
|
668
865
|
return;
|
|
669
866
|
}
|
|
@@ -682,8 +879,8 @@ var createCommand = new import_commander.Command("create").description("Create a
|
|
|
682
879
|
if (error instanceof Error) {
|
|
683
880
|
console.error(import_chalk3.default.red(error.message));
|
|
684
881
|
}
|
|
685
|
-
if (
|
|
686
|
-
|
|
882
|
+
if (fs3.existsSync(dest)) {
|
|
883
|
+
fs3.rmSync(dest, { recursive: true, force: true });
|
|
687
884
|
}
|
|
688
885
|
return;
|
|
689
886
|
}
|
|
@@ -734,54 +931,22 @@ var createCommand = new import_commander.Command("create").description("Create a
|
|
|
734
931
|
console.log();
|
|
735
932
|
});
|
|
736
933
|
|
|
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
934
|
// src/commands/link.ts
|
|
770
935
|
var import_prompts2 = require("@inquirer/prompts");
|
|
771
|
-
var
|
|
936
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
772
937
|
var import_commander3 = require("commander");
|
|
773
938
|
var import_ora3 = __toESM(require("ora"));
|
|
774
939
|
|
|
775
940
|
// src/utils/gitignore-updater.ts
|
|
776
|
-
var
|
|
777
|
-
var
|
|
941
|
+
var fs4 = __toESM(require("fs"));
|
|
942
|
+
var path4 = __toESM(require("path"));
|
|
778
943
|
function updateGitignore(entry) {
|
|
779
944
|
const cwd = process.cwd();
|
|
780
|
-
const gitignorePath =
|
|
945
|
+
const gitignorePath = path4.join(cwd, ".gitignore");
|
|
781
946
|
try {
|
|
782
947
|
let content = "";
|
|
783
|
-
if (
|
|
784
|
-
content =
|
|
948
|
+
if (fs4.existsSync(gitignorePath)) {
|
|
949
|
+
content = fs4.readFileSync(gitignorePath, "utf8");
|
|
785
950
|
const lines = content.split("\n");
|
|
786
951
|
for (const line of lines) {
|
|
787
952
|
const trimmed = line.trim();
|
|
@@ -795,7 +960,7 @@ function updateGitignore(entry) {
|
|
|
795
960
|
}
|
|
796
961
|
content += `${entry}
|
|
797
962
|
`;
|
|
798
|
-
|
|
963
|
+
fs4.writeFileSync(gitignorePath, content, "utf8");
|
|
799
964
|
return { success: true };
|
|
800
965
|
} catch (error) {
|
|
801
966
|
return {
|
|
@@ -806,20 +971,34 @@ function updateGitignore(entry) {
|
|
|
806
971
|
}
|
|
807
972
|
|
|
808
973
|
// src/commands/link.ts
|
|
809
|
-
var linkCommand = new import_commander3.Command("link").description(
|
|
974
|
+
var linkCommand = new import_commander3.Command("link").description(
|
|
975
|
+
"Link this project directory to a Commet organization. Creates .commet/config.json with the selected org."
|
|
976
|
+
).option(
|
|
977
|
+
"--org <slug-or-id>",
|
|
978
|
+
"Organization slug or ID \u2014 skips interactive selection"
|
|
979
|
+
).addHelpText(
|
|
980
|
+
"after",
|
|
981
|
+
`
|
|
982
|
+
Examples:
|
|
983
|
+
$ commet link Interactive \u2014 choose from a list
|
|
984
|
+
$ commet link --org acme Non-interactive \u2014 match by slug or ID
|
|
985
|
+
|
|
986
|
+
Use 'commet orgs' to see available organizations and their slugs.
|
|
987
|
+
`
|
|
988
|
+
).action(async (options) => {
|
|
810
989
|
if (!authExists()) {
|
|
811
|
-
console.log(
|
|
812
|
-
console.log(
|
|
813
|
-
|
|
990
|
+
console.log(import_chalk4.default.red("\u2717 Not authenticated"));
|
|
991
|
+
console.log(import_chalk4.default.dim("Run `commet login` first"));
|
|
992
|
+
process.exit(1);
|
|
814
993
|
}
|
|
815
994
|
if (projectConfigExists()) {
|
|
816
995
|
const config = loadProjectConfig();
|
|
817
|
-
console.log(
|
|
996
|
+
console.log(import_chalk4.default.yellow("\u26A0 This project is already linked"));
|
|
818
997
|
console.log(
|
|
819
|
-
|
|
998
|
+
import_chalk4.default.dim(`Organization: ${config?.orgName} \xB7 ${config?.mode}`)
|
|
820
999
|
);
|
|
821
1000
|
console.log(
|
|
822
|
-
|
|
1001
|
+
import_chalk4.default.dim(
|
|
823
1002
|
"\nRun `commet unlink` first if you want to change the organization"
|
|
824
1003
|
)
|
|
825
1004
|
);
|
|
@@ -831,37 +1010,52 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
|
|
|
831
1010
|
);
|
|
832
1011
|
if (result.error || !result.data) {
|
|
833
1012
|
spinner.fail("Failed to fetch organizations");
|
|
834
|
-
console.error(
|
|
835
|
-
|
|
1013
|
+
console.error(import_chalk4.default.red("Error:"), result.error);
|
|
1014
|
+
process.exit(1);
|
|
836
1015
|
}
|
|
837
1016
|
const { organizations } = result.data;
|
|
838
1017
|
if (organizations.length === 0) {
|
|
839
1018
|
spinner.stop();
|
|
840
|
-
console.log(
|
|
1019
|
+
console.log(import_chalk4.default.yellow("\u26A0 No organizations found"));
|
|
841
1020
|
console.log(
|
|
842
|
-
|
|
1021
|
+
import_chalk4.default.dim("Create an organization at https://commet.co first")
|
|
843
1022
|
);
|
|
844
1023
|
return;
|
|
845
1024
|
}
|
|
846
1025
|
spinner.stop();
|
|
847
|
-
let
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1026
|
+
let selectedOrg;
|
|
1027
|
+
if (options.org) {
|
|
1028
|
+
selectedOrg = organizations.find(
|
|
1029
|
+
(org) => org.slug === options.org || org.id === options.org
|
|
1030
|
+
);
|
|
1031
|
+
if (!selectedOrg) {
|
|
1032
|
+
console.log(import_chalk4.default.red(`\u2717 Organization "${options.org}" not found`));
|
|
1033
|
+
console.log(import_chalk4.default.dim("\nAvailable organizations:"));
|
|
1034
|
+
for (const org of organizations) {
|
|
1035
|
+
console.log(import_chalk4.default.dim(` ${org.slug} (${org.mode})`));
|
|
1036
|
+
}
|
|
1037
|
+
process.exit(1);
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
let orgId;
|
|
1041
|
+
try {
|
|
1042
|
+
orgId = await (0, import_prompts2.select)({
|
|
1043
|
+
message: "Select organization:",
|
|
1044
|
+
choices: organizations.map((org) => ({
|
|
1045
|
+
name: `${org.name} ${import_chalk4.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
|
|
1046
|
+
value: org.id
|
|
1047
|
+
})),
|
|
1048
|
+
theme: promptTheme
|
|
1049
|
+
});
|
|
1050
|
+
} catch (_error) {
|
|
1051
|
+
console.log(import_chalk4.default.yellow("\n\u26A0 Link cancelled"));
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
selectedOrg = organizations.find((org) => org.id === orgId);
|
|
1055
|
+
if (!selectedOrg) {
|
|
1056
|
+
console.log(import_chalk4.default.red("\u2717 Organization not found"));
|
|
1057
|
+
process.exit(1);
|
|
1058
|
+
}
|
|
865
1059
|
}
|
|
866
1060
|
saveProjectConfig({
|
|
867
1061
|
orgId: selectedOrg.id,
|
|
@@ -869,119 +1063,171 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
|
|
|
869
1063
|
mode: selectedOrg.mode
|
|
870
1064
|
});
|
|
871
1065
|
const gitignoreResult = updateGitignore(".commet/");
|
|
872
|
-
console.log(
|
|
1066
|
+
console.log(import_chalk4.default.green("\n\u2713 Project linked successfully"));
|
|
873
1067
|
console.log(
|
|
874
|
-
|
|
1068
|
+
import_chalk4.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
875
1069
|
);
|
|
876
1070
|
if (gitignoreResult.success) {
|
|
877
|
-
console.log(
|
|
1071
|
+
console.log(import_chalk4.default.green("\u2713 Updated .gitignore"));
|
|
878
1072
|
} else {
|
|
879
|
-
console.log(
|
|
880
|
-
console.log(
|
|
1073
|
+
console.log(import_chalk4.default.yellow("\u26A0 No .gitignore found"));
|
|
1074
|
+
console.log(import_chalk4.default.dim("Add .commet/ to your .gitignore file"));
|
|
881
1075
|
}
|
|
882
|
-
console.log(
|
|
1076
|
+
console.log(import_chalk4.default.dim("\nRun 'commet pull' to generate TypeScript types"));
|
|
883
1077
|
});
|
|
884
1078
|
|
|
885
1079
|
// src/commands/list.ts
|
|
886
|
-
var
|
|
1080
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
887
1081
|
var import_commander4 = require("commander");
|
|
888
1082
|
var import_ora4 = __toESM(require("ora"));
|
|
889
1083
|
var validTypes = ["features", "seats", "plans"];
|
|
890
|
-
var listCommand = new import_commander4.Command("list").description(
|
|
1084
|
+
var listCommand = new import_commander4.Command("list").description(
|
|
1085
|
+
"List resources from your linked organization. Shows features, seat types, or plans currently configured on remote."
|
|
1086
|
+
).argument("<type>", "What to list: features, seats, or plans").option("--json", "Output as JSON array").addHelpText(
|
|
1087
|
+
"after",
|
|
1088
|
+
`
|
|
1089
|
+
Examples:
|
|
1090
|
+
$ commet list features Show all features with type and description
|
|
1091
|
+
$ commet list plans Show all plans
|
|
1092
|
+
$ commet list seats Show all seat types
|
|
1093
|
+
$ commet list features --json JSON array for agent/CI use
|
|
1094
|
+
`
|
|
1095
|
+
).action(async (type, options) => {
|
|
1096
|
+
const jsonMode = options.json;
|
|
891
1097
|
if (!validTypes.includes(type)) {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1098
|
+
if (jsonMode) {
|
|
1099
|
+
console.log(
|
|
1100
|
+
JSON.stringify({
|
|
1101
|
+
error: `Invalid type "${type}". Use: features, seats, or plans`
|
|
1102
|
+
})
|
|
1103
|
+
);
|
|
1104
|
+
} else {
|
|
1105
|
+
console.log(
|
|
1106
|
+
import_chalk5.default.red('\u2717 Invalid type. Use "features", "seats", or "plans"')
|
|
1107
|
+
);
|
|
1108
|
+
console.log(import_chalk5.default.dim("\nExamples:"));
|
|
1109
|
+
console.log(import_chalk5.default.dim(" commet list features"));
|
|
1110
|
+
console.log(import_chalk5.default.dim(" commet list seats"));
|
|
1111
|
+
console.log(import_chalk5.default.dim(" commet list plans"));
|
|
1112
|
+
}
|
|
1113
|
+
process.exit(1);
|
|
900
1114
|
}
|
|
901
1115
|
if (!authExists()) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1116
|
+
if (jsonMode) {
|
|
1117
|
+
console.log(JSON.stringify({ error: "Not authenticated" }));
|
|
1118
|
+
} else {
|
|
1119
|
+
console.log(import_chalk5.default.red("\u2717 Not authenticated"));
|
|
1120
|
+
console.log(import_chalk5.default.dim("Run `commet login` first"));
|
|
1121
|
+
}
|
|
1122
|
+
process.exit(1);
|
|
905
1123
|
}
|
|
906
1124
|
if (!projectConfigExists()) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1125
|
+
if (jsonMode) {
|
|
1126
|
+
console.log(JSON.stringify({ error: "Project not linked" }));
|
|
1127
|
+
} else {
|
|
1128
|
+
console.log(import_chalk5.default.red("\u2717 Project not linked"));
|
|
1129
|
+
console.log(
|
|
1130
|
+
import_chalk5.default.dim("Run `commet link` first to connect to an organization")
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
process.exit(1);
|
|
912
1134
|
}
|
|
913
1135
|
const projectConfig = loadProjectConfig();
|
|
914
1136
|
if (!projectConfig) {
|
|
915
|
-
|
|
916
|
-
|
|
1137
|
+
if (jsonMode) {
|
|
1138
|
+
console.log(JSON.stringify({ error: "Invalid project configuration" }));
|
|
1139
|
+
} else {
|
|
1140
|
+
console.log(import_chalk5.default.red("\u2717 Invalid project configuration"));
|
|
1141
|
+
}
|
|
1142
|
+
process.exit(1);
|
|
917
1143
|
}
|
|
918
|
-
const spinner = (0, import_ora4.default)(`Fetching ${type}...`).start();
|
|
1144
|
+
const spinner = jsonMode ? null : (0, import_ora4.default)(`Fetching ${type}...`).start();
|
|
919
1145
|
const result = await apiRequest(
|
|
920
1146
|
`${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
|
|
921
1147
|
);
|
|
922
1148
|
if (result.error || !result.data) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1149
|
+
if (jsonMode) {
|
|
1150
|
+
console.log(JSON.stringify({ error: result.error }));
|
|
1151
|
+
} else {
|
|
1152
|
+
spinner?.fail(`Failed to fetch ${type}`);
|
|
1153
|
+
console.error(import_chalk5.default.red("Error:"), result.error);
|
|
1154
|
+
}
|
|
1155
|
+
process.exit(1);
|
|
926
1156
|
}
|
|
927
|
-
spinner
|
|
1157
|
+
spinner?.stop();
|
|
928
1158
|
if (type === "features") {
|
|
929
1159
|
const { features } = result.data;
|
|
1160
|
+
if (jsonMode) {
|
|
1161
|
+
console.log(JSON.stringify(features));
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
930
1164
|
if (features.length === 0) {
|
|
931
|
-
console.log(
|
|
1165
|
+
console.log(import_chalk5.default.yellow("\u26A0 No features found"));
|
|
932
1166
|
console.log(
|
|
933
|
-
|
|
1167
|
+
import_chalk5.default.dim("Create features in your Commet dashboard first")
|
|
934
1168
|
);
|
|
935
1169
|
return;
|
|
936
1170
|
}
|
|
937
|
-
console.log(
|
|
938
|
-
|
|
1171
|
+
console.log(import_chalk5.default.bold(`
|
|
1172
|
+
Features (${features.length})
|
|
939
1173
|
`));
|
|
940
1174
|
for (const feature of features) {
|
|
941
|
-
console.log(
|
|
942
|
-
|
|
1175
|
+
console.log(
|
|
1176
|
+
import_chalk5.default.green(` ${feature.code} ${import_chalk5.default.dim(`(${feature.type})`)}`)
|
|
1177
|
+
);
|
|
1178
|
+
console.log(import_chalk5.default.dim(` ${feature.name}`));
|
|
943
1179
|
if (feature.description) {
|
|
944
|
-
console.log(
|
|
1180
|
+
console.log(import_chalk5.default.dim(` ${feature.description}`));
|
|
945
1181
|
}
|
|
946
1182
|
console.log("");
|
|
947
1183
|
}
|
|
948
1184
|
} else if (type === "seats") {
|
|
949
1185
|
const { seatTypes } = result.data;
|
|
1186
|
+
if (jsonMode) {
|
|
1187
|
+
console.log(JSON.stringify(seatTypes));
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
950
1190
|
if (seatTypes.length === 0) {
|
|
951
|
-
console.log(
|
|
1191
|
+
console.log(import_chalk5.default.yellow("\u26A0 No seat types found"));
|
|
952
1192
|
console.log(
|
|
953
|
-
|
|
1193
|
+
import_chalk5.default.dim("Create seat types in your Commet dashboard first")
|
|
954
1194
|
);
|
|
955
1195
|
return;
|
|
956
1196
|
}
|
|
957
|
-
console.log(
|
|
958
|
-
|
|
1197
|
+
console.log(import_chalk5.default.bold(`
|
|
1198
|
+
Seat Types (${seatTypes.length})
|
|
959
1199
|
`));
|
|
960
1200
|
for (const seatType of seatTypes) {
|
|
961
1201
|
console.log(
|
|
962
|
-
|
|
1202
|
+
import_chalk5.default.green(
|
|
1203
|
+
` ${seatType.code}${seatType.isFree ? import_chalk5.default.dim(" (free)") : ""}`
|
|
1204
|
+
)
|
|
963
1205
|
);
|
|
964
|
-
console.log(
|
|
1206
|
+
console.log(import_chalk5.default.dim(` ${seatType.name}`));
|
|
965
1207
|
if (seatType.description) {
|
|
966
|
-
console.log(
|
|
1208
|
+
console.log(import_chalk5.default.dim(` ${seatType.description}`));
|
|
967
1209
|
}
|
|
968
1210
|
console.log("");
|
|
969
1211
|
}
|
|
970
1212
|
} else {
|
|
971
1213
|
const { plans } = result.data;
|
|
1214
|
+
if (jsonMode) {
|
|
1215
|
+
console.log(JSON.stringify(plans));
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
972
1218
|
if (plans.length === 0) {
|
|
973
|
-
console.log(
|
|
974
|
-
console.log(
|
|
1219
|
+
console.log(import_chalk5.default.yellow("\u26A0 No plans found"));
|
|
1220
|
+
console.log(import_chalk5.default.dim("Create plans in your Commet dashboard first"));
|
|
975
1221
|
return;
|
|
976
1222
|
}
|
|
977
|
-
console.log(
|
|
978
|
-
|
|
1223
|
+
console.log(import_chalk5.default.bold(`
|
|
1224
|
+
Plans (${plans.length})
|
|
979
1225
|
`));
|
|
980
1226
|
for (const plan of plans) {
|
|
981
|
-
console.log(
|
|
982
|
-
console.log(
|
|
1227
|
+
console.log(import_chalk5.default.green(` ${plan.code}`));
|
|
1228
|
+
console.log(import_chalk5.default.dim(` ${plan.name}`));
|
|
983
1229
|
if (plan.description) {
|
|
984
|
-
console.log(
|
|
1230
|
+
console.log(import_chalk5.default.dim(` ${plan.description}`));
|
|
985
1231
|
}
|
|
986
1232
|
console.log("");
|
|
987
1233
|
}
|
|
@@ -990,21 +1236,21 @@ var listCommand = new import_commander4.Command("list").description("List featur
|
|
|
990
1236
|
|
|
991
1237
|
// src/commands/listen.ts
|
|
992
1238
|
var import_ably = __toESM(require("ably"));
|
|
993
|
-
var
|
|
1239
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
994
1240
|
var import_commander5 = require("commander");
|
|
995
1241
|
function printEventLine(line) {
|
|
996
1242
|
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
997
|
-
const eventName =
|
|
998
|
-
const timing =
|
|
1243
|
+
const eventName = import_chalk6.default.yellow(line.event.padEnd(28));
|
|
1244
|
+
const timing = import_chalk6.default.dim(`(${line.ms}ms)`);
|
|
999
1245
|
if ("error" in line) {
|
|
1000
1246
|
console.log(
|
|
1001
|
-
` ${
|
|
1247
|
+
` ${import_chalk6.default.dim(time)} ${eventName} \u2192 ${import_chalk6.default.red("Error")} ${timing}`
|
|
1002
1248
|
);
|
|
1003
|
-
console.log(` ${" ".repeat(12)}${
|
|
1249
|
+
console.log(` ${" ".repeat(12)}${import_chalk6.default.red(line.error)}`);
|
|
1004
1250
|
return;
|
|
1005
1251
|
}
|
|
1006
|
-
const status = line.statusCode < 400 ?
|
|
1007
|
-
console.log(` ${
|
|
1252
|
+
const status = line.statusCode < 400 ? import_chalk6.default.green(`${line.statusCode} OK`) : import_chalk6.default.red(`${line.statusCode} Error`);
|
|
1253
|
+
console.log(` ${import_chalk6.default.dim(time)} ${eventName} \u2192 ${status} ${timing}`);
|
|
1008
1254
|
}
|
|
1009
1255
|
function isListenMessage(data) {
|
|
1010
1256
|
if (typeof data !== "object" || data === null) return false;
|
|
@@ -1027,18 +1273,31 @@ function resolveTargetUrl(input2) {
|
|
|
1027
1273
|
}
|
|
1028
1274
|
return parsed.toString();
|
|
1029
1275
|
}
|
|
1030
|
-
var listenCommand = new import_commander5.Command("listen").description(
|
|
1276
|
+
var listenCommand = new import_commander5.Command("listen").description(
|
|
1277
|
+
"Forward webhook events from Commet to your local server in real time. Opens a persistent connection and replays every event as an HTTP POST to your URL."
|
|
1278
|
+
).argument(
|
|
1031
1279
|
"<url>",
|
|
1032
|
-
"URL
|
|
1033
|
-
).option(
|
|
1280
|
+
"Target URL \u2014 a port (3000), host:port (localhost:3000), or full URL"
|
|
1281
|
+
).option(
|
|
1282
|
+
"--events <types>",
|
|
1283
|
+
"Only forward these event types (comma-separated)"
|
|
1284
|
+
).addHelpText(
|
|
1285
|
+
"after",
|
|
1286
|
+
`
|
|
1287
|
+
Examples:
|
|
1288
|
+
$ commet listen 3000 Forward to http://localhost:3000/
|
|
1289
|
+
$ commet listen localhost:3000/webhooks Forward to a specific path
|
|
1290
|
+
$ commet listen 3000 --events invoice.paid Only forward invoice.paid events
|
|
1291
|
+
`
|
|
1292
|
+
).action(async (url, options) => {
|
|
1034
1293
|
const auth = loadAuth();
|
|
1035
1294
|
if (!auth) {
|
|
1036
|
-
console.log(
|
|
1295
|
+
console.log(import_chalk6.default.red("Not authenticated. Run: commet login"));
|
|
1037
1296
|
process.exit(1);
|
|
1038
1297
|
}
|
|
1039
1298
|
const projectConfig = loadProjectConfig();
|
|
1040
1299
|
if (!projectConfig) {
|
|
1041
|
-
console.log(
|
|
1300
|
+
console.log(import_chalk6.default.red("No project linked. Run: commet link"));
|
|
1042
1301
|
process.exit(1);
|
|
1043
1302
|
}
|
|
1044
1303
|
let targetUrl;
|
|
@@ -1046,7 +1305,7 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
|
|
|
1046
1305
|
targetUrl = resolveTargetUrl(url);
|
|
1047
1306
|
} catch (error) {
|
|
1048
1307
|
console.log(
|
|
1049
|
-
|
|
1308
|
+
import_chalk6.default.red(error instanceof Error ? error.message : "Invalid URL")
|
|
1050
1309
|
);
|
|
1051
1310
|
process.exit(1);
|
|
1052
1311
|
}
|
|
@@ -1059,9 +1318,9 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
|
|
|
1059
1318
|
}
|
|
1060
1319
|
);
|
|
1061
1320
|
if (result.error || !result.data) {
|
|
1062
|
-
console.log(
|
|
1321
|
+
console.log(import_chalk6.default.red("Failed to start listen session"));
|
|
1063
1322
|
if (result.error) {
|
|
1064
|
-
console.log(
|
|
1323
|
+
console.log(import_chalk6.default.dim(result.error));
|
|
1065
1324
|
}
|
|
1066
1325
|
process.exit(1);
|
|
1067
1326
|
}
|
|
@@ -1107,27 +1366,27 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
|
|
|
1107
1366
|
let wasConnected = false;
|
|
1108
1367
|
ably.connection.on("connected", () => {
|
|
1109
1368
|
if (wasConnected) {
|
|
1110
|
-
console.log(
|
|
1369
|
+
console.log(import_chalk6.default.green(" \u2713 Reconnected"));
|
|
1111
1370
|
}
|
|
1112
1371
|
wasConnected = true;
|
|
1113
1372
|
});
|
|
1114
1373
|
ably.connection.on("disconnected", () => {
|
|
1115
|
-
console.log(
|
|
1374
|
+
console.log(import_chalk6.default.yellow("\n \u26A0 Disconnected. Reconnecting..."));
|
|
1116
1375
|
});
|
|
1117
1376
|
ably.connection.on("failed", () => {
|
|
1118
1377
|
console.log(
|
|
1119
|
-
|
|
1378
|
+
import_chalk6.default.red("\n \u2717 Connection failed. Check your authentication.")
|
|
1120
1379
|
);
|
|
1121
1380
|
process.exit(1);
|
|
1122
1381
|
});
|
|
1123
1382
|
const channel = ably.channels.get(channelName);
|
|
1124
1383
|
console.log("");
|
|
1125
1384
|
console.log(
|
|
1126
|
-
|
|
1385
|
+
import_chalk6.default.green(` \u2713 Authenticated (org: ${projectConfig.orgName})`)
|
|
1127
1386
|
);
|
|
1128
|
-
console.log(
|
|
1129
|
-
console.log(
|
|
1130
|
-
console.log(
|
|
1387
|
+
console.log(import_chalk6.default.green(" \u2713 Connected to Commet webhook stream"));
|
|
1388
|
+
console.log(import_chalk6.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
|
|
1389
|
+
console.log(import_chalk6.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
|
|
1131
1390
|
console.log("");
|
|
1132
1391
|
console.log(" Ready! Listening for webhook events...");
|
|
1133
1392
|
console.log("");
|
|
@@ -1158,7 +1417,7 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
|
|
|
1158
1417
|
process.on("SIGINT", async () => {
|
|
1159
1418
|
if (isShuttingDown) return;
|
|
1160
1419
|
isShuttingDown = true;
|
|
1161
|
-
console.log(
|
|
1420
|
+
console.log(import_chalk6.default.dim("\n Disconnecting..."));
|
|
1162
1421
|
ably.close();
|
|
1163
1422
|
await apiRequest(`${BASE_URL}/api/cli/listen/stop`, {
|
|
1164
1423
|
method: "POST",
|
|
@@ -1172,13 +1431,15 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
|
|
|
1172
1431
|
});
|
|
1173
1432
|
|
|
1174
1433
|
// src/commands/login.ts
|
|
1175
|
-
var
|
|
1434
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
1176
1435
|
var import_commander6 = require("commander");
|
|
1177
|
-
var loginCommand = new import_commander6.Command("login").description(
|
|
1436
|
+
var loginCommand = new import_commander6.Command("login").description(
|
|
1437
|
+
"Authenticate with Commet via browser. Opens a device-code flow \u2014 you confirm in the browser and the CLI stores your token locally at ~/.commet/auth.json."
|
|
1438
|
+
).action(async () => {
|
|
1178
1439
|
if (authExists()) {
|
|
1179
|
-
console.log(
|
|
1440
|
+
console.log(import_chalk7.default.yellow("\u26A0 You are already logged in."));
|
|
1180
1441
|
console.log(
|
|
1181
|
-
|
|
1442
|
+
import_chalk7.default.dim(
|
|
1182
1443
|
"Run `commet logout` first if you want to login with a different account."
|
|
1183
1444
|
)
|
|
1184
1445
|
);
|
|
@@ -1186,9 +1447,9 @@ var loginCommand = new import_commander6.Command("login").description("Authentic
|
|
|
1186
1447
|
}
|
|
1187
1448
|
const success = await performLogin();
|
|
1188
1449
|
if (success) {
|
|
1189
|
-
console.log(
|
|
1450
|
+
console.log(import_chalk7.default.green("\n\u2713 Authentication complete"));
|
|
1190
1451
|
console.log(
|
|
1191
|
-
|
|
1452
|
+
import_chalk7.default.dim(
|
|
1192
1453
|
"\nNext steps:\n 1. Run `commet link` to connect a project\n 2. Run `commet pull` to generate types\n"
|
|
1193
1454
|
)
|
|
1194
1455
|
);
|
|
@@ -1196,134 +1457,93 @@ var loginCommand = new import_commander6.Command("login").description("Authentic
|
|
|
1196
1457
|
});
|
|
1197
1458
|
|
|
1198
1459
|
// src/commands/logout.ts
|
|
1199
|
-
var
|
|
1460
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
1200
1461
|
var import_commander7 = require("commander");
|
|
1201
|
-
var logoutCommand = new import_commander7.Command("logout").description(
|
|
1462
|
+
var logoutCommand = new import_commander7.Command("logout").description(
|
|
1463
|
+
"Log out and remove stored credentials from ~/.commet/auth.json."
|
|
1464
|
+
).action(async () => {
|
|
1202
1465
|
if (!authExists()) {
|
|
1203
|
-
console.log(
|
|
1466
|
+
console.log(import_chalk8.default.yellow("\u26A0 You are not logged in."));
|
|
1204
1467
|
return;
|
|
1205
1468
|
}
|
|
1206
1469
|
clearAuth();
|
|
1207
|
-
console.log(
|
|
1470
|
+
console.log(import_chalk8.default.green("\u2713 Successfully logged out"));
|
|
1208
1471
|
});
|
|
1209
1472
|
|
|
1210
|
-
// src/commands/
|
|
1211
|
-
var
|
|
1212
|
-
var path5 = __toESM(require("path"));
|
|
1213
|
-
var import_prompts3 = require("@inquirer/prompts");
|
|
1214
|
-
var import_chalk11 = __toESM(require("chalk"));
|
|
1473
|
+
// src/commands/orgs.ts
|
|
1474
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
1215
1475
|
var import_commander8 = require("commander");
|
|
1216
1476
|
var import_ora5 = __toESM(require("ora"));
|
|
1477
|
+
var orgsCommand = new import_commander8.Command("orgs").description(
|
|
1478
|
+
"List all organizations you have access to. Shows name, slug, mode (live/sandbox), and which one is currently linked."
|
|
1479
|
+
).option("--json", "Output as JSON array").addHelpText(
|
|
1480
|
+
"after",
|
|
1481
|
+
`
|
|
1482
|
+
Examples:
|
|
1483
|
+
$ commet orgs Show orgs with current selection marked
|
|
1484
|
+
$ commet orgs --json JSON array for agent/CI use
|
|
1217
1485
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
for (const name of CONFIG_NAMES) {
|
|
1229
|
-
const fullPath = path4.resolve(cwd, name);
|
|
1230
|
-
if (fs4.existsSync(fullPath)) {
|
|
1231
|
-
return fullPath;
|
|
1486
|
+
The slug shown here is what you pass to 'commet link --org <slug>'.
|
|
1487
|
+
`
|
|
1488
|
+
).action(async (options) => {
|
|
1489
|
+
const jsonMode = options.json;
|
|
1490
|
+
if (!authExists()) {
|
|
1491
|
+
if (jsonMode) {
|
|
1492
|
+
console.log(JSON.stringify({ error: "Not authenticated" }));
|
|
1493
|
+
} else {
|
|
1494
|
+
console.log(import_chalk9.default.red("\u2717 Not authenticated"));
|
|
1495
|
+
console.log(import_chalk9.default.dim("Run `commet login` first"));
|
|
1232
1496
|
}
|
|
1497
|
+
process.exit(1);
|
|
1233
1498
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
if (!
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1499
|
+
const spinner = jsonMode ? null : (0, import_ora5.default)("Fetching organizations...").start();
|
|
1500
|
+
const result = await apiRequest(
|
|
1501
|
+
`${BASE_URL}/api/cli/organizations`
|
|
1502
|
+
);
|
|
1503
|
+
if (result.error || !result.data) {
|
|
1504
|
+
if (jsonMode) {
|
|
1505
|
+
console.log(JSON.stringify({ error: result.error }));
|
|
1506
|
+
} else {
|
|
1507
|
+
spinner?.fail("Failed to fetch organizations");
|
|
1508
|
+
console.error(import_chalk9.default.red("Error:"), result.error);
|
|
1509
|
+
}
|
|
1510
|
+
process.exit(1);
|
|
1242
1511
|
}
|
|
1243
|
-
|
|
1244
|
-
const
|
|
1245
|
-
if (
|
|
1246
|
-
|
|
1512
|
+
spinner?.stop();
|
|
1513
|
+
const { organizations } = result.data;
|
|
1514
|
+
if (jsonMode) {
|
|
1515
|
+
console.log(JSON.stringify(organizations));
|
|
1516
|
+
return;
|
|
1247
1517
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1518
|
+
if (organizations.length === 0) {
|
|
1519
|
+
console.log(import_chalk9.default.yellow("\u26A0 No organizations found"));
|
|
1520
|
+
console.log(
|
|
1521
|
+
import_chalk9.default.dim("Create an organization at https://commet.co first")
|
|
1252
1522
|
);
|
|
1523
|
+
return;
|
|
1253
1524
|
}
|
|
1254
|
-
const
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
]);
|
|
1266
|
-
function validateConfig(config, configPath) {
|
|
1267
|
-
if (!config || typeof config !== "object") {
|
|
1268
|
-
throw new Error(`${configPath}: config must be an object`);
|
|
1269
|
-
}
|
|
1270
|
-
if (!config.features || typeof config.features !== "object") {
|
|
1271
|
-
throw new Error(`${configPath}: config.features must be an object`);
|
|
1272
|
-
}
|
|
1273
|
-
if (!config.plans || typeof config.plans !== "object") {
|
|
1274
|
-
throw new Error(`${configPath}: config.plans must be an object`);
|
|
1275
|
-
}
|
|
1276
|
-
for (const [code, feature] of Object.entries(config.features)) {
|
|
1277
|
-
if (!feature.name || typeof feature.name !== "string") {
|
|
1278
|
-
throw new Error(`Feature "${code}": name is required`);
|
|
1279
|
-
}
|
|
1280
|
-
if (!VALID_FEATURE_TYPES.has(feature.type)) {
|
|
1281
|
-
throw new Error(
|
|
1282
|
-
`Feature "${code}": type must be one of: boolean, usage, seats`
|
|
1283
|
-
);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
for (const [code, plan] of Object.entries(config.plans)) {
|
|
1287
|
-
if (!plan.name || typeof plan.name !== "string") {
|
|
1288
|
-
throw new Error(`Plan "${code}": name is required`);
|
|
1289
|
-
}
|
|
1290
|
-
if (!Array.isArray(plan.prices)) {
|
|
1291
|
-
throw new Error(`Plan "${code}": prices must be an array`);
|
|
1292
|
-
}
|
|
1293
|
-
for (const price of plan.prices) {
|
|
1294
|
-
if (!VALID_INTERVALS.has(price.interval)) {
|
|
1295
|
-
throw new Error(
|
|
1296
|
-
`Plan "${code}": price interval "${price.interval}" is not valid`
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
if (typeof price.amount !== "number") {
|
|
1300
|
-
throw new Error(`Plan "${code}": price amount must be a number`);
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
if (plan.prices.length > 0) {
|
|
1304
|
-
if (!plan.defaultInterval) {
|
|
1305
|
-
throw new Error(
|
|
1306
|
-
`Plan "${code}": defaultInterval is required when prices are defined`
|
|
1307
|
-
);
|
|
1308
|
-
}
|
|
1309
|
-
const priceIntervals = new Set(plan.prices.map((p) => p.interval));
|
|
1310
|
-
if (!priceIntervals.has(plan.defaultInterval)) {
|
|
1311
|
-
throw new Error(
|
|
1312
|
-
`Plan "${code}": defaultInterval "${plan.defaultInterval}" does not match any price interval (${[...priceIntervals].join(", ")})`
|
|
1313
|
-
);
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
if (plan.features) {
|
|
1317
|
-
for (const featureCode of Object.keys(plan.features)) {
|
|
1318
|
-
if (!config.features[featureCode]) {
|
|
1319
|
-
throw new Error(
|
|
1320
|
-
`Plan "${code}": references feature "${featureCode}" which is not defined in config.features`
|
|
1321
|
-
);
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1525
|
+
const currentProject = loadProjectConfig();
|
|
1526
|
+
console.log(import_chalk9.default.bold(`
|
|
1527
|
+
Organizations (${organizations.length})
|
|
1528
|
+
`));
|
|
1529
|
+
for (const org of organizations) {
|
|
1530
|
+
const isCurrent = currentProject?.orgId === org.id;
|
|
1531
|
+
const marker = isCurrent ? import_chalk9.default.green("\u25CF") : import_chalk9.default.dim("\u25CB");
|
|
1532
|
+
const mode = org.mode === "live" ? import_chalk9.default.green(org.mode) : import_chalk9.default.yellow(org.mode);
|
|
1533
|
+
console.log(
|
|
1534
|
+
` ${marker} ${org.name} ${import_chalk9.default.dim(`(${org.slug})`)} ${mode}`
|
|
1535
|
+
);
|
|
1325
1536
|
}
|
|
1326
|
-
|
|
1537
|
+
console.log("");
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
// src/commands/pull.ts
|
|
1541
|
+
var fs5 = __toESM(require("fs"));
|
|
1542
|
+
var path5 = __toESM(require("path"));
|
|
1543
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
1544
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1545
|
+
var import_commander9 = require("commander");
|
|
1546
|
+
var import_ora6 = __toESM(require("ora"));
|
|
1327
1547
|
|
|
1328
1548
|
// src/utils/diff.ts
|
|
1329
1549
|
var import_chalk10 = __toESM(require("chalk"));
|
|
@@ -1532,7 +1752,18 @@ function generateConfigFile(features, plans) {
|
|
|
1532
1752
|
}
|
|
1533
1753
|
|
|
1534
1754
|
// src/commands/pull.ts
|
|
1535
|
-
var pullCommand = new
|
|
1755
|
+
var pullCommand = new import_commander9.Command("pull").description(
|
|
1756
|
+
"Fetch your billing config from Commet and generate (or update) commet.config.ts with features and plans."
|
|
1757
|
+
).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without writing any files").option("--json", "Output structured JSON (no colors, no prompts)").addHelpText(
|
|
1758
|
+
"after",
|
|
1759
|
+
`
|
|
1760
|
+
Examples:
|
|
1761
|
+
$ commet pull Interactive \u2014 shows diff, asks to confirm
|
|
1762
|
+
$ commet pull --dry-run Preview changes without applying
|
|
1763
|
+
$ commet pull --yes Apply without confirmation
|
|
1764
|
+
$ commet pull --json --yes Agent/CI \u2014 structured JSON, no prompts
|
|
1765
|
+
`
|
|
1766
|
+
).action(async (options) => {
|
|
1536
1767
|
const jsonMode = options.json;
|
|
1537
1768
|
if (!authExists()) {
|
|
1538
1769
|
if (jsonMode) {
|
|
@@ -1563,7 +1794,7 @@ var pullCommand = new import_commander8.Command("pull").description("Pull config
|
|
|
1563
1794
|
}
|
|
1564
1795
|
process.exit(1);
|
|
1565
1796
|
}
|
|
1566
|
-
const spinner = jsonMode ? null : (0,
|
|
1797
|
+
const spinner = jsonMode ? null : (0, import_ora6.default)("Fetching config from remote...").start();
|
|
1567
1798
|
const result = await apiRequest(
|
|
1568
1799
|
`${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
|
|
1569
1800
|
);
|
|
@@ -1778,9 +2009,24 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
|
|
|
1778
2009
|
// src/commands/push.ts
|
|
1779
2010
|
var import_prompts4 = require("@inquirer/prompts");
|
|
1780
2011
|
var import_chalk12 = __toESM(require("chalk"));
|
|
1781
|
-
var
|
|
1782
|
-
var
|
|
1783
|
-
var pushCommand = new
|
|
2012
|
+
var import_commander10 = require("commander");
|
|
2013
|
+
var import_ora7 = __toESM(require("ora"));
|
|
2014
|
+
var pushCommand = new import_commander10.Command("push").description(
|
|
2015
|
+
"Push your local commet.config.ts to Commet. Creates or updates features and plans to match your config file."
|
|
2016
|
+
).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without pushing").option("--json", "Output structured JSON (no colors, no prompts)").addHelpText(
|
|
2017
|
+
"after",
|
|
2018
|
+
`
|
|
2019
|
+
Examples:
|
|
2020
|
+
$ commet push Interactive \u2014 shows diff, asks to confirm
|
|
2021
|
+
$ commet push --dry-run Preview what would change on remote
|
|
2022
|
+
$ commet push --yes Push without confirmation
|
|
2023
|
+
$ commet push --json --yes Agent/CI \u2014 structured JSON, no prompts
|
|
2024
|
+
|
|
2025
|
+
Notes:
|
|
2026
|
+
Feature types (boolean, usage, seats) cannot be changed via push.
|
|
2027
|
+
Resources in remote but not in your config are left as-is (not deleted).
|
|
2028
|
+
`
|
|
2029
|
+
).action(async (options) => {
|
|
1784
2030
|
const jsonMode = options.json;
|
|
1785
2031
|
if (!authExists()) {
|
|
1786
2032
|
if (jsonMode) {
|
|
@@ -1811,7 +2057,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
|
|
|
1811
2057
|
}
|
|
1812
2058
|
process.exit(1);
|
|
1813
2059
|
}
|
|
1814
|
-
const loadSpinner = jsonMode ? null : (0,
|
|
2060
|
+
const loadSpinner = jsonMode ? null : (0, import_ora7.default)("Loading commet.config.ts...").start();
|
|
1815
2061
|
const loaded = await loadBillingConfig(process.cwd()).catch(
|
|
1816
2062
|
(error) => {
|
|
1817
2063
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1827,7 +2073,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
|
|
|
1827
2073
|
if (!loaded) process.exit(1);
|
|
1828
2074
|
const { config, configPath } = loaded;
|
|
1829
2075
|
loadSpinner?.succeed(`Loaded ${configPath}`);
|
|
1830
|
-
const fetchSpinner = jsonMode ? null : (0,
|
|
2076
|
+
const fetchSpinner = jsonMode ? null : (0, import_ora7.default)("Fetching remote state...").start();
|
|
1831
2077
|
const remoteResult = await apiRequest(
|
|
1832
2078
|
`${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
|
|
1833
2079
|
);
|
|
@@ -1903,7 +2149,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
|
|
|
1903
2149
|
return;
|
|
1904
2150
|
}
|
|
1905
2151
|
}
|
|
1906
|
-
const pushSpinner = jsonMode ? null : (0,
|
|
2152
|
+
const pushSpinner = jsonMode ? null : (0, import_ora7.default)("Pushing config...").start();
|
|
1907
2153
|
const pushResult = await apiRequest(
|
|
1908
2154
|
`${BASE_URL}/api/cli/push`,
|
|
1909
2155
|
{
|
|
@@ -1974,65 +2220,94 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
|
|
|
1974
2220
|
// src/commands/switch.ts
|
|
1975
2221
|
var import_prompts5 = require("@inquirer/prompts");
|
|
1976
2222
|
var import_chalk13 = __toESM(require("chalk"));
|
|
1977
|
-
var
|
|
1978
|
-
var
|
|
1979
|
-
var switchCommand = new
|
|
2223
|
+
var import_commander11 = require("commander");
|
|
2224
|
+
var import_ora8 = __toESM(require("ora"));
|
|
2225
|
+
var switchCommand = new import_commander11.Command("switch").description(
|
|
2226
|
+
"Switch this project to a different organization. Updates .commet/config.json with the new org."
|
|
2227
|
+
).option(
|
|
2228
|
+
"--org <slug-or-id>",
|
|
2229
|
+
"Organization slug or ID \u2014 skips interactive selection"
|
|
2230
|
+
).addHelpText(
|
|
2231
|
+
"after",
|
|
2232
|
+
`
|
|
2233
|
+
Examples:
|
|
2234
|
+
$ commet switch Interactive \u2014 choose from a list
|
|
2235
|
+
$ commet switch --org acme Non-interactive \u2014 match by slug or ID
|
|
2236
|
+
|
|
2237
|
+
Use 'commet orgs' to see available organizations and their slugs.
|
|
2238
|
+
After switching, run 'commet pull' to update your config file.
|
|
2239
|
+
`
|
|
2240
|
+
).action(async (options) => {
|
|
1980
2241
|
if (!authExists()) {
|
|
1981
2242
|
console.log(import_chalk13.default.red("\u2717 Not authenticated"));
|
|
1982
2243
|
console.log(import_chalk13.default.dim("Run `commet login` first"));
|
|
1983
|
-
|
|
2244
|
+
process.exit(1);
|
|
1984
2245
|
}
|
|
1985
2246
|
if (!projectConfigExists()) {
|
|
1986
2247
|
console.log(import_chalk13.default.yellow("\u26A0 Project not linked"));
|
|
1987
2248
|
console.log(
|
|
1988
2249
|
import_chalk13.default.dim("Run `commet link` first to connect to an organization")
|
|
1989
2250
|
);
|
|
1990
|
-
|
|
2251
|
+
process.exit(1);
|
|
1991
2252
|
}
|
|
1992
|
-
const spinner = (0,
|
|
2253
|
+
const spinner = (0, import_ora8.default)("Fetching organizations...").start();
|
|
1993
2254
|
const result = await apiRequest(
|
|
1994
2255
|
`${BASE_URL}/api/cli/organizations`
|
|
1995
2256
|
);
|
|
1996
2257
|
if (result.error || !result.data) {
|
|
1997
2258
|
spinner.fail("Failed to fetch organizations");
|
|
1998
2259
|
console.error(import_chalk13.default.red("Error:"), result.error);
|
|
1999
|
-
|
|
2260
|
+
process.exit(1);
|
|
2000
2261
|
}
|
|
2001
2262
|
const { organizations } = result.data;
|
|
2002
2263
|
if (organizations.length === 0) {
|
|
2003
2264
|
spinner.stop();
|
|
2004
2265
|
console.log(import_chalk13.default.yellow("\u26A0 No organizations found"));
|
|
2005
|
-
|
|
2266
|
+
process.exit(1);
|
|
2006
2267
|
}
|
|
2007
2268
|
spinner.stop();
|
|
2008
|
-
let
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2269
|
+
let selectedOrg;
|
|
2270
|
+
if (options.org) {
|
|
2271
|
+
selectedOrg = organizations.find(
|
|
2272
|
+
(org) => org.slug === options.org || org.id === options.org
|
|
2273
|
+
);
|
|
2274
|
+
if (!selectedOrg) {
|
|
2275
|
+
console.log(import_chalk13.default.red(`\u2717 Organization "${options.org}" not found`));
|
|
2276
|
+
console.log(import_chalk13.default.dim("\nAvailable organizations:"));
|
|
2277
|
+
for (const org of organizations) {
|
|
2278
|
+
console.log(import_chalk13.default.dim(` ${org.slug} (${org.mode})`));
|
|
2279
|
+
}
|
|
2280
|
+
process.exit(1);
|
|
2281
|
+
}
|
|
2282
|
+
} else {
|
|
2283
|
+
let orgId;
|
|
2284
|
+
try {
|
|
2285
|
+
orgId = await (0, import_prompts5.select)({
|
|
2286
|
+
message: "Select organization:",
|
|
2287
|
+
choices: organizations.map((org) => ({
|
|
2288
|
+
name: `${org.name} ${import_chalk13.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
|
|
2289
|
+
value: org.id
|
|
2290
|
+
})),
|
|
2291
|
+
theme: promptTheme
|
|
2292
|
+
});
|
|
2293
|
+
} catch (_error) {
|
|
2294
|
+
console.log(import_chalk13.default.yellow("\n\u26A0 Switch cancelled"));
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
selectedOrg = organizations.find((org) => org.id === orgId);
|
|
2298
|
+
if (!selectedOrg) {
|
|
2299
|
+
console.log(import_chalk13.default.red("\u2717 Organization not found"));
|
|
2300
|
+
process.exit(1);
|
|
2301
|
+
}
|
|
2026
2302
|
}
|
|
2027
2303
|
saveProjectConfig({
|
|
2028
2304
|
orgId: selectedOrg.id,
|
|
2029
2305
|
orgName: selectedOrg.name,
|
|
2030
2306
|
mode: selectedOrg.mode
|
|
2031
2307
|
});
|
|
2032
|
-
console.log(import_chalk13.default.green("\n\u2713 Switched organization
|
|
2308
|
+
console.log(import_chalk13.default.green("\n\u2713 Switched organization"));
|
|
2033
2309
|
console.log(
|
|
2034
|
-
import_chalk13.default.dim(`
|
|
2035
|
-
Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
2310
|
+
import_chalk13.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
2036
2311
|
);
|
|
2037
2312
|
console.log(
|
|
2038
2313
|
import_chalk13.default.dim(
|
|
@@ -2043,8 +2318,10 @@ Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
|
2043
2318
|
|
|
2044
2319
|
// src/commands/unlink.ts
|
|
2045
2320
|
var import_chalk14 = __toESM(require("chalk"));
|
|
2046
|
-
var
|
|
2047
|
-
var unlinkCommand = new
|
|
2321
|
+
var import_commander12 = require("commander");
|
|
2322
|
+
var unlinkCommand = new import_commander12.Command("unlink").description(
|
|
2323
|
+
"Unlink this project from its organization. Removes the .commet/ directory. Does not delete anything on remote."
|
|
2324
|
+
).action(async () => {
|
|
2048
2325
|
if (!projectConfigExists()) {
|
|
2049
2326
|
console.log(
|
|
2050
2327
|
import_chalk14.default.yellow("\u26A0 This project is not linked to any organization")
|
|
@@ -2059,48 +2336,40 @@ var unlinkCommand = new import_commander11.Command("unlink").description("Unlink
|
|
|
2059
2336
|
);
|
|
2060
2337
|
});
|
|
2061
2338
|
|
|
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
2339
|
// src/index.ts
|
|
2088
2340
|
var program = new import_commander13.Command();
|
|
2089
2341
|
program.name("commet").description(
|
|
2090
|
-
"Commet CLI
|
|
2091
|
-
).version(package_default.version)
|
|
2342
|
+
"Commet CLI \u2014 billing infrastructure as code.\nManage features, plans, and billing config from the command line."
|
|
2343
|
+
).version(package_default.version).addHelpText(
|
|
2344
|
+
"after",
|
|
2345
|
+
`
|
|
2346
|
+
Workflow:
|
|
2347
|
+
$ commet pull Sync remote \u2192 commet.config.ts
|
|
2348
|
+
$ commet push Push local changes \u2192 remote
|
|
2349
|
+
$ commet list features See what's configured
|
|
2350
|
+
|
|
2351
|
+
For agents and CI:
|
|
2352
|
+
$ commet agent-info JSON with status + every command's usage
|
|
2353
|
+
$ commet pull --json --yes Structured output, no prompts
|
|
2354
|
+
$ commet orgs --json List orgs as JSON
|
|
2355
|
+
$ commet link --org <slug> Link without interactive selection
|
|
2356
|
+
|
|
2357
|
+
Run commet <command> --help for detailed usage and examples.
|
|
2358
|
+
`
|
|
2359
|
+
);
|
|
2092
2360
|
program.addCommand(createCommand);
|
|
2093
2361
|
program.addCommand(loginCommand);
|
|
2094
2362
|
program.addCommand(logoutCommand);
|
|
2095
|
-
program.addCommand(whoamiCommand);
|
|
2096
2363
|
program.addCommand(linkCommand);
|
|
2097
2364
|
program.addCommand(unlinkCommand);
|
|
2098
2365
|
program.addCommand(switchCommand);
|
|
2099
|
-
program.addCommand(
|
|
2366
|
+
program.addCommand(agentInfoCommand);
|
|
2367
|
+
program.addCommand(orgsCommand);
|
|
2100
2368
|
program.addCommand(pushCommand);
|
|
2101
2369
|
program.addCommand(pullCommand);
|
|
2102
2370
|
program.addCommand(listCommand);
|
|
2103
2371
|
program.addCommand(listenCommand);
|
|
2372
|
+
program.showSuggestionAfterError();
|
|
2104
2373
|
program.exitOverride();
|
|
2105
2374
|
try {
|
|
2106
2375
|
program.parse(process.argv);
|
|
@@ -2110,10 +2379,69 @@ try {
|
|
|
2110
2379
|
if (code === "commander.version" || code === "commander.help" || code === "commander.helpDisplayed") {
|
|
2111
2380
|
process.exit(0);
|
|
2112
2381
|
}
|
|
2113
|
-
console.error(
|
|
2382
|
+
console.error(import_chalk15.default.red("Error:"), error.message);
|
|
2114
2383
|
}
|
|
2115
2384
|
process.exit(1);
|
|
2116
2385
|
}
|
|
2117
2386
|
if (!process.argv.slice(2).length) {
|
|
2118
|
-
|
|
2387
|
+
printDefaultScreen();
|
|
2388
|
+
}
|
|
2389
|
+
function printDefaultScreen() {
|
|
2390
|
+
const version = import_chalk15.default.dim(`v${package_default.version}`);
|
|
2391
|
+
console.log(`
|
|
2392
|
+
${commetColor.bold("Commet")} ${version}`);
|
|
2393
|
+
console.log(import_chalk15.default.dim(" Billing infrastructure as code\n"));
|
|
2394
|
+
const authenticated = authExists();
|
|
2395
|
+
const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
|
|
2396
|
+
const configFile = findConfigFile(process.cwd());
|
|
2397
|
+
if (authenticated) {
|
|
2398
|
+
console.log(import_chalk15.default.green(" \u2713 Authenticated"));
|
|
2399
|
+
} else {
|
|
2400
|
+
console.log(
|
|
2401
|
+
` ${import_chalk15.default.yellow("\u26A0")} Not logged in ${import_chalk15.default.dim("\u2192 commet login")}`
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
if (projectConfig) {
|
|
2405
|
+
console.log(
|
|
2406
|
+
import_chalk15.default.green(
|
|
2407
|
+
` \u2713 ${projectConfig.orgName} ${import_chalk15.default.dim(`(${projectConfig.mode})`)}`
|
|
2408
|
+
)
|
|
2409
|
+
);
|
|
2410
|
+
console.log(import_chalk15.default.dim(` ${projectConfig.orgId}`));
|
|
2411
|
+
} else if (authenticated) {
|
|
2412
|
+
console.log(
|
|
2413
|
+
` ${import_chalk15.default.yellow("\u26A0")} No project linked ${import_chalk15.default.dim("\u2192 commet link")}`
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
if (configFile) {
|
|
2417
|
+
const fileName = configFile.split("/").pop();
|
|
2418
|
+
console.log(import_chalk15.default.green(` \u2713 ${fileName}`));
|
|
2419
|
+
} else if (projectConfig) {
|
|
2420
|
+
console.log(
|
|
2421
|
+
` ${import_chalk15.default.yellow("\u26A0")} No config file ${import_chalk15.default.dim("\u2192 commet pull")}`
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
const cmd = (name) => commetColor(name.padEnd(22));
|
|
2425
|
+
const dim = import_chalk15.default.dim;
|
|
2426
|
+
console.log(dim("\n Config"));
|
|
2427
|
+
console.log(` ${cmd("pull")}${dim("Sync remote \u2192 commet.config.ts")}`);
|
|
2428
|
+
console.log(` ${cmd("push")}${dim("Sync commet.config.ts \u2192 remote")}`);
|
|
2429
|
+
console.log(
|
|
2430
|
+
` ${cmd("list <type>")}${dim("List features, plans, or seats")}`
|
|
2431
|
+
);
|
|
2432
|
+
console.log(dim("\n Development"));
|
|
2433
|
+
console.log(` ${cmd("listen <url>")}${dim("Forward webhooks locally")}`);
|
|
2434
|
+
console.log(dim("\n Project"));
|
|
2435
|
+
console.log(` ${cmd("link")}${dim("Link to an organization")}`);
|
|
2436
|
+
console.log(` ${cmd("switch")}${dim("Switch organization")}`);
|
|
2437
|
+
console.log(` ${cmd("orgs")}${dim("List organizations")}`);
|
|
2438
|
+
console.log(dim("\n Setup"));
|
|
2439
|
+
console.log(` ${cmd("create")}${dim("Scaffold a new Commet app")}`);
|
|
2440
|
+
console.log(` ${cmd("login")}${dim("Authenticate")}`);
|
|
2441
|
+
console.log(` ${cmd("logout")}${dim("Log out")}`);
|
|
2442
|
+
console.log(
|
|
2443
|
+
dim(
|
|
2444
|
+
"\n commet --help for full reference \xB7 commet agent-info for agents/CI\n"
|
|
2445
|
+
)
|
|
2446
|
+
);
|
|
2119
2447
|
}
|