@enerlence/suntropy-cli 0.4.0 → 0.5.1
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 +45 -0
- package/dist/bin/suntropy.js +166 -1
- package/dist/bin/suntropy.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,6 +209,51 @@ suntropy solarform calculate --data '{"center":{"lat":37.39,"lng":-5.99},...}'
|
|
|
209
209
|
suntropy solarform config
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
### `suntropy ppa` - PPA Analysis
|
|
213
|
+
|
|
214
|
+
Read PPA (Power Purchase Agreement) templates and run PPA simulations on a solar study.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Read-only templates
|
|
218
|
+
suntropy ppa templates list --fields _id,name,mode,ppaPrice,useInSolarForm
|
|
219
|
+
suntropy ppa templates get <ppaTemplateId>
|
|
220
|
+
|
|
221
|
+
# Run a PPA simulation on a study (read-only, does not persist)
|
|
222
|
+
suntropy ppa calculate --study <studyId> # uses client useInSolarForm templates
|
|
223
|
+
suntropy ppa calculate --study <studyId> --template <ppaTemplateId> # uses a single template
|
|
224
|
+
cat study.json | suntropy ppa calculate --data - # full SolarStudy via stdin
|
|
225
|
+
suntropy ppa calculate --study <studyId> --raw --save-file ppa.json
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The response is the study with `economicResults.ppaAnalysis` populated
|
|
229
|
+
(`simulationResults[].profitabilityResults` → `irr`, `paybackYears`). Heavy
|
|
230
|
+
PowerCurve objects are summarized unless `--raw` is passed.
|
|
231
|
+
|
|
232
|
+
### `suntropy shareables` - Shareable Links
|
|
233
|
+
|
|
234
|
+
Create shareable links for studies (sharing service, exposed under `/templates`).
|
|
235
|
+
The backend fills `uid`, `url`, `clientUID` and `idShareable` from the token.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Public shareable for a solar study
|
|
239
|
+
suntropy shareables create --element-id <studyId>
|
|
240
|
+
|
|
241
|
+
# With template, name and expiration
|
|
242
|
+
suntropy shareables create --element-id <studyId> \
|
|
243
|
+
--template-id <templateId> --name "Estudio Juan" --expiration-date 2026-12-31
|
|
244
|
+
|
|
245
|
+
# Private (password) + send to recipients
|
|
246
|
+
suntropy shareables create --element-id <studyId> \
|
|
247
|
+
--privacy PRIVATE --password secret --email-list "a@x.com;b@y.com"
|
|
248
|
+
|
|
249
|
+
# Extra link query params / extra DTO fields
|
|
250
|
+
suntropy shareables create --element-id <studyId> --link-params "utm_source=cli"
|
|
251
|
+
suntropy shareables create --element-id <studyId> --data '{"customLayout":true}'
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`--element-type` (default `solarStudy`): `solarStudy | colectiveSolarStudy | veChargerStudy | heatpumpStudy | billing`.
|
|
255
|
+
`--shareable-type` (default `TEMPLATE`): `TEMPLATE | CONTRACT`. The response includes the public `url` and `uid`.
|
|
256
|
+
|
|
212
257
|
### `suntropy config` - Configuration
|
|
213
258
|
|
|
214
259
|
```bash
|
package/dist/bin/suntropy.js
CHANGED
|
@@ -4044,10 +4044,173 @@ Examples:
|
|
|
4044
4044
|
});
|
|
4045
4045
|
}
|
|
4046
4046
|
|
|
4047
|
+
// src/commands/ppa/index.ts
|
|
4048
|
+
function getGlobalOpts12(cmd) {
|
|
4049
|
+
let root = cmd;
|
|
4050
|
+
while (root.parent) root = root.parent;
|
|
4051
|
+
return root.opts();
|
|
4052
|
+
}
|
|
4053
|
+
function readStdin3() {
|
|
4054
|
+
return new Promise((resolve, reject) => {
|
|
4055
|
+
let data = "";
|
|
4056
|
+
process.stdin.setEncoding("utf-8");
|
|
4057
|
+
process.stdin.on("data", (chunk) => {
|
|
4058
|
+
data += chunk;
|
|
4059
|
+
});
|
|
4060
|
+
process.stdin.on("end", () => resolve(data));
|
|
4061
|
+
process.stdin.on("error", reject);
|
|
4062
|
+
});
|
|
4063
|
+
}
|
|
4064
|
+
async function parseData5(data) {
|
|
4065
|
+
if (!data) return void 0;
|
|
4066
|
+
if (data === "-") {
|
|
4067
|
+
const input = await readStdin3();
|
|
4068
|
+
return JSON.parse(input);
|
|
4069
|
+
}
|
|
4070
|
+
return JSON.parse(data);
|
|
4071
|
+
}
|
|
4072
|
+
function compactStudyOutput2(study) {
|
|
4073
|
+
if (study === null || study === void 0 || typeof study !== "object") return study;
|
|
4074
|
+
if (Array.isArray(study)) return study.map(compactStudyOutput2);
|
|
4075
|
+
const record = study;
|
|
4076
|
+
if (Array.isArray(record.days) && record.days.length > 0 && record.identifier !== void 0) {
|
|
4077
|
+
return { _type: "PowerCurve", days: record.days.length, identifier: record.identifier };
|
|
4078
|
+
}
|
|
4079
|
+
const result = {};
|
|
4080
|
+
for (const [key, val] of Object.entries(record)) {
|
|
4081
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
4082
|
+
const v = val;
|
|
4083
|
+
if (Array.isArray(v.days) && v.days.length > 0 && v.identifier !== void 0) {
|
|
4084
|
+
result[key] = { _type: "PowerCurve", days: v.days.length, identifier: v.identifier };
|
|
4085
|
+
continue;
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
result[key] = compactStudyOutput2(val);
|
|
4089
|
+
}
|
|
4090
|
+
return result;
|
|
4091
|
+
}
|
|
4092
|
+
function registerPPACommands(program2) {
|
|
4093
|
+
const ppa = program2.command("ppa").description(
|
|
4094
|
+
"PPA (Power Purchase Agreement) analysis.\nRead PPA templates and run PPA simulations on a solar study."
|
|
4095
|
+
);
|
|
4096
|
+
const templates = ppa.command("templates").description("Read-only access to the client PPA templates.");
|
|
4097
|
+
templates.command("list").description(
|
|
4098
|
+
"List all PPA templates for the authenticated client.\nExample:\n suntropy ppa templates list --fields _id,name,mode,ppaPrice,useInSolarForm"
|
|
4099
|
+
).action(async () => {
|
|
4100
|
+
try {
|
|
4101
|
+
const global = getGlobalOpts12(ppa);
|
|
4102
|
+
const client = createServiceClient("solar", global);
|
|
4103
|
+
const res = await client.get("/solar-study/ppa-templates");
|
|
4104
|
+
output(res.data, global);
|
|
4105
|
+
} catch (err) {
|
|
4106
|
+
outputError(handleApiError(err));
|
|
4107
|
+
}
|
|
4108
|
+
});
|
|
4109
|
+
templates.command("get <id>").description(
|
|
4110
|
+
"Get a single PPA template by its Mongo _id.\nExample:\n suntropy ppa templates get 665f0a1b2c3d4e5f60718293"
|
|
4111
|
+
).action(async (id) => {
|
|
4112
|
+
try {
|
|
4113
|
+
const global = getGlobalOpts12(ppa);
|
|
4114
|
+
const client = createServiceClient("solar", global);
|
|
4115
|
+
const res = await client.get(`/solar-study/ppa-templates/${id}`);
|
|
4116
|
+
output(res.data, global);
|
|
4117
|
+
} catch (err) {
|
|
4118
|
+
outputError(handleApiError(err));
|
|
4119
|
+
}
|
|
4120
|
+
});
|
|
4121
|
+
ppa.command("calculate").description(
|
|
4122
|
+
"Run PPA simulations on a solar study (read-only, does not persist).\n\nProvide the study via --study <id> (downloaded from the API) or --data <json>|- (full SolarStudy).\nWith --template <ppaTemplateId> the simulation uses that single PPA template;\nwithout it, the backend uses the client templates flagged useInSolarForm.\n\nNote: the study must include consumption and economicResults (a study fetched\nby --study already does). The response is the study with economicResults.ppaAnalysis populated.\n\nExamples:\n suntropy ppa calculate --study 665f0a1b2c3d4e5f60718293\n suntropy ppa calculate --study 665f... --template 6700aa11bb22cc33dd44ee55\n cat study.json | suntropy ppa calculate --data -"
|
|
4123
|
+
).option("--study <id>", "Solar study Mongo id to download and analyze").option("--data <json>", "Full SolarStudy JSON body (or - for stdin)").option("--template <ppaTemplateId>", "PPA template id to apply (defaults to client useInSolarForm templates)").option("--raw", "Return full study with PowerCurve data (no compaction)").option("--save-file <file>", "Save result to local file").action(async (opts) => {
|
|
4124
|
+
try {
|
|
4125
|
+
const global = getGlobalOpts12(ppa);
|
|
4126
|
+
const client = createServiceClient("solar", global);
|
|
4127
|
+
let study;
|
|
4128
|
+
if (opts.study) {
|
|
4129
|
+
const res2 = await client.get(`/solar-study/findById/${opts.study}`);
|
|
4130
|
+
study = res2.data;
|
|
4131
|
+
} else if (opts.data) {
|
|
4132
|
+
study = await parseData5(opts.data);
|
|
4133
|
+
} else {
|
|
4134
|
+
outputError(new Error("Provide --study <id> or --data <json>|- (full SolarStudy)."));
|
|
4135
|
+
return;
|
|
4136
|
+
}
|
|
4137
|
+
const body = { data: study };
|
|
4138
|
+
if (opts.template) body.ppaTemplateId = opts.template;
|
|
4139
|
+
const res = await client.post("/solar-study/calculate-ppa-on-study", body);
|
|
4140
|
+
const result = opts.raw ? res.data : compactStudyOutput2(res.data);
|
|
4141
|
+
output(result, { ...global, save: opts.saveFile || global.save });
|
|
4142
|
+
} catch (err) {
|
|
4143
|
+
outputError(handleApiError(err));
|
|
4144
|
+
}
|
|
4145
|
+
});
|
|
4146
|
+
}
|
|
4147
|
+
|
|
4148
|
+
// src/commands/shareables/index.ts
|
|
4149
|
+
function getGlobalOpts13(cmd) {
|
|
4150
|
+
let root = cmd;
|
|
4151
|
+
while (root.parent) root = root.parent;
|
|
4152
|
+
return root.opts();
|
|
4153
|
+
}
|
|
4154
|
+
function readStdin4() {
|
|
4155
|
+
return new Promise((resolve, reject) => {
|
|
4156
|
+
let data = "";
|
|
4157
|
+
process.stdin.setEncoding("utf-8");
|
|
4158
|
+
process.stdin.on("data", (chunk) => {
|
|
4159
|
+
data += chunk;
|
|
4160
|
+
});
|
|
4161
|
+
process.stdin.on("end", () => resolve(data));
|
|
4162
|
+
process.stdin.on("error", reject);
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4165
|
+
async function parseData6(data) {
|
|
4166
|
+
if (!data) return void 0;
|
|
4167
|
+
if (data === "-") {
|
|
4168
|
+
const input = await readStdin4();
|
|
4169
|
+
return JSON.parse(input);
|
|
4170
|
+
}
|
|
4171
|
+
return JSON.parse(data);
|
|
4172
|
+
}
|
|
4173
|
+
function registerShareableCommands(program2) {
|
|
4174
|
+
const shareables = program2.command("shareables").description(
|
|
4175
|
+
"Create shareable links for studies (sharing service, exposed under /templates)."
|
|
4176
|
+
);
|
|
4177
|
+
shareables.command("create").description(
|
|
4178
|
+
'Create a shareable link for a study (POST /shareable).\nThe backend fills uid, url, clientUID and idShareable from the token.\n\nelementType: solarStudy (default), colectiveSolarStudy, veChargerStudy, heatpumpStudy, billing\nshareableType: TEMPLATE (default), CONTRACT\nprivacy: PUBLIC (default), PRIVATE (use --password)\n\nExamples:\n suntropy shareables create --element-id 665f0a1b2c3d4e5f60718293\n suntropy shareables create --element-id 665f... --template-id 6700aa... --name "Estudio Juan"\n suntropy shareables create --element-id 665f... --privacy PRIVATE --password secret --email-list "a@x.com;b@y.com"\n suntropy shareables create --element-id 665f... --expiration-date 2026-12-31 --link-params "utm_source=cli"'
|
|
4179
|
+
).requiredOption("--element-id <id>", "Id of the element (study) to share").option("--element-type <type>", "solarStudy | colectiveSolarStudy | veChargerStudy | heatpumpStudy | billing", "solarStudy").option("--shareable-type <type>", "TEMPLATE | CONTRACT", "TEMPLATE").option("--privacy <privacy>", "PUBLIC | PRIVATE", "PUBLIC").option("--template-id <id>", "Template id to apply to the shareable").option("--name <name>", "Descriptive name for the shareable").option("--password <pwd>", "Protect the link with a password (implies PRIVATE)").option("--expiration-date <date>", "Expiration date (e.g. 2026-12-31)").option("--activation-date <date>", "Activation date (e.g. 2026-01-01)").option("--email-list <emails>", "Semicolon-separated list of recipient emails").option("--reply-to <email>", "Reply-to email for notifications").option("--custom-layout", "Enable custom layout").option("--read-only", "Mark the shareable as read-only").option("--link-params <qs>", "Extra query params appended to the shareable link (k=v&k2=v2)").option("--data <json>", "Extra ShareableDto fields as JSON (or - for stdin), merged last").action(async (opts) => {
|
|
4180
|
+
try {
|
|
4181
|
+
const global = getGlobalOpts13(shareables);
|
|
4182
|
+
const client = createServiceClient("templates", global);
|
|
4183
|
+
const extra = await parseData6(opts.data) || {};
|
|
4184
|
+
const body = {
|
|
4185
|
+
elementId: opts.elementId,
|
|
4186
|
+
elementType: opts.elementType,
|
|
4187
|
+
shareableType: opts.shareableType,
|
|
4188
|
+
privacy: opts.password ? "PRIVATE" : opts.privacy,
|
|
4189
|
+
...opts.templateId ? { templateId: opts.templateId } : {},
|
|
4190
|
+
...opts.name ? { name: opts.name } : {},
|
|
4191
|
+
...opts.password ? { password: opts.password, passwordConfirm: opts.password } : {},
|
|
4192
|
+
...opts.expirationDate ? { expirationDate: opts.expirationDate } : {},
|
|
4193
|
+
...opts.activationDate ? { activationDate: opts.activationDate } : {},
|
|
4194
|
+
...opts.emailList ? { emailList: opts.emailList } : {},
|
|
4195
|
+
...opts.replyTo ? { replyTo: opts.replyTo } : {},
|
|
4196
|
+
...opts.customLayout ? { customLayout: true } : {},
|
|
4197
|
+
...opts.readOnly ? { readOnly: true } : {},
|
|
4198
|
+
...extra
|
|
4199
|
+
};
|
|
4200
|
+
const url = opts.linkParams ? `/shareable?${opts.linkParams}` : "/shareable";
|
|
4201
|
+
const res = await client.post(url, body);
|
|
4202
|
+
output(res.data, { ...global, save: global.save });
|
|
4203
|
+
} catch (err) {
|
|
4204
|
+
outputError(handleApiError(err));
|
|
4205
|
+
}
|
|
4206
|
+
});
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4047
4209
|
// src/index.ts
|
|
4210
|
+
var CLI_VERSION = true ? "0.5.1" : "0.0.0-dev";
|
|
4048
4211
|
function createProgram() {
|
|
4049
4212
|
const program2 = new Command2();
|
|
4050
|
-
program2.name("suntropy").description("Agent-first CLI for Suntropy solar platform. Optimized for programmatic data manipulation and progressive exploration.").version(
|
|
4213
|
+
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)");
|
|
4051
4214
|
registerAuthCommands(program2);
|
|
4052
4215
|
registerConfigCommands(program2);
|
|
4053
4216
|
registerInventoryCommands(program2);
|
|
@@ -4055,6 +4218,8 @@ function createProgram() {
|
|
|
4055
4218
|
registerCurvesCommands(program2);
|
|
4056
4219
|
registerConsumptionCommands(program2);
|
|
4057
4220
|
registerSolarformCommands(program2);
|
|
4221
|
+
registerPPACommands(program2);
|
|
4222
|
+
registerShareableCommands(program2);
|
|
4058
4223
|
return program2;
|
|
4059
4224
|
}
|
|
4060
4225
|
|