@usesocial/cli 0.11.0 → 0.11.2

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.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#46](https://github.com/usesocial/monorepo/pull/46) [`88f7b42`](https://github.com/usesocial/monorepo/commit/88f7b42abaec62175fb6ca030e301c8497601901) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Fix `social account connect x` so it finishes when OAuth refreshes an already-connected X account instead of waiting until timeout.
8
+
9
+ ## 0.11.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#45](https://github.com/usesocial/monorepo/pull/45) [`940ff35`](https://github.com/usesocial/monorepo/commit/940ff356c07cd1f1a01be059b022eafcb5a82a1b) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Create the server-side CLI session grant after non-TTY device login approval so read/write device sessions are not reported locally as write-capable while the API still treats them as read-only.
14
+
3
15
  ## 0.11.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.mjs CHANGED
@@ -18702,6 +18702,7 @@ const prepareAccountConnect = async (deps) => {
18702
18702
  await pollForSeat$1(deps);
18703
18703
  };
18704
18704
  const accountKey = (account) => account.profileId;
18705
+ const timestampMS = (value) => value instanceof Date ? value.getTime() : void 0;
18705
18706
  const normalizeIdentifier = (value) => value.trim().replace(/^@+/, "").toLowerCase();
18706
18707
  const accountSelectorFor$1 = (account) => account.username ? `@${account.username.replace(/^@+/, "")}` : `profile_id:${account.profileId}`;
18707
18708
  const assertXAccountSelector = (target) => {
@@ -18733,13 +18734,20 @@ const createXConnectURL = (deps, reconnectProfileId) => connectURLFor(deps, reco
18733
18734
  const connectXAccount = async (deps) => {
18734
18735
  const before = await listXLifecycleAccounts(deps, true);
18735
18736
  const connectedBefore = new Set(before.filter((account) => account.status === "connected").map(accountKey));
18737
+ const lastSeenBefore = new Map(before.filter((account) => account.status === "connected").map((account) => [accountKey(account), timestampMS(account.lastSeenAt)]));
18736
18738
  await prepareAccountConnect(deps);
18737
18739
  const authURL = await authURLFor(deps, () => connectURLFor(deps));
18738
18740
  await openOrPrint(deps, authURL.url, { log: !authURL.openingLogged });
18739
18741
  return {
18740
18742
  platform: "x",
18741
18743
  status: "connected",
18742
- account: await pollForAccount(deps, (candidate) => candidate.status === "connected" && !connectedBefore.has(candidate.profileId))
18744
+ account: await pollForAccount(deps, (candidate) => {
18745
+ if (candidate.status !== "connected") return false;
18746
+ if (!connectedBefore.has(candidate.profileId)) return true;
18747
+ const previousLastSeen = lastSeenBefore.get(candidate.profileId);
18748
+ const currentLastSeen = timestampMS(candidate.lastSeenAt);
18749
+ return previousLastSeen !== void 0 && currentLastSeen !== void 0 && currentLastSeen > previousLastSeen;
18750
+ })
18743
18751
  };
18744
18752
  };
18745
18753
  const reconnectXAccount = async (deps, args) => {
@@ -21414,7 +21422,7 @@ function createEnv(opts) {
21414
21422
  }
21415
21423
  //#endregion
21416
21424
  //#region package.json
21417
- var version$1 = "0.11.0";
21425
+ var version$1 = "0.11.2";
21418
21426
  //#endregion
21419
21427
  //#region src/lib/env.ts
21420
21428
  const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
@@ -28783,13 +28791,16 @@ const errorOutput = (error) => ({
28783
28791
  const isInteractiveTerminal = () => process.stdin.isTTY === true && process.stdout.isTTY === true;
28784
28792
  const nextCommands = ["social account connect linkedin", "social account connect x"];
28785
28793
  const deviceExpiredMessage = "Sign-in expired before approval. Run `social account login` again to restart.";
28794
+ const scopeUpgradeRequiresReauthorization = "Existing CLI session is read-only. Run `social account logout`, then `social account login` to approve a new write-capable session.";
28795
+ const scopeAliasIncludes = (granted, requested) => granted === requested || granted === "read,write";
28786
28796
  /**
28787
28797
  * Drive one step of the non-TTY device-authorization login and map it onto the
28788
28798
  * login output union. The first call starts the device flow and returns
28789
28799
  * `pending_approval`; later calls poll until `logged_in` or `expired`.
28790
28800
  */
28791
- const deviceLoginOutput = async (loginArgs) => {
28792
- const state = await advanceDeviceLogin(parseScopeAlias(loginArgs.scope));
28801
+ const deviceLoginOutput = async (client, loginArgs) => {
28802
+ const scope = parseScopeAlias(loginArgs.scope);
28803
+ const state = await advanceDeviceLogin(scope);
28793
28804
  switch (state.status) {
28794
28805
  case "pending_approval": return {
28795
28806
  status: "pending_approval",
@@ -28801,7 +28812,10 @@ const deviceLoginOutput = async (loginArgs) => {
28801
28812
  };
28802
28813
  case "logged_in": return {
28803
28814
  status: "logged_in",
28804
- account: state.account,
28815
+ account: {
28816
+ ...state.account,
28817
+ scope: (await client.cli.session.upsert({ scopeAlias: scope })).scopeAlias
28818
+ },
28805
28819
  nextCommands
28806
28820
  };
28807
28821
  case "expired": return {
@@ -28891,23 +28905,30 @@ const loginCommand = defineCommand({
28891
28905
  try {
28892
28906
  const client = createORPCAPIClient();
28893
28907
  const existing = await readExistingAuthSession(client);
28894
- if (existing.kind === "existing") if (billingReady(await client.billing.status())) output = alreadyLoggedInOutput(existing.session);
28895
- else if (isInteractiveTerminal()) output = await runLogin({
28896
- ui: createUI(true),
28897
- client,
28898
- args: loginArgs
28899
- }, [{
28900
- name: "seat",
28901
- run: seatPhase
28902
- }], { authenticated: true });
28903
- else output = alreadyLoggedInOutput(existing.session);
28904
- else if (existing.kind === "offline") output = offlineOutput();
28908
+ if (existing.kind === "existing") {
28909
+ const requestedScope = parseScopeAlias(loginArgs.scope);
28910
+ if (!scopeAliasIncludes(existing.session.session.scope, requestedScope)) output = {
28911
+ status: "error",
28912
+ code: "scope_upgrade_requires_reauthorization",
28913
+ message: scopeUpgradeRequiresReauthorization
28914
+ };
28915
+ else if (billingReady(await client.billing.status())) output = alreadyLoggedInOutput(existing.session);
28916
+ else if (isInteractiveTerminal()) output = await runLogin({
28917
+ ui: createUI(true),
28918
+ client,
28919
+ args: loginArgs
28920
+ }, [{
28921
+ name: "seat",
28922
+ run: seatPhase
28923
+ }], { authenticated: true });
28924
+ else output = alreadyLoggedInOutput(existing.session);
28925
+ } else if (existing.kind === "offline") output = offlineOutput();
28905
28926
  else if (isInteractiveTerminal()) output = await runLogin({
28906
28927
  ui: createUI(true),
28907
28928
  client,
28908
28929
  args: loginArgs
28909
28930
  });
28910
- else output = await deviceLoginOutput(loginArgs);
28931
+ else output = await deviceLoginOutput(client, loginArgs);
28911
28932
  } catch (error) {
28912
28933
  output = errorOutput(error);
28913
28934
  exitCode = exitCodeFor(error);