@erdoai/cli 0.140.2 → 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.
Files changed (2) hide show
  1. package/dist/index.js +42 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2193,6 +2193,40 @@ async function promptLine(question) {
2193
2193
  }
2194
2194
  }
2195
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
+
2196
2230
  // src/integrations.ts
2197
2231
  function formatIntegrationListRow(i) {
2198
2232
  const provided = i.provided_by_organization_slug ? ` provided by ${i.provided_by_organization_slug}` : "";
@@ -8590,7 +8624,7 @@ integrationsCmd.command("apps [query]").description("Search connectable apps (na
8590
8624
  fail(e);
8591
8625
  }
8592
8626
  });
8593
- integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass its API key or other credentials with -c, 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("-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").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(
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(
8594
8628
  "--provide-to-managed",
8595
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"
8596
8630
  ).option(
@@ -8598,9 +8632,6 @@ integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass
8598
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"
8599
8633
  ).action(async (app, opts) => {
8600
8634
  try {
8601
- if (opts.fromConnection && opts.credential.length) {
8602
- fail(new Error("pass either -c or --from-connection, not both \u2014 otherwise the connection would hold a credential you did not choose"));
8603
- }
8604
8635
  if (opts.fromConnection && opts.rotate) {
8605
8636
  fail(
8606
8637
  new Error(
@@ -8608,15 +8639,13 @@ integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass
8608
8639
  )
8609
8640
  );
8610
8641
  }
8611
- let credentials;
8612
- if (opts.credential.length) {
8613
- credentials = {};
8614
- for (const c of opts.credential) {
8615
- const eq = c.indexOf("=");
8616
- if (eq < 1) fail(new Error(`invalid --credential ${JSON.stringify(c)}; expected key=value`));
8617
- credentials[c.slice(0, eq)] = c.slice(eq + 1);
8618
- }
8619
- }
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
+ });
8620
8649
  const res = await new ErdoClient().connectIntegration({
8621
8650
  app,
8622
8651
  name: opts.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.140.2",
3
+ "version": "0.141.0",
4
4
  "description": "Erdo CLI \u2014 drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {