owosk 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -7
  2. package/dist/index.js +225 -145
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -13,7 +13,7 @@ npm install -g owosk
13
13
  Or use it directly with npx:
14
14
 
15
15
  ```bash
16
- npx owo --help
16
+ npx owosk --help
17
17
  ```
18
18
 
19
19
  ## Commands
@@ -23,7 +23,7 @@ npx owo --help
23
23
  Initialize a new Owostack project with a default configuration file (`owo.config.ts` or `owo.config.js`). JavaScript configs use ESM `import`/`export` syntax.
24
24
 
25
25
  ```bash
26
- npx owo init
26
+ npx owosk init
27
27
  ```
28
28
 
29
29
  ### `sync`
@@ -31,7 +31,7 @@ npx owo init
31
31
  Push your local catalog configuration to the Owostack cloud.
32
32
 
33
33
  ```bash
34
- npx owo sync
34
+ npx owosk sync
35
35
  ```
36
36
 
37
37
  ### `pull`
@@ -39,7 +39,7 @@ npx owo sync
39
39
  Pull existing plans and features from the cloud into your local configuration.
40
40
 
41
41
  ```bash
42
- npx owo pull
42
+ npx owosk pull
43
43
  ```
44
44
 
45
45
  ### `diff`
@@ -47,7 +47,7 @@ npx owo pull
47
47
  Preview changes by comparing your local configuration with the cloud.
48
48
 
49
49
  ```bash
50
- npx owo diff
50
+ npx owosk diff
51
51
  ```
52
52
 
53
53
  ### `validate`
@@ -55,7 +55,7 @@ npx owo diff
55
55
  Check your local configuration for errors without applying changes.
56
56
 
57
57
  ```bash
58
- npx owo validate
58
+ npx owosk validate
59
59
  ```
60
60
 
61
61
  ### `connect`
@@ -63,7 +63,7 @@ npx owo validate
63
63
  Authenticate and connect your local environment to an organization.
64
64
 
65
65
  ```bash
66
- npx owo connect
66
+ npx owosk connect
67
67
  ```
68
68
 
69
69
  ## Features
package/dist/index.js CHANGED
@@ -17,6 +17,9 @@ var GLOBAL_CONFIG_PATH = join(GLOBAL_CONFIG_DIR, "config.json");
17
17
  function getApiUrl(configUrl) {
18
18
  return process.env.OWOSTACK_API_URL || configUrl || "https://sandbox.owostack.com";
19
19
  }
20
+ function getLiveApiUrl(configUrl) {
21
+ return process.env.OWOSTACK_API_LIVE_URL || process.env.OWOSTACK_API_URL || configUrl || "https://api.owostack.com";
22
+ }
20
23
  function getTestApiUrl(configUrl) {
21
24
  return process.env.OWOSTACK_API_TEST_URL || configUrl || "https://sandbox.owostack.com";
22
25
  }
