birdclaw 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/package.json +2 -4
- package/scripts/browser-perf.mjs +27 -0
- package/src/cli/command-context.ts +17 -0
- package/src/cli/register-analysis.ts +500 -0
- package/src/cli/register-compose.ts +40 -0
- package/src/cli/register-graph.ts +132 -0
- package/src/cli/register-inbox.ts +41 -0
- package/src/cli/register-storage.ts +106 -0
- package/src/cli.ts +30 -750
- package/src/components/AccountSwitcher.tsx +7 -15
- package/src/components/AvatarChip.tsx +1 -1
- package/src/components/AvatarPreload.ts +149 -0
- package/src/components/ConversationThread.tsx +4 -0
- package/src/components/EmbeddedTweetCard.tsx +4 -0
- package/src/components/FloatingPreview.tsx +382 -0
- package/src/components/MarkdownCitations.tsx +680 -0
- package/src/components/MarkdownViewer.tsx +7 -715
- package/src/components/ProfileAnalysisClient.ts +191 -0
- package/src/components/ProfileAnalysisStream.tsx +16 -185
- package/src/components/ProfilePreview.tsx +41 -81
- package/src/components/TimelineCard.tsx +18 -2
- package/src/components/TweetArticleCard.tsx +66 -0
- package/src/components/TweetRichText.tsx +33 -2
- package/src/components/links-controller.ts +162 -0
- package/src/components/links-model.ts +198 -0
- package/src/components/network-map-controller.ts +84 -0
- package/src/components/network-map-model.ts +255 -0
- package/src/components/useDebouncedValue.ts +12 -0
- package/src/components/useTimelineRouteData.ts +142 -170
- package/src/lib/analysis-runtime.ts +238 -0
- package/src/lib/api-client.ts +16 -100
- package/src/lib/api-contracts.ts +328 -0
- package/src/lib/archive-finder.ts +38 -0
- package/src/lib/archive-import-plan.ts +102 -0
- package/src/lib/archive-import.ts +170 -239
- package/src/lib/authored-live.ts +77 -182
- package/src/lib/backup.ts +335 -424
- package/src/lib/bird.ts +32 -1
- package/src/lib/blocks-write.ts +30 -26
- package/src/lib/blocks.ts +18 -20
- package/src/lib/database-metrics.ts +88 -0
- package/src/lib/database-migrations.ts +34 -0
- package/src/lib/database-schema.ts +312 -0
- package/src/lib/database-writer.ts +69 -0
- package/src/lib/db.ts +141 -334
- package/src/lib/dm-read-model.ts +533 -0
- package/src/lib/dms-live.ts +34 -97
- package/src/lib/follow-graph.ts +17 -27
- package/src/lib/import-repository.ts +138 -0
- package/src/lib/inbox.ts +2 -1
- package/src/lib/live-sync-engine.ts +209 -0
- package/src/lib/live-transport-gateway.ts +128 -0
- package/src/lib/mention-threads-live.ts +90 -176
- package/src/lib/mentions-export.ts +1 -1
- package/src/lib/mentions-live.ts +57 -225
- package/src/lib/moderation-target.ts +15 -4
- package/src/lib/moderation-write.ts +1 -1
- package/src/lib/mutes-write.ts +30 -26
- package/src/lib/openai-response-runtime.ts +251 -0
- package/src/lib/paginated-sync.ts +93 -0
- package/src/lib/period-digest.ts +116 -304
- package/src/lib/profile-analysis.ts +37 -111
- package/src/lib/queries.ts +6 -2380
- package/src/lib/query-actions.ts +437 -0
- package/src/lib/query-client.tsx +47 -0
- package/src/lib/query-read-model-shared.ts +52 -0
- package/src/lib/query-read-models.ts +5 -0
- package/src/lib/query-resource.ts +41 -0
- package/src/lib/query-status.ts +164 -0
- package/src/lib/research.ts +1 -1
- package/src/lib/runtime-services.ts +20 -0
- package/src/lib/search-discussion.ts +75 -279
- package/src/lib/server-runtime-services.ts +30 -0
- package/src/lib/sqlite.ts +53 -14
- package/src/lib/streaming-ingestion.ts +240 -0
- package/src/lib/sync-cache.ts +6 -1
- package/src/lib/sync-plan.ts +175 -0
- package/src/lib/timeline-collections-live.ts +83 -256
- package/src/lib/timeline-live.ts +86 -235
- package/src/lib/timeline-read-model.ts +1191 -0
- package/src/lib/tweet-render.ts +78 -0
- package/src/lib/tweet-repository.ts +156 -0
- package/src/lib/tweet-search-live.ts +63 -166
- package/src/lib/types.ts +8 -0
- package/src/lib/ui.ts +1 -1
- package/src/lib/web-sync.ts +67 -50
- package/src/lib/whois.ts +2 -1
- package/src/router.tsx +1 -1
- package/src/routes/__root.tsx +11 -21
- package/src/routes/api/action.tsx +1 -1
- package/src/routes/api/conversation.tsx +1 -1
- package/src/routes/api/query.tsx +32 -26
- package/src/routes/api/status.tsx +6 -2
- package/src/routes/api/sync.tsx +5 -2
- package/src/routes/blocks.tsx +97 -131
- package/src/routes/data-sources.tsx +17 -25
- package/src/routes/dms.tsx +168 -167
- package/src/routes/inbox.tsx +63 -57
- package/src/routes/links.tsx +31 -329
- package/src/routes/network-map.tsx +41 -344
- package/src/routes/rate-limits.tsx +17 -21
- package/vite.config.ts +0 -2
|
@@ -8,11 +8,16 @@ import { runEffectPromise, tryPromise } from "./effect-runtime";
|
|
|
8
8
|
import type { ArchiveCandidate } from "./types";
|
|
9
9
|
|
|
10
10
|
const execAsync = promisify(exec);
|
|
11
|
+
const ARCHIVE_DISCOVERY_CACHE_MS = 5 * 60_000;
|
|
11
12
|
const ARCHIVE_NAME_PATTERNS = [
|
|
12
13
|
/^twitter-.*\.zip$/i,
|
|
13
14
|
/^x-.*\.zip$/i,
|
|
14
15
|
/archive.*\.zip$/i,
|
|
15
16
|
];
|
|
17
|
+
let archiveDiscoveryCache:
|
|
18
|
+
| { value: ArchiveCandidate[]; updatedAt: number }
|
|
19
|
+
| undefined;
|
|
20
|
+
let archiveDiscoveryInFlight: Promise<ArchiveCandidate[]> | undefined;
|
|
16
21
|
|
|
17
22
|
function formatFileSize(bytes: number): string {
|
|
18
23
|
const units = ["B", "KB", "MB", "GB"];
|
|
@@ -142,6 +147,39 @@ export function findArchivesEffect(): Effect.Effect<
|
|
|
142
147
|
});
|
|
143
148
|
}
|
|
144
149
|
|
|
150
|
+
export function findArchivesCachedEffect(): Effect.Effect<
|
|
151
|
+
ArchiveCandidate[],
|
|
152
|
+
unknown
|
|
153
|
+
> {
|
|
154
|
+
return Effect.suspend(() => {
|
|
155
|
+
const cached = archiveDiscoveryCache;
|
|
156
|
+
if (cached && Date.now() - cached.updatedAt < ARCHIVE_DISCOVERY_CACHE_MS) {
|
|
157
|
+
return Effect.succeed(cached.value);
|
|
158
|
+
}
|
|
159
|
+
if (archiveDiscoveryInFlight) {
|
|
160
|
+
return Effect.promise(() => archiveDiscoveryInFlight!);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const request = runEffectPromise(findArchivesEffect())
|
|
164
|
+
.then((value) => {
|
|
165
|
+
archiveDiscoveryCache = { value, updatedAt: Date.now() };
|
|
166
|
+
return value;
|
|
167
|
+
})
|
|
168
|
+
.finally(() => {
|
|
169
|
+
if (archiveDiscoveryInFlight === request) {
|
|
170
|
+
archiveDiscoveryInFlight = undefined;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
archiveDiscoveryInFlight = request;
|
|
174
|
+
return Effect.promise(() => request);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
145
178
|
export function findArchives(): Promise<ArchiveCandidate[]> {
|
|
146
179
|
return runEffectPromise(findArchivesEffect());
|
|
147
180
|
}
|
|
181
|
+
|
|
182
|
+
export function clearArchiveFinderCacheForTests() {
|
|
183
|
+
archiveDiscoveryCache = undefined;
|
|
184
|
+
archiveDiscoveryInFlight = undefined;
|
|
185
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export type ArchiveTweetRow = {
|
|
2
|
+
id: string;
|
|
3
|
+
kind: "home" | "like" | "bookmark";
|
|
4
|
+
authorProfileId: string;
|
|
5
|
+
text: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
isReplied: number;
|
|
8
|
+
replyToId: string | null;
|
|
9
|
+
likeCount: number;
|
|
10
|
+
mediaCount: number;
|
|
11
|
+
bookmarked: number;
|
|
12
|
+
liked: number;
|
|
13
|
+
entitiesJson: string;
|
|
14
|
+
mediaJson: string;
|
|
15
|
+
quotedTweetId: string | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ArchiveCollectionRow = {
|
|
19
|
+
tweetId: string;
|
|
20
|
+
kind: "likes" | "bookmarks";
|
|
21
|
+
collectedAt: string | null;
|
|
22
|
+
source: string;
|
|
23
|
+
rawJson: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ArchiveProfileRow = {
|
|
27
|
+
id: string;
|
|
28
|
+
handle: string;
|
|
29
|
+
displayName: string;
|
|
30
|
+
bio: string;
|
|
31
|
+
followersCount: number;
|
|
32
|
+
followingCount: number;
|
|
33
|
+
publicMetricsJson: string;
|
|
34
|
+
avatarHue: number;
|
|
35
|
+
avatarUrl: string | null;
|
|
36
|
+
location: string | null;
|
|
37
|
+
url: string | null;
|
|
38
|
+
verifiedType: string | null;
|
|
39
|
+
entitiesJson: string;
|
|
40
|
+
rawJson: string;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type ArchiveConversationRow = {
|
|
45
|
+
id: string;
|
|
46
|
+
title: string;
|
|
47
|
+
accountId: string;
|
|
48
|
+
participantProfileId: string;
|
|
49
|
+
lastMessageAt: string;
|
|
50
|
+
unreadCount: number;
|
|
51
|
+
needsReply: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type ArchiveMessageRow = {
|
|
55
|
+
id: string;
|
|
56
|
+
conversationId: string;
|
|
57
|
+
senderProfileId: string;
|
|
58
|
+
text: string;
|
|
59
|
+
createdAt: string;
|
|
60
|
+
direction: "inbound" | "outbound";
|
|
61
|
+
mediaCount: number;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type ArchiveFollowRow = {
|
|
65
|
+
profileId: string;
|
|
66
|
+
externalUserId: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export class ArchiveImportPlan {
|
|
70
|
+
readonly mentionDirectory = new Map<
|
|
71
|
+
string,
|
|
72
|
+
{ handle?: string; displayName?: string }
|
|
73
|
+
>();
|
|
74
|
+
readonly tweets: ArchiveTweetRow[] = [];
|
|
75
|
+
readonly collections: ArchiveCollectionRow[] = [];
|
|
76
|
+
readonly profiles = new Map<string, ArchiveProfileRow>();
|
|
77
|
+
readonly conversations = new Map<string, ArchiveConversationRow>();
|
|
78
|
+
readonly dmMessages: ArchiveMessageRow[] = [];
|
|
79
|
+
readonly followers: ArchiveFollowRow[] = [];
|
|
80
|
+
readonly following: ArchiveFollowRow[] = [];
|
|
81
|
+
readonly followerIds = new Set<string>();
|
|
82
|
+
readonly followingIds = new Set<string>();
|
|
83
|
+
|
|
84
|
+
private readonly tweetsById = new Map<string, ArchiveTweetRow>();
|
|
85
|
+
|
|
86
|
+
addTweet(row: ArchiveTweetRow) {
|
|
87
|
+
const existing = this.tweetsById.get(row.id);
|
|
88
|
+
if (existing) {
|
|
89
|
+
existing.bookmarked = Math.max(existing.bookmarked, row.bookmarked);
|
|
90
|
+
existing.liked = Math.max(existing.liked, row.liked);
|
|
91
|
+
if (!existing.text && row.text) existing.text = row.text;
|
|
92
|
+
return existing;
|
|
93
|
+
}
|
|
94
|
+
this.tweets.push(row);
|
|
95
|
+
this.tweetsById.set(row.id, row);
|
|
96
|
+
return row;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getTweet(id: string) {
|
|
100
|
+
return this.tweetsById.get(id);
|
|
101
|
+
}
|
|
102
|
+
}
|