birdclaw 0.8.2 → 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 +16 -0
- package/package.json +2 -1
- 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/MarkdownCitations.tsx +680 -0
- package/src/components/MarkdownViewer.tsx +8 -674
- package/src/components/ProfileAnalysisClient.ts +191 -0
- package/src/components/ProfileAnalysisStream.tsx +16 -185
- package/src/components/ProfilePreview.tsx +2 -0
- 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/useTimelineRouteData.ts +105 -235
- package/src/lib/analysis-runtime.ts +238 -0
- package/src/lib/api-client.ts +16 -215
- package/src/lib/api-contracts.ts +328 -0
- package/src/lib/archive-import-plan.ts +102 -0
- package/src/lib/archive-import.ts +170 -239
- package/src/lib/authored-live.ts +75 -120
- package/src/lib/backup.ts +335 -424
- 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 +84 -330
- 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 -177
- package/src/lib/mentions-export.ts +1 -1
- package/src/lib/mentions-live.ts +57 -181
- 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 +36 -110
- package/src/lib/queries.ts +6 -2381
- 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 +48 -12
- 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 -257
- package/src/lib/timeline-live.ts +86 -236
- package/src/lib/timeline-read-model.ts +1191 -0
- package/src/lib/tweet-repository.ts +156 -0
- package/src/lib/tweet-search-live.ts +63 -167
- package/src/lib/web-sync.ts +67 -50
- package/src/lib/whois.ts +2 -1
- package/src/routes/__root.tsx +11 -8
- 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 -4
- 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 +167 -184
- package/src/routes/inbox.tsx +63 -57
- package/src/routes/links.tsx +31 -394
- package/src/routes/network-map.tsx +41 -344
- package/src/routes/rate-limits.tsx +17 -21
- package/src/lib/client-cache.ts +0 -109
package/src/lib/authored-live.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Database } from "./sqlite";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
+
import { databaseWriteEffect } from "./database-writer";
|
|
3
4
|
import { getNativeDb } from "./db";
|
|
4
|
-
import { runEffectPromise
|
|
5
|
+
import { runEffectPromise } from "./effect-runtime";
|
|
6
|
+
import { liveTransportGateway } from "./live-transport-gateway";
|
|
7
|
+
import { resolveLiveSyncAccount } from "./live-sync-engine";
|
|
5
8
|
import { readSyncCache, writeSyncCache } from "./sync-cache";
|
|
9
|
+
import { runSyncPlanEffect } from "./sync-plan";
|
|
6
10
|
import { tweetEntitiesFromXurl } from "./tweet-render";
|
|
7
11
|
import type {
|
|
8
12
|
TweetEntities,
|
|
@@ -21,11 +25,6 @@ import {
|
|
|
21
25
|
ensureStubProfileForXUser,
|
|
22
26
|
upsertProfileFromXUser,
|
|
23
27
|
} from "./x-profile";
|
|
24
|
-
import {
|
|
25
|
-
getTransportStatus,
|
|
26
|
-
listUserTweets,
|
|
27
|
-
lookupAuthenticatedUser,
|
|
28
|
-
} from "./xurl";
|
|
29
28
|
|
|
30
29
|
export type AuthoredSyncMode = "xurl";
|
|
31
30
|
|
|
@@ -360,43 +359,6 @@ function getOldestTweetId(tweets: XurlMentionData[]) {
|
|
|
360
359
|
}, null);
|
|
361
360
|
}
|
|
362
361
|
|
|
363
|
-
function resolveAccount(db: Database, accountId?: string) {
|
|
364
|
-
const row = accountId
|
|
365
|
-
? (db
|
|
366
|
-
.prepare(
|
|
367
|
-
"select id, handle, external_user_id from accounts where id = ?",
|
|
368
|
-
)
|
|
369
|
-
.get(accountId) as
|
|
370
|
-
| { id: string; handle: string; external_user_id: string | null }
|
|
371
|
-
| undefined)
|
|
372
|
-
: (db
|
|
373
|
-
.prepare(
|
|
374
|
-
`
|
|
375
|
-
select id, handle, external_user_id
|
|
376
|
-
from accounts
|
|
377
|
-
order by is_default desc, created_at asc
|
|
378
|
-
limit 1
|
|
379
|
-
`,
|
|
380
|
-
)
|
|
381
|
-
.get() as
|
|
382
|
-
| { id: string; handle: string; external_user_id: string | null }
|
|
383
|
-
| undefined);
|
|
384
|
-
|
|
385
|
-
if (!row) {
|
|
386
|
-
throw new Error(`Unknown account: ${accountId ?? "default"}`);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return {
|
|
390
|
-
accountId: row.id,
|
|
391
|
-
username: row.handle.replace(/^@/, ""),
|
|
392
|
-
externalUserId:
|
|
393
|
-
typeof row.external_user_id === "string" &&
|
|
394
|
-
row.external_user_id.length > 0
|
|
395
|
-
? row.external_user_id
|
|
396
|
-
: undefined,
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
|
|
400
362
|
function normalizeUsername(value: string) {
|
|
401
363
|
return value.replace(/^@/, "").trim().toLowerCase();
|
|
402
364
|
}
|
|
@@ -444,12 +406,14 @@ function resolveAuthoredIdentityEffect({
|
|
|
444
406
|
db: Database;
|
|
445
407
|
}) {
|
|
446
408
|
return Effect.gen(function* () {
|
|
447
|
-
const status = yield*
|
|
409
|
+
const status = yield* liveTransportGateway.xurl.getTransportStatus();
|
|
448
410
|
if (status.availableTransport !== "xurl") {
|
|
449
411
|
return yield* Effect.fail(new AuthoredSyncError(status.statusText, 4));
|
|
450
412
|
}
|
|
451
413
|
|
|
452
|
-
const resolvedAccount = yield* trySync(() =>
|
|
414
|
+
const resolvedAccount = yield* trySync(() =>
|
|
415
|
+
resolveLiveSyncAccount(db, account),
|
|
416
|
+
);
|
|
453
417
|
if (resolvedAccount.externalUserId) {
|
|
454
418
|
return {
|
|
455
419
|
...resolvedAccount,
|
|
@@ -458,7 +422,8 @@ function resolveAuthoredIdentityEffect({
|
|
|
458
422
|
};
|
|
459
423
|
}
|
|
460
424
|
|
|
461
|
-
const authenticated =
|
|
425
|
+
const authenticated =
|
|
426
|
+
yield* liveTransportGateway.xurl.lookupAuthenticatedUser();
|
|
462
427
|
const authenticatedUser = userFromAuthenticatedPayload(authenticated);
|
|
463
428
|
if (!authenticatedUser?.id) {
|
|
464
429
|
return yield* Effect.fail(
|
|
@@ -911,7 +876,7 @@ export function syncAuthoredTweetsEffect({
|
|
|
911
876
|
archiveSinceSeed ??
|
|
912
877
|
(untilId ? persistedUntilSinceId : cursor.sinceId) ??
|
|
913
878
|
null;
|
|
914
|
-
|
|
879
|
+
const initialToken = usePersistedForward
|
|
915
880
|
? cursor.token
|
|
916
881
|
: usePersistedUntil
|
|
917
882
|
? cursor.token
|
|
@@ -919,19 +884,20 @@ export function syncAuthoredTweetsEffect({
|
|
|
919
884
|
let newestSeenId = usePersistedForward
|
|
920
885
|
? maxTweetId(cursor.sinceId, cursor.pendingNewestId)
|
|
921
886
|
: cursor.sinceId;
|
|
922
|
-
const pages: XurlUserTweetsResponse[] = [];
|
|
923
887
|
const sourceUser = toFallbackUser({
|
|
924
888
|
userId: identity.userId,
|
|
925
889
|
username: identity.username,
|
|
926
890
|
authenticatedUser: identity.authenticatedUser,
|
|
927
891
|
});
|
|
928
|
-
let pageCount = 0;
|
|
929
892
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
893
|
+
const planResult = yield* runSyncPlanEffect({
|
|
894
|
+
allowPartialFailure: true,
|
|
895
|
+
initialCursor: initialToken,
|
|
896
|
+
maxPages: parsedMaxPages ?? undefined,
|
|
897
|
+
fetchPage: ({ cursor: paginationToken }) =>
|
|
898
|
+
liveTransportGateway.xurl.listUserTweets(identity.userId, {
|
|
933
899
|
maxResults: pageLimit,
|
|
934
|
-
paginationToken
|
|
900
|
+
paginationToken,
|
|
935
901
|
excludeRetweets: false,
|
|
936
902
|
sinceId: effectiveSinceId ?? undefined,
|
|
937
903
|
untilId,
|
|
@@ -942,77 +908,66 @@ export function syncAuthoredTweetsEffect({
|
|
|
942
908
|
auth: "oauth2",
|
|
943
909
|
username: identity.username,
|
|
944
910
|
}),
|
|
945
|
-
).
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
),
|
|
950
|
-
);
|
|
951
|
-
|
|
952
|
-
if (!fetched.ok) {
|
|
953
|
-
if (pages.length === 0) {
|
|
954
|
-
return yield* Effect.fail(fetched.error);
|
|
955
|
-
}
|
|
956
|
-
const payload = mergePages({
|
|
957
|
-
pages,
|
|
958
|
-
userId: identity.userId,
|
|
959
|
-
nextToken: nextToken ?? null,
|
|
960
|
-
});
|
|
961
|
-
return buildResult({
|
|
962
|
-
accountId: identity.accountId,
|
|
911
|
+
getNextCursor: (page) => page.nextToken,
|
|
912
|
+
persistPage: ({ page, nextCursor: pendingToken }) => {
|
|
913
|
+
const pagePayload = mergePages({
|
|
914
|
+
pages: [page],
|
|
963
915
|
userId: identity.userId,
|
|
964
|
-
|
|
965
|
-
nextSinceId: untilId ? cursor.sinceId : effectiveSinceId,
|
|
966
|
-
nextToken: nextToken ?? null,
|
|
967
|
-
pageCount,
|
|
968
|
-
payload,
|
|
969
|
-
partial: true,
|
|
970
|
-
error: formatError(fetched.error),
|
|
916
|
+
nextToken: page.nextToken,
|
|
971
917
|
});
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
nextToken: page.nextToken,
|
|
979
|
-
});
|
|
980
|
-
yield* trySync(() =>
|
|
981
|
-
mergeAuthoredPayloadIntoLocalStore({
|
|
982
|
-
db,
|
|
983
|
-
accountId: identity.accountId,
|
|
984
|
-
payload: pagePayload,
|
|
985
|
-
sourceUser,
|
|
986
|
-
}),
|
|
987
|
-
);
|
|
988
|
-
pages.push(page);
|
|
989
|
-
pageCount += 1;
|
|
990
|
-
newestSeenId = maxTweetId(newestSeenId, pagePayload.meta.newest_id);
|
|
991
|
-
nextToken = page.nextToken ?? undefined;
|
|
992
|
-
|
|
993
|
-
const pendingToken = nextToken;
|
|
994
|
-
if (pendingToken && untilId) {
|
|
995
|
-
yield* trySync(() =>
|
|
996
|
-
writePendingUntilCursor(db, identity.accountId, {
|
|
997
|
-
sinceId: cursor.sinceId,
|
|
998
|
-
token: pendingToken,
|
|
999
|
-
untilId,
|
|
1000
|
-
requestedSinceId: effectiveSinceId,
|
|
1001
|
-
}),
|
|
1002
|
-
);
|
|
1003
|
-
} else if (pendingToken) {
|
|
1004
|
-
yield* trySync(() =>
|
|
1005
|
-
writePendingForwardCursor(db, identity.accountId, {
|
|
1006
|
-
sinceId: effectiveSinceId,
|
|
1007
|
-
token: pendingToken,
|
|
1008
|
-
pendingNewestId: newestSeenId,
|
|
918
|
+
return databaseWriteEffect((writeDb) =>
|
|
919
|
+
mergeAuthoredPayloadIntoLocalStore({
|
|
920
|
+
db: writeDb,
|
|
921
|
+
accountId: identity.accountId,
|
|
922
|
+
payload: pagePayload,
|
|
923
|
+
sourceUser,
|
|
1009
924
|
}),
|
|
925
|
+
).pipe(
|
|
926
|
+
Effect.flatMap(() =>
|
|
927
|
+
trySync(() => {
|
|
928
|
+
newestSeenId = maxTweetId(
|
|
929
|
+
newestSeenId,
|
|
930
|
+
pagePayload.meta.newest_id,
|
|
931
|
+
);
|
|
932
|
+
if (pendingToken && untilId) {
|
|
933
|
+
writePendingUntilCursor(db, identity.accountId, {
|
|
934
|
+
sinceId: cursor.sinceId,
|
|
935
|
+
token: pendingToken,
|
|
936
|
+
untilId,
|
|
937
|
+
requestedSinceId: effectiveSinceId,
|
|
938
|
+
});
|
|
939
|
+
} else if (pendingToken) {
|
|
940
|
+
writePendingForwardCursor(db, identity.accountId, {
|
|
941
|
+
sinceId: effectiveSinceId,
|
|
942
|
+
token: pendingToken,
|
|
943
|
+
pendingNewestId: newestSeenId,
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
}),
|
|
947
|
+
),
|
|
1010
948
|
);
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
949
|
+
},
|
|
950
|
+
});
|
|
951
|
+
const pages = planResult.pages;
|
|
952
|
+
const pageCount = pages.length;
|
|
953
|
+
const nextToken = planResult.nextCursor;
|
|
954
|
+
if (planResult.stopReason === "error") {
|
|
955
|
+
const payload = mergePages({
|
|
956
|
+
pages,
|
|
957
|
+
userId: identity.userId,
|
|
958
|
+
nextToken: nextToken ?? null,
|
|
959
|
+
});
|
|
960
|
+
return buildResult({
|
|
961
|
+
accountId: identity.accountId,
|
|
962
|
+
userId: identity.userId,
|
|
963
|
+
effectiveSinceId,
|
|
964
|
+
nextSinceId: untilId ? cursor.sinceId : effectiveSinceId,
|
|
965
|
+
nextToken: nextToken ?? null,
|
|
966
|
+
pageCount,
|
|
967
|
+
payload,
|
|
968
|
+
partial: true,
|
|
969
|
+
error: formatError(planResult.error),
|
|
970
|
+
});
|
|
1016
971
|
}
|
|
1017
972
|
|
|
1018
973
|
const capped = Boolean(nextToken);
|