@autoposting.ai/cli 0.3.1 → 0.3.3

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/dist/cli.cjs CHANGED
@@ -42,11 +42,11 @@ function createError(status, body) {
42
42
  if (status >= 500) return new ServerError(message, status, code);
43
43
  return new AutopostingError(message, status, code);
44
44
  }
45
- var VERSION, AutopostingError, AuthenticationError, ScopeError, NotFoundError, ValidationError, RateLimitError, ServerError, Resource, AgentsResource, BillingResource, BrandsResource, CarouselsResource, ClipsResource, PostsResource, KbResource, IdeasResource, UsageResource, WebhooksResource, WorkspacesResource, Autoposting;
45
+ var VERSION, AutopostingError, AuthenticationError, ScopeError, NotFoundError, ValidationError, RateLimitError, ServerError, Resource, MediaResource, AgentsResource, BillingResource, BrandsResource, CarouselsResource, ClipsResource, PostsResource, KbResource, IdeasResource, UsageResource, WebhooksResource, WorkspacesResource, IDEMPOTENT_METHODS, RETRYABLE_STATUS, sleep, Autoposting;
46
46
  var init_dist = __esm({
47
47
  "../sdk/dist/index.mjs"() {
48
48
  "use strict";
49
- VERSION = "0.3.1";
49
+ VERSION = "0.3.3";
50
50
  AutopostingError = class extends Error {
51
51
  status;
52
52
  code;
@@ -92,6 +92,15 @@ var init_dist = __esm({
92
92
  return this.client.request("DELETE", path2);
93
93
  }
94
94
  };
95
+ MediaResource = class extends Resource {
96
+ /** POST /media/upload — multipart/form-data, ≤20MB, image/* | video/*. */
97
+ upload(file) {
98
+ const form = new FormData();
99
+ const blob = file.data instanceof Blob ? file.data : new Blob([file.data], { type: file.contentType });
100
+ form.append("file", blob, file.filename);
101
+ return this.client.request("POST", "/media/upload", form);
102
+ }
103
+ };
95
104
  AgentsResource = class extends Resource {
96
105
  /** GET /agents — backend returns `{ items, total, limit, offset }`. */
97
106
  list() {
@@ -233,6 +242,10 @@ var init_dist = __esm({
233
242
  schedule(id, scheduledAt) {
234
243
  return this.client.request("PUT", `/posts/${id}/schedule`, { scheduledAt });
235
244
  }
245
+ /** PUT /posts/:id/schedule with { cancel: true } — returns the post to draft. */
246
+ unschedule(id) {
247
+ return this.client.request("PUT", `/posts/${id}/schedule`, { cancel: true });
248
+ }
236
249
  retry(id) {
237
250
  return this.client.request("POST", `/posts/${id}/retry`);
238
251
  }
@@ -324,9 +337,17 @@ var init_dist = __esm({
324
337
  }
325
338
  /**
326
339
  * GET /orgs — backend returns `{ organizations, activeOrgId }`, not a bare array.
327
- * NOTE: this route is session-only (better-auth session); an API key receives 401.
340
+ * This route is session-only (better-auth session); an API key receives a bare 401,
341
+ * so guard up front with accurate guidance instead of an opaque "Unauthorized".
328
342
  */
329
343
  list() {
344
+ if (this.client.authSource === "api-key") {
345
+ return Promise.reject(
346
+ new Error(
347
+ "Listing workspaces requires session auth. An API key is scoped to a single workspace and cannot list organizations \u2014 manage workspaces from the web dashboard."
348
+ )
349
+ );
350
+ }
330
351
  return this.get("/orgs");
331
352
  }
332
353
  switchWorkspace(id) {
@@ -340,6 +361,9 @@ var init_dist = __esm({
340
361
  return this.client.request("PUT", "/orgs/active", { organizationId: id });
341
362
  }
342
363
  };
364
+ IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "PUT", "DELETE"]);
365
+ RETRYABLE_STATUS = /* @__PURE__ */ new Set([500, 502, 503, 504]);
366
+ sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
343
367
  Autoposting = class {
344
368
  /** @internal Not part of the public API — use SDK methods to make requests. */
345
369
  _apiKey;
@@ -348,6 +372,9 @@ var init_dist = __esm({
348
372
  extraHeaders;
349
373
  /** Indicates how the client was authenticated; used by WorkspacesResource. */
350
374
  authSource;
375
+ maxRetries;
376
+ retryBaseMs;
377
+ media;
351
378
  agents;
352
379
  billing;
353
380
  brands;
@@ -371,6 +398,9 @@ var init_dist = __esm({
371
398
  this.timeout = config.timeout ?? 3e4;
372
399
  this.extraHeaders = config.headers ?? {};
373
400
  this.authSource = config.authSource ?? "api-key";
401
+ this.maxRetries = Math.max(0, config.maxRetries ?? 2);
402
+ this.retryBaseMs = Math.max(0, config.retryBaseMs ?? 300);
403
+ this.media = new MediaResource(this);
374
404
  this.agents = new AgentsResource(this);
375
405
  this.billing = new BillingResource(this);
376
406
  this.brands = new BrandsResource(this);
@@ -383,7 +413,42 @@ var init_dist = __esm({
383
413
  this.webhooks = new WebhooksResource(this);
384
414
  this.workspaces = new WorkspacesResource(this);
385
415
  }
416
+ /**
417
+ * GET /auth/profile — resolve the server-side identity behind the current credential.
418
+ * Validates the key (401 on reject) and returns who/what it's scoped to.
419
+ */
420
+ getProfile() {
421
+ return this.request("GET", "/auth/profile");
422
+ }
423
+ /**
424
+ * Public entry point. Retries idempotent requests (GET/PUT/DELETE) on transient
425
+ * failures — network errors, timeouts, and 5xx — with exponential backoff + jitter.
426
+ * Non-idempotent methods (POST/PATCH) are never retried, to avoid duplicate side effects.
427
+ */
386
428
  async request(method, path2, body, query) {
429
+ let lastErr;
430
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
431
+ if (attempt > 0) {
432
+ const backoff = this.retryBaseMs * 2 ** (attempt - 1);
433
+ await sleep(backoff + Math.floor(Math.random() * this.retryBaseMs));
434
+ }
435
+ try {
436
+ return await this._send(method, path2, body, query);
437
+ } catch (err) {
438
+ lastErr = err;
439
+ if (attempt < this.maxRetries && this.isRetryable(method, err)) continue;
440
+ throw err;
441
+ }
442
+ }
443
+ throw lastErr;
444
+ }
445
+ isRetryable(method, err) {
446
+ if (!IDEMPOTENT_METHODS.has(method)) return false;
447
+ if (!(err instanceof AutopostingError)) return false;
448
+ if (err.code === "NETWORK_ERROR" || err.code === "TIMEOUT") return true;
449
+ return RETRYABLE_STATUS.has(err.status);
450
+ }
451
+ async _send(method, path2, body, query) {
387
452
  let url = `${this.baseUrl}${path2}`;
388
453
  if (query && Object.keys(query).length > 0) {
389
454
  const params = new URLSearchParams();
@@ -1792,7 +1857,7 @@ var OraSpinner = class {
1792
1857
  if (this.instance) {
1793
1858
  this.instance.fail(message);
1794
1859
  this.instance = null;
1795
- } else {
1860
+ } else if (message) {
1796
1861
  process.stderr.write(`${message}
1797
1862
  `);
1798
1863
  }
@@ -1809,6 +1874,10 @@ var Printer = class {
1809
1874
  this.mode = mode;
1810
1875
  }
1811
1876
  mode;
1877
+ /** True only for human-readable output, where extra hint lines are safe to print. */
1878
+ isTty() {
1879
+ return this.mode === "tty";
1880
+ }
1812
1881
  log(data) {
1813
1882
  const out = formatOutput(data, this.mode);
1814
1883
  if (out !== "") console.log(out);
@@ -1901,7 +1970,7 @@ function createAgentsCommand() {
1901
1970
  }));
1902
1971
  printer.table(rows, ["id", "name", "type", "brand", "frequency", "enabled"]);
1903
1972
  } catch (err) {
1904
- spinner.stop();
1973
+ spinner.fail();
1905
1974
  printer.error(err);
1906
1975
  process.exit(resolveExitCode(err));
1907
1976
  }
@@ -1917,7 +1986,7 @@ function createAgentsCommand() {
1917
1986
  spinner.stop();
1918
1987
  printer.log(agent);
1919
1988
  } catch (err) {
1920
- spinner.stop();
1989
+ spinner.fail();
1921
1990
  printer.error(err);
1922
1991
  process.exit(resolveExitCode(err));
1923
1992
  }
@@ -1943,7 +2012,7 @@ function createAgentsCommand() {
1943
2012
  spinner.stop();
1944
2013
  printer.log(agent);
1945
2014
  } catch (err) {
1946
- spinner.stop();
2015
+ spinner.fail();
1947
2016
  printer.error(err);
1948
2017
  process.exit(resolveExitCode(err));
1949
2018
  }
@@ -1967,7 +2036,7 @@ function createAgentsCommand() {
1967
2036
  spinner.stop();
1968
2037
  printer.log(agent);
1969
2038
  } catch (err) {
1970
- spinner.stop();
2039
+ spinner.fail();
1971
2040
  printer.error(err);
1972
2041
  process.exit(resolveExitCode(err));
1973
2042
  }
@@ -1988,7 +2057,7 @@ function createAgentsCommand() {
1988
2057
  spinner.stop();
1989
2058
  printer.log(`Agent "${id}" deleted.`);
1990
2059
  } catch (err) {
1991
- spinner.stop();
2060
+ spinner.fail();
1992
2061
  printer.error(err);
1993
2062
  process.exit(resolveExitCode(err));
1994
2063
  }
@@ -2004,7 +2073,7 @@ function createAgentsCommand() {
2004
2073
  spinner.stop();
2005
2074
  printer.log({ runId: queued.runId, status: queued.status });
2006
2075
  } catch (err) {
2007
- spinner.stop();
2076
+ spinner.fail();
2008
2077
  printer.error(err);
2009
2078
  process.exit(resolveExitCode(err));
2010
2079
  }
@@ -2020,7 +2089,7 @@ function createAgentsCommand() {
2020
2089
  spinner.stop();
2021
2090
  printer.log(agent);
2022
2091
  } catch (err) {
2023
- spinner.stop();
2092
+ spinner.fail();
2024
2093
  printer.error(err);
2025
2094
  process.exit(resolveExitCode(err));
2026
2095
  }
@@ -2042,7 +2111,7 @@ function createAgentsCommand() {
2042
2111
  }));
2043
2112
  printer.table(rows, ["id", "status", "output", "createdAt"]);
2044
2113
  } catch (err) {
2045
- spinner.stop();
2114
+ spinner.fail();
2046
2115
  printer.error(err);
2047
2116
  process.exit(resolveExitCode(err));
2048
2117
  }
@@ -2287,7 +2356,7 @@ function createBillingCommand() {
2287
2356
  ["plan", "status", "cycle", "accounts", "credits", "renews", "trial ends"]
2288
2357
  );
2289
2358
  } catch (err) {
2290
- spinner.stop();
2359
+ spinner.fail();
2291
2360
  printer.error(err);
2292
2361
  process.exit(resolveExitCode2(err));
2293
2362
  }
@@ -2320,7 +2389,7 @@ function createBillingCommand() {
2320
2389
  printer.table(usageRows, ["date", "description", "amount", "type"]);
2321
2390
  }
2322
2391
  } catch (err) {
2323
- spinner.stop();
2392
+ spinner.fail();
2324
2393
  printer.error(err);
2325
2394
  process.exit(resolveExitCode2(err));
2326
2395
  }
@@ -2331,6 +2400,24 @@ function createBillingCommand() {
2331
2400
  // src/commands/brands.ts
2332
2401
  var import_commander4 = require("commander");
2333
2402
  init_dist();
2403
+ function tokenStatus(c, now = Date.now()) {
2404
+ if (!c.connected) return "\u2014";
2405
+ if (c.refreshError) return "expired";
2406
+ if (c.expiresAt) {
2407
+ const expMs = Date.parse(c.expiresAt);
2408
+ if (Number.isNaN(expMs) || expMs <= now) return "expired";
2409
+ }
2410
+ return "ok";
2411
+ }
2412
+ function buildAuthStatusRow(c, now = Date.now()) {
2413
+ return {
2414
+ platform: c.platform,
2415
+ connected: c.connected ? "yes" : "no",
2416
+ username: c.platformUsername ?? "\u2014",
2417
+ "token status": tokenStatus(c, now),
2418
+ expires: c.expiresAt ?? "\u2014"
2419
+ };
2420
+ }
2334
2421
  function createBrandsCommand() {
2335
2422
  const brands = new import_commander4.Command("brands").description("Manage brands");
2336
2423
  brands.command("list").description("List all brands").action(async (_opts, cmd) => {
@@ -2350,7 +2437,7 @@ function createBrandsCommand() {
2350
2437
  }));
2351
2438
  printer.table(rows, ["slug", "name", "platforms", "timezone"]);
2352
2439
  } catch (err) {
2353
- spinner.stop();
2440
+ spinner.fail();
2354
2441
  printer.error(err);
2355
2442
  process.exit(exitCodeFromError(err));
2356
2443
  }
@@ -2366,7 +2453,7 @@ function createBrandsCommand() {
2366
2453
  spinner.stop();
2367
2454
  printer.log(brand);
2368
2455
  } catch (err) {
2369
- spinner.stop();
2456
+ spinner.fail();
2370
2457
  printer.error(err);
2371
2458
  process.exit(exitCodeFromError(err));
2372
2459
  }
@@ -2385,7 +2472,7 @@ function createBrandsCommand() {
2385
2472
  spinner.stop();
2386
2473
  printer.log(brand);
2387
2474
  } catch (err) {
2388
- spinner.stop();
2475
+ spinner.fail();
2389
2476
  printer.error(err);
2390
2477
  process.exit(exitCodeFromError(err));
2391
2478
  }
@@ -2404,7 +2491,7 @@ function createBrandsCommand() {
2404
2491
  spinner.stop();
2405
2492
  printer.log(brand);
2406
2493
  } catch (err) {
2407
- spinner.stop();
2494
+ spinner.fail();
2408
2495
  printer.error(err);
2409
2496
  process.exit(exitCodeFromError(err));
2410
2497
  }
@@ -2424,7 +2511,7 @@ function createBrandsCommand() {
2424
2511
  spinner.stop();
2425
2512
  printer.log(`Brand "${slug}" deleted.`);
2426
2513
  } catch (err) {
2427
- spinner.stop();
2514
+ spinner.fail();
2428
2515
  printer.error(err);
2429
2516
  process.exit(exitCodeFromError(err));
2430
2517
  }
@@ -2438,15 +2525,10 @@ function createBrandsCommand() {
2438
2525
  const client = new Autoposting({ apiKey: cred.apiKey });
2439
2526
  const connections = await client.brands.authStatus(slug);
2440
2527
  spinner.stop();
2441
- const rows = connections.map((c) => ({
2442
- platform: c.platform,
2443
- connected: c.connected ? "yes" : "no",
2444
- username: c.username ?? "\u2014",
2445
- "token status": c.tokenExpired === true ? "expired" : c.connected ? "ok" : "\u2014"
2446
- }));
2447
- printer.table(rows, ["platform", "connected", "username", "token status"]);
2528
+ const rows = connections.map((c) => buildAuthStatusRow(c));
2529
+ printer.table(rows, ["platform", "connected", "username", "token status", "expires"]);
2448
2530
  } catch (err) {
2449
- spinner.stop();
2531
+ spinner.fail();
2450
2532
  printer.error(err);
2451
2533
  process.exit(exitCodeFromError(err));
2452
2534
  }
@@ -2482,7 +2564,7 @@ function createCarouselsCommand() {
2482
2564
  }));
2483
2565
  printer.table(rows, ["id", "title", "slides", "status", "createdAt"]);
2484
2566
  } catch (err) {
2485
- spinner.stop();
2567
+ spinner.fail();
2486
2568
  printer.error(err);
2487
2569
  process.exit(resolveExitCode3(err));
2488
2570
  }
@@ -2498,7 +2580,7 @@ function createCarouselsCommand() {
2498
2580
  spinner.stop();
2499
2581
  printer.log(carousel);
2500
2582
  } catch (err) {
2501
- spinner.stop();
2583
+ spinner.fail();
2502
2584
  printer.error(err);
2503
2585
  process.exit(resolveExitCode3(err));
2504
2586
  }
@@ -2516,7 +2598,7 @@ function createCarouselsCommand() {
2516
2598
  spinner.stop();
2517
2599
  printer.log(carousel);
2518
2600
  } catch (err) {
2519
- spinner.stop();
2601
+ spinner.fail();
2520
2602
  printer.error(err);
2521
2603
  process.exit(resolveExitCode3(err));
2522
2604
  }
@@ -2536,7 +2618,7 @@ function createCarouselsCommand() {
2536
2618
  spinner.stop();
2537
2619
  printer.log(carousel);
2538
2620
  } catch (err) {
2539
- spinner.stop();
2621
+ spinner.fail();
2540
2622
  printer.error(err);
2541
2623
  process.exit(resolveExitCode3(err));
2542
2624
  }
@@ -2552,7 +2634,7 @@ function createCarouselsCommand() {
2552
2634
  spinner.stop();
2553
2635
  printer.log(result);
2554
2636
  } catch (err) {
2555
- spinner.stop();
2637
+ spinner.fail();
2556
2638
  printer.error(err);
2557
2639
  process.exit(resolveExitCode3(err));
2558
2640
  }
@@ -2572,7 +2654,7 @@ function createCarouselsCommand() {
2572
2654
  spinner.stop();
2573
2655
  printer.log(`Carousel "${id}" deleted.`);
2574
2656
  } catch (err) {
2575
- spinner.stop();
2657
+ spinner.fail();
2576
2658
  printer.error(err);
2577
2659
  process.exit(resolveExitCode3(err));
2578
2660
  }
@@ -2607,7 +2689,7 @@ function createClipsCommand() {
2607
2689
  }));
2608
2690
  printer.table(rows, ["id", "name", "status", "duration"]);
2609
2691
  } catch (err) {
2610
- spinner.stop();
2692
+ spinner.fail();
2611
2693
  printer.error(err);
2612
2694
  process.exit(resolveExitCode4(err));
2613
2695
  }
@@ -2623,7 +2705,7 @@ function createClipsCommand() {
2623
2705
  spinner.stop();
2624
2706
  printer.log(clip);
2625
2707
  } catch (err) {
2626
- spinner.stop();
2708
+ spinner.fail();
2627
2709
  printer.error(err);
2628
2710
  process.exit(resolveExitCode4(err));
2629
2711
  }
@@ -2644,7 +2726,7 @@ function createClipsCommand() {
2644
2726
  spinner.stop();
2645
2727
  printer.log(clip);
2646
2728
  } catch (err) {
2647
- spinner.stop();
2729
+ spinner.fail();
2648
2730
  printer.error(err);
2649
2731
  process.exit(resolveExitCode4(err));
2650
2732
  }
@@ -2663,7 +2745,7 @@ function createClipsCommand() {
2663
2745
  spinner.stop();
2664
2746
  printer.log({ clipId });
2665
2747
  } catch (err) {
2666
- spinner.stop();
2748
+ spinner.fail();
2667
2749
  printer.error(err);
2668
2750
  process.exit(resolveExitCode4(err));
2669
2751
  }
@@ -2679,7 +2761,7 @@ function createClipsCommand() {
2679
2761
  spinner.stop();
2680
2762
  printer.log({ jobIds, activeJobIds, reusedJobIds });
2681
2763
  } catch (err) {
2682
- spinner.stop();
2764
+ spinner.fail();
2683
2765
  printer.error(err);
2684
2766
  process.exit(resolveExitCode4(err));
2685
2767
  }
@@ -2699,7 +2781,7 @@ function createClipsCommand() {
2699
2781
  spinner.stop();
2700
2782
  printer.log(`Clip "${id}" deleted.`);
2701
2783
  } catch (err) {
2702
- spinner.stop();
2784
+ spinner.fail();
2703
2785
  printer.error(err);
2704
2786
  process.exit(resolveExitCode4(err));
2705
2787
  }
@@ -2710,16 +2792,226 @@ function createClipsCommand() {
2710
2792
  // src/commands/posts.ts
2711
2793
  var import_commander7 = require("commander");
2712
2794
  init_dist();
2795
+ var import_promises = __toESM(require("fs/promises"));
2796
+ var import_node_path2 = __toESM(require("path"));
2797
+
2798
+ // src/lib/media-flags.ts
2799
+ var import_node_fs2 = require("fs");
2713
2800
  var VALID_PLATFORMS = ["x", "linkedin", "instagram", "threads", "youtube"];
2801
+ var EXT_TO_MIME = {
2802
+ jpg: "image/jpeg",
2803
+ jpeg: "image/jpeg",
2804
+ png: "image/png",
2805
+ gif: "image/gif",
2806
+ webp: "image/webp",
2807
+ mp4: "video/mp4",
2808
+ mov: "video/quicktime",
2809
+ webm: "video/webm"
2810
+ };
2811
+ function extToMime(filename) {
2812
+ const ext = filename.split(".").pop()?.toLowerCase() ?? "";
2813
+ const mime = EXT_TO_MIME[ext];
2814
+ if (!mime) {
2815
+ throw new Error(
2816
+ `Unsupported file extension ".${ext || "(none)"}" in "${filename}". Supported: ${Object.keys(EXT_TO_MIME).join(", ")}`
2817
+ );
2818
+ }
2819
+ return mime;
2820
+ }
2821
+ function parsePairs(flag, vals) {
2822
+ const result = {};
2823
+ for (const val of vals) {
2824
+ const eqIdx = val.indexOf("=");
2825
+ if (eqIdx < 1) {
2826
+ throw new Error(`${flag}: expected "platform=value" format, got "${val}"`);
2827
+ }
2828
+ const p = val.slice(0, eqIdx).trim();
2829
+ const v = val.slice(eqIdx + 1);
2830
+ if (!VALID_PLATFORMS.includes(p)) {
2831
+ throw new Error(`${flag}: unknown platform "${p}". Valid: ${VALID_PLATFORMS.join(", ")}`);
2832
+ }
2833
+ result[p] = v;
2834
+ }
2835
+ return result;
2836
+ }
2837
+ function parsePlatformMediaPairs(flag, vals) {
2838
+ const result = {};
2839
+ for (const val of vals) {
2840
+ const eqIdx = val.indexOf("=");
2841
+ if (eqIdx < 1) {
2842
+ throw new Error(`${flag}: expected "platform=path[,path...]" format, got "${val}"`);
2843
+ }
2844
+ const p = val.slice(0, eqIdx).trim();
2845
+ const paths = val.slice(eqIdx + 1).split(",").map((s) => s.trim()).filter(Boolean);
2846
+ if (!VALID_PLATFORMS.includes(p)) {
2847
+ throw new Error(`${flag}: unknown platform "${p}". Valid: ${VALID_PLATFORMS.join(", ")}`);
2848
+ }
2849
+ result[p] = paths;
2850
+ }
2851
+ return result;
2852
+ }
2853
+ function validateMediaExtensions(paths) {
2854
+ for (const p of paths) extToMime(p);
2855
+ }
2856
+ function validateMediaCount(paths) {
2857
+ if (paths.length > 10) {
2858
+ throw new Error(`--media accepts at most 10 files (received ${paths.length}).`);
2859
+ }
2860
+ }
2861
+ function validateMediaPaths(paths) {
2862
+ for (const p of paths) {
2863
+ if (!(0, import_node_fs2.existsSync)(p)) {
2864
+ throw new Error(`Media file not found: "${p}"`);
2865
+ }
2866
+ }
2867
+ }
2868
+ function alignAltText(mediaPaths, altTexts) {
2869
+ if (altTexts.length > mediaPaths.length) {
2870
+ throw new Error(
2871
+ `--alt-text has more values (${altTexts.length}) than --media (${mediaPaths.length}). Alt text aligns to media by index.`
2872
+ );
2873
+ }
2874
+ return mediaPaths.map((_, i) => altTexts[i]);
2875
+ }
2876
+ function buildYoutubeOptions(opts) {
2877
+ const { ytTitle, ytDescription, ytTags, ytPrivacy, ytCategory, ytMadeForKids } = opts;
2878
+ const o = {};
2879
+ if (ytTitle) o.title = ytTitle;
2880
+ if (ytDescription) o.description = ytDescription;
2881
+ if (ytTags) o.tags = ytTags.split(",").map((t) => t.trim()).filter(Boolean);
2882
+ if (ytPrivacy) o.privacyStatus = ytPrivacy;
2883
+ if (ytCategory) o.categoryId = ytCategory;
2884
+ if (ytMadeForKids !== void 0) o.madeForKids = ytMadeForKids;
2885
+ return Object.keys(o).length > 0 ? o : void 0;
2886
+ }
2887
+ function buildInstagramOptions(opts) {
2888
+ const { igReel, igShareToFeed, igCoverUrl, igThumbOffsetMs, igCollaborators } = opts;
2889
+ const hasAnyReelFlag = igReel !== void 0 || igShareToFeed !== void 0 || igCoverUrl !== void 0 || igThumbOffsetMs !== void 0 || igCollaborators !== void 0;
2890
+ if (!hasAnyReelFlag) return void 0;
2891
+ const reel = {};
2892
+ if (igShareToFeed !== void 0) reel.shareToFeed = igShareToFeed;
2893
+ if (igCoverUrl) reel.coverUrl = igCoverUrl;
2894
+ if (igThumbOffsetMs !== void 0) {
2895
+ const n = Number(igThumbOffsetMs);
2896
+ if (!Number.isFinite(n) || n < 0) {
2897
+ throw new Error(
2898
+ `--ig-thumb-offset-ms must be a non-negative number (received "${igThumbOffsetMs}")`
2899
+ );
2900
+ }
2901
+ reel.thumbOffsetMs = n;
2902
+ }
2903
+ if (igCollaborators) {
2904
+ reel.collaborators = igCollaborators.split(",").map((s) => s.trim()).filter(Boolean);
2905
+ }
2906
+ return { reel };
2907
+ }
2908
+ function buildThreadsOptions(opts) {
2909
+ const { threadsReplyTo, threadsReplyControl } = opts;
2910
+ if (!threadsReplyTo && !threadsReplyControl) return void 0;
2911
+ const o = {};
2912
+ if (threadsReplyTo) o.replyToId = threadsReplyTo;
2913
+ if (threadsReplyControl) o.replyControl = threadsReplyControl;
2914
+ return o;
2915
+ }
2916
+
2917
+ // src/lib/account-select.ts
2918
+ var VALID_PLATFORMS2 = ["x", "linkedin", "instagram", "threads", "youtube"];
2919
+ async function resolveTargetAccounts({
2920
+ brandSlug,
2921
+ platforms,
2922
+ accountFlags,
2923
+ client,
2924
+ isTty
2925
+ }) {
2926
+ const accountMap = {};
2927
+ for (const flag of accountFlags) {
2928
+ const eqIdx = flag.indexOf("=");
2929
+ if (eqIdx < 1) {
2930
+ throw Object.assign(
2931
+ new Error(`--account: expected "platform=handle|id" format, got "${flag}"`),
2932
+ { exitCode: 1 }
2933
+ );
2934
+ }
2935
+ const p = flag.slice(0, eqIdx).trim();
2936
+ if (!VALID_PLATFORMS2.includes(p)) {
2937
+ throw Object.assign(
2938
+ new Error(`--account: unknown platform "${p}". Valid: ${VALID_PLATFORMS2.join(", ")}`),
2939
+ { exitCode: 1 }
2940
+ );
2941
+ }
2942
+ accountMap[p] = flag.slice(eqIdx + 1).trim();
2943
+ }
2944
+ const connections = await client.brands.authStatus(brandSlug);
2945
+ const byPlatform = {};
2946
+ for (const conn of connections) {
2947
+ if (!conn.connected) continue;
2948
+ const p = conn.platform;
2949
+ if (!byPlatform[p]) byPlatform[p] = [];
2950
+ byPlatform[p].push(conn);
2951
+ }
2952
+ const result = {};
2953
+ for (const platform of platforms) {
2954
+ const accounts = byPlatform[platform] ?? [];
2955
+ const specifiedValue = accountMap[platform];
2956
+ if (specifiedValue !== void 0) {
2957
+ const normalized = specifiedValue.startsWith("@") ? specifiedValue.slice(1) : specifiedValue;
2958
+ const match = accounts.find(
2959
+ (a) => a.platformUsername?.toLowerCase() === normalized.toLowerCase() || a.platformUserId === specifiedValue
2960
+ );
2961
+ if (!match?.platformUserId) {
2962
+ const valid = accounts.map((a) => ` @${a.platformUsername ?? "?"} (${a.platformUserId ?? "?"})`).join("\n");
2963
+ throw Object.assign(
2964
+ new Error(
2965
+ `--account: unknown ${platform} account "${specifiedValue}".
2966
+ Connected accounts:
2967
+ ${valid || " (none)"}`
2968
+ ),
2969
+ { exitCode: 1 }
2970
+ );
2971
+ }
2972
+ if (!result[platform]) result[platform] = [];
2973
+ result[platform].push(match.platformUserId);
2974
+ } else if (accounts.length >= 2) {
2975
+ if (isTty) {
2976
+ const { checkbox } = await import("@inquirer/prompts");
2977
+ const choices = accounts.map((a) => ({
2978
+ name: `@${a.platformUsername ?? "?"} (${a.platformUserId ?? "?"})`,
2979
+ value: a.platformUserId ?? ""
2980
+ }));
2981
+ const selected = await checkbox({
2982
+ message: `Multiple ${platform} accounts connected \u2014 select which to post to:`,
2983
+ choices
2984
+ });
2985
+ const valid = selected.filter(Boolean);
2986
+ if (valid.length > 0) result[platform] = valid;
2987
+ } else {
2988
+ const list = accounts.map((a) => ` @${a.platformUsername ?? "?"} (${a.platformUserId ?? "?"})`).join("\n");
2989
+ throw Object.assign(
2990
+ new Error(
2991
+ `Multiple ${platform} accounts connected but no --account specified (non-interactive mode).
2992
+ Connected accounts:
2993
+ ${list}
2994
+ Pass --account ${platform}=<handle|id> to select.`
2995
+ ),
2996
+ { exitCode: 1 }
2997
+ );
2998
+ }
2999
+ }
3000
+ }
3001
+ return result;
3002
+ }
3003
+
3004
+ // src/commands/posts.ts
3005
+ var VALID_PLATFORMS3 = ["x", "linkedin", "instagram", "threads", "youtube"];
2714
3006
  function parsePlatforms(raw) {
2715
3007
  const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
2716
3008
  if (parts.length === 0) {
2717
3009
  throw new Error("No platforms provided. Pass a comma-separated list, e.g. --platforms x,linkedin");
2718
3010
  }
2719
- const invalid = parts.filter((p) => !VALID_PLATFORMS.includes(p));
3011
+ const invalid = parts.filter((p) => !VALID_PLATFORMS3.includes(p));
2720
3012
  if (invalid.length > 0) {
2721
3013
  throw new Error(
2722
- `Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${VALID_PLATFORMS.join(", ")}`
3014
+ `Unsupported platform(s): ${invalid.join(", ")}. Valid platforms: ${VALID_PLATFORMS3.join(", ")}`
2723
3015
  );
2724
3016
  }
2725
3017
  return parts;
@@ -2732,11 +3024,17 @@ function parsePositiveInt(value, flag) {
2732
3024
  return n;
2733
3025
  }
2734
3026
  function validateScheduledAt(value) {
2735
- if (Number.isNaN(Date.parse(value)) || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
3027
+ const ms = Date.parse(value);
3028
+ if (Number.isNaN(ms) || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value)) {
2736
3029
  throw new Error(
2737
3030
  `--at must be a valid ISO 8601 datetime, e.g. 2026-06-30T14:00:00Z (received "${value}").`
2738
3031
  );
2739
3032
  }
3033
+ if (ms <= Date.now()) {
3034
+ throw new Error(
3035
+ `--at must be in the future (received "${value}", which is in the past). A past schedule time publishes immediately.`
3036
+ );
3037
+ }
2740
3038
  return value;
2741
3039
  }
2742
3040
  function resolveExitCode5(err) {
@@ -2770,7 +3068,7 @@ function createPostsCommand() {
2770
3068
  }));
2771
3069
  printer.table(rows, ["id", "brand", "status", "platforms", "text", "scheduledAt"]);
2772
3070
  } catch (err) {
2773
- spinner.stop();
3071
+ spinner.fail();
2774
3072
  printer.error(err);
2775
3073
  process.exit(resolveExitCode5(err));
2776
3074
  }
@@ -2786,7 +3084,7 @@ function createPostsCommand() {
2786
3084
  spinner.stop();
2787
3085
  printer.log(post);
2788
3086
  } catch (err) {
2789
- spinner.stop();
3087
+ spinner.fail();
2790
3088
  printer.error(err);
2791
3089
  process.exit(resolveExitCode5(err));
2792
3090
  }
@@ -2794,25 +3092,109 @@ function createPostsCommand() {
2794
3092
  posts.command("create").description("Create a new post").requiredOption("--brand <slug>", "Brand slug").requiredOption("--text <text>", "Post text content").requiredOption("--platforms <list>", "Comma-separated platforms (e.g. x,linkedin)").option("--at <iso>", "Schedule date/time (ISO 8601)").option(
2795
3093
  "--thread <text...>",
2796
3094
  "Additional posts appended after --text to form a thread (X/Threads only, max 25)"
2797
- ).action(
3095
+ ).option("--media <path...>", "Media file paths to attach (max 10)").option("--alt-text <text...>", "Alt text for --media files, aligned by index").option("--platform-text <pairs...>", "Per-platform text, e.g. x=Hello linkedin=World").option("--platform-media <pairs...>", "Per-platform media, e.g. instagram=photo.jpg x=banner.png").option("--yt-title <t>", "YouTube video title").option("--yt-description <d>", "YouTube video description").option("--yt-tags <list>", "YouTube tags (comma-separated)").option("--yt-privacy <v>", "YouTube privacy status (public|unlisted|private)").option("--yt-category <id>", "YouTube category ID").option("--yt-made-for-kids", "Mark YouTube video as made for kids").option("--ig-reel", "Post as an Instagram Reel").option("--ig-share-to-feed", "Share Reel to feed").option("--ig-cover-url <url>", "Reel cover image URL").option("--ig-thumb-offset-ms <n>", "Reel thumbnail offset in milliseconds").option("--ig-collaborators <list>", "Reel collaborator handles (comma-separated)").option("--threads-reply-to <id>", "Threads post ID to reply to").option("--threads-reply-control <v>", "Threads reply control (everyone|accounts_you_follow|mentioned_only)").option("--account <p=handle|id...>", "Target specific account per platform, e.g. x=@handle").action(
2798
3096
  async (opts, cmd) => {
2799
3097
  const globals = cmd.optsWithGlobals();
2800
3098
  const printer = createPrinter(globals);
2801
- const spinner = printer.spinner("Creating post\u2026");
3099
+ let spinner;
2802
3100
  try {
2803
3101
  const cred = resolveAuth({ apiKey: globals.apiKey });
2804
3102
  const client = new Autoposting({ apiKey: cred.apiKey });
3103
+ const platforms = parsePlatforms(opts.platforms);
3104
+ const scheduledAt = opts.at ? validateScheduledAt(opts.at) : void 0;
3105
+ if (opts.media && opts.media.length > 0) {
3106
+ validateMediaCount(opts.media);
3107
+ validateMediaPaths(opts.media);
3108
+ validateMediaExtensions(opts.media);
3109
+ }
3110
+ const platformTexts = opts.platformText && opts.platformText.length > 0 ? parsePairs("--platform-text", opts.platformText) : void 0;
3111
+ const platformMediaPaths = opts.platformMedia && opts.platformMedia.length > 0 ? parsePlatformMediaPairs("--platform-media", opts.platformMedia) : {};
3112
+ for (const paths of Object.values(platformMediaPaths)) {
3113
+ validateMediaPaths(paths);
3114
+ validateMediaExtensions(paths);
3115
+ }
3116
+ const altTexts = alignAltText(opts.media ?? [], opts.altText ?? []);
3117
+ const youtubeOptions = buildYoutubeOptions({
3118
+ ytTitle: opts.ytTitle,
3119
+ ytDescription: opts.ytDescription,
3120
+ ytTags: opts.ytTags,
3121
+ ytPrivacy: opts.ytPrivacy,
3122
+ ytCategory: opts.ytCategory,
3123
+ ytMadeForKids: opts.ytMadeForKids
3124
+ });
3125
+ const instagramOptions = buildInstagramOptions({
3126
+ igReel: opts.igReel,
3127
+ igShareToFeed: opts.igShareToFeed,
3128
+ igCoverUrl: opts.igCoverUrl,
3129
+ igThumbOffsetMs: opts.igThumbOffsetMs,
3130
+ igCollaborators: opts.igCollaborators
3131
+ });
3132
+ const threadsOptions = buildThreadsOptions({
3133
+ threadsReplyTo: opts.threadsReplyTo,
3134
+ threadsReplyControl: opts.threadsReplyControl
3135
+ });
3136
+ const targetAccountIds = await resolveTargetAccounts({
3137
+ brandSlug: opts.brand,
3138
+ platforms,
3139
+ accountFlags: opts.account ?? [],
3140
+ client,
3141
+ // Picker reads stdin — gate on a real interactive terminal, not the
3142
+ // output mode (printer.isTty() is true even when stdout/stdin is piped).
3143
+ isTty: Boolean(process.stdin.isTTY)
3144
+ });
3145
+ spinner = printer.spinner("Creating post\u2026");
3146
+ const mediaInputs = [];
3147
+ for (let i = 0; i < (opts.media ?? []).length; i++) {
3148
+ const filePath = opts.media[i];
3149
+ const data = await import_promises.default.readFile(filePath);
3150
+ const filename = import_node_path2.default.basename(filePath);
3151
+ const contentType = extToMime(filename);
3152
+ const uploaded = await client.media.upload({
3153
+ data: new Uint8Array(data),
3154
+ filename,
3155
+ contentType
3156
+ });
3157
+ mediaInputs.push({
3158
+ url: uploaded.url,
3159
+ type: uploaded.type,
3160
+ ...altTexts[i] ? { altText: altTexts[i] } : {}
3161
+ });
3162
+ }
3163
+ const platformMediaResult = {};
3164
+ for (const [p, paths] of Object.entries(platformMediaPaths)) {
3165
+ const uploads = [];
3166
+ for (const filePath of paths) {
3167
+ const data = await import_promises.default.readFile(filePath);
3168
+ const filename = import_node_path2.default.basename(filePath);
3169
+ const contentType = extToMime(filename);
3170
+ const uploaded = await client.media.upload({
3171
+ data: new Uint8Array(data),
3172
+ filename,
3173
+ contentType
3174
+ });
3175
+ uploads.push({ url: uploaded.url, type: uploaded.type });
3176
+ }
3177
+ platformMediaResult[p] = uploads;
3178
+ }
2805
3179
  const post = await client.posts.create({
2806
3180
  brandSlug: opts.brand,
2807
3181
  text: opts.text,
2808
- platforms: parsePlatforms(opts.platforms),
2809
- ...opts.at ? { scheduledAt: validateScheduledAt(opts.at) } : {},
2810
- ...opts.thread && opts.thread.length > 0 ? { thread: opts.thread } : {}
3182
+ platforms,
3183
+ ...scheduledAt ? { scheduledAt } : {},
3184
+ ...opts.thread && opts.thread.length > 0 ? { thread: opts.thread } : {},
3185
+ ...mediaInputs.length > 0 ? { media: mediaInputs } : {},
3186
+ ...Object.keys(platformMediaResult).length > 0 ? { platformMedia: platformMediaResult } : {},
3187
+ ...platformTexts && Object.keys(platformTexts).length > 0 ? { platformTexts } : {},
3188
+ ...Object.keys(targetAccountIds).length > 0 ? { targetAccountIds } : {},
3189
+ ...instagramOptions ? { instagramOptions } : {},
3190
+ ...threadsOptions ? { threadsOptions } : {},
3191
+ ...youtubeOptions ? { youtubeOptions } : {},
3192
+ source: "cli"
2811
3193
  });
2812
3194
  spinner.stop();
2813
3195
  printer.log(post);
2814
3196
  } catch (err) {
2815
- spinner.stop();
3197
+ spinner?.fail();
2816
3198
  printer.error(err);
2817
3199
  process.exit(resolveExitCode5(err));
2818
3200
  }
@@ -2834,7 +3216,7 @@ function createPostsCommand() {
2834
3216
  spinner.stop();
2835
3217
  printer.log(post);
2836
3218
  } catch (err) {
2837
- spinner.stop();
3219
+ spinner.fail();
2838
3220
  printer.error(err);
2839
3221
  process.exit(resolveExitCode5(err));
2840
3222
  }
@@ -2855,8 +3237,11 @@ function createPostsCommand() {
2855
3237
  spinner.stop();
2856
3238
  printer.log(`Post "${id}" deleted.`);
2857
3239
  } catch (err) {
2858
- spinner.stop();
3240
+ spinner.fail();
2859
3241
  printer.error(err);
3242
+ if (!(err instanceof NotFoundError) && printer.isTty()) {
3243
+ printer.error(`The post may not have been deleted \u2014 verify with: ap posts get "${id}"`);
3244
+ }
2860
3245
  process.exit(resolveExitCode5(err));
2861
3246
  }
2862
3247
  });
@@ -2871,23 +3256,31 @@ function createPostsCommand() {
2871
3256
  spinner.stop();
2872
3257
  printer.log(post);
2873
3258
  } catch (err) {
2874
- spinner.stop();
3259
+ spinner.fail();
2875
3260
  printer.error(err);
2876
3261
  process.exit(resolveExitCode5(err));
2877
3262
  }
2878
3263
  });
2879
- posts.command("schedule <id>").description("Schedule a post for a specific time").requiredOption("--at <iso>", "ISO 8601 datetime to schedule the post").action(async (id, opts, cmd) => {
3264
+ posts.command("schedule <id>").description("Schedule a post for a specific time, or --cancel to unschedule it").option("--at <iso>", "ISO 8601 datetime to schedule the post").option("--cancel", "Unschedule the post (return it to draft)").action(async (id, opts, cmd) => {
2880
3265
  const globals = cmd.optsWithGlobals();
2881
3266
  const printer = createPrinter(globals);
2882
- const spinner = printer.spinner(`Scheduling post "${id}"\u2026`);
3267
+ const spinner = printer.spinner(
3268
+ opts.cancel ? `Unscheduling post "${id}"\u2026` : `Scheduling post "${id}"\u2026`
3269
+ );
2883
3270
  try {
3271
+ if (opts.cancel && opts.at) {
3272
+ throw new Error("Cannot use --at together with --cancel \u2014 choose one.");
3273
+ }
3274
+ if (!opts.cancel && !opts.at) {
3275
+ throw new Error("Provide either --at <iso> to schedule, or --cancel to unschedule.");
3276
+ }
2884
3277
  const cred = resolveAuth({ apiKey: globals.apiKey });
2885
3278
  const client = new Autoposting({ apiKey: cred.apiKey });
2886
- const post = await client.posts.schedule(id, validateScheduledAt(opts.at));
3279
+ const post = opts.cancel ? await client.posts.unschedule(id) : await client.posts.schedule(id, validateScheduledAt(opts.at));
2887
3280
  spinner.stop();
2888
3281
  printer.log(post);
2889
3282
  } catch (err) {
2890
- spinner.stop();
3283
+ spinner.fail();
2891
3284
  printer.error(err);
2892
3285
  process.exit(resolveExitCode5(err));
2893
3286
  }
@@ -2903,7 +3296,7 @@ function createPostsCommand() {
2903
3296
  spinner.stop();
2904
3297
  printer.log(post);
2905
3298
  } catch (err) {
2906
- spinner.stop();
3299
+ spinner.fail();
2907
3300
  printer.error(err);
2908
3301
  process.exit(resolveExitCode5(err));
2909
3302
  }
@@ -2919,7 +3312,7 @@ function createPostsCommand() {
2919
3312
  spinner.stop();
2920
3313
  printer.log(post);
2921
3314
  } catch (err) {
2922
- spinner.stop();
3315
+ spinner.fail();
2923
3316
  printer.error(err);
2924
3317
  process.exit(resolveExitCode5(err));
2925
3318
  }
@@ -2935,7 +3328,7 @@ function createPostsCommand() {
2935
3328
  spinner.stop();
2936
3329
  printer.log(result);
2937
3330
  } catch (err) {
2938
- spinner.stop();
3331
+ spinner.fail();
2939
3332
  printer.error(err);
2940
3333
  process.exit(resolveExitCode5(err));
2941
3334
  }
@@ -2970,7 +3363,7 @@ function createKbCommand() {
2970
3363
  }));
2971
3364
  printer.table(rows, ["id", "name", "docs", "createdAt"]);
2972
3365
  } catch (err) {
2973
- spinner.stop();
3366
+ spinner.fail();
2974
3367
  printer.error(err);
2975
3368
  process.exit(resolveExitCode6(err));
2976
3369
  }
@@ -2986,7 +3379,7 @@ function createKbCommand() {
2986
3379
  spinner.stop();
2987
3380
  printer.log(result);
2988
3381
  } catch (err) {
2989
- spinner.stop();
3382
+ spinner.fail();
2990
3383
  printer.error(err);
2991
3384
  process.exit(resolveExitCode6(err));
2992
3385
  }
@@ -3002,7 +3395,7 @@ function createKbCommand() {
3002
3395
  spinner.stop();
3003
3396
  printer.log(result);
3004
3397
  } catch (err) {
3005
- spinner.stop();
3398
+ spinner.fail();
3006
3399
  printer.error(err);
3007
3400
  process.exit(resolveExitCode6(err));
3008
3401
  }
@@ -3022,7 +3415,7 @@ function createKbCommand() {
3022
3415
  spinner.stop();
3023
3416
  printer.log(`Knowledge base "${id}" deleted.`);
3024
3417
  } catch (err) {
3025
- spinner.stop();
3418
+ spinner.fail();
3026
3419
  printer.error(err);
3027
3420
  process.exit(resolveExitCode6(err));
3028
3421
  }
@@ -3044,7 +3437,7 @@ function createKbCommand() {
3044
3437
  }));
3045
3438
  printer.table(rows, ["kind", "score", "uri", "content"]);
3046
3439
  } catch (err) {
3047
- spinner.stop();
3440
+ spinner.fail();
3048
3441
  printer.error(err);
3049
3442
  process.exit(resolveExitCode6(err));
3050
3443
  }
@@ -3060,7 +3453,7 @@ function createKbCommand() {
3060
3453
  spinner.stop();
3061
3454
  printer.log(doc);
3062
3455
  } catch (err) {
3063
- spinner.stop();
3456
+ spinner.fail();
3064
3457
  printer.error(err);
3065
3458
  process.exit(resolveExitCode6(err));
3066
3459
  }
@@ -3083,7 +3476,7 @@ function createKbCommand() {
3083
3476
  }));
