@usesocial/cli 0.2.0 → 0.2.1

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,11 @@
1
1
  # @usesocial/cli
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Improve account connection progress output and hosted auth resilience.
8
+
3
9
  ## 0.2.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -188,9 +188,10 @@ jq -r '.items[].id' /tmp/hiring-posts.json \
188
188
  1. Asks what access to grant the agent. Read and Write are both selected by
189
189
  default; clear Write in the prompt to grant read-only access.
190
190
  2. Asks for your email address.
191
- 3. Shows you a verification code and approval URL, then tries to open that URL
192
- in your browser.
193
- 4. Waits until you approve the session in the browser.
191
+ 3. Sends a magic link to that email with the device approval screen already
192
+ attached.
193
+ 4. Waits until you click the magic link and approve the CLI session in the
194
+ browser.
194
195
  5. Confirms billing checkout in the terminal when a seat is needed.
195
196
  6. Stores the returned token in your OS keyring, falling back to
196
197
  `~/.social/credentials.json` (mode `0600`) when no keyring is available.
package/bin/social.mjs CHANGED
@@ -1,17 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync } from "node:fs";
3
2
  import { dirname, join } from "node:path";
4
- import { loadEnvFile } from "node:process";
5
3
  import { fileURLToPath, pathToFileURL } from "node:url";
6
4
 
7
5
  const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
- const localEnvPath = join(packageRoot, "..", "..", ".env.staging");
9
6
  const entryURL = pathToFileURL(join(packageRoot, "dist", "index.mjs")).href;
10
7
 
