@curviate/cli 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +190 -116
- package/README.md +18 -18
- package/dist/{account-A3FOYUKC.js → account-QRGWUQXF.js} +25 -25
- package/dist/{chunk-EEMJDJ4W.js → chunk-H6XD3F66.js} +7 -0
- package/dist/{chunk-LMVDURUZ.js → chunk-JA3NNST6.js} +35 -4
- package/dist/{chunk-M6UDWFHX.js → chunk-PMRQXBCP.js} +1 -1
- package/dist/{chunk-ROULHEFW.js → chunk-RFUO2G3M.js} +4 -3
- package/dist/{chunk-Q7XG2VIF.js → chunk-T5LPGAGJ.js} +6 -6
- package/dist/cli.js +58 -26
- package/dist/{comment-RPLZHFJQ.js → comment-MCGXFMN4.js} +3 -3
- package/dist/{company-DFJK2RHM.js → company-WDDLWP7E.js} +12 -12
- package/dist/{connect-ELSSMOIC.js → connect-5OROSY5C.js} +12 -12
- package/dist/{exit-codes-63JE5GM5.js → exit-codes-KAVZN5BQ.js} +1 -1
- package/dist/{feed-23Z7OQNL.js → feed-SFVYEAYP.js} +3 -3
- package/dist/{group-52MP24W3.js → group-HSZY2IJU.js} +3 -3
- package/dist/{inbox-G6KNWWLI.js → inbox-L6JBGZIB.js} +4 -4
- package/dist/{inboxes-NTTYCQPM.js → inboxes-UIO74C5Q.js} +5 -5
- package/dist/{job-WOPIAHOR.js → job-75ML7R7I.js} +4 -4
- package/dist/{message-KJXY5RCD.js → message-DAQHRBSJ.js} +2 -2
- package/dist/{notification-MNEABNVW.js → notification-DYJN4ZHN.js} +3 -3
- package/dist/{post-A7YZWBFG.js → post-RGDOCGC4.js} +5 -5
- package/dist/{profile-XOMDIYSY.js → profile-TJS2YAZD.js} +8 -8
- package/dist/{recruiter-CCLG4PM4.js → recruiter-NSM33IEK.js} +10 -10
- package/dist/{sales-nav-5ZRE2UAQ.js → sales-nav-5O7FR57S.js} +6 -6
- package/dist/{search-NDESJC3Y.js → search-TDSLK2MB.js} +13 -13
- package/dist/{webhook-TNZF3FMN.js → webhook-7ESDSIRU.js} +9 -9
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -23,20 +23,20 @@ Requires Node.js 18 or later.
|
|
|
23
23
|
|
|
24
24
|
## Authentication
|
|
25
25
|
|
|
26
|
-
**Option 1
|
|
26
|
+
**Option 1: interactive login** (stores a profile in `~/.config/curviate/`):
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
curviate login
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
**Option 2
|
|
32
|
+
**Option 2: environment variable** (preferred in CI and agent loops):
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
export CURVIATE_API_KEY=<your-api-key>
|
|
36
36
|
curviate account list
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
**Option 3
|
|
39
|
+
**Option 3: per-command flag**:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
curviate --api-key <your-api-key> account list
|
|
@@ -80,14 +80,14 @@ These examples show how coding agents compose the CLI in real workflows.
|
|
|
80
80
|
Search for matching profiles, preview the invitations, then send them once satisfied:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
# Look first
|
|
83
|
+
# Look first: a search is a read, so it just runs (no --preview on reads)
|
|
84
84
|
curviate search people \
|
|
85
85
|
--keywords "AI engineer" \
|
|
86
86
|
--location "Berlin" \
|
|
87
87
|
--limit 10 \
|
|
88
88
|
--json
|
|
89
89
|
|
|
90
|
-
# Preview a single write before sending, then pipe IDs into connect
|
|
90
|
+
# Preview a single write before sending, then pipe IDs into connect, one request per person
|
|
91
91
|
curviate connect "$SOME_ID" --note "Hi, I'd love to connect." --preview
|
|
92
92
|
|
|
93
93
|
curviate search people --keywords "AI engineer" --location "Berlin" --all \
|
|
@@ -107,7 +107,7 @@ curviate inbox list --json --all \
|
|
|
107
107
|
|
|
108
108
|
### 3. Warm up a prospect by reacting to their recent posts
|
|
109
109
|
|
|
110
|
-
Read recent posts from a profile, then react to each
|
|
110
|
+
Read recent posts from a profile, then react to each, which is useful for ambient warm-up before outreach:
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
113
|
PROFILE_URL="https://www.linkedin.com/in/example"
|
|
@@ -126,7 +126,7 @@ curviate sales-nav search people --keywords "VP Engineering" --json \
|
|
|
126
126
|
|| {
|
|
127
127
|
code=$?
|
|
128
128
|
if [ "$code" -eq 5 ]; then
|
|
129
|
-
echo "Sales Navigator add-on required
|
|
129
|
+
echo "Sales Navigator add-on required. Upgrade at https://docs.curviate.com"
|
|
130
130
|
else
|
|
131
131
|
echo "Search failed with exit code $code"
|
|
132
132
|
exit "$code"
|
|
@@ -136,7 +136,7 @@ curviate sales-nav search people --keywords "VP Engineering" --json \
|
|
|
136
136
|
|
|
137
137
|
### 5. Verify an inbound webhook signature offline
|
|
138
138
|
|
|
139
|
-
Validate a webhook payload before processing it
|
|
139
|
+
Validate a webhook payload before processing it, with no network call:
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
# Pipe the raw request body from stdin; pass the signature header and secret as flags
|
|
@@ -162,7 +162,7 @@ curviate account list --all --json \
|
|
|
162
162
|
|
|
163
163
|
### 7. Search jobs, then fetch full detail on the top result
|
|
164
164
|
|
|
165
|
-
`job get` accepts either a job URL or the bare numeric id
|
|
165
|
+
`job get` accepts either a job URL or the bare numeric id, including the `job_urn` field a
|
|
166
166
|
job-search result already returns:
|
|
167
167
|
|
|
168
168
|
```bash
|
|
@@ -178,7 +178,7 @@ curviate job get "https://www.linkedin.com/jobs/view/4428113858" --account acc_1
|
|
|
178
178
|
|
|
179
179
|
Company commands (`curviate company ...`) are Core-tier reads. `company <id>` accepts a public
|
|
180
180
|
handle (the slug in `linkedin.com/company/<handle>`) or a numeric id; the four sub-resource
|
|
181
|
-
commands require the company's **numeric provider id
|
|
181
|
+
commands require the company's **numeric provider id**, the `id` field `company <id>` returns.
|
|
182
182
|
`--account` (or a configured default account) is required on all of them.
|
|
183
183
|
|
|
184
184
|
### 1. Retrieve a company, then list its employees
|
|
@@ -199,7 +199,7 @@ curviate company jobs 112013061 --all --account acc_1 --json # streams every p
|
|
|
199
199
|
|
|
200
200
|
Sales Navigator commands (`curviate sales-nav ...`) require an account with the Sales Navigator
|
|
201
201
|
add-on tier attached. A call against an account without it fails with **exit code `5`** and a
|
|
202
|
-
`TIER_NOT_ACTIVE` error body naming the required tier (`sales_nav`)
|
|
202
|
+
`TIER_NOT_ACTIVE` error body naming the required tier (`sales_nav`); branch on the exit code the
|
|
203
203
|
same way as example 4 above. Write commands (`save-lead`, `save-account`, `message new`) accept
|
|
204
204
|
`--preview` to render the request without sending it.
|
|
205
205
|
|
|
@@ -216,7 +216,7 @@ curviate sales-nav search people \
|
|
|
216
216
|
|
|
217
217
|
### 2. Save a lead to a specific lead list
|
|
218
218
|
|
|
219
|
-
Preview first, then send. `--list` is required
|
|
219
|
+
Preview first, then send. `--list` is required; the save always targets a specific list.
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
222
|
curviate sales-nav save-lead ACwAAA1234567 \
|
|
@@ -233,7 +233,7 @@ curviate sales-nav save-lead ACwAAA1234567 --account acc_1 --list 987654
|
|
|
233
233
|
curviate sales-nav message new \
|
|
234
234
|
--to ACwAAA1234567 \
|
|
235
235
|
--account acc_1 \
|
|
236
|
-
"Hi
|
|
236
|
+
"Hi, I'd love to connect about an opportunity at our company."
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
### 4. Search Sales Navigator companies
|
|
@@ -321,7 +321,7 @@ the v2 job body. `recruiter job publish` is project-scoped and requires `--mode`
|
|
|
321
321
|
```bash
|
|
322
322
|
curviate recruiter job create \
|
|
323
323
|
--account acc_1 \
|
|
324
|
-
--project-name "Backend Hiring
|
|
324
|
+
--project-name "Backend Hiring 2026" \
|
|
325
325
|
--job-title "Senior Backend Engineer" \
|
|
326
326
|
--description "Remote-first team building the core platform." \
|
|
327
327
|
--employment-status FULL_TIME \
|
|
@@ -380,14 +380,14 @@ curviate recruiter message new \
|
|
|
380
380
|
--to AEM789 \
|
|
381
381
|
--account acc_1 \
|
|
382
382
|
--subject "A role you'd be a great fit for" \
|
|
383
|
-
--signature "
|
|
384
|
-
"Hi
|
|
383
|
+
--signature "- Alex, Talent Team" \
|
|
384
|
+
"Hi, I came across your profile and think you'd be a great fit for a role we're hiring for."
|
|
385
385
|
```
|
|
386
386
|
|
|
387
387
|
### 9. List your postings, and get any public job posting through the Recruiter lens
|
|
388
388
|
|
|
389
389
|
Unlike `recruiter jobs` (which lists postings you manage), `recruiter job get` retrieves the full
|
|
390
|
-
detail of *any* public LinkedIn job posting
|
|
390
|
+
detail of *any* public LinkedIn job posting, the Recruiter-seated counterpart to the top-level
|
|
391
391
|
`job get` command:
|
|
392
392
|
|
|
393
393
|
```bash
|
|
@@ -410,4 +410,4 @@ curviate recruiter job get "https://www.linkedin.com/jobs/view/4428113858" --acc
|
|
|
410
410
|
|
|
411
411
|
## License
|
|
412
412
|
|
|
413
|
-
MIT
|
|
413
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
} from "./chunk-H4KV7MIN.js";
|
|
5
5
|
import {
|
|
6
6
|
AUTH_NEEDED
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PMRQXBCP.js";
|
|
8
8
|
import {
|
|
9
9
|
slimAccountGet,
|
|
10
10
|
slimAccountList,
|
|
11
11
|
slimAccountListItem
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RFUO2G3M.js";
|
|
13
13
|
import {
|
|
14
14
|
pageDelayFromFlags,
|
|
15
15
|
streamAll
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-H6XD3F66.js";
|
|
17
17
|
import {
|
|
18
18
|
buildPreviewOutput
|
|
19
19
|
} from "./chunk-R3VLWLVV.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
renderSuccess,
|
|
24
24
|
renderUnexpectedError,
|
|
25
25
|
resolveEffectiveConfig
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-JA3NNST6.js";
|
|
27
27
|
import {
|
|
28
28
|
GLOBAL_FLAGS,
|
|
29
29
|
READ_SINGLE_FLAGS,
|
|
@@ -221,7 +221,7 @@ function resolveOutputOpts(flags) {
|
|
|
221
221
|
async function handleError(err, outOpts, out) {
|
|
222
222
|
const { CurviateError } = await import("@curviate/sdk");
|
|
223
223
|
if (err instanceof CurviateError) {
|
|
224
|
-
const { getExitCode } = await import("./exit-codes-
|
|
224
|
+
const { getExitCode } = await import("./exit-codes-KAVZN5BQ.js");
|
|
225
225
|
renderError(err, outOpts, out);
|
|
226
226
|
process.exit(getExitCode(err.code));
|
|
227
227
|
}
|
|
@@ -284,13 +284,13 @@ async function buildAuthBody(flags, ctx) {
|
|
|
284
284
|
readSingleLine: ctx.readSingleLine,
|
|
285
285
|
required: true,
|
|
286
286
|
// The masked-prompt/fail-fast tiers (3/4) only engage once --email is
|
|
287
|
-
// present (nothing meaningful to prompt toward yet otherwise)
|
|
287
|
+
// present (nothing meaningful to prompt toward yet otherwise), and
|
|
288
288
|
// never under --preview (a client-side render must not prompt or
|
|
289
289
|
// exit).
|
|
290
290
|
allowInteractive: !ctx.previewMode && Boolean(flags.email),
|
|
291
291
|
// Tier-1b's own gate is preview-only, deliberately NOT email-gated: a
|
|
292
292
|
// user who passed --password-stdin on a TTY gets the read regardless
|
|
293
|
-
// of --email
|
|
293
|
+
// of --email, an email-less body still fails downstream validation,
|
|
294
294
|
// as expected, rather than the flag being silently ignored.
|
|
295
295
|
allowInteractiveStdinRead: !ctx.previewMode,
|
|
296
296
|
failMessage: "no password. Pass --password, --password-stdin, or set CURVIATE_LINKEDIN_PASSWORD",
|
|
@@ -357,11 +357,11 @@ function resolveCredentialIO(io) {
|
|
|
357
357
|
isOutputTTY: io.isOutputTTY ?? (process.stdout.isTTY ?? false),
|
|
358
358
|
readline: io.readline ?? readlineSync,
|
|
359
359
|
readStdin: io.readStdin ?? defaultReadStdin,
|
|
360
|
-
// Always the masked, no-echo raw-mode branch
|
|
360
|
+
// Always the masked, no-echo raw-mode branch, never a bare readlineSync
|
|
361
361
|
// pass-through, which would default mask to false and echo the secret.
|
|
362
362
|
// Prompt is deliberately EMPTY, not `cue`: credential-resolve.ts already
|
|
363
363
|
// wrote STDIN_TTY_CUE to out.stderr before calling this reader, and
|
|
364
|
-
// readlineSync itself writes its `prompt` argument to stderr too
|
|
364
|
+
// readlineSync itself writes its `prompt` argument to stderr too, the
|
|
365
365
|
// cue text would otherwise print twice on a real terminal.
|
|
366
366
|
readSingleLine: io.readSingleLine ?? (() => readlineSync("", { mask: true })),
|
|
367
367
|
sleep: io.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))),
|
|
@@ -504,8 +504,8 @@ async function runAccountLink(client, flags, out, io = {}) {
|
|
|
504
504
|
const body = {
|
|
505
505
|
seat_id: flags["seat-id"],
|
|
506
506
|
...authBody,
|
|
507
|
-
// --account-id (0.15.0): present
|
|
508
|
-
// place (reconnect); omit
|
|
507
|
+
// --account-id (0.15.0): present -> re-authenticate this existing account in
|
|
508
|
+
// place (reconnect); omit -> connect a new account, with NO account_id key in
|
|
509
509
|
// the body at all (not undefined, not empty).
|
|
510
510
|
...flags["account-id"] ? { account_id: flags["account-id"] } : {}
|
|
511
511
|
};
|
|
@@ -548,7 +548,7 @@ async function runConnectSessionWaitLoop(client, sessionId, ctx) {
|
|
|
548
548
|
const n = ctx.now();
|
|
549
549
|
if (n >= timeoutAt) return { kind: "timeout", result };
|
|
550
550
|
if (ctx.showTicker) {
|
|
551
|
-
ctx.out.stderr.write(`\rWaiting for the account to connect
|
|
551
|
+
ctx.out.stderr.write(`\rWaiting for the account to connect... ${formatRemaining(timeoutAt - n)} remaining`);
|
|
552
552
|
}
|
|
553
553
|
await ctx.sleep(nextCheckpointPollDelayMs(n - startedAt));
|
|
554
554
|
}
|
|
@@ -570,7 +570,7 @@ function resolveWaitTimeoutOverrideMs(flags, out) {
|
|
|
570
570
|
}
|
|
571
571
|
async function runAccountConnectSessionPoll(client, flags, out, io = {}) {
|
|
572
572
|
if (!flags.session) {
|
|
573
|
-
out.stderr.write("error: --session is required (the account's acc_
|
|
573
|
+
out.stderr.write("error: --session is required (the account's acc_... id from `account link` or a checkpoint response).\n");
|
|
574
574
|
process.exit(2);
|
|
575
575
|
}
|
|
576
576
|
const sessionId = flags.session;
|
|
@@ -783,7 +783,7 @@ async function runCheckpointPollWaitLoop(client, accountId, ctx) {
|
|
|
783
783
|
const n = ctx.now();
|
|
784
784
|
if (n >= timeoutAt) return { kind: "timeout", result };
|
|
785
785
|
if (ctx.showTicker) {
|
|
786
|
-
ctx.out.stderr.write(`\rWaiting for approval
|
|
786
|
+
ctx.out.stderr.write(`\rWaiting for approval... ${formatRemaining(timeoutAt - n)} remaining`);
|
|
787
787
|
}
|
|
788
788
|
await ctx.sleep(nextCheckpointPollDelayMs(n - startedAt));
|
|
789
789
|
}
|
|
@@ -881,7 +881,7 @@ var accountGetCommand = defineCommand({
|
|
|
881
881
|
args: {
|
|
882
882
|
// Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields
|
|
883
883
|
...READ_SINGLE_FLAGS,
|
|
884
|
-
"account-id": { type: "positional", description: "Account id (acc_
|
|
884
|
+
"account-id": { type: "positional", description: "Account id (acc_...)." }
|
|
885
885
|
},
|
|
886
886
|
async run({ args }) {
|
|
887
887
|
const flags = args;
|
|
@@ -924,7 +924,7 @@ var accountLinkCommand = defineCommand({
|
|
|
924
924
|
"proxy-password": { type: "string", description: `Proxy auth password. ${OPTIONAL_SECRET_WARNING("CURVIATE_PROXY_PASSWORD")}` },
|
|
925
925
|
"user-agent": { type: "string", description: "Browser User-Agent to pin for this account." },
|
|
926
926
|
"recruiter-contract-id": { type: "string", description: "Recruiter contract to bind to (Recruiter tier only)." },
|
|
927
|
-
"account-id": { type: "string", description: "Existing account id (acc_
|
|
927
|
+
"account-id": { type: "string", description: "Existing account id (acc_...) to re-authenticate IN PLACE. Passing it makes this an in-place reconnect of that account. Omit to connect a NEW account into --seat-id." },
|
|
928
928
|
"no-interactive": {
|
|
929
929
|
type: "boolean",
|
|
930
930
|
description: "Never prompt for a checkpoint code. On a checkpoint, always render the envelope and exit 12, even on a TTY.",
|
|
@@ -957,7 +957,7 @@ var accountConnectSessionPollCommand = defineCommand({
|
|
|
957
957
|
...WRITE_SINGLE_FLAGS,
|
|
958
958
|
session: {
|
|
959
959
|
type: "string",
|
|
960
|
-
description: "The account's acc_
|
|
960
|
+
description: "The account's acc_... id, returned by `account link` or a checkpoint response.",
|
|
961
961
|
required: true
|
|
962
962
|
},
|
|
963
963
|
wait: {
|
|
@@ -966,7 +966,7 @@ var accountConnectSessionPollCommand = defineCommand({
|
|
|
966
966
|
default: false
|
|
967
967
|
},
|
|
968
968
|
// Override WRITE_SINGLE_FLAGS.timeout: on this command --timeout is the
|
|
969
|
-
// --wait loop's own wall-clock bound in MILLISECONDS (requires --wait)
|
|
969
|
+
// --wait loop's own wall-clock bound in MILLISECONDS (requires --wait),
|
|
970
970
|
// NOT the SDK request timeout. Default: the time remaining to the
|
|
971
971
|
// session's own expiry.
|
|
972
972
|
timeout: {
|
|
@@ -1003,8 +1003,8 @@ var accountUpdateCommand = defineCommand({
|
|
|
1003
1003
|
meta: { name: "update", description: "Update an account's metadata and/or custom-proxy configuration." },
|
|
1004
1004
|
args: {
|
|
1005
1005
|
...WRITE_SINGLE_FLAGS,
|
|
1006
|
-
"account-id": { type: "positional", description: "Account id (acc_
|
|
1007
|
-
metadata: { type: "string", description: `Custom metadata as a JSON object (flat string
|
|
1006
|
+
"account-id": { type: "positional", description: "Account id (acc_...)." },
|
|
1007
|
+
metadata: { type: "string", description: `Custom metadata as a JSON object (flat string->string map). Replaces the store wholesale. Example: '{"team":"growth"}'.` },
|
|
1008
1008
|
"clear-proxy": { type: "boolean", description: "Clear the custom proxy (revert to automatic proxy protection). Mutually exclusive with --proxy-host.", default: false },
|
|
1009
1009
|
"proxy-protocol": { type: "string", description: "Proxy protocol: http | https | socks5." },
|
|
1010
1010
|
"proxy-host": { type: "string", description: "Proxy host or IP." },
|
|
@@ -1033,7 +1033,7 @@ var accountDisconnectCommand = defineCommand({
|
|
|
1033
1033
|
meta: { name: "disconnect", description: "Hard-disconnect a LinkedIn account and release its seat." },
|
|
1034
1034
|
args: {
|
|
1035
1035
|
...WRITE_SINGLE_FLAGS,
|
|
1036
|
-
"account-id": { type: "positional", description: "Account id (acc_
|
|
1036
|
+
"account-id": { type: "positional", description: "Account id (acc_...)." }
|
|
1037
1037
|
},
|
|
1038
1038
|
async run({ args }) {
|
|
1039
1039
|
const flags = args;
|
|
@@ -1058,7 +1058,7 @@ var accountCheckpointSolveCommand = defineCommand({
|
|
|
1058
1058
|
...WRITE_SINGLE_FLAGS,
|
|
1059
1059
|
"account-id": {
|
|
1060
1060
|
type: "positional",
|
|
1061
|
-
description: "The provisional account_id (acc_
|
|
1061
|
+
description: "The provisional account_id (acc_...) from the 202 checkpoint_required response."
|
|
1062
1062
|
},
|
|
1063
1063
|
code: {
|
|
1064
1064
|
type: "string",
|
|
@@ -1092,7 +1092,7 @@ var accountCheckpointPollCommand = defineCommand({
|
|
|
1092
1092
|
...WRITE_SINGLE_FLAGS,
|
|
1093
1093
|
"account-id": {
|
|
1094
1094
|
type: "positional",
|
|
1095
|
-
description: "The provisional account_id (acc_
|
|
1095
|
+
description: "The provisional account_id (acc_...) from the 202 checkpoint_required response."
|
|
1096
1096
|
},
|
|
1097
1097
|
wait: {
|
|
1098
1098
|
type: "boolean",
|
|
@@ -1100,7 +1100,7 @@ var accountCheckpointPollCommand = defineCommand({
|
|
|
1100
1100
|
default: false
|
|
1101
1101
|
},
|
|
1102
1102
|
// Override WRITE_SINGLE_FLAGS.timeout: on this command --timeout is the
|
|
1103
|
-
// --wait loop's own wall-clock bound in MILLISECONDS (requires --wait)
|
|
1103
|
+
// --wait loop's own wall-clock bound in MILLISECONDS (requires --wait),
|
|
1104
1104
|
// NOT the SDK request timeout, and NOT seconds like `inbox sync-chat
|
|
1105
1105
|
// --timeout`. Default: the time remaining to the checkpoint's own expiry.
|
|
1106
1106
|
timeout: {
|
|
@@ -1133,7 +1133,7 @@ var accountCheckpointRequestCommand = defineCommand({
|
|
|
1133
1133
|
...WRITE_SINGLE_FLAGS,
|
|
1134
1134
|
"account-id": {
|
|
1135
1135
|
type: "positional",
|
|
1136
|
-
description: "The provisional account_id (acc_
|
|
1136
|
+
description: "The provisional account_id (acc_...) from the 202 checkpoint_required response."
|
|
1137
1137
|
}
|
|
1138
1138
|
},
|
|
1139
1139
|
async run({ args }) {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
renderNotices
|
|
4
|
+
} from "./chunk-JA3NNST6.js";
|
|
2
5
|
|
|
3
6
|
// src/lib/paginate.ts
|
|
4
7
|
var PaginateError = class extends Error {
|
|
@@ -50,6 +53,10 @@ async function* streamAll(fn, params, opts = {}) {
|
|
|
50
53
|
if (opts.out) opts.out.stderr.write(ndjsonModeNotice());
|
|
51
54
|
firstPage = false;
|
|
52
55
|
}
|
|
56
|
+
if (opts.out) {
|
|
57
|
+
const pageNotices = renderNotices(page.notices);
|
|
58
|
+
if (pageNotices) opts.out.stderr.write(pageNotices + "\n");
|
|
59
|
+
}
|
|
53
60
|
if (Array.isArray(items)) {
|
|
54
61
|
for (const item of items) {
|
|
55
62
|
yield item;
|
|
@@ -95,6 +95,14 @@ function applyProjection(data, fields) {
|
|
|
95
95
|
}
|
|
96
96
|
return data;
|
|
97
97
|
}
|
|
98
|
+
function withPreservedNotices(original, rendered) {
|
|
99
|
+
const isPlain = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
100
|
+
if (!isPlain(original) || !isPlain(rendered)) return rendered;
|
|
101
|
+
const notices = original["notices"];
|
|
102
|
+
if (!Array.isArray(notices) || notices.length === 0) return rendered;
|
|
103
|
+
if (rendered["notices"] === notices) return rendered;
|
|
104
|
+
return { ...rendered, notices };
|
|
105
|
+
}
|
|
98
106
|
function firstProjectableItem(data) {
|
|
99
107
|
const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
100
108
|
if (Array.isArray(data)) {
|
|
@@ -131,23 +139,45 @@ function renderSuccess(data, opts, out) {
|
|
|
131
139
|
`
|
|
132
140
|
);
|
|
133
141
|
}
|
|
134
|
-
const projected = applyProjection(slimmed, fields);
|
|
142
|
+
const projected = withPreservedNotices(data, applyProjection(slimmed, fields));
|
|
135
143
|
if (json) {
|
|
136
144
|
out.stdout.write(JSON.stringify(projected) + "\n");
|
|
137
145
|
} else {
|
|
138
146
|
out.stdout.write(renderHuman(projected) + "\n");
|
|
139
147
|
}
|
|
140
148
|
}
|
|
149
|
+
function formatNotice(notice) {
|
|
150
|
+
if (typeof notice !== "object" || notice === null) return null;
|
|
151
|
+
const n = notice;
|
|
152
|
+
const code = typeof n["code"] === "string" ? n["code"] : "NOTICE";
|
|
153
|
+
const message = typeof n["message"] === "string" ? n["message"] : "";
|
|
154
|
+
let line = `notice [${code}]${message ? ` ${message}` : ""}`;
|
|
155
|
+
const details = [];
|
|
156
|
+
if (typeof n["field"] === "string") details.push(`field: ${n["field"]}`);
|
|
157
|
+
if (typeof n["value"] === "string") details.push(`value: ${n["value"]}`);
|
|
158
|
+
if (details.length > 0) line += ` (${details.join(", ")})`;
|
|
159
|
+
return line;
|
|
160
|
+
}
|
|
161
|
+
function renderNotices(notices) {
|
|
162
|
+
if (!Array.isArray(notices) || notices.length === 0) return null;
|
|
163
|
+
const lines = notices.map(formatNotice).filter((line) => line !== null);
|
|
164
|
+
return lines.length > 0 ? lines.join("\n") : null;
|
|
165
|
+
}
|
|
141
166
|
function renderHuman(data) {
|
|
142
167
|
if (data === null || data === void 0) return "(empty)";
|
|
143
168
|
if (typeof data === "object" && !Array.isArray(data)) {
|
|
144
169
|
const obj = data;
|
|
170
|
+
const notices = renderNotices(obj["notices"]);
|
|
145
171
|
if (Array.isArray(obj["items"])) {
|
|
146
172
|
const items = obj["items"];
|
|
147
|
-
|
|
148
|
-
return
|
|
173
|
+
const body = items.length === 0 ? "(no items)" : items.map(renderHuman).join("\n");
|
|
174
|
+
return notices ? `${notices}
|
|
175
|
+
${body}` : body;
|
|
149
176
|
}
|
|
150
|
-
|
|
177
|
+
const kv = Object.entries(obj).filter(([k]) => k !== "notices").map(([k, v]) => `${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`).join("\n");
|
|
178
|
+
if (!notices) return kv;
|
|
179
|
+
return kv ? `${notices}
|
|
180
|
+
${kv}` : notices;
|
|
151
181
|
}
|
|
152
182
|
if (Array.isArray(data)) {
|
|
153
183
|
return data.map(renderHuman).join("\n");
|
|
@@ -190,6 +220,7 @@ export {
|
|
|
190
220
|
resolveEffectiveConfig,
|
|
191
221
|
createClient,
|
|
192
222
|
renderSuccess,
|
|
223
|
+
renderNotices,
|
|
193
224
|
renderError,
|
|
194
225
|
renderUnexpectedError
|
|
195
226
|
};
|
|
@@ -41,7 +41,7 @@ function slimProfileMe(data) {
|
|
|
41
41
|
first_name: d["first_name"] ?? null,
|
|
42
42
|
last_name: d["last_name"] ?? null,
|
|
43
43
|
// LinkedIn serves the profile headline in the `description` wire field
|
|
44
|
-
// on reads, not a field named `headline
|
|
44
|
+
// on reads, not a field named `headline`, see JSDoc above.
|
|
45
45
|
headline: d["description"] ?? null,
|
|
46
46
|
public_identifier: d["public_identifier"] ?? null,
|
|
47
47
|
location: d["location"] ?? null,
|
|
@@ -59,7 +59,7 @@ function slimProfile(data) {
|
|
|
59
59
|
first_name: d["first_name"] ?? null,
|
|
60
60
|
last_name: d["last_name"] ?? null,
|
|
61
61
|
// LinkedIn serves the profile headline in the `description` wire field
|
|
62
|
-
// on reads, not a field named `headline
|
|
62
|
+
// on reads, not a field named `headline`, see slimProfileMe's JSDoc.
|
|
63
63
|
headline: d["description"] ?? null,
|
|
64
64
|
location: d["location"] ?? null,
|
|
65
65
|
network_distance: specifics?.["network_distance"] ?? null,
|
|
@@ -122,7 +122,8 @@ function slimSearchPeopleItem(item) {
|
|
|
122
122
|
full_name: item["full_name"] ?? null,
|
|
123
123
|
headline: item["headline"] ?? null,
|
|
124
124
|
location: item["location"] ?? null,
|
|
125
|
-
network_distance: item["network_distance"] ?? null
|
|
125
|
+
network_distance: item["network_distance"] ?? null,
|
|
126
|
+
visibility: item["visibility"] ?? null
|
|
126
127
|
};
|
|
127
128
|
}
|
|
128
129
|
function slimSearchPeople(data) {
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
renderSuccess,
|
|
26
26
|
renderUnexpectedError,
|
|
27
27
|
resolveEffectiveConfig
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-JA3NNST6.js";
|
|
29
29
|
import {
|
|
30
30
|
READ_SINGLE_FLAGS,
|
|
31
31
|
WRITE_FLAGS
|
|
@@ -77,7 +77,7 @@ var MEMBER_PROVIDER_ID_RE = /^A[CDE][A-Za-z0-9_-]{4,}$/;
|
|
|
77
77
|
async function handleSdkError(err, outOpts, out) {
|
|
78
78
|
const { CurviateError } = await import("@curviate/sdk");
|
|
79
79
|
if (err instanceof CurviateError) {
|
|
80
|
-
const { getExitCode } = await import("./exit-codes-
|
|
80
|
+
const { getExitCode } = await import("./exit-codes-KAVZN5BQ.js");
|
|
81
81
|
renderError(err, outOpts, out);
|
|
82
82
|
process.exit(getExitCode(err.code));
|
|
83
83
|
}
|
|
@@ -390,7 +390,7 @@ var messageNewCommand = defineCommand({
|
|
|
390
390
|
...WRITE_FLAGS,
|
|
391
391
|
to: {
|
|
392
392
|
type: "string",
|
|
393
|
-
description: "Recipient: LinkedIn profile URL (e.g. https://www.linkedin.com/in/some-slug), bare slug (e.g. some-slug), or provider ID (e.g. ACoAAA
|
|
393
|
+
description: "Recipient: LinkedIn profile URL (e.g. https://www.linkedin.com/in/some-slug), bare slug (e.g. some-slug), or provider ID (e.g. ACoAAA...). URL and slug inputs resolve the provider ID automatically.",
|
|
394
394
|
required: true
|
|
395
395
|
},
|
|
396
396
|
text: { type: "positional", stdinArg: true, description: "Opening message text. Pass - to read from stdin (e.g. via heredoc or pipe)." },
|
|
@@ -565,7 +565,7 @@ var messageInMailCommand = defineCommand({
|
|
|
565
565
|
...WRITE_FLAGS,
|
|
566
566
|
to: {
|
|
567
567
|
type: "string",
|
|
568
|
-
description: "Recipient: LinkedIn profile URL, bare slug, provider-id (ACoAAA
|
|
568
|
+
description: "Recipient: LinkedIn profile URL, bare slug, provider-id (ACoAAA...), or member URN (urn:li:member:<id>). URL and slug inputs resolve the provider ID automatically.",
|
|
569
569
|
required: true
|
|
570
570
|
},
|
|
571
571
|
subject: { type: "string", description: "InMail subject line.", required: true },
|
|
@@ -657,12 +657,12 @@ var MESSAGE_SUBCOMMANDS = [
|
|
|
657
657
|
"inmail",
|
|
658
658
|
"inmail-balance"
|
|
659
659
|
];
|
|
660
|
-
var MESSAGE_USAGE = 'Usage: curviate message new --to <attendee> "<text>" [--attach <file
|
|
660
|
+
var MESSAGE_USAGE = 'Usage: curviate message new --to <attendee> "<text>" [--attach <file>...]\n curviate message send <chat_id> "<text>" [--attach <file>...]\n curviate message <chat_id> "<text>" [--attach <file>...]\n curviate message get <chat_id> <message_id>\n curviate message edit <chat_id> <message_id> "<text>"\n curviate message delete <chat_id> <message_id>\n curviate message react <chat_id> <message_id> <emoji>\n curviate message attachment <chat_id> <message_id> <attachment_id> [-o <file>]\n curviate message inmail --to <id> --subject <s> "<text>"\n curviate message inmail-balance\n';
|
|
661
661
|
function guardBareMessageForm(chatId, out) {
|
|
662
662
|
if (!looksLikeCommandWord(chatId)) return;
|
|
663
663
|
const suggestion = nearestSubcommand(chatId, [...MESSAGE_SUBCOMMANDS]);
|
|
664
664
|
out.stderr.write(
|
|
665
|
-
`error: \`${chatId}\` is not a curviate message subcommand, and it is not a chat id (a chat id looks like 2
|
|
665
|
+
`error: \`${chatId}\` is not a curviate message subcommand, and it is not a chat id (a chat id looks like 2-... or COMPANY_..., or a linkedin.com/messaging/thread/... URL).
|
|
666
666
|
`
|
|
667
667
|
);
|
|
668
668
|
if (suggestion) {
|