@enerlence/suntropy-cli 0.11.8 → 0.11.9

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.
@@ -1324,7 +1324,11 @@ function createResourceCommands(cfg) {
1324
1324
  const client = createServiceClient(service, global);
1325
1325
  const body = parseData(opts.data);
1326
1326
  if (cfg.putBodyOnly) {
1327
- const res = await client.put(cfg.basePath, { ...body, [cfg.idField]: id });
1327
+ const numericId = Number(id);
1328
+ const res = await client.put(cfg.basePath, {
1329
+ ...body,
1330
+ [cfg.idField]: Number.isFinite(numericId) ? numericId : id
1331
+ });
1328
1332
  output(res.data, global);
1329
1333
  } else {
1330
1334
  const res = await client.put(`${cfg.basePath}/${id}`, body);
@@ -2141,6 +2145,21 @@ function updateStudy(filePath, updater) {
2141
2145
  cascadeResets
2142
2146
  };
2143
2147
  }
2148
+ function distributePanels(surfaces, total) {
2149
+ if (!surfaces.length) return [];
2150
+ if (surfaces.length === 1) return [total];
2151
+ const current = surfaces.map((s) => Number(s.panelNumber) || 0);
2152
+ const currentTotal = current.reduce((a, b) => a + b, 0);
2153
+ const weights = currentTotal > 0 ? current.map((n) => n / currentTotal) : surfaces.map(() => 1 / surfaces.length);
2154
+ const counts = weights.map((w) => Math.floor(total * w));
2155
+ let remainder = total - counts.reduce((a, b) => a + b, 0);
2156
+ const order = counts.map((n, i) => ({ n, i })).sort((a, b) => b.n - a.n).map((x) => x.i);
2157
+ for (let k = 0; remainder > 0; k = (k + 1) % order.length) {
2158
+ counts[order[k]] += 1;
2159
+ remainder -= 1;
2160
+ }
2161
+ return counts;
2162
+ }
2144
2163
  function deepMerge(target, source) {
2145
2164
  for (const key of Object.keys(source)) {
2146
2165
  const sv = source[key];
@@ -2559,26 +2578,45 @@ Examples:
2559
2578
  }
2560
2579
  });
2561
2580
  set.command("panel").description(
2562
- 'Set solar panel for the study. Auto-sets peakPowerIntroductionMode to "solarPanel".\nFetches full panel data from inventory.\nExample: suntropy studies set panel --file study.json --panel-id 456 --panels-count 12'
2563
- ).option("--file <path>", "Study file path").requiredOption("--panel-id <n>", "Solar panel ID from inventory").option("--panels-count <n>", "Number of panels").action(async (opts) => {
2581
+ 'Set solar panel for the study. Auto-sets peakPowerIntroductionMode to "solarPanel".\nFetches full panel data from inventory.\n--panels-count is the TOTAL for the study: it is spread over the surfaces keeping\ntheir current proportion (use --surface-index to target a single surface).\nThe response lists the resulting panelNumber per surface.\nExample: suntropy studies set panel --file study.json --panel-id 456 --panels-count 12'
2582
+ ).option("--file <path>", "Study file path").requiredOption("--panel-id <n>", "Solar panel ID from inventory").option("--panels-count <n>", "TOTAL number of panels of the study, spread over its surfaces keeping their current proportion").option("--surface-index <n>", "Apply --panels-count to this surface only (0-based) instead of spreading it").action(async (opts) => {
2564
2583
  try {
2565
2584
  const global = getGlobalOpts7(studies);
2566
2585
  const solarClient = createServiceClient("solar", global);
2567
2586
  const panelRes = await solarClient.get(`/solar-panels/${opts.panelId}`);
2568
2587
  const panel = panelRes.data;
2588
+ let distribution = [];
2569
2589
  const result = updateStudy(resolveFile(opts), (study) => {
2570
2590
  study.solarPanel = panel;
2571
2591
  study.peakPowerIntroductionMode = "solarPanel";
2572
2592
  study.solarKit = void 0;
2573
2593
  if (opts.panelsCount) {
2574
2594
  const surfaces = study.surfaces;
2595
+ const total = parseInt(opts.panelsCount);
2575
2596
  if (surfaces?.length) {
2576
- surfaces[0].panelNumber = parseInt(opts.panelsCount);
2597
+ if (opts.surfaceIndex !== void 0) {
2598
+ const idx = parseInt(opts.surfaceIndex);
2599
+ if (!Number.isInteger(idx) || idx < 0 || idx >= surfaces.length) {
2600
+ throw new Error(
2601
+ `--surface-index ${opts.surfaceIndex} is out of range: the study has ${surfaces.length} surface(s)`
2602
+ );
2603
+ }
2604
+ surfaces[idx].panelNumber = total;
2605
+ } else {
2606
+ const counts = distributePanels(surfaces, total);
2607
+ surfaces.forEach((surface, i) => {
2608
+ surface.panelNumber = counts[i];
2609
+ });
2610
+ }
2611
+ distribution = surfaces.map((surface, i) => ({
2612
+ surfaceIndex: i,
2613
+ panelNumber: Number(surface.panelNumber) || 0
2614
+ }));
2577
2615
  }
2578
2616
  }
2579
2617
  return void 0;
2580
2618
  });
2581
- output(result, global);
2619
+ output(distribution.length ? { ...result, surfaces: distribution } : result, global);
2582
2620
  } catch (err) {
2583
2621
  outputError(handleApiError(err));
2584
2622
  }
@@ -2610,7 +2648,7 @@ Examples:
2610
2648
  }
2611
2649
  });