3084
3477
  printer.table(rows, ["id", "name", "type", "status", "createdAt"]);
3085
3478
  } catch (err) {
3086
- spinner.stop();
3479
+ spinner.fail();
3087
3480
  printer.error(err);
3088
3481
  process.exit(resolveExitCode6(err));
3089
3482
  }
@@ -3139,7 +3532,7 @@ function createIdeasCommand() {
3139
3532
  }));
3140
3533
  printer.table(rows, ["id", "platform", "score", "title"]);
3141
3534
  } catch (err) {
3142
- spinner.stop();
3535
+ spinner.fail();
3143
3536
  printer.error(err);
3144
3537
  process.exit(resolveExitCode7(err));
3145
3538
  }
@@ -3163,7 +3556,7 @@ function createIdeasCommand() {
3163
3556
  }));
3164
3557
  printer.table(rows, ["id", "topic", "platform", "score", "status", "title"]);
3165
3558
  } catch (err) {
3166
- spinner.stop();
3559
+ spinner.fail();
3167
3560
  printer.error(err);
3168
3561
  process.exit(resolveExitCode7(err));
3169
3562
  }
@@ -3184,7 +3577,7 @@ function createIdeasCommand() {
3184
3577
  spinner.stop();
3185
3578
  printer.log(`Enrichment queued. Job ID: ${jobId}`);
3186
3579
  } catch (err) {
3187
- spinner.stop();
3580
+ spinner.fail();
3188
3581
  printer.error(err);
3189
3582
  process.exit(resolveExitCode7(err));
3190
3583
  }
