@enerlence/suntropy-cli 0.6.0 → 0.8.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 +32 -0
- package/dist/bin/suntropy.js +73 -23
- package/dist/bin/suntropy.js.map +1 -1
- package/package.json +1 -1
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
|
package/dist/bin/suntropy.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
|
-
import { Command as
|
|
10
|
+
import { Command as Command3 } from "commander";
|
|
11
11
|
|
|
12
12
|
// src/config.ts
|
|
13
13
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -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").
|
|
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 =
|
|
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 (
|
|
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").
|
|
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 =
|
|
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 (
|
|
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").
|
|
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,
|
|
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").
|
|
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 =
|
|
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) {
|
|
@@ -1794,6 +1797,7 @@ function registerInventoryCommands(program2) {
|
|
|
1794
1797
|
}
|
|
1795
1798
|
|
|
1796
1799
|
// src/commands/studies/builder.ts
|
|
1800
|
+
import { Option } from "commander";
|
|
1797
1801
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync2 } from "fs";
|
|
1798
1802
|
|
|
1799
1803
|
// node_modules/uuid/dist/esm-node/rng.js
|
|
@@ -3141,11 +3145,15 @@ Example: suntropy studies set data --file study.json --data '{"referenceId":"REF
|
|
|
3141
3145
|
});
|
|
3142
3146
|
studies.command("comment <studyId>").description(
|
|
3143
3147
|
'Add a comment to an existing study via API.\nExample: suntropy studies comment abc123 --content "Revisado por agente"'
|
|
3144
|
-
).requiredOption("--content <text>", "Comment text").action(async (studyId, opts) => {
|
|
3148
|
+
).requiredOption("--content <text>", "Comment text").addOption(new Option("--as-alexandria").hideHelp()).addOption(new Option("--reply-to-user <userUID>").hideHelp()).addOption(new Option("--reply-to-name <name>").hideHelp()).action(async (studyId, opts) => {
|
|
3145
3149
|
try {
|
|
3146
3150
|
const global = getGlobalOpts7(studies);
|
|
3147
3151
|
const client = createServiceClient("solar", global);
|
|
3148
|
-
const comment = createComment("commented", opts.content
|
|
3152
|
+
const comment = createComment("commented", opts.content, {
|
|
3153
|
+
asAlexandria: Boolean(opts.asAlexandria),
|
|
3154
|
+
replyToUser: opts.replyToUser,
|
|
3155
|
+
replyToName: opts.replyToName
|
|
3156
|
+
});
|
|
3149
3157
|
const res = await client.post(`/solar-study/addSolarStudyComment/${studyId}`, comment);
|
|
3150
3158
|
output(res.data, global);
|
|
3151
3159
|
} catch (err) {
|
|
@@ -3268,19 +3276,28 @@ async function fetchPeriodDistribution(study, global) {
|
|
|
3268
3276
|
return null;
|
|
3269
3277
|
}
|
|
3270
3278
|
}
|
|
3271
|
-
function createComment(type, content) {
|
|
3279
|
+
function createComment(type, content, extra) {
|
|
3272
3280
|
const config = loadConfig();
|
|
3273
3281
|
const profile = getActiveProfile(config);
|
|
3274
3282
|
const autoContent = {
|
|
3275
3283
|
created: "Estudio creado via CLI",
|
|
3276
3284
|
modified: "Estudio actualizado via CLI"
|
|
3277
3285
|
};
|
|
3278
|
-
|
|
3279
|
-
|
|
3286
|
+
let body = content || autoContent[type] || "";
|
|
3287
|
+
if (extra?.replyToUser) {
|
|
3288
|
+
const name = extra.replyToName || extra.replyToUser;
|
|
3289
|
+
body = `@[${name}](${extra.replyToUser}) ${body}`.trim();
|
|
3290
|
+
}
|
|
3291
|
+
const comment = {
|
|
3292
|
+
content: body,
|
|
3280
3293
|
type,
|
|
3281
3294
|
creationTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3282
|
-
creationUserUID: profile.userUID || "cli-agent"
|
|
3295
|
+
creationUserUID: extra?.asAlexandria ? "alexandria" : profile.userUID || "cli-agent"
|
|
3283
3296
|
};
|
|
3297
|
+
if (extra?.asAlexandria) {
|
|
3298
|
+
comment.aiGenerated = true;
|
|
3299
|
+
}
|
|
3300
|
+
return comment;
|
|
3284
3301
|
}
|
|
3285
3302
|
|
|
3286
3303
|
// src/commands/studies/index.ts
|
|
@@ -4206,12 +4223,44 @@ function registerShareableCommands(program2) {
|
|
|
4206
4223
|
});
|
|
4207
4224
|
}
|
|
4208
4225
|
|
|
4209
|
-
// src/commands/
|
|
4226
|
+
// src/commands/templates/index.ts
|
|
4210
4227
|
function getGlobalOpts14(cmd) {
|
|
4211
4228
|
let root = cmd;
|
|
4212
4229
|
while (root.parent) root = root.parent;
|
|
4213
4230
|
return root.opts();
|
|
4214
4231
|
}
|
|
4232
|
+
function registerTemplatesCommands(program2) {
|
|
4233
|
+
const templates = program2.command("templates").description(
|
|
4234
|
+
"Read client document templates (id + name), proxied via solar from the sharing service."
|
|
4235
|
+
);
|
|
4236
|
+
templates.command("list").description(
|
|
4237
|
+
"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"
|
|
4238
|
+
).option(
|
|
4239
|
+
"--type <identifier>",
|
|
4240
|
+
"solarStudy (default) | colectiveSolarStudy | veChargerStudy | generic",
|
|
4241
|
+
"solarStudy"
|
|
4242
|
+
).action(async (opts) => {
|
|
4243
|
+
try {
|
|
4244
|
+
const global = getGlobalOpts14(templates);
|
|
4245
|
+
const client = createServiceClient("solar", global);
|
|
4246
|
+
const params = {};
|
|
4247
|
+
if (opts.type && opts.type !== "generic") {
|
|
4248
|
+
params.templateIdentifier = opts.type;
|
|
4249
|
+
}
|
|
4250
|
+
const res = await client.get("/api/templates", { params });
|
|
4251
|
+
output(res.data, global);
|
|
4252
|
+
} catch (err) {
|
|
4253
|
+
outputError(handleApiError(err));
|
|
4254
|
+
}
|
|
4255
|
+
});
|
|
4256
|
+
}
|
|
4257
|
+
|
|
4258
|
+
// src/commands/geocode/index.ts
|
|
4259
|
+
function getGlobalOpts15(cmd) {
|
|
4260
|
+
let root = cmd;
|
|
4261
|
+
while (root.parent) root = root.parent;
|
|
4262
|
+
return root.opts();
|
|
4263
|
+
}
|
|
4215
4264
|
function emit(envelope, all, query, global) {
|
|
4216
4265
|
if (envelope.error) {
|
|
4217
4266
|
outputError({
|
|
@@ -4245,7 +4294,7 @@ function registerGeocodeCommands(program2) {
|
|
|
4245
4294
|
'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
4295
|
).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
4296
|
try {
|
|
4248
|
-
const global =
|
|
4297
|
+
const global = getGlobalOpts15(geocode);
|
|
4249
4298
|
const client = createServiceClient("solar", global);
|
|
4250
4299
|
const res = await client.get(
|
|
4251
4300
|
"/api/geocode",
|
|
@@ -4260,7 +4309,7 @@ function registerGeocodeCommands(program2) {
|
|
|
4260
4309
|
"Resolve coordinates into an address (reverse geocoding).\nExample:\n suntropy geocode reverse --lat 40.4168 --lng -3.7038"
|
|
4261
4310
|
).requiredOption("--lat <number>", "Latitude").requiredOption("--lng <number>", "Longitude").option("--all", "Return all candidate matches instead of only the best one").action(async (opts) => {
|
|
4262
4311
|
try {
|
|
4263
|
-
const global =
|
|
4312
|
+
const global = getGlobalOpts15(geocode);
|
|
4264
4313
|
const client = createServiceClient("solar", global);
|
|
4265
4314
|
const res = await client.get(
|
|
4266
4315
|
"/api/reverse-geocode",
|
|
@@ -4274,9 +4323,9 @@ function registerGeocodeCommands(program2) {
|
|
|
4274
4323
|
}
|
|
4275
4324
|
|
|
4276
4325
|
// src/index.ts
|
|
4277
|
-
var CLI_VERSION = true ? "0.
|
|
4326
|
+
var CLI_VERSION = true ? "0.8.0" : "0.0.0-dev";
|
|
4278
4327
|
function createProgram() {
|
|
4279
|
-
const program2 = new
|
|
4328
|
+
const program2 = new Command3();
|
|
4280
4329
|
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)");
|
|
4281
4330
|
registerAuthCommands(program2);
|
|
4282
4331
|
registerConfigCommands(program2);
|
|
@@ -4287,6 +4336,7 @@ function createProgram() {
|
|
|
4287
4336
|
registerSolarformCommands(program2);
|
|
4288
4337
|
registerPPACommands(program2);
|
|
4289
4338
|
registerShareableCommands(program2);
|
|
4339
|
+
registerTemplatesCommands(program2);
|
|
4290
4340
|
registerGeocodeCommands(program2);
|
|
4291
4341
|
return program2;
|
|
4292
4342
|
}
|