@curviate/cli 0.12.0 → 0.14.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 CHANGED
@@ -6,6 +6,55 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6
6
  Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html):
7
7
  a new command or flag is a minor; a breaking command/flag/exit-code change is a major; a fix is a patch.
8
8
 
9
+ ## [0.14.0] - 2026-07-07
10
+
11
+ Webhooks surface cascade — the coupled release with `@curviate/sdk` 0.14.0. Additive minor.
12
+
13
+ ### Added
14
+
15
+ - **`webhook get <id>`** — get a single webhook owned by the calling tenant (`webhooks.get`). Read command; `--preview` is a usage error (nothing to mutate), matching `webhook state-diff`.
16
+
17
+ ### Changed
18
+
19
+ - **`@curviate/sdk` bumped to `^0.14.0`.** The SDK's webhook event catalogue expanded 21 → 27 (`chat.updated`, `chat.deleted`, `connection.new`, `account.initial_sync.*`, and account-lifecycle renames) and its `CurviateEvent` union re-keyed to match — this CLI never imports `CurviateEvent` directly, so `webhook verify`'s offline HMAC verification is unaffected; only the dependency range changed.
20
+
21
+ ## [0.13.0] - 2026-07-05
22
+
23
+ Accounts/Auth surface migration — the coupled release with `@curviate/sdk` 0.13.0. This is a
24
+ **breaking** minor (pre-1.0): the account connection and checkpoint commands were reshaped to
25
+ match the new account-in-path grammar.
26
+
27
+ ### Added
28
+
29
+ - **`account reconnect-link <account_id>`** — mint a one-time hosted **re-authorization** link for an existing disconnected account (the hosted counterpart of `account reconnect`). Same open+wait UX as `account connect-link`: on an interactive TTY the URL auto-opens and the command waits for the account to reconnect (exit `0` resolved, `9` expired/failed, `12` on a wait-window timeout); non-interactively it prints the url + session_id and returns immediately. Optional `--expires-in-seconds` / `--redirect-url`.
30
+ - **`account update --metadata '<json>'`** — set the account's custom metadata (a flat JSON object that replaces the store wholesale). **`account update --clear-proxy`** — clear the custom proxy (revert to automatic proxy protection).
31
+ - **New connect/checkpoint response fields ride through `--json` output** (coupled with the SDK 0.13.0 connect-fix regen — the CLI duck-types the response, so the fields pass through verbatim with no code change):
32
+ - `recovered` (boolean) on `account link` and `account checkpoint solve` completions — `true` when the connect reclaimed a LinkedIn identity already present on the workspace rather than connecting a brand-new one.
33
+ - the completed-account `status` is widened to `active | reconnect_needed | restricted | disconnected` (a recovered identity often reports needing a reconnect); the CLI reads `status` as a free-form string, so the wider set is unaffected.
34
+ - `challenge_type` (`mobile_app_approval`) + `recovery_hint` on an `account checkpoint poll` that returns `status: "expired"` (a mobile-approval timeout).
35
+ Surfacing `recovered` in the human-readable (non-`--json`) success line is a deferred UX follow-up — it would need consistent treatment across the direct-link, interactive-solve, and standalone-solve completion paths.
36
+
37
+ ### Changed (BREAKING)
38
+
39
+ - **Checkpoint commands are now account-in-path (positional), not `--checkpoint`.**
40
+ - `account checkpoint submit --checkpoint <id> --code <c>` → **`account checkpoint solve <account_id> --code <c>`**.
41
+ - `account checkpoint resend --checkpoint <id>` → **`account checkpoint request <account_id>`**.
42
+ - `account checkpoint poll --checkpoint <id>` → **`account checkpoint poll <account_id>`** (the `--checkpoint` flag becomes the account_id positional; `--wait`/`--timeout` unchanged).
43
+ - Update scripts: replace `checkpoint submit --checkpoint X --code Y` with `checkpoint solve X --code Y`, `checkpoint resend --checkpoint X` with `checkpoint request X`, and `checkpoint poll --checkpoint X` with `checkpoint poll X`.
44
+ - **`account refresh <account_id>` removed** — accounts restart and re-sync automatically now; there is no replacement command. Status freshness comes from the account-status webhook, the nightly reconcile, and `account get`.
45
+ - **`account connect-link` is create-only** — the `--purpose` and `--account-id` flags are removed; it only mints a link to connect a **new** account. Use `account reconnect-link <account_id>` for hosted re-auth of an existing account.
46
+ - **`account update` reshaped** — the managed `--country` / `--ip` flags are removed (a managed location is now chosen at connect time). The command now takes `--metadata` and/or a custom proxy (`--proxy-*` / `--clear-proxy`).
47
+ - **`account link` / `account reconnect` require `--user-agent` for cookie auth** — connecting by session cookie (`--auth-method cookie`) without a `--user-agent` fails fast at exit `2` (it stays optional for `--auth-method credentials`). Under `--preview` the check is skipped (a render never exits).
48
+ - SDK-parity manifest (`test/parity.test.ts`) repoints the checkpoint entries (`solve`/`request`/`poll`) and swaps `account refresh` → `account reconnect-link`; the manifest and SDK method count stay at 93 (`accounts` stays 12 methods).
49
+
50
+ ### Fixed
51
+
52
+ - **`account connect-session poll` now interpolates the session id correctly.** It previously passed the session id as an object to the SDK, producing a request path of `/v1/accounts/connect-sessions/[object Object]` (broken `--wait` loops). It now passes the id as a string. A regression test asserts the interpolated path is `/v1/accounts/connect-sessions/<session_id>`, never `[object Object]`.
53
+
54
+ ### Changed
55
+
56
+ - `@curviate/sdk` dependency bumped to `^0.13.0` — the coupled release carrying the reshaped `accounts` surface (see the SDK's own CHANGELOG). The CLI duck-types the SDK, so its commands are covered by the parity manifest against that release.
57
+
9
58
  ## [0.12.0] - 2026-07-05
10
59
 
11
60
  ### Added
@@ -157,7 +157,7 @@ ${challengeDescription(type)}
157
157
  function printResendHintIfApplicable(type, accountId, out) {
158
158
  if (!RESENDABLE_CHALLENGE_TYPES.has(type)) return;
159
159
  out.stderr.write(
160
- `No code/notification? Re-send it: curviate account checkpoint resend --checkpoint ${accountId}
160
+ `No code/notification? Re-send it: curviate account checkpoint request ${accountId}
161
161
  `
162
162
  );
163
163
  }
@@ -353,7 +353,7 @@ async function waitForMobileApproval(client, accountId, expiresAt, ctx) {
353
353
  for (; ; ) {
354
354
  let result;
355
355
  try {
356
- result = await client.accounts.pollCheckpoint({ account_id: accountId });
356
+ result = await client.accounts.pollCheckpoint(accountId);
357
357
  } catch (err) {
358
358
  return await handleError(err, ctx.outOpts, ctx.out);
359
359
  }
@@ -395,7 +395,7 @@ async function runInteractiveCheckpointLoop(client, initial, ctx) {
395
395
  return;
396
396
  }
397
397
  ctx.out.stderr.write(
398
- `Still waiting for approval. Finish out-of-band: curviate account checkpoint poll --checkpoint ${accountId}
398
+ `Still waiting for approval. Finish out-of-band: curviate account checkpoint poll ${accountId}
399
399
  `
400
400
  );
401
401
  process.exit(AUTH_NEEDED);
@@ -404,7 +404,7 @@ async function runInteractiveCheckpointLoop(client, initial, ctx) {
404
404
  for (; ; ) {
405
405
  const code = await ctx.readline("Enter the code: ");
406
406
  try {
407
- const result = await client.accounts.submitCheckpoint({ account_id: accountId, code });
407
+ const result = await client.accounts.solveCheckpoint(accountId, { code });
408
408
  const r = result;
409
409
  if (r.status === "checkpoint_required") {
410
410
  current = { ...r, account_id: r.account_id ?? accountId };
@@ -453,6 +453,10 @@ async function runAccountLink(client, flags, out, io = {}) {
453
453
  process.exit(2);
454
454
  }
455
455
  checkCredentialConflicts(flags, out);
456
+ if (!flags.preview && flags["auth-method"] === "cookie" && !flags["user-agent"]) {
457
+ out.stderr.write("error: --user-agent is required when --auth-method=cookie.\n");
458
+ process.exit(2);
459
+ }
456
460
  const resolvedIo = resolveCredentialIO(io);
457
461
  const authBody = await buildAuthBody(flags, {
458
462
  out,
@@ -486,12 +490,12 @@ async function runAccountLink(client, flags, out, io = {}) {
486
490
  successVerb: "linked"
487
491
  });
488
492
  }
489
- async function runConnectSessionWaitLoop(client, params, ctx) {
493
+ async function runConnectSessionWaitLoop(client, sessionId, ctx) {
490
494
  const startedAt = ctx.now();
491
495
  let timeoutAt = ctx.timeoutOverrideMs !== void 0 ? startedAt + ctx.timeoutOverrideMs : void 0;
492
496
  await ctx.sleep(CHECKPOINT_POLL_FIRST_DELAY_MS);
493
497
  for (; ; ) {
494
- const result = await client.accounts.getConnectSession(params);
498
+ const result = await client.accounts.getConnectSession(sessionId);
495
499
  const r = result;
496
500
  if (r.status === "resolved") return { kind: "resolved", result };
497
501
  if (r.status === "expired" || r.status === "failed") {
@@ -527,8 +531,6 @@ function resolveWaitTimeoutOverrideMs(flags, out) {
527
531
  async function runAccountConnectLink(client, flags, out, io = {}) {
528
532
  const body = {};
529
533
  if (flags["seat-id"]) body["seat_id"] = flags["seat-id"];
530
- if (flags["account-id"]) body["account_id"] = flags["account-id"];
531
- if (flags.purpose) body["purpose"] = flags.purpose;
532
534
  if (flags["expires-in-seconds"]) body["expires_in_seconds"] = parseInt(flags["expires-in-seconds"], 10);
533
535
  if (flags["redirect-url"]) body["redirect_url"] = flags["redirect-url"];
534
536
  const outOpts = resolveOutputOpts(flags);
@@ -570,10 +572,10 @@ async function runAccountConnectLink(client, flags, out, io = {}) {
570
572
  }
571
573
  const timeoutOverrideMs = resolveWaitTimeoutOverrideMs(flags, out);
572
574
  const showTicker = resolvedIo.isOutputTTY && !(flags.json ?? false);
573
- const params = { session_id: mint.session_id };
575
+ const sessionId = mint.session_id ?? "";
574
576
  let outcome;
575
577
  try {
576
- outcome = await runConnectSessionWaitLoop(client, params, {
578
+ outcome = await runConnectSessionWaitLoop(client, sessionId, {
577
579
  out,
578
580
  sleep: resolvedIo.sleep,
579
581
  now: resolvedIo.now,
@@ -599,7 +601,7 @@ async function runAccountConnectLink(client, flags, out, io = {}) {
599
601
  }
600
602
  renderSuccess(outcome.result, outOpts, out);
601
603
  out.stderr.write(
602
- `Still waiting for the account to connect \u2014 the wait window elapsed. Check again: curviate account connect-session poll --session ${mint.session_id} --wait
604
+ `Still waiting for the account to connect \u2014 the wait window elapsed. Check again: curviate account connect-session poll --session ${sessionId} --wait
603
605
  `
604
606
  );
605
607
  process.exit(AUTH_NEEDED);
@@ -609,16 +611,16 @@ async function runAccountConnectSessionPoll(client, flags, out, io = {}) {
609
611
  out.stderr.write("error: --session is required (the session_id from `account connect-link`).\n");
610
612
  process.exit(2);
611
613
  }
612
- const params = { session_id: flags.session };
614
+ const sessionId = flags.session;
613
615
  const outOpts = resolveOutputOpts(flags);
614
616
  if (flags.preview) {
615
- const preview = buildPreviewOutput({ method: "accounts.getConnectSession", args: {}, body: params });
617
+ const preview = buildPreviewOutput({ method: "accounts.getConnectSession", args: { session_id: sessionId }, body: {} });
616
618
  out.stdout.write(JSON.stringify(preview) + "\n");
617
619
  return;
618
620
  }
619
621
  if (!flags.wait) {
620
622
  try {
621
- const result = await client.accounts.getConnectSession(params);
623
+ const result = await client.accounts.getConnectSession(sessionId);
622
624
  renderSuccess(result, outOpts, out);
623
625
  } catch (err) {
624
626
  await handleError(err, outOpts, out);
@@ -630,7 +632,7 @@ async function runAccountConnectSessionPoll(client, flags, out, io = {}) {
630
632
  const showTicker = resolvedIo.isOutputTTY && !(flags.json ?? false);
631
633
  let outcome;
632
634
  try {
633
- outcome = await runConnectSessionWaitLoop(client, params, {
635
+ outcome = await runConnectSessionWaitLoop(client, sessionId, {
634
636
  out,
635
637
  sleep: resolvedIo.sleep,
636
638
  now: resolvedIo.now,
@@ -661,12 +663,94 @@ async function runAccountConnectSessionPoll(client, flags, out, io = {}) {
661
663
  );
662
664
  process.exit(AUTH_NEEDED);
663
665
  }
666
+ async function runAccountReconnectLink(client, flags, out, io = {}) {
667
+ const accountId = flags["account-id"] ?? "";
668
+ const body = {};
669
+ if (flags["expires-in-seconds"]) body["expires_in_seconds"] = parseInt(flags["expires-in-seconds"], 10);
670
+ if (flags["redirect-url"]) body["redirect_url"] = flags["redirect-url"];
671
+ const outOpts = resolveOutputOpts(flags);
672
+ if (flags.preview) {
673
+ const preview = buildPreviewOutput({ method: "accounts.createReconnectLink", args: { accountId }, body });
674
+ out.stdout.write(JSON.stringify(preview) + "\n");
675
+ return;
676
+ }
677
+ let result;
678
+ try {
679
+ result = await client.accounts.createReconnectLink(accountId, body);
680
+ } catch (err) {
681
+ await handleError(err, outOpts, out);
682
+ return;
683
+ }
684
+ const mint = result;
685
+ const resolvedIo = resolveCredentialIO(io);
686
+ const isInteractive = resolvedIo.isTTY && resolvedIo.isOutputTTY && !(flags["no-interactive"] ?? false);
687
+ if (!isInteractive) {
688
+ if (mint.url) {
689
+ out.stderr.write(`Open this URL in a browser to reconnect the account: ${mint.url}
690
+ `);
691
+ }
692
+ if (mint.session_id) {
693
+ out.stderr.write(
694
+ `Session: ${mint.session_id} \u2014 check it later with: curviate account connect-session poll --session ${mint.session_id} --wait
695
+ `
696
+ );
697
+ }
698
+ renderSuccess(result, outOpts, out);
699
+ return;
700
+ }
701
+ if ((flags.open ?? true) && mint.url) {
702
+ await resolvedIo.open(mint.url);
703
+ }
704
+ if (!(flags.wait ?? true)) {
705
+ renderSuccess(result, outOpts, out);
706
+ return;
707
+ }
708
+ const timeoutOverrideMs = resolveWaitTimeoutOverrideMs(flags, out);
709
+ const showTicker = resolvedIo.isOutputTTY && !(flags.json ?? false);
710
+ const sessionId = mint.session_id ?? "";
711
+ let outcome;
712
+ try {
713
+ outcome = await runConnectSessionWaitLoop(client, sessionId, {
714
+ out,
715
+ sleep: resolvedIo.sleep,
716
+ now: resolvedIo.now,
717
+ showTicker,
718
+ timeoutOverrideMs
719
+ });
720
+ } catch (err) {
721
+ await handleError(err, outOpts, out);
722
+ return;
723
+ }
724
+ if (outcome.kind === "resolved") {
725
+ printConnectSessionResolved(outcome.result, { out, outOpts });
726
+ return;
727
+ }
728
+ if (outcome.kind === "terminal_failure") {
729
+ renderSuccess(outcome.result, outOpts, out);
730
+ out.stderr.write(
731
+ `This reconnect session has ${outcome.status}. Generate a new link: curviate account reconnect-link ${accountId}.
732
+ `
733
+ );
734
+ process.exit(9);
735
+ return;
736
+ }
737
+ renderSuccess(outcome.result, outOpts, out);
738
+ out.stderr.write(
739
+ `Still waiting for the account to reconnect \u2014 the wait window elapsed. Check again: curviate account connect-session poll --session ${sessionId} --wait
740
+ `
741
+ );
742
+ process.exit(AUTH_NEEDED);
743
+ }
664
744
  async function runAccountReconnect(client, flags, out, io = {}) {
665
745
  if (!flags["auth-method"]) {
666
746
  out.stderr.write("error: --auth-method is required for account reconnect (credentials | cookie).\n");
667
747
  process.exit(2);
668
748
  }
669
749
  checkCredentialConflicts(flags, out);
750
+ if (!flags.preview && flags["auth-method"] === "cookie" && !flags["user-agent"]) {
751
+ out.stderr.write("error: --user-agent is required when --auth-method=cookie.\n");
752
+ process.exit(2);
753
+ }
670
754
  const accountId = flags["account-id"] ?? "";
671
755
  const resolvedIo = resolveCredentialIO(io);
672
756
  const body = await buildAuthBody(flags, {
@@ -701,30 +785,32 @@ async function runAccountReconnect(client, flags, out, io = {}) {
701
785
  successVerb: "reconnected"
702
786
  });
703
787
  }
704
- async function runAccountRefresh(client, flags, out) {
705
- const accountId = flags["account-id"] ?? "";
706
- const outOpts = resolveOutputOpts(flags);
707
- if (flags.preview) {
708
- const preview = buildPreviewOutput({
709
- method: "accounts.refresh",
710
- args: { accountId },
711
- body: {}
712
- });
713
- out.stdout.write(JSON.stringify(preview) + "\n");
714
- return;
715
- }
716
- try {
717
- const result = await client.accounts.refresh(accountId);
718
- renderSuccess(result, outOpts, out);
719
- } catch (err) {
720
- await handleError(err, outOpts, out);
721
- }
722
- }
723
788
  async function runAccountUpdate(client, flags, out) {
724
789
  const accountId = flags["account-id"] ?? "";
725
790
  const body = {};
726
- if (flags.country) body["country"] = flags.country;
727
- if (flags.ip) body["ip"] = flags.ip;
791
+ if (flags.metadata !== void 0) {
792
+ let parsed;
793
+ try {
794
+ parsed = JSON.parse(flags.metadata);
795
+ } catch {
796
+ out.stderr.write(`error: --metadata must be a JSON object, e.g. --metadata '{"team":"growth"}'.
797
+ `);
798
+ process.exit(2);
799
+ }
800
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
801
+ out.stderr.write(`error: --metadata must be a JSON object, e.g. --metadata '{"team":"growth"}'.
802
+ `);
803
+ process.exit(2);
804
+ }
805
+ body["metadata"] = parsed;
806
+ }
807
+ if (flags["clear-proxy"]) {
808
+ if (flags["proxy-host"]) {
809
+ out.stderr.write("error: --clear-proxy cannot be combined with --proxy-host (choose one).\n");
810
+ process.exit(2);
811
+ }
812
+ body["proxy"] = null;
813
+ }
728
814
  if (flags["proxy-host"]) {
729
815
  const proxyPassword = await resolveSecret({
730
816
  flagValue: flags["proxy-password"],
@@ -775,24 +861,24 @@ async function runAccountDisconnect(client, flags, out) {
775
861
  await handleError(err, outOpts, out);
776
862
  }
777
863
  }
778
- async function runAccountCheckpointSubmit(client, flags, out) {
779
- if (!flags.checkpoint) {
780
- out.stderr.write("error: --checkpoint is required (the provisional account_id from the 202 response).\n");
864
+ async function runAccountCheckpointSolve(client, flags, out) {
865
+ if (!flags["account-id"]) {
866
+ out.stderr.write("error: account_id is required (the provisional account_id from the 202 response).\n");
781
867
  process.exit(2);
782
868
  }
783
869
  if (!flags.code) {
784
870
  out.stderr.write("error: --code is required (the OTP / 2FA code).\n");
785
871
  process.exit(2);
786
872
  }
873
+ const accountId = flags["account-id"] ?? "";
787
874
  const body = {
788
- account_id: flags.checkpoint,
789
875
  code: flags.code
790
876
  };
791
877
  const outOpts = resolveOutputOpts(flags);
792
878
  if (flags.preview) {
793
879
  const preview = buildPreviewOutput({
794
- method: "accounts.submitCheckpoint",
795
- args: {},
880
+ method: "accounts.solveCheckpoint",
881
+ args: { accountId },
796
882
  body
797
883
  });
798
884
  out.stdout.write(JSON.stringify(preview) + "\n");
@@ -800,7 +886,7 @@ async function runAccountCheckpointSubmit(client, flags, out) {
800
886
  }
801
887
  let chained = false;
802
888
  try {
803
- const result = await client.accounts.submitCheckpoint(body);
889
+ const result = await client.accounts.solveCheckpoint(accountId, body);
804
890
  const r = result;
805
891
  renderSuccess(result, outOpts, out);
806
892
  chained = r.status === "checkpoint_required";
@@ -812,26 +898,24 @@ async function runAccountCheckpointSubmit(client, flags, out) {
812
898
  process.exit(AUTH_NEEDED);
813
899
  }
814
900
  }
815
- async function runAccountCheckpointResend(client, flags, out) {
816
- if (!flags.checkpoint) {
817
- out.stderr.write("error: --checkpoint is required (the provisional account_id from the 202 response).\n");
901
+ async function runAccountCheckpointRequest(client, flags, out) {
902
+ if (!flags["account-id"]) {
903
+ out.stderr.write("error: account_id is required (the provisional account_id from the 202 response).\n");
818
904
  process.exit(2);
819
905
  }
820
- const body = {
821
- account_id: flags.checkpoint
822
- };
906
+ const accountId = flags["account-id"] ?? "";
823
907
  const outOpts = resolveOutputOpts(flags);
824
908
  if (flags.preview) {
825
909
  const preview = buildPreviewOutput({
826
- method: "accounts.resendCheckpoint",
827
- args: {},
828
- body
910
+ method: "accounts.requestCheckpoint",
911
+ args: { accountId },
912
+ body: {}
829
913
  });
830
914
  out.stdout.write(JSON.stringify(preview) + "\n");
831
915
  return;
832
916
  }
833
917
  try {
834
- const result = await client.accounts.resendCheckpoint(body);
918
+ const result = await client.accounts.requestCheckpoint(accountId);
835
919
  renderSuccess(result, outOpts, out);
836
920
  } catch (err) {
837
921
  await handleError(err, outOpts, out);
@@ -843,12 +927,12 @@ function formatRemaining(ms) {
843
927
  const seconds = totalSec % 60;
844
928
  return `${minutes}:${String(seconds).padStart(2, "0")}`;
845
929
  }
846
- async function runCheckpointPollWaitLoop(client, body, ctx) {
930
+ async function runCheckpointPollWaitLoop(client, accountId, ctx) {
847
931
  const startedAt = ctx.now();
848
932
  let timeoutAt = ctx.timeoutOverrideMs !== void 0 ? startedAt + ctx.timeoutOverrideMs : void 0;
849
933
  await ctx.sleep(CHECKPOINT_POLL_FIRST_DELAY_MS);
850
934
  for (; ; ) {
851
- const result = await client.accounts.pollCheckpoint(body);
935
+ const result = await client.accounts.pollCheckpoint(accountId);
852
936
  const r = result;
853
937
  if (r.status === "active") return { kind: "active", result };
854
938
  if (r.status === "expired" || r.status === "failed") {
@@ -867,26 +951,24 @@ async function runCheckpointPollWaitLoop(client, body, ctx) {
867
951
  }
868
952
  }
869
953
  async function runAccountCheckpointPoll(client, flags, out, io = {}) {
870
- if (!flags.checkpoint) {
871
- out.stderr.write("error: --checkpoint is required (the provisional account_id from the 202 response).\n");
954
+ if (!flags["account-id"]) {
955
+ out.stderr.write("error: account_id is required (the provisional account_id from the 202 response).\n");
872
956
  process.exit(2);
873
957
  }
874
- const body = {
875
- account_id: flags.checkpoint
876
- };
958
+ const accountId = flags["account-id"] ?? "";
877
959
  const outOpts = resolveOutputOpts(flags);
878
960
  if (flags.preview) {
879
961
  const preview = buildPreviewOutput({
880
962
  method: "accounts.pollCheckpoint",
881
- args: {},
882
- body
963
+ args: { accountId },
964
+ body: {}
883
965
  });
884
966
  out.stdout.write(JSON.stringify(preview) + "\n");
885
967
  return;
886
968
  }
887
969
  if (!flags.wait) {
888
970
  try {
889
- const result = await client.accounts.pollCheckpoint(body);
971
+ const result = await client.accounts.pollCheckpoint(accountId);
890
972
  renderSuccess(result, outOpts, out);
891
973
  } catch (err) {
892
974
  await handleError(err, outOpts, out);
@@ -904,10 +986,10 @@ async function runAccountCheckpointPoll(client, flags, out, io = {}) {
904
986
  }
905
987
  const resolvedIo = resolveCredentialIO(io);
906
988
  const showTicker = resolvedIo.isOutputTTY && !(flags.json ?? false);
907
- printResendHintIfApplicable("mobile_app_approval", flags.checkpoint, out);
989
+ printResendHintIfApplicable("mobile_app_approval", accountId, out);
908
990
  let outcome;
909
991
  try {
910
- outcome = await runCheckpointPollWaitLoop(client, body, {
992
+ outcome = await runCheckpointPollWaitLoop(client, accountId, {
911
993
  out,
912
994
  sleep: resolvedIo.sleep,
913
995
  now: resolvedIo.now,
@@ -931,7 +1013,7 @@ async function runAccountCheckpointPoll(client, flags, out, io = {}) {
931
1013
  }
932
1014
  renderSuccess(outcome.result, outOpts, out);
933
1015
  out.stderr.write(
934
- `Still waiting for approval \u2014 the wait window elapsed. Check again: curviate account checkpoint poll --checkpoint ${flags.checkpoint} --wait
1016
+ `Still waiting for approval \u2014 the wait window elapsed. Check again: curviate account checkpoint poll ${accountId} --wait
935
1017
  `
936
1018
  );
937
1019
  process.exit(AUTH_NEEDED);
@@ -983,7 +1065,7 @@ var accountGetCommand = defineCommand({
983
1065
  var accountLinkCommand = defineCommand({
984
1066
  meta: {
985
1067
  name: "link",
986
- description: "Connect a LinkedIn account to an empty seat. If LinkedIn requires verification you'll be prompted for the code interactively; in a non-interactive shell the command exits 12 and you finish with `curviate account checkpoint submit`."
1068
+ description: "Connect a LinkedIn account to an empty seat. If LinkedIn requires verification you'll be prompted for the code interactively; in a non-interactive shell the command exits 12 and you finish with `curviate account checkpoint solve <account_id> --code`."
987
1069
  },
988
1070
  args: {
989
1071
  ...WRITE_SINGLE_FLAGS,
@@ -1030,13 +1112,11 @@ var accountLinkCommand = defineCommand({
1030
1112
  var accountConnectLinkCommand = defineCommand({
1031
1113
  meta: {
1032
1114
  name: "connect-link",
1033
- description: "Generate a one-time hosted account connection URL. On an interactive TTY the URL auto-opens in your browser and the command waits for the account to connect (exit 0 on success, 9 if the link expires or fails, 12 if the wait window elapses first). Non-interactively it never opens a browser and returns immediately with the url and session_id \u2014 poll completion later with `account connect-session poll`."
1115
+ description: "Generate a one-time hosted URL to connect a NEW LinkedIn account. On an interactive TTY the URL auto-opens in your browser and the command waits for the account to connect (exit 0 on success, 9 if the link expires or fails, 12 if the wait window elapses first). Non-interactively it never opens a browser and returns immediately with the url and session_id \u2014 poll completion later with `account connect-session poll`. To re-authorize an EXISTING account, use `account reconnect-link`."
1034
1116
  },
1035
1117
  args: {
1036
1118
  ...WRITE_SINGLE_FLAGS,
1037
- "seat-id": { type: "string", description: "Target seat (required when purpose=create)." },
1038
- "account-id": { type: "string", description: "Account to reconnect (required when purpose=reconnect)." },
1039
- purpose: { type: "string", description: "create | reconnect (default: create)." },
1119
+ "seat-id": { type: "string", description: "Empty seat to bind the new account to.", required: true },
1040
1120
  "expires-in-seconds": { type: "string", description: "Link expiry in seconds (60\u20133600, default 900)." },
1041
1121
  "redirect-url": { type: "string", description: "Browser return URL after the hosted flow." },
1042
1122
  open: {
@@ -1072,6 +1152,47 @@ var accountConnectLinkCommand = defineCommand({
1072
1152
  await runAccountConnectLink(client, flags, out);
1073
1153
  }
1074
1154
  });
1155
+ var accountReconnectLinkCommand = defineCommand({
1156
+ meta: {
1157
+ name: "reconnect-link",
1158
+ description: "Generate a one-time hosted URL to re-authorize an EXISTING disconnected account (the hosted counterpart of `account reconnect`). On an interactive TTY the URL auto-opens and the command waits for the account to reconnect (exit 0 on success, 9 if the link expires or fails, 12 if the wait window elapses first). Non-interactively it prints the url + session_id and returns immediately \u2014 poll completion later with `account connect-session poll`."
1159
+ },
1160
+ args: {
1161
+ ...WRITE_SINGLE_FLAGS,
1162
+ "account-id": { type: "positional", description: "Account id (acc_\u2026) to re-authorize." },
1163
+ "expires-in-seconds": { type: "string", description: "Link expiry in seconds (60\u20133600, default 900)." },
1164
+ "redirect-url": { type: "string", description: "Browser return URL after the hosted flow." },
1165
+ open: {
1166
+ type: "boolean",
1167
+ description: "Auto-open the URL in your default browser. Default: on for an interactive TTY, always off otherwise (ignored non-interactively)."
1168
+ },
1169
+ wait: {
1170
+ type: "boolean",
1171
+ description: "Poll for the account to reconnect on an adaptive cadence (1000ms, then 1500ms for 30s, then 3000ms) after opening the URL. Default: on for an interactive TTY, always off otherwise."
1172
+ },
1173
+ // Override WRITE_SINGLE_FLAGS.timeout: --timeout is the interactive wait
1174
+ // loop's own wall-clock bound in MILLISECONDS, not the SDK request timeout.
1175
+ timeout: {
1176
+ type: "string",
1177
+ description: "Wait-loop timeout in milliseconds (only meaningful under the interactive TTY wait; default: time remaining to the link's expiry). Note the unit: this is milliseconds."
1178
+ }
1179
+ },
1180
+ async run({ args }) {
1181
+ const flags = args;
1182
+ const cfg = await resolveEffectiveConfig({
1183
+ apiKey: flags["api-key"],
1184
+ baseUrl: flags["base-url"],
1185
+ profile: flags.profile
1186
+ });
1187
+ if (!cfg.apiKey) {
1188
+ process.stderr.write("error: no API key \u2014 run `curviate login` or pass --api-key.\n");
1189
+ process.exit(3);
1190
+ }
1191
+ const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
1192
+ const out = buildOutputStreams();
1193
+ await runAccountReconnectLink(client, flags, out);
1194
+ }
1195
+ });
1075
1196
  var accountConnectSessionPollCommand = defineCommand({
1076
1197
  meta: {
1077
1198
  name: "poll",
@@ -1126,7 +1247,7 @@ var accountConnectSessionCommand = defineCommand({
1126
1247
  var accountReconnectCommand = defineCommand({
1127
1248
  meta: {
1128
1249
  name: "reconnect",
1129
- description: "Re-authorize a disconnected account in place. If LinkedIn requires verification you'll be prompted for the code interactively; in a non-interactive shell the command exits 12 and you finish with `curviate account checkpoint submit`."
1250
+ description: "Re-authorize a disconnected account in place. If LinkedIn requires verification you'll be prompted for the code interactively; in a non-interactive shell the command exits 12 and you finish with `curviate account checkpoint solve <account_id> --code`."
1130
1251
  },
1131
1252
  args: {
1132
1253
  ...WRITE_SINGLE_FLAGS,
@@ -1170,36 +1291,13 @@ var accountReconnectCommand = defineCommand({
1170
1291
  await runAccountReconnect(client, flags, out);
1171
1292
  }
1172
1293
  });
1173
- var accountRefreshCommand = defineCommand({
1174
- meta: { name: "refresh", description: "Refresh an account's synced data sources." },
1175
- args: {
1176
- ...WRITE_SINGLE_FLAGS,
1177
- "account-id": { type: "positional", description: "Account id (acc_\u2026)." }
1178
- },
1179
- async run({ args }) {
1180
- const flags = args;
1181
- const cfg = await resolveEffectiveConfig({
1182
- apiKey: flags["api-key"],
1183
- baseUrl: flags["base-url"],
1184
- timeout: flags.timeout,
1185
- profile: flags.profile
1186
- });
1187
- if (!cfg.apiKey) {
1188
- process.stderr.write("error: no API key \u2014 run `curviate login` or pass --api-key.\n");
1189
- process.exit(3);
1190
- }
1191
- const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
1192
- const out = buildOutputStreams();
1193
- await runAccountRefresh(client, flags, out);
1194
- }
1195
- });
1196
1294
  var accountUpdateCommand = defineCommand({
1197
- meta: { name: "update", description: "Update managed-proxy configuration for an account." },
1295
+ meta: { name: "update", description: "Update an account's metadata and/or custom-proxy configuration." },
1198
1296
  args: {
1199
1297
  ...WRITE_SINGLE_FLAGS,
1200
1298
  "account-id": { type: "positional", description: "Account id (acc_\u2026)." },
1201
- country: { type: "string", description: "Proxy location hint (ISO 3166-1 alpha-2)." },
1202
- ip: { type: "string", description: "IP to infer the managed proxy location." },
1299
+ metadata: { type: "string", description: `Custom metadata as a JSON object (flat string\u2192string map) \u2014 replaces the store wholesale. Example: '{"team":"growth"}'.` },
1300
+ "clear-proxy": { type: "boolean", description: "Clear the custom proxy (revert to automatic proxy protection). Mutually exclusive with --proxy-host.", default: false },
1203
1301
  "proxy-protocol": { type: "string", description: "Proxy protocol: http | https | socks5." },
1204
1302
  "proxy-host": { type: "string", description: "Proxy host or IP." },
1205
1303
  "proxy-port": { type: "string", description: "Proxy port." },
@@ -1246,14 +1344,13 @@ var accountDisconnectCommand = defineCommand({
1246
1344
  await runAccountDisconnect(client, flags, out);
1247
1345
  }
1248
1346
  });
1249
- var accountCheckpointSubmitCommand = defineCommand({
1250
- meta: { name: "submit", description: "Submit an OTP / 2FA code to resolve a checkpoint challenge." },
1347
+ var accountCheckpointSolveCommand = defineCommand({
1348
+ meta: { name: "solve", description: "Solve a checkpoint challenge with an OTP / 2FA code." },
1251
1349
  args: {
1252
1350
  ...WRITE_SINGLE_FLAGS,
1253
- checkpoint: {
1254
- type: "string",
1255
- description: "The provisional account_id from the 202 checkpoint_required response.",
1256
- required: true
1351
+ "account-id": {
1352
+ type: "positional",
1353
+ description: "The provisional account_id (acc_\u2026) from the 202 checkpoint_required response."
1257
1354
  },
1258
1355
  code: {
1259
1356
  type: "string",
@@ -1275,7 +1372,7 @@ var accountCheckpointSubmitCommand = defineCommand({
1275
1372
  }
1276
1373
  const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
1277
1374
  const out = buildOutputStreams();
1278
- await runAccountCheckpointSubmit(client, flags, out);
1375
+ await runAccountCheckpointSolve(client, flags, out);
1279
1376
  }
1280
1377
  });
1281
1378
  var accountCheckpointPollCommand = defineCommand({
@@ -1285,10 +1382,9 @@ var accountCheckpointPollCommand = defineCommand({
1285
1382
  },
1286
1383
  args: {
1287
1384
  ...WRITE_SINGLE_FLAGS,
1288
- checkpoint: {
1289
- type: "string",
1290
- description: "The provisional account_id from the 202 checkpoint_required response.",
1291
- required: true
1385
+ "account-id": {
1386
+ type: "positional",
1387
+ description: "The provisional account_id (acc_\u2026) from the 202 checkpoint_required response."
1292
1388
  },
1293
1389
  wait: {
1294
1390
  type: "boolean",
@@ -1320,17 +1416,16 @@ var accountCheckpointPollCommand = defineCommand({
1320
1416
  await runAccountCheckpointPoll(client, flags, out);
1321
1417
  }
1322
1418
  });
1323
- var accountCheckpointResendCommand = defineCommand({
1419
+ var accountCheckpointRequestCommand = defineCommand({
1324
1420
  meta: {
1325
- name: "resend",
1326
- description: "Re-send the challenge notification for a pending checkpoint (e.g. re-send an OTP email, SMS code, or mobile-app approval push). Not every challenge type supports resend \u2014 an authenticator- app code has nothing to re-send. The response's `resent` boolean tells you honestly whether a new notification actually went out; the command still exits 0 either way."
1421
+ name: "request",
1422
+ description: "Re-request the challenge notification for a pending checkpoint (e.g. re-send an OTP email, SMS code, or mobile-app approval push). Not every challenge type supports it \u2014 an authenticator- app code has nothing to re-send. The response's `resent` boolean tells you honestly whether a new notification actually went out; the command still exits 0 either way."
1327
1423
  },
1328
1424
  args: {
1329
1425
  ...WRITE_SINGLE_FLAGS,
1330
- checkpoint: {
1331
- type: "string",
1332
- description: "The provisional account_id from the 202 checkpoint_required response.",
1333
- required: true
1426
+ "account-id": {
1427
+ type: "positional",
1428
+ description: "The provisional account_id (acc_\u2026) from the 202 checkpoint_required response."
1334
1429
  }
1335
1430
  },
1336
1431
  async run({ args }) {
@@ -1347,21 +1442,21 @@ var accountCheckpointResendCommand = defineCommand({
1347
1442
  }
1348
1443
  const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
1349
1444
  const out = buildOutputStreams();
1350
- await runAccountCheckpointResend(client, flags, out);
1445
+ await runAccountCheckpointRequest(client, flags, out);
1351
1446
  }
1352
1447
  });
1353
1448
  var accountCheckpointCommand = defineCommand({
1354
1449
  meta: {
1355
1450
  name: "checkpoint",
1356
- description: "Checkpoint challenge operations (submit OTP, poll mobile-app approval, or resend the challenge notification)."
1451
+ description: "Checkpoint challenge operations (solve with an OTP, poll mobile-app approval, or re-request the challenge notification)."
1357
1452
  },
1358
1453
  subCommands: {
1359
- submit: accountCheckpointSubmitCommand,
1454
+ solve: accountCheckpointSolveCommand,
1360
1455
  poll: accountCheckpointPollCommand,
1361
- resend: accountCheckpointResendCommand
1456
+ request: accountCheckpointRequestCommand
1362
1457
  },
1363
1458
  async run() {
1364
- process.stderr.write("Usage: curviate account checkpoint submit | poll | resend\n");
1459
+ process.stderr.write("Usage: curviate account checkpoint solve <account_id> --code <code> | poll <account_id> | request <account_id>\n");
1365
1460
  }
1366
1461
  });
1367
1462
  var accountCommand = defineCommand({
@@ -1371,24 +1466,24 @@ var accountCommand = defineCommand({
1371
1466
  get: accountGetCommand,
1372
1467
  link: accountLinkCommand,
1373
1468
  "connect-link": accountConnectLinkCommand,
1469
+ "reconnect-link": accountReconnectLinkCommand,
1374
1470
  "connect-session": accountConnectSessionCommand,
1375
1471
  reconnect: accountReconnectCommand,
1376
- refresh: accountRefreshCommand,
1377
1472
  update: accountUpdateCommand,
1378
1473
  disconnect: accountDisconnectCommand,
1379
1474
  checkpoint: accountCheckpointCommand
1380
1475
  },
1381
1476
  async run() {
1382
1477
  process.stderr.write(
1383
- "Usage: curviate account <subcommand>\n list | get | link | connect-link | connect-session poll | reconnect | refresh | update | disconnect | checkpoint\n"
1478
+ "Usage: curviate account <subcommand>\n list | get | link | connect-link | reconnect-link | connect-session poll | reconnect | update | disconnect | checkpoint\n"
1384
1479
  );
1385
1480
  }
1386
1481
  });
1387
1482
  export {
1388
1483
  accountCommand,
1389
1484
  runAccountCheckpointPoll,
1390
- runAccountCheckpointResend,
1391
- runAccountCheckpointSubmit,
1485
+ runAccountCheckpointRequest,
1486
+ runAccountCheckpointSolve,
1392
1487
  runAccountConnectLink,
1393
1488
  runAccountConnectSessionPoll,
1394
1489
  runAccountDisconnect,
@@ -1396,6 +1491,6 @@ export {
1396
1491
  runAccountLink,
1397
1492
  runAccountList,
1398
1493
  runAccountReconnect,
1399
- runAccountRefresh,
1494
+ runAccountReconnectLink,
1400
1495
  runAccountUpdate
1401
1496
  };
package/dist/cli.js CHANGED
@@ -152,8 +152,8 @@ var main = defineCommand({
152
152
  inbox: () => import("./inbox-DN7X7CM2.js").then((m) => m.inboxCommand),
153
153
  message: () => import("./message-6K5E3CUF.js").then((m) => m.messageCommand),
154
154
  post: () => import("./post-2JQZ3OSF.js").then((m) => m.postCommand),
155
- account: () => import("./account-NZBO6Y5I.js").then((m) => m.accountCommand),
156
- webhook: () => import("./webhook-XPWBI675.js").then((m) => m.webhookCommand),
155
+ account: () => import("./account-7AUDAMIK.js").then((m) => m.accountCommand),
156
+ webhook: () => import("./webhook-EIY5WWTE.js").then((m) => m.webhookCommand),
157
157
  "sales-nav": () => import("./sales-nav-QTSTJMKA.js").then((m) => m.salesNavCommand),
158
158
  recruiter: () => import("./recruiter-XHVMQWK6.js").then((m) => m.recruiterCommand)
159
159
  },
@@ -134,6 +134,17 @@ async function runWebhookEvents(client, flags, out) {
134
134
  await handleError(err, outOpts, out);
135
135
  }
136
136
  }
137
+ async function runWebhookGet(client, flags, out) {
138
+ rejectPreviewOnRead(flags.preview, out);
139
+ const id = flags.id ?? "";
140
+ const outOpts = resolveOutputOpts(flags);
141
+ try {
142
+ const result = await client.webhooks.get(id);
143
+ renderSuccess(result, outOpts, out);
144
+ } catch (err) {
145
+ await handleError(err, outOpts, out);
146
+ }
147
+ }
137
148
  async function runWebhookUpdate(client, flags, out) {
138
149
  if (flags.source !== void 0) {
139
150
  out.stderr.write("error: --source cannot be changed after creation (source is immutable).\n");
@@ -302,6 +313,29 @@ var webhookEventsCommand = defineCommand({
302
313
  await runWebhookEvents(client, flags, out);
303
314
  }
304
315
  });
316
+ var webhookGetCommand = defineCommand({
317
+ meta: { name: "get", description: "Get a single webhook owned by the calling tenant." },
318
+ args: {
319
+ ...GLOBAL_FLAGS,
320
+ id: { type: "positional", description: "Webhook id (wh_\u2026)." }
321
+ },
322
+ async run({ args }) {
323
+ const flags = args;
324
+ const cfg = await resolveEffectiveConfig({
325
+ apiKey: flags["api-key"],
326
+ baseUrl: flags["base-url"],
327
+ timeout: flags.timeout,
328
+ profile: flags.profile
329
+ });
330
+ if (!cfg.apiKey) {
331
+ process.stderr.write("error: no API key \u2014 run `curviate login` or pass --api-key.\n");
332
+ process.exit(3);
333
+ }
334
+ const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
335
+ const out = buildOutputStreams();
336
+ await runWebhookGet(client, flags, out);
337
+ }
338
+ });
305
339
  var webhookUpdateCommand = defineCommand({
306
340
  meta: { name: "update", description: "Update a webhook in place (source is immutable)." },
307
341
  args: {
@@ -423,6 +457,7 @@ var webhookCommand = defineCommand({
423
457
  create: webhookCreateCommand,
424
458
  list: webhookListCommand,
425
459
  events: webhookEventsCommand,
460
+ get: webhookGetCommand,
426
461
  update: webhookUpdateCommand,
427
462
  delete: webhookDeleteCommand,
428
463
  "state-diff": webhookStateDiffCommand,
@@ -430,7 +465,7 @@ var webhookCommand = defineCommand({
430
465
  },
431
466
  async run() {
432
467
  process.stderr.write(
433
- "Usage: curviate webhook <subcommand>\n create | list | events | update | delete | state-diff | verify\n"
468
+ "Usage: curviate webhook <subcommand>\n create | list | events | get | update | delete | state-diff | verify\n"
434
469
  );
435
470
  }
436
471
  });
@@ -438,6 +473,7 @@ export {
438
473
  runWebhookCreate,
439
474
  runWebhookDelete,
440
475
  runWebhookEvents,
476
+ runWebhookGet,
441
477
  runWebhookList,
442
478
  runWebhookStateDiff,
443
479
  runWebhookUpdate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curviate/cli",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "private": false,
5
5
  "description": "Official command-line interface for the Curviate API.",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "clean": "rm -rf dist *.tsbuildinfo"
41
41
  },
42
42
  "dependencies": {
43
- "@curviate/sdk": "^0.12.0",
43
+ "@curviate/sdk": "^0.14.0",
44
44
  "citty": "^0.1.6",
45
45
  "open": "^10.1.0"
46
46
  },