2612
2650
  set.command("inverter").description(
2613
- "Set inverter(s) for the study (when using solarPanel mode).\nExample: suntropy studies set inverter --file study.json --inverter-id 789"
2651
+ "Set inverter(s) for the study (when using solarPanel mode).\nREPLACES the whole list: pass every inverter of the installation, comma-separated,\nrepeating an id as many times as units there are. The response reports how many\nwere there before and how many are there now.\nExample: suntropy studies set inverter --file study.json --inverter-id 789,789"
2614
2652
  ).option("--file <path>", "Study file path").requiredOption("--inverter-id <ids>", "Inverter ID(s), comma-separated for multiple").action(async (opts) => {
2615
2653
  try {
2616
2654
  const global = getGlobalOpts7(studies);
@@ -2621,11 +2659,13 @@ Examples:
2621
2659
  const res = await solarClient.get(`/solar-inverter/${id}`);
2622
2660
  inverters.push(res.data);
2623
2661
  }
2662
+ let previousCount = 0;
2624
2663
  const result = updateStudy(resolveFile(opts), (study) => {
2664
+ previousCount = study.solarInverters?.length ?? 0;
2625
2665
  study.solarInverters = inverters;
2626
2666
  return void 0;
2627
2667
  });
2628
- output(result, global);
2668
+ output({ ...result, inverters: { before: previousCount, after: inverters.length } }, global);
2629
2669
  } catch (err) {
2630
2670
  outputError(handleApiError(err));
2631
2671
  }
@@ -4729,7 +4769,7 @@ function registerCommandProfileCommand(program2) {
4729
4769
  }
4730
4770
 
4731
4771
  // src/index.ts
4732
- var CLI_VERSION = true ? "0.11.8" : "0.0.0-dev";
4772
+ var CLI_VERSION = true ? "0.11.9" : "0.0.0-dev";
4733
4773
  function createProgram() {
4734
4774
  const program2 = new Command4();
4735
4775
  program2.name("suntropy").description("Agent-first CLI for Suntropy solar platform. Optimized for programmatic data manipulation and progressive exploration.").version(CLI_VERSION).option("--format <format>", "Output format: json (default), human, csv", "json").option("--fields <fields>", "Comma-separated fields to include in output").option("--server <url>", "Override API server URL").option("--token <jwt>", "Override authentication token").option("--profile <name>", "Use a specific config profile").option("--verbose", "Show HTTP request/response details on stderr").option("--quiet", "Suppress non-data output").option("--save <file>", "Save output to file (also writes to stdout)");