@usesocial/cli 0.11.5 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @usesocial/cli
2
2
 
3
+ ## 0.11.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#60](https://github.com/usesocial/monorepo/pull/60) [`7917857`](https://github.com/usesocial/monorepo/commit/7917857776089ed80dbbd03d7238941a4d95373f) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Fix account-connect billing handoffs so first paid-seat connections create checkout instead of updating a missing subscription.
8
+
9
+ ## 0.11.6
10
+
11
+ ### Patch Changes
12
+
13
+ - [#59](https://github.com/usesocial/monorepo/pull/59) [`d6f5807`](https://github.com/usesocial/monorepo/commit/d6f58074561524aff14fccb831785fcbd31b4216) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Bind X account connect links to the authenticated CLI user so copied OAuth URLs cannot attach to the wrong usesocial web session.
14
+
3
15
  ## 0.11.5
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -142,14 +142,14 @@ profile lookup is needed.
142
142
  | `(bare)` | Show who is signed in and list connected accounts. | |
143
143
  | `login` | Device sign-in, scope selection, and billing. | |
144
144
  | `logout` | Revoke the session and clear the saved token. | |
145
- | `connect linkedin` | Open or print a LinkedIn connection URL. | write |
146
- | `connect x` | Open or print an X OAuth URL. | write |
145
+ | `connect linkedin` | Print a LinkedIn connection URL and poll for approval. | write |
146
+ | `connect x` | Print an X OAuth URL and poll for approval. | write |
147
147
  | `reconnect linkedin <account>` | Re-auth an existing LinkedIn account. | write |
148
148
  | `reconnect x <account>` | Re-auth an existing X account. | write |
149
149
  | `disconnect linkedin <account>` | Disconnect a LinkedIn account. | write |
150
150
  | `disconnect x <account>` | Disconnect an X account. | write |
151
151
  | `billing` | Show seats, subscription, and current usage billing. | |
152
- | `billing portal` | Open the hosted billing portal and print its URL. | |
152
+ | `billing portal` | Print the hosted billing portal URL. | |
153
153
  | `usage` | Aggregate proxy calls and usage. | |
154
154
  | `logs` | Recent proxy calls. | |
155
155
  | `config account` | Read or set the default connected account selector. | local |
package/dist/index.mjs CHANGED
@@ -15,7 +15,6 @@ import l__default, { createInterface } from "node:readline";
15
15
  import { AsyncLocalStorage } from "node:async_hooks";
16
16
  import process$1, { stdin, stdout } from "node:process";
17
17
  import { ReadStream } from "node:tty";
18
- import { spawn } from "node:child_process";
19
18
  //#region \0rolldown/runtime.js
20
19
  var __create = Object.create;
21
20
  var __defProp = Object.defineProperty;
@@ -8204,7 +8203,7 @@ async function resolvePlugins(plugins) {
8204
8203
  function defineCommand(def) {
8205
8204
  return def;
8206
8205
  }
8207
- async function runCommand$1(cmd, opts) {
8206
+ async function runCommand(cmd, opts) {
8208
8207
  const cmdArgs = await resolveValue$1(cmd.args || {});
8209
8208
  const parsedArgs = parseArgs$1(opts.rawArgs, cmdArgs);
8210
8209
  const context = {
@@ -8226,14 +8225,14 @@ async function runCommand$1(cmd, opts) {
8226
8225
  if (explicitName) {
8227
8226
  const subCommand = await _findSubCommand(subCommands, explicitName);
8228
8227
  if (!subCommand) throw new CLIError(`Unknown command ${cyan(explicitName)}`, "E_UNKNOWN_COMMAND");
8229
- await runCommand$1(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
8228
+ await runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
8230
8229
  } else {
8231
8230
  const defaultSubCommand = await resolveValue$1(cmd.default);
8232
8231
  if (defaultSubCommand) {
8233
8232
  if (cmd.run) throw new CLIError(`Cannot specify both 'run' and 'default' on the same command.`, "E_DEFAULT_CONFLICT");
8234
8233
  const subCommand = await _findSubCommand(subCommands, defaultSubCommand);
8235
8234
  if (!subCommand) throw new CLIError(`Default sub command ${cyan(defaultSubCommand)} not found in subCommands.`, "E_UNKNOWN_COMMAND");
8236
- await runCommand$1(subCommand, { rawArgs: opts.rawArgs });
8235
+ await runCommand(subCommand, { rawArgs: opts.rawArgs });
8237
8236
  } else if (!cmd.run) throw new CLIError(`No command specified.`, "E_NO_COMMAND");
8238
8237
  }
8239
8238
  }
@@ -21509,15 +21508,9 @@ const LINKEDIN_PLATFORM = "linkedin";
21509
21508
  const DEFAULT_POLL_INTERVAL_MS = 2e3;
21510
21509
  const DEFAULT_POLL_TIMEOUT_MS = 12e4;
21511
21510
  const SEAT_POLL_TIMEOUT_MS$2 = 600 * 1e3;
21512
- const isInteractiveTerminal$1 = (deps) => deps.isInteractiveTerminal?.() ?? process.stdout.isTTY === true;
21513
- const openURL = async (deps, url, options = {}) => {
21511
+ const surfaceURL$1 = (deps, url, options = {}) => {
21514
21512
  const log = deps.log ?? console.error;
21515
- if (options.log ?? true) log(`Opening ${url}`);
21516
- if (!(isInteractiveTerminal$1(deps) && deps.openURL)) return {
21517
- opened: false,
21518
- url
21519
- };
21520
- return await Promise.resolve(deps.openURL(url));
21513
+ if (options.log ?? true) log(`Open this URL: ${url}`);
21521
21514
  };
21522
21515
  const pollForSeat$2 = async (deps) => {
21523
21516
  const intervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -21533,7 +21526,7 @@ const prepareAccountConnect$1 = async (deps) => {
21533
21526
  if ((await deps.client.billing.status()).canConnect) return;
21534
21527
  const status = await deps.client.billing.prepareAccountConnect();
21535
21528
  if (status.canConnect) return;
21536
- if (status.paymentURL) await openURL(deps, status.paymentURL);
21529
+ if (status.paymentURL) surfaceURL$1(deps, status.paymentURL);
21537
21530
  await pollForSeat$2(deps);
21538
21531
  };
21539
21532
  const accountUsername = (account) => account.username.replace(/^@+/, "");
@@ -21583,7 +21576,7 @@ const connectLinkedinAccount = async (deps) => {
21583
21576
  platform: LINKEDIN_PLATFORM,
21584
21577
  mode: "create"
21585
21578
  })).url);
21586
- await openURL(deps, authURL.url, { log: !authURL.openingLogged });
21579
+ surfaceURL$1(deps, authURL.url, { log: !authURL.openingLogged });
21587
21580
  return {
21588
21581
  platform: LINKEDIN_PLATFORM,
21589
21582
  status: "connected",
@@ -21597,7 +21590,7 @@ const reconnectLinkedinAccount = async (deps, identifier) => {
21597
21590
  mode: "reconnect",
21598
21591
  account: reconnectCommandTargetFor(account)
21599
21592
  })).url);
21600
- await openURL(deps, authURL.url, { log: !authURL.openingLogged });
21593
+ surfaceURL$1(deps, authURL.url, { log: !authURL.openingLogged });
21601
21594
  return {
21602
21595
  platform: LINKEDIN_PLATFORM,
21603
21596
  status: "connected",
@@ -21669,20 +21662,16 @@ const throwXLifecycleError = (error) => {
21669
21662
  const defaultSleep = async (ms) => {
21670
21663
  await new Promise((resolve) => setTimeout(resolve, ms));
21671
21664
  };
21672
- const resolveWebBaseURL = async (deps) => {
21673
- return (typeof deps.webBaseURL === "function" ? await deps.webBaseURL() : deps.webBaseURL).replace(/\/+$/, "");
21674
- };
21675
21665
  const connectURLFor = async (deps, reconnectProfileId) => {
21676
- const url = createURL(await resolveWebBaseURL(deps), "/connect/x/start");
21677
- if (reconnectProfileId) url.searchParams.set("reconnect", reconnectProfileId);
21678
- return url.toString();
21666
+ return (await deps.client.accounts.reconnectURL({
21667
+ platform: "x",
21668
+ mode: reconnectProfileId ? "reconnect" : "create",
21669
+ ...reconnectProfileId ? { account: `profile_id:${reconnectProfileId}` } : {}
21670
+ })).url;
21679
21671
  };
21680
- const isInteractiveTerminal = (deps) => deps.isInteractiveTerminal?.() ?? process.stdout.isTTY === true;
21681
- const openOrPrint = async (deps, url, options = {}) => {
21672
+ const surfaceURL = (deps, url, options = {}) => {
21682
21673
  const log = deps.log ?? console.error;
21683
- if (options.log ?? true) log(`Opening ${url}`);
21684
- if (!(isInteractiveTerminal(deps) && deps.openBrowser)) return;
21685
- await deps.openBrowser(url);
21674
+ if (options.log ?? true) log(`Open this URL: ${url}`);
21686
21675
  };
21687
21676
  const pollForAccount = async (deps, operation, matches) => {
21688
21677
  const sleep = deps.sleep ?? defaultSleep;
@@ -21719,7 +21708,7 @@ const prepareAccountConnect = async (deps) => {
21719
21708
  if ((await deps.client.billing.status()).canConnect) return G.ok();
21720
21709
  const status = await deps.client.billing.prepareAccountConnect();
21721
21710
  if (status.canConnect) return G.ok();
21722
- if (status.paymentURL) await openOrPrint(deps, status.paymentURL);
21711
+ if (status.paymentURL) surfaceURL(deps, status.paymentURL);
21723
21712
  return pollForSeat$1(deps);
21724
21713
  };
21725
21714
  const accountKey = (account) => account.profileId;
@@ -21768,7 +21757,7 @@ const connectXAccount = async (deps) => {
21768
21757
  const prepared = await prepareAccountConnect(deps);
21769
21758
  if (G.isError(prepared)) return G.err(prepared.error);
21770
21759
  const authURL = await authURLFor(deps, () => connectURLFor(deps));
21771
- await openOrPrint(deps, authURL.url, { log: !authURL.openingLogged });
21760
+ surfaceURL(deps, authURL.url, { log: !authURL.openingLogged });
21772
21761
  const account = await pollForAccount(deps, "connect", (candidate) => {
21773
21762
  if (candidate.status !== "connected") return false;
21774
21763
  if (!connectedBefore.has(candidate.profileId)) return true;
@@ -21795,7 +21784,7 @@ const reconnectXAccount = async (deps, args) => {
21795
21784
  account: args.account
21796
21785
  }));
21797
21786
  const authURL = await authURLFor(deps, () => connectURLFor(deps, existing.profileId));
21798
- await openOrPrint(deps, authURL.url, { log: !authURL.openingLogged });
21787
+ surfaceURL(deps, authURL.url, { log: !authURL.openingLogged });
21799
21788
  const account = await pollForAccount(deps, "reconnect", (candidate) => candidate.profileId === existing.profileId && candidate.status === "connected");
21800
21789
  if (G.isError(account)) return G.err(account.error);
21801
21790
  return G.ok({
@@ -23183,7 +23172,7 @@ function createEnv(opts) {
23183
23172
  }
23184
23173
  //#endregion
23185
23174
  //#region package.json
23186
- var version$1 = "0.11.5";
23175
+ var version$1 = "0.11.7";
23187
23176
  //#endregion
23188
23177
  //#region src/lib/env.ts
23189
23178
  const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
@@ -24378,22 +24367,21 @@ const createBillingCommand = (deps = {}) => {
24378
24367
  const portal = defineCommand({
24379
24368
  meta: commandMeta({
24380
24369
  name: "portal",
24381
- description: "Open the hosted billing portal.",
24370
+ description: "Print the hosted billing portal URL.",
24382
24371
  capability: "read",
24383
24372
  contract: readCLIContract({
24384
24373
  outputSchema: BillingPortalOutput,
24385
24374
  hazards: [{
24386
24375
  code: "billing_portal_handoff",
24387
- message: "Hosted billing portal handoff; billing changes happen outside the CLI after the URL opens."
24376
+ message: "Hosted billing portal handoff; billing changes happen outside the CLI after the URL is opened."
24388
24377
  }]
24389
24378
  })
24390
24379
  }),
24391
24380
  run: async () => {
24392
24381
  const { url } = await clientFrom(deps).billing.portalURL();
24393
- const result = deps.isInteractiveTerminal?.() === true && deps.openURL !== void 0 ? await deps.openURL?.(url) : void 0;
24394
24382
  await writeOutput$2(deps, {
24395
- url: result?.url ?? url,
24396
- opened: result?.opened === true
24383
+ url,
24384
+ opened: false
24397
24385
  });
24398
24386
  }
24399
24387
  });
@@ -30435,85 +30423,6 @@ const scopePhase = async (ctx) => {
30435
30423
  };
30436
30424
  };
30437
30425
  //#endregion
30438
- //#region src/lib/browser.ts
30439
- const WSL_VERSION = /microsoft|wsl/i;
30440
- const BROWSER_TARGET_TIMEOUT_MS = 5e3;
30441
- const LOCAL_HOSTS = new Set([
30442
- "localhost",
30443
- "127.0.0.1",
30444
- "::1"
30445
- ]);
30446
- const displayURL = (url) => {
30447
- try {
30448
- const parsed = new URL(url);
30449
- parsed.searchParams.delete("token");
30450
- parsed.hash = "";
30451
- return parsed.toString();
30452
- } catch {
30453
- return url;
30454
- }
30455
- };
30456
- const hintFor = (url) => LOCAL_HOSTS.has(url.hostname) ? "Start the web app or local worker, or set SOCIAL_API_URL to your deployed social API." : "Check SOCIAL_API_URL and your connection, then try again.";
30457
- const assertBrowserTargetReachable = async (url) => {
30458
- let parsed;
30459
- try {
30460
- parsed = new URL(url);
30461
- } catch {
30462
- return;
30463
- }
30464
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
30465
- if (!LOCAL_HOSTS.has(parsed.hostname)) return;
30466
- const controller = new AbortController();
30467
- const timeout = setTimeout(() => controller.abort(), BROWSER_TARGET_TIMEOUT_MS);
30468
- const target = parsed.origin;
30469
- try {
30470
- const response = await fetch(target, {
30471
- method: "GET",
30472
- redirect: "manual",
30473
- signal: controller.signal
30474
- });
30475
- if (response.status >= 400) throw new Error(`social is reachable at ${displayURL(target)}, but it returned ${response.status}. Try again in a moment.`);
30476
- } catch (error) {
30477
- if (error instanceof Error && error.message.startsWith("social is reachable")) throw error;
30478
- throw new Error(`Can't reach ${displayURL(target)}. ${hintFor(parsed)}`);
30479
- } finally {
30480
- clearTimeout(timeout);
30481
- }
30482
- };
30483
- const runCommand = async (command) => await new Promise((resolve) => {
30484
- const [file, ...args] = command;
30485
- if (!file) {
30486
- resolve(false);
30487
- return;
30488
- }
30489
- const proc = spawn(file, args, { stdio: "ignore" });
30490
- proc.once("error", () => resolve(false));
30491
- proc.once("exit", (code) => resolve(code === 0));
30492
- });
30493
- const isWSL = async () => {
30494
- if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) return true;
30495
- const version = await readFile("/proc/version", "utf8").catch(() => "");
30496
- return WSL_VERSION.test(version);
30497
- };
30498
- const openBrowser = async (url) => {
30499
- await assertBrowserTargetReachable(url);
30500
- if (process.platform === "darwin") return await runCommand(["open", url]);
30501
- if (process.platform === "win32") return await runCommand([
30502
- "cmd",
30503
- "/c",
30504
- "start",
30505
- url
30506
- ]);
30507
- if (await isWSL()) return await runCommand(["wslview", url]) || await runCommand([
30508
- "powershell.exe",
30509
- "-NoProfile",
30510
- "-Command",
30511
- "Start-Process",
30512
- url
30513
- ]);
30514
- return await runCommand(["xdg-open", url]);
30515
- };
30516
- //#endregion
30517
30426
  //#region src/login/phases/2.seat.ts
30518
30427
  const SEAT_BILLING_DETAILS_URL = publicRouteURLs.pricing;
30519
30428
  const SEAT_PRICING_MESSAGE = `${productBrand.name} costs $20/month/account + usage. Full details at ${SEAT_BILLING_DETAILS_URL}. Continue?`;
@@ -30535,7 +30444,6 @@ const pollForSeat = async (ctx, deps = {}) => {
30535
30444
  throw new Error("Billing checkout did not complete within 30 minutes. Finish checkout in the browser, then run `social account login` again.");
30536
30445
  };
30537
30446
  const runSeatPhase = async (ctx, deps = {}) => {
30538
- const openBrowser$1 = deps.openBrowser ?? openBrowser;
30539
30447
  const status = await ctx.client.billing.status();
30540
30448
  if (seatAvailable(status)) return {
30541
30449
  status: "done",
@@ -30551,7 +30459,6 @@ const runSeatPhase = async (ctx, deps = {}) => {
30551
30459
  };
30552
30460
  const checkout = await ctx.client.billing.checkoutURL({ returnPath: SEAT_CHECKOUT_RETURN_PATH });
30553
30461
  ctx.ui.info(checkout.url);
30554
- await openBrowser$1(checkout.url);
30555
30462
  return {
30556
30463
  status: "done",
30557
30464
  data: await ctx.ui.spinElapsed("Waiting for billing checkout", () => pollForSeat(ctx, deps), "Billing ready")
@@ -31329,7 +31236,7 @@ const lifecycleDepsWithTextLogs = (deps) => ({
31329
31236
  spinner.start(authHandshakeMessage);
31330
31237
  try {
31331
31238
  const url = await operation();
31332
- spinner.stop(`Opening ${url}`);
31239
+ spinner.stop(`Open this URL: ${url}`);
31333
31240
  return url;
31334
31241
  } catch (error) {
31335
31242
  spinner.error(authHandshakeMessage);
@@ -31338,7 +31245,7 @@ const lifecycleDepsWithTextLogs = (deps) => ({
31338
31245
  }
31339
31246
  writeText(deps, authHandshakeMessage);
31340
31247
  const url = await operation();
31341
- writeText(deps, `Opening ${url}`);
31248
+ writeText(deps, `Open this URL: ${url}`);
31342
31249
  return url;
31343
31250
  }
31344
31251
  });
@@ -31363,7 +31270,7 @@ const runConnectStep = async (deps, platform, listConnected, connectURL) => {
31363
31270
  return;
31364
31271
  }
31365
31272
  const url = await connectURL();
31366
- writeText(deps, `Opening ${url}`);
31273
+ writeText(deps, `Open this URL: ${url}`);
31367
31274
  await writeOutput(deps, {
31368
31275
  status: "pending_approval",
31369
31276
  platform,
@@ -36704,6 +36611,7 @@ const createCLIDeps = () => {
36704
36611
  disconnectAccount: client.accounts.disconnect,
36705
36612
  accounts: {
36706
36613
  list: client.accounts.list,
36614
+ reconnectURL: client.accounts.reconnectURL,
36707
36615
  disconnect: client.accounts.disconnect
36708
36616
  },
36709
36617
  billing: {
@@ -36794,17 +36702,8 @@ const createCLIDeps = () => {
36794
36702
  providerId: profile.id
36795
36703
  };
36796
36704
  },
36797
- openBrowser,
36798
36705
  isInteractiveTerminal: () => process.stdout.isTTY === true,
36799
36706
  webBaseURL: () => env.SOCIAL_WEB_URL,
36800
- openURL: async (url) => {
36801
- const opened = await openBrowser(url);
36802
- if (!opened) console.error(url);
36803
- return {
36804
- opened,
36805
- url
36806
- };
36807
- },
36808
36707
  writeOutput: printData,
36809
36708
  ui: createUI(process.stdout.isTTY === true)
36810
36709
  };
@@ -37489,7 +37388,7 @@ const runSocial = async ({ command = createSocialCommand(), now = Date.now, rawA
37489
37388
  try {
37490
37389
  const loginArgError = loginExtraArgError(rawArgs, path);
37491
37390
  if (loginArgError) throw loginArgError;
37492
- await runCommand$1(command, { rawArgs });
37391
+ await runCommand(command, { rawArgs });
37493
37392
  } catch (error) {
37494
37393
  if (error instanceof SilentExit) return await finish(error.code, telemetry, now);
37495
37394
  if (isCittyUsageError(error) && process.stderr.isTTY) await printUsage(command, rawArgs);