@@ -3205,7 +3598,7 @@ function createIdeasCommand() {
3205
3598
  spinner.stop();
3206
3599
  printer.log(`Idea "${id}" deleted.`);
3207
3600
  } catch (err) {
3208
- spinner.stop();
3601
+ spinner.fail();
3209
3602
  printer.error(err);
3210
3603
  process.exit(resolveExitCode7(err));
3211
3604
  }
@@ -3252,7 +3645,7 @@ function createUsageCommand() {
3252
3645
  }));
3253
3646
  printer.table(sourceRows, ["source", "posts"]);
3254
3647
  } catch (err) {
3255
- spinner.stop();
3648
+ spinner.fail();
3256
3649
  printer.error(err);
3257
3650
  process.exit(resolveExitCode8(err));
3258
3651
  }
@@ -3288,7 +3681,7 @@ function createWebhooksCommand() {
3288
3681
  }));
3289
3682
  printer.table(rows, ["id", "url", "events", "active", "createdAt"]);
3290
3683
  } catch (err) {
3291
- spinner.stop();
3684
+ spinner.fail();
3292
3685
  printer.error(err);
3293
3686
  process.exit(resolveExitCode9(err));
3294
3687
  }
@@ -3304,7 +3697,7 @@ function createWebhooksCommand() {
3304
3697
  spinner.stop();
3305
3698
  printer.log(webhook);
3306
3699
  } catch (err) {
3307
- spinner.stop();
3700
+ spinner.fail();
3308
3701
  printer.error(err);
3309
3702
  process.exit(resolveExitCode9(err));
3310
3703
  }