11
- if (existsSync(localEnvPath)) {
12
- loadEnvFile(localEnvPath);
13
- }
14
-
15
8
  import(entryURL).catch((error) => {
16
9
  console.error(error);
17
10
  process.exit(1);
package/dist/index.mjs CHANGED
@@ -17920,17 +17920,12 @@ const LEADING_AT_PATTERN$1 = /^@+/;
17920
17920
  const LINKEDIN_PROFILE_HOST_PATTERN = /(^|\.)linkedin\.com$/i;
17921
17921
  const isInteractiveTerminal$1 = (deps) => deps.isInteractiveTerminal?.() ?? process.stdout.isTTY === true;
17922
17922
  const openURL = async (deps, url) => {
17923
- const log = deps.log ?? console.error;
17924
- if (!(isInteractiveTerminal$1(deps) && deps.openURL)) {
17925
- log(url);
17926
- return {
17927
- opened: false,
17928
- url
17929
- };
17930
- }
17931
- const handoff = await Promise.resolve(deps.openURL(url));
17932
- if (!handoff.opened) log(url);
17933
- return handoff;
17923
+ (deps.log ?? console.error)(`Opening ${url}`);
17924
+ if (!(isInteractiveTerminal$1(deps) && deps.openURL)) return {
17925
+ opened: false,
17926
+ url
17927
+ };
17928
+ return await Promise.resolve(deps.openURL(url));
17934
17929
  };
17935
17930
  const pollForSeat$2 = async (deps) => {
17936
17931
  const intervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -18084,12 +18079,9 @@ const connectURLFor = async (deps, reconnectProfileId) => {
18084
18079
  };
18085
18080
  const isInteractiveTerminal = (deps) => deps.isInteractiveTerminal?.() ?? process.stdout.isTTY === true;
18086
18081
  const openOrPrint = async (deps, url) => {
18087
- const log = deps.log ?? console.error;
18088
- if (!(isInteractiveTerminal(deps) && deps.openBrowser)) {
18089
- log(url);
18090
- return;
18091
- }
18092
- if (await deps.openBrowser(url) === false) log(url);
18082
+ (deps.log ?? console.error)(`Opening ${url}`);
18083
+ if (!(isInteractiveTerminal(deps) && deps.openBrowser)) return;
18084
+ await deps.openBrowser(url);
18093
18085
  };
18094
18086
  const pollForAccount = async (deps, matches) => {
18095
18087
  const sleep = deps.sleep ?? defaultSleep;
@@ -20775,7 +20767,7 @@ const env = createEnv({
20775
20767
  }
20776
20768
  });
20777
20769
  const SERVICE_NAME = "social-cli";
20778
- const VERSION = "0.2.0";
20770
+ const VERSION = "0.2.1";
20779
20771
  const apiURL = (path) => createAbsoluteURL(env.SOCIAL_API_URL, path);
20780
20772
  //#endregion
20781
20773
  //#region src/lib/bearer.ts
@@ -21131,6 +21123,9 @@ const writeStdoutBuffer = (buffer) => {
21131
21123
  const writeStdout = (value) => {
21132
21124
  writeStdoutBuffer(Buffer.from(`${value}\n`));
21133
21125
  };
21126
+ const printLine = (value) => {
21127
+ writeStdout(value);
21128
+ };
21134
21129
  const printData = (value) => {
21135
21130
  writeStdout(JSON.stringify(value));
21136
21131
  };
@@ -27067,96 +27062,21 @@ const accessPhase = async (ctx) => {
27067
27062
  };
27068
27063
  };
27069
27064
  //#endregion
27070
- //#region src/lib/browser.ts
27071
- const WSL_VERSION = /microsoft|wsl/i;
27072
- const BROWSER_TARGET_TIMEOUT_MS = 5e3;
27073
- const LOCAL_HOSTS = new Set([
27074
- "localhost",
27075
- "127.0.0.1",
27076
- "::1"
27077
- ]);
27078
- const displayURL = (url) => {
27079
- try {
27080
- const parsed = new URL(url);
27081
- parsed.searchParams.delete("token");
27082
- parsed.hash = "";
27083
- return parsed.toString();
27084
- } catch {
27085
- return url;
27086
- }
27087
- };
27088
- 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.";
27089
- const assertBrowserTargetReachable = async (url) => {
27090
- let parsed;
27091
- try {
27092
- parsed = new URL(url);
27093
- } catch {
27094
- return;
27095
- }
27096
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
27097
- if (!LOCAL_HOSTS.has(parsed.hostname)) return;
27098
- const controller = new AbortController();
27099
- const timeout = setTimeout(() => controller.abort(), BROWSER_TARGET_TIMEOUT_MS);
27100
- const target = parsed.origin;
27101
- try {
27102
- const response = await fetch(target, {
27103
- method: "GET",
27104
- redirect: "manual",
27105
- signal: controller.signal
27106
- });
27107
- if (response.status >= 400) throw new Error(`social is reachable at ${displayURL(target)}, but it returned ${response.status}. Try again in a moment.`);
27108
- } catch (error) {
27109
- if (error instanceof Error && error.message.startsWith("social is reachable")) throw error;
27110
- throw new Error(`Can't reach ${displayURL(target)}. ${hintFor(parsed)}`);
27111
- } finally {
27112
- clearTimeout(timeout);
27113
- }
27114
- };
27115
- const runCommand = async (command) => await new Promise((resolve) => {
27116
- const [file, ...args] = command;
27117
- if (!file) {
27118
- resolve(false);
27119
- return;
27120
- }
27121
- const proc = spawn(file, args, { stdio: "ignore" });
27122
- proc.once("error", () => resolve(false));
27123
- proc.once("exit", (code) => resolve(code === 0));
27124
- });
27125
- const isWSL = async () => {
27126
- if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) return true;
27127
- const version = await readFile("/proc/version", "utf8").catch(() => "");
27128
- return WSL_VERSION.test(version);
27129
- };
27130
- const openBrowser = async (url) => {
27131
- await assertBrowserTargetReachable(url);
27132
- if (process.platform === "darwin") return await runCommand(["open", url]);
27133
- if (process.platform === "win32") return await runCommand([
27134
- "cmd",
27135
- "/c",
27136
- "start",
27137
- url
27138
- ]);
27139
- if (await isWSL()) return await runCommand(["wslview", url]) || await runCommand([
27140
- "powershell.exe",
27141
- "-NoProfile",
27142
- "-Command",
27143
- "Start-Process",
27144
- url
27145
- ]);
27146
- return await runCommand(["xdg-open", url]);
27147
- };
27148
- //#endregion
27149
27065
  //#region src/login/phases/0.sign-in.ts
27150
27066
  const CLIENT_ID = "social-cli";
27151
27067
  const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
27068
+ const MAGIC_LINK_PATH = "/api/auth/sign-in/magic-link";
27152
27069
  const TERMS_URL = new URL(siteConfig.links.terms, siteConfig.publicWebURL).toString();
27070
+ const LOGIN_SUCCESS_MESSAGE = "Logged in! 🎉";
27071
+ const NEXT_COMMANDS_MESSAGE = "Now run `social account connect linkedin` or `social account connect x`.";
27153
27072
  const formatUserCode = (code) => `${code.slice(0, 4)}-${code.slice(4)}`;
27154
27073
  const isRecord$2 = (value) => typeof value === "object" && value !== null;
27155
- const isAuthErrorResponse = (data) => isRecord$2(data) && (typeof data.error === "string" || typeof data.error_description === "string");
27074
+ const isAuthErrorResponse = (data) => isRecord$2(data) && (typeof data.error === "string" || typeof data.error_description === "string" || typeof data.message === "string");
27156
27075
  const isDeviceCodeResponse = (data) => isRecord$2(data) && typeof data.device_code === "string" && typeof data.user_code === "string" && typeof data.verification_uri === "string" && typeof data.verification_uri_complete === "string" && typeof data.expires_in === "number" && (data.interval === void 0 || typeof data.interval === "number");
27157
27076
  const isDeviceTokenResponse = (data) => isRecord$2(data) && typeof data.access_token === "string" && (data.token_type === void 0 || typeof data.token_type === "string") && (data.expires_in === void 0 || typeof data.expires_in === "number") && (data.scope === void 0 || typeof data.scope === "string");
27077
+ const isMagicLinkResponse = (data) => isRecord$2(data) && data.status === true;
27158
27078
  const authErrorMessage = (fallback, response, data) => {
27159
- if (data?.error_description || data?.error) return data.error_description ?? data.error ?? fallback;
27079
+ if (data?.error_description || data?.error || data?.message) return data.error_description ?? data.error ?? data.message ?? fallback;
27160
27080
  const status = [response.status, response.statusText].filter(Boolean).join(" ");
27161
27081
  return status ? `${fallback}: ${status}` : fallback;
27162
27082
  };
@@ -27196,30 +27116,60 @@ const pollDeviceToken = async (deviceCode) => {
27196
27116
  }
27197
27117
  throw new Error("Device sign-in expired before approval.");
27198
27118
  };
27199
- const verificationURLFor = (url, email, webBaseURL = process.env.SOCIAL_WEB_URL) => {
27119
+ const magicLinkURLFor = (webBaseURL = env.SOCIAL_WEB_URL) => createAbsoluteURL(webBaseURL, MAGIC_LINK_PATH);
27120
+ const deviceCallbackURLFor = (deviceCode, email) => {
27121
+ let parsed;
27200
27122
  try {
27201
- const parsed = new URL(url);
27202
- const userCode = parsed.searchParams.get("user_code");
27203
- if (userCode) parsed.searchParams.set("user_code", formatUserCode(userCode));
27204
- if (webBaseURL) try {
27205
- const base = new URL(webBaseURL);
27206
- parsed.protocol = base.protocol;
27207
- parsed.host = base.host;
27208
- parsed.username = "";
27209
- parsed.password = "";
27210
- } catch {}
27211
- parsed.searchParams.set("email", email);
27212
- return parsed.toString();
27123
+ parsed = new URL(deviceCode.verification_uri_complete);
27213
27124
  } catch {
27214
- return url;
27125
+ parsed = new URL("/device", "https://social.local");
27126
+ }
27127
+ parsed.searchParams.set("user_code", formatUserCode(deviceCode.user_code));
27128
+ parsed.searchParams.set("email", email);
27129
+ return `${parsed.pathname}${parsed.search}`;
27130
+ };
27131
+ const sendMagicLinkSignIn = async (email, callbackURL, deps = {}) => {
27132
+ const response = await (deps.fetch ?? fetch)(magicLinkURLFor(deps.webBaseURL), {
27133
+ method: "POST",
27134
+ headers: {
27135
+ "content-type": "application/json",
27136
+ "x-social-surface": "cli"
27137
+ },
27138
+ body: JSON.stringify({
27139
+ email,
27140
+ callbackURL
27141
+ })
27142
+ });
27143
+ const data = await response.json().catch(() => void 0);
27144
+ if (!(response.ok && isMagicLinkResponse(data))) {
27145
+ const error = isAuthErrorResponse(data) ? data : void 0;
27146
+ throw new Error(authErrorMessage("Could not send sign-in email", response, error));
27147
+ }
27148
+ };
27149
+ const waitForDeviceToken = async (ctx, deviceCode) => {
27150
+ const spinner = ctx.ui.spinner();
27151
+ const message = "Waiting for magic link approval";
27152
+ spinner.start(message);
27153
+ const clearTick = spinner.withElapsed(message);
27154
+ try {
27155
+ const token = await pollDeviceToken(deviceCode);
27156
+ const tokenExpiresAt = token.expires_in ? Date.now() + token.expires_in * 1e3 : void 0;
27157
+ await writeCredentials({
27158
+ accessToken: token.access_token,
27159
+ tokenType: token.token_type,
27160
+ expiresAt: tokenExpiresAt,
27161
+ scope: token.scope
27162
+ });
27163
+ clearTick();
27164
+ spinner.stop(LOGIN_SUCCESS_MESSAGE);
27165
+ ctx.ui.info(NEXT_COMMANDS_MESSAGE);
27166
+ return token;
27167
+ } catch (error) {
27168
+ clearTick();
27169
+ spinner.error(message);
27170
+ throw error;
27215
27171
  }
27216
27172
  };
27217
- const onboardingLegalNotice = (termsURL = TERMS_URL) => `By signing up and connecting accounts, you accept the terms and conditions and use this software at your own risk.\nRead the terms: ${termsURL}`;
27218
- const resolveEmail = async (ctx) => await ctx.ui.text({
27219
- message: "Email",
27220
- placeholder: "you@example.com",
27221
- validate: (value) => value?.includes("@") ? void 0 : "Enter an email address."
27222
- });
27223
27173
  const signInPhase = async (ctx) => {
27224
27174
  const email = await resolveEmail(ctx);
27225
27175
  if (!email) return {
@@ -27229,27 +27179,25 @@ const signInPhase = async (ctx) => {
27229
27179
  };
27230
27180
  const deviceCode = await createDeviceCode(ctx.args.scope ?? "read");
27231
27181
  const expiresAt = new Date(Date.now() + deviceCode.expires_in * 1e3).toISOString();
27232
- const verificationURL = verificationURLFor(deviceCode.verification_uri_complete, email);
27182
+ const callbackURL = deviceCallbackURLFor(deviceCode, email);
27233
27183
  const userCode = formatUserCode(deviceCode.user_code);
27234
- ctx.ui.note(`Code: ${userCode}\nURL: ${verificationURL}\n\n${onboardingLegalNotice()}`, `Sign in as ${email}`);
27235
- await openBrowser(verificationURL);
27236
- const token = await ctx.ui.spinElapsed("Waiting for browser approval", () => pollDeviceToken(deviceCode), "Device approved");
27237
- const tokenExpiresAt = token.expires_in ? Date.now() + token.expires_in * 1e3 : void 0;
27238
- await writeCredentials({
27239
- accessToken: token.access_token,
27240
- tokenType: token.token_type,
27241
- expiresAt: tokenExpiresAt,
27242
- scope: token.scope
27243
- });
27184
+ await ctx.ui.spin("Sending sign-in email", () => sendMagicLinkSignIn(email, callbackURL), "Magic link sent");
27185
+ ctx.ui.note(`Code: ${userCode}\nEmail: ${email}\n\nClick the magic link in your inbox to approve this CLI session.\n\n${onboardingLegalNotice()}`, "Check your email");
27244
27186
  return {
27245
27187
  status: "done",
27246
27188
  data: {
27247
27189
  email,
27248
27190
  expiresAt,
27249
- scope: token.scope
27191
+ scope: (await waitForDeviceToken(ctx, deviceCode)).scope
27250
27192
  }
27251
27193
  };
27252
27194
  };
27195
+ const onboardingLegalNotice = (termsURL = TERMS_URL) => `By signing up and connecting accounts, you accept the terms and conditions and use this software at your own risk.\nRead the terms: ${termsURL}`;
27196
+ const resolveEmail = async (ctx) => await ctx.ui.text({
27197
+ message: "Email",
27198
+ placeholder: "you@example.com",
27199
+ validate: (value) => value?.includes("@") ? void 0 : "Enter an email address."
27200
+ });
27253
27201
  //#endregion
27254
27202
  //#region src/login/phases/1.scope.ts
27255
27203
  const DEFAULT_SCOPE_ALIAS = "read,write";
@@ -27268,6 +27216,85 @@ const scopePhase = async (ctx) => {
27268
27216
  data: await ctx.client.cli.session.upsert({ scopeAlias })
27269
27217
  };
27270
27218
  };
27219
+ //#endregion
27220
+ //#region src/lib/browser.ts
27221
+ const WSL_VERSION = /microsoft|wsl/i;
27222
+ const BROWSER_TARGET_TIMEOUT_MS = 5e3;
27223
+ const LOCAL_HOSTS = new Set([
27224
+ "localhost",
27225
+ "127.0.0.1",
27226
+ "::1"
27227
+ ]);
27228
+ const displayURL = (url) => {
27229
+ try {
27230
+ const parsed = new URL(url);
27231
+ parsed.searchParams.delete("token");
27232
+ parsed.hash = "";
27233
+ return parsed.toString();
27234
+ } catch {
27235
+ return url;
27236
+ }
27237
+ };
27238
+ 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.";
27239
+ const assertBrowserTargetReachable = async (url) => {
27240
+ let parsed;
27241
+ try {
27242
+ parsed = new URL(url);
27243
+ } catch {
27244
+ return;
27245
+ }
27246
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
27247
+ if (!LOCAL_HOSTS.has(parsed.hostname)) return;
27248
+ const controller = new AbortController();
27249
+ const timeout = setTimeout(() => controller.abort(), BROWSER_TARGET_TIMEOUT_MS);
27250
+ const target = parsed.origin;
27251
+ try {
27252
+ const response = await fetch(target, {
27253
+ method: "GET",
27254
+ redirect: "manual",
27255
+ signal: controller.signal
27256
+ });
27257
+ if (response.status >= 400) throw new Error(`social is reachable at ${displayURL(target)}, but it returned ${response.status}. Try again in a moment.`);
27258
+ } catch (error) {
27259
+ if (error instanceof Error && error.message.startsWith("social is reachable")) throw error;
27260
+ throw new Error(`Can't reach ${displayURL(target)}. ${hintFor(parsed)}`);
27261
+ } finally {
27262
+ clearTimeout(timeout);
27263
+ }
27264
+ };
27265
+ const runCommand = async (command) => await new Promise((resolve) => {
27266
+ const [file, ...args] = command;
27267
+ if (!file) {
27268
+ resolve(false);
27269
+ return;
27270
+ }
27271
+ const proc = spawn(file, args, { stdio: "ignore" });
27272
+ proc.once("error", () => resolve(false));
27273
+ proc.once("exit", (code) => resolve(code === 0));
27274
+ });
27275
+ const isWSL = async () => {
27276
+ if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) return true;
27277
+ const version = await readFile("/proc/version", "utf8").catch(() => "");
27278
+ return WSL_VERSION.test(version);
27279
+ };
27280
+ const openBrowser = async (url) => {
27281
+ await assertBrowserTargetReachable(url);
27282
+ if (process.platform === "darwin") return await runCommand(["open", url]);
27283
+ if (process.platform === "win32") return await runCommand([
27284
+ "cmd",
27285
+ "/c",
27286
+ "start",
27287
+ url
27288
+ ]);
27289
+ if (await isWSL()) return await runCommand(["wslview", url]) || await runCommand([
27290
+ "powershell.exe",
27291
+ "-NoProfile",
27292
+ "-Command",
27293
+ "Start-Process",
27294
+ url
27295
+ ]);
27296
+ return await runCommand(["xdg-open", url]);
27297
+ };
27271
27298
  const SEAT_CHECKOUT_RETURN_PATH = "/setup/billing/done";
27272
27299
  const SEAT_POLL_INTERVAL_MS = 2e3;
27273
27300
  const SEAT_POLL_TIMEOUT_MS = 1800 * 1e3;
@@ -27710,11 +27737,7 @@ const LifecycleAccountOutput = object({
27710
27737
  connectedAt: string().optional(),
27711
27738
  lastSeenAt: string().optional()
27712
27739
  }).passthrough();
27713
- const AccountConnectOutput = object({
27714
- platform: _enum(["linkedin", "x"]),
27715
- status: literal("connected"),
27716
- account: LifecycleAccountOutput
27717
- });
27740
+ const AccountConnectOutput = string();
27718
27741
  const AccountDisconnectOutput = object({
27719
27742
  platform: _enum(["linkedin", "x"]),
27720
27743
  account: LifecycleAccountOutput,
@@ -27789,20 +27812,38 @@ const writeOutput = async (deps, value) => {
27789
27812
  }
27790
27813
  printData(value);
27791
27814
  };
27815
+ const writeText = (deps, value) => {
27816
+ if (deps.writeText) {
27817
+ deps.writeText(value);
27818
+ return;
27819
+ }
27820
+ printLine(value);
27821
+ };
27792
27822
  const platformLabel = (platform) => platform === "x" ? "X" : "LinkedIn";
27823
+ const lifecycleConnectContract = {
27824
+ capability: "write",
27825
+ auth: writeAuthContract,
27826
+ mutates: true,
27827
+ outputSchema: AccountConnectOutput,
27828
+ responseShaping: { supported: false },
27829
+ idempotency: "non-idempotent",
27830
+ confirmation: false
27831
+ };
27832
+ const handleFor = (account) => account.handle.startsWith("@") ? account.handle : `@${account.handle}`;
27833
+ const lifecycleDepsWithTextLogs = (deps) => ({
27834
+ ...deps,
27835
+ log: (message) => writeText(deps, message)
27836
+ });
27837
+ const runConnectOutput = async (deps, connect) => {
27838
+ writeText(deps, "Handshaking auth...");
27839
+ writeText(deps, `${handleFor((await connect(lifecycleDepsWithTextLogs(deps))).account)} connected!`);
27840
+ };
27793
27841
  const createConnectPlatformCommand = (platform, run) => defineCommand({
27794
27842
  meta: commandMeta({
27795
27843
  name: platform,
27796
27844
  description: `Connect a ${platformLabel(platform)} account.`,
27797
27845
  capability: "write",
27798
- contract: {
27799
- capability: "write",
27800
- auth: writeAuthContract,
27801
- mutates: true,
27802
- outputSchema: AccountConnectOutput,
27803
- idempotency: "non-idempotent",
27804
- confirmation: false
27805
- },
27846
+ contract: lifecycleConnectContract,
27806
27847
  mutates: true
27807
27848
  }),
27808
27849
  run: async () => {
@@ -27814,14 +27855,7 @@ const createAccountPlatformCommand = (platform, description, run, options = {})
27814
27855
  name: platform,
27815
27856
  description,
27816
27857
  capability: "write",
27817
- contract: options.contract ?? {
27818
- capability: "write",
27819
- auth: writeAuthContract,
27820
- mutates: true,
27821
- outputSchema: AccountConnectOutput,
27822
- idempotency: "non-idempotent",
27823
- confirmation: false
27824
- },
27858
+ contract: options.contract ?? lifecycleConnectContract,
27825
27859
  mutates: true
27826
27860
  }),
27827
27861
  args: { account: options.accountArg ?? accountArg },
@@ -27848,10 +27882,10 @@ const createAccountCommand = (deps) => {
27848
27882
  },
27849
27883
  subCommands: {
27850
27884
  linkedin: createConnectPlatformCommand("linkedin", async () => {
27851
- await writeOutput(deps, await connectLinkedinAccount(deps));
27885
+ await runConnectOutput(deps, connectLinkedinAccount);
27852
27886
  }),
27853
27887
  x: createConnectPlatformCommand("x", async () => {
27854
- await writeOutput(deps, await connectXAccount(deps));
27888
+ await runConnectOutput(deps, connectXAccount);
27855
27889
  })
27856
27890
  }
27857
27891
  }),
@@ -27862,10 +27896,10 @@ const createAccountCommand = (deps) => {
27862
27896
  },
27863
27897
  subCommands: {
27864
27898
  linkedin: createAccountPlatformCommand("linkedin", "Reconnect a LinkedIn account.", async (args) => {
27865
- await writeOutput(deps, await reconnectLinkedinAccount(deps, String(args.account)));
27899
+ await runConnectOutput(deps, (connectDeps) => reconnectLinkedinAccount(connectDeps, String(args.account)));
27866
27900
  }, { accountArg: linkedinReconnectAccountArg }),
27867
27901
  x: createAccountPlatformCommand("x", "Reconnect an X account.", async (args) => {
27868
- await writeOutput(deps, await reconnectXAccount(deps, { account: String(args.account) }));
27902
+ await runConnectOutput(deps, (connectDeps) => reconnectXAccount(connectDeps, { account: String(args.account) }));
27869
27903
  }, { accountArg: xAccountArg })
27870
27904
  }
27871
27905
  }),
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "access": "public"
12
12
  },
13
13
  "private": false,
14
- "version": "0.2.0",
14
+ "version": "0.2.1",
15
15
  "type": "module",
16
16
  "engines": {
17
17
  "node": ">=22.5.0"