commet 1.9.1 → 1.10.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 +762 -195
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
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.10.0",
|
|
34
34
|
description: "Commet CLI - Manage your billing platform from the command line",
|
|
35
35
|
bin: {
|
|
36
36
|
commet: "./bin/commet"
|
|
@@ -61,6 +61,7 @@ var package_default = {
|
|
|
61
61
|
ably: "^2.21.0",
|
|
62
62
|
chalk: "5.6.2",
|
|
63
63
|
commander: "14.0.3",
|
|
64
|
+
jiti: "^2.7.0",
|
|
64
65
|
"jsonc-parser": "3.3.1",
|
|
65
66
|
open: "11.0.0",
|
|
66
67
|
ora: "9.4.0",
|
|
@@ -218,7 +219,7 @@ var promptTheme = {
|
|
|
218
219
|
|
|
219
220
|
// src/utils/login-flow.ts
|
|
220
221
|
function sleep(ms) {
|
|
221
|
-
return new Promise((
|
|
222
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
222
223
|
}
|
|
223
224
|
async function performLogin() {
|
|
224
225
|
const spinner = (0, import_ora.default)("Initiating login flow...").start();
|
|
@@ -492,7 +493,7 @@ async function resolveSkills(opts) {
|
|
|
492
493
|
}
|
|
493
494
|
async function installSkills(projectRoot) {
|
|
494
495
|
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
495
|
-
return new Promise((
|
|
496
|
+
return new Promise((resolve4) => {
|
|
496
497
|
const child = (0, import_node_child_process.spawn)(
|
|
497
498
|
npx,
|
|
498
499
|
["-y", "--loglevel=error", "skills", "add", "commet-labs/commet-skills"],
|
|
@@ -503,7 +504,7 @@ async function installSkills(projectRoot) {
|
|
|
503
504
|
console.log(import_chalk3.default.dim(" You can install them manually by running:"));
|
|
504
505
|
console.log(import_chalk3.default.dim(" npx skills add commet-labs/commet-skills"));
|
|
505
506
|
}
|
|
506
|
-
|
|
507
|
+
resolve4();
|
|
507
508
|
});
|
|
508
509
|
});
|
|
509
510
|
}
|
|
@@ -916,7 +917,7 @@ var listCommand = new import_commander4.Command("list").description("List featur
|
|
|
916
917
|
}
|
|
917
918
|
const spinner = (0, import_ora4.default)(`Fetching ${type}...`).start();
|
|
918
919
|
const result = await apiRequest(
|
|
919
|
-
`${BASE_URL}/api/cli/
|
|
920
|
+
`${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
|
|
920
921
|
);
|
|
921
922
|
if (result.error || !result.data) {
|
|
922
923
|
spinner.fail(`Failed to fetch ${type}`);
|
|
@@ -1207,255 +1208,820 @@ var logoutCommand = new import_commander7.Command("logout").description("Log out
|
|
|
1207
1208
|
});
|
|
1208
1209
|
|
|
1209
1210
|
// src/commands/pull.ts
|
|
1210
|
-
var
|
|
1211
|
-
var
|
|
1212
|
-
var
|
|
1211
|
+
var fs5 = __toESM(require("fs"));
|
|
1212
|
+
var path5 = __toESM(require("path"));
|
|
1213
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
1214
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1213
1215
|
var import_commander8 = require("commander");
|
|
1214
1216
|
var import_ora5 = __toESM(require("ora"));
|
|
1215
1217
|
|
|
1216
|
-
// src/utils/
|
|
1218
|
+
// src/utils/config-loader.ts
|
|
1217
1219
|
var fs4 = __toESM(require("fs"));
|
|
1218
1220
|
var path4 = __toESM(require("path"));
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1221
|
+
var import_jiti = require("jiti");
|
|
1222
|
+
var CONFIG_NAMES = [
|
|
1223
|
+
"commet.config.ts",
|
|
1224
|
+
"commet.config.js",
|
|
1225
|
+
"commet.config.mjs"
|
|
1226
|
+
];
|
|
1227
|
+
function findConfigFile(cwd) {
|
|
1228
|
+
for (const name of CONFIG_NAMES) {
|
|
1229
|
+
const fullPath = path4.resolve(cwd, name);
|
|
1230
|
+
if (fs4.existsSync(fullPath)) {
|
|
1231
|
+
return fullPath;
|
|
1232
|
+
}
|
|
1224
1233
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1234
|
+
return null;
|
|
1235
|
+
}
|
|
1236
|
+
async function loadBillingConfig(cwd) {
|
|
1237
|
+
const configPath = findConfigFile(cwd);
|
|
1238
|
+
if (!configPath) {
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
`No commet.config.ts found in ${cwd}. Create one with defineConfig() or run 'commet pull' to generate it.`
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
const jiti = (0, import_jiti.createJiti)(configPath, { interopDefault: true });
|
|
1244
|
+
const mod = await jiti.import(configPath);
|
|
1245
|
+
if (!mod || typeof mod !== "object") {
|
|
1246
|
+
throw new Error(`${configPath}: failed to load config module`);
|
|
1231
1247
|
}
|
|
1248
|
+
const moduleRecord = mod;
|
|
1249
|
+
if (!moduleRecord.default) {
|
|
1250
|
+
throw new Error(
|
|
1251
|
+
`${configPath}: must use \`export default defineConfig({...})\``
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
const config = moduleRecord.default;
|
|
1255
|
+
validateConfig(config, configPath);
|
|
1256
|
+
return { config, configPath };
|
|
1232
1257
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1258
|
+
var VALID_FEATURE_TYPES = /* @__PURE__ */ new Set(["boolean", "usage", "seats"]);
|
|
1259
|
+
var VALID_INTERVALS = /* @__PURE__ */ new Set([
|
|
1260
|
+
"weekly",
|
|
1261
|
+
"monthly",
|
|
1262
|
+
"quarterly",
|
|
1263
|
+
"yearly",
|
|
1264
|
+
"one_time"
|
|
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
|
+
}
|
|
1270
1325
|
}
|
|
1271
1326
|
}
|
|
1272
1327
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1328
|
+
// src/utils/diff.ts
|
|
1329
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
1330
|
+
function computeDiff(config, remote) {
|
|
1331
|
+
const remoteFeatureMap = new Map(remote.features.map((f) => [f.code, f]));
|
|
1332
|
+
const remotePlanMap = new Map(remote.plans.map((p) => [p.code, p]));
|
|
1333
|
+
const featureChanges = [];
|
|
1334
|
+
for (const [code, localFeature] of Object.entries(config.features)) {
|
|
1335
|
+
const remoteFeature = remoteFeatureMap.get(code);
|
|
1336
|
+
if (!remoteFeature) {
|
|
1337
|
+
featureChanges.push({ code, action: "create" });
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
const changes = [];
|
|
1341
|
+
if (remoteFeature.name !== localFeature.name) {
|
|
1342
|
+
changes.push(`name: "${remoteFeature.name}" \u2192 "${localFeature.name}"`);
|
|
1343
|
+
}
|
|
1344
|
+
if (remoteFeature.type !== localFeature.type) {
|
|
1345
|
+
changes.push(
|
|
1346
|
+
`type: "${remoteFeature.type}" \u2192 "${localFeature.type}" (BLOCKED)`
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
if ("unitName" in localFeature && (remoteFeature.unitName ?? void 0) !== localFeature.unitName) {
|
|
1350
|
+
changes.push(
|
|
1351
|
+
`unitName: "${remoteFeature.unitName ?? ""}" \u2192 "${localFeature.unitName}"`
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
featureChanges.push(
|
|
1355
|
+
changes.length > 0 ? { code, action: "update", changes } : { code, action: "unchanged" }
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
const unmanagedFeatures = remote.features.filter((f) => !config.features[f.code]).map((f) => f.code);
|
|
1359
|
+
const planChanges = [];
|
|
1360
|
+
for (const [code, localPlan] of Object.entries(config.plans)) {
|
|
1361
|
+
const remotePlan = remotePlanMap.get(code);
|
|
1362
|
+
if (!remotePlan) {
|
|
1363
|
+
planChanges.push({ code, action: "create" });
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
const changes = [];
|
|
1367
|
+
if (remotePlan.name !== localPlan.name) {
|
|
1368
|
+
changes.push(`name: "${remotePlan.name}" \u2192 "${localPlan.name}"`);
|
|
1369
|
+
}
|
|
1370
|
+
const remoteDefaultInterval = remotePlan.defaultInterval ?? remotePlan.prices.find((p) => p.isDefault)?.billingInterval ?? null;
|
|
1371
|
+
if (localPlan.defaultInterval && remoteDefaultInterval !== localPlan.defaultInterval) {
|
|
1372
|
+
changes.push(
|
|
1373
|
+
`defaultInterval: "${remoteDefaultInterval ?? "none"}" \u2192 "${localPlan.defaultInterval}"`
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
const localPriceMap = new Map(localPlan.prices.map((p) => [p.interval, p]));
|
|
1377
|
+
const remotePriceMap = new Map(
|
|
1378
|
+
remotePlan.prices.map((p) => [p.billingInterval, p])
|
|
1379
|
+
);
|
|
1380
|
+
for (const [interval, localPrice] of localPriceMap) {
|
|
1381
|
+
const remotePrice = remotePriceMap.get(interval);
|
|
1382
|
+
if (!remotePrice) {
|
|
1383
|
+
changes.push(
|
|
1384
|
+
`price ${interval}: new ($${(localPrice.amount / 1e4).toFixed(2)})`
|
|
1385
|
+
);
|
|
1386
|
+
} else if (remotePrice.price !== localPrice.amount) {
|
|
1387
|
+
changes.push(
|
|
1388
|
+
`price ${interval}: $${(remotePrice.price / 1e4).toFixed(2)} \u2192 $${(localPrice.amount / 1e4).toFixed(2)}`
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
if (localPlan.features) {
|
|
1393
|
+
const remotePlanFeatureMap = new Map(
|
|
1394
|
+
remotePlan.features.map((f) => [f.featureCode, f])
|
|
1395
|
+
);
|
|
1396
|
+
for (const featureCode of Object.keys(localPlan.features)) {
|
|
1397
|
+
if (!remotePlanFeatureMap.has(featureCode)) {
|
|
1398
|
+
changes.push(`feature ${featureCode}: new`);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
planChanges.push(
|
|
1403
|
+
changes.length > 0 ? { code, action: "update", changes } : { code, action: "unchanged" }
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
const unmanagedPlans = remote.plans.filter((p) => !config.plans[p.code]).map((p) => p.code);
|
|
1407
|
+
const hasChanges = featureChanges.some((c) => c.action !== "unchanged") || planChanges.some((c) => c.action !== "unchanged");
|
|
1408
|
+
return {
|
|
1409
|
+
features: { changes: featureChanges, unmanaged: unmanagedFeatures },
|
|
1410
|
+
plans: { changes: planChanges, unmanaged: unmanagedPlans },
|
|
1411
|
+
hasChanges
|
|
1412
|
+
};
|
|
1275
1413
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
};
|
|
1414
|
+
function formatDiff(diff) {
|
|
1415
|
+
const lines = [];
|
|
1416
|
+
lines.push(import_chalk10.default.bold("\nFeatures:"));
|
|
1417
|
+
for (const change of diff.features.changes) {
|
|
1418
|
+
if (change.action === "create") {
|
|
1419
|
+
lines.push(import_chalk10.default.green(` + ${change.code}`));
|
|
1420
|
+
} else if (change.action === "update") {
|
|
1421
|
+
lines.push(import_chalk10.default.yellow(` ~ ${change.code}`));
|
|
1422
|
+
for (const c of change.changes ?? []) {
|
|
1423
|
+
lines.push(import_chalk10.default.dim(` ${c}`));
|
|
1424
|
+
}
|
|
1425
|
+
} else {
|
|
1426
|
+
lines.push(import_chalk10.default.dim(` ${change.code}`));
|
|
1290
1427
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1428
|
+
}
|
|
1429
|
+
if (diff.features.unmanaged.length > 0) {
|
|
1430
|
+
lines.push(
|
|
1431
|
+
import_chalk10.default.dim(
|
|
1432
|
+
` ? unmanaged: ${diff.features.unmanaged.join(", ")} (not in config, left as-is)`
|
|
1433
|
+
)
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
lines.push(import_chalk10.default.bold("\nPlans:"));
|
|
1437
|
+
for (const change of diff.plans.changes) {
|
|
1438
|
+
if (change.action === "create") {
|
|
1439
|
+
lines.push(import_chalk10.default.green(` + ${change.code}`));
|
|
1440
|
+
} else if (change.action === "update") {
|
|
1441
|
+
lines.push(import_chalk10.default.yellow(` ~ ${change.code}`));
|
|
1442
|
+
for (const c of change.changes ?? []) {
|
|
1443
|
+
lines.push(import_chalk10.default.dim(` ${c}`));
|
|
1296
1444
|
}
|
|
1445
|
+
} else {
|
|
1446
|
+
lines.push(import_chalk10.default.dim(` ${change.code}`));
|
|
1297
1447
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1448
|
+
}
|
|
1449
|
+
if (diff.plans.unmanaged.length > 0) {
|
|
1450
|
+
lines.push(
|
|
1451
|
+
import_chalk10.default.dim(
|
|
1452
|
+
` ? unmanaged: ${diff.plans.unmanaged.join(", ")} (not in config, left as-is)`
|
|
1453
|
+
)
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
return lines.join("\n");
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// src/utils/generator.ts
|
|
1460
|
+
function generateConfigFile(features, plans) {
|
|
1461
|
+
const lines = [];
|
|
1462
|
+
lines.push('import { defineConfig } from "@commet/node";');
|
|
1463
|
+
lines.push("");
|
|
1464
|
+
lines.push("export default defineConfig({");
|
|
1465
|
+
lines.push(" features: {");
|
|
1466
|
+
for (const f of features) {
|
|
1467
|
+
const parts = [`name: "${f.name}"`, `type: "${f.type}"`];
|
|
1468
|
+
if (f.unitName) parts.push(`unitName: "${f.unitName}"`);
|
|
1469
|
+
if (f.description) parts.push(`description: "${f.description}"`);
|
|
1470
|
+
lines.push(` ${f.code}: { ${parts.join(", ")} },`);
|
|
1471
|
+
}
|
|
1472
|
+
lines.push(" },");
|
|
1473
|
+
lines.push(" plans: {");
|
|
1474
|
+
for (const p of plans) {
|
|
1475
|
+
lines.push(` ${p.code}: {`);
|
|
1476
|
+
lines.push(` name: "${p.name}",`);
|
|
1477
|
+
if (p.description) lines.push(` description: "${p.description}",`);
|
|
1478
|
+
if (p.consumptionModel)
|
|
1479
|
+
lines.push(` consumptionModel: "${p.consumptionModel}",`);
|
|
1480
|
+
if (p.isFree) lines.push(" isFree: true,");
|
|
1481
|
+
if (p.isPublic === false) lines.push(" isPublic: false,");
|
|
1482
|
+
if (p.sortOrder != null && p.sortOrder !== 0)
|
|
1483
|
+
lines.push(` sortOrder: ${p.sortOrder},`);
|
|
1484
|
+
const prices = p.prices ?? [];
|
|
1485
|
+
const defaultInterval = p.defaultInterval ?? prices.find((pr) => pr.isDefault)?.billingInterval ?? prices[0]?.billingInterval;
|
|
1486
|
+
if (defaultInterval)
|
|
1487
|
+
lines.push(` defaultInterval: "${defaultInterval}",`);
|
|
1488
|
+
if (prices.length === 0) {
|
|
1489
|
+
lines.push(" prices: [],");
|
|
1490
|
+
} else {
|
|
1491
|
+
lines.push(" prices: [");
|
|
1492
|
+
for (const price of prices) {
|
|
1493
|
+
const priceParts = [
|
|
1494
|
+
`interval: "${price.billingInterval}"`,
|
|
1495
|
+
`amount: ${price.price}`
|
|
1496
|
+
];
|
|
1497
|
+
if (price.trialDays) priceParts.push(`trialDays: ${price.trialDays}`);
|
|
1498
|
+
lines.push(` { ${priceParts.join(", ")} },`);
|
|
1304
1499
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1500
|
+
lines.push(" ],");
|
|
1501
|
+
}
|
|
1502
|
+
const planFeatures = p.features ?? [];
|
|
1503
|
+
if (planFeatures.length > 0) {
|
|
1504
|
+
lines.push(" features: {");
|
|
1505
|
+
for (const pf of planFeatures) {
|
|
1506
|
+
const featureDef = features.find((f) => f.code === pf.featureCode);
|
|
1507
|
+
const isBoolean = featureDef?.type === "boolean";
|
|
1508
|
+
if (isBoolean) {
|
|
1509
|
+
lines.push(` ${pf.featureCode}: ${pf.enabled ?? true},`);
|
|
1510
|
+
} else {
|
|
1511
|
+
const parts = [];
|
|
1512
|
+
if (pf.includedAmount) parts.push(`included: ${pf.includedAmount}`);
|
|
1513
|
+
if (pf.unlimited) parts.push("unlimited: true");
|
|
1514
|
+
if (pf.overageEnabled && pf.overageUnitPrice) {
|
|
1515
|
+
parts.push(`overage: { unitPrice: ${pf.overageUnitPrice} }`);
|
|
1516
|
+
}
|
|
1517
|
+
if (parts.length > 0) {
|
|
1518
|
+
lines.push(` ${pf.featureCode}: { ${parts.join(", ")} },`);
|
|
1519
|
+
} else {
|
|
1520
|
+
lines.push(` ${pf.featureCode}: {},`);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
lines.push(" },");
|
|
1525
|
+
}
|
|
1526
|
+
lines.push(" },");
|
|
1314
1527
|
}
|
|
1528
|
+
lines.push(" },");
|
|
1529
|
+
lines.push("});");
|
|
1530
|
+
lines.push("");
|
|
1531
|
+
return lines.join("\n");
|
|
1315
1532
|
}
|
|
1316
1533
|
|
|
1317
1534
|
// src/commands/pull.ts
|
|
1318
|
-
var pullCommand = new import_commander8.Command("pull").description("Pull
|
|
1535
|
+
var pullCommand = new import_commander8.Command("pull").description("Pull config from Commet and generate commet.config.ts").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show diff without applying changes").option("--json", "Output structured JSON (no colors, no prompts)").action(async (options) => {
|
|
1536
|
+
const jsonMode = options.json;
|
|
1319
1537
|
if (!authExists()) {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1538
|
+
if (jsonMode) {
|
|
1539
|
+
console.log(JSON.stringify({ error: "Not authenticated" }));
|
|
1540
|
+
} else {
|
|
1541
|
+
console.log(import_chalk11.default.red("\u2717 Not authenticated"));
|
|
1542
|
+
console.log(import_chalk11.default.dim("Run `commet login` first"));
|
|
1543
|
+
}
|
|
1544
|
+
process.exit(1);
|
|
1323
1545
|
}
|
|
1324
|
-
const hasTsConfig = validateTypeScriptProject();
|
|
1325
1546
|
if (!projectConfigExists()) {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1547
|
+
if (jsonMode) {
|
|
1548
|
+
console.log(JSON.stringify({ error: "Project not linked" }));
|
|
1549
|
+
} else {
|
|
1550
|
+
console.log(import_chalk11.default.red("\u2717 Project not linked"));
|
|
1551
|
+
console.log(
|
|
1552
|
+
import_chalk11.default.dim("Run `commet link` first to connect to an organization")
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
process.exit(1);
|
|
1331
1556
|
}
|
|
1332
1557
|
const projectConfig = loadProjectConfig();
|
|
1333
1558
|
if (!projectConfig) {
|
|
1334
|
-
|
|
1335
|
-
|
|
1559
|
+
if (jsonMode) {
|
|
1560
|
+
console.log(JSON.stringify({ error: "Invalid project configuration" }));
|
|
1561
|
+
} else {
|
|
1562
|
+
console.log(import_chalk11.default.red("\u2717 Invalid project configuration"));
|
|
1563
|
+
}
|
|
1564
|
+
process.exit(1);
|
|
1336
1565
|
}
|
|
1337
|
-
const spinner = (0, import_ora5.default)("Fetching
|
|
1566
|
+
const spinner = jsonMode ? null : (0, import_ora5.default)("Fetching config from remote...").start();
|
|
1338
1567
|
const result = await apiRequest(
|
|
1339
|
-
`${BASE_URL}/api/cli/
|
|
1568
|
+
`${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
|
|
1340
1569
|
);
|
|
1341
1570
|
if (result.error || !result.data) {
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1571
|
+
if (jsonMode) {
|
|
1572
|
+
console.log(JSON.stringify({ error: result.error }));
|
|
1573
|
+
} else {
|
|
1574
|
+
spinner?.fail("Failed to fetch config");
|
|
1575
|
+
console.error(import_chalk11.default.red("Error:"), result.error);
|
|
1576
|
+
}
|
|
1577
|
+
process.exit(1);
|
|
1345
1578
|
}
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
const
|
|
1349
|
-
const outputPath =
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1579
|
+
spinner?.succeed("Remote state fetched");
|
|
1580
|
+
const { features, plans } = result.data;
|
|
1581
|
+
const configContent = generateConfigFile(features, plans);
|
|
1582
|
+
const outputPath = path5.resolve(process.cwd(), "commet.config.ts");
|
|
1583
|
+
const existingConfigPath = findConfigFile(process.cwd());
|
|
1584
|
+
if (!existingConfigPath) {
|
|
1585
|
+
if (options.dryRun) {
|
|
1586
|
+
if (jsonMode) {
|
|
1587
|
+
console.log(
|
|
1588
|
+
JSON.stringify({
|
|
1589
|
+
action: "create",
|
|
1590
|
+
features: features.length,
|
|
1591
|
+
plans: plans.length,
|
|
1592
|
+
applied: false
|
|
1593
|
+
})
|
|
1594
|
+
);
|
|
1595
|
+
} else {
|
|
1596
|
+
console.log(
|
|
1597
|
+
import_chalk11.default.green(
|
|
1598
|
+
`
|
|
1599
|
+
Would create commet.config.ts (${features.length} features, ${plans.length} plans)`
|
|
1600
|
+
)
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
fs5.writeFileSync(outputPath, configContent, "utf8");
|
|
1606
|
+
if (jsonMode) {
|
|
1607
|
+
console.log(
|
|
1608
|
+
JSON.stringify({
|
|
1609
|
+
action: "create",
|
|
1610
|
+
features: features.length,
|
|
1611
|
+
plans: plans.length,
|
|
1612
|
+
applied: true
|
|
1613
|
+
})
|
|
1614
|
+
);
|
|
1357
1615
|
} else {
|
|
1358
|
-
console.log(
|
|
1616
|
+
console.log(import_chalk11.default.green(`
|
|
1617
|
+
\u2713 Created commet.config.ts`));
|
|
1359
1618
|
console.log(
|
|
1360
|
-
|
|
1361
|
-
'Add ".commet/types.d.ts" to your tsconfig.json include array'
|
|
1362
|
-
)
|
|
1619
|
+
import_chalk11.default.dim(` ${features.length} features, ${plans.length} plans`)
|
|
1363
1620
|
);
|
|
1364
1621
|
}
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
const localLoaded = await loadBillingConfig(process.cwd()).catch(
|
|
1625
|
+
(error) => ({
|
|
1626
|
+
parseError: error instanceof Error ? error.message : String(error)
|
|
1627
|
+
})
|
|
1628
|
+
);
|
|
1629
|
+
if ("parseError" in localLoaded) {
|
|
1630
|
+
if (options.dryRun) {
|
|
1631
|
+
if (jsonMode) {
|
|
1632
|
+
console.log(
|
|
1633
|
+
JSON.stringify({
|
|
1634
|
+
action: "overwrite",
|
|
1635
|
+
reason: localLoaded.parseError,
|
|
1636
|
+
applied: false
|
|
1637
|
+
})
|
|
1638
|
+
);
|
|
1639
|
+
} else {
|
|
1640
|
+
console.log(
|
|
1641
|
+
import_chalk11.default.yellow(
|
|
1642
|
+
`
|
|
1643
|
+
\u26A0 Local config is invalid: ${localLoaded.parseError}`
|
|
1644
|
+
)
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
if (!options.yes && !jsonMode) {
|
|
1650
|
+
console.log(import_chalk11.default.yellow(`
|
|
1651
|
+
\u26A0 ${localLoaded.parseError}`));
|
|
1652
|
+
const shouldProceed = await (0, import_prompts3.confirm)({
|
|
1653
|
+
message: "Overwrite with remote?",
|
|
1654
|
+
default: true
|
|
1655
|
+
});
|
|
1656
|
+
if (!shouldProceed) {
|
|
1657
|
+
console.log(import_chalk11.default.dim("Pull cancelled"));
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
fs5.writeFileSync(outputPath, configContent, "utf8");
|
|
1662
|
+
if (jsonMode) {
|
|
1663
|
+
console.log(JSON.stringify({ action: "overwrite", applied: true }));
|
|
1664
|
+
} else {
|
|
1665
|
+
console.log(import_chalk11.default.green("\n\u2713 Overwritten commet.config.ts"));
|
|
1666
|
+
}
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
const localConfig = localLoaded.config;
|
|
1670
|
+
const remoteAsConfig = {
|
|
1671
|
+
features: Object.fromEntries(
|
|
1672
|
+
features.map((f) => [
|
|
1673
|
+
f.code,
|
|
1674
|
+
{
|
|
1675
|
+
name: f.name,
|
|
1676
|
+
type: f.type,
|
|
1677
|
+
...f.unitName ? { unitName: f.unitName } : {},
|
|
1678
|
+
...f.description ? { description: f.description } : {}
|
|
1679
|
+
}
|
|
1680
|
+
])
|
|
1681
|
+
),
|
|
1682
|
+
plans: Object.fromEntries(
|
|
1683
|
+
plans.map((p) => [
|
|
1684
|
+
p.code,
|
|
1685
|
+
{
|
|
1686
|
+
name: p.name,
|
|
1687
|
+
...p.description ? { description: p.description } : {},
|
|
1688
|
+
...p.consumptionModel ? {
|
|
1689
|
+
consumptionModel: p.consumptionModel
|
|
1690
|
+
} : {},
|
|
1691
|
+
...p.isFree ? { isFree: true } : {},
|
|
1692
|
+
...p.isPublic === false ? { isPublic: false } : {},
|
|
1693
|
+
...p.sortOrder ? { sortOrder: p.sortOrder } : {},
|
|
1694
|
+
...(() => {
|
|
1695
|
+
const planPrices = p.prices ?? [];
|
|
1696
|
+
const defaultPrice = planPrices.find((pr) => pr.isDefault);
|
|
1697
|
+
const defaultInterval = defaultPrice?.billingInterval ?? planPrices[0]?.billingInterval;
|
|
1698
|
+
return defaultInterval ? { defaultInterval } : {};
|
|
1699
|
+
})(),
|
|
1700
|
+
prices: (p.prices ?? []).map((pr) => ({
|
|
1701
|
+
interval: pr.billingInterval,
|
|
1702
|
+
amount: pr.price,
|
|
1703
|
+
...pr.trialDays ? { trialDays: pr.trialDays } : {}
|
|
1704
|
+
}))
|
|
1705
|
+
}
|
|
1706
|
+
])
|
|
1707
|
+
)
|
|
1708
|
+
};
|
|
1709
|
+
const localAsRemote = {
|
|
1710
|
+
features: Object.entries(localConfig.features).map(([code, f]) => ({
|
|
1711
|
+
code,
|
|
1712
|
+
name: f.name,
|
|
1713
|
+
type: f.type,
|
|
1714
|
+
description: f.description ?? null,
|
|
1715
|
+
unitName: f.unitName ?? null
|
|
1716
|
+
})),
|
|
1717
|
+
plans: Object.entries(localConfig.plans).map(([code, p]) => ({
|
|
1718
|
+
code,
|
|
1719
|
+
name: p.name,
|
|
1720
|
+
description: p.description ?? null,
|
|
1721
|
+
consumptionModel: p.consumptionModel ?? null,
|
|
1722
|
+
defaultInterval: p.defaultInterval ?? null,
|
|
1723
|
+
isFree: p.isFree,
|
|
1724
|
+
isPublic: p.isPublic,
|
|
1725
|
+
sortOrder: p.sortOrder,
|
|
1726
|
+
prices: p.prices.map((pr) => ({
|
|
1727
|
+
billingInterval: pr.interval,
|
|
1728
|
+
price: pr.amount,
|
|
1729
|
+
trialDays: pr.trialDays ?? null
|
|
1730
|
+
})),
|
|
1731
|
+
features: []
|
|
1732
|
+
}))
|
|
1733
|
+
};
|
|
1734
|
+
const diff = computeDiff(remoteAsConfig, localAsRemote);
|
|
1735
|
+
if (!diff.hasChanges && diff.features.unmanaged.length === 0 && diff.plans.unmanaged.length === 0) {
|
|
1736
|
+
if (jsonMode) {
|
|
1737
|
+
console.log(JSON.stringify({ diff, applied: false, upToDate: true }));
|
|
1738
|
+
} else {
|
|
1739
|
+
console.log(import_chalk11.default.green("\n\u2713 Already up to date"));
|
|
1740
|
+
}
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
if (jsonMode) {
|
|
1744
|
+
if (options.dryRun) {
|
|
1745
|
+
console.log(JSON.stringify({ diff, applied: false }));
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1365
1748
|
} else {
|
|
1366
|
-
console.log(
|
|
1749
|
+
console.log(formatDiff(diff));
|
|
1750
|
+
}
|
|
1751
|
+
if (options.dryRun) {
|
|
1752
|
+
if (!jsonMode) {
|
|
1753
|
+
console.log(import_chalk11.default.dim("\n(dry run \u2014 no changes applied)"));
|
|
1754
|
+
}
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
if (!options.yes && !jsonMode) {
|
|
1758
|
+
const shouldProceed = await (0, import_prompts3.confirm)({
|
|
1759
|
+
message: "Overwrite commet.config.ts with remote state?",
|
|
1760
|
+
default: true
|
|
1761
|
+
});
|
|
1762
|
+
if (!shouldProceed) {
|
|
1763
|
+
console.log(import_chalk11.default.dim("Pull cancelled"));
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
fs5.writeFileSync(outputPath, configContent, "utf8");
|
|
1768
|
+
if (jsonMode) {
|
|
1769
|
+
console.log(JSON.stringify({ diff, applied: true }));
|
|
1770
|
+
} else {
|
|
1771
|
+
console.log(import_chalk11.default.green("\n\u2713 Updated commet.config.ts"));
|
|
1367
1772
|
console.log(
|
|
1368
|
-
|
|
1369
|
-
'Add ".commet/types.d.ts" to your tsconfig.json to enable types'
|
|
1370
|
-
)
|
|
1773
|
+
import_chalk11.default.dim(` ${features.length} features, ${plans.length} plans`)
|
|
1371
1774
|
);
|
|
1372
1775
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
// src/commands/push.ts
|
|
1779
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
1780
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
1781
|
+
var import_commander9 = require("commander");
|
|
1782
|
+
var import_ora6 = __toESM(require("ora"));
|
|
1783
|
+
var pushCommand = new import_commander9.Command("push").description("Push commet.config.ts to your Commet organization").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show diff without applying changes").option("--json", "Output structured JSON (no colors, no prompts)").action(async (options) => {
|
|
1784
|
+
const jsonMode = options.json;
|
|
1785
|
+
if (!authExists()) {
|
|
1786
|
+
if (jsonMode) {
|
|
1787
|
+
console.log(JSON.stringify({ error: "Not authenticated" }));
|
|
1788
|
+
} else {
|
|
1789
|
+
console.log(import_chalk12.default.red("\u2717 Not authenticated"));
|
|
1790
|
+
console.log(import_chalk12.default.dim("Run `commet login` first"));
|
|
1791
|
+
}
|
|
1792
|
+
process.exit(1);
|
|
1379
1793
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1794
|
+
if (!projectConfigExists()) {
|
|
1795
|
+
if (jsonMode) {
|
|
1796
|
+
console.log(JSON.stringify({ error: "Project not linked" }));
|
|
1797
|
+
} else {
|
|
1798
|
+
console.log(import_chalk12.default.red("\u2717 Project not linked"));
|
|
1799
|
+
console.log(
|
|
1800
|
+
import_chalk12.default.dim("Run `commet link` first to connect to an organization")
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1803
|
+
process.exit(1);
|
|
1804
|
+
}
|
|
1805
|
+
const projectConfig = loadProjectConfig();
|
|
1806
|
+
if (!projectConfig) {
|
|
1807
|
+
if (jsonMode) {
|
|
1808
|
+
console.log(JSON.stringify({ error: "Invalid project configuration" }));
|
|
1809
|
+
} else {
|
|
1810
|
+
console.log(import_chalk12.default.red("\u2717 Invalid project configuration"));
|
|
1811
|
+
}
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
}
|
|
1814
|
+
const loadSpinner = jsonMode ? null : (0, import_ora6.default)("Loading commet.config.ts...").start();
|
|
1815
|
+
const loaded = await loadBillingConfig(process.cwd()).catch(
|
|
1816
|
+
(error) => {
|
|
1817
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1818
|
+
if (jsonMode) {
|
|
1819
|
+
console.log(JSON.stringify({ error: message }));
|
|
1820
|
+
} else {
|
|
1821
|
+
loadSpinner?.fail("Failed to load config");
|
|
1822
|
+
console.error(import_chalk12.default.red(message));
|
|
1823
|
+
}
|
|
1824
|
+
return null;
|
|
1825
|
+
}
|
|
1386
1826
|
);
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1827
|
+
if (!loaded) process.exit(1);
|
|
1828
|
+
const { config, configPath } = loaded;
|
|
1829
|
+
loadSpinner?.succeed(`Loaded ${configPath}`);
|
|
1830
|
+
const fetchSpinner = jsonMode ? null : (0, import_ora6.default)("Fetching remote state...").start();
|
|
1831
|
+
const remoteResult = await apiRequest(
|
|
1832
|
+
`${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
|
|
1391
1833
|
);
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1834
|
+
if (remoteResult.error || !remoteResult.data) {
|
|
1835
|
+
if (jsonMode) {
|
|
1836
|
+
console.log(JSON.stringify({ error: remoteResult.error }));
|
|
1837
|
+
} else {
|
|
1838
|
+
fetchSpinner?.fail("Failed to fetch remote state");
|
|
1839
|
+
console.error(import_chalk12.default.red("Error:"), remoteResult.error);
|
|
1840
|
+
}
|
|
1841
|
+
process.exit(1);
|
|
1842
|
+
}
|
|
1843
|
+
fetchSpinner?.succeed("Remote state fetched");
|
|
1844
|
+
const remote = {
|
|
1845
|
+
features: remoteResult.data.features,
|
|
1846
|
+
plans: remoteResult.data.plans
|
|
1847
|
+
};
|
|
1848
|
+
const diff = computeDiff(config, remote);
|
|
1849
|
+
if (jsonMode) {
|
|
1850
|
+
if (options.dryRun) {
|
|
1851
|
+
console.log(JSON.stringify({ diff, applied: false }));
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
} else {
|
|
1855
|
+
console.log(formatDiff(diff));
|
|
1856
|
+
}
|
|
1857
|
+
if (!diff.hasChanges) {
|
|
1858
|
+
if (jsonMode) {
|
|
1859
|
+
console.log(JSON.stringify({ diff, applied: false, upToDate: true }));
|
|
1860
|
+
} else {
|
|
1861
|
+
console.log(import_chalk12.default.green("\n\u2713 Everything is up to date"));
|
|
1862
|
+
}
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const typeChanges = diff.features.changes.filter(
|
|
1866
|
+
(c) => c.action === "update" && c.changes?.some((ch) => ch.includes("BLOCKED"))
|
|
1867
|
+
);
|
|
1868
|
+
if (typeChanges.length > 0) {
|
|
1869
|
+
const blockedCodes = typeChanges.map((c) => c.code);
|
|
1870
|
+
if (jsonMode) {
|
|
1871
|
+
console.log(
|
|
1872
|
+
JSON.stringify({
|
|
1873
|
+
error: "Feature type changes blocked",
|
|
1874
|
+
blockedCodes,
|
|
1875
|
+
diff
|
|
1876
|
+
})
|
|
1877
|
+
);
|
|
1878
|
+
} else {
|
|
1879
|
+
console.log(
|
|
1880
|
+
import_chalk12.default.red(
|
|
1881
|
+
"\n\u2717 Cannot change feature types. Update them in the dashboard:"
|
|
1882
|
+
)
|
|
1883
|
+
);
|
|
1884
|
+
for (const change of typeChanges) {
|
|
1885
|
+
console.log(import_chalk12.default.red(` - ${change.code}`));
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
process.exit(1);
|
|
1889
|
+
}
|
|
1890
|
+
if (options.dryRun) {
|
|
1891
|
+
if (!jsonMode) {
|
|
1892
|
+
console.log(import_chalk12.default.dim("\n(dry run \u2014 no changes applied)"));
|
|
1893
|
+
}
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
if (!options.yes && !jsonMode) {
|
|
1897
|
+
const shouldProceed = await (0, import_prompts4.confirm)({
|
|
1898
|
+
message: "Apply these changes?",
|
|
1899
|
+
default: true
|
|
1900
|
+
});
|
|
1901
|
+
if (!shouldProceed) {
|
|
1902
|
+
console.log(import_chalk12.default.dim("Push cancelled"));
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
const pushSpinner = jsonMode ? null : (0, import_ora6.default)("Pushing config...").start();
|
|
1907
|
+
const pushResult = await apiRequest(
|
|
1908
|
+
`${BASE_URL}/api/cli/push`,
|
|
1909
|
+
{
|
|
1910
|
+
method: "POST",
|
|
1911
|
+
body: JSON.stringify({
|
|
1912
|
+
orgId: projectConfig.orgId,
|
|
1913
|
+
config: {
|
|
1914
|
+
features: config.features,
|
|
1915
|
+
plans: config.plans
|
|
1916
|
+
}
|
|
1917
|
+
})
|
|
1918
|
+
}
|
|
1396
1919
|
);
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1920
|
+
if (pushResult.error || !pushResult.data) {
|
|
1921
|
+
if (jsonMode) {
|
|
1922
|
+
console.log(JSON.stringify({ error: pushResult.error }));
|
|
1923
|
+
} else {
|
|
1924
|
+
pushSpinner?.fail("Push failed");
|
|
1925
|
+
console.error(import_chalk12.default.red("Error:"), pushResult.error);
|
|
1926
|
+
}
|
|
1927
|
+
process.exit(1);
|
|
1928
|
+
}
|
|
1929
|
+
const pushOutcome = pushResult.data;
|
|
1930
|
+
if (jsonMode) {
|
|
1931
|
+
console.log(JSON.stringify({ diff, applied: true, result: pushOutcome }));
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
const errors = [
|
|
1935
|
+
...pushOutcome.features.errors,
|
|
1936
|
+
...pushOutcome.plans.errors
|
|
1937
|
+
];
|
|
1938
|
+
if (errors.length > 0) {
|
|
1939
|
+
pushSpinner?.warn("Push completed with errors");
|
|
1940
|
+
for (const error of errors) {
|
|
1941
|
+
console.log(import_chalk12.default.red(` \u2717 ${error.code}: ${error.message}`));
|
|
1942
|
+
}
|
|
1943
|
+
} else {
|
|
1944
|
+
pushSpinner?.succeed("Push complete");
|
|
1945
|
+
}
|
|
1946
|
+
if (pushOutcome.features.created.length > 0) {
|
|
1947
|
+
console.log(
|
|
1948
|
+
import_chalk12.default.green(
|
|
1949
|
+
` Created features: ${pushOutcome.features.created.join(", ")}`
|
|
1950
|
+
)
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1953
|
+
if (pushOutcome.features.updated.length > 0) {
|
|
1954
|
+
console.log(
|
|
1955
|
+
import_chalk12.default.yellow(
|
|
1956
|
+
` Updated features: ${pushOutcome.features.updated.join(", ")}`
|
|
1957
|
+
)
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
if (pushOutcome.plans.created.length > 0) {
|
|
1400
1961
|
console.log(
|
|
1401
|
-
|
|
1402
|
-
|
|
1962
|
+
import_chalk12.default.green(` Created plans: ${pushOutcome.plans.created.join(", ")}`)
|
|
1963
|
+
);
|
|
1964
|
+
}
|
|
1965
|
+
if (pushOutcome.plans.updated.length > 0) {
|
|
1966
|
+
console.log(
|
|
1967
|
+
import_chalk12.default.yellow(
|
|
1968
|
+
` Updated plans: ${pushOutcome.plans.updated.join(", ")}`
|
|
1403
1969
|
)
|
|
1404
1970
|
);
|
|
1405
1971
|
}
|
|
1406
1972
|
});
|
|
1407
1973
|
|
|
1408
1974
|
// src/commands/switch.ts
|
|
1409
|
-
var
|
|
1410
|
-
var
|
|
1411
|
-
var
|
|
1412
|
-
var
|
|
1413
|
-
var switchCommand = new
|
|
1975
|
+
var import_prompts5 = require("@inquirer/prompts");
|
|
1976
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
1977
|
+
var import_commander10 = require("commander");
|
|
1978
|
+
var import_ora7 = __toESM(require("ora"));
|
|
1979
|
+
var switchCommand = new import_commander10.Command("switch").description("Switch to a different organization").action(async () => {
|
|
1414
1980
|
if (!authExists()) {
|
|
1415
|
-
console.log(
|
|
1416
|
-
console.log(
|
|
1981
|
+
console.log(import_chalk13.default.red("\u2717 Not authenticated"));
|
|
1982
|
+
console.log(import_chalk13.default.dim("Run `commet login` first"));
|
|
1417
1983
|
return;
|
|
1418
1984
|
}
|
|
1419
1985
|
if (!projectConfigExists()) {
|
|
1420
|
-
console.log(
|
|
1986
|
+
console.log(import_chalk13.default.yellow("\u26A0 Project not linked"));
|
|
1421
1987
|
console.log(
|
|
1422
|
-
|
|
1988
|
+
import_chalk13.default.dim("Run `commet link` first to connect to an organization")
|
|
1423
1989
|
);
|
|
1424
1990
|
return;
|
|
1425
1991
|
}
|
|
1426
|
-
const spinner = (0,
|
|
1992
|
+
const spinner = (0, import_ora7.default)("Fetching organizations...").start();
|
|
1427
1993
|
const result = await apiRequest(
|
|
1428
1994
|
`${BASE_URL}/api/cli/organizations`
|
|
1429
1995
|
);
|
|
1430
1996
|
if (result.error || !result.data) {
|
|
1431
1997
|
spinner.fail("Failed to fetch organizations");
|
|
1432
|
-
console.error(
|
|
1998
|
+
console.error(import_chalk13.default.red("Error:"), result.error);
|
|
1433
1999
|
return;
|
|
1434
2000
|
}
|
|
1435
2001
|
const { organizations } = result.data;
|
|
1436
2002
|
if (organizations.length === 0) {
|
|
1437
2003
|
spinner.stop();
|
|
1438
|
-
console.log(
|
|
2004
|
+
console.log(import_chalk13.default.yellow("\u26A0 No organizations found"));
|
|
1439
2005
|
return;
|
|
1440
2006
|
}
|
|
1441
2007
|
spinner.stop();
|
|
1442
2008
|
let orgId;
|
|
1443
2009
|
try {
|
|
1444
|
-
orgId = await (0,
|
|
2010
|
+
orgId = await (0, import_prompts5.select)({
|
|
1445
2011
|
message: "Select organization:",
|
|
1446
2012
|
choices: organizations.map((org) => ({
|
|
1447
|
-
name: `${org.name} ${
|
|
2013
|
+
name: `${org.name} ${import_chalk13.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
|
|
1448
2014
|
value: org.id
|
|
1449
2015
|
})),
|
|
1450
2016
|
theme: promptTheme
|
|
1451
2017
|
});
|
|
1452
2018
|
} catch (_error) {
|
|
1453
|
-
console.log(
|
|
2019
|
+
console.log(import_chalk13.default.yellow("\n\u26A0 Switch cancelled"));
|
|
1454
2020
|
return;
|
|
1455
2021
|
}
|
|
1456
2022
|
const selectedOrg = organizations.find((org) => org.id === orgId);
|
|
1457
2023
|
if (!selectedOrg) {
|
|
1458
|
-
console.log(
|
|
2024
|
+
console.log(import_chalk13.default.red("\u2717 Organization not found"));
|
|
1459
2025
|
return;
|
|
1460
2026
|
}
|
|
1461
2027
|
saveProjectConfig({
|
|
@@ -1463,55 +2029,55 @@ var switchCommand = new import_commander9.Command("switch").description("Switch
|
|
|
1463
2029
|
orgName: selectedOrg.name,
|
|
1464
2030
|
mode: selectedOrg.mode
|
|
1465
2031
|
});
|
|
1466
|
-
console.log(
|
|
2032
|
+
console.log(import_chalk13.default.green("\n\u2713 Switched organization successfully!"));
|
|
1467
2033
|
console.log(
|
|
1468
|
-
|
|
2034
|
+
import_chalk13.default.dim(`
|
|
1469
2035
|
Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
|
|
1470
2036
|
);
|
|
1471
2037
|
console.log(
|
|
1472
|
-
|
|
2038
|
+
import_chalk13.default.dim(
|
|
1473
2039
|
"\nRun `commet pull` to update TypeScript types for this organization"
|
|
1474
2040
|
)
|
|
1475
2041
|
);
|
|
1476
2042
|
});
|
|
1477
2043
|
|
|
1478
2044
|
// src/commands/unlink.ts
|
|
1479
|
-
var
|
|
1480
|
-
var
|
|
1481
|
-
var unlinkCommand = new
|
|
2045
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
2046
|
+
var import_commander11 = require("commander");
|
|
2047
|
+
var unlinkCommand = new import_commander11.Command("unlink").description("Unlink this project from Commet").action(async () => {
|
|
1482
2048
|
if (!projectConfigExists()) {
|
|
1483
2049
|
console.log(
|
|
1484
|
-
|
|
2050
|
+
import_chalk14.default.yellow("\u26A0 This project is not linked to any organization")
|
|
1485
2051
|
);
|
|
1486
2052
|
return;
|
|
1487
2053
|
}
|
|
1488
2054
|
clearProjectConfig();
|
|
1489
|
-
console.log(
|
|
1490
|
-
console.log(
|
|
2055
|
+
console.log(import_chalk14.default.green("\u2713 Project unlinked successfully"));
|
|
2056
|
+
console.log(import_chalk14.default.dim("\u2713 Removed .commet/ directory"));
|
|
1491
2057
|
console.log(
|
|
1492
|
-
|
|
2058
|
+
import_chalk14.default.dim("\nRun `commet link` to connect to a different organization")
|
|
1493
2059
|
);
|
|
1494
2060
|
});
|
|
1495
2061
|
|
|
1496
2062
|
// src/commands/whoami.ts
|
|
1497
|
-
var
|
|
1498
|
-
var
|
|
1499
|
-
var whoamiCommand = new
|
|
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 () => {
|
|
1500
2066
|
if (!authExists()) {
|
|
1501
|
-
console.log(
|
|
1502
|
-
console.log(
|
|
2067
|
+
console.log(import_chalk15.default.yellow("\u26A0 Not logged in"));
|
|
2068
|
+
console.log(import_chalk15.default.dim("Run `commet login` to authenticate"));
|
|
1503
2069
|
return;
|
|
1504
2070
|
}
|
|
1505
|
-
console.log(
|
|
2071
|
+
console.log(import_chalk15.default.green("\u2713 Logged in"));
|
|
1506
2072
|
const projectConfig = loadProjectConfig();
|
|
1507
2073
|
if (projectConfig) {
|
|
1508
|
-
console.log(
|
|
1509
|
-
console.log(
|
|
1510
|
-
console.log(
|
|
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);
|
|
1511
2077
|
} else {
|
|
1512
|
-
console.log(
|
|
2078
|
+
console.log(import_chalk15.default.yellow("\n\u26A0 No project linked"));
|
|
1513
2079
|
console.log(
|
|
1514
|
-
|
|
2080
|
+
import_chalk15.default.dim(
|
|
1515
2081
|
"Run `commet link` to connect this directory to an organization"
|
|
1516
2082
|
)
|
|
1517
2083
|
);
|
|
@@ -1519,7 +2085,7 @@ var whoamiCommand = new import_commander11.Command("whoami").description("Displa
|
|
|
1519
2085
|
});
|
|
1520
2086
|
|
|
1521
2087
|
// src/index.ts
|
|
1522
|
-
var program = new
|
|
2088
|
+
var program = new import_commander13.Command();
|
|
1523
2089
|
program.name("commet").description(
|
|
1524
2090
|
"Commet CLI - Manage your billing platform from the command line"
|
|
1525
2091
|
).version(package_default.version);
|
|
@@ -1531,6 +2097,7 @@ program.addCommand(linkCommand);
|
|
|
1531
2097
|
program.addCommand(unlinkCommand);
|
|
1532
2098
|
program.addCommand(switchCommand);
|
|
1533
2099
|
program.addCommand(infoCommand);
|
|
2100
|
+
program.addCommand(pushCommand);
|
|
1534
2101
|
program.addCommand(pullCommand);
|
|
1535
2102
|
program.addCommand(listCommand);
|
|
1536
2103
|
program.addCommand(listenCommand);
|
|
@@ -1543,7 +2110,7 @@ try {
|
|
|
1543
2110
|
if (code === "commander.version" || code === "commander.help" || code === "commander.helpDisplayed") {
|
|
1544
2111
|
process.exit(0);
|
|
1545
2112
|
}
|
|
1546
|
-
console.error(
|
|
2113
|
+
console.error(import_chalk16.default.red("Error:"), error.message);
|
|
1547
2114
|
}
|
|
1548
2115
|
process.exit(1);
|
|
1549
2116
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commet",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Commet CLI - Manage your billing platform from the command line",
|
|
5
5
|
"bin": {
|
|
6
6
|
"commet": "./bin/commet"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"ably": "^2.21.0",
|
|
24
24
|
"chalk": "5.6.2",
|
|
25
25
|
"commander": "14.0.3",
|
|
26
|
+
"jiti": "^2.7.0",
|
|
26
27
|
"jsonc-parser": "3.3.1",
|
|
27
28
|
"open": "11.0.0",
|
|
28
29
|
"ora": "9.4.0",
|