@audienti/cli 0.1.80 → 0.1.81

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/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.81] - 2026-09-28
8
+
9
+ ### Added
10
+
11
+ - Manage social accounts from the CLI: `social-cookies show`, `pause`, `resume`, `resume-autopilot`, `recheck-account-type`, `reconnect`, `delete`, `submit-otp`, `create`, `update`, `rights` (`--workspace --grant|--revoke` or `--workspaces`), and `settings --scope`.
12
+ - Secrets never go on the command line. Passwords, one-time codes, cookies, TOTP secrets, and messaging PINs come from stdin (`--password-stdin`, `--secrets-stdin`, `--otp-stdin`), an environment variable (`--password-env` reads `AUDIENTI_SOCIAL_PASSWORD`, `--otp-env` reads `AUDIENTI_OTP_CODE`), or a hidden terminal prompt. Flags such as `--password` or `--otp` are refused before any request is sent, and the CLI never prints a secret.
13
+
7
14
  ## [0.1.80] - 2026-09-28
8
15
 
9
16
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.80",
3
+ "version": "0.1.81",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api-client.js CHANGED
@@ -74,6 +74,54 @@ export class AudientiClient {
74
74
  return this.requestJson(accountPath(accountId, ["social_cookies"], query));
75
75
  }
76
76
 
