@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 +12 -0
- package/README.md +9 -9
- package/dist/index.mjs +121 -81
- package/package.json +1 -1
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
|
|
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
|
|
87
|
-
|
|
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.
|
|
219
|
-
|
|
220
|
-
`~/.social/credentials.json` (mode `0600`)
|
|
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
|
|
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}
|
|
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
|
|
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}
|
|
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}
|
|
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}
|
|
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 =
|
|
13305
|
+
const bodyTextArg = op.config.bodyTextArg;
|
|
13296
13306
|
if (bodyTextArg === void 0) return;
|
|
13297
|
-
const
|
|
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
|
|
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>`
|
|
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
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
|
|
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
|
-
|
|
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 = (
|
|
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"
|
|
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}
|
|
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}
|
|
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 =
|
|
17395
|
+
const bodyTextArg = op.config.bodyTextArg;
|
|
17390
17396
|
if (bodyTextArg === void 0) return;
|
|
17391
|
-
const
|
|
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
|
|
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>`
|
|
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
|
-
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
|
|
17978
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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",
|