@enerlence/suntropy-cli 0.12.0 → 0.13.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/dist/bin/suntropy.js +92 -9
- package/dist/bin/suntropy.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/suntropy.js
CHANGED
|
@@ -1872,7 +1872,7 @@ function registerInventoryCommands(program2) {
|
|
|
1872
1872
|
import { Option } from "commander";
|
|
1873
1873
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync2 } from "fs";
|
|
1874
1874
|
|
|
1875
|
-
// node_modules/uuid/dist/esm-node/rng.js
|
|
1875
|
+
// ../suntropy-cli/node_modules/uuid/dist/esm-node/rng.js
|
|
1876
1876
|
import crypto from "crypto";
|
|
1877
1877
|
var rnds8Pool = new Uint8Array(256);
|
|
1878
1878
|
var poolPtr = rnds8Pool.length;
|
|
@@ -1884,16 +1884,16 @@ function rng() {
|
|
|
1884
1884
|
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
1885
1885
|
}
|
|
1886
1886
|
|
|
1887
|
-
// node_modules/uuid/dist/esm-node/regex.js
|
|
1887
|
+
// ../suntropy-cli/node_modules/uuid/dist/esm-node/regex.js
|
|
1888
1888
|
var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
|
|
1889
1889
|
|
|
1890
|
-
// node_modules/uuid/dist/esm-node/validate.js
|
|
1890
|
+
// ../suntropy-cli/node_modules/uuid/dist/esm-node/validate.js
|
|
1891
1891
|
function validate(uuid) {
|
|
1892
1892
|
return typeof uuid === "string" && regex_default.test(uuid);
|
|
1893
1893
|
}
|
|
1894
1894
|
var validate_default = validate;
|
|
1895
1895
|
|
|
1896
|
-
// node_modules/uuid/dist/esm-node/stringify.js
|
|
1896
|
+
// ../suntropy-cli/node_modules/uuid/dist/esm-node/stringify.js
|
|
1897
1897
|
var byteToHex = [];
|
|
1898
1898
|
for (let i = 0; i < 256; ++i) {
|
|
1899
1899
|
byteToHex.push((i + 256).toString(16).substr(1));
|
|
@@ -1907,7 +1907,7 @@ function stringify(arr, offset = 0) {
|
|
|
1907
1907
|
}
|
|
1908
1908
|
var stringify_default = stringify;
|
|
1909
1909
|
|
|
1910
|
-
// node_modules/uuid/dist/esm-node/v4.js
|
|
1910
|
+
// ../suntropy-cli/node_modules/uuid/dist/esm-node/v4.js
|
|
1911
1911
|
function v4(options, buf, offset) {
|
|
1912
1912
|
options = options || {};
|
|
1913
1913
|
const rnds = options.random || (options.rng || rng)();
|
|
@@ -4753,7 +4753,7 @@ function buildArea(opts) {
|
|
|
4753
4753
|
return { type: "polygon", coordinates: parsed };
|
|
4754
4754
|
}
|
|
4755
4755
|
function registerSatvoltCampaignCommands(satvolt) {
|
|
4756
|
-
const campaigns = satvolt.command("campaigns").description("Satvolt campaigns: list, create from an area, start, reset, resume, usage, logs and funnel.");
|
|
4756
|
+
const campaigns = satvolt.command("campaigns").description("Satvolt campaigns: list, create from an area, start, pause, cancel, reset, resume, usage, logs and funnel.");
|
|
4757
4757
|
campaigns.command("list").description(
|
|
4758
4758
|
'List campaigns, newest first.\nExamples:\n suntropy satvolt campaigns list --state completed,inProgress\n suntropy satvolt campaigns list --search "Alcobendas" --format human'
|
|
4759
4759
|
).option("--limit <n>", "Max results (max 200)", "50").option("--offset <n>", "Skip results", "0").option("--search <text>", "Match name, region or input address").option("--state <states>", "Comma-separated campaign states (see: satvolt catalog states)").option("--source <source>", "maps | excel | campaign").action(async (opts) => {
|
|
@@ -4831,6 +4831,25 @@ search query and lead limit come from it; any flag you pass wins.
|
|
|
4831
4831
|
outputError(satvoltError(err));
|
|
4832
4832
|
}
|
|
4833
4833
|
});
|
|
4834
|
+
campaigns.command("estimate").summary("Count the businesses a campaign would find over an area, without creating it.").description(
|
|
4835
|
+
'Preview of FIND_LEADS: how many businesses Google Maps returns inside the area with\nthe given business groups (nearby search) or text query, plus a sample to inspect.\nNothing is created and no credits are spent. Use it to iterate the filters before\n`campaigns create`: check `places.atLeast`, `byType` and the `sample` names, then\ntighten or widen --business-groups until the sample looks like the target.\n\nThe count is a minimum: the preview has a request budget and is cached (7 days) per\narea, groups and query; `places.exhaustive:false` means dense zones were left out.\nA text query matches place NAMES, not categories ("manufactura" only finds businesses\ncalled "Manufacturas \u2026"): for a category, use business groups without --search-query.\n\nArea (exactly one): --circle <lat,lng> --radius <m> | --bounds <nwLat,nwLng,seLat,seLng>\n| --polygon <json|@file|->. Base (optional): --template or --from-campaign give the\ngroups and query when not passed explicitly.\n\nExamples:\n suntropy satvolt campaigns estimate --circle 43.3934,-3.8445 --radius 1500 \\\n --business-groups businesses --format human\n suntropy satvolt campaigns estimate --circle 43.3934,-3.8445 --radius 1500 \\\n --business-groups industrial_logistics,automotive --sample 50\n suntropy satvolt campaigns estimate --bounds 40.45,-3.70,40.44,-3.68 --template "Industria"'
|
|
4836
|
+
).option("--circle <lat,lng>", "Circle center").option("--radius <meters>", "Circle radius in meters").option("--bounds <nwLat,nwLng,seLat,seLng>", "Rectangle corners").option("--polygon <json>", "Polygon points, GeoJSON, @file or - for stdin").option("--template <idOrName>", "Take business groups and query from a campaign template").option("--from-campaign <id>", "Take business groups and query from another Maps campaign").option("--business-groups <ids>", "Comma-separated group ids or raw Google Places types. See: satvolt catalog business-groups").option("--search-query <text>", "Places text search with this query instead of nearby search (matches names, not categories)").option("--sample <n>", "Sample places to return (1-100, default 20)").option("--offset <n>", "Skip this many sample places (to page through them)").action(async (opts) => {
|
|
4837
|
+
const global = getGlobalOpts17(campaigns);
|
|
4838
|
+
try {
|
|
4839
|
+
const body = { area: buildArea(opts) };
|
|
4840
|
+
if (opts.template && opts.fromCampaign) throw new Error("Use either --template or --from-campaign, not both");
|
|
4841
|
+
if (opts.template) body.templateId = opts.template;
|
|
4842
|
+
if (opts.fromCampaign) body.fromCampaignId = parseId(opts.fromCampaign, "--from-campaign");
|
|
4843
|
+
if (opts.businessGroups) body.businessGroups = opts.businessGroups.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4844
|
+
if (opts.searchQuery) body.searchQuery = opts.searchQuery;
|
|
4845
|
+
if (opts.sample !== void 0) body.sample = parseIntOption(opts.sample, "--sample");
|
|
4846
|
+
if (opts.offset !== void 0) body.offset = parseIntOption(opts.offset, "--offset");
|
|
4847
|
+
const data = await call(satvoltClient(global, 12e4), "post", "/campaigns/estimate", { data: body });
|
|
4848
|
+
output(data, global);
|
|
4849
|
+
} catch (err) {
|
|
4850
|
+
outputError(satvoltError(err));
|
|
4851
|
+
}
|
|
4852
|
+
});
|
|
4834
4853
|
campaigns.command("delete <campaignId>").summary("Delete a campaign and everything it generated (irreversible, --yes).").description(
|
|
4835
4854
|
"Delete a campaign with its sectors, leads, step executions, pipeline configuration\nand queued jobs. Irreversible. Export tables of the campaign stop working.\nExample:\n suntropy satvolt campaigns delete 63 --yes"
|
|
4836
4855
|
).option("--yes", "Confirm the deletion (required)").action(async (campaignId, opts) => {
|
|
@@ -4852,6 +4871,40 @@ search query and lead limit come from it; any flag you pass wins.
|
|
|
4852
4871
|
outputError(satvoltError(err));
|
|
4853
4872
|
}
|
|
4854
4873
|
});
|
|
4874
|
+
campaigns.command("pause <campaignId>").summary("Pause a running campaign: nothing else runs or is charged until unpause.").description(
|
|
4875
|
+
"Pause a running campaign. Queued work is withdrawn and steps already in flight\nfinish without queuing their successors, so no more credits are spent. Results\nof async steps whose webhook arrives meanwhile are kept. Only a running\ncampaign (inProgress, sectorized, leadsFound, analyzed) can be paused;\notherwise 409 INVALID_CAMPAIGN_STATE.\nExample:\n suntropy satvolt campaigns pause 72"
|
|
4876
|
+
).action(async (campaignId) => {
|
|
4877
|
+
const global = getGlobalOpts17(campaigns);
|
|
4878
|
+
try {
|
|
4879
|
+
const data = await call(satvoltClient(global, 12e4), "post", `/campaigns/${parseId(campaignId, "campaignId")}/pause`);
|
|
4880
|
+
output(data, global);
|
|
4881
|
+
} catch (err) {
|
|
4882
|
+
outputError(satvoltError(err));
|
|
4883
|
+
}
|
|
4884
|
+
});
|
|
4885
|
+
campaigns.command("unpause <campaignId>").summary("Resume a paused campaign where it stopped (no step is run twice).").description(
|
|
4886
|
+
"Resume a paused campaign where it stopped: every lead that is not finished gets\nits next pending step queued, and steps already executed are neither re-run\nnor charged again. Only a paused campaign can be unpaused. This is not\n`resume`, which appends a NEW step to a finished campaign.\nExample:\n suntropy satvolt campaigns unpause 72"
|
|
4887
|
+
).action(async (campaignId) => {
|
|
4888
|
+
const global = getGlobalOpts17(campaigns);
|
|
4889
|
+
try {
|
|
4890
|
+
const data = await call(satvoltClient(global, 12e4), "post", `/campaigns/${parseId(campaignId, "campaignId")}/unpause`);
|
|
4891
|
+
output(data, global);
|
|
4892
|
+
} catch (err) {
|
|
4893
|
+
outputError(satvoltError(err));
|
|
4894
|
+
}
|
|
4895
|
+
});
|
|
4896
|
+
campaigns.command("cancel <campaignId>").summary("Cancel a running or paused campaign for good, keeping its leads and data (--yes).").description(
|
|
4897
|
+
"Cancel a running, paused or queued campaign. Irreversible: it cannot be unpaused\nor started again (reset relaunches it from scratch, deleting the leads). Leads\nand the data already enriched are kept, can be exported and are still charged.\nExample:\n suntropy satvolt campaigns cancel 72 --yes"
|
|
4898
|
+
).option("--yes", "Confirm the cancellation (required)").action(async (campaignId, opts) => {
|
|
4899
|
+
const global = getGlobalOpts17(campaigns);
|
|
4900
|
+
try {
|
|
4901
|
+
if (!opts.yes) throw new Error("Cancelling a campaign is irreversible. Re-run with --yes to confirm.");
|
|
4902
|
+
const data = await call(satvoltClient(global, 12e4), "post", `/campaigns/${parseId(campaignId, "campaignId")}/cancel`);
|
|
4903
|
+
output(data, global);
|
|
4904
|
+
} catch (err) {
|
|
4905
|
+
outputError(satvoltError(err));
|
|
4906
|
+
}
|
|
4907
|
+
});
|
|
4855
4908
|
campaigns.command("reset <campaignId>").summary("Relaunch a campaign from scratch: deletes its leads and results (--yes).").description(
|
|
4856
4909
|
"Relaunch a campaign from scratch: deletes its sectors, leads and processing data\nand returns it to queued, keeping the configuration. Excel/campaign-sourced\ncampaigns keep their imported leads. Irreversible.\nExample:\n suntropy satvolt campaigns reset 59 --yes --start"
|
|
4857
4910
|
).option("--start", "Start the pipeline again after resetting").option("--yes", "Confirm the reset (required)").action(async (campaignId, opts) => {
|
|
@@ -5719,13 +5772,40 @@ Typical flow:
|
|
|
5719
5772
|
satvolt export-tables create <id> ... \xB7 satvolt export-tables export <tableId>
|
|
5720
5773
|
satvolt campaigns resume <id> --action AI_AGENT --config @step.json
|
|
5721
5774
|
satvolt templates create --name <n> --from-campaign <id> \xB7 campaigns create --template <n>
|
|
5722
|
-
satvolt campaigns extend <id> --max-leads N \xB7 leads run-step <id> <leadId> <step
|
|
5775
|
+
satvolt campaigns extend <id> --max-leads N \xB7 leads run-step <id> <leadId> <step>
|
|
5776
|
+
satvolt campaigns pause <id> \xB7 unpause <id> \xB7 cancel <id> --yes
|
|
5777
|
+
satvolt usage [--month YYYY-MM] credits spent by the account this month`
|
|
5723
5778
|
);
|
|
5724
5779
|
registerSatvoltCampaignCommands(satvolt);
|
|
5725
5780
|
registerSatvoltPipelineCommands(satvolt);
|
|
5726
5781
|
registerSatvoltLeadCommands(satvolt);
|
|
5727
5782
|
registerSatvoltExportTableCommands(satvolt);
|
|
5728
5783
|
registerSatvoltTemplateCommands(satvolt);
|
|
5784
|
+
satvolt.command("usage").summary("Credits spent by the whole account in a calendar month, by campaign and by step.").description(
|
|
5785
|
+
"Credits spent by the whole account in a calendar month (UTC): total, previous\nmonth for comparison, and the breakdown by campaign and by step. It sums every\ncharge in the period, including the Google Maps search (FIND_LEADS), so it\ndoes not have to match `campaigns usage`, which reconstructs the cost of one\ncampaign and leaves the search out.\nExamples:\n suntropy satvolt usage\n suntropy satvolt usage --month 2026-08 --format human"
|
|
5786
|
+
).option("--month <YYYY-MM>", "Month to report (default: current month)").action(async (opts) => {
|
|
5787
|
+
const global = getGlobalOpts17(satvolt);
|
|
5788
|
+
try {
|
|
5789
|
+
if (opts.month !== void 0 && !/^\d{4}-\d{2}$/.test(String(opts.month))) {
|
|
5790
|
+
throw new Error("--month must be YYYY-MM, e.g. 2026-08");
|
|
5791
|
+
}
|
|
5792
|
+
const data = await call(satvoltClient(global, 12e4), "get", "/usage", { params: { month: opts.month } });
|
|
5793
|
+
if (global.format === "human" || global.format === "csv") {
|
|
5794
|
+
if (global.format === "human") {
|
|
5795
|
+
process.stderr.write(
|
|
5796
|
+
`${data.month}: ${data.credits} credits \xB7 ${data.executions} executions \xB7 previous month (${data.previous.month}): ${data.previous.credits} credits
|
|
5797
|
+
|
|
5798
|
+
`
|
|
5799
|
+
);
|
|
5800
|
+
}
|
|
5801
|
+
output(data.byCampaign, global);
|
|
5802
|
+
return;
|
|
5803
|
+
}
|
|
5804
|
+
output(data, global);
|
|
5805
|
+
} catch (err) {
|
|
5806
|
+
outputError(satvoltError(err));
|
|
5807
|
+
}
|
|
5808
|
+
});
|
|
5729
5809
|
const catalog = satvolt.command("catalog").description("Reference data for building campaigns and pipelines.");
|
|
5730
5810
|
const catalogEntries = [
|
|
5731
5811
|
["actions", "/catalog/actions", "Pipeline actions: credits per lead (fixed per action; failed or skipped runs are free), dependencies, multiple, and the JSON Schema of their config (the input). The data each step writes: satvolt export-tables fields <campaignId>."],
|
|
@@ -5818,7 +5898,10 @@ var WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
|
5818
5898
|
"duplicate",
|
|
5819
5899
|
"extend",
|
|
5820
5900
|
"move",
|
|
5821
|
-
"reorder"
|
|
5901
|
+
"reorder",
|
|
5902
|
+
"pause",
|
|
5903
|
+
"unpause",
|
|
5904
|
+
"cancel"
|
|
5822
5905
|
]);
|
|
5823
5906
|
var PATH_OVERRIDES = {
|
|
5824
5907
|
"studies calculate production": "write"
|
|
@@ -5928,7 +6011,7 @@ function registerCommandProfileCommand(program2) {
|
|
|
5928
6011
|
}
|
|
5929
6012
|
|
|
5930
6013
|
// src/index.ts
|
|
5931
|
-
var CLI_VERSION = true ? "0.
|
|
6014
|
+
var CLI_VERSION = true ? "0.13.0" : "0.0.0-dev";
|
|
5932
6015
|
function createProgram() {
|
|
5933
6016
|
const program2 = new Command4();
|
|
5934
6017
|
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)");
|