birdclaw 0.9.2 → 0.9.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cli/birdclaw.js +1227 -1065
  3. package/dist/client/assets/AvatarChip-C8zELEVt.js +1 -0
  4. package/dist/client/assets/ProfilePreview-CsiLpFrb.js +1 -0
  5. package/dist/client/assets/SavedTimelineView-BSnwnujP.js +1 -0
  6. package/dist/client/assets/SmartTimestamp-BypN4KPq.js +1 -0
  7. package/dist/client/assets/TimelineRouteFrame-H3D4R0Xu.js +1 -0
  8. package/dist/client/assets/blocks-WsAOmHsv.js +1 -0
  9. package/dist/client/assets/{bookmarks-DZIMTKEp.js → bookmarks-B2I0kAWY.js} +1 -1
  10. package/dist/client/assets/circle-check-BCK_qvXt.js +1 -0
  11. package/dist/client/assets/{client-stream-contracts-DcKcyZhg.js → client-stream-contracts-D1QUdWfh.js} +1 -1
  12. package/dist/client/assets/createLucideIcon-CMLcIxOz.js +1 -0
  13. package/dist/client/assets/data-sources-DI5W9oVq.js +1 -0
  14. package/dist/client/assets/discuss-t2QxXK0h.js +1 -0
  15. package/dist/client/assets/dms-DjyXURaJ.js +1 -0
  16. package/dist/client/assets/inbox-CkHVoykL.js +1 -0
  17. package/dist/client/assets/index-BR860w88.js +31 -0
  18. package/dist/client/assets/likes-CenHqxQG.js +1 -0
  19. package/dist/client/assets/links-lDqy93u8.js +1 -0
  20. package/dist/client/assets/mapbox-CVPXIYUZ.js +2 -0
  21. package/dist/client/assets/{mapbox-gl-CPemBIlO.js → mapbox-gl-DRRiLKWb.js} +1 -1
  22. package/dist/client/assets/{mentions-DBrY-UPg.js → mentions-B6BXntri.js} +1 -1
  23. package/dist/client/assets/network-map-BCUtBk8G.js +2 -0
  24. package/dist/client/assets/profile-analyze-YExKd6Lx.js +1 -0
  25. package/dist/client/assets/{profile-hydration-client-BNe0sUmK.js → profile-hydration-client-BQyC5--e.js} +1 -1
  26. package/dist/client/assets/profiles._handle-idhd3kYo.js +1 -0
  27. package/dist/client/assets/query-client-DZ5C7UNo.js +17 -0
  28. package/dist/client/assets/rate-limits-jGxIfvUD.js +1 -0
  29. package/dist/client/assets/react-BwGzW7mM.js +1 -0
  30. package/dist/client/assets/react-dom-DEMAB80t.js +1 -0
  31. package/dist/client/assets/{repeat-2-xCwHM9hD.js → repeat-2-CQ0lek01.js} +1 -1
  32. package/dist/client/assets/rolldown-runtime-B-9mzPXz.js +1 -0
  33. package/dist/client/assets/{routes-Bl-Lw2hN.js → routes-6U4Cc79j.js} +1 -1
  34. package/dist/client/assets/schemas-CntsNLmN.js +64 -0
  35. package/dist/client/assets/search-C_LqodNa.js +1 -0
  36. package/dist/client/assets/{sparkles-CQVlvHsZ.js → sparkles-Gx_BHkDX.js} +1 -1
  37. package/dist/client/assets/{styles-zihNNCGg.css → styles-DT4Pxoi-.css} +1 -1
  38. package/dist/client/assets/today-COmNSYaE.js +1 -0
  39. package/dist/client/assets/{triangle-alert-CBL7s2pf.js → triangle-alert-DJPCW6jQ.js} +1 -1
  40. package/dist/client/assets/useTimelineRouteData-BpQzU16P.js +1 -0
  41. package/dist/client/assets/{users-CHbKlqUr.js → users-DI3w8Mol.js} +1 -1
  42. package/dist/server/assets/{_tanstack-start-manifest_v-CVfs07C8.js → _tanstack-start-manifest_v-mm-2VYBo.js} +35 -35
  43. package/dist/server/assets/{empty-plugin-adapters-DzrEkdBF.js → empty-plugin-adapters-D9UWiqvJ.js} +1 -1
  44. package/dist/server/assets/{router-D8AUR_Gz.js → router-B874XqWN.js} +708 -689
  45. package/dist/server/assets/start-5Z2QO8AU.js +4 -0
  46. package/dist/server/server.js +50 -3413
  47. package/package.json +1 -1
  48. package/dist/client/assets/AvatarChip-BN3b4MhM.js +0 -1
  49. package/dist/client/assets/ProfilePreview-CHbT0U9a.js +0 -1
  50. package/dist/client/assets/SavedTimelineView-mGXE9EYJ.js +0 -1
  51. package/dist/client/assets/SmartTimestamp-DG8XJKUz.js +0 -1
  52. package/dist/client/assets/TimelineRouteFrame-BmMC7qLj.js +0 -1
  53. package/dist/client/assets/blocks-CawnB0mN.js +0 -1
  54. package/dist/client/assets/chunk-C0phanGV.js +0 -1
  55. package/dist/client/assets/circle-check-C_Vq3ElD.js +0 -1
  56. package/dist/client/assets/createLucideIcon-JV8isFFR.js +0 -1
  57. package/dist/client/assets/data-sources-BJUT2qaO.js +0 -1
  58. package/dist/client/assets/discuss-DMPJVaRk.js +0 -1
  59. package/dist/client/assets/dms-BfPFG4Qr.js +0 -1
  60. package/dist/client/assets/inbox-DmeYJ7tt.js +0 -1
  61. package/dist/client/assets/index-Bqmo4MbK.js +0 -31
  62. package/dist/client/assets/likes-BxVL-j_C.js +0 -1
  63. package/dist/client/assets/links-BeA1Wt8F.js +0 -1
  64. package/dist/client/assets/mapbox-BTSMNSry.js +0 -2
  65. package/dist/client/assets/network-map-MqbE164r.js +0 -2
  66. package/dist/client/assets/profile-analyze-DEA75Reh.js +0 -1
  67. package/dist/client/assets/profiles._handle-K34ofz9U.js +0 -1
  68. package/dist/client/assets/query-client-DUP1ZzP7.js +0 -17
  69. package/dist/client/assets/rate-limits-B8Wmpt9r.js +0 -1
  70. package/dist/client/assets/react-dom-DHl6Ug7F.js +0 -1
  71. package/dist/client/assets/react-nBylq1U5.js +0 -1
  72. package/dist/client/assets/schemas-CaALBBpc.js +0 -64
  73. package/dist/client/assets/search-Dq5kYsxA.js +0 -1
  74. package/dist/client/assets/today-DFvZ0Ys5.js +0 -1
  75. package/dist/client/assets/useTimelineRouteData-Cpf-Jqsx.js +0 -1
  76. package/dist/server/assets/start-FaDOTmto.js +0 -4
@@ -5090,7 +5090,7 @@ function createCommandContext(program2) {
5090
5090
 
5091
5091
  // src/lib/period-digest.ts
5092
5092
  import { createHash as createHash5 } from "node:crypto";
5093
- import { Effect as Effect17 } from "effect";
5093
+ import { Effect as Effect16 } from "effect";
5094
5094
  import { z } from "zod";
5095
5095
 
5096
5096
  // src/lib/analysis-runtime.ts
@@ -6273,10 +6273,7 @@ function getLinkInsights(query = {}) {
6273
6273
  }
6274
6274
 
6275
6275
  // src/lib/mention-threads-live.ts
6276
- import { Effect as Effect14 } from "effect";
6277
-
6278
- // src/lib/live-transport-gateway.ts
6279
- import { Effect as Effect10 } from "effect";
6276
+ import { Effect as Effect13 } from "effect";
6280
6277
 
6281
6278
  // src/lib/bird.ts
6282
6279
  import { execFile as execFile2 } from "node:child_process";
@@ -6386,8 +6383,8 @@ function makeBirdStdoutTempEffect() {
6386
6383
  ({ tempDir }) => Effect8.sync(() => rmSync(tempDir, { recursive: true, force: true }))
6387
6384
  );
6388
6385
  }
