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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getFollowGraphSummary,
|
|
3
|
+
listFollowEvents,
|
|
4
|
+
listMutuals,
|
|
5
|
+
listNonMutualFollowing,
|
|
6
|
+
listTopFollowers,
|
|
7
|
+
listUnfollowedSince,
|
|
8
|
+
} from "#/lib/follow-graph";
|
|
9
|
+
import type { CliCommandContext } from "./command-context";
|
|
10
|
+
|
|
11
|
+
export function registerGraphCommands({
|
|
12
|
+
program,
|
|
13
|
+
print,
|
|
14
|
+
autoUpdateBeforeRead,
|
|
15
|
+
}: CliCommandContext) {
|
|
16
|
+
const graphCommand = program
|
|
17
|
+
.command("graph")
|
|
18
|
+
.description("Query the local cache-only follow graph");
|
|
19
|
+
|
|
20
|
+
graphCommand
|
|
21
|
+
.command("summary")
|
|
22
|
+
.description(
|
|
23
|
+
"Summarize cached followers, following, mutuals, and snapshots",
|
|
24
|
+
)
|
|
25
|
+
.option("--account <accountId>", "Account id")
|
|
26
|
+
.action(async (options) => {
|
|
27
|
+
await autoUpdateBeforeRead();
|
|
28
|
+
print(getFollowGraphSummary({ account: options.account }), true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
graphCommand
|
|
32
|
+
.command("top-followers")
|
|
33
|
+
.description("List current followers sorted by their follower count")
|
|
34
|
+
.option("--account <accountId>", "Account id")
|
|
35
|
+
.option("--limit <n>", "Limit results", "20")
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
await autoUpdateBeforeRead();
|
|
38
|
+
print(
|
|
39
|
+
listTopFollowers({
|
|
40
|
+
account: options.account,
|
|
41
|
+
limit: Number(options.limit),
|
|
42
|
+
}),
|
|
43
|
+
true,
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
graphCommand
|
|
48
|
+
.command("unfollowed")
|
|
49
|
+
.description("List cached ended follow edges since a date")
|
|
50
|
+
.requiredOption("--date <date>", "YYYY-MM-DD or ISO timestamp")
|
|
51
|
+
.option("--account <accountId>", "Account id")
|
|
52
|
+
.option("--direction <direction>", "followers or following", "followers")
|
|
53
|
+
.option("--limit <n>", "Limit results", "100")
|
|
54
|
+
.action(async (options) => {
|
|
55
|
+
await autoUpdateBeforeRead();
|
|
56
|
+
print(
|
|
57
|
+
listUnfollowedSince({
|
|
58
|
+
account: options.account,
|
|
59
|
+
date: options.date,
|
|
60
|
+
direction:
|
|
61
|
+
options.direction === "following" ? "following" : "followers",
|
|
62
|
+
limit: Number(options.limit),
|
|
63
|
+
}),
|
|
64
|
+
true,
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
graphCommand
|
|
69
|
+
.command("events")
|
|
70
|
+
.description("List cached append-only follow graph events")
|
|
71
|
+
.option("--account <accountId>", "Account id")
|
|
72
|
+
.option("--direction <direction>", "followers or following")
|
|
73
|
+
.option("--kind <kind>", "started or ended")
|
|
74
|
+
.option("--since <date>", "YYYY-MM-DD or ISO timestamp")
|
|
75
|
+
.option("--until <date>", "YYYY-MM-DD or ISO timestamp")
|
|
76
|
+
.option("--limit <n>", "Limit results", "100")
|
|
77
|
+
.action(async (options) => {
|
|
78
|
+
await autoUpdateBeforeRead();
|
|
79
|
+
print(
|
|
80
|
+
listFollowEvents({
|
|
81
|
+
account: options.account,
|
|
82
|
+
direction:
|
|
83
|
+
options.direction === "followers" ||
|
|
84
|
+
options.direction === "following"
|
|
85
|
+
? options.direction
|
|
86
|
+
: undefined,
|
|
87
|
+
kind:
|
|
88
|
+
options.kind === "started" || options.kind === "ended"
|
|
89
|
+
? options.kind
|
|
90
|
+
: undefined,
|
|
91
|
+
since: options.since,
|
|
92
|
+
until: options.until,
|
|
93
|
+
limit: Number(options.limit),
|
|
94
|
+
}),
|
|
95
|
+
true,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
graphCommand
|
|
100
|
+
.command("non-mutual-following")
|
|
101
|
+
.description("List current following who are not current followers")
|
|
102
|
+
.option("--account <accountId>", "Account id")
|
|
103
|
+
.option("--sort <mode>", "followers or handle", "followers")
|
|
104
|
+
.option("--limit <n>", "Limit results", "100")
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
await autoUpdateBeforeRead();
|
|
107
|
+
print(
|
|
108
|
+
listNonMutualFollowing({
|
|
109
|
+
account: options.account,
|
|
110
|
+
sort: options.sort === "handle" ? "handle" : "followers",
|
|
111
|
+
limit: Number(options.limit),
|
|
112
|
+
}),
|
|
113
|
+
true,
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
graphCommand
|
|
118
|
+
.command("mutuals")
|
|
119
|
+
.description("List profiles that are both followers and following")
|
|
120
|
+
.option("--account <accountId>", "Account id")
|
|
121
|
+
.option("--limit <n>", "Limit results", "100")
|
|
122
|
+
.action(async (options) => {
|
|
123
|
+
await autoUpdateBeforeRead();
|
|
124
|
+
print(
|
|
125
|
+
listMutuals({
|
|
126
|
+
account: options.account,
|
|
127
|
+
limit: Number(options.limit),
|
|
128
|
+
}),
|
|
129
|
+
true,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { listInboxItems, scoreInbox } from "#/lib/inbox";
|
|
2
|
+
import type { CliCommandContext } from "./command-context";
|
|
3
|
+
|
|
4
|
+
export function registerInboxCommand({
|
|
5
|
+
program,
|
|
6
|
+
print,
|
|
7
|
+
asJson,
|
|
8
|
+
autoSyncAfterWrite,
|
|
9
|
+
autoUpdateBeforeRead,
|
|
10
|
+
}: CliCommandContext) {
|
|
11
|
+
program
|
|
12
|
+
.command("inbox")
|
|
13
|
+
.option("--kind <kind>", "mixed, mentions, or dms", "mixed")
|
|
14
|
+
.option("--min-score <n>", "Minimum rank", "0")
|
|
15
|
+
.option("--hide-low-signal", "Hide low-signal items")
|
|
16
|
+
.option("--score", "Score top items with OpenAI before listing")
|
|
17
|
+
.option("--limit <n>", "Limit results", "20")
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
await autoUpdateBeforeRead();
|
|
20
|
+
const kind =
|
|
21
|
+
options.kind === "mentions" || options.kind === "dms"
|
|
22
|
+
? options.kind
|
|
23
|
+
: "mixed";
|
|
24
|
+
if (options.score) {
|
|
25
|
+
await scoreInbox({
|
|
26
|
+
kind,
|
|
27
|
+
limit: Number(options.limit),
|
|
28
|
+
});
|
|
29
|
+
await autoSyncAfterWrite();
|
|
30
|
+
}
|
|
31
|
+
print(
|
|
32
|
+
listInboxItems({
|
|
33
|
+
kind,
|
|
34
|
+
minScore: Number(options.minScore),
|
|
35
|
+
hideLowSignal: Boolean(options.hideLowSignal),
|
|
36
|
+
limit: Number(options.limit),
|
|
37
|
+
}),
|
|
38
|
+
asJson(),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
exportBackup,
|
|
3
|
+
importBackup,
|
|
4
|
+
syncBackup,
|
|
5
|
+
validateBackup,
|
|
6
|
+
} from "#/lib/backup";
|
|
7
|
+
import { getBirdclawPaths } from "#/lib/config";
|
|
8
|
+
import { getDatabaseRuntimeMetrics } from "#/lib/database-metrics";
|
|
9
|
+
import { getQueryEnvelope } from "#/lib/queries";
|
|
10
|
+
import type { CliCommandContext } from "./command-context";
|
|
11
|
+
|
|
12
|
+
export function registerStorageCommands({
|
|
13
|
+
program,
|
|
14
|
+
print,
|
|
15
|
+
asJson,
|
|
16
|
+
autoUpdateBeforeRead,
|
|
17
|
+
}: CliCommandContext) {
|
|
18
|
+
program
|
|
19
|
+
.command("db stats")
|
|
20
|
+
.description("Show local storage and dataset stats")
|
|
21
|
+
.action(async () => {
|
|
22
|
+
await autoUpdateBeforeRead();
|
|
23
|
+
const meta = await getQueryEnvelope();
|
|
24
|
+
const paths = getBirdclawPaths();
|
|
25
|
+
print(
|
|
26
|
+
{
|
|
27
|
+
paths,
|
|
28
|
+
database: getDatabaseRuntimeMetrics(),
|
|
29
|
+
stats: meta.stats,
|
|
30
|
+
transport: meta.transport,
|
|
31
|
+
},
|
|
32
|
+
asJson(),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const backupCommand = program
|
|
37
|
+
.command("backup")
|
|
38
|
+
.description("Export, import, and validate Git-friendly text backups");
|
|
39
|
+
|
|
40
|
+
backupCommand
|
|
41
|
+
.command("export")
|
|
42
|
+
.description("Export canonical JSONL backup shards")
|
|
43
|
+
.requiredOption("--repo <path>", "Backup repository/path")
|
|
44
|
+
.option("--commit", "Create a git commit in the backup repo")
|
|
45
|
+
.option("--push", "Push the backup repo after committing")
|
|
46
|
+
.option(
|
|
47
|
+
"--message <message>",
|
|
48
|
+
"Git commit message",
|
|
49
|
+
"archive: update birdclaw backup",
|
|
50
|
+
)
|
|
51
|
+
.option("--no-validate", "Skip post-export validation")
|
|
52
|
+
.action(async (options) => {
|
|
53
|
+
const result = await exportBackup({
|
|
54
|
+
repoPath: options.repo,
|
|
55
|
+
commit: Boolean(options.commit) || Boolean(options.push),
|
|
56
|
+
push: Boolean(options.push),
|
|
57
|
+
message: options.message,
|
|
58
|
+
validate: options.validate,
|
|
59
|
+
});
|
|
60
|
+
print(result, true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
backupCommand
|
|
64
|
+
.command("import <repo>")
|
|
65
|
+
.description("Merge a canonical JSONL backup into the local SQLite store")
|
|
66
|
+
.option("--no-validate", "Skip backup validation before import")
|
|
67
|
+
.option("--replace", "Replace local portable tables instead of merging")
|
|
68
|
+
.action(async (repo, options) => {
|
|
69
|
+
const result = await importBackup({
|
|
70
|
+
repoPath: repo,
|
|
71
|
+
validate: options.validate,
|
|
72
|
+
mode: options.replace ? "replace" : "merge",
|
|
73
|
+
});
|
|
74
|
+
print(result, true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
backupCommand
|
|
78
|
+
.command("sync")
|
|
79
|
+
.description("Pull, merge-import, export, commit, and push a backup repo")
|
|
80
|
+
.requiredOption("--repo <path>", "Backup repository/path")
|
|
81
|
+
.option("--remote <url>", "Git remote to clone/configure")
|
|
82
|
+
.option(
|
|
83
|
+
"--message <message>",
|
|
84
|
+
"Git commit message",
|
|
85
|
+
"archive: sync birdclaw backup",
|
|
86
|
+
)
|
|
87
|
+
.action(async (options) => {
|
|
88
|
+
const result = await syncBackup({
|
|
89
|
+
repoPath: options.repo,
|
|
90
|
+
remote: options.remote,
|
|
91
|
+
message: options.message,
|
|
92
|
+
});
|
|
93
|
+
print(result, true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
backupCommand
|
|
97
|
+
.command("validate <repo>")
|
|
98
|
+
.description("Validate backup manifest, shard hashes, and JSONL rows")
|
|
99
|
+
.action(async (repo) => {
|
|
100
|
+
const result = await validateBackup(repo);
|
|
101
|
+
print(result, true);
|
|
102
|
+
if (!result.ok) {
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|