77
+ socialCookie(accountId, socialCookieId) {
78
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId]));
79
+ }
80
+
81
+ createSocialCookie(accountId, body) {
82
+ return this.requestJson(accountPath(accountId, ["social_cookies"]), { method: "POST", body });
83
+ }
84
+
85
+ updateSocialCookie(accountId, socialCookieId, body) {
86
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId]), { method: "PATCH", body });
87
+ }
88
+
89
+ deleteSocialCookie(accountId, socialCookieId) {
90
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId]), { method: "DELETE" });
91
+ }
92
+
93
+ pauseSocialCookie(accountId, socialCookieId) {
94
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "pause"]), { method: "POST" });
95
+ }
96
+
97
+ resumeSocialCookie(accountId, socialCookieId) {
98
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "resume"]), { method: "POST" });
99
+ }
100
+
101
+ resumeSocialCookieAutopilot(accountId, socialCookieId) {
102
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "resume_autopilot"]), { method: "POST" });
103
+ }
104
+
105
+ recheckSocialCookieAccountType(accountId, socialCookieId) {
106
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "recheck_account_type"]), { method: "POST" });
107
+ }
108
+
109
+ reconnectSocialCookie(accountId, socialCookieId) {
110
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "reconnect"]), { method: "POST" });
111
+ }
112
+
113
+ submitSocialCookieOtp(accountId, socialCookieId, body) {
114
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "submit_otp"]), { method: "POST", body });
115
+ }
116
+
117
+ updateSocialCookieRights(accountId, socialCookieId, body) {
118
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "rights"]), { method: "PATCH", body });
119
+ }
120
+
121
+ updateSocialCookieSettings(accountId, socialCookieId, body) {
122
+ return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "settings"]), { method: "PATCH", body });
123
+ }
124
+
77
125
  syncSocialCookieMessages(accountId, socialCookieId, body = {}) {
78
126
  return this.requestJson(accountPath(accountId, ["social_cookies", socialCookieId, "sync_messages"]), {
79
127
  method: "POST",
package/src/cli.js CHANGED
@@ -14070,6 +14070,195 @@ const HELP_TOPICS = new Map([
14070
14070
  ].join("\n")]
14071
14071
  ]);
14072
14072
 
14073
+ // Social account credentials (#2306). Secrets never travel as argv flags,
14074
+ // because argv lands in shell history and the process list. They come from
14075
+ // stdin, one named environment variable, or a no-echo terminal prompt, and the
14076
+ // CLI never prints them.
14077
+ const SOCIAL_SECRET_KEYS = ["password", "otp_secret", "cookie_bundle", "messaging_pin", "otp_code"];
14078
+ const SOCIAL_SECRET_FLAGS = new Set(["password", "pass", "otp", "otp-code", "code", "otp-secret", "totp", "totp-secret", "cookie", "cookies", "cookie-bundle", "messaging-pin", "pin", "secret"]);
14079
+ const SOCIAL_PASSWORD_ENV = "AUDIENTI_SOCIAL_PASSWORD";
14080
+ const SOCIAL_OTP_ENV = "AUDIENTI_OTP_CODE";
14081
+ const SOCIAL_COOKIE_ATTRIBUTE_KEYS = new Set([
14082
+ "service_identifier", "username", "name", "email", "phone_number",
14083
+ "geo_location", "country_code", "postal_code", "state_code", "city",
14084
+ "imap_server", "imap_port", "smtp_server", "smtp_port", "use_ssl",
14085
+ "automation_controls", "rate_limit_overrides", "operator_controls"
14086
+ ]);
14087
+ const SOCIAL_COOKIE_FIELD_OPTIONS = {
14088
+ name: { type: "string" },
14089
+ email: { type: "string" },
14090
+ attributes: { type: "string" },
14091
+ "password-stdin": { type: "boolean" },
14092
+ "secrets-stdin": { type: "boolean" },
14093
+ "password-env": { type: "boolean" },
14094
+ "prompt-password": { type: "boolean" }
14095
+ };
14096
+
14097
+ function rejectSecretArgs(args) {
14098
+ for (const arg of args) {
14099
+ if (!String(arg).startsWith("--")) continue;
14100
+ const flag = String(arg).slice(2).split("=")[0];
14101
+ if (SOCIAL_SECRET_FLAGS.has(flag)) {
14102
+ throw new CommandError(`--${flag} is not accepted because command-line secrets leak into shell history. Use --password-stdin, --secrets-stdin, --otp-stdin, the ${SOCIAL_PASSWORD_ENV} or ${SOCIAL_OTP_ENV} variable, or the hidden prompt.`);
14103
+ }
14104
+ }
14105
+ }
14106
+
14107
+ function rejectSecretKeys(object, flag) {
14108
+ const walk = (value) => {
14109
+ if (!value || typeof value !== "object") return;
14110
+ for (const [key, nested] of Object.entries(value)) {
14111
+ if (SOCIAL_SECRET_KEYS.includes(key)) throw new CommandError(`${flag} must not contain ${key}. Send secrets through stdin, an environment variable, or the hidden prompt.`);
14112
+ walk(nested);
14113
+ }
14114
+ };
14115
+ walk(object);
14116
+ }
14117
+
14118
+ function trimSecret(text) {
14119
+ return String(text ?? "").replace(/\r?\n$/, "");
14120
+ }
14121
+
14122
+ // Reads one line from the terminal without echoing it.
14123
+ async function promptHidden(context, question) {
14124
+ const input = context.stdin;
14125
+ if (!input?.isTTY || typeof input.setRawMode !== "function") {
14126
+ throw new CommandError("No terminal is available for a hidden prompt. Use stdin or an environment variable instead.");
14127
+ }
14128
+ const output = context.stderr || context.stdout;
14129
+ output.write(question);
14130
+ input.setRawMode(true);
14131
+ input.resume();
14132
+
14133
+ return new Promise((resolve, reject) => {
14134
+ let value = "";
14135
+ const finish = (error) => {
14136
+ input.removeListener("data", onData);
14137
+ input.setRawMode(false);
14138
+ input.pause();
14139
+ output.write("\n");
14140
+ if (error) reject(error);
14141
+ else resolve(value);
14142
+ };
14143
+ const onData = (chunk) => {
14144
+ for (const char of String(chunk)) {
14145
+ if (char === "\r" || char === "\n" || char === "\u0004") return finish();
14146
+ if (char === "\u0003") return finish(new CommandError("Cancelled."));
14147
+ if (char === "\u007f" || char === "\b") value = value.slice(0, -1);
14148
+ else value += char;
14149
+ }
14150
+ };
14151
+ input.on("data", onData);
14152
+ });
14153
+ }
14154
+
14155
+ async function readSocialOtp(values, usageLine, context) {
14156
+ if (values["otp-stdin"] && values["otp-env"]) throw new CommandError(usageLine);
14157
+ let code;
14158
+ if (values["otp-stdin"]) code = await readStdinText(context);
14159
+ else if (values["otp-env"]) code = context.env?.[SOCIAL_OTP_ENV];
14160
+ else if (stdinIsInteractive(context)) code = await promptHidden(context, "One-time code (hidden): ");
14161
+ else throw new CommandError(`Provide the code with --otp-stdin, --otp-env (${SOCIAL_OTP_ENV}), or run in a terminal for a hidden prompt.`);
14162
+
14163
+ const trimmed = String(code ?? "").trim();
14164
+ if (!trimmed) throw new CommandError("The one-time code is empty.");
14165
+ return trimmed;
14166
+ }
14167
+
14168
+ // Returns the secret fields to send, or {} when none were requested.
14169
+ async function readSocialSecrets(values, usageLine, context, { allowBundle = true } = {}) {
14170
+ const sources = ["password-stdin", "secrets-stdin", "password-env", "prompt-password"].filter((key) => values[key]);
14171
+ if (sources.length > 1) throw new CommandError(`Use only one of --${sources.join(", --")}.`);
14172
+ const [source] = sources;
14173
+ if (!source) return {};
14174
+
14175
+ if (source === "password-stdin") return { password: trimSecret(await readStdinText(context)) };
14176
+ if (source === "password-env") {
14177
+ const password = context.env?.[SOCIAL_PASSWORD_ENV];
14178
+ if (!password) throw new CommandError(`${SOCIAL_PASSWORD_ENV} is not set.`);
14179
+ return { password };
14180
+ }
14181
+ if (source === "prompt-password") return { password: await promptHidden(context, "Password (hidden): ") };
14182
+
14183
+ if (!allowBundle) throw new CommandError(usageLine);
14184
+ let parsed;
14185
+ try {
14186
+ parsed = JSON.parse(await readStdinText(context));
14187
+ } catch {
14188
+ throw new CommandError("--secrets-stdin expects a JSON object with password, otp_secret, cookie_bundle, or messaging_pin.");
14189
+ }
14190
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14191
+ throw new CommandError("--secrets-stdin expects a JSON object with password, otp_secret, cookie_bundle, or messaging_pin.");
14192
+ }
14193
+ const allowed = ["password", "otp_secret", "cookie_bundle", "messaging_pin"];
14194
+ const unknown = Object.keys(parsed).filter((key) => !allowed.includes(key));
14195
+ if (unknown.length > 0) throw new CommandError(`--secrets-stdin accepts only ${allowed.join(", ")}.`);
14196
+ return parsed;
14197
+ }
14198
+
14199
+ async function socialCookieBody(values, usageLine, context, fields) {
14200
+ const attributes = parseJsonObjectOption(values.attributes, "--attributes") || {};
14201
+ const unknown = Object.keys(attributes).filter((key) => !SOCIAL_COOKIE_ATTRIBUTE_KEYS.has(key));
14202
+ if (unknown.length > 0) {
14203
+ throw new CommandError(`--attributes accepts only ${[...SOCIAL_COOKIE_ATTRIBUTE_KEYS].join(", ")}. Secrets go through stdin, an environment variable, or the hidden prompt.`);
14204
+ }
14205
+ rejectSecretKeys(attributes, "--attributes");
14206
+
14207
+ return {
14208
+ ...attributes,
14209
+ ...compactObject({ ...fields, name: values.name, email: values.email }),
14210
+ ...(await readSocialSecrets(values, usageLine, context))
14211
+ };
14212
+ }
14213
+
14214
+ function socialCookieRightsBody(values, usageLine) {
14215
+ if (values.workspaces !== undefined) {
14216
+ if (values.workspace || values.grant || values.revoke) throw new CommandError(usageLine);
14217
+ return { access_account_ids: values.workspaces.split(",").map((id) => id.trim()).filter(Boolean) };
14218
+ }
14219
+ if (!values.workspace || values.grant === values.revoke) throw new CommandError(usageLine);
14220
+ return { access_account_id: values.workspace, granted: Boolean(values.grant) };
14221
+ }
14222
+
14223
+ async function socialCookieSettingsBody(values, usageLine, context) {
14224
+ const body = parseJsonObjectOption(values.body, "--body") || {};
14225
+ rejectSecretKeys(body, "--body");
14226
+ const merged = {
14227
+ ...body,
14228
+ ...compactObject({
14229
+ scope: values.scope,
14230
+ enabled: values.enabled,
14231
+ key: values.key,
14232
+ target: values.target,
14233
+ daily: values.daily,
14234
+ hourly: values.hourly,
14235
+ action_type: values["action-type"],
14236
+ mode: values.mode,
14237
+ level: values.level
14238
+ })
14239
+ };
14240
+
14241
+ const secrets = await readSocialSecrets(values, usageLine, context, { allowBundle: false });
14242
+ if (Object.keys(secrets).length > 0) {
14243
+ if (!["details", "servers"].includes(values.scope)) throw new CommandError("A password is accepted only with --scope details or --scope servers.");
14244
+ merged.social_cookie = { ...(merged.social_cookie || {}), ...secrets };
14245
+ }
14246
+ return merged;
14247
+ }
14248
+
14249
+ function describeSocialCookie(cookie) {
14250
+ if (!cookie) return "Social account not found.";
14251
+ const lines = [
14252
+ `${display(cookie.name || cookie.username)} (${display(cookie.prefix_id)})`,
14253
+ `Platform: ${display(cookie.platform)} | Status: ${display(cookie.status)}${cookie.autopilot_paused ? " | Autopilot paused" : ""}`,
14254
+ `Connection: ${display(cookie.connection?.title)}`
14255
+ ];
14256
+ if (cookie.connection?.body) lines.push(` ${cookie.connection.body}`);
14257
+ if (cookie.otp_pending) lines.push("Waiting for a one-time code: run `audienti social-cookies submit-otp`.");
14258
+ for (const fact of cookie.account_facts || []) lines.push(`${display(fact?.label)}: ${display(fact?.value)}`);
14259
+ return lines.join("\n");
14260
+ }
14261
+
14073
14262
  // Single-purpose actions that mirror one web button each (#2300, #2302).