@@ -228,19 +231,51 @@ async function fetchCreditPacks(apiKey, apiUrl) {
228
231
  // src/lib/diff.ts
229
232
  import pc2 from "picocolors";
230
233
  import * as p from "@clack/prompts";
231
- function normalizeFeature(pf) {
234
+ function normalizeReset(reset) {
235
+ switch (reset) {
236
+ case "hour":
237
+ return "hourly";
238
+ case "day":
239
+ return "daily";
240
+ case "week":
241
+ return "weekly";
242
+ case "month":
243
+ return "monthly";
244
+ case "quarter":
245
+ return "quarterly";
246
+ case "year":
247
+ case "annually":
248
+ return "yearly";
249
+ default:
250
+ return reset || "monthly";
251
+ }
252
+ }
253
+ function normalizeOverage(usageModel, overage) {
254
+ if (usageModel === "usage_based") return "charge";
255
+ return overage === "charge" ? "charge" : "block";
256
+ }
257
+ function normalizeFeature(pf, creditSystemSlugs) {
258
+ const usageModel = pf.usageModel || "included";
259
+ const isCreditSystemFeature = creditSystemSlugs.has(pf.slug);
232
260
  return {
233
261
  slug: pf.slug,
234
262
  enabled: pf.enabled,
235
263
  limit: pf.limit ?? null,
236
264
  // Handle both SDK 'reset' and API 'resetInterval'
237
- reset: pf.reset || pf.resetInterval || "monthly",
265
+ reset: normalizeReset(pf.reset || pf.resetInterval),
266
+ usageModel: isCreditSystemFeature ? "included" : usageModel,
267
+ pricePerUnit: isCreditSystemFeature ? null : pf.pricePerUnit ?? null,
268
+ billingUnits: isCreditSystemFeature ? 1 : pf.billingUnits ?? 1,
269
+ ratingModel: isCreditSystemFeature ? "package" : pf.ratingModel || "package",
270
+ tiers: isCreditSystemFeature ? null : pf.tiers ?? null,
238
271
  // Handle both SDK 'overage' and API 'overage' (same name)
239
- overage: pf.overage || "block",
240
- overagePrice: pf.overagePrice ?? null
272
+ overage: normalizeOverage(usageModel, pf.overage),
273
+ overagePrice: isCreditSystemFeature ? null : pf.overagePrice ?? null,
274
+ maxOverageUnits: isCreditSystemFeature ? null : pf.maxOverageUnits ?? null,
275
+ creditCost: isCreditSystemFeature ? 0 : pf.creditCost ?? 0
241
276
  };
242
277
  }
243
- function normalizePlan(plan) {
278
+ function normalizePlan(plan, creditSystemSlugs) {
244
279
  return {
245
280
  slug: plan.slug,
246
281
  name: plan.name ?? null,
@@ -252,7 +287,18 @@ function normalizePlan(plan) {
252
287
  trialDays: plan.trialDays ?? 0,
253
288
  isAddon: plan.isAddon ?? false,
254
289
  autoEnable: plan.autoEnable ?? false,
255
- features: (plan.features || []).map(normalizeFeature).sort((a, b) => a.slug.localeCompare(b.slug))
290
+ features: (plan.features || []).map((feature) => normalizeFeature(feature, creditSystemSlugs)).sort((a, b) => a.slug.localeCompare(b.slug))
291
+ };
292
+ }
293
+ function normalizeCreditSystem(cs) {
294
+ return {
295
+ slug: cs.slug,
296
+ name: cs.name ?? null,
297
+ description: cs.description ?? null,
298
+ features: (cs.features || []).map((feature) => ({
299
+ feature: feature.feature,
300
+ creditCost: feature.creditCost ?? 0
301
+ })).sort((a, b) => a.feature.localeCompare(b.feature))
256
302
  };
257
303
  }
258
304
  function normalizeCreditPack(pack) {
@@ -268,10 +314,18 @@ function normalizeCreditPack(pack) {
268
314
  };
269
315
  }
270
316
  function diffPlans(localPlans, remotePlans, localCreditSystems = [], remoteCreditSystems = [], localCreditPacks = [], remoteCreditPacks = []) {
317
+ const creditSystemSlugs = /* @__PURE__ */ new Set([
318
+ ...localCreditSystems.map((cs) => cs.slug),
319
+ ...remoteCreditSystems.map((cs) => cs.slug)
320
+ ]);
271
321
  const localMap = /* @__PURE__ */ new Map();
272
322
  const remoteMap = /* @__PURE__ */ new Map();
273
- for (const p9 of localPlans) localMap.set(p9.slug, normalizePlan(p9));
274
- for (const p9 of remotePlans) remoteMap.set(p9.slug, normalizePlan(p9));
323
+ for (const p9 of localPlans) {
324
+ localMap.set(p9.slug, normalizePlan(p9, creditSystemSlugs));
325
+ }
326
+ for (const p9 of remotePlans) {
327
+ remoteMap.set(p9.slug, normalizePlan(p9, creditSystemSlugs));
328
+ }
275
329
  const onlyLocal = [];
276
330
  const onlyRemote = [];
277
331
  const changed = [];
@@ -325,8 +379,15 @@ function diffPlans(localPlans, remotePlans, localCreditSystems = [], remoteCredi
325
379
  "enabled",
326
380
  "limit",
327
381
  "reset",
382
+ "usageModel",
383
+ "pricePerUnit",
384
+ "billingUnits",
385
+ "ratingModel",
386
+ "tiers",
328
387
  "overage",
329
- "overagePrice"
388
+ "overagePrice",
389
+ "maxOverageUnits",
390
+ "creditCost"
330
391
  ];
331
392
  for (const ff of featureFields) {
332
393
  if (JSON.stringify(lf[ff]) !== JSON.stringify(rf[ff])) {
@@ -350,8 +411,12 @@ function diffPlans(localPlans, remotePlans, localCreditSystems = [], remoteCredi
350
411
  }
351
412
  const localCsMap = /* @__PURE__ */ new Map();
352
413
  const remoteCsMap = /* @__PURE__ */ new Map();
353
- for (const cs of localCreditSystems) localCsMap.set(cs.slug, cs);
354
- for (const cs of remoteCreditSystems) remoteCsMap.set(cs.slug, cs);
414
+ for (const cs of localCreditSystems) {
415
+ localCsMap.set(cs.slug, normalizeCreditSystem(cs));
416
+ }
417
+ for (const cs of remoteCreditSystems) {
418
+ remoteCsMap.set(cs.slug, normalizeCreditSystem(cs));
419
+ }
355
420
  const csOnlyLocal = [];
356
421
  const csOnlyRemote = [];
357
422
  const csChanged = [];
@@ -746,7 +811,7 @@ async function runSync(options) {
746
811
  p2.intro(pc3.bgYellow(pc3.black(" sync ")));
747
812
  const configSettings = await loadConfigSettings(options.config);
748
813
  const testUrl = getTestApiUrl(configSettings.environments?.test);
749
- const liveUrl = getApiUrl(configSettings.environments?.live);
814
+ const liveUrl = getLiveApiUrl(configSettings.environments?.live);
750
815
  if (options.prod) {
751
816
  p2.log.step(pc3.magenta("Production Mode: Syncing to PROD environment"));
752
817
  await runSyncSingle({
@@ -754,7 +819,7 @@ async function runSync(options) {
754
819
  dryRun: !!options.dryRun,
755
820
  autoApprove: !!options.yes,
756
821
  apiKey: options.key || "",
757
- apiUrl: configSettings.apiUrl || liveUrl,
822
+ apiUrl: liveUrl,
758
823
  environment: "prod"
759
824
  });
760
825
  } else {
@@ -776,7 +841,10 @@ import * as p3 from "@clack/prompts";
776
841
  import pc4 from "picocolors";
777
842
  import { existsSync as existsSync3 } from "fs";
778
843
  import { writeFile as writeFile2 } from "fs/promises";
779
- import { resolve as resolve2, extname as extname2, isAbsolute as isAbsolute2 } from "path";
844
+ import { resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
845
+
846
+ // src/lib/catalog-import.ts
847
+ import { extname as extname2 } from "path";
780
848
 
781
849
  // src/lib/generate.ts
782
850
  function slugToIdentifier(slug, used) {
@@ -831,6 +899,28 @@ function slugToIdentifier(slug, used) {
831
899
  used.add(candidate);
832
900
  return candidate;
833
901
  }
902
+ function normalizeResetForCodegen(reset) {
903
+ switch (reset) {
904
+ case void 0:
905
+ case null:
906
+ return void 0;
907
+ case "hour":
908
+ return "hourly";
909
+ case "day":
910
+ return "daily";
911
+ case "week":
912
+ return "weekly";
913
+ case "month":
914
+ return "monthly";
915
+ case "quarter":
916
+ return "quarterly";
917
+ case "year":
918
+ case "annually":
919
+ return "yearly";
920
+ default:
921
+ return reset;
922
+ }
923
+ }
834
924
  function generateConfig(plans, creditSystems = [], creditPacks = [], defaultProvider, format = "ts") {
835
925
  const isTs = format === "ts";
836
926
  const creditSystemSlugs = new Set(creditSystems.map((cs) => cs.slug));
@@ -922,8 +1012,12 @@ function generateConfig(plans, creditSystems = [], creditPacks = [], defaultProv
922
1012
  const csVar = creditSystemVars.get(pf.slug);
923
1013
  if (csVar && pf.enabled) {
924
1014
  const opts = [];
925
- if (pf.resetInterval || pf.reset)
926
- opts.push(`reset: "${pf.resetInterval || pf.reset || "monthly"}"`);
1015
+ const creditReset = normalizeResetForCodegen(
1016
+ pf.resetInterval || pf.reset
1017
+ );
1018
+ if (creditReset !== void 0) {
1019
+ opts.push(`reset: "${creditReset}"`);
1020
+ }
927
1021
  if (pf.overage) opts.push(`overage: "${pf.overage}"`);
928
1022
  if (opts.length > 0) {
929
1023
  featureEntries.push(
@@ -947,11 +1041,23 @@ function generateConfig(plans, creditSystems = [], creditPacks = [], defaultProv
947
1041
  if (pf.enabled === false) {
948
1042
  const config2 = { enabled: false };
949
1043
  if (pf.limit !== void 0) config2.limit = pf.limit;
950
- if (pf.resetInterval || pf.reset)
951
- config2.reset = pf.resetInterval || pf.reset;
1044
+ const disabledReset = normalizeResetForCodegen(
1045
+ pf.resetInterval || pf.reset
1046
+ );
1047
+ if (disabledReset !== void 0) config2.reset = disabledReset;
1048
+ if (pf.usageModel) config2.usageModel = pf.usageModel;
1049
+ if (pf.pricePerUnit !== void 0)
1050
+ config2.pricePerUnit = pf.pricePerUnit;
1051
+ if (pf.ratingModel) config2.ratingModel = pf.ratingModel;
1052
+ if (pf.tiers !== void 0) config2.tiers = pf.tiers;
1053
+ if (pf.billingUnits !== void 0)
1054
+ config2.billingUnits = pf.billingUnits;
952
1055
  if (pf.overage) config2.overage = pf.overage;
953
1056
  if (pf.overagePrice !== void 0)
954
1057
  config2.overagePrice = pf.overagePrice;
1058
+ if (pf.maxOverageUnits !== void 0)
1059
+ config2.maxOverageUnits = pf.maxOverageUnits;
1060
+ if (pf.creditCost !== void 0) config2.creditCost = pf.creditCost;
955
1061
  featureEntries.push(`${varName}.config(${JSON.stringify(config2)})`);
956
1062
  continue;
957
1063
  }
@@ -959,11 +1065,21 @@ function generateConfig(plans, creditSystems = [], creditPacks = [], defaultProv
959
1065
  const config = {};
960
1066
  if (pf.limit !== void 0) config.limit = pf.limit;
961
1067
  if (!isEntityFeature) {
962
- const reset = pf.resetInterval || pf.reset || "monthly";
963
- if (reset !== "none") config.reset = reset;
1068
+ const reset = normalizeResetForCodegen(
1069
+ pf.resetInterval || pf.reset || "monthly"
1070
+ );
1071
+ if (reset !== void 0) config.reset = reset;
964
1072
  }
1073
+ if (pf.usageModel) config.usageModel = pf.usageModel;
1074
+ if (pf.pricePerUnit !== void 0) config.pricePerUnit = pf.pricePerUnit;
1075
+ if (pf.ratingModel) config.ratingModel = pf.ratingModel;
1076
+ if (pf.tiers !== void 0) config.tiers = pf.tiers;
1077
+ if (pf.billingUnits !== void 0) config.billingUnits = pf.billingUnits;
965
1078
  if (pf.overage) config.overage = pf.overage;
966
1079
  if (pf.overagePrice !== void 0) config.overagePrice = pf.overagePrice;
1080
+ if (pf.maxOverageUnits !== void 0)
1081
+ config.maxOverageUnits = pf.maxOverageUnits;
1082
+ if (pf.creditCost !== void 0) config.creditCost = pf.creditCost;
967
1083
  const configKeys = Object.keys(config);
968
1084
  const hasExtras = configKeys.some((k) => k !== "limit");
969
1085
  if (config.limit === null && !hasExtras) {
@@ -1042,8 +1158,8 @@ function generateConfig(plans, creditSystems = [], creditPacks = [], defaultProv
1042
1158
  ].filter(Boolean).join("\n");
1043
1159
  }
1044
1160
 
1045
- // src/commands/pull.ts
1046
- function determineFormat(fullPath) {
1161
+ // src/lib/catalog-import.ts
1162
+ function determineConfigFormat(fullPath) {
1047
1163
  const ext = extname2(fullPath);
1048
1164
  if (ext === ".ts" || ext === ".mts" || ext === ".cts") return "ts";
1049
1165
  if (ext === ".mjs") return "esm";
@@ -1055,6 +1171,32 @@ function determineFormat(fullPath) {
1055
1171
  if (ext === ".js") return "esm";
1056
1172
  return "ts";
1057
1173
  }
1174
+ async function buildRemoteCatalogSnapshot(params) {
1175
+ const plans = await fetchPlans({
1176
+ apiKey: params.apiKey,
1177
+ apiUrl: params.apiUrl,
1178
+ ...params.filters || {}
1179
+ });
1180
+ const creditSystems = await fetchCreditSystems(params.apiKey, params.apiUrl);
1181
+ const creditPacks = await fetchCreditPacks(params.apiKey, params.apiUrl);
1182
+ const providers = new Set(plans.map((plan) => plan.provider).filter(Boolean));
1183
+ const defaultProvider = providers.size === 1 ? Array.from(providers)[0] : void 0;
1184
+ return {
1185
+ plans,
1186
+ creditSystems,
1187
+ creditPacks,
1188
+ defaultProvider,
1189
+ configContent: generateConfig(
1190
+ plans,
1191
+ creditSystems,
1192
+ creditPacks,
1193
+ defaultProvider,
1194
+ params.format
1195
+ )
1196
+ };
1197
+ }
1198
+
1199
+ // src/commands/pull.ts
1058
1200
  async function runPull(options) {
1059
1201
  p3.intro(pc4.bgYellow(pc4.black(" pull ")));
1060
1202
  let fullPath;
@@ -1073,109 +1215,57 @@ async function runPull(options) {
1073
1215
  const apiKey = getApiKey(options.key);
1074
1216
  const configSettings = await loadConfigSettings(options.config);
1075
1217
  const testUrl = getTestApiUrl(configSettings.environments?.test);
1076
- const liveUrl = getApiUrl(configSettings.environments?.live);
1218
+ const liveUrl = getLiveApiUrl(configSettings.environments?.live);
1077
1219
  const filters = configSettings.filters || {};
1078
1220
  let format;
1079
1221
  try {
1080
- format = determineFormat(fullPath);
1222
+ format = determineConfigFormat(fullPath);
1081
1223
  } catch (e) {
1082
1224
  p3.log.error(pc4.red(e.message));
1083
1225
  process.exit(1);
1084
1226
  }
1085
1227
  const s = p3.spinner();
1086
- if (options.prod) {
1087
- p3.log.step(pc4.magenta("Production Mode: Pulling from PROD environment"));
1088
- const apiUrl = `${liveUrl}/api/v1`;
1089
- s.start(`Fetching plans from ${pc4.dim("prod")}...`);
1090
- const plans = await fetchPlans({
1091
- apiKey,
1092
- apiUrl,
1093
- ...filters
1094
- });
1095
- s.stop(`Fetched ${plans.length} plans from prod`);
1096
- s.start(`Fetching credit systems...`);
1097
- const creditSystems = await fetchCreditSystems(apiKey, apiUrl);
1098
- s.stop(`Fetched ${creditSystems.length} credit systems`);
1099
- s.start(`Fetching credit packs...`);
1100
- const creditPacks = await fetchCreditPacks(apiKey, apiUrl);
1101
- s.stop(`Fetched ${creditPacks.length} credit packs`);
1102
- const providers = new Set(
1103
- plans.map((p9) => p9.provider).filter(Boolean)
1104
- );
1105
- const defaultProvider = providers.size === 1 ? Array.from(providers)[0] : void 0;
1106
- const configContent = generateConfig(
1107
- plans,
1108
- creditSystems,
1109
- creditPacks,
1110
- defaultProvider,
1111
- format
1228
+ const modeLabel = options.prod ? "prod" : "sandbox";
1229
+ const modeMessage = options.prod ? pc4.magenta("Production Mode: Pulling from PROD environment") : pc4.cyan("Sandbox Mode: Pulling from SANDBOX environment");
1230
+ const apiUrl = `${options.prod ? liveUrl : testUrl}/api/v1`;
1231
+ p3.log.step(modeMessage);
1232
+ s.start(`Fetching remote catalog from ${pc4.dim(modeLabel)}...`);
1233
+ const snapshot = await buildRemoteCatalogSnapshot({
1234
+ apiKey,
1235
+ apiUrl,
1236
+ format,
1237
+ filters
1238
+ });
1239
+ s.stop(
1240
+ `Fetched ${snapshot.plans.length} plans, ${snapshot.creditSystems.length} credit systems, and ${snapshot.creditPacks.length} credit packs from ${modeLabel}`
1241
+ );
1242
+ if (options.dryRun) {
1243
+ p3.note(snapshot.configContent, "Generated Config (Dry Run)");
1244
+ printPullSummary(
1245
+ snapshot.plans,
1246
+ snapshot.creditSystems,
1247
+ snapshot.creditPacks
1112
1248
  );
1113
- if (options.dryRun) {
1114
- p3.note(configContent, "Generated Config (Dry Run)");
1115
- printPullSummary(plans, creditSystems, creditPacks);
1116
- p3.outro(pc4.yellow("Dry run complete. No changes made."));
1117
- return;
1118
- }
1119
- if (existsSync3(fullPath) && !options.force) {
1120
- const confirm4 = await p3.confirm({
1121
- message: `Config file already exists at ${fullPath}. Overwrite?`,
1122
- initialValue: false
1123
- });
1124
- if (p3.isCancel(confirm4) || !confirm4) {
1125
- p3.outro(pc4.yellow("Operation cancelled"));
1126
- process.exit(0);
1127
- }
1128
- }
1129
- await writeFile2(fullPath, configContent, "utf8");
1130
- p3.log.success(pc4.green(`Wrote configuration to ${fullPath}`));
1131
- printPullSummary(plans, creditSystems, creditPacks);
1132
- } else {
1133
- p3.log.step(pc4.cyan("Sandbox Mode: Pulling from SANDBOX environment"));
1134
- const apiUrl = `${testUrl}/api/v1`;
1135
- s.start(`Fetching plans from ${pc4.dim("sandbox")}...`);
1136
- const plans = await fetchPlans({
1137
- apiKey,
1138
- apiUrl,
1139
- ...filters
1249
+ p3.outro(pc4.yellow("Dry run complete. No changes made."));
1250
+ return;
1251
+ }
1252
+ if (existsSync3(fullPath) && !options.force) {
1253
+ const confirm4 = await p3.confirm({
1254
+ message: `Config file already exists${options.prod ? ` at ${fullPath}` : ""}. Overwrite?`,
1255
+ initialValue: false
1140
1256
  });
1141
- s.stop(`Fetched ${plans.length} plans from sandbox`);
1142
- s.start(`Fetching credit systems...`);
1143
- const creditSystems = await fetchCreditSystems(apiKey, apiUrl);
1144
- s.stop(`Fetched ${creditSystems.length} credit systems`);
1145
- s.start(`Fetching credit packs...`);
1146
- const creditPacks = await fetchCreditPacks(apiKey, apiUrl);
1147
- s.stop(`Fetched ${creditPacks.length} credit packs`);
1148
- const providers = new Set(
1149
- plans.map((p9) => p9.provider).filter(Boolean)
1150
- );
1151
- const defaultProvider = providers.size === 1 ? Array.from(providers)[0] : void 0;
1152
- const configContent = generateConfig(
1153
- plans,
1154
- creditSystems,
1155
- creditPacks,
1156
- defaultProvider,
1157
- format
1158
- );
1159
- if (options.dryRun) {
1160
- p3.note(configContent, "Generated Config (Dry Run)");
1161
- printPullSummary(plans, creditSystems, creditPacks);
1162
- p3.outro(pc4.yellow("Dry run complete. No changes made."));
1163
- return;
1164
- }
1165
- if (existsSync3(fullPath) && !options.force) {
1166
- const confirm4 = await p3.confirm({
1167
- message: `Config file already exists. Overwrite?`,
1168
- initialValue: false
1169
- });
1170
- if (p3.isCancel(confirm4) || !confirm4) {
1171
- p3.outro(pc4.yellow("Operation cancelled"));
1172
- process.exit(0);
1173
- }
1257
+ if (p3.isCancel(confirm4) || !confirm4) {
1258
+ p3.outro(pc4.yellow("Operation cancelled"));
1259
+ process.exit(0);
1174
1260
  }
1175
- await writeFile2(fullPath, configContent, "utf8");
1176
- p3.log.success(pc4.green(`Wrote configuration to ${fullPath}`));
1177
- printPullSummary(plans, creditSystems, creditPacks);
1178
1261
  }
1262
+ await writeFile2(fullPath, snapshot.configContent, "utf8");
1263
+ p3.log.success(pc4.green(`Wrote configuration to ${fullPath}`));
1264
+ printPullSummary(
1265
+ snapshot.plans,
1266
+ snapshot.creditSystems,
1267
+ snapshot.creditPacks
1268
+ );
1179
1269
  p3.outro(pc4.green("Pull complete! \u2728"));
1180
1270
  }
1181
1271
  function printPullSummary(plans, creditSystems, creditPacks = []) {
@@ -1231,7 +1321,7 @@ async function runDiff(options) {
1231
1321
  const apiKey = getApiKey(options.key);
1232
1322
  const configSettings = await loadConfigSettings(options.config);
1233
1323
  const testUrl = getTestApiUrl(configSettings.environments?.test);
1234
- const liveUrl = getApiUrl(configSettings.environments?.live);
1324
+ const liveUrl = getLiveApiUrl(configSettings.environments?.live);
1235
1325
  const s = p4.spinner();
1236
1326
  if (options.prod) {
1237
1327
  p4.log.step(pc5.magenta("Production Mode: Comparing with PROD environment"));
@@ -1329,7 +1419,7 @@ import * as p6 from "@clack/prompts";
1329
1419
  import pc7 from "picocolors";
1330
1420
  import { existsSync as existsSync4 } from "fs";
1331
1421
  import { writeFile as writeFile3 } from "fs/promises";
1332
- import { join as join2, resolve as resolve3, isAbsolute as isAbsolute3, extname as extname3 } from "path";
1422
+ import { join as join2, resolve as resolve3, isAbsolute as isAbsolute3 } from "path";
1333
1423
 
1334
1424
  // src/lib/connect.ts
1335
1425
  import * as p5 from "@clack/prompts";
@@ -1447,16 +1537,21 @@ async function runInit(options) {
1447
1537
  }
1448
1538
  }
1449
1539
  const fullPath = isAbsolute3(targetPath) ? targetPath : resolve3(process.cwd(), targetPath);
1540
+ const configSettings = await loadConfigSettings(options.config);
1541
+ const testUrl = getTestApiUrl(
1542
+ configSettings.environments?.test || configSettings.apiUrl
1543
+ );
1544
+ const filters = configSettings.filters || {};
1450
1545
  let apiKey = getApiKey(options.key);
1451
1546
  if (!apiKey) {
1452
1547
  p6.log.warn(
1453
1548
  pc7.yellow("No API key found. Let's connect your account first.")
1454
1549
  );
1455
1550
  apiKey = await executeConnectFlow({
1456
- apiUrl: getApiUrl(),
1457
- dashboardUrl: getDashboardUrl(),
1551
+ apiUrl: testUrl,
1552
+ dashboardUrl: getDashboardUrl(configSettings.connect?.dashboardUrl),
1458
1553
  noBrowser: false,
1459
- timeout: 300
1554
+ timeout: configSettings.connect?.timeout || 300
1460
1555
  }) || "";
1461
1556
  if (!apiKey) {
1462
1557
  p6.log.error(pc7.red("Could not obtain API key. Initialization aborted."));
@@ -1476,36 +1571,21 @@ async function runInit(options) {
1476
1571
  const s = p6.spinner();
1477
1572
  s.start("Generating project configuration...");
1478
1573
  try {
1479
- const apiUrl = `${getApiUrl()}/api/v1`;
1480
- const plans = await fetchPlans({ apiKey, apiUrl });
1481
- const creditSystems = await fetchCreditSystems(apiKey, apiUrl);
1482
- const ext = extname3(fullPath);
1483
- let format = "ts";
1484
- if (ext === ".ts" || ext === ".mts" || ext === ".cts") {
1485
- format = "ts";
1486
- } else if (ext === ".mjs") {
1487
- format = "esm";
1488
- } else if (ext === ".cjs") {
1489
- throw new Error(
1490
- "CommonJS config files are not supported. Use owo.config.js or owo.config.ts."
1491
- );
1492
- } else if (ext === ".js") {
1493
- format = "esm";
1494
- }
1495
- const configContent = generateConfig(
1496
- plans,
1497
- creditSystems,
1498
- [],
1499
- void 0,
1500
- format
1501
- );
1502
- await writeFile3(fullPath, configContent, "utf8");
1574
+ const format = determineConfigFormat(fullPath);
1575
+ const snapshot = await buildRemoteCatalogSnapshot({
1576
+ apiKey,
1577
+ apiUrl: `${testUrl}/api/v1`,
1578
+ format,
1579
+ filters
1580
+ });
1581
+ await writeFile3(fullPath, snapshot.configContent, "utf8");
1503
1582
  s.stop(pc7.green("Configuration created"));
1504
1583
  p6.note(
1505
1584
  `${pc7.dim("File:")} ${fullPath}
1506
1585
  ${pc7.dim("Format:")} ${format.toUpperCase()}
1507
- ${pc7.dim("Plans:")} ${plans.length} imported
1508
- ${pc7.dim("Credit Systems:")} ${creditSystems.length}`,
1586
+ ${pc7.dim("Plans:")} ${snapshot.plans.length} imported
1587
+ ${pc7.dim("Credit Systems:")} ${snapshot.creditSystems.length}
1588
+ ${pc7.dim("Credit Packs:")} ${snapshot.creditPacks.length}`,
1509
1589
  "\u2728 Project Initialized"
1510
1590
  );
1511
1591
  p6.outro(
@@ -1585,7 +1665,7 @@ async function runValidate(options) {
1585
1665
  }
1586
1666
  const configSettings = await loadConfigSettings(options.config);
1587
1667
  const testUrl = getTestApiUrl(configSettings.environments?.test);
1588
- const liveUrl = getApiUrl(configSettings.environments?.live);
1668
+ const liveUrl = getLiveApiUrl(configSettings.environments?.live);
1589
1669
  const apiKey = getApiKey();
1590
1670
  if (options.prod) {
1591
1671
  p7.log.step(pc8.magenta("Production Mode: Checking PROD environment"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "owosk",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for Owostack - sync catalog, manage billing infrastructure",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -24,8 +24,8 @@
24
24
  "commander": "^14.0.3",
25
25
  "jiti": "^2.6.1",
26
26
  "picocolors": "^1.1.1",
27
- "@owostack/types": "0.2.0",
28
- "owostack": "0.2.0"
27
+ "@owostack/types": "0.3.0",
28
+ "owostack": "0.3.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.19.10",