@usesocial/cli 0.3.1 → 0.3.3

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
@@ -1,5 +1,17 @@
1
1
  # @usesocial/cli
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#14](https://github.com/usesocial/monorepo/pull/14) [`22ba757`](https://github.com/usesocial/monorepo/commit/22ba757efd459a0cb3009c91ba833687e2ce2796) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Ask for phone number during `social account login`, save it after device approval, and include it in account identity output.
8
+
9
+ ## 0.3.2
10
+
11
+ ### Patch Changes
12
+
13
+ - [#12](https://github.com/usesocial/monorepo/pull/12) [`a85b525`](https://github.com/usesocial/monorepo/commit/a85b525d36908cf25da92a0538b77ac39eb817c6) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Read structured write payloads from stdin as JSON objects and remove `--body` from stdin-backed post, comment, and message commands. Plain stdin still becomes body text; valid non-object JSON now fails fast instead of being sent.
14
+
3
15
  ## 0.3.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -74,8 +74,7 @@ connected account.
74
74
  ### Body text
75
75
 
76
76
  Commands that create a post, comment, message, or connection note read the body
77
- from **stdin** — there is no text argument. Pipe it in, or pass a raw `--body`
78
- JSON object:
77
+ from **stdin** — there is no text argument. Pipe it in:
79
78
 
80
79
  ```sh
81
80
  echo "gm" | social x post
@@ -83,8 +82,9 @@ social linkedin post < draft.md
83
82
  social linkedin requests send @handle < note.txt # optional connection note
84
83
  ```
85
84
 
86
- On an interactive terminal with nothing piped (and no `--body`), these commands
87
- fail fast rather than hang.
85
+ On an interactive terminal with nothing piped, required body-text commands fail
86
+ fast rather than hang. To send advanced payload fields, pipe a JSON object via
87
+ stdin; if stdin parses as JSON but is not an object, the command rejects it.
88
88
 
89
89
  ### Top level
90
90
 
@@ -212,12 +212,12 @@ jq -r '.items[].id' /tmp/recent-posts.json \
212
212
  default; clear Write in the prompt to grant read-only access.
213
213
  2. Asks for your email address.
214
214
  3. Sends a magic link to that email with the device approval screen already
215
- attached.
215
+ attached while asking for your phone number.
216
216
  4. Waits until you click the magic link and approve the CLI session in the
217
- browser.
218
- 5. Confirms billing checkout in the terminal when a seat is needed.
219
- 6. Stores the returned token in your OS keyring, falling back to
220
- `~/.social/credentials.json` (mode `0600`) when no keyring is available.
217
+ browser, then stores the returned token in your OS keyring.
218
+ 5. Stores the phone number, then confirms billing checkout in the terminal when
219
+ a seat is needed. If no keyring is available, credentials fall back to
220
+ `~/.social/credentials.json` (mode `0600`).
221
221
  Non-production `SOCIAL_API_URL` values use an API-specific credential
222
222
  namespace so staging tests do not overwrite the production session.
223
223
 
package/dist/index.mjs CHANGED
@@ -9583,6 +9583,7 @@ const user = pgTable("user", {
9583
9583
  emailVerified: boolean("emailVerified").notNull().default(false),
9584
9584
  name: text$1("name").notNull(),
9585
9585
  image: text$1("image"),
9586
+ phoneNumber: text$1("phone_number"),
9586
9587
  createdAt: timestamp("createdAt", { mode: "date" }).notNull().defaultNow(),
9587
9588
  updatedAt: timestamp("updatedAt", { mode: "date" }).notNull().defaultNow().$onUpdate(() => /* @__PURE__ */ new Date())
9588
9589
  });
@@ -12971,6 +12972,7 @@ const positionalDisplayKeyFor$1 = (op, param) => op.config.paramLabels?.[param.n
12971
12972
  const bodyTextDisplayKeyFor$1 = (op) => op.config.bodyTextArg === void 0 ? void 0 : op.config.paramLabels?.[op.config.bodyTextArg] ?? op.config.bodyTextArg;
12972
12973
  const hasStdinBodyText$1 = (op) => op.config.bodyTextArg !== void 0;
12973
12974
  const stdinBodyTextRequired$1 = (op) => hasStdinBodyText$1(op) && op.config.bodyTextRequired !== false;
12975
+ const hasBodyOption = (op) => op.hasBody && !hasStdinBodyText$1(op);
12974
12976
  const idempotencyFor$1 = (op) => {
12975
12977
  if (op.endpoint.capability === "read") return {
12976
12978
  safe: true,
@@ -13062,7 +13064,7 @@ const exampleCommandFor$1 = (op, commandName, root) => {
13062
13064
  parts.push(param.in === "path" || param.kind !== "array" ? hint : `${hint}...`);
13063
13065
  }
13064
13066
  if (hasStdinBodyText$1(op)) return `echo "..." | ${parts.join(" ")}`;
13065
- if (op.hasBody) parts.push("--body", "'{...}'");
13067
+ if (hasBodyOption(op)) parts.push("--body", "'{...}'");
13066
13068
  return parts.join(" ");
13067
13069
  };
13068
13070
  const operationModeFor$1 = (op) => {
@@ -13106,7 +13108,7 @@ const helpDescriptionFor$1 = (op, commandName, root) => {
13106
13108
  ...root ? [] : [op.config.group],
13107
13109
  commandName
13108
13110
  ].join(" ");
13109
- return `${description} | ${usage}${hasStdinBodyText$1(op) ? ` | body text is read from stdin: \`echo "..." | ${label}\` (or pass --body)` : ""}`;
13111
+ return `${description} | ${usage}${hasStdinBodyText$1(op) ? ` | body text is read from stdin: \`echo "..." | ${label}\`` : ""}`;
13110
13112
  };
13111
13113
  const commandPathFor = (op) => `linkedin ${op.config.root ? op.config.command : `${op.config.group} ${op.config.command}`}`;
13112
13114
  const contractInputSchemaName = (op) => op.hasBody ? `${op.schemaBaseName}ContractInput` : `${op.schemaBaseName}Input`;
@@ -13160,7 +13162,7 @@ const buildArgs$1 = (op) => {
13160
13162
  description: param.description
13161
13163
  };
13162
13164
  }
13163
- if (op.hasBody) args.body = {
13165
+ if (hasBodyOption(op)) args.body = {
13164
13166
  type: "string",
13165
13167
  description: "JSON request body"
13166
13168
  };
@@ -13252,7 +13254,7 @@ const buildBody = (deps, op, input) => {
13252
13254
  const bodyTextArg = op.config.bodyTextArg;
13253
13255
  const bodyParamEntries = Object.entries(op.config.bodyParams ?? {});
13254
13256
  if (!bodyTextArg && bodyParamEntries.length === 0) return input.body === void 0 ? void 0 : deps.parseJSONObject("--body", input.body);
13255
- const bodyFromJSON = input.body === void 0 ? void 0 : deps.parseJSONObject("--body", input.body);
13257
+ const bodyFromJSON = input.body === void 0 ? void 0 : deps.parseJSONObject(hasStdinBodyText$1(op) ? "stdin" : "--body", input.body);
13256
13258
  const bodyText = bodyTextArg && input[bodyTextArg] !== void 0 ? deps.parseBodyText(`<${bodyTextDisplayKeyFor$1(op) ?? bodyTextArg}>`, input[bodyTextArg]) : void 0;
13257
13259
  const bodyParamValues = /* @__PURE__ */ new Map();
13258
13260
  for (const [name, param] of bodyParamEntries) {
@@ -13266,7 +13268,7 @@ const buildBody = (deps, op, input) => {
13266
13268
  }
13267
13269
  if (bodyTextArg && bodyText !== void 0) body[bodyTextArg] = bodyText;
13268
13270
  if (op.config.bodyTextRequired !== false && (Boolean(bodyTextArg) || bodyParamEntries.some(([, param]) => param.required)) && bodyFromJSON === void 0) {
13269
- if ((bodyTextArg ? bodyText === void 0 : false) || bodyParamEntries.some(([name, param]) => param.required && bodyParamValues.get(name) === void 0)) throw new UsageError(bodyTextArg && bodyParamEntries.length === 0 ? `Pass <${bodyTextDisplayKeyFor$1(op) ?? bodyTextArg}> or --body.` : "Pass required body positional arguments or --body.");
13271
+ if ((bodyTextArg ? bodyText === void 0 : false) || bodyParamEntries.some(([name, param]) => param.required && bodyParamValues.get(name) === void 0)) throw new UsageError(bodyTextArg && bodyParamEntries.length === 0 ? `Pass <${bodyTextDisplayKeyFor$1(op) ?? bodyTextArg}>.` : "Pass required body positional arguments or --body.");
13270
13272
  }
13271
13273
  return body;
13272
13274
  };
@@ -13276,7 +13278,7 @@ const checkBodyRequired = (op, args) => {
13276
13278
  const bodyTextArg = op.config.bodyTextArg;
13277
13279
  if (bodyTextArg && op.config.bodyTextRequired !== false) expressions.push(args[bodyTextDisplayKeyFor$1(op) ?? bodyTextArg] === void 0);
13278
13280
  for (const [name, param] of Object.entries(op.config.bodyParams ?? {})) if (param.required) expressions.push(args[flagName(bodyParamArg(name, param))] === void 0);
13279
- if (expressions.length > 0 && typeof args.body !== "string" && expressions.some(Boolean)) throw new UsageError(op.config.bodyTextArg && Object.keys(op.config.bodyParams ?? {}).length === 0 ? `Pass <${bodyTextDisplayKeyFor$1(op) ?? op.config.bodyTextArg}> or --body.` : "Pass required body positional arguments or --body.");
13281
+ if (expressions.length > 0 && typeof args.body !== "string" && expressions.some(Boolean)) throw new UsageError(op.config.bodyTextArg && Object.keys(op.config.bodyParams ?? {}).length === 0 ? `Pass <${bodyTextDisplayKeyFor$1(op) ?? op.config.bodyTextArg}>.` : "Pass required body positional arguments or --body.");
13280
13282
  };
13281
13283
  const dataWithAuditableWriteTarget = (op, data, body) => {
13282
13284
  if (op.schemaBaseName !== "PostsReact") return data;
@@ -13289,18 +13291,27 @@ const dataWithAuditableWriteTarget = (op, data, body) => {
13289
13291
  };
13290
13292
  };
13291
13293
  const trimTrailingNewline$1 = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
13292
- const noBodyTextError$1 = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\` (or pass --body '<json>').`);
13294
+ const noBodyTextError$1 = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
13295
+ const parseStdinBody$1 = (deps, bodyTextName, bodyTextKey, value) => {
13296
+ try {
13297
+ JSON.parse(value);
13298
+ } catch {
13299
+ return { [bodyTextKey]: deps.parseBodyText(`<${bodyTextName}>`, value) };
13300
+ }
13301
+ return deps.parseJSONObject("stdin", value);
13302
+ };
13293
13303
  const resolveBodyTextFromStdin$1 = async (op, deps, args, commandLabel) => {
13294
13304
  if (!hasStdinBodyText$1(op)) return;
13295
- const bodyTextArg = bodyTextDisplayKeyFor$1(op) ?? op.config.bodyTextArg;
13305
+ const bodyTextArg = op.config.bodyTextArg;
13296
13306
  if (bodyTextArg === void 0) return;
13297
- const required = stdinBodyTextRequired$1(op) && typeof args.body !== "string";
13307
+ const displayTextArg = bodyTextDisplayKeyFor$1(op) ?? bodyTextArg;
13308
+ const required = stdinBodyTextRequired$1(op);
13298
13309
  if (deps.isStdinTTY()) {
13299
13310
  if (required) throw noBodyTextError$1(commandLabel);
13300
13311
  return;
13301
13312
  }
13302
13313
  const piped = trimTrailingNewline$1(await deps.readStdin());
13303
- if (piped.length > 0) args[bodyTextArg] = piped;
13314
+ if (piped.length > 0) args.body = JSON.stringify(parseStdinBody$1(deps, displayTextArg, bodyTextArg, piped));
13304
13315
  else if (required) throw noBodyTextError$1(commandLabel);
13305
13316
  };
13306
13317
  const runOperation$1 = async (op, contract, deps, args, rawArgs = []) => {
@@ -13989,7 +14000,7 @@ const buildMessage$1 = (deps, sendCommand) => {
13989
14000
  return defineCommand({
13990
14001
  meta: commandMeta$2({
13991
14002
  name: "message",
13992
- description: "Send a LinkedIn message to a person or conversation [write] | usage: `social linkedin message <target> < body-text` | body text is read from stdin: `echo \"...\" | social linkedin message <target>` (or pass --body)",
14003
+ description: "Send a LinkedIn message to a person or conversation [write] | usage: `social linkedin message <target> < body-text` | body text is read from stdin: `echo \"...\" | social linkedin message <target>`",
13993
14004
  method: "POST",
13994
14005
  capability: "write",
13995
14006
  mutates: true,
@@ -14007,10 +14018,6 @@ const buildMessage$1 = (deps, sendCommand) => {
14007
14018
  description: "LinkedIn person or conversation target: @handle, profile URL, profile_id:<id>, chat_id:<id>, or a messaging thread URL",
14008
14019
  valueHint: "target"
14009
14020
  },
14010
- body: {
14011
- type: "string",
14012
- description: "JSON request body"
14013
- },
14014
14021
  account: {
14015
14022
  type: "string",
14016
14023
  description: "Account selector: @handle or profile_id:<id>; overrides config default"
@@ -14018,6 +14025,7 @@ const buildMessage$1 = (deps, sendCommand) => {
14018
14025
  },
14019
14026
  run: async ({ args }) => {
14020
14027
  const positionals = Array.isArray(args._) ? args._.map(String) : [];
14028
+ if (typeof args.body === "string") throw new UsageError("Unsupported option --body for linkedin message.");
14021
14029
  if (positionals[1] === "delete" || positionals[1] === "edit") {
14022
14030
  const verbCommand = positionals[1] === "delete" ? deleteCommand : editCommand;
14023
14031
  await verbCommand.run?.({
@@ -14033,23 +14041,13 @@ const buildMessage$1 = (deps, sendCommand) => {
14033
14041
  return;
14034
14042
  }
14035
14043
  const target = args.target ?? (Array.isArray(args._) && args._.length > 0 ? args._[0] : void 0);
14036
- const hasBody = typeof args.body === "string";
14037
- let textInput;
14038
- if (deps.isStdinTTY()) {
14039
- if (!hasBody) throw noBodyTextError$1("social linkedin message <target>");
14040
- } else {
14041
- const piped = trimTrailingNewline$1(await deps.readStdin());
14042
- if (piped.length > 0) textInput = piped;
14043
- else if (!hasBody) throw noBodyTextError$1("social linkedin message <target>");
14044
- }
14044
+ let body;
14045
+ if (deps.isStdinTTY()) throw noBodyTextError$1("social linkedin message <target>");
14046
+ const piped = trimTrailingNewline$1(await deps.readStdin());
14047
+ if (piped.length > 0) body = parseStdinBody$1(deps, "text", "text", piped);
14048
+ else throw noBodyTextError$1("social linkedin message <target>");
14045
14049
  if (target === void 0) throw new UsageError("Pass a conversation or person target.");
14046
- const bodyFromJSON = typeof args.body === "string" ? deps.parseJSONObject("--body", args.body) : void 0;
14047
- const text = textInput === void 0 ? void 0 : deps.parseBodyText("<text>", textInput);
14048
- const body = text === void 0 ? bodyFromJSON : {
14049
- ...bodyFromJSON ?? {},
14050
- text
14051
- };
14052
- if (body === void 0 || typeof body.text !== "string") throw new UsageError("Pass <text> or --body.");
14050
+ if (body === void 0 || typeof body.text !== "string") throw new UsageError("Pass body text via stdin.");
14053
14051
  const account = typeof args.account === "string" ? args.account : void 0;
14054
14052
  const classified = await deps.classifyMessageTarget("target", target, {
14055
14053
  platform: "linkedin",
@@ -17155,14 +17153,14 @@ const operationModeFor = (op) => {
17155
17153
  if (op.endpoint.method === "DELETE") return "destructive";
17156
17154
  return op.endpoint.mutates ? "write" : "read";
17157
17155
  };
17158
- const signaturePartFor = (op, name, arg) => {
17156
+ const signaturePartFor = (_op, name, arg) => {
17159
17157
  if (!isObjectRecord$1(arg)) return;
17160
17158
  if (arg.type === "positional") {
17161
17159
  const valueHint = typeof arg.valueHint === "string" ? arg.valueHint : name;
17162
17160
  return arg.required === true ? `<${valueHint}>` : `[${valueHint}]`;
17163
17161
  }
17164
17162
  if (name === "limit") return "--limit N";
17165
- if (name === "body" && !hasStdinBodyText(op)) return "--body JSON";
17163
+ if (name === "body") return "--body JSON";
17166
17164
  };
17167
17165
  const stdinSignaturePart = (op) => hasStdinBodyText(op) ? "< body-text" : void 0;
17168
17166
  const usageFor = (op, commandName, group) => {
@@ -17191,7 +17189,7 @@ const helpDescriptionFor = (op, commandName, group) => {
17191
17189
  group,
17192
17190
  commandName
17193
17191
  ].filter(Boolean).join(" ");
17194
- return `${description} | ${usage}${hasStdinBodyText(op) ? ` | body text is read from stdin: \`echo "..." | ${label}\` (or pass --body)` : ""}`;
17192
+ return `${description} | ${usage}${hasStdinBodyText(op) ? ` | body text is read from stdin: \`echo "..." | ${label}\`` : ""}`;
17195
17193
  };
17196
17194
  const contractFor$1 = (op, commandName, group) => {
17197
17195
  const mutates = op.endpoint.mutates;
@@ -17237,7 +17235,7 @@ const buildArgs = (op) => {
17237
17235
  description: param.description
17238
17236
  };
17239
17237
  }
17240
- if (op.hasBody) args.body = {
17238
+ if (op.hasBody && !hasStdinBodyText(op)) args.body = {
17241
17239
  type: "string",
17242
17240
  description: "JSON request body"
17243
17241
  };
@@ -17383,18 +17381,27 @@ const withEchoedTargetId = (op, data, body) => {
17383
17381
  };
17384
17382
  };
17385
17383
  const trimTrailingNewline = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
17386
- const noBodyTextError = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\` (or pass --body '<json>').`);
17384
+ const noBodyTextError = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
17385
+ const parseStdinBody = (deps, bodyTextName, bodyTextKey, value) => {
17386
+ try {
17387
+ JSON.parse(value);
17388
+ } catch {
17389
+ return { [bodyTextKey]: deps.parseBodyText(`<${bodyTextName}>`, value) };
17390
+ }
17391
+ return deps.parseJSONObject("stdin", value);
17392
+ };
17387
17393
  const resolveBodyTextFromStdin = async (op, deps, args, commandLabel) => {
17388
17394
  if (!hasStdinBodyText(op)) return;
17389
- const bodyTextArg = bodyTextDisplayKeyFor(op) ?? op.config.bodyTextArg;
17395
+ const bodyTextArg = op.config.bodyTextArg;
17390
17396
  if (bodyTextArg === void 0) return;
17391
- const required = stdinBodyTextRequired(op) && args.body === void 0;
17397
+ const displayTextArg = bodyTextDisplayKeyFor(op) ?? bodyTextArg;
17398
+ const required = stdinBodyTextRequired(op);
17392
17399
  if (deps.isStdinTTY()) {
17393
17400
  if (required) throw noBodyTextError(commandLabel);
17394
17401
  return;
17395
17402
  }
17396
17403
  const piped = trimTrailingNewline(await deps.readStdin());
17397
- if (piped.length > 0) args[bodyTextArg] = piped;
17404
+ if (piped.length > 0) args.body = JSON.stringify(parseStdinBody(deps, displayTextArg, bodyTextArg, piped));
17398
17405
  else if (required) throw noBodyTextError(commandLabel);
17399
17406
  };
17400
17407
  const runOperation = async (op, contract, deps, args, rawArgs = []) => {
@@ -17483,7 +17490,7 @@ const runOperation = async (op, contract, deps, args, rawArgs = []) => {
17483
17490
  };
17484
17491
  let body;
17485
17492
  if (op.hasBody) {
17486
- const bodyFromJSON = input.body === void 0 ? void 0 : deps.parseJSONObject("--body", input.body);
17493
+ const bodyFromJSON = input.body === void 0 ? void 0 : deps.parseJSONObject(hasStdinBodyText(op) ? "stdin" : "--body", input.body);
17487
17494
  const textArg = op.config.bodyTextArg;
17488
17495
  if (textArg) {
17489
17496
  const displayTextArg = bodyTextDisplayKeyFor(op) ?? textArg;
@@ -17946,7 +17953,7 @@ const buildMessage = (deps) => {
17946
17953
  return defineCommand({
17947
17954
  meta: commandMeta$1({
17948
17955
  name: "message",
17949
- description: "Send a DM to a participant or conversation [write] | usage: `social x message <recipients> < body-text` | body text is read from stdin: `echo \"...\" | social x message <recipients>` (or pass --body)",
17956
+ description: "Send a DM to a participant or conversation [write] | usage: `social x message <recipients> < body-text` | body text is read from stdin: `echo \"...\" | social x message <recipients>`",
17950
17957
  method: "POST",
17951
17958
  capability: "write",
17952
17959
  mutates: true,
@@ -17960,36 +17967,23 @@ const buildMessage = (deps) => {
17960
17967
  description: "Recipient(s): chat URL, chat_id:<id>, @handle, profile URL, or profile_id:<id>; comma-separate profiles for a group",
17961
17968
  valueHint: "recipients"
17962
17969
  },
17963
- body: {
17964
- type: "string",
17965
- description: "JSON request body"
17966
- },
17967
17970
  account: {
17968
17971
  type: "string",
17969
17972
  description: "Account selector: @handle or profile_id:<id>; overrides config default"
17970
17973
  }
17971
17974
  },
17972
17975
  run: async ({ args }) => {
17976
+ if (typeof args.body === "string") throw new UsageError("Unsupported option --body for x message.");
17973
17977
  const recipientsInput = args.recipients ?? args.target ?? (Array.isArray(args._) && args._.length > 0 ? args._[0] : void 0);
17974
- const hasBody = typeof args.body === "string";
17975
- let textInput;
17976
- if (deps.isStdinTTY()) {
17977
- if (!hasBody) throw noBodyTextError("social x message <recipients>");
17978
- } else {
17979
- const piped = trimTrailingNewline(await deps.readStdin());
17980
- if (piped.length > 0) textInput = piped;
17981
- else if (!hasBody) throw noBodyTextError("social x message <recipients>");
17982
- }
17978
+ let body;
17979
+ if (deps.isStdinTTY()) throw noBodyTextError("social x message <recipients>");
17980
+ const piped = trimTrailingNewline(await deps.readStdin());
17981
+ if (piped.length > 0) body = parseStdinBody(deps, "text", "text", piped);
17982
+ else throw noBodyTextError("social x message <recipients>");
17983
17983
  if (recipientsInput === void 0) throw new UsageError("Pass a conversation or user target.");
17984
17984
  const recipients = String(recipientsInput).split(",").map((recipient) => recipient.trim()).filter((recipient) => recipient.length > 0);
17985
17985
  if (recipients.length === 0) throw new UsageError("Pass at least one recipient.");
17986
- const bodyFromJSON = typeof args.body === "string" ? deps.parseJSONObject("--body", args.body) : void 0;
17987
- const text = textInput === void 0 ? void 0 : deps.parseBodyText("--text", textInput);
17988
- const body = text === void 0 ? bodyFromJSON : {
17989
- ...bodyFromJSON ?? {},
17990
- text
17991
- };
17992
- if (body === void 0) throw new UsageError("Pass <text> or --body.");
17986
+ if (body === void 0) throw noBodyTextError("social x message <recipients>");
17993
17987
  const resolved = [];
17994
17988
  const account = typeof args.account === "string" ? args.account : void 0;
17995
17989
  const selectedAccount = await deps.resolveAccount({
@@ -18018,12 +18012,13 @@ const buildMessage = (deps) => {
18018
18012
  })));
18019
18013
  for (const resolution of participantResolutions) resolved.push(resolution.record);
18020
18014
  const participant_ids = participantResolutions.map((resolution) => resolution.id);
18015
+ const { text, ...bodyFields } = body;
18021
18016
  requestPath = "x/dm_conversations";
18022
18017
  requestBody = {
18023
18018
  conversation_type: "Group",
18024
- ...bodyFromJSON ?? {},
18025
18019
  participant_ids,
18026
- ...text === void 0 ? {} : { message: { text } }
18020
+ ...bodyFields,
18021
+ ...typeof text === "string" ? { message: { text } } : {}
18027
18022
  };
18028
18023
  }
18029
18024
  const response = await deps.api.post(requestPath, {
@@ -21064,7 +21059,7 @@ const createInstance = (defaults) => {
21064
21059
  const ky = createInstance();
21065
21060
  //#endregion
21066
21061
  //#region package.json
21067
- var version$1 = "0.3.1";
21062
+ var version$1 = "0.3.3";
21068
21063
  //#endregion
21069
21064
  //#region src/lib/env.ts
21070
21065
  const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
@@ -27417,6 +27412,23 @@ const accessPhase = async (ctx) => {
27417
27412
  };
27418
27413
  };
27419
27414
  //#endregion
27415
+ //#region ../../packages/lib/src/cli-schema-contract.ts
27416
+ const isZodSchema = (value) => Boolean(value && typeof value === "object" && "safeParse" in value && typeof value.safeParse === "function");
27417
+ const isZodUnknownSchema = (value) => value instanceof ZodUnknown;
27418
+ const zodToJSONSchema = (schema, options) => toJSONSchema(schema, { io: options?.io ?? "output" });
27419
+ const unknownOutputContract = (message = "Output schema is not declared.") => ({
27420
+ outputSchema: { unknown: true },
27421
+ hazards: [{
27422
+ code: "output_schema_unknown",
27423
+ message
27424
+ }]
27425
+ });
27426
+ new TextEncoder();
27427
+ //#endregion
27428
+ //#region ../../packages/lib/src/phone.ts
27429
+ const phoneFormattingRegex = /[\s().-]/g;
27430
+ const PhoneNumber = string().trim().transform((value) => value.replace(phoneFormattingRegex, "")).pipe(string().regex(/^\+[1-9]\d{1,14}$/, { message: "Enter a phone number with +country code." }));
27431
+ //#endregion
27420
27432
  //#region src/login/phases/0.sign-in.ts
27421
27433
  const CLIENT_ID = "social-cli";
27422
27434
  const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
@@ -27501,7 +27513,30 @@ const sendMagicLinkSignIn = async (email, callbackURL, deps = {}) => {
27501
27513
  throw new Error(authErrorMessage("Could not send sign-in email", response, error));
27502
27514
  }
27503
27515
  };
27504
- const waitForDeviceToken = async (ctx, deviceCode) => {
27516
+ const startMagicLinkSignIn = (email, callbackURL) => sendMagicLinkSignIn(email, callbackURL).then(() => ({ status: "fulfilled" }), (error) => ({
27517
+ status: "rejected",
27518
+ error
27519
+ }));
27520
+ const waitForMagicLinkEmail = async (ctx, emailPromise) => {
27521
+ const result = await emailPromise;
27522
+ if (result.status === "rejected") {
27523
+ ctx.ui.error("Could not send sign-in email");
27524
+ throw result.error;
27525
+ }
27526
+ };
27527
+ const isMissingPhoneEndpointError = (error) => isRecord$2(error) && "status" in error && error.status === 404 && "message" in error && typeof error.message === "string" && error.message.toLowerCase().includes("not found");
27528
+ const updatePhoneNumber = async (ctx, phoneNumber) => {
27529
+ try {
27530
+ await ctx.client.cli.user.updatePhone({ phoneNumber });
27531
+ } catch (error) {
27532
+ if (isMissingPhoneEndpointError(error)) {
27533
+ ctx.ui.warn("Phone number could not be saved yet; login is complete.");
27534
+ return;
27535
+ }
27536
+ throw error;
27537
+ }
27538
+ };
27539
+ const waitForDeviceToken = async (ctx, deviceCode, phoneNumber) => {
27505
27540
  const spinner = ctx.ui.spinner();
27506
27541
  const message = "Waiting for magic link approval";
27507
27542
  spinner.start(message);
@@ -27515,6 +27550,7 @@ const waitForDeviceToken = async (ctx, deviceCode) => {
27515
27550
  expiresAt: tokenExpiresAt,
27516
27551
  scope: token.scope
27517
27552
  });
27553
+ await updatePhoneNumber(ctx, phoneNumber);
27518
27554
  clearTick();
27519
27555
  spinner.stop(LOGIN_SUCCESS_MESSAGE);
27520
27556
  ctx.ui.info(NEXT_COMMANDS_MESSAGE);
@@ -27536,14 +27572,17 @@ const signInPhase = async (ctx) => {
27536
27572
  const expiresAt = new Date(Date.now() + deviceCode.expires_in * 1e3).toISOString();
27537
27573
  const callbackURL = deviceCallbackURLFor(deviceCode, email);
27538
27574
  const userCode = formatUserCode(deviceCode.user_code);
27539
- await ctx.ui.spin("Sending sign-in email", () => sendMagicLinkSignIn(email, callbackURL), "Magic link sent");
27575
+ const emailPromise = startMagicLinkSignIn(email, callbackURL);
27576
+ const phoneNumber = await resolvePhoneNumber(ctx);
27577
+ await waitForMagicLinkEmail(ctx, emailPromise);
27540
27578
  ctx.ui.note(`Code: ${userCode}\nEmail: ${email}\n\nClick the magic link in your inbox to approve this CLI session.\n\n${onboardingLegalNotice()}`, "Check your email");
27541
27579
  return {
27542
27580
  status: "done",
27543
27581
  data: {
27544
27582
  email,
27545
27583
  expiresAt,
27546
- scope: (await waitForDeviceToken(ctx, deviceCode)).scope
27584
+ phoneNumber,
27585
+ scope: (await waitForDeviceToken(ctx, deviceCode, phoneNumber)).scope
27547
27586
  }
27548
27587
  };
27549
27588
  };
@@ -27553,6 +27592,17 @@ const resolveEmail = async (ctx) => await ctx.ui.text({
27553
27592
  placeholder: "you@example.com",
27554
27593
  validate: (value) => value?.includes("@") ? void 0 : "Enter an email address."
27555
27594
  });
27595
+ const resolvePhoneNumber = async (ctx) => {
27596
+ const phoneNumber = await ctx.ui.text({
27597
+ message: "Phone number",
27598
+ placeholder: "+1 555 123 4567",
27599
+ validate: (value) => {
27600
+ const result = PhoneNumber.safeParse(value);
27601
+ return result.success ? void 0 : result.error.issues[0]?.message;
27602
+ }
27603
+ });
27604
+ return PhoneNumber.parse(phoneNumber);
27605
+ };
27556
27606
  //#endregion
27557
27607
  //#region src/login/phases/1.scope.ts
27558
27608
  const DEFAULT_SCOPE_ALIAS = "read,write";
@@ -27697,7 +27747,8 @@ const ScopeAlias = _enum(["read", "read,write"]);
27697
27747
  const LoginUserOutput = object({
27698
27748
  id: string(),
27699
27749
  email: string().nullable(),
27700
- name: string().nullable()
27750
+ name: string().nullable(),
27751
+ phoneNumber: string().nullable()
27701
27752
  });
27702
27753
  const LoginSeatsOutput = object({
27703
27754
  total: number().int().nonnegative(),
@@ -28117,7 +28168,8 @@ const AccountSessionOutput = object({
28117
28168
  user: object({
28118
28169
  id: string(),
28119
28170
  email: string().nullable(),
28120
- name: string().nullable()
28171
+ name: string().nullable(),
28172
+ phoneNumber: string().nullable()
28121
28173
  }),
28122
28174
  scope: _enum(["read", "read,write"]),
28123
28175
  capabilities: array(string())
@@ -28597,18 +28649,6 @@ const createCLIDeps = () => {
28597
28649
  };
28598
28650
  };
28599
28651
  //#endregion
28600
- //#region ../../packages/lib/src/cli-schema-contract.ts
28601
- const isZodSchema = (value) => Boolean(value && typeof value === "object" && "safeParse" in value && typeof value.safeParse === "function");
28602
- const isZodUnknownSchema = (value) => value instanceof ZodUnknown;
28603
- const zodToJSONSchema = (schema, options) => toJSONSchema(schema, { io: options?.io ?? "output" });
28604
- const unknownOutputContract = (message = "Output schema is not declared.") => ({
28605
- outputSchema: { unknown: true },
28606
- hazards: [{
28607
- code: "output_schema_unknown",
28608
- message
28609
- }]
28610
- });
28611
- //#endregion
28612
28652
  //#region src/schema/index.ts
28613
28653
  const HTTPMethod = _enum([
28614
28654
  "DELETE",
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "access": "public"
12
12
  },
13
13
  "private": false,
14
- "version": "0.3.1",
14
+ "version": "0.3.3",
15
15
  "type": "module",
16
16
  "engines": {
17
17
  "node": ">=22.5.0"