@@ -3325,7 +3718,7 @@ function createWebhooksCommand() {
3325
3718
  spinner.stop();
3326
3719
  printer.log(webhook);
3327
3720
  } catch (err) {
3328
- spinner.stop();
3721
+ spinner.fail();
3329
3722
  printer.error(err);
3330
3723
  process.exit(resolveExitCode9(err));
3331
3724
  }
@@ -3347,7 +3740,7 @@ function createWebhooksCommand() {
3347
3740
  spinner.stop();
3348
3741
  printer.log(webhook);
3349
3742
  } catch (err) {
3350
- spinner.stop();
3743
+ spinner.fail();
3351
3744
  printer.error(err);
3352
3745
  process.exit(resolveExitCode9(err));
3353
3746
  }
@@ -3368,7 +3761,7 @@ function createWebhooksCommand() {
3368
3761
  spinner.stop();
3369
3762
  printer.log(`Webhook "${id}" deleted.`);
3370
3763
  } catch (err) {
3371
- spinner.stop();
3764
+ spinner.fail();
3372
3765
  printer.error(err);
3373
3766
  process.exit(resolveExitCode9(err));
3374
3767
  }
@@ -3386,7 +3779,7 @@ function createWebhooksCommand() {
3386
3779
  `Test event sent to webhook "${id}" \u2014 delivered: ${result.delivered ? "yes" : "no"} (HTTP ${result.httpStatus}).`
3387
3780
  );
