birdclaw 0.5.1 → 0.7.0
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 +92 -1
- package/README.md +75 -5
- package/package.json +8 -2
- package/scripts/browser-perf.mjs +1 -0
- package/scripts/start-test-server.mjs +16 -3
- package/src/cli.ts +812 -37
- package/src/components/AccountSwitcher.tsx +186 -0
- package/src/components/AppNav.tsx +37 -7
- package/src/components/AvatarChip.tsx +9 -3
- package/src/components/DmWorkspace.tsx +18 -8
- package/src/components/LinkPreviewCard.tsx +40 -18
- package/src/components/MarkdownViewer.tsx +818 -0
- package/src/components/ProfileAnalysisStream.tsx +428 -0
- package/src/components/ProfilePreview.tsx +120 -9
- package/src/components/SavedTimelineView.tsx +30 -8
- package/src/components/SyncNowButton.tsx +60 -25
- package/src/components/ThemeSlider.tsx +55 -50
- package/src/components/TimelineCard.tsx +240 -92
- package/src/components/TimelineRouteFrame.tsx +38 -8
- package/src/components/TweetMediaGrid.tsx +87 -38
- package/src/components/TweetRichText.tsx +45 -17
- package/src/components/account-selection.ts +64 -0
- package/src/components/useTimelineRouteData.ts +97 -13
- package/src/lib/account-sync-job.ts +666 -0
- package/src/lib/actions-transport.ts +216 -146
- package/src/lib/api-client.ts +128 -53
- package/src/lib/archive-finder.ts +78 -63
- package/src/lib/archive-import.ts +1593 -1291
- package/src/lib/authored-live.ts +262 -204
- package/src/lib/avatar-cache.ts +208 -43
- package/src/lib/backup.ts +1536 -954
- package/src/lib/bird-actions.ts +139 -57
- package/src/lib/bird-command.ts +101 -28
- package/src/lib/bird.ts +582 -194
- package/src/lib/blocklist.ts +40 -23
- package/src/lib/blocks-write.ts +129 -80
- package/src/lib/blocks.ts +165 -97
- package/src/lib/bookmark-sync-job.ts +250 -160
- package/src/lib/config.ts +35 -2
- package/src/lib/conversation-surface.ts +79 -48
- package/src/lib/data-sources.ts +219 -0
- package/src/lib/db.ts +95 -4
- package/src/lib/dms-live.ts +720 -66
- package/src/lib/effect-runtime.ts +45 -0
- package/src/lib/follow-graph.ts +224 -180
- package/src/lib/geocoding.ts +296 -0
- package/src/lib/http-effect.ts +222 -0
- package/src/lib/inbox.ts +74 -43
- package/src/lib/link-index.ts +88 -76
- package/src/lib/link-insights.ts +24 -0
- package/src/lib/link-preview-metadata.ts +472 -52
- package/src/lib/location.ts +137 -0
- package/src/lib/media-fetch.ts +286 -213
- package/src/lib/mention-threads-live.ts +445 -288
- package/src/lib/mentions-live.ts +549 -354
- package/src/lib/moderation-target.ts +102 -65
- package/src/lib/moderation-write.ts +77 -18
- package/src/lib/mutes-write.ts +129 -80
- package/src/lib/mutes.ts +8 -1
- package/src/lib/network-map.ts +382 -0
- package/src/lib/openai.ts +84 -53
- package/src/lib/period-digest.ts +1317 -0
- package/src/lib/profile-affiliation-hydration.ts +93 -54
- package/src/lib/profile-analysis.ts +1272 -0
- package/src/lib/profile-bio-entities.ts +1 -1
- package/src/lib/profile-hydration.ts +124 -72
- package/src/lib/profile-replies.ts +60 -43
- package/src/lib/profile-resolver.ts +402 -294
- package/src/lib/queries.ts +983 -203
- package/src/lib/research.ts +165 -120
- package/src/lib/search-discussion.ts +1016 -0
- package/src/lib/sqlite.ts +1 -0
- package/src/lib/timeline-collections-live.ts +204 -167
- package/src/lib/timeline-live.ts +325 -51
- package/src/lib/tweet-account-edges.ts +2 -0
- package/src/lib/tweet-lookup.ts +30 -19
- package/src/lib/tweet-render.ts +141 -1
- package/src/lib/tweet-search-live.ts +565 -0
- package/src/lib/types.ts +75 -3
- package/src/lib/ui.ts +31 -8
- package/src/lib/url-expansion.ts +226 -55
- package/src/lib/url-safety.ts +220 -0
- package/src/lib/web-sync.ts +222 -149
- package/src/lib/whois.ts +166 -147
- package/src/lib/x-web.ts +102 -71
- package/src/lib/xurl-rate-limits.ts +267 -0
- package/src/lib/xurl.ts +1185 -405
- package/src/routeTree.gen.ts +273 -0
- package/src/routes/__root.tsx +24 -5
- package/src/routes/api/action.tsx +127 -78
- package/src/routes/api/avatar.tsx +39 -30
- package/src/routes/api/blocks.tsx +26 -23
- package/src/routes/api/conversation.tsx +25 -14
- package/src/routes/api/data-sources.tsx +24 -0
- package/src/routes/api/inbox.tsx +27 -21
- package/src/routes/api/link-insights.tsx +31 -25
- package/src/routes/api/link-preview.tsx +25 -21
- package/src/routes/api/network-map.tsx +55 -0
- package/src/routes/api/period-digest.tsx +133 -0
- package/src/routes/api/profile-analysis.tsx +152 -0
- package/src/routes/api/profile-hydrate.tsx +31 -28
- package/src/routes/api/query.tsx +80 -55
- package/src/routes/api/search-discussion.tsx +169 -0
- package/src/routes/api/status.tsx +18 -10
- package/src/routes/api/sync.tsx +75 -29
- package/src/routes/api/xurl-rate-limits.tsx +24 -0
- package/src/routes/data-sources.tsx +255 -0
- package/src/routes/discuss.tsx +419 -0
- package/src/routes/dms.tsx +95 -28
- package/src/routes/inbox.tsx +32 -19
- package/src/routes/network-map.tsx +1035 -0
- package/src/routes/profile-analyze.tsx +112 -0
- package/src/routes/profiles.$handle.tsx +228 -0
- package/src/routes/rate-limits.tsx +309 -0
- package/src/routes/today.tsx +455 -0
- package/src/styles.css +22 -0
package/src/lib/link-index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
1
2
|
import { getNativeDb } from "./db";
|
|
3
|
+
import { runEffectPromise, tryPromise } from "./effect-runtime";
|
|
2
4
|
import type { Database } from "./sqlite";
|
|
3
5
|
import {
|
|
4
6
|
normalizeUrlExpansionForIndex,
|
|
@@ -246,11 +248,19 @@ function rebuildOccurrences(
|
|
|
246
248
|
return { occurrences, entityExpansions };
|
|
247
249
|
}
|
|
248
250
|
|
|
249
|
-
|
|
251
|
+
function expandWithConcurrencyEffect(
|
|
250
252
|
db: Database,
|
|
251
253
|
urls: string[],
|
|
252
254
|
options: LinkBackfillOptions,
|
|
253
|
-
)
|
|
255
|
+
): Effect.Effect<
|
|
256
|
+
{
|
|
257
|
+
networkExpansions: number;
|
|
258
|
+
cacheExpansions: number;
|
|
259
|
+
misses: number;
|
|
260
|
+
errors: number;
|
|
261
|
+
},
|
|
262
|
+
unknown
|
|
263
|
+
> {
|
|
254
264
|
const concurrency = Math.max(
|
|
255
265
|
1,
|
|
256
266
|
Math.min(options.concurrency ?? DEFAULT_EXPAND_CONCURRENCY, 64),
|
|
@@ -261,62 +271,57 @@ async function expandWithConcurrency(
|
|
|
261
271
|
misses: 0,
|
|
262
272
|
errors: 0,
|
|
263
273
|
};
|
|
264
|
-
let nextIndex = 0;
|
|
265
274
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const result = (
|
|
274
|
-
await expandUrls([url], {
|
|
275
|
+
return Effect.forEach(
|
|
276
|
+
urls,
|
|
277
|
+
(url) =>
|
|
278
|
+
tryPromise(() =>
|
|
279
|
+
expandUrls([url], {
|
|
275
280
|
refresh: options.refresh,
|
|
276
281
|
fetchImpl: options.fetchImpl,
|
|
277
282
|
timeoutMs: options.timeoutMs,
|
|
278
|
-
})
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
);
|
|
298
|
-
return counts;
|
|
283
|
+
}),
|
|
284
|
+
).pipe(
|
|
285
|
+
Effect.map((results) => {
|
|
286
|
+
const result = results[0]!;
|
|
287
|
+
if (result.source === "network") {
|
|
288
|
+
counts.networkExpansions++;
|
|
289
|
+
} else {
|
|
290
|
+
counts.cacheExpansions++;
|
|
291
|
+
}
|
|
292
|
+
if (result.status === "miss") {
|
|
293
|
+
counts.misses++;
|
|
294
|
+
}
|
|
295
|
+
if (result.status === "error") {
|
|
296
|
+
counts.errors++;
|
|
297
|
+
}
|
|
298
|
+
upsertUrlExpansion(db, normalizeUrlExpansionForIndex(result));
|
|
299
|
+
}),
|
|
300
|
+
),
|
|
301
|
+
{ concurrency },
|
|
302
|
+
).pipe(Effect.as(counts));
|
|
299
303
|
}
|
|
300
304
|
|
|
301
|
-
export
|
|
305
|
+
export function backfillLinkIndexEffect(
|
|
302
306
|
options: LinkBackfillOptions = {},
|
|
303
|
-
):
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
307
|
+
): Effect.Effect<LinkBackfillResult, unknown> {
|
|
308
|
+
return Effect.gen(function* () {
|
|
309
|
+
const db = getNativeDb({ seedDemoData: false });
|
|
310
|
+
const { occurrences, entityExpansions } = rebuildOccurrences(
|
|
311
|
+
db,
|
|
312
|
+
Boolean(options.includeAllUrls),
|
|
313
|
+
options.source,
|
|
314
|
+
);
|
|
315
|
+
const limit =
|
|
316
|
+
typeof options.limit === "number" && Number.isFinite(options.limit)
|
|
317
|
+
? Math.max(0, Math.trunc(options.limit))
|
|
318
|
+
: undefined;
|
|
319
|
+
const needsExpansionClause = options.refresh
|
|
320
|
+
? "1 = 1"
|
|
321
|
+
: "(e.short_url is null or e.status in ('error', 'miss'))";
|
|
322
|
+
|
|
323
|
+
const urlsToExpand = db
|
|
324
|
+
.prepare(`
|
|
320
325
|
select distinct o.short_url
|
|
321
326
|
from link_occurrences o
|
|
322
327
|
left join url_expansions e on e.short_url = o.short_url
|
|
@@ -325,43 +330,50 @@ export async function backfillLinkIndex(
|
|
|
325
330
|
order by o.short_url
|
|
326
331
|
${limit === undefined ? "" : "limit ?"}
|
|
327
332
|
`)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
333
|
+
.all(
|
|
334
|
+
...(options.source ? [options.source] : []),
|
|
335
|
+
...(limit === undefined ? [] : [limit]),
|
|
336
|
+
) as Array<{
|
|
337
|
+
short_url: string;
|
|
338
|
+
}>;
|
|
339
|
+
|
|
340
|
+
const expansionCounts = yield* expandWithConcurrencyEffect(
|
|
341
|
+
db,
|
|
342
|
+
urlsToExpand.map((row) => row.short_url),
|
|
343
|
+
options,
|
|
344
|
+
);
|
|
340
345
|
|
|
341
|
-
|
|
342
|
-
|
|
346
|
+
const uniqueUrls = db
|
|
347
|
+
.prepare(`
|
|
343
348
|
select count(distinct short_url) as count
|
|
344
349
|
from link_occurrences
|
|
345
350
|
${options.source ? "where source_kind = ?" : ""}
|
|
346
351
|
`)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
352
|
+
.get(...(options.source ? [options.source] : [])) as { count: number };
|
|
353
|
+
const remaining = db
|
|
354
|
+
.prepare(`
|
|
350
355
|
select count(distinct o.short_url) as count
|
|
351
356
|
from link_occurrences o
|
|
352
357
|
left join url_expansions e on e.short_url = o.short_url
|
|
353
358
|
where (e.short_url is null or e.status in ('error', 'miss'))
|
|
354
359
|
${options.source ? "and o.source_kind = ?" : ""}
|
|
355
360
|
`)
|
|
356
|
-
|
|
361
|
+
.get(...(options.source ? [options.source] : [])) as { count: number };
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
occurrences,
|
|
365
|
+
uniqueUrls: Number(uniqueUrls.count),
|
|
366
|
+
entityExpansions,
|
|
367
|
+
...expansionCounts,
|
|
368
|
+
remainingUnexpanded: Number(remaining.count),
|
|
369
|
+
};
|
|
370
|
+
});
|
|
371
|
+
}
|
|
357
372
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
...expansionCounts,
|
|
363
|
-
remainingUnexpanded: Number(remaining.count),
|
|
364
|
-
};
|
|
373
|
+
export function backfillLinkIndex(
|
|
374
|
+
options: LinkBackfillOptions = {},
|
|
375
|
+
): Promise<LinkBackfillResult> {
|
|
376
|
+
return runEffectPromise(backfillLinkIndexEffect(options));
|
|
365
377
|
}
|
|
366
378
|
|
|
367
379
|
function likePattern(value: string) {
|
package/src/lib/link-insights.ts
CHANGED
|
@@ -635,6 +635,30 @@ export function getLinkInsights(
|
|
|
635
635
|
conditions.push("o.source_kind = ?");
|
|
636
636
|
params.push(source);
|
|
637
637
|
}
|
|
638
|
+
if (query.account && query.account !== "all") {
|
|
639
|
+
conditions.push(`(
|
|
640
|
+
(o.source_kind = 'dm' and o.account_id = ?)
|
|
641
|
+
or (
|
|
642
|
+
o.source_kind = 'tweet'
|
|
643
|
+
and (
|
|
644
|
+
o.account_id = ?
|
|
645
|
+
or exists (
|
|
646
|
+
select 1
|
|
647
|
+
from tweet_account_edges edge
|
|
648
|
+
where edge.account_id = ?
|
|
649
|
+
and edge.tweet_id = o.source_id
|
|
650
|
+
)
|
|
651
|
+
or exists (
|
|
652
|
+
select 1
|
|
653
|
+
from tweet_collections collection
|
|
654
|
+
where collection.account_id = ?
|
|
655
|
+
and collection.tweet_id = o.source_id
|
|
656
|
+
)
|
|
657
|
+
)
|
|
658
|
+
)
|
|
659
|
+
)`);
|
|
660
|
+
params.push(query.account, query.account, query.account, query.account);
|
|
661
|
+
}
|
|
638
662
|
if (kind === "videos") {
|
|
639
663
|
addVideoUrlPrefilter(conditions);
|
|
640
664
|
}
|