@curviate/cli 0.21.1 → 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.
Files changed (27) hide show
  1. package/CHANGELOG.md +171 -116
  2. package/README.md +18 -18
  3. package/dist/{account-OIV73PXX.js → account-QRGWUQXF.js} +25 -25
  4. package/dist/{chunk-HZFK45IW.js → chunk-H6XD3F66.js} +1 -1
  5. package/dist/{chunk-VPKMJYIF.js → chunk-JA3NNST6.js} +9 -1
  6. package/dist/{chunk-M6UDWFHX.js → chunk-PMRQXBCP.js} +1 -1
  7. package/dist/{chunk-WYRYCTUM.js → chunk-RFUO2G3M.js} +2 -2
  8. package/dist/{chunk-OE7OMTCM.js → chunk-T5LPGAGJ.js} +6 -6
  9. package/dist/cli.js +58 -26
  10. package/dist/{comment-W4XTOTHN.js → comment-MCGXFMN4.js} +3 -3
  11. package/dist/{company-NT4VO6JV.js → company-WDDLWP7E.js} +12 -12
  12. package/dist/{connect-VXWRLGQZ.js → connect-5OROSY5C.js} +12 -12
  13. package/dist/{exit-codes-63JE5GM5.js → exit-codes-KAVZN5BQ.js} +1 -1
  14. package/dist/{feed-4Q3B56WI.js → feed-SFVYEAYP.js} +3 -3
  15. package/dist/{group-43PHTFP7.js → group-HSZY2IJU.js} +3 -3
  16. package/dist/{inbox-GZAOUWZP.js → inbox-L6JBGZIB.js} +4 -4
  17. package/dist/{inboxes-6AZOB43U.js → inboxes-UIO74C5Q.js} +5 -5
  18. package/dist/{job-VOVQZGCK.js → job-75ML7R7I.js} +4 -4
  19. package/dist/{message-FIEWH2BU.js → message-DAQHRBSJ.js} +2 -2
  20. package/dist/{notification-65V67NWK.js → notification-DYJN4ZHN.js} +3 -3
  21. package/dist/{post-G4IMWUE2.js → post-RGDOCGC4.js} +5 -5
  22. package/dist/{profile-GADE33FY.js → profile-TJS2YAZD.js} +8 -8
  23. package/dist/{recruiter-LI2TMRHY.js → recruiter-NSM33IEK.js} +10 -10
  24. package/dist/{sales-nav-OXLRN6YK.js → sales-nav-5O7FR57S.js} +6 -6
  25. package/dist/{search-V35MFTXL.js → search-TDSLK2MB.js} +13 -13
  26. package/dist/{webhook-5HXMJQFO.js → webhook-7ESDSIRU.js} +9 -9
  27. 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 interactive login** (stores a profile in `~/.config/curviate/`):
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 environment variable** (preferred in CI and agent loops):
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 per-command flag**:
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 a search is a read, so it just runs (no --preview on reads)
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 one request per person
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 useful for ambient warm-up before outreach:
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 upgrade at https://docs.curviate.com"
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 works without a network call:
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 including the `job_urn` field a
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** the `id` field `company <id>` returns.
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`) branch on the exit code the
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 the save always targets a specific list.
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 I'd love to connect about an opportunity at our company."
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 2026" \
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 " 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."
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 the Recruiter-seated counterpart to the top-level
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 see [LICENSE](LICENSE).
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-M6UDWFHX.js";
7
+ } from "./chunk-PMRQXBCP.js";
8
8
  import {
9
9
  slimAccountGet,
10
10
  slimAccountList,
11
11
  slimAccountListItem
12
- } from "./chunk-WYRYCTUM.js";
12
+ } from "./chunk-RFUO2G3M.js";
13
13
  import {
14
14
  pageDelayFromFlags,
15
15
  streamAll
16
- } from "./chunk-HZFK45IW.js";
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-VPKMJYIF.js";
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-63JE5GM5.js");
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) and
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 an email-less body still fails downstream validation,
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 never a bare readlineSync
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 the
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 re-authenticate this existing account in
508
- // place (reconnect); omit connect a new account, with NO account_id key in
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\u2026 ${formatRemaining(timeoutAt - n)} remaining`);
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_\u2026 id from `account link` or a checkpoint response).\n");
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\u2026 ${formatRemaining(timeoutAt - n)} remaining`);
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_\u2026)." }
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_\u2026) 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." },
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_\u2026 id, returned by `account link` or a checkpoint response.",
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_\u2026)." },
1007
- metadata: { type: "string", description: `Custom metadata as a JSON object (flat string\u2192string map). Replaces the store wholesale. Example: '{"team":"growth"}'.` },
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_\u2026)." }
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_\u2026) from the 202 checkpoint_required response."
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_\u2026) from the 202 checkpoint_required response."
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_\u2026) from the 202 checkpoint_required response."
1136
+ description: "The provisional account_id (acc_...) from the 202 checkpoint_required response."
1137
1137
  }
1138
1138
  },
1139
1139
  async run({ args }) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  renderNotices
4
- } from "./chunk-VPKMJYIF.js";
4
+ } from "./chunk-JA3NNST6.js";
5
5
 
6
6
  // src/lib/paginate.ts
7
7
  var PaginateError = class extends Error {
@@ -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,7 +139,7 @@ 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 {
@@ -51,7 +51,7 @@ var EXIT_CODE_MAP = {
51
51
  PAYMENT_FAILED: 11,
52
52
  SUBSCRIPTION_BUSY: 11,
53
53
  SEAT_CANCELLED: 11,
54
- // Internal / uncaught (1) last resort bucket
54
+ // Internal / uncaught (1), last resort bucket
55
55
  INTERNAL: 1,
56
56
  PLATFORM_NOT_IMPLEMENTED: 1
57
57
  };
@@ -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` see JSDoc above.
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` see slimProfileMe's JSDoc.
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,
@@ -25,7 +25,7 @@ import {
25
25
  renderSuccess,
26
26
  renderUnexpectedError,
27
27
  resolveEffectiveConfig
28
- } from "./chunk-VPKMJYIF.js";
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-63JE5GM5.js");
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\u2026). URL and slug inputs resolve the provider ID automatically.",
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\u2026), or member URN (urn:li:member:<id>). URL and slug inputs resolve the provider ID automatically.",
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>\u2026]\n curviate message send <chat_id> "<text>" [--attach <file>\u2026]\n curviate message <chat_id> "<text>" [--attach <file>\u2026]\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';
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-\u2026 or COMPANY_\u2026, or a linkedin.com/messaging/thread/\u2026 URL).
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) {
package/dist/cli.js CHANGED
@@ -78,6 +78,15 @@ async function nodeName(cmd) {
78
78
  const meta = await resolveValue(cmd.meta ?? {});
79
79
  return meta.name ?? "this command";
80
80
  }
81
+ function renderPath(path) {
82
+ const parts = path[0] === "curviate" ? path.slice(1) : path;
83
+ return ["curviate", ...parts].join(" ");
84
+ }
85
+ function arityPhrase(count) {
86
+ if (count === 0) return "no positional arguments";
87
+ if (count === 1) return "1 positional argument";
88
+ return `${count} positional arguments`;
89
+ }
81
90
  function positionalTokens(rawArgs, booleanFlags) {
82
91
  const out = [];
83
92
  let afterDoubleDash = false;
@@ -104,6 +113,24 @@ function positionalTokens(rawArgs, booleanFlags) {
104
113
  }
105
114
  return out;
106
115
  }
116
+ async function extraPositionals(cmd, rawArgs) {
117
+ const booleanFlags = await booleanFlagNames(cmd);
118
+ const positionals = positionalTokens(rawArgs, booleanFlags);
119
+ const declaredCount = await nodePositionalCount(cmd);
120
+ return positionals.slice(declaredCount);
121
+ }
122
+ async function assertLeafConsumesPositionals(leaf, leafArgs, path, siblings) {
123
+ const extras = await extraPositionals(leaf, leafArgs);
124
+ if (extras.length === 0) return;
125
+ const token = extras[0].token;
126
+ const form = renderPath(path);
127
+ const arity = arityPhrase(await nodePositionalCount(leaf));
128
+ const hint = siblings && Object.prototype.hasOwnProperty.call(siblings, token) ? `Did you mean \`${renderPath([...path.slice(0, -1), token])}\`?` : void 0;
129
+ usageError(
130
+ `unexpected argument \`${token}\` after \`${form}\`. \`${form}\` takes ${arity}. Run \`${form} --help\` for its usage.`,
131
+ hint
132
+ );
133
+ }
107
134
  async function declaredArgNames(cmd) {
108
135
  const names = /* @__PURE__ */ new Set();
109
136
  const argsDef = await resolveValue(cmd.args ?? {});
@@ -155,7 +182,8 @@ function usageError(message, hint) {
155
182
  process.stderr.write("Run `curviate --help` for usage.\n");
156
183
  process.exit(2);
157
184
  }
158
- async function resolveLeaf(cmd, rawArgs) {
185
+ async function resolveLeaf(cmd, rawArgs, descent) {
186
+ const here = descent ?? { path: [await nodeName(cmd)] };
159
187
  const subCommands = await resolveValue(cmd.subCommands);
160
188
  if (subCommands && Object.keys(subCommands).length > 0) {
161
189
  const idx = firstPositionalIndex(rawArgs);
@@ -163,7 +191,10 @@ async function resolveLeaf(cmd, rawArgs) {
163
191
  const hasBarePositional = await nodeHasPositional(cmd);
164
192
  if (token !== void 0 && subCommands[token]) {
165
193
  const sub = await resolveValue(subCommands[token]);
166
- return resolveLeaf(sub, rawArgs.slice(idx + 1));
194
+ return resolveLeaf(sub, rawArgs.slice(idx + 1), {
195
+ path: [...here.path, token],
196
+ siblings: subCommands
197
+ });
167
198
  }
168
199
  if (token !== void 0) {
169
200
  const hint = successorHint(await nodeName(cmd), token);
@@ -172,16 +203,16 @@ async function resolveLeaf(cmd, rawArgs) {
172
203
  }
173
204
  }
174
205
  if (token !== void 0 && hasBarePositional) {
175
- const booleanFlags = await booleanFlagNames(cmd);
176
- const positionals = positionalTokens(rawArgs, booleanFlags);
177
- const declaredCount = await nodePositionalCount(cmd);
178
- const extras = positionals.slice(declaredCount);
206
+ const extras = await extraPositionals(cmd, rawArgs);
179
207
  if (extras.length > 0) {
180
208
  const first = extras[0];
181
209
  if (extras.length === 1 && Object.prototype.hasOwnProperty.call(subCommands, first.token)) {
182
210
  const sub = await resolveValue(subCommands[first.token]);
183
211
  const remaining = rawArgs.filter((_, i) => i !== first.index);
184
- return resolveLeaf(sub, remaining);
212
+ return resolveLeaf(sub, remaining, {
213
+ path: [...here.path, first.token],
214
+ siblings: subCommands
215
+ });
185
216
  }
186
217
  const name = await nodeName(cmd);
187
218
  usageError(
@@ -195,6 +226,7 @@ async function resolveLeaf(cmd, rawArgs) {
195
226
  }
196
227
  return { leaf: cmd, leafArgs: rawArgs };
197
228
  }
229
+ await assertLeafConsumesPositionals(cmd, rawArgs, here.path, here.siblings);
198
230
  return { leaf: cmd, leafArgs: rawArgs };
199
231
  }
200
232
  async function dispatch(root, rawArgs) {
@@ -259,31 +291,31 @@ var main = defineCommand({
259
291
  version: pkg.version,
260
292
  description: "Official command-line interface for the Curviate API."
261
293
  },
262
- // Subcommand registry names and descriptions are static for help rendering;
294
+ // Subcommand registry, names and descriptions are static for help rendering;
263
295
  // the handler implementation is loaded lazily on first invocation.
264
296
  subCommands: {
265
297
  login: () => import("./login-ZLDDIAFW.js").then((m) => m.loginCommand),
266
298
  config: () => import("./config-FPWWYG7G.js").then((m) => m.configCommand),
267
299
  // ---------------------------------------------------------------------------
268
- // Noun groups lazy-loaded on first invocation.
300
+ // Noun groups, lazy-loaded on first invocation.
269
301
  // ---------------------------------------------------------------------------
270
- profile: () => import("./profile-GADE33FY.js").then((m) => m.profileCommand),
271
- company: () => import("./company-NT4VO6JV.js").then((m) => m.companyCommand),
272
- job: () => import("./job-VOVQZGCK.js").then((m) => m.jobCommand),
273
- connect: () => import("./connect-VXWRLGQZ.js").then((m) => m.connectCommand),
274
- search: () => import("./search-V35MFTXL.js").then((m) => m.searchCommand),
275
- inbox: () => import("./inbox-GZAOUWZP.js").then((m) => m.inboxCommand),
276
- inboxes: () => import("./inboxes-6AZOB43U.js").then((m) => m.inboxesCommand),
277
- message: () => import("./message-FIEWH2BU.js").then((m) => m.messageCommand),
278
- post: () => import("./post-G4IMWUE2.js").then((m) => m.postCommand),
279
- comment: () => import("./comment-W4XTOTHN.js").then((m) => m.commentCommand),
280
- account: () => import("./account-OIV73PXX.js").then((m) => m.accountCommand),
281
- webhook: () => import("./webhook-5HXMJQFO.js").then((m) => m.webhookCommand),
282
- "sales-nav": () => import("./sales-nav-OXLRN6YK.js").then((m) => m.salesNavCommand),
283
- recruiter: () => import("./recruiter-LI2TMRHY.js").then((m) => m.recruiterCommand),
284
- group: () => import("./group-43PHTFP7.js").then((m) => m.groupCommand),
285
- feed: () => import("./feed-4Q3B56WI.js").then((m) => m.feedCommand),
286
- notification: () => import("./notification-65V67NWK.js").then((m) => m.notificationCommand)
302
+ profile: () => import("./profile-TJS2YAZD.js").then((m) => m.profileCommand),
303
+ company: () => import("./company-WDDLWP7E.js").then((m) => m.companyCommand),
304
+ job: () => import("./job-75ML7R7I.js").then((m) => m.jobCommand),
305
+ connect: () => import("./connect-5OROSY5C.js").then((m) => m.connectCommand),
306
+ search: () => import("./search-TDSLK2MB.js").then((m) => m.searchCommand),
307
+ inbox: () => import("./inbox-L6JBGZIB.js").then((m) => m.inboxCommand),
308
+ inboxes: () => import("./inboxes-UIO74C5Q.js").then((m) => m.inboxesCommand),
309
+ message: () => import("./message-DAQHRBSJ.js").then((m) => m.messageCommand),
310
+ post: () => import("./post-RGDOCGC4.js").then((m) => m.postCommand),
311
+ comment: () => import("./comment-MCGXFMN4.js").then((m) => m.commentCommand),
312
+ account: () => import("./account-QRGWUQXF.js").then((m) => m.accountCommand),
313
+ webhook: () => import("./webhook-7ESDSIRU.js").then((m) => m.webhookCommand),
314
+ "sales-nav": () => import("./sales-nav-5O7FR57S.js").then((m) => m.salesNavCommand),
315
+ recruiter: () => import("./recruiter-NSM33IEK.js").then((m) => m.recruiterCommand),
316
+ group: () => import("./group-HSZY2IJU.js").then((m) => m.groupCommand),
317
+ feed: () => import("./feed-SFVYEAYP.js").then((m) => m.feedCommand),
318
+ notification: () => import("./notification-DYJN4ZHN.js").then((m) => m.notificationCommand)
287
319
  },
288
320
  async run() {
289
321
  const { runMain } = await import("citty");
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  pageDelayFromFlags,
13
13
  streamAll
14
- } from "./chunk-HZFK45IW.js";
14
+ } from "./chunk-H6XD3F66.js";
15
15
  import "./chunk-DMQZEPQE.js";
16
16
  import {
17
17
  buildPreviewOutput
@@ -22,7 +22,7 @@ import {
22
22
  renderSuccess,
23
23
  renderUnexpectedError,
24
24
  resolveEffectiveConfig
25
- } from "./chunk-VPKMJYIF.js";
25
+ } from "./chunk-JA3NNST6.js";
26
26
  import {
27
27
  GLOBAL_FLAGS,
28
28
  WRITE_SINGLE_FLAGS
@@ -70,7 +70,7 @@ function buildListQuery(flags) {
70
70
  async function handleSdkError(err, outOpts, out) {
71
71
  const { CurviateError } = await import("@curviate/sdk");
72
72
  if (err instanceof CurviateError) {
73
- const { getExitCode } = await import("./exit-codes-63JE5GM5.js");
73
+ const { getExitCode } = await import("./exit-codes-KAVZN5BQ.js");
74
74
  renderError(err, outOpts, out);
75
75
  process.exit(getExitCode(err.code));
76
76
  }