6389
- function runBirdJsonCommandEffect(args, timeoutMs) {
6390
- return Effect8.scoped(
6386
+ var runBirdJsonCommandEffect = Effect8.fn("bird.runJsonCommand")(
6387
+ (args, timeoutMs) => Effect8.scoped(
6391
6388
  Effect8.gen(function* () {
6392
6389
  const birdCommand = yield* Effect8.try({
6393
6390
  try: () => getBirdCommand(),
@@ -6414,10 +6411,12 @@ function runBirdJsonCommandEffect(args, timeoutMs) {
6414
6411
  catch: (error) => error
6415
6412
  });
6416
6413
  })
6417
- );
6418
- }
6419
- function runBirdJsonCommandAllowFailureEffect(args, timeoutMs) {
6420
- return Effect8.scoped(
6414
+ )
6415
+ );
6416
+ var runBirdJsonCommandAllowFailureEffect = Effect8.fn(
6417
+ "bird.runJsonCommandAllowFailure"
6418
+ )(
6419
+ (args, timeoutMs) => Effect8.scoped(
6421
6420
  Effect8.gen(function* () {
6422
6421
  const birdCommand = yield* Effect8.try({
6423
6422
  try: () => getBirdCommand(),
@@ -6450,8 +6449,8 @@ function runBirdJsonCommandAllowFailureEffect(args, timeoutMs) {
6450
6449
  catch: (error) => error
6451
6450
  });
6452
6451
  })
6453
- );
6454
- }
6452
+ )
6453
+ );
6455
6454
  function runBirdTweetJsonCommandEffect(args, timeoutMs) {
6456
6455
  return runBirdJsonCommandEffect([...args, "--json-full"], timeoutMs).pipe(
6457
6456
  Effect8.catchAll((error) => {
@@ -6641,10 +6640,8 @@ function normalizeBirdTweetItemEffect(payload, command) {
6641
6640
  catch: (error) => error
6642
6641
  });
6643
6642
  }
6644
- function listMentionsViaBirdEffect({
6645
- maxResults
6646
- }) {
6647
- return Effect8.gen(function* () {
6643
+ var listMentionsViaBirdEffect = Effect8.fn("bird.listMentions")(
6644
+ function* ({ maxResults }) {
6648
6645
  const stdout = yield* runBirdTweetJsonCommandEffect([
6649
6646
  "mentions",
6650
6647
  "-n",
@@ -6652,27 +6649,25 @@ function listMentionsViaBirdEffect({
6652
6649
  ]);
6653
6650
  const payload = yield* parseBirdJsonEffect(stdout);
6654
6651
  return yield* normalizeBirdTweetsPayloadEffect(payload, "mentions");
6655
- });
6656
- }
6657
- function listTweetsViaBirdCommandEffect({
6652
+ }
6653
+ );
6654
+ var listTweetsViaBirdCommandEffect = Effect8.fn("bird.listTweets")(function* ({
6658
6655
  command,
6659
6656
  maxResults,
6660
6657
  all,
6661
6658
  maxPages
6662
6659
  }) {
6663
- return Effect8.gen(function* () {
6664
- const args = [command, "-n", String(maxResults)];
6665
- if (all) {
6666
- args.push("--all");
6667
- if (maxPages !== void 0) {
6668
- args.push("--max-pages", String(maxPages));
6669
- }
6660
+ const args = [command, "-n", String(maxResults)];
6661
+ if (all) {
6662
+ args.push("--all");
6663
+ if (maxPages !== void 0) {
6664
+ args.push("--max-pages", String(maxPages));
6670
6665
  }
6671
- const stdout = yield* runBirdTweetJsonCommandEffect(args);
6672
- const payload = yield* parseBirdJsonEffect(stdout);
6673
- return yield* normalizeBirdTweetsPayloadEffect(payload, command);
6674
- });
6675
- }
6666
+ }
6667
+ const stdout = yield* runBirdTweetJsonCommandEffect(args);
6668
+ const payload = yield* parseBirdJsonEffect(stdout);
6669
+ return yield* normalizeBirdTweetsPayloadEffect(payload, command);
6670
+ });
6676
6671
  function listLikedTweetsViaBirdEffect(options) {
6677
6672
  return listTweetsViaBirdCommandEffect({
6678
6673
  command: "likes",
@@ -6685,8 +6680,8 @@ function listBookmarkedTweetsViaBirdEffect(options) {
6685
6680
  ...options
6686
6681
  });
6687
6682
  }
6688
- function searchTweetsViaBirdEffect(query, options) {
6689
- return Effect8.gen(function* () {
6683
+ var searchTweetsViaBirdEffect = Effect8.fn("bird.searchTweets")(
6684
+ function* (query, options) {
6690
6685
  const args = ["search", query, "-n", String(options.maxResults)];
6691
6686
  if (options.all) {
6692
6687
  args.push("--all");
@@ -6697,30 +6692,30 @@ function searchTweetsViaBirdEffect(query, options) {
6697
6692
  const stdout = yield* runBirdTweetJsonCommandEffect(args);
6698
6693
  const payload = yield* parseBirdJsonEffect(stdout);
6699
6694
  return yield* normalizeBirdTweetsPayloadEffect(payload, "search");
6700
- });
6701
- }
6702
- function lookupTweetsByIdsViaBirdEffect(ids) {
6695
+ }
6696
+ );
6697
+ var lookupTweetsByIdsViaBirdEffect = Effect8.fn(
6698
+ "bird.lookupTweetsByIds"
6699
+ )(function* (ids) {
6703
6700
  if (ids.length === 0) {
6704
- return Effect8.succeed({ data: [] });
6705
- }
6706
- return Effect8.gen(function* () {
6707
- const tweets = yield* Effect8.forEach(
6708
- ids,
6709
- (id) => Effect8.gen(function* () {
6710
- const stdout = yield* runBirdTweetJsonCommandEffect(["read", id]);
6711
- const payload = yield* parseBirdJsonEffect(stdout);
6712
- return yield* normalizeBirdTweetItemEffect(payload, "read");
6713
- }),
6714
- { concurrency: "unbounded" }
6715
- );
6716
- return normalizeBirdTweets(tweets);
6717
- });
6718
- }
6719
- function listHomeTimelineViaBirdEffect({
6720
- maxResults,
6721
- following = true
6722
- }) {
6723
- return Effect8.gen(function* () {
6701
+ return { data: [] };
6702
+ }
6703
+ const tweets = yield* Effect8.forEach(
6704
+ ids,
6705
+ Effect8.fn("bird.readTweet")(function* (id) {
6706
+ const stdout = yield* runBirdTweetJsonCommandEffect(["read", id]);
6707
+ const payload = yield* parseBirdJsonEffect(stdout);
6708
+ return yield* normalizeBirdTweetItemEffect(payload, "read");
6709
+ }),
6710
+ { concurrency: "unbounded" }
6711
+ );
6712
+ return normalizeBirdTweets(tweets);
6713
+ });
6714
+ var listHomeTimelineViaBirdEffect = Effect8.fn("bird.listHomeTimeline")(
6715
+ function* ({
6716
+ maxResults,
6717
+ following = true
6718
+ }) {
6724
6719
  const args = ["home", "-n", String(maxResults)];
6725
6720
  if (following) {
6726
6721
  args.push("--following");
@@ -6728,8 +6723,8 @@ function listHomeTimelineViaBirdEffect({
6728
6723
  const stdout = yield* runBirdTweetJsonCommandEffect(args);
6729
6724
  const payload = yield* parseBirdJsonEffect(stdout);
6730
6725
  return yield* normalizeBirdTweetsPayloadEffect(payload, "home");
6731
- });
6732
- }
6726
+ }
6727
+ );
6733
6728
  function normalizeBirdFollowUsers(payload, command, maxResults) {
6734
6729
  const rawPayload = payload;
6735
6730
  const users = Array.isArray(rawPayload) ? rawPayload : rawPayload.users;
@@ -6755,14 +6750,14 @@ function normalizeBirdFollowUsersEffect(payload, command, maxResults) {
6755
6750
  catch: (error) => error
6756
6751
  });
6757
6752
  }
6758
- function listFollowUsersViaBirdEffect({
6759
- direction,
6760
- userId,
6761
- maxResults,
6762
- all,
6763
- maxPages
6764
- }) {
6765
- return Effect8.gen(function* () {
6753
+ var listFollowUsersViaBirdEffect = Effect8.fn("bird.listFollowUsers")(
6754
+ function* ({
6755
+ direction,
6756
+ userId,
6757
+ maxResults,
6758
+ all,
6759
+ maxPages
6760
+ }) {
6766
6761
  const args = [direction, "-n", String(maxResults), "--json"];
6767
6762
  if (userId) {
6768
6763
  args.push("--user", userId);
@@ -6780,8 +6775,8 @@ function listFollowUsersViaBirdEffect({
6780
6775
  direction,
6781
6776
  maxResults
6782
6777
  );
6783
- });
6784
- }
6778
+ }
6779
+ );
6785
6780
  function normalizeBirdLists(payload) {
6786
6781
  if (!Array.isArray(payload)) {
6787
6782
  throw new Error("bird lists returned unexpected JSON");
@@ -6805,10 +6800,8 @@ function normalizeBirdLists(payload) {
6805
6800
  }).filter((item) => Boolean(item));
6806
6801
  return { data, meta: { result_count: data.length, next_token: null } };
6807
6802
  }
6808
- function listOwnedXListsViaBirdEffect({
6809
- maxResults
6810
- }) {
6811
- return Effect8.gen(function* () {
6803
+ var listOwnedXListsViaBirdEffect = Effect8.fn("bird.listOwnedXLists")(
6804
+ function* ({ maxResults }) {
6812
6805
  const stdout = yield* runBirdJsonCommandEffect([
6813
6806
  "lists",
6814
6807
  "-n",
@@ -6820,14 +6813,14 @@ function listOwnedXListsViaBirdEffect({
6820
6813
  try: () => normalizeBirdLists(payload),
6821
6814
  catch: (error) => error
6822
6815
  });
6823
- });
6824
- }
6825
- function listXListMembersViaBirdEffect({
6826
- listId,
6827
- maxResults,
6828
- maxPages = 1
6829
- }) {
6830
- return Effect8.gen(function* () {
6816
+ }
6817
+ );
6818
+ var listXListMembersViaBirdEffect = Effect8.fn("bird.listXListMembers")(
6819
+ function* ({
6820
+ listId,
6821
+ maxResults,
6822
+ maxPages = 1
6823
+ }) {
6831
6824
  const args = ["list-members", listId, "-n", String(maxResults), "--json"];
6832
6825
  if (maxPages > 1) {
6833
6826
  args.push("--all", "--max-pages", String(maxPages));
@@ -6847,27 +6840,25 @@ function listXListMembersViaBirdEffect({
6847
6840
  };
6848
6841
  }
6849
6842
  return normalized;
6850
- });
6851
- }
6852
- function listThreadViaBirdEffect({
6843
+ }
6844
+ );
6845
+ var listThreadViaBirdEffect = Effect8.fn("bird.listThread")(function* ({
6853
6846
  tweetId,
6854
6847
  all,
6855
6848
  maxPages,
6856
6849
  timeoutMs
6857
6850
  }) {
6858
- return Effect8.gen(function* () {
6859
- const args = ["thread", tweetId];
6860
- if (all) {
6861
- args.push("--all");
6862
- }
6863
- if (maxPages !== void 0) {
6864
- args.push("--max-pages", String(maxPages));
6865
- }
6866
- const stdout = yield* runBirdTweetJsonCommandEffect(args, timeoutMs);
6867
- const payload = yield* parseBirdJsonEffect(stdout);
6868
- return yield* normalizeBirdTweetsPayloadEffect(payload, "thread");
6869
- });
6870
- }
6851
+ const args = ["thread", tweetId];
6852
+ if (all) {
6853
+ args.push("--all");
6854
+ }
6855
+ if (maxPages !== void 0) {
6856
+ args.push("--max-pages", String(maxPages));
6857
+ }
6858
+ const stdout = yield* runBirdTweetJsonCommandEffect(args, timeoutMs);
6859
+ const payload = yield* parseBirdJsonEffect(stdout);
6860
+ return yield* normalizeBirdTweetsPayloadEffect(payload, "thread");
6861
+ });
6871
6862
  function normalizeBirdDmsPayloadEffect(payload) {
6872
6863
  return Effect8.try({
6873
6864
  try: () => {
@@ -6903,74 +6894,71 @@ function parseBirdWhoami(stdout) {
6903
6894
  ...name ? { name } : {}
6904
6895
  };
6905
6896
  }
6906
- function getAuthenticatedBirdAccountEffect() {
6907
- return Effect8.gen(function* () {
6908
- const stdout = yield* runBirdJsonCommandEffect(["whoami"]);
6909
- return yield* Effect8.try({
6910
- try: () => parseBirdWhoami(stdout),
6911
- catch: (error) => error
6912
- });
6897
+ var getAuthenticatedBirdAccountEffect = Effect8.fn(
6898
+ "bird.getAuthenticatedAccount"
6899
+ )(function* () {
6900
+ const stdout = yield* runBirdJsonCommandEffect(["whoami"]);
6901
+ return yield* Effect8.try({
6902
+ try: () => parseBirdWhoami(stdout),
6903
+ catch: (error) => error
6913
6904
  });
6914
- }
6915
- function getAuthenticatedBirdAccount() {
6916
- return runEffectPromise(getAuthenticatedBirdAccountEffect());
6917
- }
6918
- function listDirectMessagesViaBirdEffect({
6905
+ });
6906
+ var listDirectMessagesViaBirdEffect = Effect8.fn(
6907
+ "bird.listDirectMessages"
6908
+ )(function* ({
6919
6909
  maxResults,
6920
6910
  inbox = "all",
6921
6911
  maxPages,
6922
6912
  allPages = false,
6923
6913
  pageDelayMs
6924
6914
  }) {
6925
- return Effect8.gen(function* () {
6926
- const args = ["dms", "-n", String(maxResults), "--json"];
6927
- if (inbox !== "all") {
6928
- args.push("--inbox", inbox);
6929
- }
6930
- if (allPages) {
6931
- args.push("--all-pages");
6932
- } else if (typeof maxPages === "number") {
6933
- args.push("--max-pages", String(maxPages));
6934
- }
6935
- if (typeof pageDelayMs === "number" && pageDelayMs > 0) {
6936
- args.push("--page-delay-ms", String(pageDelayMs));
6937
- }
6938
- const stdout = yield* runBirdJsonCommandEffect(args);
6939
- const payload = yield* parseBirdJsonEffect(stdout);
6940
- return yield* normalizeBirdDmsPayloadEffect(payload);
6941
- });
6942
- }
6943
- function runDirectMessageRequestMutationViaBirdEffect({
6915
+ const args = ["dms", "-n", String(maxResults), "--json"];
6916
+ if (inbox !== "all") {
6917
+ args.push("--inbox", inbox);
6918
+ }
6919
+ if (allPages) {
6920
+ args.push("--all-pages");
6921
+ } else if (typeof maxPages === "number") {
6922
+ args.push("--max-pages", String(maxPages));
6923
+ }
6924
+ if (typeof pageDelayMs === "number" && pageDelayMs > 0) {
6925
+ args.push("--page-delay-ms", String(pageDelayMs));
6926
+ }
6927
+ const stdout = yield* runBirdJsonCommandEffect(args);
6928
+ const payload = yield* parseBirdJsonEffect(stdout);
6929
+ return yield* normalizeBirdDmsPayloadEffect(payload);
6930
+ });
6931
+ var runDirectMessageRequestMutationViaBirdEffect = Effect8.fn(
6932
+ "bird.runDirectMessageRequestMutation"
6933
+ )(function* ({
6944
6934
  action,
6945
6935
  conversationId,
6946
6936
  maxPages,
6947
6937
  allPages = false
6948
6938
  }) {
6949
- return Effect8.gen(function* () {
6950
- const command = action === "accept" ? "dm-accept" : action === "reject" ? "dm-reject" : "dm-block";
6951
- const args = [command, conversationId, "--json"];
6952
- if (action === "block") {
6953
- if (allPages) {
6954
- args.push("--all-pages");
6955
- } else if (typeof maxPages === "number") {
6956
- args.push("--max-pages", String(maxPages));
6957
- }
6958
- }
6959
- const stdout = yield* runBirdJsonCommandAllowFailureEffect(args);
6960
- const payload = yield* parseBirdJsonEffect(stdout);
6961
- if (payload && typeof payload === "object" && typeof payload.success === "boolean") {
6962
- return payload;
6939
+ const command = action === "accept" ? "dm-accept" : action === "reject" ? "dm-reject" : "dm-block";
6940
+ const args = [command, conversationId, "--json"];
6941
+ if (action === "block") {
6942
+ if (allPages) {
6943
+ args.push("--all-pages");
6944
+ } else if (typeof maxPages === "number") {
6945
+ args.push("--max-pages", String(maxPages));
6963
6946
  }
6964
- throw new Error(`bird ${command} returned unexpected JSON`);
6965
- });
6966
- }
6947
+ }
6948
+ const stdout = yield* runBirdJsonCommandAllowFailureEffect(args);
6949
+ const payload = yield* parseBirdJsonEffect(stdout);
6950
+ if (payload && typeof payload === "object" && typeof payload.success === "boolean") {
6951
+ return payload;
6952
+ }
6953
+ throw new Error(`bird ${command} returned unexpected JSON`);
6954
+ });
6967
6955
  function runDirectMessageRequestMutationViaBird(options) {
6968
6956
  return runEffectPromise(
6969
6957
  runDirectMessageRequestMutationViaBirdEffect(options)
6970
6958
  );
6971
6959
  }
6972
- function lookupProfileViaBirdEffect(usernameOrId) {
6973
- return Effect8.gen(function* () {
6960
+ var lookupProfileViaBirdEffect = Effect8.fn("bird.lookupProfile")(
6961
+ function* (usernameOrId) {
6974
6962
  const target = usernameOrId.trim().replace(/^@/, "");
6975
6963
  if (!target) {
6976
6964
  return null;
@@ -6998,8 +6986,8 @@ function lookupProfileViaBirdEffect(usernameOrId) {
6998
6986
  stdout
6999
6987
  );
7000
6988
  return toXurlMentionUser(payload.user);
7001
- });
7002
- }
6989
+ }
6990
+ );
7003
6991
  function toXurlMentionUser(user) {
7004
6992
  if (!user?.id || !user.username) {
7005
6993
  return null;
@@ -7023,7 +7011,7 @@ function toXurlMentionUser(user) {
7023
7011
  }
7024
7012
  };
7025
7013
  }
7026
- function lookupProfilesViaBirdEffect(usernameOrIds) {
7014
+ var lookupProfilesViaBirdEffect = Effect8.fn("bird.lookupProfiles")((usernameOrIds) => {
7027
7015
  const targets = Array.from(
7028
7016
  new Set(
7029
7017
  usernameOrIds.map((target) => target.trim().replace(/^@/, "")).filter((target) => target.length > 0)
@@ -7077,10 +7065,10 @@ function lookupProfilesViaBirdEffect(usernameOrIds) {
7077
7065
  );
7078
7066
  })
7079
7067
  );
7080
- }
7068
+ });
7081
7069
 
7082
7070
  // src/lib/xurl.ts
7083
- import { Effect as Effect9 } from "effect";
7071
+ import { Data as Data3, Duration, Effect as Effect9, Schedule } from "effect";
7084
7072
  var TRANSPORT_STATUS_TTL_MS = 5 * 6e4;
7085
7073
  var AUTHENTICATED_USER_TTL_MS = 6e4;
7086
7074
  var JSON_RETRY_LIMIT = 6;
@@ -7091,8 +7079,18 @@ var RICH_USER_FIELDS = "description,entities,location,public_metrics,profile_ima
7091
7079
  var DM_EVENT_FIELDS = "attachments,created_at,dm_conversation_id,entities,event_type,id,participant_ids,referenced_tweets,sender_id,text";
7092
7080
  var THREAD_TWEET_FIELDS = "created_at,conversation_id,entities,public_metrics,referenced_tweets,in_reply_to_user_id,attachments";
7093
7081
  var BOOKMARKS_MAX_RESULTS_CAP = 90;
7094
- var transportStatusCache;
7095
- var authenticatedUserCache;
7082
+ var cachedTransportStatus = Effect9.runSync(
7083
+ Effect9.cachedWithTTL(
7084
+ Effect9.suspend(() => readTransportStatusEffect()),
7085
+ Duration.millis(TRANSPORT_STATUS_TTL_MS)
7086
+ )
7087
+ );
7088
+ var [cachedAuthenticatedUser, invalidateAuthenticatedUser] = Effect9.runSync(
7089
+ Effect9.cachedInvalidateWithTTL(
7090
+ Effect9.suspend(() => lookupAuthenticatedUserFreshEffect()),
7091
+ Duration.millis(AUTHENTICATED_USER_TTL_MS)
7092
+ )
7093
+ );
7096
7094
  var oauth2CandidateCache = /* @__PURE__ */ new Map();
7097
7095
  function liveWritesDisabled() {
7098
7096
  return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
@@ -7120,8 +7118,16 @@ function formatExecError(error, fallback) {
7120
7118
  }
7121
7119
  return parts.join("\n");
7122
7120
  }
7123
- function formatXurlCommandError(error, args) {
7124
- return new Error(formatExecError(error, `xurl ${args.join(" ")} failed`));
7121
+ var XurlCommandError = class extends Data3.TaggedError("XurlCommandError") {
7122
+ };
7123
+ function toXurlCommandError(cause, args) {
7124
+ if (cause instanceof XurlCommandError) return cause;
7125
+ return new XurlCommandError({
7126
+ message: formatExecError(cause, `xurl ${args.join(" ")} failed`),
7127
+ commandArgs: args,
7128
+ rateLimited: isRateLimitedFailure(cause),
7129
+ cause
7130
+ });
7125
7131
  }
7126
7132
  function normalizeError(error) {
7127
7133
  return error instanceof Error ? error : new Error(String(error));
@@ -7139,14 +7145,11 @@ function parseErrorPayload(error) {
7139
7145
  return null;
7140
7146
  }
7141
7147
  }
7142
- function getRetryDelayMs(error, attempt) {
7143
- const payload = parseErrorPayload(error);
7144
- const status = Number(payload?.status ?? 0);
7145
- if (status !== 429) {
7146
- return null;
7147
- }
7148
- const baseDelay = getJsonRetryBaseDelayMs();
7149
- return Math.min(baseDelay * 2 ** attempt, 3e4);
7148
+ function isRateLimitedFailure(error) {
7149
+ return Number(parseErrorPayload(error)?.status ?? 0) === 429;
7150
+ }
7151
+ function jsonRetryDelayMs(retries) {
7152
+ return Math.min(getJsonRetryBaseDelayMs() * 2 ** retries, 3e4);
7150
7153
  }
7151
7154
  function emitJsonCommandAttempt(options, attempt) {
7152
7155
  try {
@@ -7168,8 +7171,8 @@ function isUnauthenticatedXurlStatus(status) {
7168
7171
  status
7169
7172
  );
7170
7173
  }
7171
- function readTransportStatusEffect() {
7172
- return Effect9.gen(function* () {
7174
+ var readTransportStatusEffect = Effect9.fn("xurl.readTransportStatus")(
7175
+ function* () {
7173
7176
  const installed = yield* hasXurlEffect();
7174
7177
  if (!installed) {
7175
7178
  return {
@@ -7207,68 +7210,31 @@ function readTransportStatusEffect() {
7207
7210
  })
7208
7211
  )
7209
7212
  );
7210
- });
7211
- }
7213
+ }
7214
+ );
7212
7215
  function getTransportStatusEffect() {
7213
- return Effect9.gen(function* () {
7214
- const now2 = Date.now();
7215
- if (transportStatusCache?.value && transportStatusCache.expiresAt > now2) {
7216
- return transportStatusCache.value;
7217
- }
7218
- if (transportStatusCache?.pending) {
7219
- const status2 = yield* Effect9.tryPromise({
7220
- try: () => transportStatusCache?.pending ?? Promise.resolve(void 0),
7221
- catch: normalizeError
7222
- });
7223
- if (status2) return status2;
7224
- }
7225
- const pending = runEffectPromise(readTransportStatusEffect());
7226
- transportStatusCache = {
7227
- expiresAt: 0,
7228
- pending
7229
- };
7230
- const status = yield* Effect9.tryPromise({
7231
- try: () => pending,
7232
- catch: normalizeError
7233
- }).pipe(
7234
- Effect9.catchAll(
7235
- (error) => Effect9.sync(() => {
7236
- transportStatusCache = void 0;
7237
- }).pipe(Effect9.flatMap(() => Effect9.fail(error)))
7238
- )
7239
- );
7240
- transportStatusCache = {
7241
- expiresAt: Date.now() + TRANSPORT_STATUS_TTL_MS,
7242
- value: status
7243
- };
7244
- return status;
7245
- });
7216
+ return cachedTransportStatus;
7246
7217
  }
7247
- function getTransportStatus() {
7248
- return runEffectPromise(getTransportStatusEffect());
7249
- }
7250
- function runShortcutEffect(args) {
7251
- return Effect9.gen(function* () {
7252
- if (liveWritesDisabled()) {
7253
- if (e2eFakeLiveWritesEnabled()) {
7254
- return { ok: true, output: "e2e fake live write" };
7255
- }
7256
- return { ok: false, output: "live writes disabled" };
7218
+ var runShortcutEffect = Effect9.fn("xurl.runShortcut")(function* (args) {
7219
+ if (liveWritesDisabled()) {
7220
+ if (e2eFakeLiveWritesEnabled()) {
7221
+ return { ok: true, output: "e2e fake live write" };
7257
7222
  }
7258
- return yield* runSubprocessEffect({ command: "xurl", args }).pipe(
7259
- Effect9.map(({ stdout, stderr }) => ({
7260
- ok: true,
7261
- output: stdout || stderr
7262
- })),
7263
- Effect9.catchAll(
7264
- (error) => Effect9.succeed({
7265
- ok: false,
7266
- output: formatExecError(error, "xurl execution failed")
7267
- })
7268
- )
7269
- );
7270
- });
7271
- }
7223
+ return { ok: false, output: "live writes disabled" };
7224
+ }
7225
+ return yield* runSubprocessEffect({ command: "xurl", args }).pipe(
7226
+ Effect9.map(({ stdout, stderr }) => ({
7227
+ ok: true,
7228
+ output: stdout || stderr
7229
+ })),
7230
+ Effect9.catchAll(
7231
+ (error) => Effect9.succeed({
7232
+ ok: false,
7233
+ output: formatExecError(error, "xurl execution failed")
7234
+ })
7235
+ )
7236
+ );
7237
+ });
7272
7238
  function execXurlJsonEffect(args, timeoutMs, signal) {
7273
7239
  return runSubprocessEffect({
7274
7240
  command: "xurl",
@@ -7301,47 +7267,50 @@ function execXurlTextEffect(args, deadlineMs) {
7301
7267
  function parseJsonPayloadEffect(stdout, args) {
7302
7268
  return Effect9.try({
7303
7269
  try: () => JSON.parse(stdout),
7304
- catch: (error) => formatXurlCommandError(error, args)
7305
- });
7306
- }
7307
- function runJsonCommandEffect(args, options = {}, attempt = 0) {
7308
- return Effect9.gen(function* () {
7309
- const deadlineMs = options.deadlineMs ?? (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
7310
- const timeoutMs = deadlineMs ? Math.max(0, deadlineMs - Date.now()) : void 0;
7311
- return yield* execXurlJsonEffect(args, timeoutMs, options.signal).pipe(
7312
- Effect9.flatMap(({ stdout }) => parseJsonPayloadEffect(stdout, args)),
7313
- Effect9.tap(
7314
- () => Effect9.sync(
7315
- () => emitJsonCommandAttempt(options, { args, attempt, status: "ok" })
7316
- )
7317
- ),
7318
- Effect9.catchAll((error) => {
7319
- const retryDelayMs = getRetryDelayMs(error, attempt);
7270
+ catch: (error) => toXurlCommandError(error, args)
7271
+ });
7272
+ }
7273
+ var runJsonCommandEffect = Effect9.fn("xurl.runJsonCommand")(function* (args, options = {}) {
7274
+ const deadlineMs = options.deadlineMs ?? (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
7275
+ let attempt = 0;
7276
+ const attemptCommand = Effect9.suspend(
7277
+ () => execXurlJsonEffect(
7278
+ args,
7279
+ deadlineMs === void 0 ? void 0 : Math.max(0, deadlineMs - Date.now()),
7280
+ options.signal
7281
+ )
7282
+ ).pipe(
7283
+ Effect9.flatMap(({ stdout }) => parseJsonPayloadEffect(stdout, args)),
7284
+ Effect9.mapError((error) => toXurlCommandError(error, args)),
7285
+ Effect9.tap(
7286
+ () => Effect9.sync(
7287
+ () => emitJsonCommandAttempt(options, { args, attempt, status: "ok" })
7288
+ )
7289
+ ),
7290
+ Effect9.tapError(
7291
+ (error) => Effect9.sync(() => {
7320
7292
  emitJsonCommandAttempt(options, {
7321
7293
  args,
7322
7294
  attempt,
7323
- status: retryDelayMs === null ? "error" : "rate_limited",
7295
+ status: error.rateLimited ? "rate_limited" : "error",
7324
7296
  error
7325
7297
  });
7326
- if (retryDelayMs === null || attempt >= JSON_RETRY_LIMIT - 1) {
7327
- return Effect9.fail(formatXurlCommandError(error, args));
7328
- }
7329
- if (options.signal?.aborted) {
7330
- return Effect9.fail(formatXurlCommandError(error, args));
7331
- }
7332
- const remainingMs = deadlineMs ? Math.max(0, deadlineMs - Date.now()) : void 0;
7333
- if (remainingMs !== void 0 && retryDelayMs >= remainingMs) {
7334
- return Effect9.fail(formatXurlCommandError(error, args));
7335
- }
7336
- return Effect9.sleep(retryDelayMs).pipe(
7337
- Effect9.flatMap(
7338
- () => runJsonCommandEffect(args, { ...options, deadlineMs }, attempt + 1)
7339
- )
7340
- );
7298
+ attempt += 1;
7341
7299
  })
7342
- );
7343
- });
7344
- }
7300
+ )
7301
+ );
7302
+ const rateLimitRetries = Schedule.recurs(JSON_RETRY_LIMIT - 1).pipe(
7303
+ Schedule.whileInput(
7304
+ (error) => error.rateLimited && options.signal?.aborted !== true
7305
+ ),
7306
+ Schedule.check((_error, retries) => {
7307
+ if (deadlineMs === void 0) return true;
7308
+ return jsonRetryDelayMs(retries) < Math.max(0, deadlineMs - Date.now());
7309
+ }),
7310
+ Schedule.addDelay((retries) => Duration.millis(jsonRetryDelayMs(retries)))
7311
+ );
7312
+ return yield* Effect9.retry(attemptCommand, rateLimitRetries);
7313
+ });
7345
7314
  function cleanXurlUsernameLabel(username) {
7346
7315
  const label = username?.trim().replace(/^@/, "");
7347
7316
  return label && label !== "-" && label !== "\u2013" && label !== "(none)" && label.toLowerCase() !== "none" && label.toLowerCase() !== "unknown" && /^[^\s]{1,128}$/.test(label) ? label : void 0;
@@ -7375,33 +7344,35 @@ function parseOAuth2UsernamesFromStatus(rawStatus) {
7375
7344
  }
7376
7345
  return usernames;
7377
7346
  }
7378
- function readOAuth2UsernameCandidatesEffect(deadlineMs) {
7379
- return execXurlTextEffect(["auth", "status"], deadlineMs).pipe(
7347
+ var readOAuth2UsernameCandidatesEffect = Effect9.fn(
7348
+ "xurl.readOAuth2UsernameCandidates"
7349
+ )(
7350
+ (deadlineMs) => execXurlTextEffect(["auth", "status"], deadlineMs).pipe(
7380
7351
  Effect9.map(({ stdout }) => parseOAuth2UsernamesFromStatus(stdout)),
7381
7352
  Effect9.catchAll(() => Effect9.succeed([]))
7382
- );
7383
- }
7384
- function lookupOAuth2UsernameForAccountEffect(expectedUsername, attemptedUsernames, deadlineMs, knownCandidates) {
7385
- return Effect9.gen(function* () {
7386
- const expected = comparableXurlUsername(expectedUsername);
7387
- if (!expected) return void 0;
7388
- const candidates = knownCandidates ?? (yield* readOAuth2UsernameCandidatesEffect(deadlineMs));
7389
- for (const candidate of candidates) {
7390
- const candidateKey = `${candidate.app ?? "default"}:${candidate.username}`;
7391
- if (attemptedUsernames.has(candidateKey)) continue;
7392
- const payload = yield* runJsonCommandEffect(
7393
- oauth2ArgsForCandidate(candidate, ["/2/users/me"]),
7394
- { deadlineMs }
7395
- ).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
7396
- const user = payload ? authenticatedUserFromPayload(payload) : null;
7397
- const actual = comparableXurlUsername(String(user?.username ?? ""));
7398
- if (actual === expected) {
7399
- return candidate;
7400
- }
7353
+ )
7354
+ );
7355
+ var lookupOAuth2UsernameForAccountEffect = Effect9.fn(
7356
+ "xurl.lookupOAuth2UsernameForAccount"
7357
+ )(function* (expectedUsername, attemptedUsernames, deadlineMs, knownCandidates) {
7358
+ const expected = comparableXurlUsername(expectedUsername);
7359
+ if (!expected) return void 0;
7360
+ const candidates = knownCandidates ?? (yield* readOAuth2UsernameCandidatesEffect(deadlineMs));
7361
+ for (const candidate of candidates) {
7362
+ const candidateKey = `${candidate.app ?? "default"}:${candidate.username}`;
7363
+ if (attemptedUsernames.has(candidateKey)) continue;
7364
+ const payload = yield* runJsonCommandEffect(
7365
+ oauth2ArgsForCandidate(candidate, ["/2/users/me"]),
7366
+ { deadlineMs }
7367
+ ).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
7368
+ const user = payload ? authenticatedUserFromPayload(payload) : null;
7369
+ const actual = comparableXurlUsername(String(user?.username ?? ""));
7370
+ if (actual === expected) {
7371
+ return candidate;
7401
7372
  }
7402
- return void 0;
7403
- });
7404
- }
7373
+ }
7374
+ return void 0;
7375
+ });
7405
7376
  function oauth2ArgsForCandidate(candidate, args) {
7406
7377
  return [
7407
7378
  ...candidate?.app ? ["--app", candidate.app] : [],
@@ -7424,14 +7395,14 @@ function configuredOAuth2Candidate(primaryUsername) {
7424
7395
  ...effectiveUsername ? { username: effectiveUsername } : {}
7425
7396
  };
7426
7397
  }
7427
- function runOAuth2JsonCommandEffect({
7428
- args,
7429
- username,
7430
- options,
7431
- useConfiguredCandidate = true
7432
- }) {
7433
- const primaryUsername = cleanXurlUsernameLabel(username);
7434
- return Effect9.gen(function* () {
7398
+ var runOAuth2JsonCommandEffect = Effect9.fn("xurl.runOAuth2JsonCommand")(
7399
+ function* ({
7400
+ args,
7401
+ username,
7402
+ options,
7403
+ useConfiguredCandidate = true
7404
+ }) {
7405
+ const primaryUsername = cleanXurlUsernameLabel(username);
7435
7406
  const deadlineMs = options?.deadlineMs ?? (typeof options?.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
7436
7407
  const scopedOptions = { ...options, deadlineMs };
7437
7408
  let authCandidate = primaryUsername ? { username: primaryUsername } : void 0;
@@ -7478,7 +7449,12 @@ function runOAuth2JsonCommandEffect({
7478
7449
  }
7479
7450
  }
7480
7451
  if (deadlineMs !== void 0 && Date.now() >= deadlineMs) {
7481
- return yield* Effect9.fail(new Error("xurl OAuth2 fallback timed out"));
7452
+ return yield* new XurlCommandError({
7453
+ message: "xurl OAuth2 fallback timed out",
7454
+ commandArgs: args,
7455
+ rateLimited: false,
7456
+ cause: void 0
7457
+ });
7482
7458
  }
7483
7459
  return yield* runJsonCommandEffect(
7484
7460
  oauth2ArgsForCandidate(authCandidate, args),
@@ -7517,10 +7493,10 @@ function runOAuth2JsonCommandEffect({
7517
7493
  );
7518
7494
  })
7519
7495
  );
7520
- });
7521
- }
7522
- function runMutationCommandEffect(args) {
7523
- return Effect9.gen(function* () {
7496
+ }
7497
+ );
7498
+ var runMutationCommandEffect = Effect9.fn("xurl.runMutationCommand")(
7499
+ function* (args) {
7524
7500
  if (liveWritesDisabled()) {
7525
7501
  if (e2eFakeLiveWritesEnabled()) {
7526
7502
  return { ok: true, output: "e2e fake live write" };
@@ -7539,9 +7515,9 @@ function runMutationCommandEffect(args) {
7539
7515
  })
7540
7516
  )
7541
7517
  );
7542
- });
7543
- }
7544
- function lookupUsersByIdsEffect(ids) {
7518
+ }
7519
+ );
7520
+ var lookupUsersByIdsEffect = Effect9.fn("xurl.lookupUsersByIds")((ids) => {
7545
7521
  if (ids.length === 0) {
7546
7522
  return Effect9.succeed([]);
7547
7523
  }
@@ -7554,10 +7530,7 @@ function lookupUsersByIdsEffect(ids) {
7554
7530
  (payload) => Array.isArray(payload.data) ? payload.data : []
7555
7531
  )
7556
7532
  );
7557
- }
7558
- function lookupUsersByIds(ids) {
7559
- return runEffectPromise(lookupUsersByIdsEffect(ids));
7560
- }
7533
+ });
7561
7534
  function normalizeXListPage(payload) {
7562
7535
  const rawItems = Array.isArray(payload.data) ? payload.data : [];
7563
7536
  const data = rawItems.map((value) => {
@@ -7586,12 +7559,12 @@ function normalizeXListPage(payload) {
7586
7559
  }
7587
7560
  };
7588
7561
  }
7589
- function listOwnedXListsViaXurlEffect({
7562
+ var listOwnedXListsViaXurlEffect = Effect9.fn("xurl.listOwnedXLists")(({
7590
7563
  userId,
7591
7564
  username,
7592
7565
  maxResults,
7593
7566
  paginationToken
7594
- }) {
7567
+ }) => {
7595
7568
  const query = new URLSearchParams({
7596
7569
  max_results: String(maxResults),
7597
7570
  "list.fields": "created_at,description,follower_count,member_count,name,owner_id,private"
@@ -7601,29 +7574,33 @@ function listOwnedXListsViaXurlEffect({
7601
7574
  args: [`/2/users/${userId}/owned_lists?${query.toString()}`],
7602
7575
  username
7603
7576
  }).pipe(Effect9.map(normalizeXListPage));
7604
- }
7605
- function listXListMembersViaXurlEffect({
7606
- listId,
7607
- username,
7608
- maxResults,
7609
- paginationToken
7610
- }) {
7611
- const query = new URLSearchParams({
7612
- max_results: String(maxResults),
7613
- "user.fields": RICH_USER_FIELDS
7614
- });
7615
- if (paginationToken) query.set("pagination_token", paginationToken);
7616
- return runOAuth2JsonCommandEffect({
7617
- args: [`/2/lists/${listId}/members?${query.toString()}`],
7618
- username
7619
- }).pipe(
7620
- Effect9.map((payload) => ({
7621
- data: Array.isArray(payload.data) ? payload.data : [],
7622
- meta: payload.meta && typeof payload.meta === "object" ? payload.meta : {}
7623
- }))
7624
- );
7625
- }
7626
- function lookupUsersByHandlesEffect(handles, options = {}) {
7577
+ });
7578
+ var listXListMembersViaXurlEffect = Effect9.fn("xurl.listXListMembers")(
7579
+ ({
7580
+ listId,
7581
+ username,
7582
+ maxResults,
7583
+ paginationToken
7584
+ }) => {
7585
+ const query = new URLSearchParams({
7586
+ max_results: String(maxResults),
7587
+ "user.fields": RICH_USER_FIELDS
7588
+ });
7589
+ if (paginationToken) query.set("pagination_token", paginationToken);
7590
+ return runOAuth2JsonCommandEffect({
7591
+ args: [`/2/lists/${listId}/members?${query.toString()}`],
7592
+ username
7593
+ }).pipe(
7594
+ Effect9.map((payload) => ({
7595
+ data: Array.isArray(payload.data) ? payload.data : [],
7596
+ meta: payload.meta && typeof payload.meta === "object" ? payload.meta : {}
7597
+ }))
7598
+ );
7599
+ }
7600
+ );
7601
+ var lookupUsersByHandlesEffect = Effect9.fn(
7602
+ "xurl.lookupUsersByHandles"
7603
+ )((handles, options = {}) => {
7627
7604
  if (handles.length === 0) {
7628
7605
  return Effect9.succeed([]);
7629
7606
  }
@@ -7643,98 +7620,65 @@ function lookupUsersByHandlesEffect(handles, options = {}) {
7643
7620
  (payload) => Array.isArray(payload.data) ? payload.data : []
7644
7621
  )
7645
7622
  );
7646
- }
7647
- function lookupUsersByHandles(handles, options = {}) {
7648
- return runEffectPromise(lookupUsersByHandlesEffect(handles, options));
7649
- }
7623
+ });
7650
7624
  function authenticatedUserFromPayload(payload) {
7651
7625
  const data = payload.data;
7652
7626
  return data && typeof data === "object" ? data : null;
7653
7627
  }
7654
- function lookupAuthenticatedUserFreshEffect() {
7655
- return runJsonCommandEffect(["whoami"]).pipe(
7628
+ var lookupAuthenticatedUserFreshEffect = Effect9.fn(
7629
+ "xurl.lookupAuthenticatedUserFresh"
7630
+ )(
7631
+ () => runJsonCommandEffect(["whoami"]).pipe(
7656
7632
  Effect9.map(authenticatedUserFromPayload)
7657
- );
7658
- }
7659
- function lookupAuthenticatedOAuth2UserEffect(username) {
7660
- return runOAuth2JsonCommandEffect({
7633
+ )
7634
+ );
7635
+ var lookupAuthenticatedOAuth2UserEffect = Effect9.fn(
7636
+ "xurl.lookupAuthenticatedOAuth2User"
7637
+ )(
7638
+ (username) => runOAuth2JsonCommandEffect({
7661
7639
  args: ["whoami"],
7662
7640
  username
7663
- }).pipe(Effect9.map(authenticatedUserFromPayload));
7664
- }
7641
+ }).pipe(Effect9.map(authenticatedUserFromPayload))
7642
+ );
7665
7643
  function lookupAuthenticatedUserEffect() {
7666
- return Effect9.gen(function* () {
7667
- const now2 = Date.now();
7668
- if (authenticatedUserCache && "value" in authenticatedUserCache && authenticatedUserCache.expiresAt > now2) {
7669
- return authenticatedUserCache.value ?? null;
7670
- }
7671
- if (authenticatedUserCache?.pending) {
7672
- return yield* Effect9.tryPromise({
7673
- try: () => authenticatedUserCache?.pending ?? Promise.resolve(null),
7674
- catch: normalizeError
7675
- });
7676
- }
7677
- const pending = runEffectPromise(lookupAuthenticatedUserFreshEffect());
7678
- authenticatedUserCache = {
7679
- expiresAt: 0,
7680
- pending
7681
- };
7682
- const value = yield* Effect9.tryPromise({
7683
- try: () => pending,
7684
- catch: normalizeError
7685
- }).pipe(
7686
- Effect9.catchAll(
7687
- (error) => Effect9.sync(() => {
7688
- authenticatedUserCache = void 0;
7689
- }).pipe(Effect9.flatMap(() => Effect9.fail(error)))
7690
- )
7691
- );
7692
- authenticatedUserCache = {
7693
- expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
7694
- value
7695
- };
7696
- return value;
7697
- });
7698
- }
7699
- function lookupAuthenticatedUser() {
7700
- return runEffectPromise(lookupAuthenticatedUserEffect());
7644
+ return cachedAuthenticatedUser.pipe(
7645
+ Effect9.tapError(() => invalidateAuthenticatedUser)
7646
+ );
7701
7647
  }
7702
7648
  function lookupAuthenticatedUserFresh() {
7703
7649
  return runEffectPromise(lookupAuthenticatedUserFreshEffect());
7704
7650
  }
7705
- function resolveUserIdEffect({
7651
+ var resolveUserIdEffect = Effect9.fn("xurl.resolveUserId")(function* ({
7706
7652
  username,
7707
7653
  userId
7708
7654
  }) {
7709
- return Effect9.gen(function* () {
7710
- if (userId) return userId;
7711
- if (username) {
7712
- const [user2] = yield* lookupUsersByHandlesEffect([username]);
7713
- if (!user2?.id) {
7714
- return yield* Effect9.fail(
7715
- new Error(`Could not resolve Twitter user id for @${username}`)
7716
- );
7717
- }
7718
- return String(user2.id);
7719
- }
7720
- const user = yield* lookupAuthenticatedUserEffect();
7721
- if (!user?.id) {
7655
+ if (userId) return userId;
7656
+ if (username) {
7657
+ const [user2] = yield* lookupUsersByHandlesEffect([username]);
7658
+ if (!user2?.id) {
7722
7659
  return yield* Effect9.fail(
7723
- new Error("Could not resolve authenticated Twitter user id")
7660
+ new Error(`Could not resolve Twitter user id for @${username}`)
7724
7661
  );
7725
7662
  }
7726
- return String(user.id);
7727
- });
7728
- }
7729
- function listMentionsViaXurlEffect({
7730
- maxResults,
7731
- username,
7732
- userId,
7733
- paginationToken,
7734
- sinceId,
7735
- startTime
7736
- }) {
7737
- return Effect9.gen(function* () {
7663
+ return String(user2.id);
7664
+ }
7665
+ const user = yield* lookupAuthenticatedUserEffect();
7666
+ if (!user?.id) {
7667
+ return yield* Effect9.fail(
7668
+ new Error("Could not resolve authenticated Twitter user id")
7669
+ );
7670
+ }
7671
+ return String(user.id);
7672
+ });
7673
+ var listMentionsViaXurlEffect = Effect9.fn("xurl.listMentions")(
7674
+ function* ({
7675
+ maxResults,
7676
+ username,
7677
+ userId,
7678
+ paginationToken,
7679
+ sinceId,
7680
+ startTime
7681
+ }) {
7738
7682
  const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
7739
7683
  const query = new URLSearchParams({
7740
7684
  max_results: String(maxResults),
@@ -7757,19 +7701,16 @@ function listMentionsViaXurlEffect({
7757
7701
  username
7758
7702
  });
7759
7703
  return toXurlMentionsResponse(payload);
7760
- });
7761
- }
7762
- function listMentionsViaXurl(options) {
7763
- return runEffectPromise(listMentionsViaXurlEffect(options));
7764
- }
7765
- function listHomeTimelineViaXurlEffect({
7766
- maxResults,
7767
- username,
7768
- userId,
7769
- paginationToken,
7770
- timeoutMs
7771
- }) {
7772
- return Effect9.gen(function* () {
7704
+ }
7705
+ );
7706
+ var listHomeTimelineViaXurlEffect = Effect9.fn("xurl.listHomeTimeline")(
7707
+ function* ({
7708
+ maxResults,
7709
+ username,
7710
+ userId,
7711
+ paginationToken,
7712
+ timeoutMs
7713
+ }) {
7773
7714
  const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
7774
7715
  const query = new URLSearchParams({
7775
7716
  max_results: String(maxResults),
@@ -7789,8 +7730,8 @@ function listHomeTimelineViaXurlEffect({
7789
7730
  options: { timeoutMs }
7790
7731
  });
7791
7732
  return toXurlMentionsResponse(payload);
7792
- });
7793
- }
7733
+ }
7734
+ );
7794
7735
  function toXurlMentionsResponse(payload) {
7795
7736
  return {
7796
7737
  data: Array.isArray(payload.data) ? payload.data : [],
@@ -7798,7 +7739,9 @@ function toXurlMentionsResponse(payload) {
7798
7739
  meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
7799
7740
  };
7800
7741
  }
7801
- function listTimelineCollectionViaXurlEffect({
7742
+ var listTimelineCollectionViaXurlEffect = Effect9.fn(
7743
+ "xurl.listTimelineCollection"
7744
+ )(function* ({
7802
7745
  collection,
7803
7746
  maxResults,
7804
7747
  username,
@@ -7806,48 +7749,40 @@ function listTimelineCollectionViaXurlEffect({
7806
7749
  isPaginatedWalk = false,
7807
7750
  paginationToken
7808
7751
  }) {
7809
- return Effect9.gen(function* () {
7810
- const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
7811
- const requestMaxResults = capTimelineCollectionMaxResults(
7812
- collection,
7813
- maxResults,
7814
- isPaginatedWalk
7815
- );
7816
- const query = new URLSearchParams({
7817
- max_results: String(requestMaxResults),
7818
- expansions: AUTHOR_MEDIA_EXPANSIONS,
7819
- "tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
7820
- "media.fields": MEDIA_FIELDS,
7821
- "user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
7822
- });
7823
- if (paginationToken) {
7824
- query.set("pagination_token", paginationToken);
7825
- }
7826
- const payload = yield* runOAuth2JsonCommandEffect({
7827
- args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
7828
- username
7829
- });
7830
- return toXurlMentionsResponse(payload);
7752
+ const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
7753
+ const requestMaxResults = capTimelineCollectionMaxResults(
7754
+ collection,
7755
+ maxResults,
7756
+ isPaginatedWalk
7757
+ );
7758
+ const query = new URLSearchParams({
7759
+ max_results: String(requestMaxResults),
7760
+ expansions: AUTHOR_MEDIA_EXPANSIONS,
7761
+ "tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
7762
+ "media.fields": MEDIA_FIELDS,
7763
+ "user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
7831
7764
  });
7832
- }
7765
+ if (paginationToken) {
7766
+ query.set("pagination_token", paginationToken);
7767
+ }
7768
+ const payload = yield* runOAuth2JsonCommandEffect({
7769
+ args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
7770
+ username
7771
+ });
7772
+ return toXurlMentionsResponse(payload);
7773
+ });
7833
7774
  function listLikedTweetsViaXurlEffect(options) {
7834
7775
  return listTimelineCollectionViaXurlEffect({
7835
7776
  ...options,
7836
7777
  collection: "liked_tweets"
7837
7778
  });
7838
7779
  }
7839
- function listLikedTweetsViaXurl(options) {
7840
- return runEffectPromise(listLikedTweetsViaXurlEffect(options));
7841
- }
7842
7780
  function listBookmarkedTweetsViaXurlEffect(options) {
7843
7781
  return listTimelineCollectionViaXurlEffect({
7844
7782
  ...options,
7845
7783
  collection: "bookmarks"
7846
7784
  });
7847
7785
  }
7848
- function listBookmarkedTweetsViaXurl(options) {
7849
- return runEffectPromise(listBookmarkedTweetsViaXurlEffect(options));
7850
- }
7851
7786
  function listDirectMessageEventsViaXurlEffect({
7852
7787
  maxResults,
7853
7788
  username,
@@ -7869,19 +7804,19 @@ function listDirectMessageEventsViaXurlEffect({
7869
7804
  }).pipe(
7870
7805
  Effect9.map((payload) => ({
7871
7806
  data: Array.isArray(payload.data) ? payload.data : [],
7872
- includes: payload.includes && typeof payload.includes === "object" ? payload.includes : void 0,
7873
- meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
7874
- }))
7875
- );
7876
- }
7877
- function listFollowUsersViaXurlEffect({
7878
- direction,
7879
- maxResults,
7880
- username,
7881
- userId,
7882
- paginationToken
7883
- }) {
7884
- return Effect9.gen(function* () {
7807
+ includes: payload.includes && typeof payload.includes === "object" ? payload.includes : void 0,
7808
+ meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
7809
+ }))
7810
+ );
7811
+ }
7812
+ var listFollowUsersViaXurlEffect = Effect9.fn("xurl.listFollowUsers")(
7813
+ function* ({
7814
+ direction,
7815
+ maxResults,
7816
+ username,
7817
+ userId,
7818
+ paginationToken
7819
+ }) {
7885
7820
  const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
7886
7821
  const query = new URLSearchParams({
7887
7822
  max_results: String(maxResults),
@@ -7898,12 +7833,9 @@ function listFollowUsersViaXurlEffect({
7898
7833
  data: Array.isArray(payload.data) ? payload.data : [],
7899
7834
  meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
7900
7835
  };
7901
- });
7902
- }
7903
- function listFollowUsersViaXurl(options) {
7904
- return runEffectPromise(listFollowUsersViaXurlEffect(options));
7905
- }
7906
- function listBlockedUsersEffect(userId, paginationToken) {
7836
+ }
7837
+ );
7838
+ var listBlockedUsersEffect = Effect9.fn("xurl.listBlockedUsers")((userId, paginationToken) => {
7907
7839
  const query = new URLSearchParams({
7908
7840
  max_results: "100",
7909
7841
  "user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
@@ -7921,11 +7853,8 @@ function listBlockedUsersEffect(userId, paginationToken) {
7921
7853
  };
7922
7854
  })
7923
7855
  );
7924
- }
7925
- function listBlockedUsers(userId, paginationToken) {
7926
- return runEffectPromise(listBlockedUsersEffect(userId, paginationToken));
7927
- }
7928
- function listUserTweetsEffect(userId, {
7856
+ });
7857
+ var listUserTweetsEffect = Effect9.fn("xurl.listUserTweets")((userId, {
7929
7858
  maxResults,
7930
7859
  paginationToken,
7931
7860
  excludeRetweets = true,
@@ -7940,7 +7869,7 @@ function listUserTweetsEffect(userId, {
7940
7869
  signal,
7941
7870
  onAttempt,
7942
7871
  useConfiguredCandidate
7943
- }) {
7872
+ }) => {
7944
7873
  const query = new URLSearchParams({
7945
7874
  max_results: String(maxResults),
7946
7875
  expansions: MEDIA_EXPANSION,
@@ -7987,7 +7916,7 @@ function listUserTweetsEffect(userId, {
7987
7916
  };
7988
7917
  })
7989
7918
  );
7990
- }
7919
+ });
7991
7920
  function listUserTweets(userId, options) {
7992
7921
  return runEffectPromise(listUserTweetsEffect(userId, options));
7993
7922
  }
@@ -7998,7 +7927,7 @@ function toXurlTweetsResponse(payload) {
7998
7927
  meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
7999
7928
  };
8000
7929
  }
8001
- function lookupTweetsByIdsEffect(ids) {
7930
+ var lookupTweetsByIdsEffect = Effect9.fn("xurl.lookupTweetsByIds")((ids) => {
8002
7931
  if (ids.length === 0) {
8003
7932
  return Effect9.succeed({ data: [] });
8004
7933
  }
@@ -8012,8 +7941,10 @@ function lookupTweetsByIdsEffect(ids) {
8012
7941
  return runJsonCommandEffect([`/2/tweets?${query.toString()}`]).pipe(
8013
7942
  Effect9.map(toXurlTweetsResponse)
8014
7943
  );
8015
- }
8016
- function searchRecentByConversationIdEffect(conversationId, {
7944
+ });
7945
+ var searchRecentByConversationIdEffect = Effect9.fn(
7946
+ "xurl.searchRecentByConversationId"
7947
+ )((conversationId, {
8017
7948
  maxResults,
8018
7949
  paginationToken,
8019
7950
  timeoutMs,
@@ -8021,7 +7952,7 @@ function searchRecentByConversationIdEffect(conversationId, {
8021
7952
  username,
8022
7953
  signal,
8023
7954
  onAttempt
8024
- }) {
7955
+ }) => {
8025
7956
  const query = new URLSearchParams({
8026
7957
  query: `conversation_id:${conversationId}`,
8027
7958
  max_results: String(maxResults),
@@ -8041,15 +7972,15 @@ function searchRecentByConversationIdEffect(conversationId, {
8041
7972
  useConfiguredCandidate: false
8042
7973
  }) : runJsonCommandEffect(args, { timeoutMs, signal, onAttempt });
8043
7974
  return command.pipe(Effect9.map(toXurlTweetsResponse));
8044
- }
8045
- function searchRecentTweetsEffect(searchQuery, {
7975
+ });
7976
+ var searchRecentTweetsEffect = Effect9.fn("xurl.searchRecentTweets")((searchQuery, {
8046
7977
  maxResults,
8047
7978
  paginationToken,
8048
7979
  startTime,
8049
7980
  endTime,
8050
7981
  username,
8051
7982
  timeoutMs
8052
- }) {
7983
+ }) => {
8053
7984
  const query = new URLSearchParams({
8054
7985
  query: searchQuery,
8055
7986
  max_results: String(maxResults),
@@ -8073,8 +8004,8 @@ function searchRecentTweetsEffect(searchQuery, {
8073
8004
  options: { timeoutMs },
8074
8005
  useConfiguredCandidate: false
8075
8006
  }).pipe(Effect9.map(toXurlTweetsResponse));
8076
- }
8077
- function getTweetByIdEffect(id, { timeoutMs } = {}) {
8007
+ });
8008
+ var getTweetByIdEffect = Effect9.fn("xurl.getTweetById")((id, { timeoutMs } = {}) => {
8078
8009
  const query = new URLSearchParams({
8079
8010
  expansions: AUTHOR_MEDIA_EXPANSIONS,
8080
8011
  "tweet.fields": THREAD_TWEET_FIELDS,
@@ -8093,7 +8024,7 @@ function getTweetByIdEffect(id, { timeoutMs } = {}) {
8093
8024
  };
8094
8025
  })
8095
8026
  );
8096
- }
8027
+ });
8097
8028
  function postViaXurlEffect(text) {
8098
8029
  return runShortcutEffect(["post", text]);
8099
8030
  }
@@ -8116,9 +8047,6 @@ function blockUserViaXurlEffect(sourceUserId, targetUserId) {
8116
8047
  JSON.stringify({ target_user_id: targetUserId })
8117
8048
  ]);
8118
8049
  }
8119
- function blockUserViaXurl(sourceUserId, targetUserId) {
8120
- return runEffectPromise(blockUserViaXurlEffect(sourceUserId, targetUserId));
8121
- }
8122
8050
  function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
8123
8051
  return runMutationCommandEffect([
8124
8052
  "-X",
@@ -8126,9 +8054,6 @@ function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
8126
8054
  `/2/users/${sourceUserId}/blocking/${targetUserId}`
8127
8055
  ]);
8128
8056
  }
8129
- function unblockUserViaXurl(sourceUserId, targetUserId) {
8130
- return runEffectPromise(unblockUserViaXurlEffect(sourceUserId, targetUserId));
8131
- }
8132
8057
  function muteUserViaXurlEffect(sourceUserId, targetUserId) {
8133
8058
  return runMutationCommandEffect([
8134
8059
  "-X",
@@ -8138,9 +8063,6 @@ function muteUserViaXurlEffect(sourceUserId, targetUserId) {
8138
8063
  JSON.stringify({ target_user_id: targetUserId })
8139
8064
  ]);
8140
8065
  }
8141
- function muteUserViaXurl(sourceUserId, targetUserId) {
8142
- return runEffectPromise(muteUserViaXurlEffect(sourceUserId, targetUserId));
8143
- }
8144
8066
  function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
8145
8067
  return runMutationCommandEffect([
8146
8068
  "-X",
@@ -8148,49 +8070,40 @@ function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
8148
8070
  `/2/users/${sourceUserId}/muting/${targetUserId}`
8149
8071
  ]);
8150
8072
  }
8151
- function unmuteUserViaXurl(sourceUserId, targetUserId) {
8152
- return runEffectPromise(unmuteUserViaXurlEffect(sourceUserId, targetUserId));
8153
- }
8154
8073
 
8155
8074
  // src/lib/live-transport-gateway.ts
8156
- function fromPromise(run) {
8157
- return Effect10.tryPromise({
8158
- try: run,
8159
- catch: (error) => error instanceof Error ? error : new Error(String(error))
8160
- });
8161
- }
8162
8075
  var liveTransportGateway = {
8163
8076
  bird: {
8164
- getAuthenticatedAccount: () => getAuthenticatedBirdAccountEffect(),
8165
- listBookmarks: (options) => listBookmarkedTweetsViaBirdEffect(options),
8166
- listDirectMessages: (options) => listDirectMessagesViaBirdEffect(options),
8167
- listFollowUsers: (options) => listFollowUsersViaBirdEffect(options),
8168
- listHomeTimeline: (options) => listHomeTimelineViaBirdEffect(options),
8169
- listLikes: (options) => listLikedTweetsViaBirdEffect(options),
8170
- listMentions: (options) => listMentionsViaBirdEffect(options),
8171
- listThread: (options) => listThreadViaBirdEffect(options),
8172
- searchTweets: (query, options) => searchTweetsViaBirdEffect(query, options)
8077
+ getAuthenticatedAccount: (...args) => getAuthenticatedBirdAccountEffect(...args),
8078
+ listBookmarks: (...args) => listBookmarkedTweetsViaBirdEffect(...args),
8079
+ listDirectMessages: (...args) => listDirectMessagesViaBirdEffect(...args),
8080
+ listFollowUsers: (...args) => listFollowUsersViaBirdEffect(...args),
8081
+ listHomeTimeline: (...args) => listHomeTimelineViaBirdEffect(...args),
8082
+ listLikes: (...args) => listLikedTweetsViaBirdEffect(...args),
8083
+ listMentions: (...args) => listMentionsViaBirdEffect(...args),
8084
+ listThread: (...args) => listThreadViaBirdEffect(...args),
8085
+ searchTweets: (...args) => searchTweetsViaBirdEffect(...args)
8173
8086
  },
8174
8087
  xurl: {
8175
- getTransportStatus: (...args) => fromPromise(() => getTransportStatus(...args)),
8176
- getTweetById: (id, options) => getTweetByIdEffect(id, options),
8177
- listBookmarks: (...args) => fromPromise(() => listBookmarkedTweetsViaXurl(...args)),
8178
- listDirectMessages: (options) => listDirectMessageEventsViaXurlEffect(options),
8179
- listFollowUsers: (...args) => fromPromise(() => listFollowUsersViaXurl(...args)),
8180
- listHomeTimeline: (options) => listHomeTimelineViaXurlEffect(options),
8181
- listLikes: (...args) => fromPromise(() => listLikedTweetsViaXurl(...args)),
8182
- listMentions: (...args) => fromPromise(() => listMentionsViaXurl(...args)),
8183
- listUserTweets: (...args) => fromPromise(() => listUserTweets(...args)),
8184
- lookupAuthenticatedOAuth2User: (username) => lookupAuthenticatedOAuth2UserEffect(username),
8185
- lookupAuthenticatedUser: (...args) => fromPromise(() => lookupAuthenticatedUser(...args)),
8186
- lookupUsersByHandles: (...args) => fromPromise(() => lookupUsersByHandles(...args)),
8187
- searchConversation: (conversationId, options) => searchRecentByConversationIdEffect(conversationId, options),
8188
- searchRecentTweets: (query, options) => searchRecentTweetsEffect(query, options)
8088
+ getTransportStatus: (...args) => getTransportStatusEffect(...args),
8089
+ getTweetById: (...args) => getTweetByIdEffect(...args),
8090
+ listBookmarks: (...args) => listBookmarkedTweetsViaXurlEffect(...args),
8091
+ listDirectMessages: (...args) => listDirectMessageEventsViaXurlEffect(...args),
8092
+ listFollowUsers: (...args) => listFollowUsersViaXurlEffect(...args),
8093
+ listHomeTimeline: (...args) => listHomeTimelineViaXurlEffect(...args),
8094
+ listLikes: (...args) => listLikedTweetsViaXurlEffect(...args),
8095
+ listMentions: (...args) => listMentionsViaXurlEffect(...args),
8096
+ listUserTweets: (...args) => listUserTweetsEffect(...args),
8097
+ lookupAuthenticatedOAuth2User: (...args) => lookupAuthenticatedOAuth2UserEffect(...args),
8098
+ lookupAuthenticatedUser: (...args) => lookupAuthenticatedUserEffect(...args),
8099
+ lookupUsersByHandles: (...args) => lookupUsersByHandlesEffect(...args),
8100
+ searchConversation: (...args) => searchRecentByConversationIdEffect(...args),
8101
+ searchRecentTweets: (...args) => searchRecentTweetsEffect(...args)
8189
8102
  }
8190
8103
  };
8191
8104
 
8192
8105
  // src/lib/live-sync-engine.ts
8193
- import { Effect as Effect11 } from "effect";
8106
+ import { Effect as Effect10 } from "effect";
8194
8107
 
8195
8108
  // src/lib/sync-cache.ts
8196
8109
  function readSyncCacheRow(cacheKey3, db = getNativeDb()) {
@@ -8259,7 +8172,7 @@ function resolveLiveSyncAccount(db, accountId) {
8259
8172
  function createLiveTransportAdapter(source, fetch2) {
8260
8173
  return {
8261
8174
  source,
8262
- fetch: fetch2.pipe(Effect11.mapError(toError))
8175
+ fetch: fetch2.pipe(Effect10.mapError(toError))
8263
8176
  };
8264
8177
  }
8265
8178
  function assertLiveAccountMatches({
@@ -8291,12 +8204,12 @@ function normalizeCacheTtlMs(value, defaultValue) {
8291
8204
  function fetchWithTransportFallbackEffect(transports) {
8292
8205
  const [first, ...rest] = transports;
8293
8206
  if (!first) {
8294
- return Effect11.fail(new Error("No live transport adapters configured"));
8207
+ return Effect10.fail(new Error("No live transport adapters configured"));
8295
8208
  }
8296
8209
  return first.fetch.pipe(
8297
- Effect11.map((payload) => ({ source: first.source, payload })),
8298
- Effect11.catchAll(
8299
- (error) => rest.length > 0 ? fetchWithTransportFallbackEffect(rest) : Effect11.fail(toError(error))
8210
+ Effect10.map((payload) => ({ source: first.source, payload })),
8211
+ Effect10.catchAll(
8212
+ (error) => rest.length > 0 ? fetchWithTransportFallbackEffect(rest) : Effect10.fail(toError(error))
8300
8213
  )
8301
8214
  );
8302
8215
  }
@@ -8309,8 +8222,8 @@ function runCachedLiveSyncEffect({
8309
8222
  persistLive,
8310
8223
  persistCached
8311
8224
  }) {
8312
- return Effect11.gen(function* () {
8313
- const cached = yield* Effect11.try({
8225
+ return Effect10.gen(function* () {
8226
+ const cached = yield* Effect10.try({
8314
8227
  try: () => readSyncCache(cacheKey3, db),
8315
8228
  catch: toError
8316
8229
  });
@@ -8340,7 +8253,7 @@ function runCachedLiveSyncEffect({
8340
8253
  }
8341
8254
 
8342
8255
  // src/lib/sync-plan.ts
8343
- import { Effect as Effect12 } from "effect";
8256
+ import { Effect as Effect11 } from "effect";
8344
8257
  function normalizeCursor(value) {
8345
8258
  const normalized = value?.trim();
8346
8259
  return normalized || void 0;
@@ -8358,7 +8271,7 @@ function runSyncPlanEffect({
8358
8271
  shouldStop,
8359
8272
  allowPartialFailure = false
8360
8273
  }) {
8361
- return Effect12.gen(function* () {
8274
+ return Effect11.gen(function* () {
8362
8275
  const pages = [];
8363
8276
  const seenCursors = /* @__PURE__ */ new Set();
8364
8277
  let cursor = normalizeCursor(initialCursor);
@@ -8369,14 +8282,14 @@ function runSyncPlanEffect({
8369
8282
  while (pages.length < pageLimit) {
8370
8283
  const pageIndex = pages.length;
8371
8284
  const outcome = yield* fetchPage({ cursor, fetched, pageIndex }).pipe(
8372
- Effect12.map((page2) => ({ ok: true, page: page2 })),
8373
- Effect12.catchAll(
8374
- (error) => Effect12.succeed({ ok: false, error })
8285
+ Effect11.map((page2) => ({ ok: true, page: page2 })),
8286
+ Effect11.catchAll(
8287
+ (error) => Effect11.succeed({ ok: false, error })
8375
8288
  )
8376
8289
  );
8377
8290
  if (!outcome.ok) {
8378
8291
  if (!allowPartialFailure || pages.length === 0) {
8379
- return yield* Effect12.fail(outcome.error);
8292
+ return yield* Effect11.fail(outcome.error);
8380
8293
  }
8381
8294
  return {
8382
8295
  complete: false,
@@ -8432,7 +8345,7 @@ function runSyncPlanEffect({
8432
8345
  cursor = nextCursor;
8433
8346
  seenCursors.add(nextCursor);
8434
8347
  if (typeof pageDelayMs === "number" && pageDelayMs > 0) {
8435
- yield* Effect12.sleep(pageDelayMs);
8348
+ yield* Effect11.sleep(pageDelayMs);
8436
8349
  }
8437
8350
  }
8438
8351
  return {
@@ -8852,7 +8765,7 @@ function renderTweetMarkdown(text, entities) {
8852
8765
  import { createHash as createHash2 } from "node:crypto";
8853
8766
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
8854
8767
  import path3 from "node:path";
8855
- import { Effect as Effect13 } from "effect";
8768
+ import { Effect as Effect12 } from "effect";
8856
8769
  var AVATAR_SIZE_SUFFIX = /(?:(?:_normal|_bigger|_mini))(?=\.(?:jpg|jpeg|png|webp|gif)(?:$|\?))/i;
8857
8770
  var MAX_AVATAR_BYTES = 2 * 1024 * 1024;
8858
8771
  var MAX_AVATAR_DATA_URL_CHARS = MAX_AVATAR_BYTES * 4;
@@ -8940,7 +8853,7 @@ function toError5(error) {
8940
8853
  return error instanceof Error ? error : new Error(String(error));
8941
8854
  }
8942
8855
  function trySync3(try_) {
8943
- return Effect13.try({
8856
+ return Effect12.try({
8944
8857
  try: try_,
8945
8858
  catch: toError5
8946
8859
  });
@@ -9007,7 +8920,7 @@ function getAvatarCachePath(profileId, avatarUrl) {
9007
8920
  );
9008
8921
  }
9009
8922
  function fetchRemoteAvatarEffect(avatarUrl) {
9010
- return Effect13.gen(function* () {
8923
+ return Effect12.gen(function* () {
9011
8924
  const safeUrl = yield* trySync3(() => assertSafeRemoteAvatarUrl(avatarUrl));
9012
8925
  const response = yield* tryPromise(
9013
8926
  () => fetch(safeUrl, {
@@ -9019,13 +8932,13 @@ function fetchRemoteAvatarEffect(avatarUrl) {
9019
8932
  })
9020
8933
  );
9021
8934
  if (!response.ok) {
9022
- return yield* Effect13.fail(
8935
+ return yield* Effect12.fail(
9023
8936
  new Error(`Avatar fetch failed with ${response.status}`)
9024
8937
  );
9025
8938
  }
9026
8939
  const buffer = Buffer.from(yield* tryPromise(() => response.arrayBuffer()));
9027
8940
  if (buffer.byteLength > MAX_AVATAR_BYTES) {
9028
- return yield* Effect13.fail(new Error("Avatar response is too large"));
8941
+ return yield* Effect12.fail(new Error("Avatar response is too large"));
9029
8942
  }
9030
8943
  const contentType = yield* trySync3(
9031
8944
  () => detectRasterContentType(
@@ -9040,7 +8953,7 @@ function fetchRemoteAvatarEffect(avatarUrl) {
9040
8953
  });
9041
8954
  }
9042
8955
  function readCachedAvatarEffect(profileId) {
9043
- return Effect13.gen(function* () {
8956
+ return Effect12.gen(function* () {
9044
8957
  const avatarUrl = yield* trySync3(() => getAvatarUrlForProfile(profileId));
9045
8958
  if (!avatarUrl) {
9046
8959
  return null;
@@ -9054,8 +8967,8 @@ function readCachedAvatarEffect(profileId) {
9054
8967
  );
9055
8968
  const cachedExtension = path3.extname(cachePath);
9056
8969
  const cached = yield* trySync3(() => readFileSync3(cachePath)).pipe(
9057
- Effect13.map((buffer) => ({ ok: true, buffer })),
9058
- Effect13.catchAll(() => Effect13.succeed({ ok: false }))
8970
+ Effect12.map((buffer) => ({ ok: true, buffer })),
8971
+ Effect12.catchAll(() => Effect12.succeed({ ok: false }))
9059
8972
  );
9060
8973
  if (cached.ok) {
9061
8974
  return {
@@ -9088,17 +9001,17 @@ function prefetchCachedAvatarsForProfileIdsEffect(profileIds, options = {}) {
9088
9001
  )
9089
9002
  );
9090
9003
  const concurrency = normalizePrefetchConcurrency(options.concurrency);
9091
- return Effect13.forEach(
9004
+ return Effect12.forEach(
9092
9005
  uniqueProfileIds,
9093
9006
  (profileId) => readCachedAvatarEffect(profileId).pipe(
9094
- Effect13.map(
9007
+ Effect12.map(
9095
9008
  (avatar) => avatar ? "available" : "missing"
9096
9009
  ),
9097
- Effect13.catchAll(() => Effect13.succeed("failed"))
9010
+ Effect12.catchAll(() => Effect12.succeed("failed"))
9098
9011
  ),
9099
9012
  { concurrency }
9100
9013
  ).pipe(
9101
- Effect13.map((statuses) => ({
9014
+ Effect12.map((statuses) => ({
9102
9015
  requested: uniqueProfileIds.length,
9103
9016
  available: statuses.filter((status) => status === "available").length,
9104
9017
  missing: statuses.filter((status) => status === "missing").length,
@@ -10507,10 +10420,10 @@ function fetchConversationViaRecentSearchEffect({
10507
10420
  timeoutMs,
10508
10421
  deadlineMs
10509
10422
  }) {
10510
- return Effect14.gen(function* () {
10423
+ return Effect13.gen(function* () {
10511
10424
  const result = yield* runSyncPlanEffect({
10512
10425
  fetchPage: ({ cursor }) => trySync(() => getRemainingThreadTimeoutMs(deadlineMs, timeoutMs)).pipe(
10513
- Effect14.flatMap(
10426
+ Effect13.flatMap(
10514
10427
  (remainingTimeoutMs) => liveTransportGateway.xurl.searchConversation(conversationId, {
10515
10428
  maxResults: MAX_XURL_SEARCH_RESULTS,
10516
10429
  paginationToken: cursor,
@@ -10537,7 +10450,7 @@ function fetchParentChainViaXurlEffect({
10537
10450
  timeoutMs,
10538
10451
  deadlineMs
10539
10452
  }) {
10540
- return Effect14.gen(function* () {
10453
+ return Effect13.gen(function* () {
10541
10454
  const pages = [];
10542
10455
  const warnings = [];
10543
10456
  const seenTweetIds = /* @__PURE__ */ new Set([mention.id]);
@@ -10635,7 +10548,7 @@ function fetchThreadContextViaXurlEffect({
10635
10548
  maxFallbackDepth,
10636
10549
  timeoutMs
10637
10550
  }) {
10638
- return Effect14.gen(function* () {
10551
+ return Effect13.gen(function* () {
10639
10552
  const deadlineMs = Date.now() + timeoutMs;
10640
10553
  if (!mention.conversationId) {
10641
10554
  if (mention.replyToId) {
@@ -10735,7 +10648,7 @@ function syncMentionThreadsEffect({
10735
10648
  maxPages,
10736
10649
  onProgress
10737
10650
  }) {
10738
- return Effect14.gen(function* () {
10651
+ return Effect13.gen(function* () {
10739
10652
  const parsedMode = yield* trySync(() => parseMode(mode));
10740
10653
  const parsedLimit = yield* trySync(
10741
10654
  () => assertPositiveInteger(limit, "--limit")
@@ -10768,7 +10681,7 @@ function syncMentionThreadsEffect({
10768
10681
  const warnings = [];
10769
10682
  for (const [index, mention] of mentions.entries()) {
10770
10683
  if (index > 0 && parsedDelayMs > 0) {
10771
- yield* Effect14.sleep(parsedDelayMs);
10684
+ yield* Effect13.sleep(parsedDelayMs);
10772
10685
  }
10773
10686
  const fetchEffect = parsedMode === "bird" ? liveTransportGateway.bird.listThread({
10774
10687
  tweetId: mention.id,
@@ -10776,7 +10689,7 @@ function syncMentionThreadsEffect({
10776
10689
  maxPages: parsedMaxPages,
10777
10690
  timeoutMs: parsedTimeoutMs
10778
10691
  }).pipe(
10779
- Effect14.map((payload2) => ({
10692
+ Effect13.map((payload2) => ({
10780
10693
  strategy: "bird",
10781
10694
  payload: payload2,
10782
10695
  pages: void 0,
@@ -10793,7 +10706,7 @@ function syncMentionThreadsEffect({
10793
10706
  timeoutMs: parsedTimeoutMs
10794
10707
  });
10795
10708
  const fetched = yield* fetchEffect.pipe(
10796
- Effect14.flatMap(
10709
+ Effect13.flatMap(
10797
10710
  (fetchResult2) => databaseWriteEffect(
10798
10711
  (writeDb) => mergeMentionThreadIntoLocalStore({
10799
10712
  db: writeDb,
@@ -10804,11 +10717,11 @@ function syncMentionThreadsEffect({
10804
10717
  source: parsedMode,
10805
10718
  writeThreadContextEdges: parsedMode === "xurl"
10806
10719
  })
10807
- ).pipe(Effect14.as(fetchResult2))
10720
+ ).pipe(Effect13.as(fetchResult2))
10808
10721
  ),
10809
- Effect14.map((fetchResult2) => ({ ok: true, fetchResult: fetchResult2 })),
10810
- Effect14.catchAll(
10811
- (error) => Effect14.succeed({ ok: false, error })
10722
+ Effect13.map((fetchResult2) => ({ ok: true, fetchResult: fetchResult2 })),
10723
+ Effect13.catchAll(
10724
+ (error) => Effect13.succeed({ ok: false, error })
10812
10725
  )
10813
10726
  );
10814
10727
  if (!fetched.ok) {
@@ -10820,7 +10733,7 @@ function syncMentionThreadsEffect({
10820
10733
  strategy: parsedMode,
10821
10734
  error: fetched.error instanceof Error ? fetched.error.message : String(fetched.error)
10822
10735
  });
10823
- yield* Effect14.sync(
10736
+ yield* Effect13.sync(
10824
10737
  () => onProgress?.({
10825
10738
  source: parsedMode,
10826
10739
  processed: index + 1,
@@ -10850,7 +10763,7 @@ function syncMentionThreadsEffect({
10850
10763
  truncated: fetchResult.truncated,
10851
10764
  warnings: fetchResult.warnings.length > 0 ? fetchResult.warnings : void 0
10852
10765
  });
10853
- yield* Effect14.sync(
10766
+ yield* Effect13.sync(
10854
10767
  () => onProgress?.({
10855
10768
  source: parsedMode,
10856
10769
  processed: index + 1,
@@ -10899,7 +10812,7 @@ function syncMentionThreads(options) {
10899
10812
  }
10900
10813
 
10901
10814
  // src/lib/mentions-live.ts
10902
- import { Effect as Effect15 } from "effect";
10815
+ import { Effect as Effect14 } from "effect";
10903
10816
 
10904
10817
  // src/lib/query-read-model-shared.ts
10905
10818
  function parseJsonField2(value, fallback) {
@@ -12070,9 +11983,9 @@ function writeMentionHighWaterId(db, mode, accountId, sinceId) {
12070
11983
  writeSyncCache(getMentionHighWaterKey({ mode, accountId }), { sinceId }, db);
12071
11984
  }
12072
11985
  function verifyBirdAccountMatchesEffect(account) {
12073
- return Effect15.gen(function* () {
11986
+ return Effect14.gen(function* () {
12074
11987
  const authenticated = yield* liveTransportGateway.bird.getAuthenticatedAccount();
12075
- return yield* Effect15.try({
11988
+ return yield* Effect14.try({
12076
11989
  try: () => assertLiveAccountMatches({
12077
11990
  source: "bird",
12078
11991
  account,
@@ -12188,10 +12101,10 @@ function fetchMentionsViaXurlEffect({
12188
12101
  startTime,
12189
12102
  onProgress
12190
12103
  }) {
12191
- return Effect15.gen(function* () {
12192
- const accountUserId = resolvedAccount.externalUserId ?? (yield* liveTransportGateway.xurl.lookupUsersByHandles([resolvedAccount.username]).pipe(Effect15.map((users) => users[0]?.id)));
12104
+ return Effect14.gen(function* () {
12105
+ const accountUserId = resolvedAccount.externalUserId ?? (yield* liveTransportGateway.xurl.lookupUsersByHandles([resolvedAccount.username]).pipe(Effect14.map((users) => users[0]?.id)));
12193
12106
  if (!accountUserId) {
12194
- return yield* Effect15.fail(
12107
+ return yield* Effect14.fail(
12195
12108
  new Error(
12196
12109
  `Could not resolve Twitter user id for @${resolvedAccount.username}`
12197
12110
  )
@@ -12243,13 +12156,13 @@ function syncMentionsEffect({
12243
12156
  startTime,
12244
12157
  onProgress
12245
12158
  }) {
12246
- return Effect15.gen(function* () {
12159
+ return Effect14.gen(function* () {
12247
12160
  const parsedMode = yield* trySync(() => parseSyncMode(mode));
12248
12161
  const primaryMode = parsedMode === "auto" ? "xurl" : parsedMode;
12249
12162
  const explicitSinceId = sinceId?.trim() || void 0;
12250
12163
  const explicitStartTime = startTime?.trim() || void 0;
12251
12164
  if (primaryMode === "bird" && (explicitSinceId || explicitStartTime)) {
12252
- return yield* Effect15.fail(
12165
+ return yield* Effect14.fail(
12253
12166
  new Error("bird mode does not support --since-id or --start-time")
12254
12167
  );
12255
12168
  }
@@ -12325,7 +12238,7 @@ function syncMentionsEffect({
12325
12238
  primaryMode
12326
12239
  )
12327
12240
  );
12328
- yield* Effect15.sync(
12241
+ yield* Effect14.sync(
12329
12242
  () => onProgress?.({
12330
12243
  source: "cache",
12331
12244
  fetched: cached.value.data.length,
@@ -12363,16 +12276,16 @@ function syncMentionsEffect({
12363
12276
  startTime: resolvedStartTime,
12364
12277
  onProgress
12365
12278
  }).pipe(
12366
- Effect15.catchAll((error) => {
12367
- if (!canFallbackToBird) return Effect15.fail(error);
12279
+ Effect14.catchAll((error) => {
12280
+ if (!canFallbackToBird) return Effect14.fail(error);
12368
12281
  source = "bird";
12369
12282
  return verifyBirdAccountMatchesEffect(resolvedAccount).pipe(
12370
- Effect15.flatMap(() => fetchMentionsViaBirdEffect({ limit }))
12283
+ Effect14.flatMap(() => fetchMentionsViaBirdEffect({ limit }))
12371
12284
  );
12372
12285
  })
12373
12286
  );
12374
12287
  if (source === "bird") {
12375
- yield* Effect15.sync(
12288
+ yield* Effect14.sync(
12376
12289
  () => onProgress?.({
12377
12290
  source: "bird",
12378
12291
  fetched: payload.data.length,
@@ -12475,7 +12388,7 @@ function exportMentionsViaCachedLiveSourceEffect({
12475
12388
  refresh = false,
12476
12389
  cacheTtlMs
12477
12390
  }) {
12478
- return Effect15.gen(function* () {
12391
+ return Effect14.gen(function* () {
12479
12392
  const primaryMode = mode === "auto" ? "xurl" : mode;
12480
12393
  if (primaryMode === "xurl") {
12481
12394
  yield* trySync(() => assertXurlLimit(limit));
@@ -12527,14 +12440,14 @@ function exportMentionsViaCachedLiveSourceEffect({
12527
12440
  all: fetchAll,
12528
12441
  parsedMaxPages
12529
12442
  })).pipe(
12530
- Effect15.catchAll((error) => {
12531
- if (mode !== "auto" || fetchAll) return Effect15.fail(error);
12443
+ Effect14.catchAll((error) => {
12444
+ if (mode !== "auto" || fetchAll) return Effect14.fail(error);
12532
12445
  source = "bird";
12533
12446
  return verifyBirdAccountMatchesEffect(resolvedAccount).pipe(
12534
- Effect15.flatMap(() => fetchMentionsViaBirdEffect({ limit }))
12447
+ Effect14.flatMap(() => fetchMentionsViaBirdEffect({ limit }))
12535
12448
  );
12536
12449
  }),
12537
- Effect15.flatMap(
12450
+ Effect14.flatMap(
12538
12451
  (payload) => databaseWriteEffect((writeDb) => {
12539
12452
  mergeMentionsIntoLocalStore(
12540
12453
  writeDb,
@@ -12546,17 +12459,17 @@ function exportMentionsViaCachedLiveSourceEffect({
12546
12459
  return readFilteredOrRaw(payload);
12547
12460
  })
12548
12461
  ),
12549
- Effect15.map((payload) => ({ ok: true, payload })),
12550
- Effect15.catchAll((error) => {
12462
+ Effect14.map((payload) => ({ ok: true, payload })),
12463
+ Effect14.catchAll((error) => {
12551
12464
  if (!refresh && cached) {
12552
- return Effect15.succeed({ ok: false });
12465
+ return Effect14.succeed({ ok: false });
12553
12466
  }
12554
- return Effect15.fail(error);
12467
+ return Effect14.fail(error);
12555
12468
  })
12556
12469
  );
12557
12470
  if (!liveResult.ok) {
12558
12471
  if (!cached) {
12559
- return yield* Effect15.fail(
12472
+ return yield* Effect14.fail(
12560
12473
  new Error("Mention export failed without cache")
12561
12474
  );
12562
12475
  }
@@ -13033,7 +12946,7 @@ function getDmSearchMatches({
13033
12946
  }
13034
12947
 
13035
12948
  // src/lib/timeline-live.ts
13036
- import { Effect as Effect16 } from "effect";
12949
+ import { Effect as Effect15 } from "effect";
13037
12950
  var DEFAULT_TIMELINE_CACHE_TTL_MS = 2 * 6e4;
13038
12951
  var MAX_XURL_TIMELINE_PAGE_SIZE = 100;
13039
12952
  function assertLimit(limit) {
@@ -13124,8 +13037,8 @@ function syncHomeTimelineEffect({
13124
13037
  timeoutMs,
13125
13038
  onProgress
13126
13039
  } = {}) {
13127
- return Effect16.gen(function* () {
13128
- const parsedStartTime = yield* Effect16.try({
13040
+ return Effect15.gen(function* () {
13041
+ const parsedStartTime = yield* Effect15.try({
13129
13042
  try: () => parseStartTime(startTime),
13130
13043
  catch: (error) => error
13131
13044
  });
@@ -13139,9 +13052,9 @@ function syncHomeTimelineEffect({
13139
13052
  const accountId = resolvedAccount.accountId;
13140
13053
  const effectiveMode = parsedMode === "auto" && account !== void 0 && !resolvedAccount.isDefault ? "xurl" : parsedMode;
13141
13054
  const cacheKey3 = `timeline:${effectiveMode}:${accountId}:${following ? "following" : "for-you"}:${Number.isFinite(effectiveLimit) ? String(effectiveLimit) : "all"}:${Number.isFinite(parsedMaxPages) ? String(parsedMaxPages) : "all-pages"}:${parsedStartTime?.iso ?? "no-start"}`;
13142
- const fetchViaXurl = Effect16.gen(function* () {
13055
+ const fetchViaXurl = Effect15.gen(function* () {
13143
13056
  if (!following) {
13144
- return yield* Effect16.fail(
13057
+ return yield* Effect15.fail(
13145
13058
  new Error("xurl home timeline mode does not support --for-you")
13146
13059
  );
13147
13060
  }
@@ -13205,7 +13118,7 @@ function syncHomeTimelineEffect({
13205
13118
  });
13206
13119
  const { source, payload } = syncResult;
13207
13120
  if (source === "cache") {
13208
- yield* Effect16.sync(
13121
+ yield* Effect15.sync(
13209
13122
  () => onProgress?.({
13210
13123
  source: "cache",
13211
13124
  fetched: payload.data.length,
@@ -13215,7 +13128,7 @@ function syncHomeTimelineEffect({
13215
13128
  );
13216
13129
  }
13217
13130
  if (source === "bird") {
13218
- yield* Effect16.sync(
13131
+ yield* Effect15.sync(
13219
13132
  () => onProgress?.({
13220
13133
  source: "bird",
13221
13134
  fetched: payload.data.length,
@@ -13309,7 +13222,7 @@ function toError6(error) {
13309
13222
  return error instanceof Error ? error : new Error(String(error));
13310
13223
  }
13311
13224
  function tryDigestSync(try_) {
13312
- return Effect17.try({
13225
+ return Effect16.try({
13313
13226
  try: try_,
13314
13227
  catch: toError6
13315
13228
  });
@@ -13682,7 +13595,7 @@ function formatPageDetail({
13682
13595
  }
13683
13596
  function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13684
13597
  if (!options.liveSync) {
13685
- return Effect17.void;
13598
+ return Effect16.void;
13686
13599
  }
13687
13600
  const includeTimeline = phase.timeline ?? true;
13688
13601
  const includeMentions = phase.mentions ?? true;
@@ -13707,9 +13620,9 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13707
13620
  );
13708
13621
  const timelineMaxPages = options.liveTimelineMaxPages === void 0 ? DEFAULT_LIVE_TIMELINE_MAX_PAGES : boundedPositiveInteger(options.liveTimelineMaxPages, 3, 1e3);
13709
13622
  const mentionsMaxPages = options.liveMentionsMaxPages === void 0 ? DEFAULT_LIVE_MENTIONS_MAX_PAGES : boundedPositiveInteger(options.liveMentionsMaxPages, 3, 1e3);
13710
- return Effect17.gen(function* () {
13623
+ return Effect16.gen(function* () {
13711
13624
  if (includeTimeline) {
13712
- yield* Effect17.sync(
13625
+ yield* Effect16.sync(
13713
13626
  () => emitDigestStatus(
13714
13627
  handlers,
13715
13628
  "Fetching home timeline from X",
@@ -13741,12 +13654,12 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13741
13654
  })
13742
13655
  )
13743
13656
  }).pipe(
13744
- Effect17.match({
13657
+ Effect16.match({
13745
13658
  onFailure: () => null,
13746
13659
  onSuccess: (value) => value
13747
13660
  })
13748
13661
  );
13749
- yield* Effect17.sync(
13662
+ yield* Effect16.sync(
13750
13663
  () => emitDigestStatus(
13751
13664
  handlers,
13752
13665
  result ? `Fetched ${String(result.count)} home tweets from ${result.source}` : "Home timeline fetch failed; using local data"
@@ -13754,7 +13667,7 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13754
13667
  );
13755
13668
  }
13756
13669
  if (includeMentions) {
13757
- yield* Effect17.sync(
13670
+ yield* Effect16.sync(
13758
13671
  () => emitDigestStatus(
13759
13672
  handlers,
13760
13673
  "Fetching mentions from X",
@@ -13784,12 +13697,12 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13784
13697
  })
13785
13698
  )
13786
13699
  }).pipe(
13787
- Effect17.match({
13700
+ Effect16.match({
13788
13701
  onFailure: () => null,
13789
13702
  onSuccess: (value) => value
13790
13703
  })
13791
13704
  );
13792
- yield* Effect17.sync(
13705
+ yield* Effect16.sync(
13793
13706
  () => emitDigestStatus(
13794
13707
  handlers,
13795
13708
  result ? `Fetched ${String(result.count)} mentions from ${result.source}` : "Mention fetch failed; using local data"
@@ -13797,7 +13710,7 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13797
13710
  );
13798
13711
  }
13799
13712
  if (includeThreads) {
13800
- yield* Effect17.sync(
13713
+ yield* Effect16.sync(
13801
13714
  () => emitDigestStatus(
13802
13715
  handlers,
13803
13716
  "Fetching mention conversations",
@@ -13818,23 +13731,23 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
13818
13731
  `${String(progress.fetched)} tweets \xB7 ${progress.source}${progress.done ? " \xB7 done" : ""}`
13819
13732
  )
13820
13733
  }).pipe(
13821
- Effect17.match({
13734
+ Effect16.match({
13822
13735
  onFailure: () => null,
13823
13736
  onSuccess: (value) => value
13824
13737
  })
13825
13738
  );
13826
- yield* Effect17.sync(
13739
+ yield* Effect16.sync(
13827
13740
  () => emitDigestStatus(
13828
13741
  handlers,
13829
13742
  result ? `Fetched ${String(result.uniqueTweets)} conversation tweets` : "Conversation fetch failed; using available context"
13830
13743
  )
13831
13744
  );
13832
13745
  }
13833
- yield* Effect17.sync(
13746
+ yield* Effect16.sync(
13834
13747
  () => emitDigestStatus(handlers, "Preparing local AI context")
13835
13748
  );
13836
- yield* maybeAutoSyncBackupEffect().pipe(Effect17.catchAll(() => Effect17.void));
13837
- }).pipe(Effect17.asVoid);
13749
+ yield* maybeAutoSyncBackupEffect().pipe(Effect16.catchAll(() => Effect16.void));
13750
+ }).pipe(Effect16.asVoid);
13838
13751
  }
13839
13752
  function digestCacheKey(context, options) {
13840
13753
  const parts = [
@@ -14043,7 +13956,7 @@ function createOpenAIRequestBody(context, options) {
14043
13956
  });
14044
13957
  }
14045
13958
  function completeOpenAIStreamEffect(stream, context, options, handlers) {
14046
- return Effect17.gen(function* () {
13959
+ return Effect16.gen(function* () {
14047
13960
  const enrichedContext = yield* tryDigestSync(
14048
13961
  () => enrichContextWithCitedTweets(context, stream.value)
14049
13962
  );
@@ -14085,7 +13998,7 @@ function completeOpenAIStreamEffect(stream, context, options, handlers) {
14085
13998
  });
14086
13999
  }
14087
14000
  function streamPeriodDigestEffect(options = {}, handlers = {}) {
14088
- return Effect17.gen(function* () {
14001
+ return Effect16.gen(function* () {
14089
14002
  const resolvedOptions = {
14090
14003
  ...options,
14091
14004
  language: yield* tryDigestSync(() => languageFromOptions(options))
@@ -14107,7 +14020,7 @@ function streamPeriodDigestEffect(options = {}, handlers = {}) {
14107
14020
  resolvedOptions,
14108
14021
  { threads: false },
14109
14022
  handlers
14110
- ).pipe(Effect17.catchAll(() => Effect17.void));
14023
+ ).pipe(Effect16.catchAll(() => Effect16.void));
14111
14024
  let context = yield* tryDigestSync(
14112
14025
  () => collectPeriodDigestContext(resolvedOptions)
14113
14026
  );
@@ -14142,7 +14055,7 @@ function streamPeriodDigestEffect(options = {}, handlers = {}) {
14142
14055
  threadTweetIds: context.tweets.filter((tweet) => tweet.source === "mentions").map((tweet) => tweet.id)
14143
14056
  },
14144
14057
  handlers
14145
- ).pipe(Effect17.catchAll(() => Effect17.void));
14058
+ ).pipe(Effect16.catchAll(() => Effect16.void));
14146
14059
  context = yield* tryDigestSync(
14147
14060
  () => collectPeriodDigestContext(resolvedOptions)
14148
14061
  );
@@ -14174,7 +14087,7 @@ function streamPeriodDigest(options = {}, handlers = {}) {
14174
14087
 
14175
14088
  // src/lib/profile-analysis.ts
14176
14089
  import { createHash as createHash6 } from "node:crypto";
14177
- import { Effect as Effect18 } from "effect";
14090
+ import { Effect as Effect17 } from "effect";
14178
14091
  import { z as z2 } from "zod";
14179
14092
 
14180
14093
  // src/lib/xurl-rate-limits.ts
@@ -14277,13 +14190,17 @@ function toError7(error) {
14277
14190
  return error instanceof Error ? error : new Error(String(error));
14278
14191
  }
14279
14192
  function isXurlRateLimitError(error) {
14193
+ const tagged = error;
14194
+ if (tagged._tag === "XurlCommandError" && tagged.rateLimited === true) {
14195
+ return true;
14196
+ }
14280
14197
  return error.message.includes("Too Many Requests") || error.message.includes('"status":429') || /\b429\b/.test(error.message);
14281
14198
  }
14282
14199
  function tryProfileSync(try_) {
14283
- return Effect18.try({ try: try_, catch: toError7 });
14200
+ return Effect17.try({ try: try_, catch: toError7 });
14284
14201
  }
14285
14202
  function tryProfilePromise(try_) {
14286
- return tryPromise(try_).pipe(Effect18.mapError(toError7));
14203
+ return tryPromise(try_).pipe(Effect17.mapError(toError7));
14287
14204
  }
14288
14205
  function normalizeHandle(value) {
14289
14206
  const handle = value.trim().replace(/^https?:\/\/(x|twitter)\.com\//i, "").replace(/^@/, "").split(/[/?#]/)[0]?.trim();
@@ -14668,7 +14585,7 @@ function sleepWithAbortEffect(ms, signal) {
14668
14585
  );
14669
14586
  }
14670
14587
  function collectProfileAnalysisContextEffect(options, handlers = {}) {
14671
- return Effect18.gen(function* () {
14588
+ return Effect17.gen(function* () {
14672
14589
  const db = getNativeDb();
14673
14590
  const handle = yield* tryProfileSync(() => normalizeHandle(options.handle));
14674
14591
  const account = yield* tryProfileSync(
@@ -14745,7 +14662,7 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
14745
14662
  });
14746
14663
  yield* abortIfRequestedEffect(options.signal);
14747
14664
  if (!user) {
14748
- return yield* Effect18.fail(new Error(`Could not resolve @${handle}`));
14665
+ return yield* Effect17.fail(new Error(`Could not resolve @${handle}`));
14749
14666
  }
14750
14667
  const resolved = yield* tryProfileSync(
14751
14668
  () => upsertProfileFromXUser(db, user)
@@ -14849,9 +14766,9 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
14849
14766
  signal: options.signal,
14850
14767
  onAttempt: recordConversationAttempt
14851
14768
  }).pipe(
14852
- Effect18.catchAll((error) => {
14769
+ Effect17.catchAll((error) => {
14853
14770
  if (!isXurlRateLimitError(error)) {
14854
- return Effect18.fail(error);
14771
+ return Effect17.fail(error);
14855
14772
  }
14856
14773
  if (attempt < rateLimitMaxRetries) {
14857
14774
  emitStatus(
@@ -14862,7 +14779,7 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
14862
14779
  return sleepWithAbortEffect(
14863
14780
  rateLimitRetryMs,
14864
14781
  options.signal
14865
- ).pipe(Effect18.as(null));
14782
+ ).pipe(Effect17.as(null));
14866
14783
  }
14867
14784
  conversationRateLimited = true;
14868
14785
  emitStatus(
@@ -14870,7 +14787,7 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
14870
14787
  "Conversation fetch rate limited",
14871
14788
  "using partial profile context"
14872
14789
  );
14873
- return Effect18.succeed(null);
14790
+ return Effect17.succeed(null);
14874
14791
  })
14875
14792
  );
14876
14793
  if (response || conversationRateLimited) {
@@ -15016,7 +14933,7 @@ function createOpenAIRequestBody2(context, options) {
15016
14933
  });
15017
14934
  }
15018
14935
  function streamProfileAnalysisEffect(options, handlers = {}) {
15019
- return Effect18.gen(function* () {
14936
+ return Effect17.gen(function* () {
15020
14937
  const context = yield* collectProfileAnalysisContextEffect(
15021
14938
  options,
15022
14939
  handlers
@@ -15082,10 +14999,10 @@ function streamProfileAnalysis(options, handlers = {}) {
15082
14999
  // src/lib/research.ts
15083
15000
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
15084
15001
  import path4 from "node:path";
15085
- import { Effect as Effect20 } from "effect";
15002
+ import { Effect as Effect19 } from "effect";
15086
15003
 
15087
15004
  // src/lib/tweet-lookup.ts
15088
- import { Effect as Effect19 } from "effect";
15005
+ import { Effect as Effect18 } from "effect";
15089
15006
  function errorMessage2(error) {
15090
15007
  return error instanceof Error ? error.message : String(error);
15091
15008
  }
@@ -15097,10 +15014,10 @@ function lookupTweetsByIdsEffect2(ids, mode = "auto") {
15097
15014
  return lookupTweetsByIdsEffect(ids);
15098
15015
  }
15099
15016
  return lookupTweetsByIdsEffect(ids).pipe(
15100
- Effect19.catchAll(
15017
+ Effect18.catchAll(
15101
15018
  (xurlError) => lookupTweetsByIdsViaBirdEffect(ids).pipe(
15102
- Effect19.catchAll(
15103
- (birdError) => Effect19.fail(
15019
+ Effect18.catchAll(
15020
+ (birdError) => Effect18.fail(
15104
15021
  new Error(
15105
15022
  `Tweet lookup failed via xurl and bird: xurl: ${errorMessage2(
15106
15023
  xurlError
@@ -15115,7 +15032,7 @@ function lookupTweetsByIdsEffect2(ids, mode = "auto") {
15115
15032
 
15116
15033
  // src/lib/research.ts
15117
15034
  function trySync4(try_) {
15118
- return Effect20.try({
15035
+ return Effect19.try({
15119
15036
  try: try_,
15120
15037
  catch: (cause) => cause
15121
15038
  });
@@ -15299,7 +15216,7 @@ function getTweetDescendants(rootTweetId, maxThreadDepth) {
15299
15216
  return rows.map((row) => toResearchNode(row, "local"));
15300
15217
  }
15301
15218
  function lookupTweetNodeEffect(tweetId) {
15302
- return Effect20.gen(function* () {
15219
+ return Effect19.gen(function* () {
15303
15220
  const payload = yield* lookupTweetsByIdsEffect2([tweetId]);
15304
15221
  const tweet = payload.data[0];
15305
15222
  if (!tweet) {
@@ -15337,7 +15254,7 @@ function lookupTweetNodeEffect(tweetId) {
15337
15254
  });
15338
15255
  }
15339
15256
  function collectAncestorChainEffect(tweetId, maxThreadDepth) {
15340
- return Effect20.gen(function* () {
15257
+ return Effect19.gen(function* () {
15341
15258
  const chain = [];
15342
15259
  let currentId = tweetId;
15343
15260
  const visited = /* @__PURE__ */ new Set();
@@ -15504,7 +15421,7 @@ function resolveSeedTimelineItems({
15504
15421
  });
15505
15422
  }
15506
15423
  function runResearchModeEffect(options = {}) {
15507
- return Effect20.gen(function* () {
15424
+ return Effect19.gen(function* () {
15508
15425
  const limit = yield* trySync4(
15509
15426
  () => Number.isFinite(options.limit ?? 20) ? Math.max(1, Math.floor(options.limit ?? 20)) : 20
15510
15427
  );
@@ -15577,11 +15494,11 @@ function runResearchMode(options = {}) {
15577
15494
 
15578
15495
  // src/lib/search-discussion.ts
15579
15496
  import { createHash as createHash7 } from "node:crypto";
15580
- import { Effect as Effect22 } from "effect";
15497
+ import { Effect as Effect21 } from "effect";
15581
15498
  import { z as z3 } from "zod";
15582
15499
 
15583
15500
  // src/lib/tweet-search-live.ts
15584
- import { Effect as Effect21 } from "effect";
15501
+ import { Effect as Effect20 } from "effect";
15585
15502
  var DEFAULT_SEARCH_LIMIT = 2e4;
15586
15503
  var DEFAULT_MAX_PAGES2 = 200;
15587
15504
  var DEFAULT_CACHE_TTL_MS2 = 2 * 6e4;
@@ -15698,7 +15615,7 @@ function fetchXurlSearchEffect({
15698
15615
  since,
15699
15616
  until
15700
15617
  }) {
15701
- return Effect21.gen(function* () {
15618
+ return Effect20.gen(function* () {
15702
15619
  const result = yield* runSyncPlanEffect({
15703
15620
  fetchPage: ({ cursor, fetched }) => {
15704
15621
  const remaining = Math.max(1, limit - fetched);
@@ -15708,7 +15625,7 @@ function fetchXurlSearchEffect({
15708
15625
  startTime: since,
15709
15626
  endTime: until,
15710
15627
  timeoutMs
15711
- }).pipe(Effect21.map(toMentionsResponse));
15628
+ }).pipe(Effect20.map(toMentionsResponse));
15712
15629
  },
15713
15630
  getItemCount: (page) => page.data.length,
15714
15631
  getNextCursor: (page) => typeof page.meta?.next_token === "string" ? page.meta.next_token : void 0,
@@ -15719,10 +15636,10 @@ function fetchXurlSearchEffect({
15719
15636
  });
15720
15637
  }
15721
15638
  function runModeEffect(mode, options) {
15722
- return Effect21.gen(function* () {
15639
+ return Effect20.gen(function* () {
15723
15640
  const db = getNativeDb();
15724
15641
  const key = cacheKey({ ...options, mode });
15725
- const fetch2 = mode === "bird" ? fetchBirdSearchEffect(options).pipe(Effect21.mapError(toError)) : fetchXurlSearchEffect(options);
15642
+ const fetch2 = mode === "bird" ? fetchBirdSearchEffect(options).pipe(Effect20.mapError(toError)) : fetchXurlSearchEffect(options);
15726
15643
  const syncResult = yield* runCachedLiveSyncEffect({
15727
15644
  db,
15728
15645
  cacheKey: key,
@@ -15732,7 +15649,7 @@ function runModeEffect(mode, options) {
15732
15649
  {
15733
15650
  source: mode,
15734
15651
  fetch: fetch2.pipe(
15735
- Effect21.map((response) => limitResponse(response, options.limit))
15652
+ Effect20.map((response) => limitResponse(response, options.limit))
15736
15653
  )
15737
15654
  }
15738
15655
  ],
@@ -15803,10 +15720,10 @@ function syncTweetSearchEffect({
15803
15720
  cacheTtlMs,
15804
15721
  timeoutMs
15805
15722
  }) {
15806
- return Effect21.gen(function* () {
15723
+ return Effect20.gen(function* () {
15807
15724
  const normalizedQuery = query.trim();
15808
15725
  if (!normalizedQuery) {
15809
- return yield* Effect21.fail(new Error("Search query is required"));
15726
+ return yield* Effect20.fail(new Error("Search query is required"));
15810
15727
  }
15811
15728
  const normalizedLimit = normalizeLimit(limit);
15812
15729
  const normalizedMaxPages = normalizeMaxPages(maxPages);
@@ -15847,8 +15764,8 @@ function syncTweetSearchEffect({
15847
15764
  };
15848
15765
  if (mode === "bird" || mode === "xurl") {
15849
15766
  return yield* runModeEffect(mode, runOptions).pipe(
15850
- Effect21.catchAll(
15851
- (error) => Effect21.succeed({
15767
+ Effect20.catchAll(
15768
+ (error) => Effect20.succeed({
15852
15769
  ok: false,
15853
15770
  source: mode,
15854
15771
  accountId,
@@ -15860,8 +15777,8 @@ function syncTweetSearchEffect({
15860
15777
  }
15861
15778
  if (normalizedSince || normalizedUntil) {
15862
15779
  return yield* runModeEffect("xurl", runOptions).pipe(
15863
- Effect21.catchAll(
15864
- (error) => Effect21.succeed({
15780
+ Effect20.catchAll(
15781
+ (error) => Effect20.succeed({
15865
15782
  ok: false,
15866
15783
  source: "auto",
15867
15784
  accountId,
@@ -15872,8 +15789,8 @@ function syncTweetSearchEffect({
15872
15789
  );
15873
15790
  }
15874
15791
  const birdResult = yield* runModeEffect("bird", runOptions).pipe(
15875
- Effect21.catchAll(
15876
- (error) => Effect21.succeed({
15792
+ Effect20.catchAll(
15793
+ (error) => Effect20.succeed({
15877
15794
  ok: false,
15878
15795
  source: "bird",
15879
15796
  accountId,
@@ -15883,8 +15800,8 @@ function syncTweetSearchEffect({
15883
15800
  )
15884
15801
  );
15885
15802
  const xurlResult = yield* runModeEffect("xurl", runOptions).pipe(
15886
- Effect21.catchAll(
15887
- (error) => Effect21.succeed({
15803
+ Effect20.catchAll(
15804
+ (error) => Effect20.succeed({
15888
15805
  ok: false,
15889
15806
  source: "xurl",
15890
15807
  accountId,
@@ -15923,7 +15840,7 @@ function toError8(error) {
15923
15840
  return error instanceof Error ? error : new Error(String(error));
15924
15841
  }
15925
15842
  function trySearchSync(try_) {
15926
- return Effect22.try({
15843
+ return Effect21.try({
15927
15844
  try: try_,
15928
15845
  catch: toError8
15929
15846
  });
@@ -16214,8 +16131,8 @@ function prefetchDiscussionAvatars(context) {
16214
16131
  }
16215
16132
  runEffectBackground(
16216
16133
  prefetchCachedAvatarsForProfileIdsEffect(profileIds).pipe(
16217
- Effect22.catchAll(
16218
- () => Effect22.succeed({
16134
+ Effect21.catchAll(
16135
+ () => Effect21.succeed({
16219
16136
  requested: 0,
16220
16137
  available: 0,
16221
16138
  missing: 0,
@@ -16351,7 +16268,7 @@ function createOpenAIRequestBody3(context, options) {
16351
16268
  });
16352
16269
  }
16353
16270
  function completeOpenAIStreamEffect2(stream, context, options, handlers) {
16354
- return Effect22.gen(function* () {
16271
+ return Effect21.gen(function* () {
16355
16272
  const updatedAt = yield* trySearchSync(
16356
16273
  () => writeSyncCache(cacheKey2(context, options), {
16357
16274
  discussion: stream.value,
@@ -16378,7 +16295,7 @@ function completeOpenAIStreamEffect2(stream, context, options, handlers) {
16378
16295
  });
16379
16296
  }
16380
16297
  function streamSearchDiscussionEffect(options, handlers = {}) {
16381
- return Effect22.gen(function* () {
16298
+ return Effect21.gen(function* () {
16382
16299
  const mode = options.mode ?? "auto";
16383
16300
  const liveSearch = mode === "local" ? void 0 : yield* syncTweetSearchEffect({
16384
16301
  query: options.query,
@@ -16392,7 +16309,7 @@ function streamSearchDiscussionEffect(options, handlers = {}) {
16392
16309
  timeoutMs: 3e4
16393
16310
  });
16394
16311
  if (liveSearch && !liveSearch.ok) {
16395
- return yield* Effect22.fail(
16312
+ return yield* Effect21.fail(
16396
16313
  new Error(
16397
16314
  `Live tweet search failed via ${liveSearch.source}: ${liveSearch.error}`
16398
16315
  )
@@ -16772,12 +16689,12 @@ function registerAnalysisCommands({
16772
16689
 
16773
16690
  // src/lib/query-actions.ts
16774
16691
  import { randomUUID as randomUUID2 } from "node:crypto";
16775
- import { Effect as Effect23 } from "effect";
16692
+ import { Effect as Effect22 } from "effect";
16776
16693
  function toError9(error) {
16777
16694
  return error instanceof Error ? error : new Error(String(error));
16778
16695
  }
16779
16696
  function trySync5(try_) {
16780
- return Effect23.try({ try: try_, catch: toError9 });
16697
+ return Effect22.try({ try: try_, catch: toError9 });
16781
16698
  }
16782
16699
  function e2eFakeLiveWritesEnabled2() {
16783
16700
  return process.env.BIRDCLAW_E2E === "1" && process.env.BIRDCLAW_E2E_FAKE_LIVE_WRITES === "1";
@@ -16786,7 +16703,7 @@ function liveWritesDisabled2() {
16786
16703
  return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
16787
16704
  }
16788
16705
  function verifySelectedXurlAccountEffect(accountId) {
16789
- return Effect23.gen(function* () {
16706
+ return Effect22.gen(function* () {
16790
16707
  if (liveWritesDisabled2()) return;
16791
16708
  if (e2eFakeLiveWritesEnabled2()) return;
16792
16709
  const db = yield* trySync5(() => getReadDb());
@@ -16794,7 +16711,7 @@ function verifySelectedXurlAccountEffect(accountId) {
16794
16711
  () => db.prepare("select handle, external_user_id from accounts where id = ?").get(accountId)
16795
16712
  );
16796
16713
  if (!account) {
16797
- return yield* Effect23.fail(new Error(`Unknown account: ${accountId}`));
16714
+ return yield* Effect22.fail(new Error(`Unknown account: ${accountId}`));
16798
16715
  }
16799
16716
  const authenticated = yield* tryPromise(
16800
16717
  () => lookupAuthenticatedUserFresh()
@@ -16803,7 +16720,7 @@ function verifySelectedXurlAccountEffect(accountId) {
16803
16720
  const authenticatedHandle = typeof authenticated?.username === "string" ? authenticated.username.replace(/^@/, "") : "";
16804
16721
  const expectedHandle = account.handle.replace(/^@/, "");
16805
16722
  if (account.external_user_id && account.external_user_id !== authenticatedId || !account.external_user_id && (!authenticatedHandle || authenticatedHandle.toLowerCase() !== expectedHandle.toLowerCase())) {
16806
- return yield* Effect23.fail(
16723
+ return yield* Effect22.fail(
16807
16724
  new Error(
16808
16725
  `xurl is authenticated as @${authenticatedHandle || authenticatedId}, not @${expectedHandle}`
16809
16726
  )
@@ -16915,7 +16832,7 @@ function writePostDraft(db, accountId, text, draft) {
16915
16832
  );
16916
16833
  }
16917
16834
  function createPostEffect(accountId, text) {
16918
- return Effect23.gen(function* () {
16835
+ return Effect22.gen(function* () {
16919
16836
  const draft = yield* trySync5(() => preparePostDraft(accountId));
16920
16837
  yield* databaseWriteEffect(
16921
16838
  (db) => preflightWrite(
@@ -16926,7 +16843,7 @@ function createPostEffect(accountId, text) {
16926
16843
  yield* verifySelectedXurlAccountEffect(accountId);
16927
16844
  const transport = yield* postViaXurlEffect(text);
16928
16845
  if (!transport.ok) {
16929
- return yield* Effect23.fail(new Error(transport.output || "post failed"));
16846
+ return yield* Effect22.fail(new Error(transport.output || "post failed"));
16930
16847
  }
16931
16848
  yield* databaseWriteEffect(
16932
16849
  (db) => writePostDraft(db, accountId, text, draft)
@@ -16970,7 +16887,7 @@ function createTweetReplyEffect(accountId, tweetId, text) {
16970
16887
  "insert into tweet_actions (id, account_id, tweet_id, kind, body, created_at) values (?, ?, ?, ?, ?, ?)"
16971
16888
  ).run(draft.actionId, accountId, tweetId, "reply", text, draft.createdAt);
16972
16889
  }
16973
- return Effect23.gen(function* () {
16890
+ return Effect22.gen(function* () {
16974
16891
  const draft = yield* trySync5(() => prepareReplyDraft());
16975
16892
  yield* databaseWriteEffect(
16976
16893
  (db) => preflightWrite(db, (writeDb) => writeReplyDraft(writeDb, draft))
@@ -16978,7 +16895,7 @@ function createTweetReplyEffect(accountId, tweetId, text) {
16978
16895
  yield* verifySelectedXurlAccountEffect(accountId);
16979
16896
  const transport = yield* replyViaXurlEffect(tweetId, text);
16980
16897
  if (!transport.ok) {
16981
- return yield* Effect23.fail(new Error(transport.output || "reply failed"));
16898
+ return yield* Effect22.fail(new Error(transport.output || "reply failed"));
16982
16899
  }
16983
16900
  yield* databaseWriteEffect((db) => writeReplyDraft(db, draft));
16984
16901
  return { ok: true, transport, replyId: draft.replyId };
@@ -16988,7 +16905,7 @@ function createTweetReply(accountId, tweetId, text) {
16988
16905
  return runEffectPromise(createTweetReplyEffect(accountId, tweetId, text));
16989
16906
  }
16990
16907
  function createDmReplyEffect(conversationId, text) {
16991
- return Effect23.gen(function* () {
16908
+ return Effect22.gen(function* () {
16992
16909
  const draft = yield* trySync5(() => {
16993
16910
  const conversation = getConversationThread(conversationId);
16994
16911
  if (!conversation) {
@@ -17037,7 +16954,7 @@ function createDmReplyEffect(conversationId, text) {
17037
16954
  yield* verifySelectedXurlAccountEffect(draft.accountId);
17038
16955
  const transport = yield* dmViaXurlEffect(draft.handle, text);
17039
16956
  if (!transport.ok) {
17040
- return yield* Effect23.fail(new Error(transport.output || "dm failed"));
16957
+ return yield* Effect22.fail(new Error(transport.output || "dm failed"));
17041
16958
  }
17042
16959
  yield* databaseWriteEffect((db) => {
17043
16960
  db.prepare(
@@ -17111,7 +17028,7 @@ async function applyDmRequestMutationToLocalStore(conversationId, action) {
17111
17028
  }
17112
17029
 
17113
17030
  // src/lib/query-status.ts
17114
- import { Effect as Effect25 } from "effect";
17031
+ import { Effect as Effect24 } from "effect";
17115
17032
 
17116
17033
  // src/lib/archive-finder.ts
17117
17034
  import { exec } from "node:child_process";
@@ -17119,7 +17036,7 @@ import { existsSync as existsSync3, promises as fs2 } from "node:fs";
17119
17036
  import { homedir } from "node:os";
17120
17037
  import path5 from "node:path";
17121
17038
  import { promisify as promisify3 } from "node:util";
17122
- import { Effect as Effect24 } from "effect";
17039
+ import { Effect as Effect23 } from "effect";
17123
17040
  var execAsync = promisify3(exec);
17124
17041
  var ARCHIVE_DISCOVERY_CACHE_MS = 5 * 6e4;
17125
17042
  var ARCHIVE_NAME_PATTERNS = [
@@ -17151,7 +17068,7 @@ function formatRelativeDate(date) {
17151
17068
  return `${Math.floor(days / 365)} years ago`;
17152
17069
  }
17153
17070
  function getCandidateEffect(filePath) {
17154
- return Effect24.gen(function* () {
17071
+ return Effect23.gen(function* () {
17155
17072
  const stats = yield* tryPromise(() => fs2.stat(filePath));
17156
17073
  if (!stats.isFile() || stats.size < 1024 * 1024) {
17157
17074
  return null;
@@ -17164,43 +17081,43 @@ function getCandidateEffect(filePath) {
17164
17081
  modifiedTime: stats.mtime.toISOString(),
17165
17082
  dateFormatted: formatRelativeDate(stats.mtime)
17166
17083
  };
17167
- }).pipe(Effect24.catchAll(() => Effect24.succeed(null)));
17084
+ }).pipe(Effect23.catchAll(() => Effect23.succeed(null)));
17168
17085
  }
17169
17086
  function searchDirectoryEffect(directoryPath) {
17170
17087
  if (!existsSync3(directoryPath)) {
17171
- return Effect24.succeed([]);
17088
+ return Effect23.succeed([]);
17172
17089
  }
17173
- return Effect24.gen(function* () {
17090
+ return Effect23.gen(function* () {
17174
17091
  const entries = yield* tryPromise(() => fs2.readdir(directoryPath));
17175
17092
  const matches = entries.filter(
17176
17093
  (entry) => ARCHIVE_NAME_PATTERNS.some((pattern) => pattern.test(entry))
17177
17094
  );
17178
- const candidates = yield* Effect24.forEach(
17095
+ const candidates = yield* Effect23.forEach(
17179
17096
  matches,
17180
17097
  (entry) => getCandidateEffect(path5.join(directoryPath, entry)),
17181
17098
  { concurrency: "unbounded" }
17182
17099
  );
17183
17100
  return candidates.filter((item) => item !== null);
17184
- }).pipe(Effect24.catchAll(() => Effect24.succeed([])));
17101
+ }).pipe(Effect23.catchAll(() => Effect23.succeed([])));
17185
17102
  }
17186
17103
  function searchSpotlightEffect(query) {
17187
- return Effect24.gen(function* () {
17104
+ return Effect23.gen(function* () {
17188
17105
  const { stdout } = yield* tryPromise(
17189
17106
  () => execAsync(`mdfind -onlyin ~ '${query}'`, {
17190
17107
  timeout: 5e3
17191
17108
  })
17192
17109
  );
17193
17110
  const paths = stdout.split("\n").map((item) => item.trim()).filter((item) => item.length > 0 && item.endsWith(".zip"));
17194
- return yield* Effect24.forEach(paths, getCandidateEffect, {
17111
+ return yield* Effect23.forEach(paths, getCandidateEffect, {
17195
17112
  concurrency: "unbounded"
17196
17113
  });
17197
- }).pipe(Effect24.catchAll(() => Effect24.succeed([])));
17114
+ }).pipe(Effect23.catchAll(() => Effect23.succeed([])));
17198
17115
  }
17199
17116
  function findArchivesEffect() {
17200
17117
  if (process.platform !== "darwin") {
17201
- return Effect24.succeed([]);
17118
+ return Effect23.succeed([]);
17202
17119
  }
17203
- return Effect24.gen(function* () {
17120
+ return Effect23.gen(function* () {
17204
17121
  const found = /* @__PURE__ */ new Map();
17205
17122
  const downloads = yield* searchDirectoryEffect(
17206
17123
  path5.join(homedir(), "Downloads")
@@ -17213,7 +17130,7 @@ function findArchivesEffect() {
17213
17130
  'kMDItemDisplayName == "x-*.zip"',
17214
17131
  'kMDItemDisplayName == "*archive*.zip" && kMDItemKind == "Zip archive"'
17215
17132
  ];
17216
- const spotlightCandidates = yield* Effect24.forEach(
17133
+ const spotlightCandidates = yield* Effect23.forEach(
17217
17134
  queries,
17218
17135
  searchSpotlightEffect,
17219
17136
  { concurrency: "unbounded" }
@@ -17231,13 +17148,13 @@ function findArchivesEffect() {
17231
17148
  });
17232
17149
  }
17233
17150
  function findArchivesCachedEffect() {
17234
- return Effect24.suspend(() => {
17151
+ return Effect23.suspend(() => {
17235
17152
  const cached = archiveDiscoveryCache;
17236
17153
  if (cached && Date.now() - cached.updatedAt < ARCHIVE_DISCOVERY_CACHE_MS) {
17237
- return Effect24.succeed(cached.value);
17154
+ return Effect23.succeed(cached.value);
17238
17155
  }
17239
17156
  if (archiveDiscoveryInFlight) {
17240
- return Effect24.promise(() => archiveDiscoveryInFlight);
17157
+ return Effect23.promise(() => archiveDiscoveryInFlight);
17241
17158
  }
17242
17159
  const request = runEffectPromise(findArchivesEffect()).then((value) => {
17243
17160
  archiveDiscoveryCache = { value, updatedAt: Date.now() };
@@ -17248,7 +17165,7 @@ function findArchivesCachedEffect() {
17248
17165
  }
17249
17166
  });
17250
17167
  archiveDiscoveryInFlight = request;
17251
- return Effect24.promise(() => request);
17168
+ return Effect23.promise(() => request);
17252
17169
  });
17253
17170
  }
17254
17171
  function findArchives() {
@@ -17260,7 +17177,7 @@ function toError10(error) {
17260
17177
  return error instanceof Error ? error : new Error(String(error));
17261
17178
  }
17262
17179
  function trySync6(try_) {
17263
- return Effect25.try({ try: try_, catch: toError10 });
17180
+ return Effect24.try({ try: try_, catch: toError10 });
17264
17181
  }
17265
17182
  function countTimelineEdges(db, kind) {
17266
17183
  const row = db.prepare(
@@ -17294,7 +17211,7 @@ function getAccountProfileMeta(db, account) {
17294
17211
  function getQueryEnvelopeEffect({
17295
17212
  includeArchives = true
17296
17213
  } = {}) {
17297
- return Effect25.gen(function* () {
17214
+ return Effect24.gen(function* () {
17298
17215
  const nativeDb2 = yield* trySync6(() => getReadDb());
17299
17216
  const homeCount = yield* trySync6(
17300
17217
  () => countTimelineEdges(nativeDb2, "home")
@@ -17302,7 +17219,7 @@ function getQueryEnvelopeEffect({
17302
17219
  const mentionCount = yield* trySync6(
17303
17220
  () => countTimelineEdges(nativeDb2, "mention")
17304
17221
  );
17305
- const counts = yield* Effect25.all({
17222
+ const counts = yield* Effect24.all({
17306
17223
  dms: trySync6(
17307
17224
  () => nativeDb2.prepare("select count(*) as count from dm_conversations").get()
17308
17225
  ),
@@ -17316,7 +17233,7 @@ function getQueryEnvelopeEffect({
17316
17233
  "select * from accounts order by is_default desc, name asc"
17317
17234
  ).all()
17318
17235
  ),
17319
- archives: includeArchives ? findArchivesCachedEffect() : Effect25.succeed([]),
17236
+ archives: includeArchives ? findArchivesCachedEffect() : Effect24.succeed([]),
17320
17237
  transport: getTransportStatusEffect()
17321
17238
  });
17322
17239
  return {
@@ -17379,7 +17296,7 @@ function registerComposeCommands({
17379
17296
  }
17380
17297
 
17381
17298
  // src/lib/archive-import.ts
17382
- import { Effect as Effect33 } from "effect";
17299
+ import { Effect as Effect32 } from "effect";
17383
17300
 
17384
17301
  // src/lib/archive-import-plan.ts
17385
17302
  var ArchiveImportPlan = class {
@@ -17413,7 +17330,7 @@ var ArchiveImportPlan = class {
17413
17330
 
17414
17331
  // src/lib/archive/apply.ts
17415
17332
  import { randomUUID as randomUUID3 } from "node:crypto";
17416
- import { Effect as Effect26 } from "effect";
17333
+ import { Effect as Effect25 } from "effect";
17417
17334
  function applyArchiveImportPlanEffect({
17418
17335
  archivePath,
17419
17336
  db,
@@ -17443,7 +17360,7 @@ function applyArchiveImportPlanEffect({
17443
17360
  followers: followerRows,
17444
17361
  following: followingRows
17445
17362
  } = plan;
17446
- return Effect26.gen(function* () {
17363
+ return Effect25.gen(function* () {
17447
17364
  const insertAccount = db.prepare(`
17448
17365
  insert into accounts (id, name, handle, external_user_id, transport, is_default, created_at)
17449
17366
  values (?, ?, ?, ?, ?, 1, ?)
@@ -18028,7 +17945,7 @@ function applyArchiveImportPlanEffect({
18028
17945
  }
18029
17946
 
18030
17947
  // src/lib/archive/collection-slice.ts
18031
- import { Effect as Effect28 } from "effect";
17948
+ import { Effect as Effect27 } from "effect";
18032
17949
 
18033
17950
  // src/lib/archive/parsing.ts
18034
17951
  function parseTwitterDate(value) {
@@ -18220,7 +18137,7 @@ function getArchiveFollowRow(wrapper, key) {
18220
18137
 
18221
18138
  // src/lib/archive/reader.ts
18222
18139
  import { spawn } from "node:child_process";
18223
- import { Effect as Effect27 } from "effect";
18140
+ import { Effect as Effect26 } from "effect";
18224
18141
  var ARCHIVE_JSON_PAYLOAD = /=\s*(\[[\s\S]*\]|\{[\s\S]*\})/s;
18225
18142
  function normalizeArchivePath(value) {
18226
18143
  return value.replaceAll("\\", "/");
@@ -18238,10 +18155,10 @@ function runUnzipEffect(args, maxBuffer = 1024 * 1024 * 256) {
18238
18155
  command: "unzip",
18239
18156
  args,
18240
18157
  maxBufferBytes: maxBuffer
18241
- }).pipe(Effect27.map(({ stdout }) => stdout));
18158
+ }).pipe(Effect26.map(({ stdout }) => stdout));
18242
18159
  }
18243
18160
  function listArchiveEntriesEffect(archivePath) {
18244
- return Effect27.gen(function* () {
18161
+ return Effect26.gen(function* () {
18245
18162
  const stdout = yield* runUnzipEffect(
18246
18163
  ["-Z1", archivePath],
18247
18164
  1024 * 1024 * 64
@@ -18250,7 +18167,7 @@ function listArchiveEntriesEffect(archivePath) {
18250
18167
  });
18251
18168
  }
18252
18169
  function listArchiveEntryDetailsEffect(archivePath) {
18253
- return Effect27.gen(function* () {
18170
+ return Effect26.gen(function* () {
18254
18171
  const stdout = yield* runUnzipEffect(
18255
18172
  ["-Z", "-l", archivePath],
18256
18173
  1024 * 1024 * 64
@@ -18317,7 +18234,7 @@ function parseCollectionSliceEffect({
18317
18234
  plan,
18318
18235
  onProgress
18319
18236
  }) {
18320
- return Effect28.gen(function* () {
18237
+ return Effect27.gen(function* () {
18321
18238
  const slice = kind === "like" ? "likes" : "bookmarks";
18322
18239
  if (entries.length > 0) {
18323
18240
  onProgress({ kind: "slice-start", slice, files: entries.length });
@@ -18367,7 +18284,7 @@ function parseCollectionSliceEffect({
18367
18284
  }
18368
18285
 
18369
18286
  // src/lib/archive/dm-slice.ts
18370
- import { Effect as Effect29 } from "effect";
18287
+ import { Effect as Effect28 } from "effect";
18371
18288
 
18372
18289
  // src/lib/archive/profile-reconciler.ts
18373
18290
  var defaultArchiveProfileMetadata = {
@@ -18602,7 +18519,7 @@ function parseDirectMessagesEffect({
18602
18519
  onProgress
18603
18520
  }) {
18604
18521
  const { mentionDirectory, profiles, conversations, dmMessages } = plan;
18605
- return Effect29.gen(function* () {
18522
+ return Effect28.gen(function* () {
18606
18523
  const existingDmConversationAccounts = new Map(
18607
18524
  db.prepare("select id, account_id from dm_conversations").all().map((row) => [row.id, row.account_id])
18608
18525
  );
@@ -18836,7 +18753,7 @@ function parseDirectMessagesEffect({
18836
18753
  }
18837
18754
 
18838
18755
  // src/lib/archive/follow-slice.ts
18839
- import { Effect as Effect30 } from "effect";
18756
+ import { Effect as Effect29 } from "effect";
18840
18757
  function parseFollowSliceEffect({
18841
18758
  archivePath,
18842
18759
  entries,
@@ -18847,7 +18764,7 @@ function parseFollowSliceEffect({
18847
18764
  const key = direction === "followers" ? "follower" : "following";
18848
18765
  const rows = direction === "followers" ? plan.followers : plan.following;
18849
18766
  const ids = direction === "followers" ? plan.followerIds : plan.followingIds;
18850
- return Effect30.gen(function* () {
18767
+ return Effect29.gen(function* () {
18851
18768
  if (entries.length > 0) {
18852
18769
  onProgress({
18853
18770
  kind: "slice-start",
@@ -18897,14 +18814,14 @@ function reconcileFollowProfiles({
18897
18814
 
18898
18815
  // src/lib/archive/tweet-slices.ts
18899
18816
  import { randomUUID as randomUUID4 } from "node:crypto";
18900
- import { Effect as Effect31 } from "effect";
18817
+ import { Effect as Effect30 } from "effect";
18901
18818
  function parseTweetSliceEffect({
18902
18819
  archivePath,
18903
18820
  entries,
18904
18821
  plan,
18905
18822
  onProgress
18906
18823
  }) {
18907
- return Effect31.gen(function* () {
18824
+ return Effect30.gen(function* () {
18908
18825
  if (entries.length > 0) {
18909
18826
  onProgress({
18910
18827
  kind: "slice-start",
@@ -18977,7 +18894,7 @@ function parseNoteTweetSliceEffect({
18977
18894
  plan,
18978
18895
  onProgress
18979
18896
  }) {
18980
- return Effect31.gen(function* () {
18897
+ return Effect30.gen(function* () {
18981
18898
  if (entries.length > 0) {
18982
18899
  onProgress({
18983
18900
  kind: "slice-start",
@@ -19038,7 +18955,7 @@ import {
19038
18955
  } from "node:fs";
19039
18956
  import path6 from "node:path";
19040
18957
  import { pipeline } from "node:stream/promises";
19041
- import { Effect as Effect32 } from "effect";
18958
+ import { Effect as Effect31 } from "effect";
19042
18959
  var ARCHIVE_MEDIA_DIRECTORIES = [
19043
18960
  { directory: "tweets_media", kind: "tweets" },
19044
18961
  { directory: "direct_messages_media", kind: "dms" },
@@ -19124,7 +19041,7 @@ function copyArchiveEntryToFileEffect(archivePath, entryPath, destinationPath) {
19124
19041
  });
19125
19042
  }
19126
19043
  function extractArchiveMediaFilesEffect(archivePath, selectedKinds) {
19127
- return Effect32.gen(function* () {
19044
+ return Effect31.gen(function* () {
19128
19045
  const counts = createArchiveMediaFileCounts();
19129
19046
  if (selectedKinds?.size === 0) return counts;
19130
19047
  const entries = yield* listArchiveEntryDetailsEffect(archivePath);
@@ -19207,7 +19124,7 @@ var ARCHIVE_IMPORT_SLICES = [
19207
19124
 
19208
19125
  // src/lib/archive-import.ts
19209
19126
  function importArchiveInternalEffect(archivePath, options = {}) {
19210
- return Effect33.gen(function* () {
19127
+ return Effect32.gen(function* () {
19211
19128
  const onProgress = options.onProgress ?? (() => {
19212
19129
  });
19213
19130
  const entries = yield* listArchiveEntriesEffect(archivePath);
@@ -19232,11 +19149,11 @@ function importArchiveInternalEffect(archivePath, options = {}) {
19232
19149
  followingEntries
19233
19150
  } = selectArchiveSliceEntries(entries, selection);
19234
19151
  if (!accountEntry) {
19235
- return yield* Effect33.fail(new Error("Archive missing data/account.js"));
19152
+ return yield* Effect32.fail(new Error("Archive missing data/account.js"));
19236
19153
  }
19237
- const [accountContent, profileContent] = yield* Effect33.all([
19154
+ const [accountContent, profileContent] = yield* Effect32.all([
19238
19155
  readArchiveEntryEffect(archivePath, accountEntry),
19239
- profileEntry ? readArchiveEntryEffect(archivePath, profileEntry) : Effect33.succeed("[]")
19156
+ profileEntry ? readArchiveEntryEffect(archivePath, profileEntry) : Effect32.succeed("[]")
19240
19157
  ]);
19241
19158
  const accountPayload = buildAccountPayload(
19242
19159
  parseArchiveArray(accountContent)[0] ?? null,
@@ -19388,7 +19305,7 @@ function importArchive(archivePath, options = {}) {
19388
19305
  }
19389
19306
 
19390
19307
  // src/lib/profile-hydration.ts
19391
- import { Effect as Effect34 } from "effect";
19308
+ import { Effect as Effect33 } from "effect";
19392
19309
  function asRecord3(value) {
19393
19310
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
19394
19311
  }
@@ -19400,7 +19317,7 @@ function toError11(error) {
19400
19317
  return error instanceof Error ? error : new Error(String(error));
19401
19318
  }
19402
19319
  function trySync7(try_) {
19403
- return Effect34.try({
19320
+ return Effect33.try({
19404
19321
  try: try_,
19405
19322
  catch: toError11
19406
19323
  });
@@ -19408,9 +19325,9 @@ function trySync7(try_) {
19408
19325
  var SEEDED_ACCOUNT_HANDLE = "steipete";
19409
19326
  var SEEDED_ACCOUNT_EXTERNAL_USER_ID = "25401953";
19410
19327
  function hydrateAccountFromBirdEffect() {
19411
- return Effect34.gen(function* () {
19412
- const account = yield* tryPromise(() => getAuthenticatedBirdAccount()).pipe(
19413
- Effect34.catchAll(() => Effect34.succeed(null))
19328
+ return Effect33.gen(function* () {
19329
+ const account = yield* getAuthenticatedBirdAccountEffect().pipe(
19330
+ Effect33.catchAll(() => Effect33.succeed(null))
19414
19331
  );
19415
19332
  if (!account?.username) return false;
19416
19333
  const handle = account.username.replace(/^@/, "");
@@ -19468,8 +19385,8 @@ function hydrateAccountFromBirdEffect() {
19468
19385
  });
19469
19386
  }
19470
19387
  function hydrateProfilesFromXEffect() {
19471
- return Effect34.gen(function* () {
19472
- const transport = yield* tryPromise(() => getTransportStatus());
19388
+ return Effect33.gen(function* () {
19389
+ const transport = yield* getTransportStatusEffect();
19473
19390
  if (transport.availableTransport !== "xurl") {
19474
19391
  const hydratedAccount2 = yield* hydrateAccountFromBirdEffect();
19475
19392
  return {
@@ -19531,7 +19448,7 @@ function hydrateProfilesFromXEffect() {
19531
19448
  let hydratedProfiles = 0;
19532
19449
  for (let index = 0; index < candidateIds.length; index += 100) {
19533
19450
  const batch = candidateIds.slice(index, index + 100);
19534
- const users = yield* tryPromise(() => lookupUsersByIds(batch));
19451
+ const users = yield* lookupUsersByIdsEffect(batch);
19535
19452
  yield* trySync7(() => {
19536
19453
  db.transaction(() => {
19537
19454
  for (const user of users) {
@@ -19548,8 +19465,8 @@ function hydrateProfilesFromXEffect() {
19548
19465
  });
19549
19466
  }
19550
19467
  let hydratedAccount = false;
19551
- const me = yield* tryPromise(() => lookupAuthenticatedUser()).pipe(
19552
- Effect34.catchAll(() => Effect34.succeed(null))
19468
+ const me = yield* lookupAuthenticatedUserEffect().pipe(
19469
+ Effect33.catchAll(() => Effect33.succeed(null))
19553
19470
  );
19554
19471
  if (me) {
19555
19472
  const metrics = asRecord3(me.public_metrics);
@@ -19769,7 +19686,7 @@ function registerCoreCommands({
19769
19686
  }
19770
19687
 
19771
19688
  // src/lib/dms-live.ts
19772
- import { Effect as Effect35 } from "effect";
19689
+ import { Effect as Effect34 } from "effect";
19773
19690
  var DEFAULT_DMS_CACHE_TTL_MS = 2 * 6e4;
19774
19691
  var PREVIEW_MESSAGE_ID_PREFIX = "preview:";
19775
19692
  var XURL_DMS_MAX_RESULTS = 100;
@@ -20246,7 +20163,7 @@ function fetchDirectMessagesViaXurlEffect({
20246
20163
  allPages,
20247
20164
  pageDelayMs
20248
20165
  }) {
20249
- return Effect35.gen(function* () {
20166
+ return Effect34.gen(function* () {
20250
20167
  const pageLimit = allPages ? Number.POSITIVE_INFINITY : Math.max(1, (maxPages ?? 0) + 1);
20251
20168
  const result = yield* runSyncPlanEffect({
20252
20169
  fetchPage: ({ cursor }) => liveTransportGateway.xurl.listDirectMessages({
@@ -20272,7 +20189,7 @@ function syncDirectMessagesViaCachedBirdEffect({
20272
20189
  refresh = false,
20273
20190
  cacheTtlMs
20274
20191
  } = {}) {
20275
- return Effect35.gen(function* () {
20192
+ return Effect34.gen(function* () {
20276
20193
  const parsedMode = parseSyncMode2(mode);
20277
20194
  if (parsedMode === "xurl") {
20278
20195
  assertXurlLimit2(limit);
@@ -20301,14 +20218,14 @@ function syncDirectMessagesViaCachedBirdEffect({
20301
20218
  } else {
20302
20219
  const tryXurl = (parsedMode === "xurl" || parsedMode === "auto") && inbox !== "requests";
20303
20220
  if (tryXurl) {
20304
- const xurlPayload = yield* Effect35.gen(function* () {
20221
+ const xurlPayload = yield* Effect34.gen(function* () {
20305
20222
  const authenticated = getAuthenticatedXurlAccount(
20306
20223
  yield* liveTransportGateway.xurl.lookupAuthenticatedOAuth2User(
20307
20224
  resolvedAccount.username
20308
20225
  )
20309
20226
  );
20310
20227
  if (!authenticated.username && !authenticated.id) {
20311
- return yield* Effect35.fail(
20228
+ return yield* Effect34.fail(
20312
20229
  new Error("xurl authenticated user unavailable")
20313
20230
  );
20314
20231
  }
@@ -20327,7 +20244,7 @@ function syncDirectMessagesViaCachedBirdEffect({
20327
20244
  );
20328
20245
  }
20329
20246
  if (!accountExternalUserId) {
20330
- return yield* Effect35.fail(
20247
+ return yield* Effect34.fail(
20331
20248
  new Error(
20332
20249
  "xurl authenticated user id unavailable; refusing to sync DMs"
20333
20250
  )
@@ -20341,9 +20258,9 @@ function syncDirectMessagesViaCachedBirdEffect({
20341
20258
  ...typeof pageDelayMs === "number" ? { pageDelayMs } : {}
20342
20259
  });
20343
20260
  }).pipe(
20344
- Effect35.catchAll((error) => {
20345
- if (parsedMode === "xurl") return Effect35.fail(error);
20346
- return Effect35.succeed(void 0);
20261
+ Effect34.catchAll((error) => {
20262
+ if (parsedMode === "xurl") return Effect34.fail(error);
20263
+ return Effect34.succeed(void 0);
20347
20264
  })
20348
20265
  );
20349
20266
  if (xurlPayload) {
@@ -20414,15 +20331,15 @@ function syncDirectMessagesViaCachedBird(options = {}) {
20414
20331
  }
20415
20332
 
20416
20333
  // src/lib/profile-resolver.ts
20417
- import { Effect as Effect37 } from "effect";
20334
+ import { Effect as Effect36 } from "effect";
20418
20335
 
20419
20336
  // src/lib/profile-affiliation-hydration.ts
20420
- import { Effect as Effect36 } from "effect";
20337
+ import { Effect as Effect35 } from "effect";
20421
20338
  function toError12(error) {
20422
20339
  return error instanceof Error ? error : new Error(String(error));
20423
20340
  }
20424
20341
  function trySync8(try_) {
20425
- return Effect36.try({
20342
+ return Effect35.try({
20426
20343
  try: try_,
20427
20344
  catch: toError12
20428
20345
  });
@@ -20499,7 +20416,7 @@ function findLocalOrganizationProfileId(db, handle) {
20499
20416
  return row?.id ?? null;
20500
20417
  }
20501
20418
  function hydrateProfileAffiliationOrganizationsEffect(db, subjectProfileId) {
20502
- return Effect36.gen(function* () {
20419
+ return Effect35.gen(function* () {
20503
20420
  const rows = yield* trySync8(
20504
20421
  () => db.prepare(
20505
20422
  `
@@ -20526,7 +20443,7 @@ function hydrateProfileAffiliationOrganizationsEffect(db, subjectProfileId) {
20526
20443
  result.skipped += 1;
20527
20444
  continue;
20528
20445
  }
20529
- const hydrated = yield* Effect36.gen(function* () {
20446
+ const hydrated = yield* Effect35.gen(function* () {
20530
20447
  const localOrganizationProfileId = yield* trySync8(
20531
20448
  () => findLocalOrganizationProfileId(db, handle)
20532
20449
  );
@@ -20557,12 +20474,12 @@ function hydrateProfileAffiliationOrganizationsEffect(db, subjectProfileId) {
20557
20474
  result.hydrated += 1;
20558
20475
  return true;
20559
20476
  }).pipe(
20560
- Effect36.catchAll((error) => {
20477
+ Effect35.catchAll((error) => {
20561
20478
  result.errors.push({
20562
20479
  handle,
20563
20480
  error: error instanceof Error ? error.message : String(error)
20564
20481
  });
20565
- return Effect36.succeed(false);
20482
+ return Effect35.succeed(false);
20566
20483
  })
20567
20484
  );
20568
20485
  if (!hydrated) {
@@ -20587,7 +20504,7 @@ function toError13(error) {
20587
20504
  return error instanceof Error ? error : new Error(String(error));
20588
20505
  }
20589
20506
  function trySync9(try_) {
20590
- return Effect37.try({
20507
+ return Effect36.try({
20591
20508
  try: try_,
20592
20509
  catch: toError13
20593
20510
  });
@@ -20629,16 +20546,15 @@ function updateConversationTitles(profile, db = getNativeDb()) {
20629
20546
  ).run(profile.displayName || profile.handle, profile.id);
20630
20547
  }
20631
20548
  function lookupViaXurlEffect(externalUserId) {
20632
- return Effect37.gen(function* () {
20633
- const [user] = yield* tryPromise(() => lookupUsersByIds([externalUserId]));
20634
- return user ?? null;
20635
- });
20549
+ return lookupUsersByIdsEffect([externalUserId]).pipe(
20550
+ Effect36.map(([user]) => user ?? null)
20551
+ );
20636
20552
  }
20637
20553
  function fetchProfileUserEffect(externalUserId, xurlFallback) {
20638
- return Effect37.gen(function* () {
20554
+ return Effect36.gen(function* () {
20639
20555
  const birdResult = yield* lookupProfileViaBirdEffect(externalUserId).pipe(
20640
- Effect37.map((user) => ({ ok: true, user })),
20641
- Effect37.catchAll((error) => Effect37.succeed({ ok: false, error }))
20556
+ Effect36.map((user) => ({ ok: true, user })),
20557
+ Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
20642
20558
  );
20643
20559
  if (birdResult.ok) {
20644
20560
  const birdUser = birdResult.user;
@@ -20656,8 +20572,8 @@ function fetchProfileUserEffect(externalUserId, xurlFallback) {
20656
20572
  return { status: "miss", source: "bird" };
20657
20573
  }
20658
20574
  const xurlResult = yield* lookupViaXurlEffect(externalUserId).pipe(
20659
- Effect37.map((user) => ({ ok: true, user })),
20660
- Effect37.catchAll((error) => Effect37.succeed({ ok: false, error }))
20575
+ Effect36.map((user) => ({ ok: true, user })),
20576
+ Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
20661
20577
  );
20662
20578
  if (xurlResult.ok) {
20663
20579
  const xurlUser = xurlResult.user;
@@ -20671,13 +20587,13 @@ function fetchProfileUserEffect(externalUserId, xurlFallback) {
20671
20587
  });
20672
20588
  }
20673
20589
  function fetchProfileUsersEffect(externalUserIds, xurlFallback) {
20674
- return Effect37.gen(function* () {
20590
+ return Effect36.gen(function* () {
20675
20591
  const uniqueIds = Array.from(new Set(externalUserIds));
20676
20592
  const results = /* @__PURE__ */ new Map();
20677
20593
  let unresolved = uniqueIds;
20678
20594
  const birdResult = yield* lookupProfilesViaBirdEffect(uniqueIds).pipe(
20679
- Effect37.map((items) => ({ ok: true, items })),
20680
- Effect37.catchAll((error) => Effect37.succeed({ ok: false, error }))
20595
+ Effect36.map((items) => ({ ok: true, items })),
20596
+ Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
20681
20597
  );
20682
20598
  if (birdResult.ok) {
20683
20599
  const birdResults = birdResult.items;
@@ -20717,11 +20633,9 @@ function fetchProfileUsersEffect(externalUserIds, xurlFallback) {
20717
20633
  }
20718
20634
  return results;
20719
20635
  }
20720
- const xurlResult = yield* tryPromise(
20721
- () => lookupUsersByIds(unresolved)
20722
- ).pipe(
20723
- Effect37.map((users) => ({ ok: true, users })),
20724
- Effect37.catchAll((error) => Effect37.succeed({ ok: false, error }))
20636
+ const xurlResult = yield* lookupUsersByIdsEffect(unresolved).pipe(
20637
+ Effect36.map((users) => ({ ok: true, users })),
20638
+ Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
20725
20639
  );
20726
20640
  if (xurlResult.ok) {
20727
20641
  const xurlUsers = xurlResult.users;
@@ -20748,7 +20662,7 @@ function fetchProfileUsersEffect(externalUserIds, xurlFallback) {
20748
20662
  });
20749
20663
  }
20750
20664
  function resolveProfilesForIdsEffect(profileIds, options = {}) {
20751
- return Effect37.gen(function* () {
20665
+ return Effect36.gen(function* () {
20752
20666
  const db = yield* trySync9(() => getNativeDb());
20753
20667
  const maxAgeMs = options.maxAgeMs ?? PROFILE_CACHE_TTL_MS;
20754
20668
  const negativeMaxAgeMs = options.negativeMaxAgeMs ?? PROFILE_NEGATIVE_CACHE_TTL_MS;
@@ -20870,7 +20784,7 @@ function resolveProfilesForIds(profileIds, options = {}) {
20870
20784
  }
20871
20785
 
20872
20786
  // src/lib/url-expansion.ts
20873
- import { Effect as Effect39 } from "effect";
20787
+ import { Effect as Effect38 } from "effect";
20874
20788
 
20875
20789
  // src/lib/link-preview-metadata.ts
20876
20790
  import { lookup } from "node:dns/promises";
@@ -20878,7 +20792,8 @@ import http from "node:http";
20878
20792
  import https from "node:https";
20879
20793
  import net from "node:net";
20880
20794
  import { Readable } from "node:stream";
20881
- import { Effect as Effect38 } from "effect";
20795
+ import { createBrotliDecompress, createGunzip, createInflate } from "node:zlib";
20796
+ import { Effect as Effect37 } from "effect";
20882
20797
 
20883
20798
  // src/lib/url-expansion-store.ts
20884
20799
  function getTweetTarget(url) {
@@ -20962,42 +20877,89 @@ function upsertUrlExpansion(db, item) {
20962
20877
 
20963
20878
  // src/lib/link-preview-metadata.ts
20964
20879
  var FETCH_TIMEOUT_MS = 12e3;
20880
+ var MAX_HTML_CHARS = 2e6;
20965
20881
  var MAX_REDIRECTS = 4;
20966
20882
  var NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([204, 205, 304]);
20883
+ function cleanText(value) {
20884
+ if (!value) return null;
20885
+ const cleaned = decodeHtmlEntities(value).replace(/\s+/g, " ").trim();
20886
+ return cleaned.length > 0 ? cleaned : null;
20887
+ }
20967
20888
  function decodeCodePoint(value, fallback) {
20968
20889
  try {
20969
20890
  return String.fromCodePoint(value);
20970
20891
  } catch {
20971
- return fallback;
20892
+ return fallback;
20893
+ }
20894
+ }
20895
+ function decodeHtmlEntities(value) {
20896
+ return value.replace(
20897
+ /&#(\d+);/g,
20898
+ (entity, code) => decodeCodePoint(Number(code), entity)
20899
+ ).replace(
20900
+ /&#x([a-f0-9]+);/gi,
20901
+ (entity, code) => decodeCodePoint(Number.parseInt(code, 16), entity)
20902
+ ).replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&apos;/g, "'");
20903
+ }
20904
+ function parseAttributes(tag) {
20905
+ const attributes = /* @__PURE__ */ new Map();
20906
+ for (const match of tag.matchAll(
20907
+ /([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/g
20908
+ )) {
20909
+ const key = match[1]?.toLowerCase();
20910
+ const value = match[3] ?? match[4] ?? match[5] ?? "";
20911
+ if (key) {
20912
+ attributes.set(key, value);
20913
+ }
20914
+ }
20915
+ return attributes;
20916
+ }
20917
+ function metaContents(html) {
20918
+ const values = /* @__PURE__ */ new Map();
20919
+ for (const match of html.matchAll(/<meta\b[^>]*>/gi)) {
20920
+ const attributes = parseAttributes(match[0]);
20921
+ const key = (attributes.get("property") ?? attributes.get("name") ?? "").toLowerCase();
20922
+ const content = cleanText(
20923
+ attributes.get("content") ?? attributes.get("value") ?? ""
20924
+ );
20925
+ if (key && content && !values.has(key)) {
20926
+ values.set(key, content);
20927
+ }
20928
+ }
20929
+ return values;
20930
+ }
20931
+ function pick(values, keys) {
20932
+ for (const key of keys) {
20933
+ const value = cleanText(values.get(key));
20934
+ if (value) return value;
20935
+ }
20936
+ return null;
20937
+ }
20938
+ function titleFromHtml(html) {
20939
+ const match = html.match(/<title\b[^>]*>([\s\S]*?)<\/title>/i);
20940
+ return cleanText(match?.[1]);
20941
+ }
20942
+ function absoluteUrl(value, baseUrl) {
20943
+ if (!value) return null;
20944
+ try {
20945
+ return new URL(value, baseUrl).toString();
20946
+ } catch {
20947
+ return null;
20948
+ }
20949
+ }
20950
+ function hostLabel(url) {
20951
+ try {
20952
+ return new URL(url).hostname.replace(/^www\./, "");
20953
+ } catch {
20954
+ return url;
20972
20955
  }
20973
20956
  }
20974
- function decodeHtmlEntities(value) {
20975
- return value.replace(
20976
- /&#(\d+);/g,
20977
- (entity, code) => decodeCodePoint(Number(code), entity)
20978
- ).replace(
20979
- /&#x([a-f0-9]+);/gi,
20980
- (entity, code) => decodeCodePoint(Number.parseInt(code, 16), entity)
20981
- ).replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&apos;/g, "'");
20982
- }
20983
20957
  function isInjectedFetchAllowed() {
20984
20958
  return process.env.NODE_ENV === "test" || process.env.VITEST === "true";
20985
20959
  }
20986
20960
  function stripAddressBrackets(value) {
20987
20961
  return value.replace(/^\[|\]$/g, "");
20988
20962
  }
20989
- function withTimeout(promise, ms) {
20990
- let timer = null;
20991
- const timeout = new Promise((_resolve, reject) => {
20992
- timer = setTimeout(
20993
- () => reject(new Error("Link preview request timed out")),
20994
- ms
20995
- );
20996
- });
20997
- return Promise.race([promise, timeout]).finally(() => {
20998
- if (timer) clearTimeout(timer);
20999
- });
21000
- }
21001
20963
  async function resolvePublicAddresses(hostname) {
21002
20964
  const normalized = stripAddressBrackets(hostname);
21003
20965
  if (net.isIP(normalized)) return [normalized];
@@ -21012,22 +20974,34 @@ function validateResolvedAddresses(addresses) {
21012
20974
  throw new Error("Link preview URL resolves to a private address");
21013
20975
  }
21014
20976
  }
21015
- async function resolveSafeAddresses(hostname, resolveHost, timeoutMs) {
21016
- const normalizedHostname = stripAddressBrackets(hostname);
21017
- const addresses = await withTimeout(
21018
- resolveHost(normalizedHostname),
21019
- timeoutMs
21020
- );
21021
- validateResolvedAddresses(addresses);
21022
- return addresses.map((address) => {
21023
- const normalized = stripAddressBrackets(address);
21024
- const family = net.isIP(normalized);
21025
- if (family !== 4 && family !== 6) {
21026
- throw new Error("Link preview host resolved to an invalid address");
21027
- }
21028
- return { address: normalized, family };
21029
- });
21030
- }
20977
+ var resolveSafeAddressesEffect = Effect37.fn(
20978
+ "linkPreview.resolveSafeAddresses"
20979
+ )(
20980
+ (hostname, resolveHost, timeoutMs) => tryPromise(() => resolveHost(stripAddressBrackets(hostname))).pipe(
20981
+ Effect37.timeoutFail({
20982
+ duration: timeoutMs,
20983
+ onTimeout: () => new Error("Link preview request timed out")
20984
+ }),
20985
+ Effect37.flatMap(
20986
+ (addresses) => Effect37.try({
20987
+ try: () => {
20988
+ validateResolvedAddresses(addresses);
20989
+ return addresses.map((address) => {
20990
+ const normalized = stripAddressBrackets(address);
20991
+ const family = net.isIP(normalized);
20992
+ if (family !== 4 && family !== 6) {
20993
+ throw new Error(
20994
+ "Link preview host resolved to an invalid address"
20995
+ );
20996
+ }
20997
+ return { address: normalized, family };
20998
+ });
20999
+ },
21000
+ catch: (error) => error
21001
+ })
21002
+ )
21003
+ )
21004
+ );
21031
21005
  function headersFromIncoming(headers) {
21032
21006
  const result = new Headers();
21033
21007
  for (const [key, value] of Object.entries(headers)) {
@@ -21039,9 +21013,30 @@ function headersFromIncoming(headers) {
21039
21013
  }
21040
21014
  return result;
21041
21015
  }
21016
+ function decodedResponseBody(response) {
21017
+ const body = response.body;
21018
+ if (!body) return null;
21019
+ const encoding = response.headers.get("content-encoding")?.split(",").at(-1)?.trim().toLowerCase();
21020
+ if (!encoding || encoding === "identity") return body;
21021
+ const nodeBody = Readable.fromWeb(
21022
+ body
21023
+ );
21024
+ if (encoding === "gzip" || encoding === "x-gzip") {
21025
+ return Readable.toWeb(nodeBody.pipe(createGunzip()));
21026
+ }
21027
+ if (encoding === "br") {
21028
+ return Readable.toWeb(
21029
+ nodeBody.pipe(createBrotliDecompress())
21030
+ );
21031
+ }
21032
+ if (encoding === "deflate") {
21033
+ return Readable.toWeb(nodeBody.pipe(createInflate()));
21034
+ }
21035
+ return body;
21036
+ }
21042
21037
  function cancelResponseBodyEffect(response) {
21043
21038
  return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(
21044
- Effect38.catchAll(() => Effect38.void)
21039
+ Effect37.catchAll(() => Effect37.void)
21045
21040
  );
21046
21041
  }
21047
21042
  function respondWithResolvedAddress(selected, lookupOptions, callback) {
@@ -21135,61 +21130,67 @@ function nodeSafeFetch(url, options) {
21135
21130
  Promise.reject(new Error("Link preview host did not resolve"))
21136
21131
  );
21137
21132
  }
21138
- function safePreviewFetchEffect(url, options) {
21139
- const resolveHost = options.resolveHost ?? (options.fetchImpl ? null : (hostname) => resolvePublicAddresses(hostname));
21140
- const timeoutMs = options.timeoutMs ?? FETCH_TIMEOUT_MS;
21141
- const method = options.method ?? "GET";
21142
- const deadline = Date.now() + timeoutMs;
21143
- const remainingTimeoutMs = () => Math.max(1, deadline - Date.now());
21144
- const headers = {
21145
- "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36 birdclaw/0.4",
21146
- accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
21147
- "accept-language": "en-US,en;q=0.9"
21148
- };
21149
- return Effect38.gen(function* () {
21133
+ var safePreviewFetchEffect = Effect37.fn("linkPreview.safePreviewFetch")(
21134
+ function* (url, options) {
21135
+ const resolveHost = options.resolveHost ?? (options.fetchImpl ? null : (hostname) => resolvePublicAddresses(hostname));
21136
+ const timeoutMs = options.timeoutMs ?? FETCH_TIMEOUT_MS;
21137
+ const method = options.method ?? "GET";
21138
+ const deadline = Date.now() + timeoutMs;
21139
+ const remainingTimeoutMs = () => Math.max(1, deadline - Date.now());
21140
+ const headers = {
21141
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36 birdclaw/0.4",
21142
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
21143
+ "accept-language": "en-US,en;q=0.9"
21144
+ };
21150
21145
  if (options.fetchImpl && !isInjectedFetchAllowed()) {
21151
- return yield* Effect38.fail(
21146
+ return yield* Effect37.fail(
21152
21147
  new Error("Custom link preview fetch is only available in tests")
21153
21148
  );
21154
21149
  }
21155
21150
  let currentUrl = url;
21156
21151
  for (let redirect = 0; redirect <= MAX_REDIRECTS; redirect += 1) {
21157
21152
  if (Date.now() >= deadline) {
21158
- return yield* Effect38.fail(new Error("Link preview request timed out"));
21153
+ return yield* Effect37.fail(new Error("Link preview request timed out"));
21159
21154
  }
21160
- let remainingMs = remainingTimeoutMs();
21161
- const parsed = yield* Effect38.try({
21155
+ const parsed = yield* Effect37.try({
21162
21156
  try: () => assertSafePreviewUrl(currentUrl),
21163
21157
  catch: (error) => error
21164
21158
  });
21165
21159
  if (options.fetchImpl && resolveHost) {
21166
- yield* tryPromise(
21167
- () => resolveSafeAddresses(parsed.hostname, resolveHost, remainingMs)
21160
+ yield* resolveSafeAddressesEffect(
21161
+ parsed.hostname,
21162
+ resolveHost,
21163
+ remainingTimeoutMs()
21168
21164
  );
21169
21165
  }
21170
21166
  if (Date.now() >= deadline) {
21171
- return yield* Effect38.fail(new Error("Link preview request timed out"));
21167
+ return yield* Effect37.fail(new Error("Link preview request timed out"));
21172
21168
  }
21173
- remainingMs = remainingTimeoutMs();
21174
21169
  const response = options.fetchImpl ? yield* tryPromise(
21175
21170
  () => options.fetchImpl?.(parsed.toString(), {
21176
21171
  headers,
21177
21172
  method,
21178
21173
  redirect: "manual",
21179
- signal: AbortSignal.timeout(remainingMs)
21174
+ signal: AbortSignal.timeout(remainingTimeoutMs())
21180
21175
  })
21181
- ) : yield* tryPromise(
21182
- () => resolveSafeAddresses(
21183
- parsed.hostname,
21184
- options.resolveHost ?? resolvePublicAddresses,
21185
- remainingMs
21186
- ).then(
21187
- (addresses) => Date.now() >= deadline ? Promise.reject(new Error("Link preview request timed out")) : nodeSafeFetch(parsed, {
21188
- addresses,
21189
- headers,
21190
- method,
21191
- timeoutMs: remainingTimeoutMs()
21192
- })
21176
+ ) : yield* resolveSafeAddressesEffect(
21177
+ parsed.hostname,
21178
+ options.resolveHost ?? resolvePublicAddresses,
21179
+ remainingTimeoutMs()
21180
+ ).pipe(
21181
+ Effect37.filterOrFail(
21182
+ () => Date.now() < deadline,
21183
+ () => new Error("Link preview request timed out")
21184
+ ),
21185
+ Effect37.flatMap(
21186
+ (addresses) => tryPromise(
21187
+ () => nodeSafeFetch(parsed, {
21188
+ addresses,
21189
+ headers,
21190
+ method,
21191
+ timeoutMs: remainingTimeoutMs()
21192
+ })
21193
+ )
21193
21194
  )
21194
21195
  );
21195
21196
  if (response.status < 300 || response.status >= 400) return response;
@@ -21197,20 +21198,58 @@ function safePreviewFetchEffect(url, options) {
21197
21198
  if (!location) return response;
21198
21199
  yield* cancelResponseBodyEffect(response);
21199
21200
  if (redirect === MAX_REDIRECTS) {
21200
- return yield* Effect38.fail(
21201
+ return yield* Effect37.fail(
21201
21202
  new Error("Link preview redirected too many times")
21202
21203
  );
21203
21204
  }
21204
- const nextUrl = yield* Effect38.try({
21205
+ const nextUrl = yield* Effect37.try({
21205
21206
  try: () => new URL(location, parsed).toString(),
21206
21207
  catch: (error) => error
21207
21208
  });
21208
21209
  currentUrl = nextUrl;
21209
21210
  }
21210
- return yield* Effect38.fail(
21211
+ return yield* Effect37.fail(
21211
21212
  new Error("Link preview redirected too many times")
21212
21213
  );
21213
- });
21214
+ }
21215
+ );
21216
+ function readResponseTextEffect(response) {
21217
+ const contentLength2 = Number(response.headers.get("content-length") ?? 0);
21218
+ if (Number.isFinite(contentLength2) && contentLength2 > MAX_HTML_CHARS) {
21219
+ return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(
21220
+ Effect37.flatMap(
21221
+ () => Effect37.fail(new Error("Link preview response is too large"))
21222
+ )
21223
+ );
21224
+ }
21225
+ const reader = decodedResponseBody(response)?.getReader();
21226
+ if (!reader) return tryPromise(() => response.text());
21227
+ const decoder = new TextDecoder();
21228
+ let total = 0;
21229
+ let text = "";
21230
+ return Effect37.gen(function* () {
21231
+ for (; ; ) {
21232
+ const { done, value } = yield* tryPromise(() => reader.read());
21233
+ if (done) break;
21234
+ total += value.byteLength;
21235
+ if (total > MAX_HTML_CHARS) {
21236
+ yield* tryPromise(
21237
+ () => reader.cancel(new Error("Link preview response is too large"))
21238
+ );
21239
+ return yield* Effect37.fail(
21240
+ new Error("Link preview response is too large")
21241
+ );
21242
+ }
21243
+ text += decoder.decode(value, { stream: true });
21244
+ }
21245
+ return text + decoder.decode();
21246
+ }).pipe(
21247
+ Effect37.ensuring(
21248
+ Effect37.sync(() => {
21249
+ reader.releaseLock();
21250
+ })
21251
+ )
21252
+ );
21214
21253
  }
21215
21254
  function youtubeThumbnail(url) {
21216
21255
  try {
@@ -21234,6 +21273,157 @@ function youtubeThumbnail(url) {
21234
21273
  return null;
21235
21274
  }
21236
21275
  }
21276
+ function extractLinkPreviewMetadata(html, url) {
21277
+ const meta = metaContents(html);
21278
+ const title = pick(meta, ["og:title", "twitter:title"]) ?? titleFromHtml(html) ?? hostLabel(url);
21279
+ const description = pick(meta, [
21280
+ "og:description",
21281
+ "twitter:description",
21282
+ "description"
21283
+ ]);
21284
+ const siteName = pick(meta, ["og:site_name", "application-name"]) ?? hostLabel(url);
21285
+ const image = pick(meta, [
21286
+ "og:image:secure_url",
21287
+ "og:image:url",
21288
+ "og:image",
21289
+ "twitter:image:src",
21290
+ "twitter:image"
21291
+ ]) ?? youtubeThumbnail(url);
21292
+ return {
21293
+ url,
21294
+ title,
21295
+ description,
21296
+ imageUrl: absoluteUrl(image, url),
21297
+ siteName
21298
+ };
21299
+ }
21300
+ var fetchLinkPreviewMetadataEffect = Effect37.fn(
21301
+ "linkPreview.fetchMetadata"
21302
+ )(
21303
+ function* (url, _options = {}) {
21304
+ const response = yield* safePreviewFetchEffect(url, _options);
21305
+ const finalUrl = response.url || url;
21306
+ const contentType = response.headers.get("content-type") ?? "";
21307
+ if (!response.ok) {
21308
+ yield* cancelResponseBodyEffect(response);
21309
+ return yield* Effect37.fail(new Error(`HTTP ${response.status}`));
21310
+ }
21311
+ if (contentType.toLowerCase().startsWith("image/")) {
21312
+ yield* cancelResponseBodyEffect(response);
21313
+ return {
21314
+ url: finalUrl,
21315
+ title: hostLabel(finalUrl),
21316
+ description: null,
21317
+ imageUrl: finalUrl,
21318
+ siteName: hostLabel(finalUrl)
21319
+ };
21320
+ }
21321
+ const content = yield* readResponseTextEffect(response);
21322
+ return yield* Effect37.try({
21323
+ try: () => extractLinkPreviewMetadata(content.slice(0, MAX_HTML_CHARS), finalUrl),
21324
+ catch: (error) => error
21325
+ });
21326
+ },
21327
+ // Any failure degrades to a host-label preview carrying the error message.
21328
+ (effect, url) => Effect37.catchAll(
21329
+ effect,
21330
+ (error) => Effect37.succeed({
21331
+ url,
21332
+ title: hostLabel(url),
21333
+ description: null,
21334
+ imageUrl: youtubeThumbnail(url),
21335
+ siteName: hostLabel(url),
21336
+ error: error instanceof Error ? error.message : String(error)
21337
+ })
21338
+ )
21339
+ );
21340
+ function readCachedPreview(db, url, shortUrl) {
21341
+ return db.prepare(
21342
+ `
21343
+ select short_url, expanded_url, final_url, status, title, description,
21344
+ image_url, site_name, error, source, updated_at
21345
+ from url_expansions
21346
+ where short_url in (?, ?)
21347
+ or expanded_url in (?, ?)
21348
+ or final_url in (?, ?)
21349
+ order by
21350
+ case
21351
+ when short_url = ? then 0
21352
+ when final_url = ? then 1
21353
+ else 2
21354
+ end
21355
+ limit 1
21356
+ `
21357
+ ).get(
21358
+ shortUrl ?? url,
21359
+ url,
21360
+ shortUrl ?? url,
21361
+ url,
21362
+ shortUrl ?? url,
21363
+ url,
21364
+ shortUrl ?? url,
21365
+ url
21366
+ );
21367
+ }
21368
+ function hasUsefulPreview(row) {
21369
+ if (row.status !== "hit") return false;
21370
+ try {
21371
+ assertSafePreviewUrl(row.final_url || row.expanded_url || row.short_url);
21372
+ if (row.image_url) assertSafePreviewUrl(row.image_url);
21373
+ } catch {
21374
+ return false;
21375
+ }
21376
+ return Boolean(
21377
+ row.title || row.description || row.image_url || row.site_name
21378
+ );
21379
+ }
21380
+ function rowToPreview(row) {
21381
+ const url = assertSafePreviewUrl(
21382
+ row.final_url || row.expanded_url || row.short_url
21383
+ ).toString();
21384
+ return {
21385
+ url,
21386
+ title: row.title ?? null,
21387
+ description: row.description ?? null,
21388
+ imageUrl: row.image_url ? assertSafePreviewUrl(row.image_url).toString() : null,
21389
+ siteName: row.site_name ?? null,
21390
+ ...row.error ? { error: row.error } : {}
21391
+ };
21392
+ }
21393
+ function persistPreview(db, url, shortUrl, cached, preview) {
21394
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
21395
+ upsertUrlExpansion(
21396
+ db,
21397
+ normalizeUrlExpansionForIndex({
21398
+ url: cached?.short_url ?? shortUrl ?? url,
21399
+ expandedUrl: cached?.expanded_url ?? preview.url,
21400
+ finalUrl: preview.url,
21401
+ status: preview.error ? "error" : "hit",
21402
+ title: preview.title,
21403
+ description: preview.description,
21404
+ imageUrl: preview.imageUrl,
21405
+ siteName: preview.siteName,
21406
+ ...preview.error ? { error: preview.error } : {},
21407
+ source: "metadata",
21408
+ updatedAt: now2
21409
+ })
21410
+ );
21411
+ }
21412
+ var getOrFetchLinkPreviewEffect = Effect37.fn("linkPreview.getOrFetch")(
21413
+ function* (url, options = {}) {
21414
+ const db = getNativeDb({ seedDemoData: false });
21415
+ const cached = readCachedPreview(db, url, options.shortUrl);
21416
+ if (cached && hasUsefulPreview(cached) && !options.refresh) {
21417
+ return rowToPreview(cached);
21418
+ }
21419
+ const preview = yield* fetchLinkPreviewMetadataEffect(
21420
+ cached?.final_url || cached?.expanded_url || url,
21421
+ options
21422
+ );
21423
+ persistPreview(db, url, options.shortUrl, cached, preview);
21424
+ return preview;
21425
+ }
21426
+ );
21237
21427
  var __test__2 = {
21238
21428
  ...__test__,
21239
21429
  decodeHtmlEntities,
@@ -21260,14 +21450,14 @@ function toError14(error) {
21260
21450
  return error instanceof Error ? error : new Error(String(error));
21261
21451
  }
21262
21452
  function trySync10(try_) {
21263
- return Effect39.try({
21453
+ return Effect38.try({
21264
21454
  try: try_,
21265
21455
  catch: toError14
21266
21456
  });
21267
21457
  }
21268
21458
  function cancelBodyEffect(response) {
21269
21459
  return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(
21270
- Effect39.catchAll(() => Effect39.void)
21460
+ Effect38.catchAll(() => Effect38.void)
21271
21461
  );
21272
21462
  }
21273
21463
  function extractUrls(text) {
@@ -21303,7 +21493,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21303
21493
  headers: { "user-agent": "birdclaw/0.3 url-expander" },
21304
21494
  signal: AbortSignal.timeout(timeoutMs)
21305
21495
  };
21306
- return Effect39.gen(function* () {
21496
+ return Effect38.gen(function* () {
21307
21497
  if (resolveHost) {
21308
21498
  const headResponse = yield* safePreviewFetchEffect(url, {
21309
21499
  ...usesInjectedFetch ? { fetchImpl } : {},
@@ -21314,7 +21504,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21314
21504
  yield* cancelBodyEffect(headResponse);
21315
21505
  const headFinalUrl = headResponse.url || url;
21316
21506
  if (headFinalUrl !== url && headResponse.status < 400) {
21317
- yield* Effect39.try({
21507
+ yield* Effect38.try({
21318
21508
  try: () => assertSafePreviewUrl(headFinalUrl),
21319
21509
  catch: (error) => error
21320
21510
  });
@@ -21333,7 +21523,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21333
21523
  yield* cancelBodyEffect(response2);
21334
21524
  const finalUrl2 = response2.url || headFinalUrl;
21335
21525
  if (finalUrl2 !== url) {
21336
- yield* Effect39.try({
21526
+ yield* Effect38.try({
21337
21527
  try: () => assertSafePreviewUrl(finalUrl2),
21338
21528
  catch: (error) => error
21339
21529
  });
@@ -21348,7 +21538,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21348
21538
  let currentUrl = url;
21349
21539
  let response = null;
21350
21540
  for (let redirect = 0; redirect <= MAX_REDIRECTS2; redirect += 1) {
21351
- yield* Effect39.try({
21541
+ yield* Effect38.try({
21352
21542
  try: () => assertSafePreviewUrl(currentUrl),
21353
21543
  catch: (error) => error
21354
21544
  });
@@ -21362,11 +21552,11 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21362
21552
  const location = response.headers.get("location");
21363
21553
  if (!location) break;
21364
21554
  if (redirect >= MAX_REDIRECTS2) {
21365
- return yield* Effect39.fail(
21555
+ return yield* Effect38.fail(
21366
21556
  new Error("URL expansion redirected too many times")
21367
21557
  );
21368
21558
  }
21369
- currentUrl = yield* Effect39.try({
21559
+ currentUrl = yield* Effect38.try({
21370
21560
  try: () => new URL(location, currentUrl).toString(),
21371
21561
  catch: (error) => error
21372
21562
  });
@@ -21383,11 +21573,11 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21383
21573
  const location = response.headers.get("location");
21384
21574
  if (!location) break;
21385
21575
  if (redirect >= MAX_REDIRECTS2) {
21386
- return yield* Effect39.fail(
21576
+ return yield* Effect38.fail(
21387
21577
  new Error("URL expansion redirected too many times")
21388
21578
  );
21389
21579
  }
21390
- currentUrl = yield* Effect39.try({
21580
+ currentUrl = yield* Effect38.try({
21391
21581
  try: () => new URL(location, currentUrl).toString(),
21392
21582
  catch: (error) => error
21393
21583
  });
@@ -21397,14 +21587,14 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21397
21587
  break;
21398
21588
  }
21399
21589
  if (!response) {
21400
- return yield* Effect39.fail(new Error("URL expansion failed"));
21590
+ return yield* Effect38.fail(new Error("URL expansion failed"));
21401
21591
  }
21402
21592
  if (response.status >= 300 && response.status < 400) {
21403
- return yield* Effect39.fail(new Error("URL expansion ended on a redirect"));
21593
+ return yield* Effect38.fail(new Error("URL expansion ended on a redirect"));
21404
21594
  }
21405
21595
  const finalUrl = response.url || currentUrl;
21406
21596
  if (finalUrl !== url) {
21407
- yield* Effect39.try({
21597
+ yield* Effect38.try({
21408
21598
  try: () => assertSafePreviewUrl(finalUrl),
21409
21599
  catch: (error) => error
21410
21600
  });
@@ -21416,8 +21606,8 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21416
21606
  ...response.ok ? {} : { error: `HTTP ${response.status}` }
21417
21607
  };
21418
21608
  }).pipe(
21419
- Effect39.catchAll(
21420
- (error) => Effect39.succeed({
21609
+ Effect38.catchAll(
21610
+ (error) => Effect38.succeed({
21421
21611
  expandedUrl: url,
21422
21612
  finalUrl: url,
21423
21613
  status: "error",
@@ -21427,7 +21617,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
21427
21617
  );
21428
21618
  }
21429
21619
  function expandUrlsEffect(urls, options = {}) {
21430
- return Effect39.gen(function* () {
21620
+ return Effect38.gen(function* () {
21431
21621
  const uniqueUrls = Array.from(new Set(urls));
21432
21622
  const usesInjectedFetch = options.fetchImpl !== void 0;
21433
21623
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
@@ -21654,7 +21844,7 @@ function registerDirectMessageCommands({
21654
21844
 
21655
21845
  // src/lib/follow-graph.ts
21656
21846
  import { randomUUID as randomUUID6 } from "node:crypto";
21657
- import { Effect as Effect40 } from "effect";
21847
+ import { Effect as Effect39 } from "effect";
21658
21848
  var DEFAULT_FOLLOW_CACHE_TTL_MS = 24 * 60 * 6e4;
21659
21849
  var DEFAULT_FOLLOW_PAGE_LIMIT = 1e3;
21660
21850
  var MIN_FOLLOW_PAGE_LIMIT = 1;
@@ -21796,7 +21986,7 @@ function fetchFollowGraphViaXurlEffect({
21796
21986
  maxPages,
21797
21987
  maxResources
21798
21988
  }) {
21799
- return Effect40.gen(function* () {
21989
+ return Effect39.gen(function* () {
21800
21990
  const result = yield* runSyncPlanEffect({
21801
21991
  fetchPage: ({ cursor }) => liveTransportGateway.xurl.listFollowUsers({
21802
21992
  direction,
@@ -21820,7 +22010,7 @@ function fetchFollowGraphViaBirdEffect({
21820
22010
  maxPages,
21821
22011
  maxResources
21822
22012
  }) {
21823
- return Effect40.gen(function* () {
22013
+ return Effect39.gen(function* () {
21824
22014
  const birdLimit = Math.min(limit, BIRD_FOLLOW_PAGE_LIMIT);
21825
22015
  const cappedMaxPages = maxResources === void 0 ? maxPages : Math.min(
21826
22016
  maxPages ?? Number.POSITIVE_INFINITY,
@@ -21849,7 +22039,7 @@ function fetchFollowGraphEffect({
21849
22039
  maxPages,
21850
22040
  maxResources
21851
22041
  }) {
21852
- return Effect40.gen(function* () {
22042
+ return Effect39.gen(function* () {
21853
22043
  if (mode === "bird") {
21854
22044
  return {
21855
22045
  source: "bird",
@@ -21882,8 +22072,8 @@ function fetchFollowGraphEffect({
21882
22072
  maxPages,
21883
22073
  maxResources
21884
22074
  }).pipe(
21885
- Effect40.map((payload) => ({ ok: true, payload })),
21886
- Effect40.catchAll((error) => Effect40.succeed({ ok: false, error }))
22075
+ Effect39.map((payload) => ({ ok: true, payload })),
22076
+ Effect39.catchAll((error) => Effect39.succeed({ ok: false, error }))
21887
22077
  );
21888
22078
  if (birdResult.ok) {
21889
22079
  return {
@@ -21899,8 +22089,8 @@ function fetchFollowGraphEffect({
21899
22089
  maxPages,
21900
22090
  maxResources
21901
22091
  }).pipe(
21902
- Effect40.map((payload) => ({ ok: true, payload })),
21903
- Effect40.catchAll((error) => Effect40.succeed({ ok: false, error }))
22092
+ Effect39.map((payload) => ({ ok: true, payload })),
22093
+ Effect39.catchAll((error) => Effect39.succeed({ ok: false, error }))
21904
22094
  );
21905
22095
  if (xurlResult.ok) {
21906
22096
  return {
@@ -21908,7 +22098,7 @@ function fetchFollowGraphEffect({
21908
22098
  payload: xurlResult.payload
21909
22099
  };
21910
22100
  }
21911
- return yield* Effect40.fail(
22101
+ return yield* Effect39.fail(
21912
22102
  new Error(
21913
22103
  `follow graph sync failed via bird and xurl: bird: ${errorMessage3(
21914
22104
  birdResult.error
@@ -22098,7 +22288,7 @@ function errorMessage3(error) {
22098
22288
  return error instanceof Error ? error.message : String(error);
22099
22289
  }
22100
22290
  function syncFollowGraphEffect(options) {
22101
- return Effect40.gen(function* () {
22291
+ return Effect39.gen(function* () {
22102
22292
  const db = yield* trySync(() => getNativeDb());
22103
22293
  const mode = yield* trySync(() => parseMode3(options.mode));
22104
22294
  const limit = yield* trySync(() => parseLimit(options.limit));
@@ -22545,10 +22735,10 @@ function registerGraphCommands({
22545
22735
  }
22546
22736
 
22547
22737
  // src/lib/inbox.ts
22548
- import { Effect as Effect42 } from "effect";
22738
+ import { Effect as Effect41 } from "effect";
22549
22739
 
22550
22740
  // src/lib/openai.ts
22551
- import { Effect as Effect41 } from "effect";
22741
+ import { Effect as Effect40 } from "effect";
22552
22742
  var getEnv = (name) => process.env[name];
22553
22743
  function clampScore(value) {
22554
22744
  return Math.max(0, Math.min(100, Math.round(value)));
@@ -22557,16 +22747,16 @@ function toError15(error) {
22557
22747
  return error instanceof Error ? error : new Error(String(error));
22558
22748
  }
22559
22749
  function trySync11(try_) {
22560
- return Effect41.try({
22750
+ return Effect40.try({
22561
22751
  try: try_,
22562
22752
  catch: toError15
22563
22753
  });
22564
22754
  }
22565
22755
  function scoreInboxItemWithOpenAIEffect(input) {
22566
- return Effect41.gen(function* () {
22756
+ return Effect40.gen(function* () {
22567
22757
  const apiKey = process.env.OPENAI_API_KEY;
22568
22758
  if (!apiKey) {
22569
- return yield* Effect41.fail(new Error("OPENAI_API_KEY is not set"));
22759
+ return yield* Effect40.fail(new Error("OPENAI_API_KEY is not set"));
22570
22760
  }
22571
22761
  const model = process.env.BIRDCLAW_OPENAI_MODEL || "gpt-5.2";
22572
22762
  const baseUrl = resolveOpenAIBaseUrl(getEnv);
@@ -22595,30 +22785,30 @@ function scoreInboxItemWithOpenAIEffect(input) {
22595
22785
  })
22596
22786
  })
22597
22787
  ).pipe(
22598
- Effect41.mapError(toError15),
22599
- Effect41.tapError(
22600
- (error) => Effect41.sync(
22788
+ Effect40.mapError(toError15),
22789
+ Effect40.tapError(
22790
+ (error) => Effect40.sync(
22601
22791
  () => debugLog(getEnv, `network error for ${url}: ${error.message}`)
22602
22792
  )
22603
22793
  )
22604
22794
  );
22605
22795
  if (!response.ok) {
22606
22796
  const text = yield* tryPromise(() => response.text()).pipe(
22607
- Effect41.mapError(toError15)
22797
+ Effect40.mapError(toError15)
22608
22798
  );
22609
22799
  debugLog(getEnv, `${url} -> ${response.status} ${text.slice(0, 400)}`);
22610
- return yield* Effect41.fail(
22800
+ return yield* Effect40.fail(
22611
22801
  new Error(
22612
22802
  `OpenAI request failed: ${response.status} ${text.slice(0, 400)}`
22613
22803
  )
22614
22804
  );
22615
22805
  }
22616
22806
  const payload = yield* tryPromise(() => response.json()).pipe(
22617
- Effect41.mapError(toError15)
22807
+ Effect40.mapError(toError15)
22618
22808
  );
22619
22809
  const content = payload.choices?.[0]?.message?.content;
22620
22810
  if (!content) {
22621
- return yield* Effect41.fail(new Error("OpenAI returned no content"));
22811
+ return yield* Effect40.fail(new Error("OpenAI returned no content"));
22622
22812
  }
22623
22813
  const parsed = yield* trySync11(
22624
22814
  () => JSON.parse(content)
@@ -22761,7 +22951,7 @@ function toError16(error) {
22761
22951
  return error instanceof Error ? error : new Error(String(error));
22762
22952
  }
22763
22953
  function trySync12(try_) {
22764
- return Effect42.try({
22954
+ return Effect41.try({
22765
22955
  try: try_,
22766
22956
  catch: toError16
22767
22957
  });
@@ -22771,7 +22961,7 @@ function scoreInboxEffect({
22771
22961
  account,
22772
22962
  limit = 8
22773
22963
  } = {}) {
22774
- return Effect42.gen(function* () {
22964
+ return Effect41.gen(function* () {
22775
22965
  const db = yield* trySync12(() => getNativeDb());
22776
22966
  const items = yield* trySync12(
22777
22967
  () => listInboxItems({ kind, account, limit }).items
@@ -22867,7 +23057,7 @@ import path9 from "node:path";
22867
23057
  import fs3 from "node:fs/promises";
22868
23058
  import os2 from "node:os";
22869
23059
  import path7 from "node:path";
22870
- import { Effect as Effect43 } from "effect";
23060
+ import { Effect as Effect42 } from "effect";
22871
23061
  var DEFAULT_LAUNCHD_PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
22872
23062
  function expandHomePath(input) {
22873
23063
  return input === "~" || input.startsWith("~/") ? path7.join(os2.homedir(), input.slice(2)) : input;
@@ -22955,7 +23145,7 @@ function buildLaunchAgent({
22955
23145
  };
22956
23146
  }
22957
23147
  function installLaunchAgentEffect(agent, options = {}) {
22958
- return Effect43.gen(function* () {
23148
+ return Effect42.gen(function* () {
22959
23149
  const launchAgentsDir = resolveUserPath(
22960
23150
  options.launchAgentsDir ?? "~/Library/LaunchAgents"
22961
23151
  );
@@ -22974,7 +23164,7 @@ function installLaunchAgentEffect(agent, options = {}) {
22974
23164
  yield* runSubprocessEffect({
22975
23165
  command: "launchctl",
22976
23166
  args: ["unload", plistPath]
22977
- }).pipe(Effect43.catchAll(() => Effect43.void));
23167
+ }).pipe(Effect42.catchAll(() => Effect42.void));
22978
23168
  yield* runSubprocessEffect({
22979
23169
  command: "launchctl",
22980
23170
  args: ["load", "-w", plistPath]
@@ -23003,7 +23193,7 @@ function installLaunchAgent(agent, options = {}) {
23003
23193
  import fs4 from "node:fs/promises";
23004
23194
  import os3 from "node:os";
23005
23195
  import path8 from "node:path";
23006
- import { Effect as Effect44 } from "effect";
23196
+ import { Effect as Effect43 } from "effect";
23007
23197
  function isFileExistsError(error) {
23008
23198
  return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
23009
23199
  }
@@ -23061,17 +23251,17 @@ async function acquireScheduledJobLock(lockPath, staleMs) {
23061
23251
  }
23062
23252
  function acquireScheduledJobLockEffect(lockPath, staleMs) {
23063
23253
  return tryPromise(() => acquireScheduledJobLock(lockPath, staleMs)).pipe(
23064
- Effect44.map(
23254
+ Effect43.map(
23065
23255
  (release) => release ? () => tryPromise(release).pipe(
23066
- Effect44.asVoid,
23067
- Effect44.catchAll(() => Effect44.void)
23256
+ Effect43.asVoid,
23257
+ Effect43.catchAll(() => Effect43.void)
23068
23258
  ) : void 0
23069
23259
  )
23070
23260
  );
23071
23261
  }
23072
23262
 
23073
23263
  // src/lib/timeline-collections-live.ts
23074
- import { Effect as Effect45 } from "effect";
23264
+ import { Effect as Effect44 } from "effect";
23075
23265
  var DEFAULT_COLLECTION_CACHE_TTL_MS = 2 * 6e4;
23076
23266
  var DEFAULT_EARLY_STOP_MAX_PAGES = 10;
23077
23267
  var MIN_XURL_LIMIT = 5;
@@ -23194,12 +23384,12 @@ function fetchXurlCollectionEffect({
23194
23384
  maxPages,
23195
23385
  earlyStop
23196
23386
  }) {
23197
- return Effect45.gen(function* () {
23387
+ return Effect44.gen(function* () {
23198
23388
  let resolvedUserId = userId;
23199
23389
  if (!resolvedUserId) {
23200
23390
  const [accountUser] = yield* liveTransportGateway.xurl.lookupUsersByHandles([username]);
23201
23391
  if (!accountUser?.id) {
23202
- return yield* Effect45.fail(
23392
+ return yield* Effect44.fail(
23203
23393
  new Error(`Could not resolve Twitter user id for @${username}`)
23204
23394
  );
23205
23395
  }
@@ -23207,7 +23397,7 @@ function fetchXurlCollectionEffect({
23207
23397
  }
23208
23398
  let saturatedAtPage;
23209
23399
  const result = yield* runSyncPlanEffect({
23210
- fetchPage: ({ cursor, pageIndex }) => Effect45.gen(function* () {
23400
+ fetchPage: ({ cursor, pageIndex }) => Effect44.gen(function* () {
23211
23401
  const payload = yield* kind === "likes" ? liveTransportGateway.xurl.listLikes({
23212
23402
  maxResults: limit,
23213
23403
  username,
@@ -23289,7 +23479,7 @@ function syncTimelineCollectionEffect({
23289
23479
  cacheTtlMs,
23290
23480
  earlyStop = false
23291
23481
  }) {
23292
- return Effect45.gen(function* () {
23482
+ return Effect44.gen(function* () {
23293
23483
  yield* trySync(() => assertLimit2(limit));
23294
23484
  const parsedMaxPages = yield* trySync(() => parseMaxPages3(maxPages));
23295
23485
  const shouldApplyEarlyStopCap = earlyStop && !all && parsedMaxPages === null && mode !== "bird";
@@ -23718,7 +23908,7 @@ function parseAccountSyncSteps(value) {
23718
23908
 
23719
23909
  // src/lib/bookmark-sync-job.ts
23720
23910
  import path10 from "node:path";
23721
- import { Effect as Effect46 } from "effect";
23911
+ import { Effect as Effect45 } from "effect";
23722
23912
  var DEFAULT_BOOKMARK_SYNC_INTERVAL_SECONDS = 3 * 60 * 60;
23723
23913
  var DEFAULT_BOOKMARK_SYNC_LIMIT = 100;
23724
23914
  var DEFAULT_BOOKMARK_SYNC_MAX_PAGES = 5;
@@ -23738,7 +23928,7 @@ function toError17(error) {
23738
23928
  return error instanceof Error ? error : new Error(String(error));
23739
23929
  }
23740
23930
  function trySync13(try_) {
23741
- return Effect46.try({
23931
+ return Effect45.try({
23742
23932
  try: try_,
23743
23933
  catch: toError17
23744
23934
  });
@@ -23758,7 +23948,7 @@ function runBookmarkSyncJobEffect({
23758
23948
  lockPath,
23759
23949
  db
23760
23950
  } = {}) {
23761
- return Effect46.gen(function* () {
23951
+ return Effect45.gen(function* () {
23762
23952
  yield* trySync13(() => ensureBirdclawDirs());
23763
23953
  const database = db ?? (yield* trySync13(() => getNativeDb({ seedDemoData: false })));
23764
23954
  const resolvedLogPath = yield* trySync13(
@@ -23799,8 +23989,8 @@ function runBookmarkSyncJobEffect({
23799
23989
  yield* appendScheduledJobAuditEffect(resolvedLogPath, entry);
23800
23990
  return entry;
23801
23991
  }
23802
- return yield* Effect46.gen(function* () {
23803
- const result = yield* Effect46.gen(function* () {
23992
+ return yield* Effect45.gen(function* () {
23993
+ const result = yield* Effect45.gen(function* () {
23804
23994
  const sync = yield* syncTimelineCollectionEffect({
23805
23995
  kind: "bookmarks",
23806
23996
  account,
@@ -23831,8 +24021,8 @@ function runBookmarkSyncJobEffect({
23831
24021
  backup
23832
24022
  };
23833
24023
  }).pipe(
23834
- Effect46.catchAll(
23835
- (error) => Effect46.gen(function* () {
24024
+ Effect45.catchAll(
24025
+ (error) => Effect45.gen(function* () {
23836
24026
  const after = yield* trySync13(() => ({
23837
24027
  bookmarks: countBookmarks(database)
23838
24028
  }));
@@ -23851,7 +24041,7 @@ function runBookmarkSyncJobEffect({
23851
24041
  );
23852
24042
  yield* appendScheduledJobAuditEffect(resolvedLogPath, result);
23853
24043
  return result;
23854
- }).pipe(Effect46.ensuring(releaseLock()));
24044
+ }).pipe(Effect45.ensuring(releaseLock()));
23855
24045
  });
23856
24046
  }
23857
24047
  function runBookmarkSyncJob(options = {}) {
@@ -23917,7 +24107,7 @@ function buildBookmarkSyncLaunchAgentPlist(options = {}) {
23917
24107
  });
23918
24108
  }
23919
24109
  function installBookmarkSyncLaunchAgentEffect(options = {}) {
23920
- return Effect46.gen(function* () {
24110
+ return Effect45.gen(function* () {
23921
24111
  yield* trySync13(() => ensureBirdclawDirs());
23922
24112
  const agent = yield* trySync13(
23923
24113
  () => buildBookmarkSyncLaunchAgentPlist(options)
@@ -24020,7 +24210,7 @@ function registerJobCommands({ program: program2, print: print2 }) {
24020
24210
  }
24021
24211
 
24022
24212
  // src/lib/x-lists.ts
24023
- import { Effect as Effect47 } from "effect";
24213
+ import { Effect as Effect46 } from "effect";
24024
24214
  var DEFAULT_MAX_LISTS = 20;
24025
24215
  var DEFAULT_MEMBER_LIMIT = 20;
24026
24216
  var DEFAULT_MAX_MEMBER_PAGES = 1;
@@ -24068,7 +24258,7 @@ function fetchOwnedListsEffect({
24068
24258
  maxLists
24069
24259
  }) {
24070
24260
  if (source === "bird") {
24071
- return Effect47.gen(function* () {
24261
+ return Effect46.gen(function* () {
24072
24262
  const authenticated = yield* getAuthenticatedBirdAccountEffect();
24073
24263
  yield* trySync(
24074
24264
  () => assertLiveAccountMatches({
@@ -24081,12 +24271,12 @@ function fetchOwnedListsEffect({
24081
24271
  return yield* listOwnedXListsViaBirdEffect({ maxResults: maxLists });
24082
24272
  });
24083
24273
  }
24084
- return Effect47.gen(function* () {
24274
+ return Effect46.gen(function* () {
24085
24275
  const authenticated = yield* lookupAuthenticatedOAuth2UserEffect(
24086
24276
  account.username
24087
24277
  );
24088
24278
  if (!authenticated) {
24089
- return yield* Effect47.fail(
24279
+ return yield* Effect46.fail(
24090
24280
  new Error("xurl did not return an OAuth2 user")
24091
24281
  );
24092
24282
  }
@@ -24102,7 +24292,7 @@ function fetchOwnedListsEffect({
24102
24292
  );
24103
24293
  const userId = account.externalUserId ?? liveExternalUserId;
24104
24294
  if (!userId) {
24105
- return yield* Effect47.fail(
24295
+ return yield* Effect46.fail(
24106
24296
  new Error("xurl authenticated user is missing an id")
24107
24297
  );
24108
24298
  }
@@ -24127,7 +24317,7 @@ function fetchListMembersEffect({
24127
24317
  maxPages: maxMemberPages
24128
24318
  });
24129
24319
  }
24130
- return Effect47.gen(function* () {
24320
+ return Effect46.gen(function* () {
24131
24321
  const data = [];
24132
24322
  const seen = /* @__PURE__ */ new Set();
24133
24323
  let paginationToken;
@@ -24280,7 +24470,7 @@ function persistListResult({
24280
24470
  })();
24281
24471
  }
24282
24472
  function syncXListsEffect(options = {}) {
24283
- return Effect47.suspend(() => {
24473
+ return Effect46.suspend(() => {
24284
24474
  const mode = normalizedMode(options.mode);
24285
24475
  const maxLists = positiveInteger(
24286
24476
  "--max-lists",
@@ -24310,13 +24500,13 @@ function syncXListsEffect(options = {}) {
24310
24500
  fetchOwnedListsEffect({ source, account, maxLists })
24311
24501
  )
24312
24502
  );
24313
- return Effect47.gen(function* () {
24503
+ return Effect46.gen(function* () {
24314
24504
  const discovered = yield* fetchWithTransportFallbackEffect(transports);
24315
24505
  const source = discovered.source;
24316
24506
  const lists = discovered.payload.data.slice(0, maxLists);
24317
24507
  const results = [];
24318
24508
  for (const [index, list] of lists.entries()) {
24319
- if (index > 0 && delayMs > 0) yield* Effect47.sleep(delayMs);
24509
+ if (index > 0 && delayMs > 0) yield* Effect46.sleep(delayMs);
24320
24510
  const fetched = yield* fetchListMembersEffect({
24321
24511
  source,
24322
24512
  list,
@@ -24324,9 +24514,9 @@ function syncXListsEffect(options = {}) {
24324
24514
  memberLimit,
24325
24515
  maxMemberPages
24326
24516
  }).pipe(
24327
- Effect47.map((payload) => ({ ok: true, payload })),
24328
- Effect47.catchAll(
24329
- (error) => Effect47.succeed({ ok: false, error: toError(error) })
24517
+ Effect46.map((payload) => ({ ok: true, payload })),
24518
+ Effect46.catchAll(
24519
+ (error) => Effect46.succeed({ ok: false, error: toError(error) })
24330
24520
  )
24331
24521
  );
24332
24522
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
@@ -24551,23 +24741,23 @@ function registerListCommands({
24551
24741
  }
24552
24742
 
24553
24743
  // src/lib/profile-replies.ts
24554
- import { Effect as Effect51 } from "effect";
24744
+ import { Effect as Effect50 } from "effect";
24555
24745
 
24556
24746
  // src/lib/moderation-target.ts
24557
- import { Effect as Effect50 } from "effect";
24747
+ import { Effect as Effect49 } from "effect";
24558
24748
 
24559
24749
  // src/lib/bird-actions.ts
24560
- import { Effect as Effect49 } from "effect";
24750
+ import { Effect as Effect48 } from "effect";
24561
24751
 
24562
24752
  // src/lib/bird-command.ts
24563
24753
  import { access } from "node:fs/promises";
24564
24754
  import { constants as constants2 } from "node:fs";
24565
- import { Data as Data3, Effect as Effect48 } from "effect";
24566
- var BirdCommandUnavailableError = class extends Data3.TaggedError(
24755
+ import { Data as Data4, Effect as Effect47 } from "effect";
24756
+ var BirdCommandUnavailableError = class extends Data4.TaggedError(
24567
24757
  "BirdCommandUnavailableError"
24568
24758
  ) {
24569
24759
  };
24570
- var BirdCommandExecutionError = class extends Data3.TaggedError(
24760
+ var BirdCommandExecutionError = class extends Data4.TaggedError(
24571
24761
  "BirdCommandExecutionError"
24572
24762
  ) {
24573
24763
  };
@@ -24615,25 +24805,25 @@ function execFailureFromCause(command, cause) {
24615
24805
  }
24616
24806
  function assertBirdCommandAvailableEffect(command) {
24617
24807
  if (!isPathCommand(command)) {
24618
- return Effect48.void;
24808
+ return Effect47.void;
24619
24809
  }
24620
- return Effect48.tryPromise({
24810
+ return Effect47.tryPromise({
24621
24811
  try: () => Promise.resolve(access(command, constants2.X_OK)),
24622
24812
  catch: (cause) => new BirdCommandUnavailableError({
24623
24813
  message: formatBirdInstallHint(command),
24624
24814
  command,
24625
24815
  cause
24626
24816
  })
24627
- }).pipe(Effect48.asVoid);
24817
+ }).pipe(Effect47.asVoid);
24628
24818
  }
24629
24819
  function getBirdCommandEffect() {
24630
- return Effect48.try({
24820
+ return Effect47.try({
24631
24821
  try: () => getBirdCommand(),
24632
24822
  catch: (cause) => cause instanceof Error ? cause : new Error(String(cause))
24633
24823
  });
24634
24824
  }
24635
24825
  function runBirdCommandEffect(args, options) {
24636
- return Effect48.gen(function* () {
24826
+ return Effect47.gen(function* () {
24637
24827
  const birdCommand = yield* getBirdCommandEffect();
24638
24828
  yield* assertBirdCommandAvailableEffect(birdCommand);
24639
24829
  return yield* runSubprocessEffect({
@@ -24646,8 +24836,8 @@ function runBirdCommandEffect(args, options) {
24646
24836
  ...options?.signal ? { signal: options.signal } : {},
24647
24837
  ...options?.killSignal ? { killSignal: options.killSignal } : {}
24648
24838
  }).pipe(
24649
- Effect48.map(({ stdout, stderr }) => ({ stdout, stderr })),
24650
- Effect48.mapError((cause) => execFailureFromCause(birdCommand, cause))
24839
+ Effect47.map(({ stdout, stderr }) => ({ stdout, stderr })),
24840
+ Effect47.mapError((cause) => execFailureFromCause(birdCommand, cause))
24651
24841
  );
24652
24842
  });
24653
24843
  }
@@ -24685,9 +24875,9 @@ function toError18(error) {
24685
24875
  return error instanceof Error ? error : new Error(String(error));
24686
24876
  }
24687
24877
  function runBirdJsonCommandEffect2(args) {
24688
- return Effect49.gen(function* () {
24878
+ return Effect48.gen(function* () {
24689
24879
  const { stdout } = yield* runBirdCommandEffect(args);
24690
- return yield* Effect49.try({
24880
+ return yield* Effect48.try({
24691
24881
  try: () => JSON.parse(stripAnsi2(stdout)),
24692
24882
  catch: toError18
24693
24883
  });
@@ -24701,16 +24891,13 @@ function safeBirdProfileArg(query) {
24701
24891
  throw new Error("Invalid bird profile query");
24702
24892
  }
24703
24893
  function readBirdStatusViaBirdEffect(query) {
24704
- return Effect49.gen(function* () {
24705
- const safeQuery = yield* Effect49.try({
24894
+ return Effect48.gen(function* () {
24895
+ const safeQuery = yield* Effect48.try({
24706
24896
  try: () => safeBirdProfileArg(query),
24707
24897
  catch: toError18
24708
24898
  });
24709
24899
  return yield* runBirdJsonCommandEffect2(["status", safeQuery, "--json"]);
24710
- }).pipe(Effect49.catchAll(() => Effect49.succeed(null)));
24711
- }
24712
- function readBirdStatusViaBird(query) {
24713
- return runEffectPromise(readBirdStatusViaBirdEffect(query));
24900
+ }).pipe(Effect48.catchAll(() => Effect48.succeed(null)));
24714
24901
  }
24715
24902
  function toBirdLookupUser(payload) {
24716
24903
  const user = payload.user && typeof payload.user === "object" ? payload.user : null;
@@ -24744,8 +24931,8 @@ function toBirdLookupUser(payload) {
24744
24931
  };
24745
24932
  }
24746
24933
  function lookupProfileViaBirdEffect2(query) {
24747
- return Effect49.gen(function* () {
24748
- const safeQuery = yield* Effect49.try({
24934
+ return Effect48.gen(function* () {
24935
+ const safeQuery = yield* Effect48.try({
24749
24936
  try: () => safeBirdProfileArg(query),
24750
24937
  catch: toError18
24751
24938
  });
@@ -24756,14 +24943,11 @@ function lookupProfileViaBirdEffect2(query) {
24756
24943
  "1",
24757
24944
  "--json"
24758
24945
  ]);
24759
- return yield* Effect49.try({
24946
+ return yield* Effect48.try({
24760
24947
  try: () => toBirdLookupUser(payload),
24761
24948
  catch: toError18
24762
24949
  });
24763
- }).pipe(Effect49.catchAll(() => Effect49.succeed(null)));
24764
- }
24765
- function lookupProfileViaBird(query) {
24766
- return runEffectPromise(lookupProfileViaBirdEffect2(query));
24950
+ }).pipe(Effect48.catchAll(() => Effect48.succeed(null)));
24767
24951
  }
24768
24952
  function runVerifiedBirdMutationEffect({
24769
24953
  action,
@@ -24771,8 +24955,8 @@ function runVerifiedBirdMutationEffect({
24771
24955
  verifyField,
24772
24956
  expectedValue
24773
24957
  }) {
24774
- return Effect49.gen(function* () {
24775
- const safeQuery = yield* Effect49.try({
24958
+ return Effect48.gen(function* () {
24959
+ const safeQuery = yield* Effect48.try({
24776
24960
  try: () => safeBirdProfileArg(query),
24777
24961
  catch: toError18
24778
24962
  });
@@ -24786,12 +24970,12 @@ function runVerifiedBirdMutationEffect({
24786
24970
  action,
24787
24971
  safeQuery
24788
24972
  ]).pipe(
24789
- Effect49.map(({ stdout, stderr }) => ({
24973
+ Effect48.map(({ stdout, stderr }) => ({
24790
24974
  ok: true,
24791
24975
  output: normalizeOutput(stdout, stderr)
24792
24976
  })),
24793
- Effect49.catchAll(
24794
- (error) => Effect49.succeed({
24977
+ Effect48.catchAll(
24978
+ (error) => Effect48.succeed({
24795
24979
  ok: false,
24796
24980
  output: formatExecError2(error, `bird ${action} failed`)
24797
24981
  })
@@ -24828,9 +25012,6 @@ function blockUserViaBirdEffect(query) {
24828
25012
  expectedValue: true
24829
25013
  });
24830
25014
  }
24831
- function blockUserViaBird(query) {
24832
- return runEffectPromise(blockUserViaBirdEffect(query));
24833
- }
24834
25015
  function unblockUserViaBirdEffect(query) {
24835
25016
  return runVerifiedBirdMutationEffect({
24836
25017
  action: "unblock",
@@ -24839,9 +25020,6 @@ function unblockUserViaBirdEffect(query) {
24839
25020
  expectedValue: false
24840
25021
  });
24841
25022
  }
24842
- function unblockUserViaBird(query) {
24843
- return runEffectPromise(unblockUserViaBirdEffect(query));
24844
- }
24845
25023
  function muteUserViaBirdEffect(query) {
24846
25024
  return runVerifiedBirdMutationEffect({
24847
25025
  action: "mute",
@@ -24850,9 +25028,6 @@ function muteUserViaBirdEffect(query) {
24850
25028
  expectedValue: true
24851
25029
  });
24852
25030
  }
24853
- function muteUserViaBird(query) {
24854
- return runEffectPromise(muteUserViaBirdEffect(query));
24855
- }
24856
25031
  function unmuteUserViaBirdEffect(query) {
24857
25032
  return runVerifiedBirdMutationEffect({
24858
25033
  action: "unmute",
@@ -24861,16 +25036,13 @@ function unmuteUserViaBirdEffect(query) {
24861
25036
  expectedValue: false
24862
25037
  });
24863
25038
  }
24864
- function unmuteUserViaBird(query) {
24865
- return runEffectPromise(unmuteUserViaBirdEffect(query));
24866
- }
24867
25039
 
24868
25040
  // src/lib/moderation-target.ts
24869
25041
  function toError19(error) {
24870
25042
  return error instanceof Error ? error : new Error(String(error));
24871
25043
  }
24872
25044
  function trySync14(try_) {
24873
- return Effect50.try({
25045
+ return Effect49.try({
24874
25046
  try: try_,
24875
25047
  catch: toError19
24876
25048
  });
@@ -24918,11 +25090,11 @@ function resolveLocalProfile(db, normalizedQuery) {
24918
25090
  };
24919
25091
  }
24920
25092
  function resolveProfileEffect(query, providedDb) {
24921
- return Effect50.gen(function* () {
25093
+ return Effect49.gen(function* () {
24922
25094
  const db = providedDb ?? (yield* trySync14(() => getNativeDb()));
24923
25095
  const normalizedQuery = normalizeProfileQuery(query);
24924
25096
  if (!normalizedQuery) {
24925
- return yield* Effect50.fail(new Error("Missing profile handle or id"));
25097
+ return yield* Effect49.fail(new Error("Missing profile handle or id"));
24926
25098
  }
24927
25099
  const local = yield* trySync14(
24928
25100
  () => resolveLocalProfile(db, normalizedQuery)
@@ -24931,7 +25103,7 @@ function resolveProfileEffect(query, providedDb) {
24931
25103
  if (local) {
24932
25104
  return local;
24933
25105
  }
24934
- return yield* Effect50.fail(
25106
+ return yield* Effect49.fail(
24935
25107
  new Error(`Profile not found locally: ${query}`)
24936
25108
  );
24937
25109
  }
@@ -24940,11 +25112,11 @@ function resolveProfileEffect(query, providedDb) {
24940
25112
  }
24941
25113
  let user;
24942
25114
  let lastError;
24943
- const birdResult = yield* tryPromise(
24944
- () => lookupProfileViaBird(local?.profile.handle ?? normalizedQuery)
25115
+ const birdResult = yield* lookupProfileViaBirdEffect2(
25116
+ local?.profile.handle ?? normalizedQuery
24945
25117
  ).pipe(
24946
- Effect50.map((value) => ({ ok: true, value })),
24947
- Effect50.catchAll((error) => Effect50.succeed({ ok: false, error }))
25118
+ Effect49.map((value) => ({ ok: true, value })),
25119
+ Effect49.catchAll((error) => Effect49.succeed({ ok: false, error }))
24948
25120
  );
24949
25121
  if (birdResult.ok) {
24950
25122
  user = birdResult.value ?? void 0;
@@ -24952,12 +25124,12 @@ function resolveProfileEffect(query, providedDb) {
24952
25124
  lastError = birdResult.error;
24953
25125
  }
24954
25126
  if (!user) {
24955
- const xurlResult = yield* tryPromise(
24956
- () => /^\d+$/.test(normalizedQuery) ? lookupUsersByIds([normalizedQuery]) : lookupUsersByHandles([local?.profile.handle ?? normalizedQuery])
24957
- ).pipe(
24958
- Effect50.map((value) => ({ ok: true, value })),
24959
- Effect50.catchAll(
24960
- (error) => Effect50.succeed({ ok: false, error })
25127
+ const xurlResult = yield* (/^\d+$/.test(normalizedQuery) ? lookupUsersByIdsEffect([normalizedQuery]) : lookupUsersByHandlesEffect([
25128
+ local?.profile.handle ?? normalizedQuery
25129
+ ])).pipe(
25130
+ Effect49.map((value) => ({ ok: true, value })),
25131
+ Effect49.catchAll(
25132
+ (error) => Effect49.succeed({ ok: false, error })
24961
25133
  )
24962
25134
  );
24963
25135
  if (xurlResult.ok) {
@@ -24970,13 +25142,13 @@ function resolveProfileEffect(query, providedDb) {
24970
25142
  if (local) {
24971
25143
  return local;
24972
25144
  }
24973
- return yield* Effect50.fail(lastError);
25145
+ return yield* Effect49.fail(lastError);
24974
25146
  }
24975
25147
  if (!user) {
24976
25148
  if (local) {
24977
25149
  return local;
24978
25150
  }
24979
- return yield* Effect50.fail(new Error(`Profile not found: ${query}`));
25151
+ return yield* Effect49.fail(new Error(`Profile not found: ${query}`));
24980
25152
  }
24981
25153
  if (local) {
24982
25154
  return yield* databaseWriteEffect(
@@ -25017,11 +25189,11 @@ function inspectProfileReplies(query, { limit = 12 } = {}) {
25017
25189
  return runEffectPromise(inspectProfileRepliesEffect(query, { limit }));
25018
25190
  }
25019
25191
  function inspectProfileRepliesEffect(query, { limit = 12 } = {}) {
25020
- return Effect51.gen(function* () {
25192
+ return Effect50.gen(function* () {
25021
25193
  const resolved = yield* tryPromise(() => resolveProfile(query));
25022
25194
  const externalUserId = resolved.externalUserId;
25023
25195
  if (!externalUserId) {
25024
- return yield* Effect51.fail(
25196
+ return yield* Effect50.fail(
25025
25197
  new Error(`Profile has no external Twitter user id: ${query}`)
25026
25198
  );
25027
25199
  }
@@ -25116,18 +25288,18 @@ function registerMentionCommands({
25116
25288
  }
25117
25289
 
25118
25290
  // src/lib/blocks.ts
25119
- import { Effect as Effect54 } from "effect";
25291
+ import { Effect as Effect53 } from "effect";
25120
25292
 
25121
25293
  // src/lib/moderation-state.ts
25122
- import { Effect as Effect53 } from "effect";
25294
+ import { Effect as Effect52 } from "effect";
25123
25295
 
25124
25296
  // src/lib/actions-transport.ts
25125
- import { Effect as Effect52 } from "effect";
25297
+ import { Effect as Effect51 } from "effect";
25126
25298
  function toError20(error) {
25127
25299
  return error instanceof Error ? error : new Error(String(error));
25128
25300
  }
25129
25301
  function trySync15(try_) {
25130
- return Effect52.try({
25302
+ return Effect51.try({
25131
25303
  try: try_,
25132
25304
  catch: toError20
25133
25305
  });
@@ -25139,17 +25311,17 @@ function liveWritesDisabled4() {
25139
25311
  return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
25140
25312
  }
25141
25313
  function verifyExpectedAccountEffect(expectedAccount) {
25142
- return Effect52.gen(function* () {
25314
+ return Effect51.gen(function* () {
25143
25315
  if (!expectedAccount) return null;
25144
25316
  if (liveWritesDisabled4()) return null;
25145
- const sourceUser = yield* tryPromise(() => lookupAuthenticatedUserFresh());
25317
+ const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
25146
25318
  const sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
25147
25319
  const sourceUsername = sourceUser && typeof sourceUser.username === "string" ? profileHandleKey(sourceUser.username) : "";
25148
25320
  const expectedExternalUserId = expectedAccount.externalUserId?.trim() ?? "";
25149
25321
  const expectedHandle = profileHandleKey(expectedAccount.handle);
25150
25322
  if (expectedExternalUserId) {
25151
25323
  if (sourceUserId === expectedExternalUserId) return sourceUserId;
25152
- return yield* Effect52.fail(
25324
+ return yield* Effect51.fail(
25153
25325
  new Error(
25154
25326
  `xurl is authenticated as user ${sourceUserId || "unknown"}, not account ${expectedAccount.id}`
25155
25327
  )
@@ -25157,7 +25329,7 @@ function verifyExpectedAccountEffect(expectedAccount) {
25157
25329
  }
25158
25330
  if (expectedHandle && sourceUsername === expectedHandle)
25159
25331
  return sourceUserId;
25160
- return yield* Effect52.fail(
25332
+ return yield* Effect51.fail(
25161
25333
  new Error(
25162
25334
  sourceUsername ? `xurl is authenticated as @${sourceUsername}, not @${expectedHandle}` : "xurl authenticated user unavailable"
25163
25335
  )
@@ -25165,10 +25337,8 @@ function verifyExpectedAccountEffect(expectedAccount) {
25165
25337
  });
25166
25338
  }
25167
25339
  function runBirdActionEffect(action, query) {
25168
- return Effect52.gen(function* () {
25169
- const result = yield* tryPromise(
25170
- () => action === "block" ? blockUserViaBird(query) : action === "unblock" ? unblockUserViaBird(query) : action === "mute" ? muteUserViaBird(query) : unmuteUserViaBird(query)
25171
- );
25340
+ return Effect51.gen(function* () {
25341
+ const result = yield* action === "block" ? blockUserViaBirdEffect(query) : action === "unblock" ? unblockUserViaBirdEffect(query) : action === "mute" ? muteUserViaBirdEffect(query) : unmuteUserViaBirdEffect(query);
25172
25342
  return {
25173
25343
  ...result,
25174
25344
  transport: "bird"
@@ -25185,7 +25355,7 @@ function getVerifyExpectation(action) {
25185
25355
  };
25186
25356
  }
25187
25357
  function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId) {
25188
- return Effect52.gen(function* () {
25358
+ return Effect51.gen(function* () {
25189
25359
  if (!targetUserId) {
25190
25360
  return {
25191
25361
  ok: false,
@@ -25195,9 +25365,7 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
25195
25365
  }
25196
25366
  let sourceUserId = verifiedSourceUserId ?? "";
25197
25367
  if (!sourceUserId) {
25198
- const sourceUser = yield* tryPromise(
25199
- () => lookupAuthenticatedUserFresh()
25200
- );
25368
+ const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
25201
25369
  sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
25202
25370
  }
25203
25371
  if (!sourceUserId) {
@@ -25207,16 +25375,14 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
25207
25375
  transport: "xurl"
25208
25376
  };
25209
25377
  }
25210
- const result = yield* tryPromise(
25211
- () => action === "block" ? blockUserViaXurl(sourceUserId, targetUserId) : action === "unblock" ? unblockUserViaXurl(sourceUserId, targetUserId) : action === "mute" ? muteUserViaXurl(sourceUserId, targetUserId) : unmuteUserViaXurl(sourceUserId, targetUserId)
25212
- );
25378
+ const result = yield* action === "block" ? blockUserViaXurlEffect(sourceUserId, targetUserId) : action === "unblock" ? unblockUserViaXurlEffect(sourceUserId, targetUserId) : action === "mute" ? muteUserViaXurlEffect(sourceUserId, targetUserId) : unmuteUserViaXurlEffect(sourceUserId, targetUserId);
25213
25379
  if (!result.ok) {
25214
25380
  return {
25215
25381
  ...result,
25216
25382
  transport: "xurl"
25217
25383
  };
25218
25384
  }
25219
- const status = yield* tryPromise(() => readBirdStatusViaBird(query));
25385
+ const status = yield* readBirdStatusViaBirdEffect(query);
25220
25386
  const { field: verifyField, expected: expectedValue } = getVerifyExpectation(action);
25221
25387
  const actualValue = status && typeof status[verifyField] === "boolean" ? Boolean(status[verifyField]) : null;
25222
25388
  if (actualValue === null) {
@@ -25250,13 +25416,13 @@ function runModerationActionEffect({
25250
25416
  transport,
25251
25417
  expectedAccount
25252
25418
  }) {
25253
- return Effect52.gen(function* () {
25419
+ return Effect51.gen(function* () {
25254
25420
  const requestedTransport = yield* trySync15(
25255
25421
  () => resolveActionsTransport(transport)
25256
25422
  );
25257
25423
  const verifyXurlAccount = () => verifyExpectedAccountEffect(expectedAccount).pipe(
25258
- Effect52.catchAll(
25259
- (error) => Effect52.succeed({
25424
+ Effect51.catchAll(
25425
+ (error) => Effect51.succeed({
25260
25426
  ok: false,
25261
25427
  output: error instanceof Error ? error.message : String(error),
25262
25428
  transport: "xurl"
@@ -25345,7 +25511,7 @@ function toError21(error) {
25345
25511
  return error instanceof Error ? error : new Error(String(error));
25346
25512
  }
25347
25513
  function trySync16(try_) {
25348
- return Effect53.try({
25514
+ return Effect52.try({
25349
25515
  try: try_,
25350
25516
  catch: toError21
25351
25517
  });
@@ -25464,20 +25630,20 @@ function resolveModerationTargetEffect({
25464
25630
  query,
25465
25631
  selfActionError
25466
25632
  }) {
25467
- return Effect53.gen(function* () {
25633
+ return Effect52.gen(function* () {
25468
25634
  const db = yield* trySync16(() => getNativeDb());
25469
25635
  const resolvedAccountId = accountId || getDefaultAccountId(db);
25470
25636
  const accountHandle = yield* trySync16(
25471
25637
  () => getAccountHandle(db, resolvedAccountId)
25472
25638
  );
25473
25639
  if (!accountHandle) {
25474
- return yield* Effect53.fail(
25640
+ return yield* Effect52.fail(
25475
25641
  new Error(`Unknown account: ${resolvedAccountId}`)
25476
25642
  );
25477
25643
  }
25478
25644
  const normalizedQuery = normalizeProfileQuery(query);
25479
25645
  if (normalizedQuery.toLowerCase() === accountHandle.toLowerCase()) {
25480
- return yield* Effect53.fail(new Error(selfActionError));
25646
+ return yield* Effect52.fail(new Error(selfActionError));
25481
25647
  }
25482
25648
  const resolved = yield* resolveProfileEffect(query, db);
25483
25649
  const account = yield* trySync16(
@@ -25488,7 +25654,7 @@ function resolveModerationTargetEffect({
25488
25654
  );
25489
25655
  const accountExternalUserId = account?.external_user_id ?? getExternalUserId(accountProfile?.id ?? "");
25490
25656
  if (profileHandleKey(resolved.profile.handle) === profileHandleKey(accountHandle) || accountProfile?.id && resolved.profile.id === accountProfile.id || accountExternalUserId && resolved.externalUserId === accountExternalUserId) {
25491
- return yield* Effect53.fail(new Error(selfActionError));
25657
+ return yield* Effect52.fail(new Error(selfActionError));
25492
25658
  }
25493
25659
  return {
25494
25660
  db,
@@ -25505,7 +25671,7 @@ function resolveModerationTargetEffect({
25505
25671
  }
25506
25672
  function addModerationEffect(kind, accountId, query, options = {}) {
25507
25673
  const descriptor = moderationDescriptors[kind];
25508
- return Effect53.gen(function* () {
25674
+ return Effect52.gen(function* () {
25509
25675
  const { db, resolved, resolvedAccountId, accountIdentity, actionQuery } = yield* resolveModerationTargetEffect({
25510
25676
  accountId,
25511
25677
  query,
@@ -25555,7 +25721,7 @@ function addModeration(kind, accountId, query, options = {}) {
25555
25721
  }
25556
25722
  function recordModerationEffect(kind, accountId, query) {
25557
25723
  const descriptor = moderationDescriptors[kind];
25558
- return Effect53.gen(function* () {
25724
+ return Effect52.gen(function* () {
25559
25725
  const { db, resolved, resolvedAccountId } = yield* resolveModerationTargetEffect({
25560
25726
  accountId,
25561
25727
  query,
@@ -25586,7 +25752,7 @@ function recordModeration(kind, accountId, query) {
25586
25752
  }
25587
25753
  function removeModerationEffect(kind, accountId, query, options = {}) {
25588
25754
  const descriptor = moderationDescriptors[kind];
25589
- return Effect53.gen(function* () {
25755
+ return Effect52.gen(function* () {
25590
25756
  const { db, resolved, resolvedAccountId, accountIdentity, actionQuery } = yield* resolveModerationTargetEffect({
25591
25757
  accountId,
25592
25758
  query,
@@ -25662,7 +25828,7 @@ function toError22(error) {
25662
25828
  return error instanceof Error ? error : new Error(String(error));
25663
25829
  }
25664
25830
  function trySync17(try_) {
25665
- return Effect54.try({
25831
+ return Effect53.try({
25666
25832
  try: try_,
25667
25833
  catch: toError22
25668
25834
  });
@@ -25675,12 +25841,12 @@ function listBlocks({
25675
25841
  return listModerationState("block", { account, search, limit });
25676
25842
  }
25677
25843
  function syncBlocksEffect(accountId) {
25678
- return Effect54.gen(function* () {
25844
+ return Effect53.gen(function* () {
25679
25845
  const db = yield* trySync17(() => getNativeDb());
25680
25846
  const resolvedAccountId = accountId || getDefaultAccountId(db);
25681
25847
  const accountHandle = getAccountHandle(db, resolvedAccountId);
25682
25848
  if (!accountHandle) {
25683
- return yield* Effect54.fail(
25849
+ return yield* Effect53.fail(
25684
25850
  new Error(`Unknown account: ${resolvedAccountId}`)
25685
25851
  );
25686
25852
  }
@@ -25701,10 +25867,8 @@ function syncBlocksEffect(accountId) {
25701
25867
  }
25702
25868
  };
25703
25869
  }
25704
- return yield* Effect54.gen(function* () {
25705
- const me = yield* tryPromise(() => lookupAuthenticatedUserFresh()).pipe(
25706
- Effect54.mapError(toError22)
25707
- );
25870
+ return yield* Effect53.gen(function* () {
25871
+ const me = yield* lookupAuthenticatedUserFreshEffect();
25708
25872
  const sourceUserId = typeof me?.id === "string" && me.id.length > 0 ? me.id : null;
25709
25873
  const sourceUsername = typeof me?.username === "string" ? me.username.replace(/^@/, "") : "";
25710
25874
  const accountExternalUserId = account?.external_user_id?.trim() ?? "";
@@ -25748,9 +25912,7 @@ function syncBlocksEffect(accountId) {
25748
25912
  let pageCount = 0;
25749
25913
  let completed = false;
25750
25914
  do {
25751
- const page = yield* tryPromise(
25752
- () => listBlockedUsers(sourceUserId, nextToken ?? void 0)
25753
- ).pipe(Effect54.mapError(toError22));
25915
+ const page = yield* listBlockedUsersEffect(sourceUserId, nextToken ?? void 0);
25754
25916
  const pageProfileIds = yield* databaseWriteEffect((writeDb) => {
25755
25917
  return page.items.map((user) => {
25756
25918
  const resolved = upsertProfileFromXUser(writeDb, user);
@@ -25792,8 +25954,8 @@ function syncBlocksEffect(accountId) {
25792
25954
  }
25793
25955
  };
25794
25956
  }).pipe(
25795
- Effect54.catchAll(
25796
- (error) => Effect54.succeed({
25957
+ Effect53.catchAll(
25958
+ (error) => Effect53.succeed({
25797
25959
  ok: false,
25798
25960
  accountId: resolvedAccountId,
25799
25961
  synced: remoteProfileIds.length > 0,
@@ -25944,7 +26106,7 @@ function registerModerationCommands({
25944
26106
 
25945
26107
  // src/lib/blocklist.ts
25946
26108
  import { readFile } from "node:fs/promises";
25947
- import { Effect as Effect55 } from "effect";
26109
+ import { Effect as Effect54 } from "effect";
25948
26110
  function stripInlineCode(value) {
25949
26111
  const match = value.match(/`([^`]+)`/);
25950
26112
  return match?.[1] ?? value;
@@ -25981,15 +26143,15 @@ function parseBlocklistText(text) {
25981
26143
  return items;
25982
26144
  }
25983
26145
  function importBlocklistEffect(accountId, filePath) {
25984
- return Effect55.gen(function* () {
26146
+ return Effect54.gen(function* () {
25985
26147
  const text = yield* tryPromise(() => readFile(filePath, "utf8"));
25986
26148
  const queries = parseBlocklistText(text);
25987
26149
  const items = [];
25988
26150
  for (const query of queries) {
25989
26151
  const blocked = yield* tryPromise(() => addBlock(accountId, query)).pipe(
25990
- Effect55.map((result2) => ({ ok: true, result: result2 })),
25991
- Effect55.catchAll(
25992
- (error) => Effect55.succeed({ ok: false, error })
26152
+ Effect54.map((result2) => ({ ok: true, result: result2 })),
26153
+ Effect54.catchAll(
26154
+ (error) => Effect54.succeed({ ok: false, error })
25993
26155
  )
25994
26156
  );
25995
26157
  if (!blocked.ok) {
@@ -26045,7 +26207,7 @@ function registerModerationCommands2(context) {
26045
26207
  }
26046
26208
 
26047
26209
  // src/lib/link-index.ts
26048
- import { Effect as Effect56 } from "effect";
26210
+ import { Effect as Effect55 } from "effect";
26049
26211
  var DEFAULT_EXPAND_CONCURRENCY = 12;
26050
26212
  function parseJsonField5(value, fallback) {
26051
26213
  if (typeof value !== "string" || value.length === 0) {
@@ -26207,7 +26369,7 @@ function expandWithConcurrencyEffect(db, urls, options) {
26207
26369
  misses: 0,
26208
26370
  errors: 0
26209
26371
  };
26210
- return Effect56.forEach(
26372
+ return Effect55.forEach(
26211
26373
  urls,
26212
26374
  (url) => tryPromise(
26213
26375
  () => expandUrls([url], {
@@ -26216,7 +26378,7 @@ function expandWithConcurrencyEffect(db, urls, options) {
26216
26378
  timeoutMs: options.timeoutMs
26217
26379
  })
26218
26380
  ).pipe(
26219
- Effect56.map((results) => {
26381
+ Effect55.map((results) => {
26220
26382
  const result = results[0];
26221
26383
  if (result.source === "network") {
26222
26384
  counts.networkExpansions++;
@@ -26233,10 +26395,10 @@ function expandWithConcurrencyEffect(db, urls, options) {
26233
26395
  })
26234
26396
  ),
26235
26397
  { concurrency }
26236
- ).pipe(Effect56.as(counts));
26398
+ ).pipe(Effect55.as(counts));
26237
26399
  }
26238
26400
  function backfillLinkIndexEffect(options = {}) {
26239
- return Effect56.gen(function* () {
26401
+ return Effect55.gen(function* () {
26240
26402
  const db = getNativeDb({ seedDemoData: false });
26241
26403
  const { occurrences, entityExpansions } = rebuildOccurrences(
26242
26404
  db,
@@ -26531,7 +26693,7 @@ import {
26531
26693
  } from "node:fs";
26532
26694
  import { appendFile, copyFile, rename, rm, writeFile } from "node:fs/promises";
26533
26695
  import path11 from "node:path";
26534
- import { Effect as Effect57 } from "effect";
26696
+ import { Effect as Effect56 } from "effect";
26535
26697
  var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
26536
26698
  var PBS_PREFIXES = [
26537
26699
  "/media/",
@@ -26550,13 +26712,13 @@ function toMediaError(error) {
26550
26712
  return error instanceof Error ? error : new Error(String(error));
26551
26713
  }
26552
26714
  function tryMediaSync(try_) {
26553
- return Effect57.try({
26715
+ return Effect56.try({
26554
26716
  try: try_,
26555
26717
  catch: toMediaError
26556
26718
  });
26557
26719
  }
26558
26720
  function tryMediaPromise(try_) {
26559
- return Effect57.tryPromise({
26721
+ return Effect56.tryPromise({
26560
26722
  try: try_,
26561
26723
  catch: toMediaError
26562
26724
  });
@@ -26821,7 +26983,7 @@ function archivePathFor(item, mediaOriginalsDir) {
26821
26983
  return archiveTweetDirs(mediaOriginalsDir, item.tweetId).map((tweetDir) => path11.join(tweetDir, fileName)).find((archivePath) => existsSync5(archivePath)) ?? null;
26822
26984
  }
26823
26985
  function reuseFromArchiveEffect(item, mediaOriginalsDir, maxBytes) {
26824
- return Effect57.gen(function* () {
26986
+ return Effect56.gen(function* () {
26825
26987
  const archivePath = archivePathFor(item, mediaOriginalsDir);
26826
26988
  if (!archivePath || !existsSync5(archivePath)) return null;
26827
26989
  const bytes = fileSize(archivePath);
@@ -26848,15 +27010,15 @@ function contentRangeTotal(response) {
26848
27010
  return total ? Number(total) : null;
26849
27011
  }
26850
27012
  function writeResponseBodyEffect(response, tmpPath, append, maxBytes, initialBytes) {
26851
- return Effect57.gen(function* () {
27013
+ return Effect56.gen(function* () {
26852
27014
  if (!response.body)
26853
- return yield* Effect57.fail(new Error("missing response body"));
27015
+ return yield* Effect56.fail(new Error("missing response body"));
26854
27016
  let bytes = 0;
26855
27017
  if (!append) {
26856
27018
  yield* tryMediaPromise(() => writeFile(tmpPath, Buffer.alloc(0)));
26857
27019
  }
26858
27020
  const reader = response.body.getReader();
26859
- const result = yield* Effect57.gen(function* () {
27021
+ const result = yield* Effect56.gen(function* () {
26860
27022
  for (; ; ) {
26861
27023
  const { done, value } = yield* tryMediaPromise(() => reader.read());
26862
27024
  if (done) {
@@ -26866,31 +27028,31 @@ function writeResponseBodyEffect(response, tmpPath, append, maxBytes, initialByt
26866
27028
  bytes += chunk.length;
26867
27029
  if (initialBytes + bytes > maxBytes) {
26868
27030
  yield* tryMediaPromise(() => rm(tmpPath, { force: true })).pipe(
26869
- Effect57.catchAll(() => Effect57.void)
27031
+ Effect56.catchAll(() => Effect56.void)
26870
27032
  );
26871
- return yield* Effect57.fail(new Error("max-bytes"));
27033
+ return yield* Effect56.fail(new Error("max-bytes"));
26872
27034
  }
26873
27035
  yield* tryMediaPromise(() => appendFile(tmpPath, chunk));
26874
27036
  }
26875
27037
  return bytes;
26876
27038
  }).pipe(
26877
- Effect57.map((writtenBytes) => ({ ok: true, writtenBytes })),
26878
- Effect57.catchAll(
26879
- (error) => Effect57.gen(function* () {
27039
+ Effect56.map((writtenBytes) => ({ ok: true, writtenBytes })),
27040
+ Effect56.catchAll(
27041
+ (error) => Effect56.gen(function* () {
26880
27042
  if (error.message === "max-bytes") {
26881
27043
  yield* tryMediaPromise(() => rm(tmpPath, { force: true })).pipe(
26882
- Effect57.catchAll(() => Effect57.void)
27044
+ Effect56.catchAll(() => Effect56.void)
26883
27045
  );
26884
27046
  }
26885
27047
  yield* tryMediaPromise(() => reader.cancel(error)).pipe(
26886
- Effect57.catchAll(() => Effect57.void)
27048
+ Effect56.catchAll(() => Effect56.void)
26887
27049
  );
26888
27050
  return { error, ok: false };
26889
27051
  })
26890
27052
  ),
26891
- Effect57.ensuring(Effect57.sync(() => reader.releaseLock()))
27053
+ Effect56.ensuring(Effect56.sync(() => reader.releaseLock()))
26892
27054
  );
26893
- if (!result.ok) return yield* Effect57.fail(result.error);
27055
+ if (!result.ok) return yield* Effect56.fail(result.error);
26894
27056
  return result.writtenBytes;
26895
27057
  });
26896
27058
  }
@@ -26902,13 +27064,13 @@ function fetchOneEffect({
26902
27064
  userAgent,
26903
27065
  maxBytes
26904
27066
  }) {
26905
- return Effect57.gen(function* () {
27067
+ return Effect56.gen(function* () {
26906
27068
  let rateLimited = false;
26907
27069
  for (let attempt = 0; attempt <= retryMax; attempt += 1) {
26908
27070
  const partialBytes = item.kind === "image" ? 0 : fileSize(item.tmpPath);
26909
27071
  if (partialBytes > maxBytes) {
26910
27072
  yield* tryMediaPromise(() => rm(item.tmpPath, { force: true })).pipe(
26911
- Effect57.catchAll(() => Effect57.void)
27073
+ Effect56.catchAll(() => Effect56.void)
26912
27074
  );
26913
27075
  return fail(item, "max-bytes");
26914
27076
  }
@@ -26920,9 +27082,9 @@ function fetchOneEffect({
26920
27082
  }
26921
27083
  })
26922
27084
  ).pipe(
26923
- Effect57.map((response2) => ({ ok: true, response: response2 })),
26924
- Effect57.catchAll(
26925
- (error) => Effect57.succeed({ error, ok: false })
27085
+ Effect56.map((response2) => ({ ok: true, response: response2 })),
27086
+ Effect56.catchAll(
27087
+ (error) => Effect56.succeed({ error, ok: false })
26926
27088
  )
26927
27089
  );
26928
27090
  if (!responseResult.ok) {
@@ -26933,7 +27095,7 @@ function fetchOneEffect({
26933
27095
  rateLimited = true;
26934
27096
  if (attempt < retryMax) {
26935
27097
  yield* tryMediaPromise(() => sleep(1e3 * 2 ** attempt)).pipe(
26936
- Effect57.catchAll(() => Effect57.void)
27098
+ Effect56.catchAll(() => Effect56.void)
26937
27099
  );
26938
27100
  continue;
26939
27101
  }
@@ -26945,7 +27107,7 @@ function fetchOneEffect({
26945
27107
  const expectedTotal = contentRangeTotal(response) ?? (contentLength(response) ?? 0) + (response.status === 206 ? partialBytes : 0);
26946
27108
  if (expectedTotal > maxBytes) {
26947
27109
  yield* tryMediaPromise(() => rm(item.tmpPath, { force: true })).pipe(
26948
- Effect57.catchAll(() => Effect57.void)
27110
+ Effect56.catchAll(() => Effect56.void)
26949
27111
  );
26950
27112
  return fail(item, "max-bytes");
26951
27113
  }
@@ -26957,9 +27119,9 @@ function fetchOneEffect({
26957
27119
  maxBytes,
26958
27120
  append ? partialBytes : 0
26959
27121
  ).pipe(
26960
- Effect57.map((bytes) => ({ bytes, ok: true })),
26961
- Effect57.catchAll(
26962
- (error) => Effect57.succeed({ error, ok: false })
27122
+ Effect56.map((bytes) => ({ bytes, ok: true })),
27123
+ Effect56.catchAll(
27124
+ (error) => Effect56.succeed({ error, ok: false })
26963
27125
  )
26964
27126
  );
26965
27127
  if (!bytesResult.ok) {
@@ -27033,7 +27195,7 @@ function runGroupEffect(items, parallel, pacingMs, now2, sleep, worker) {
27033
27195
  );
27034
27196
  })
27035
27197
  );
27036
- return Effect57.forEach(pacedItems, runPaced, {
27198
+ return Effect56.forEach(pacedItems, runPaced, {
27037
27199
  concurrency: Math.min(parallel, items.length),
27038
27200
  discard: true
27039
27201
  });
@@ -27042,7 +27204,7 @@ function fetchTweetMedia(options = {}) {
27042
27204
  return runEffectPromise(fetchTweetMediaEffect(options));
27043
27205
  }
27044
27206
  function fetchTweetMediaEffect(options = {}) {
27045
- return Effect57.gen(function* () {
27207
+ return Effect56.gen(function* () {
27046
27208
  const now2 = options.now ?? Date.now;
27047
27209
  const startedAt = now2();
27048
27210
  const sleep = options.sleep ?? defaultSleep;
@@ -27109,7 +27271,7 @@ function fetchTweetMediaEffect(options = {}) {
27109
27271
  retryMax,
27110
27272
  userAgent,
27111
27273
  maxBytes
27112
- }).pipe(Effect57.map((fetched) => applyFetched(result, fetched)))
27274
+ }).pipe(Effect56.map((fetched) => applyFetched(result, fetched)))
27113
27275
  );
27114
27276
  yield* runGroupEffect(
27115
27277
  httpCandidates.filter((item) => item.kind === "image"),
@@ -27157,9 +27319,9 @@ function formatMediaFetchResult(result) {
27157
27319
  }
27158
27320
 
27159
27321
  // src/lib/whois.ts
27160
- import { Effect as Effect58 } from "effect";
27322
+ import { Effect as Effect57 } from "effect";
27161
27323
  function trySync18(try_) {
27162
- return Effect58.try({
27324
+ return Effect57.try({
27163
27325
  try: try_,
27164
27326
  catch: (cause) => cause
27165
27327
  });
@@ -27690,7 +27852,7 @@ function loadWhoisConversations(query, options, includeDms, context, limit) {
27690
27852
  return [...merged.values()];
27691
27853
  }
27692
27854
  function runWhoisEffect(query, options = {}) {
27693
- return Effect58.gen(function* () {
27855
+ return Effect57.gen(function* () {
27694
27856
  const includeDms = options.dms ?? true;
27695
27857
  const includeTweets = options.tweets ?? false;
27696
27858
  const limit = options.limit ?? 10;
@@ -28141,7 +28303,7 @@ import { Readable as Readable2 } from "node:stream";
28141
28303
  import { pathToFileURL } from "node:url";
28142
28304
 
28143
28305
  // src/lib/http-effect.ts
28144
- import { Effect as Effect59 } from "effect";
28306
+ import { Effect as Effect58 } from "effect";
28145
28307
  var LOCAL_WEB_PEER_HEADER = "x-birdclaw-local-peer";
28146
28308
 
28147
28309
  // src/lib/production-server.ts
@@ -28415,7 +28577,7 @@ function registerStorageCommands({
28415
28577
  }
28416
28578
 
28417
28579
  // src/lib/authored-live.ts
28418
- import { Effect as Effect60 } from "effect";
28580
+ import { Effect as Effect59 } from "effect";
28419
28581
  var AuthoredSyncError = class extends Error {
28420
28582
  constructor(message, exitCode) {
28421
28583
  super(message);
@@ -28657,10 +28819,10 @@ function resolveAuthoredIdentityEffect({
28657
28819
  account,
28658
28820
  db
28659
28821
  }) {
28660
- return Effect60.gen(function* () {
28822
+ return Effect59.gen(function* () {
28661
28823
  const status = yield* liveTransportGateway.xurl.getTransportStatus();
28662
28824
  if (status.availableTransport !== "xurl") {
28663
- return yield* Effect60.fail(new AuthoredSyncError(status.statusText, 4));
28825
+ return yield* Effect59.fail(new AuthoredSyncError(status.statusText, 4));
28664
28826
  }
28665
28827
  const resolvedAccount = yield* trySync(
28666
28828
  () => resolveLiveSyncAccount(db, account)
@@ -28675,7 +28837,7 @@ function resolveAuthoredIdentityEffect({
28675
28837
  const authenticated = yield* liveTransportGateway.xurl.lookupAuthenticatedUser();
28676
28838
  const authenticatedUser = userFromAuthenticatedPayload(authenticated);
28677
28839
  if (!authenticatedUser?.id) {
28678
- return yield* Effect60.fail(
28840
+ return yield* Effect59.fail(
28679
28841
  new AuthoredSyncError(
28680
28842
  "Could not resolve authenticated Twitter user id",
28681
28843
  4
@@ -28683,7 +28845,7 @@ function resolveAuthoredIdentityEffect({
28683
28845
  );
28684
28846
  }
28685
28847
  if (normalizeUsername(authenticatedUser.username) !== normalizeUsername(resolvedAccount.username)) {
28686
- return yield* Effect60.fail(
28848
+ return yield* Effect59.fail(
28687
28849
  new AuthoredSyncError(
28688
28850
  `xurl is authenticated as @${authenticatedUser.username}, but selected account ${resolvedAccount.accountId} is @${resolvedAccount.username}. Link the account external_user_id or switch xurl login before syncing authored tweets.`,
28689
28851
  4
@@ -28982,9 +29144,9 @@ function syncAuthoredTweetsEffect({
28982
29144
  sinceId,
28983
29145
  untilId
28984
29146
  }) {
28985
- return Effect60.gen(function* () {
29147
+ return Effect59.gen(function* () {
28986
29148
  if (mode !== "xurl") {
28987
- return yield* Effect60.fail(
29149
+ return yield* Effect59.fail(
28988
29150
  new Error("authored sync only supports --mode xurl")
28989
29151
  );
28990
29152
  }
@@ -29047,7 +29209,7 @@ function syncAuthoredTweetsEffect({
29047
29209
  sourceUser
29048
29210
  })
29049
29211
  ).pipe(
29050
- Effect60.flatMap(
29212
+ Effect59.flatMap(
29051
29213
  () => trySync(() => {
29052
29214
  newestSeenId = maxTweetId(
29053
29215
  newestSeenId,