@erdoai/cli 0.140.1 → 0.141.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/index.js +87 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1217,6 +1217,15 @@ var ErdoClient = class {
|
|
|
1217
1217
|
const qs = q.toString();
|
|
1218
1218
|
return this.request("GET", `/v1/datasets${qs ? `?${qs}` : ""}`);
|
|
1219
1219
|
}
|
|
1220
|
+
getDatasetRowActions(slug, opts) {
|
|
1221
|
+
const q = new URLSearchParams({ dataset: slug });
|
|
1222
|
+
if (opts?.include_receipts) q.set("include_receipts", "true");
|
|
1223
|
+
if (opts?.include_unresolved) q.set("include_unresolved", "true");
|
|
1224
|
+
return this.request("GET", `/v1/dataset-row-actions?${q.toString()}`);
|
|
1225
|
+
}
|
|
1226
|
+
setDatasetRowAction(input) {
|
|
1227
|
+
return this.request("PUT", "/v1/dataset-row-actions", input, false);
|
|
1228
|
+
}
|
|
1220
1229
|
listDatasetUserPermissions(slug) {
|
|
1221
1230
|
return this.request("GET", `/v1/datasets/${encodeURIComponent(slug)}/permissions/users`);
|
|
1222
1231
|
}
|
|
@@ -2184,6 +2193,40 @@ async function promptLine(question) {
|
|
|
2184
2193
|
}
|
|
2185
2194
|
}
|
|
2186
2195
|
|
|
2196
|
+
// src/integration-credential-input.ts
|
|
2197
|
+
var PROMPT_FIELD = /^[A-Za-z0-9_.-]+$/;
|
|
2198
|
+
async function resolveIntegrationCredentials(options) {
|
|
2199
|
+
if (options.fromConnection && (options.credentialArgs.length || options.promptFields.length)) {
|
|
2200
|
+
throw new Error("pass either credentials or --from-connection, not both \u2014 otherwise the connection would hold a credential you did not choose");
|
|
2201
|
+
}
|
|
2202
|
+
if (options.promptFields.length && !options.interactive) {
|
|
2203
|
+
throw new Error("--prompt-credential requires both standard input and standard output to be terminals");
|
|
2204
|
+
}
|
|
2205
|
+
const credentials = /* @__PURE__ */ new Map();
|
|
2206
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2207
|
+
const claim = (field) => {
|
|
2208
|
+
if (seen.has(field)) throw new Error("duplicate credential field");
|
|
2209
|
+
seen.add(field);
|
|
2210
|
+
};
|
|
2211
|
+
for (const value of options.credentialArgs) {
|
|
2212
|
+
const separator = value.indexOf("=");
|
|
2213
|
+
if (separator < 1) throw new Error("invalid --credential; expected key=value");
|
|
2214
|
+
const field = value.slice(0, separator);
|
|
2215
|
+
claim(field);
|
|
2216
|
+
credentials.set(field, value.slice(separator + 1));
|
|
2217
|
+
}
|
|
2218
|
+
for (const field of options.promptFields) {
|
|
2219
|
+
if (!PROMPT_FIELD.test(field)) throw new Error("prompt credential field must contain only letters, digits, dots, hyphens and underscores");
|
|
2220
|
+
claim(field);
|
|
2221
|
+
}
|
|
2222
|
+
for (const field of options.promptFields) {
|
|
2223
|
+
const value = await options.prompt(field);
|
|
2224
|
+
if (!value.trim()) throw new Error("prompted credential cannot be empty");
|
|
2225
|
+
credentials.set(field, value);
|
|
2226
|
+
}
|
|
2227
|
+
return seen.size ? Object.fromEntries(credentials) : void 0;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2187
2230
|
// src/integrations.ts
|
|
2188
2231
|
function formatIntegrationListRow(i) {
|
|
2189
2232
|
const provided = i.provided_by_organization_slug ? ` provided by ${i.provided_by_organization_slug}` : "";
|
|
@@ -2450,6 +2493,19 @@ function readJSONObject(file, label) {
|
|
|
2450
2493
|
}
|
|
2451
2494
|
return parsed;
|
|
2452
2495
|
}
|
|
2496
|
+
async function readJSONInput(value, label) {
|
|
2497
|
+
if (value !== "-") return readJSONObject(value, label);
|
|
2498
|
+
let parsed;
|
|
2499
|
+
try {
|
|
2500
|
+
parsed = JSON.parse(await readAllStdin());
|
|
2501
|
+
} catch (error) {
|
|
2502
|
+
throw new Error(`${label} must contain valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
2503
|
+
}
|
|
2504
|
+
if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
|
|
2505
|
+
throw new Error(`${label} must contain one JSON object`);
|
|
2506
|
+
}
|
|
2507
|
+
return parsed;
|
|
2508
|
+
}
|
|
2453
2509
|
function requireOutreachProject() {
|
|
2454
2510
|
if (!process.env.ERDO_PROJECT?.trim()) {
|
|
2455
2511
|
throw new Error(
|
|
@@ -6961,6 +7017,29 @@ voiceAgentsCmd.command("whatsapp-replies <slug>").description("Turn a voice agen
|
|
|
6961
7017
|
}
|
|
6962
7018
|
});
|
|
6963
7019
|
var datasetsCmd = program.command("datasets").description("Datasets");
|
|
7020
|
+
var datasetRowActionsCmd = datasetsCmd.command("row-actions").description("Read and replace actions triggered by rows arriving in a dataset");
|
|
7021
|
+
datasetRowActionsCmd.command("get <slug>").description("Read the row-action declarations and optional bounded diagnostics for a dataset").option("--include-receipts", "include bounded provider receipt diagnostics").option("--include-unresolved", "include bounded unresolved lookup diagnostics").action(async (slug, opts) => {
|
|
7022
|
+
try {
|
|
7023
|
+
print(await new ErdoClient().getDatasetRowActions(slug, {
|
|
7024
|
+
...opts.includeReceipts ? { include_receipts: true } : {},
|
|
7025
|
+
...opts.includeUnresolved ? { include_unresolved: true } : {}
|
|
7026
|
+
}));
|
|
7027
|
+
} catch (e) {
|
|
7028
|
+
fail(e);
|
|
7029
|
+
}
|
|
7030
|
+
});
|
|
7031
|
+
datasetRowActionsCmd.command("set").description("Replace one row-action declaration from a JSON file, or from stdin with --file -").requiredOption("-f, --file <path>", "one JSON declaration object; use - to read exactly one object from stdin").action(async (opts) => {
|
|
7032
|
+
try {
|
|
7033
|
+
const input = await readJSONInput(opts.file, "--file");
|
|
7034
|
+
const dataset = typeof input.dataset === "string" ? input.dataset.trim() : "";
|
|
7035
|
+
const name = typeof input.name === "string" ? input.name.trim() : "";
|
|
7036
|
+
if (!dataset) throw new Error("--file dataset must be a non-empty string");
|
|
7037
|
+
if (!name) throw new Error("--file name must be a non-empty string");
|
|
7038
|
+
print(await new ErdoClient().setDatasetRowAction(input));
|
|
7039
|
+
} catch (e) {
|
|
7040
|
+
fail(e);
|
|
7041
|
+
}
|
|
7042
|
+
});
|
|
6964
7043
|
var datasetPermissionsCmd = datasetsCmd.command("permissions").description("Manage dataset user permissions");
|
|
6965
7044
|
datasetPermissionsCmd.command("list <slug>").option("--json", "output raw JSON").action(async (slug, opts) => {
|
|
6966
7045
|
try {
|
|
@@ -8545,7 +8624,7 @@ integrationsCmd.command("apps [query]").description("Search connectable apps (na
|
|
|
8545
8624
|
fail(e);
|
|
8546
8625
|
}
|
|
8547
8626
|
});
|
|
8548
|
-
integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass
|
|
8627
|
+
integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass credentials with -c or --prompt-credential, or omit them to get a browser connect URL for OAuth apps").option("-c, --credential <key=value...>", "credential field, e.g. -c api_key=\u2026 (repeatable); rejected by OAuth apps, which have no credential to pass", (v, acc) => acc.concat(v), []).option("--prompt-credential <field>", "prompt securely for a credential field (repeatable; interactive terminals only)", (v, acc) => acc.concat(v), []).option("-n, --name <name>", "display name for the connection").option("--rotate", "replace the credentials of the app's existing connection instead of adding a second one \u2014 for a key that was rotated at the provider; requires -c or --prompt-credential").option("--share", "share the connection with the whole organization instead of keeping it private to you \u2014 for org-level credentials teammates and agents should see").option(
|
|
8549
8628
|
"--provide-to-managed",
|
|
8550
8629
|
"let the organizations you manage use this connection as if it were their own \u2014 for a vendor key your agency pays for. Key-authenticated native integrations only, connected in a manager organization"
|
|
8551
8630
|
).option(
|
|
@@ -8553,9 +8632,6 @@ integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass
|
|
|
8553
8632
|
"reuse a credential you verifiably supplied \u2014 find its connection id in column 5 of `erdo integrations list --org <that-org>`. The credential is copied inside Erdo and never shown. Legacy keys without supplier records require an authenticated credential update"
|
|
8554
8633
|
).action(async (app, opts) => {
|
|
8555
8634
|
try {
|
|
8556
|
-
if (opts.fromConnection && opts.credential.length) {
|
|
8557
|
-
fail(new Error("pass either -c or --from-connection, not both \u2014 otherwise the connection would hold a credential you did not choose"));
|
|
8558
|
-
}
|
|
8559
8635
|
if (opts.fromConnection && opts.rotate) {
|
|
8560
8636
|
fail(
|
|
8561
8637
|
new Error(
|
|
@@ -8563,15 +8639,13 @@ integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass
|
|
|
8563
8639
|
)
|
|
8564
8640
|
);
|
|
8565
8641
|
}
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
}
|
|
8574
|
-
}
|
|
8642
|
+
const credentials = await resolveIntegrationCredentials({
|
|
8643
|
+
credentialArgs: opts.credential,
|
|
8644
|
+
fromConnection: opts.fromConnection,
|
|
8645
|
+
promptFields: opts.promptCredential,
|
|
8646
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
8647
|
+
prompt: (field) => password({ message: `Credential ${field}:`, mask: "*" })
|
|
8648
|
+
});
|
|
8575
8649
|
const res = await new ErdoClient().connectIntegration({
|
|
8576
8650
|
app,
|
|
8577
8651
|
name: opts.name,
|