14074
14263
  // Every entry calls one API route that shares its service with the web action.
14075
14264
  const ACCOUNT_OPTION_USAGE = "[--json] [--account <acct_id>]";
@@ -14427,6 +14616,131 @@ const PARITY_ACTION_COMMANDS = new Map([
14427
14616
  required: ["premise"],
14428
14617
  run: (client, accountId, [id], values) => client.updateMotionPremise(accountId, id, { motion: { premise: values.premise } }),
14429
14618
  done: ([id]) => `Updated the premise for ${id}.`
14619
+ }],
14620
+ // Social account page (#2306). Passwords, OTP codes, cookies, TOTP secrets
14621
+ // and the messaging PIN are never argv flags: they come from stdin, an
14622
+ // environment variable, or a no-echo terminal prompt, and are never printed.
14623
+ ["social-cookies show", {
14624
+ usage: "audienti social-cookies show <scok_id>",
14625
+ purpose: "Show one social account's connection state and facts, like its Overview. Never shows credentials.",
14626
+ api: "GET /api/v1/accounts/:account_id/social_cookies/:id.json",
14627
+ run: (client, accountId, [id]) => client.socialCookie(accountId, id),
14628
+ done: (_positionals, payload) => describeSocialCookie(payload?.social_cookie)
14629
+ }],
14630
+ ["social-cookies pause", {
14631
+ usage: "audienti social-cookies pause <scok_id>",
14632
+ purpose: "Pause one social account so no activity runs until it is resumed.",
14633
+ api: "POST /api/v1/accounts/:account_id/social_cookies/:id/pause.json",
14634
+ run: (client, accountId, [id]) => client.pauseSocialCookie(accountId, id),
14635
+ done: (_positionals, payload) => display(payload?.message)
14636
+ }],
14637
+ ["social-cookies resume", {
14638
+ usage: "audienti social-cookies resume <scok_id>",
14639
+ purpose: "Resume a paused social account; it reconnects before activity restarts.",
14640
+ api: "POST /api/v1/accounts/:account_id/social_cookies/:id/resume.json",
14641
+ run: (client, accountId, [id]) => client.resumeSocialCookie(accountId, id),
14642
+ done: (_positionals, payload) => display(payload?.message)
14643
+ }],
14644
+ ["social-cookies resume-autopilot", {
14645
+ usage: "audienti social-cookies resume-autopilot <scok_id>",
14646
+ purpose: "Clear an automatic autopilot pause on one social account.",
14647
+ api: "POST /api/v1/accounts/:account_id/social_cookies/:id/resume_autopilot.json",
14648
+ run: (client, accountId, [id]) => client.resumeSocialCookieAutopilot(accountId, id),
14649
+ done: (_positionals, payload) => display(payload?.message)
14650
+ }],
14651
+ ["social-cookies recheck-account-type", {
14652
+ usage: "audienti social-cookies recheck-account-type <scok_id>",
14653
+ purpose: "Queue a LinkedIn account-type check (Premium, Sales Navigator) for one connected account.",
14654
+ api: "POST /api/v1/accounts/:account_id/social_cookies/:id/recheck_account_type.json",
14655
+ run: (client, accountId, [id]) => client.recheckSocialCookieAccountType(accountId, id),
14656
+ done: (_positionals, payload) => display(payload?.message)
14657
+ }],
14658
+ ["social-cookies reconnect", {
14659
+ usage: "audienti social-cookies reconnect <scok_id>",
14660
+ purpose: "Ask one social account to reconnect; a mailbox rechecks its connection.",
14661
+ api: "POST /api/v1/accounts/:account_id/social_cookies/:id/reconnect.json",
14662
+ run: (client, accountId, [id]) => client.reconnectSocialCookie(accountId, id),
14663
+ done: (_positionals, payload) => display(payload?.message)
14664
+ }],
14665
+ ["social-cookies delete", {
14666
+ usage: "audienti social-cookies delete <scok_id>",
14667
+ purpose: "Delete one social account you own.",
14668
+ api: "DELETE /api/v1/accounts/:account_id/social_cookies/:id.json",
14669
+ run: (client, accountId, [id]) => client.deleteSocialCookie(accountId, id),
14670
+ done: (_positionals, payload) => display(payload?.message)
14671
+ }],
14672
+ ["social-cookies submit-otp", {
14673
+ usage: "audienti social-cookies submit-otp <scok_id> [--otp-stdin | --otp-env]",
14674
+ purpose: "Send the one-time login code a social account is waiting for. Reads the code from stdin (--otp-stdin), AUDIENTI_OTP_CODE (--otp-env), or a hidden prompt.",
14675
+ api: "POST /api/v1/accounts/:account_id/social_cookies/:id/submit_otp.json",
14676
+ secretInput: true,
14677
+ options: { "otp-stdin": { type: "boolean" }, "otp-env": { type: "boolean" } },
14678
+ parse: async (values, usageLine, context) => ({ ...values, otpCode: await readSocialOtp(values, usageLine, context) }),
14679
+ run: (client, accountId, [id], values) => client.submitSocialCookieOtp(accountId, id, { otp_code: values.otpCode }),
14680
+ done: (_positionals, payload) => display(payload?.message)
14681
+ }],
14682
+ ["social-cookies create", {
14683
+ usage: "audienti social-cookies create --service <linkedin|gmail|...> --username <name> [--name <label>] [--email <email>] [--attributes <json>] [--password-stdin | --secrets-stdin | --password-env | --prompt-password]",
14684
+ minPositionals: 0,
14685
+ maxPositionals: 0,
14686
+ purpose: "Add a social account and queue its login, like Add account on the web. Secrets come from stdin, AUDIENTI_SOCIAL_PASSWORD, or a hidden prompt; never from flags.",
14687
+ api: "POST /api/v1/accounts/:account_id/social_cookies.json",
14688
+ secretInput: true,
14689
+ options: { service: { type: "string" }, username: { type: "string" }, ...SOCIAL_COOKIE_FIELD_OPTIONS },
14690
+ required: ["service"],
14691
+ parse: async (values, usageLine, context) => ({
14692
+ ...values,
14693
+ body: { social_cookie: await socialCookieBody(values, usageLine, context, { service_identifier: values.service, username: values.username }) }
14694
+ }),
14695
+ run: (client, accountId, _positionals, values) => client.createSocialCookie(accountId, values.body),
14696
+ done: (_positionals, payload) => `Added social account ${display(payload?.social_cookie?.prefix_id)}; login is queued.`
14697
+ }],
14698
+ ["social-cookies update", {
14699
+ usage: "audienti social-cookies update <scok_id> [--username <name>] [--name <label>] [--email <email>] [--attributes <json>] [--password-stdin | --secrets-stdin | --password-env | --prompt-password]",
14700
+ purpose: "Edit one social account's details, controls and limits, like its edit page. A stored password is kept unless a new one is sent.",
14701
+ api: "PATCH /api/v1/accounts/:account_id/social_cookies/:id.json",
14702
+ secretInput: true,
14703
+ options: { username: { type: "string" }, ...SOCIAL_COOKIE_FIELD_OPTIONS },
14704
+ parse: async (values, usageLine, context) => ({
14705
+ ...values,
14706
+ body: { social_cookie: await socialCookieBody(values, usageLine, context, { username: values.username }) }
14707
+ }),
14708
+ run: (client, accountId, [id], values) => client.updateSocialCookie(accountId, id, values.body),
14709
+ done: ([id]) => `Updated social account ${id}.`
14710
+ }],
14711
+ ["social-cookies rights", {
14712
+ usage: "audienti social-cookies rights <scok_id> (--workspace <acct_id> (--grant | --revoke) | --workspaces <acct_id,acct_id>)",
14713
+ purpose: "Choose which of your workspaces may use one social account, like its Access tab.",
14714
+ api: "PATCH /api/v1/accounts/:account_id/social_cookies/:id/rights.json",
14715
+ options: { workspace: { type: "string" }, workspaces: { type: "string" }, grant: { type: "boolean" }, revoke: { type: "boolean" } },
14716
+ parse: (values, usageLine) => ({ ...values, body: socialCookieRightsBody(values, usageLine) }),
14717
+ run: (client, accountId, [id], values) => client.updateSocialCookieRights(accountId, id, values.body),
14718
+ done: (_positionals, payload) => `Workspaces: ${(payload?.accounts || []).map((account) => `${display(account?.name)} (${display(account?.prefix_id)})`).join(", ") || "none"}.`
14719
+ }],
14720
+ ["social-cookies settings", {
14721
+ usage: "audienti social-cookies settings <scok_id> --scope <scope> [--enabled <true|false>] [--key <key>] [--target <n>] [--daily <n>] [--hourly <n>] [--action-type <type>] [--mode <mode>] [--level <level>] [--body <json>] [--password-stdin | --password-env | --prompt-password]",
14722
+ purpose: "Change one Overview setting (scopes: sending, gate, action, visibility_mode, operator, limits, limits_preset, hours, advanced, automation, details, location, servers). Only details and servers accept a password, from stdin, AUDIENTI_SOCIAL_PASSWORD, or a hidden prompt.",
14723
+ api: "PATCH /api/v1/accounts/:account_id/social_cookies/:id/settings.json",
14724
+ secretInput: true,
14725
+ options: {
14726
+ scope: { type: "string" },
14727
+ enabled: { type: "string" },
14728
+ key: { type: "string" },
14729
+ target: { type: "string" },
14730
+ daily: { type: "string" },
14731
+ hourly: { type: "string" },
14732
+ "action-type": { type: "string" },
14733
+ mode: { type: "string" },
14734
+ level: { type: "string" },
14735
+ body: { type: "string" },
14736
+ "password-stdin": { type: "boolean" },
14737
+ "password-env": { type: "boolean" },
14738
+ "prompt-password": { type: "boolean" }
14739
+ },
14740
+ required: ["scope"],
14741
+ parse: async (values, usageLine, context) => ({ ...values, body: await socialCookieSettingsBody(values, usageLine, context) }),
14742
+ run: (client, accountId, [id], values) => client.updateSocialCookieSettings(accountId, id, values.body),
14743
+ done: ([id], payload) => `Saved ${display(payload?.scope)} for social account ${id}.`
14430
14744
  }]
14431
14745
  ]);
14432
14746
 
@@ -14476,13 +14790,14 @@ function parseJsonObjectOption(raw, flag) {
14476
14790
  async function runParityActionCommand(topic, args, context, { accountOverride } = {}) {
14477
14791
  const spec = PARITY_ACTION_COMMANDS.get(topic);
14478
14792
  const usageLine = `Usage: ${spec.usage} ${ACCOUNT_OPTION_USAGE}`;
14793
+ if (spec.secretInput) rejectSecretArgs(args);
14479
14794
  const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), ...(spec.options || {}) });
14480
14795
  const minPositionals = spec.minPositionals ?? 1;
14481
14796
  const maxPositionals = spec.maxPositionals ?? 1;
14482
14797
  if (positionals.length < minPositionals || positionals.length > maxPositionals) throw new CommandError(usageLine);
14483
14798
  if ((spec.required || []).some((key) => !values[key])) throw new CommandError(usageLine);
14484
14799
 
14485
- const parsedValues = spec.parse ? spec.parse(values, usageLine) : values;
14800
+ const parsedValues = spec.parse ? await spec.parse(values, usageLine, context) : values;
14486
14801
 
14487
14802
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
14488
14803
  const payload = await spec.run(client, accountId, positionals, parsedValues);