@enerlence/suntropy-cli 0.6.0 → 0.7.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/README.md CHANGED
@@ -20,6 +20,10 @@ npx @enerlence/suntropy-cli <command>
20
20
  # API key (preferred for agents)
21
21
  suntropy auth set-key --key <jwt-api-key>
22
22
 
23
+ # Target a specific server / profile (global options — see "Global Options")
24
+ suntropy --server https://api-dev.example.cloud --profile dev auth set-key --key <jwt>
25
+ suntropy --profile dev auth status
26
+
23
27
  # Email/password login
24
28
  suntropy auth login --email user@co.com --password pass
25
29
 
@@ -27,6 +31,10 @@ suntropy auth login --email user@co.com --password pass
27
31
  suntropy auth status
28
32
  ```
29
33
 
34
+ > `--server`, `--profile` and `--token` are **global** options: pass them on the
35
+ > root command (e.g. `suntropy --profile dev auth status`). They apply to every
36
+ > subcommand, including `auth`.
37
+
30
38
  ## Global Options
31
39
 
32
40
  | Option | Default | Description |
@@ -254,6 +262,30 @@ suntropy shareables create --element-id <studyId> --data '{"customLayout":true}'
254
262
  `--element-type` (default `solarStudy`): `solarStudy | colectiveSolarStudy | veChargerStudy | heatpumpStudy | billing`.
255
263
  `--shareable-type` (default `TEMPLATE`): `TEMPLATE | CONTRACT`. The response includes the public `url` and `uid`.
256
264
 
265
+ ### `suntropy templates` - Document Templates
266
+
267
+ List the client document templates (budget/document templates from the sharing
268
+ service), proxied via solar under `GET /api/templates`. Returns only `_id` and
269
+ `templateName`.
270
+
271
+ ```bash
272
+ # Solar study templates (default --type solarStudy)
273
+ suntropy templates list
274
+
275
+ # Other template types
276
+ suntropy templates list --type colectiveSolarStudy
277
+ suntropy templates list --type veChargerStudy
278
+
279
+ # Generic (untyped) templates
280
+ suntropy templates list --type generic
281
+
282
+ # Pick fields / change format
283
+ suntropy templates list --fields _id,templateName --format csv
284
+ ```
285
+
286
+ `--type` (default `solarStudy`): `solarStudy | colectiveSolarStudy | veChargerStudy | generic`.
287
+ `generic` lists the untyped templates (sends no `templateIdentifier` filter).
288
+
257
289
  ### `suntropy config` - Configuration
258
290
 
259
291
  ```bash
@@ -275,17 +275,18 @@ function getGlobalOpts(cmd) {
275
275
  }