3388
3781
  } catch (err) {
3389
- spinner.stop();
3782
+ spinner.fail();
3390
3783
  printer.error(err);
3391
3784
  process.exit(resolveExitCode9(err));
3392
3785
  }
@@ -3422,7 +3815,7 @@ function createWorkspacesCommand() {
3422
3815
  }));
3423
3816
  printer.table(rows, ["id", "name", "slug", "active", "createdAt"]);
3424
3817
  } catch (err) {
3425
- spinner.stop();
3818
+ spinner.fail();
3426
3819
  printer.error(err);
3427
3820
  process.exit(resolveExitCode10(err));
3428
3821
  }
@@ -3433,13 +3826,12 @@ function createWorkspacesCommand() {
3433
3826
  const spinner = printer.spinner(`Switching to workspace "${id}"\u2026`);
3434
3827
  try {
3435
3828
  const cred = resolveAuth({ apiKey: globals.apiKey });
3436
- const authSource = cred.source === "flag" || cred.source === "env" || cred.source === "stored" ? "api-key" : "session";
3437
- const client = new Autoposting({ apiKey: cred.apiKey, authSource });
3829
+ const client = new Autoposting({ apiKey: cred.apiKey });
3438
3830
  await client.workspaces.switchWorkspace(id);
3439
3831
  spinner.stop();
3440
3832
  printer.log(`Switched to workspace "${id}".`);
3441
3833
  } catch (err) {
3442
- spinner.stop();
3834
+ spinner.fail();
3443
3835
  printer.error(err);
3444
3836
  process.exit(resolveExitCode10(err));
3445
3837
  }
@@ -3450,7 +3842,7 @@ function createWorkspacesCommand() {
3450
3842
  // src/commands/doctor.ts
3451
3843
  var import_commander13 = require("commander");
3452
3844
  init_dist();
3453
- var import_node_fs2 = __toESM(require("fs"));
3845
+ var import_node_fs3 = __toESM(require("fs"));
3454
3846
  var API_HEALTH_URL = "https://app.autoposting.ai/api-proxy/health";
3455
3847
  async function runChecks() {
3456
3848
  const results = [];
@@ -3470,7 +3862,7 @@ async function runChecks() {
3470
3862
  }
3471
3863
  const credPath = getCredentialsPath();
3472
3864
  try {
3473
- const stat = import_node_fs2.default.statSync(credPath);
3865
+ const stat = import_node_fs3.default.statSync(credPath);
3474
3866
  const mode = stat.mode & 511;
3475
3867
  const isSecure = mode === 384;
3476
3868
  results.push({
@@ -3536,9 +3928,10 @@ function createWhoamiCommand() {
3536
3928
  }
3537
3929
  const masked = `${cred.apiKey.slice(0, 8)}${"*".repeat(Math.max(0, cred.apiKey.length - 8))}`;
3538
3930
  let validity = "unverified";
3931
+ let identity;
3539
3932
  try {
3540
- const client = new Autoposting({ apiKey: cred.apiKey });
3541
- await client.usage.summary();
3933
+ const client = new Autoposting({ apiKey: cred.apiKey, maxRetries: 0 });
3934
+ identity = await client.getProfile();
3542
3935
  validity = "valid";
3543
3936
  } catch (err) {
3544
3937
  const code = exitCodeFromError(err);
@@ -3555,6 +3948,11 @@ function createWhoamiCommand() {
3555
3948
  valid: validity === "valid",
3556
3949
  validity
3557
3950
  };
3951
+ if (identity?.orgId) {
3952
+ out.orgId = identity.orgId;
3953
+ out.authType = identity.authType;
3954
+ if (identity.email) out.email = identity.email;
3955
+ }
3558
3956
  if (cred.source === "stored") {
3559
3957
  out.profile = profileName;
3560
3958
  if (email) out.email = email;
@@ -3568,6 +3966,10 @@ function createWhoamiCommand() {
3568
3966
  console.log(`Profile: ${profileName}`);
3569
3967
  if (email) console.log(`Email: ${email}`);
3570
3968
  }
3969
+ if (identity?.orgId) {
3970
+ console.log(`Org: ${identity.orgId}`);
3971
+ console.log(`Auth: ${identity.authType}`);
3972
+ }
3571
3973
  const tag = validity === "valid" ? "\u2713 valid" : validity === "rejected" ? "\u2717 rejected by server" : "\u26A0 unverified (could not reach server)";
3572
3974
  console.log(`API key: ${masked} ${tag}`);
3573
3975
  process.exitCode = exitCode;