@enerlence/suntropy-cli 0.11.2 → 0.11.4

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/README.md CHANGED
@@ -35,6 +35,27 @@ suntropy auth status
35
35
  > root command (e.g. `suntropy --profile dev auth status`). They apply to every
36
36
  > subcommand, including `auth`.
37
37
 
38
+ ### Token resolution order
39
+
40
+ The auth token is resolved in this order (first non-empty wins):
41
+
42
+ 1. `--token <jwt>` global flag
43
+ 2. `SUNTROPY_API_KEY` environment variable
44
+ 3. `token` stored in the active profile (`~/.suntropy/config.json`, written by
45
+ `auth set-key` / `auth login`)
46
+
47
+ The `SUNTROPY_API_KEY` fallback (available since `0.11.3`) lets a host runtime
48
+ inject a just-in-time credential without a prior `auth login` / `auth set-key`:
49
+
50
+ ```bash
51
+ SUNTROPY_API_KEY=<jwt> suntropy studies list
52
+ ```
53
+
54
+ > ⚠️ The value must be a **JWT** (the one you'd pass to `auth set-key --key`),
55
+ > since the CLI sends it as `Authorization: Bearer <token>`. Despite the name, it
56
+ > is not a `shp_`/`devic-`-style API key — an invalid format yields a 401 from
57
+ > the backend.
58
+
38
59
  ## Global Options
39
60
 
40
61
  | Option | Default | Description |
@@ -42,7 +63,7 @@ suntropy auth status
42
63
  | `--format json\|human\|csv` | `json` | Output format |
43
64
  | `--fields f1,f2,...` | all | Select specific fields |
44
65
  | `--server <url>` | config | Override API server URL |
45
- | `--token <jwt>` | config | Override auth token |
66
+ | `--token <jwt>` | `SUNTROPY_API_KEY` env / config | Override auth token (see "Token resolution order") |
46
67
  | `--profile <name>` | default | Config profile |
47
68
  | `--verbose` | false | Show HTTP details on stderr |
48
69
  | `--quiet` | false | Suppress non-data output |
@@ -92,9 +92,9 @@ function resolveAuth(opts) {
92
92
  const config = loadConfig();
93
93
  const profile = getActiveProfile(config, opts.profile);
94
94
  const server = opts.server || profile.server;
95
- const token = opts.token || profile.token;
95
+ const token = opts.token || process.env.SUNTROPY_API_KEY || profile.token;
96
96
  if (!token) {
97
- throw new Error("Not authenticated. Run: suntropy auth set-key --key <jwt> or suntropy auth login");
97
+ throw new Error("Not authenticated. Run: suntropy auth set-key --key <jwt> or suntropy auth login, or set the SUNTROPY_API_KEY env var.");
98
98
  }
99
99
  return { server, token };
100
100
  }
@@ -2635,7 +2635,7 @@ Examples:
2635
2635
  });
2636
2636
  set.command("economics").description(
2637
2637
  "Set economic parameters for the study.\nExample: suntropy studies set economics --file study.json --margin 15 --total-cost 6500 --lifetime 25"
2638
- ).option("--file <path>", "Study file path").option("--margin <n>", "Margin percentage").option("--total-cost <n>", "Total installation cost (\u20AC)").option("--lifetime <n>", "Installation lifetime in years").option("--inflation <n>", "Inflation rate percentage").option("--taxes-pct <n>", "Tax percentage").option("--include-taxes", "Include taxes in pricing").option("--commercial-fee <n>", "Commercial fee percentage").option("--excesses-mode <mode>", "Excesses compensation: gridSelling, PPA, noInjection, virtualBattery").option("--excesses-buy-price <n>", "Excesses buy price (\u20AC/MWh)").option("--excesses-selling-price <n>", "Excesses selling price (\u20AC/MWh)").option("--peak-power-cost <n>", "Cost per kWp (\u20AC/kWp)").option("--guarantee-production <n>", "Guarantee production percentage").action(async (opts) => {
2638
+ ).option("--file <path>", "Study file path").option("--margin <n>", "Margin percentage").option("--total-cost <n>", "Total installation cost (\u20AC)").option("--lifetime <n>", "Installation lifetime in years").option("--inflation <n>", "Inflation rate percentage").option("--taxes-pct <n>", "Tax percentage").option("--include-taxes", "Include taxes in pricing").option("--commercial-fee <n>", "Commercial fee percentage").option("--excesses-mode <mode>", "Excesses compensation: gridSelling, PPA, noInjection, virtualBattery").option("--excesses-buy-price <n>", "Excesses buy price (\u20AC/MWh)").option("--excesses-selling-price <n>", "Excesses selling price (\u20AC/MWh)").option("--peak-power-cost <n>", "Cost per Wp (\u20AC/Wp). If --total-cost is omitted, totalCost is derived as cost \xD7 installed peak power (W)").option("--guarantee-production <n>", "Guarantee production percentage").action(async (opts) => {
2639
2639
  try {
2640
2640
  const result = updateStudy(resolveFile(opts), (study) => {
2641
2641
  const er = study.economicResults || {};
@@ -2651,6 +2651,30 @@ Examples:
2651
2651
  if (opts.excessesSellingPrice !== void 0) er.excessesSellingPrice = parseFloat(opts.excessesSellingPrice);
2652
2652
  if (opts.peakPowerCost !== void 0) er.peakPowerCost = parseFloat(opts.peakPowerCost);
2653
2653
  if (opts.guaranteeProduction !== void 0) er.guaranteeProductionPercentage = parseFloat(opts.guaranteeProduction);
2654
+ if (opts.peakPowerCost !== void 0 && opts.totalCost === void 0) {
2655
+ const surfaces = study.surfaces || [];
2656
+ let totalWatts = 0;
2657
+ let resolvedCount = 0;
2658
+ let anyDefault = false;
2659
+ for (const surface of surfaces) {
2660
+ const { installedPower, isDefault } = resolveSurfaceInstalledPower(surface, study);
2661
+ if (isDefault) {
2662
+ anyDefault = true;
2663
+ continue;
2664
+ }
2665
+ totalWatts += installedPower;
2666
+ resolvedCount += 1;
2667
+ }
2668
+ if (resolvedCount > 0 && !anyDefault) {
2669
+ er.totalCost = Math.round(parseFloat(opts.peakPowerCost) * totalWatts * 100) / 100;
2670
+ } else {
2671
+ process.stderr.write(
2672
+ JSON.stringify({
2673
+ warning: "Set --peak-power-cost but could not derive totalCost: " + (surfaces.length === 0 ? "the study has no surfaces." : "some surfaces have no resolvable installed power (set panels + kit/panel first).") + " Pass --total-cost explicitly to set it."
2674
+ }) + "\n"
2675
+ );
2676
+ }
2677
+ }
2654
2678
  study.economicResults = er;
2655
2679
  return void 0;
2656
2680
  });
@@ -4598,7 +4622,7 @@ function registerCommandProfileCommand(program2) {
4598
4622
  }
4599
4623
 
4600
4624
  // src/index.ts
4601
- var CLI_VERSION = true ? "0.11.2" : "0.0.0-dev";
4625
+ var CLI_VERSION = true ? "0.11.4" : "0.0.0-dev";
4602
4626
  function createProgram() {
4603
4627
  const program2 = new Command3();
4604
4628
  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)");