276
276
  function registerAuthCommands(program2) {
277
277
  const auth = program2.command("auth").description("Authentication management");
278
- auth.command("set-key").description("Set an API key (JWT) for authentication. Preferred method for agents.").requiredOption("--key <jwt>", "JWT API key").option("--server <url>", "API server URL").option("--profile <name>", "Profile name").action(async (opts) => {
278
+ auth.command("set-key").description("Set an API key (JWT) for authentication. Preferred method for agents.").requiredOption("--key <jwt>", "JWT API key").action(async (opts) => {
279
279
  try {
280
+ const global = getGlobalOpts(auth);
280
281
  const config = loadConfig();
281
- const profileName = opts.profile || config.activeProfile;
282
+ const profileName = global.profile || config.activeProfile;
282
283
  if (!config.profiles[profileName]) {
283
284
  config.profiles[profileName] = { server: "https://api.enerlence.com" };
284
285
  }
285
286
  const profile = config.profiles[profileName];
286
287
  profile.token = opts.key;
287
288
  profile.authMethod = "api-key";
288
- if (opts.server) profile.server = opts.server;
289
+ if (global.server) profile.server = global.server;
289
290
  try {
290
291
  const payload = JSON.parse(Buffer.from(opts.key.split(".")[1], "base64").toString());
291
292
  profile.clientUID = payload.clientUID;
@@ -298,15 +299,16 @@ function registerAuthCommands(program2) {
298
299
  outputError(err);
299
300
  }
300
301
  });
301
- auth.command("login").description("Login with email and password").requiredOption("--email <email>", "User email").requiredOption("--password <password>", "User password").option("--server <url>", "API server URL").option("--profile <name>", "Profile name").action(async (opts) => {
302
+ auth.command("login").description("Login with email and password").requiredOption("--email <email>", "User email").requiredOption("--password <password>", "User password").action(async (opts) => {
302
303
  try {
304
+ const global = getGlobalOpts(auth);
303
305
  const config = loadConfig();
304
- const profileName = opts.profile || config.activeProfile;
306
+ const profileName = global.profile || config.activeProfile;
305
307
  if (!config.profiles[profileName]) {
306
308
  config.profiles[profileName] = { server: "https://api.enerlence.com" };
307
309
  }
308
310
  const profile = config.profiles[profileName];
309
- if (opts.server) profile.server = opts.server;
311
+ if (global.server) profile.server = global.server;
310
312
  const securityUrl = getServiceUrl(profile.server, "security");
311
313
  const client = createUnauthClient(securityUrl);
312
314
  const res = await client.post("/auth/login", { email: opts.email, password: opts.password });
@@ -334,10 +336,11 @@ function registerAuthCommands(program2) {
334
336
  outputError(handleApiError(err));
335
337
  }
336
338
  });
337
- auth.command("status").description("Show current authentication status").option("--profile <name>", "Profile name").action(async (opts) => {
339
+ auth.command("status").description("Show current authentication status").action(async () => {
338
340
  try {
341
+ const global = getGlobalOpts(auth);
339
342
  const config = loadConfig();
340
- const profile = getActiveProfile(config, opts.profile);
343
+ const profile = getActiveProfile(config, global.profile);
341
344
  if (!profile.token) {
342
345
  output({ authenticated: false, message: "No token configured" }, getGlobalOpts(auth));
343
346
  return;
@@ -367,11 +370,11 @@ function registerAuthCommands(program2) {
367
370
  outputError(err);
368
371
  }
369
372
  });
370
- auth.command("refresh").description("Refresh the current JWT token").option("--profile <name>", "Profile name").action(async (opts) => {
373
+ auth.command("refresh").description("Refresh the current JWT token").action(async () => {
371
374
  try {
375
+ const globalOpts = getGlobalOpts(auth);
372
376
  const config = loadConfig();
373
- const profileName = opts.profile || config.activeProfile;
374
- const globalOpts = program2.opts();
377
+ const profileName = globalOpts.profile || config.activeProfile;
375
378
  const client = createServiceClient("security", { ...globalOpts, profile: profileName });
376
379
  const res = await client.get("/auth/jwt/refreshToken");
377
380
  if (res.data?.access_token || res.data?.token?.access_token) {
@@ -4206,12 +4209,44 @@ function registerShareableCommands(program2) {
4206
4209
  });
4207
4210
  }
4208
4211
 
4209
- // src/commands/geocode/index.ts
4212
+ // src/commands/templates/index.ts
4210
4213
  function getGlobalOpts14(cmd) {
4211
4214
  let root = cmd;
4212
4215
  while (root.parent) root = root.parent;
4213
4216
  return root.opts();
4214
4217
  }
4218
+ function registerTemplatesCommands(program2) {
4219
+ const templates = program2.command("templates").description(
4220
+ "Read client document templates (id + name), proxied via solar from the sharing service."
4221
+ );
4222
+ templates.command("list").description(
4223
+ "List templates returning only _id and templateName (GET /api/templates).\nDefaults to the solar study templates (--type solarStudy).\n\nTypes: solarStudy (default) | colectiveSolarStudy | veChargerStudy.\nUse --type generic for the generic (untyped) templates.\n\nExamples:\n suntropy templates list\n suntropy templates list --type colectiveSolarStudy\n suntropy templates list --type generic --fields _id,templateName --format csv"
4224
+ ).option(
4225
+ "--type <identifier>",
4226
+ "solarStudy (default) | colectiveSolarStudy | veChargerStudy | generic",
4227
+ "solarStudy"
4228
+ ).action(async (opts) => {
4229
+ try {
4230
+ const global = getGlobalOpts14(templates);
4231
+ const client = createServiceClient("solar", global);
4232
+ const params = {};
4233
+ if (opts.type && opts.type !== "generic") {
4234
+ params.templateIdentifier = opts.type;
4235
+ }
4236
+ const res = await client.get("/api/templates", { params });
4237
+ output(res.data, global);
4238
+ } catch (err) {
4239
+ outputError(handleApiError(err));
4240
+ }
4241
+ });
4242
+ }
4243
+
4244
+ // src/commands/geocode/index.ts
4245
+ function getGlobalOpts15(cmd) {
4246
+ let root = cmd;
4247
+ while (root.parent) root = root.parent;
4248
+ return root.opts();
4249
+ }
4215
4250
  function emit(envelope, all, query, global) {
4216
4251
  if (envelope.error) {
4217
4252
  outputError({
@@ -4245,7 +4280,7 @@ function registerGeocodeCommands(program2) {
4245
4280
  'Resolve an address into coordinates (lat/lng + formatted address).\nExamples:\n suntropy geocode resolve --address "Calle Mayor 1, Madrid"\n suntropy geocode resolve --address "Gran V\xEDa, Madrid" --country es --all'
4246
4281
  ).requiredOption("--address <string>", "Address to geocode (quote it)").option("--country <code>", "Bias results to a country (ISO code, e.g. es, pt, it)").option("--all", "Return all candidate matches instead of only the best one").action(async (opts) => {
4247
4282
  try {
4248
- const global = getGlobalOpts14(geocode);
4283
+ const global = getGlobalOpts15(geocode);
4249
4284
  const client = createServiceClient("solar", global);
4250
4285
  const res = await client.get(
4251
4286
  "/api/geocode",
@@ -4260,7 +4295,7 @@ function registerGeocodeCommands(program2) {
4260
4295
  "Resolve coordinates into an address (reverse geocoding).\nExample:\n suntropy geocode reverse --lat 40.4168 --lng -3.7038"
4261
4296
  ).requiredOption("--lat <number>", "Latitude").requiredOption("--lng <number>", "Longitude").option("--all", "Return all candidate matches instead of only the best one").action(async (opts) => {
4262
4297
  try {
4263
- const global = getGlobalOpts14(geocode);
4298
+ const global = getGlobalOpts15(geocode);
4264
4299
  const client = createServiceClient("solar", global);
4265
4300
  const res = await client.get(
4266
4301
  "/api/reverse-geocode",
@@ -4274,7 +4309,7 @@ function registerGeocodeCommands(program2) {
4274
4309
  }
4275
4310
 
4276
4311
  // src/index.ts
4277
- var CLI_VERSION = true ? "0.6.0" : "0.0.0-dev";
4312
+ var CLI_VERSION = true ? "0.7.0" : "0.0.0-dev";
4278
4313
  function createProgram() {
4279
4314
  const program2 = new Command2();
4280
4315
  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)");
@@ -4287,6 +4322,7 @@ function createProgram() {
4287
4322
  registerSolarformCommands(program2);
4288
4323
  registerPPACommands(program2);
4289
4324
  registerShareableCommands(program2);
4325
+ registerTemplatesCommands(program2);
4290
4326
  registerGeocodeCommands(program2);
4291
4327
  return program2;
4292
4328
  }