@character-foundry/character-foundry 0.1.3 → 0.1.5
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/README.md +70 -0
- package/dist/app-framework.cjs +1742 -0
- package/dist/app-framework.cjs.map +1 -0
- package/dist/app-framework.d.cts +881 -0
- package/dist/app-framework.d.ts +881 -2
- package/dist/app-framework.js +1718 -1
- package/dist/app-framework.js.map +1 -1
- package/dist/charx.cjs +917 -0
- package/dist/charx.cjs.map +1 -0
- package/dist/charx.d.cts +640 -0
- package/dist/charx.d.ts +640 -2
- package/dist/charx.js +893 -1
- package/dist/charx.js.map +1 -1
- package/dist/core.cjs +668 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +363 -0
- package/dist/core.d.ts +363 -2
- package/dist/core.js +644 -1
- package/dist/core.js.map +1 -1
- package/dist/exporter.cjs +7539 -0
- package/dist/exporter.cjs.map +1 -0
- package/dist/exporter.d.cts +681 -0
- package/dist/exporter.d.ts +681 -2
- package/dist/exporter.js +7522 -1
- package/dist/exporter.js.map +1 -1
- package/dist/federation.cjs +3915 -0
- package/dist/federation.cjs.map +1 -0
- package/dist/federation.d.cts +2951 -0
- package/dist/federation.d.ts +2951 -2
- package/dist/federation.js +3891 -1
- package/dist/federation.js.map +1 -1
- package/dist/index.cjs +9109 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1119 -0
- package/dist/index.d.ts +1113 -20
- package/dist/index.js +9092 -26
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +8923 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.d.cts +1037 -0
- package/dist/loader.d.ts +1037 -2
- package/dist/loader.js +8906 -1
- package/dist/loader.js.map +1 -1
- package/dist/lorebook.cjs +865 -0
- package/dist/lorebook.cjs.map +1 -0
- package/dist/lorebook.d.cts +1008 -0
- package/dist/lorebook.d.ts +1008 -2
- package/dist/lorebook.js +841 -1
- package/dist/lorebook.js.map +1 -1
- package/dist/media.cjs +6660 -0
- package/dist/media.cjs.map +1 -0
- package/dist/media.d.cts +87 -0
- package/dist/media.d.ts +87 -2
- package/dist/media.js +6643 -1
- package/dist/media.js.map +1 -1
- package/dist/normalizer.cjs +502 -0
- package/dist/normalizer.cjs.map +1 -0
- package/dist/normalizer.d.cts +1216 -0
- package/dist/normalizer.d.ts +1216 -2
- package/dist/normalizer.js +478 -1
- package/dist/normalizer.js.map +1 -1
- package/dist/png.cjs +778 -0
- package/dist/png.cjs.map +1 -0
- package/dist/png.d.cts +786 -0
- package/dist/png.d.ts +786 -2
- package/dist/png.js +754 -1
- package/dist/png.js.map +1 -1
- package/dist/schemas.cjs +799 -0
- package/dist/schemas.cjs.map +1 -0
- package/dist/schemas.d.cts +2178 -0
- package/dist/schemas.d.ts +2178 -2
- package/dist/schemas.js +775 -1
- package/dist/schemas.js.map +1 -1
- package/dist/tokenizers.cjs +153 -0
- package/dist/tokenizers.cjs.map +1 -0
- package/dist/tokenizers.d.cts +155 -0
- package/dist/tokenizers.d.ts +155 -2
- package/dist/tokenizers.js +129 -1
- package/dist/tokenizers.js.map +1 -1
- package/dist/voxta.cjs +7995 -0
- package/dist/voxta.cjs.map +1 -0
- package/dist/voxta.d.cts +1349 -0
- package/dist/voxta.d.ts +1349 -2
- package/dist/voxta.js +7978 -1
- package/dist/voxta.js.map +1 -1
- package/package.json +177 -45
- package/dist/app-framework.d.ts.map +0 -1
- package/dist/charx.d.ts.map +0 -1
- package/dist/core.d.ts.map +0 -1
- package/dist/exporter.d.ts.map +0 -1
- package/dist/federation.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/loader.d.ts.map +0 -1
- package/dist/lorebook.d.ts.map +0 -1
- package/dist/media.d.ts.map +0 -1
- package/dist/normalizer.d.ts.map +0 -1
- package/dist/png.d.ts.map +0 -1
- package/dist/schemas.d.ts.map +0 -1
- package/dist/tokenizers.d.ts.map +0 -1
- package/dist/voxta.d.ts.map +0 -1
package/dist/federation.d.ts
CHANGED
|
@@ -1,2 +1,2951 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const CCv3DataSchema: z.ZodObject<{
|
|
4
|
+
spec: z.ZodLiteral<"chara_card_v3">;
|
|
5
|
+
spec_version: z.ZodLiteral<"3.0">;
|
|
6
|
+
data: z.ZodObject<{
|
|
7
|
+
name: z.ZodDefault<z.ZodString>;
|
|
8
|
+
description: z.ZodDefault<z.ZodString>;
|
|
9
|
+
personality: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
10
|
+
scenario: z.ZodDefault<z.ZodString>;
|
|
11
|
+
first_mes: z.ZodDefault<z.ZodString>;
|
|
12
|
+
mes_example: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
13
|
+
creator: z.ZodDefault<z.ZodString>;
|
|
14
|
+
character_version: z.ZodDefault<z.ZodString>;
|
|
15
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
16
|
+
group_only_greetings: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
17
|
+
creator_notes: z.ZodOptional<z.ZodString>;
|
|
18
|
+
system_prompt: z.ZodOptional<z.ZodString>;
|
|
19
|
+
post_history_instructions: z.ZodOptional<z.ZodString>;
|
|
20
|
+
alternate_greetings: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
21
|
+
character_book: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
22
|
+
name: z.ZodOptional<z.ZodString>;
|
|
23
|
+
description: z.ZodOptional<z.ZodString>;
|
|
24
|
+
scan_depth: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
token_budget: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
recursive_scanning: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
28
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
29
|
+
keys: z.ZodArray<z.ZodString, "many">;
|
|
30
|
+
content: z.ZodString;
|
|
31
|
+
enabled: z.ZodBoolean;
|
|
32
|
+
insertion_order: z.ZodNumber;
|
|
33
|
+
case_sensitive: z.ZodOptional<z.ZodBoolean>;
|
|
34
|
+
name: z.ZodOptional<z.ZodString>;
|
|
35
|
+
priority: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
id: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
comment: z.ZodOptional<z.ZodString>;
|
|
38
|
+
selective: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
secondary_keys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
40
|
+
constant: z.ZodOptional<z.ZodBoolean>;
|
|
41
|
+
position: z.ZodOptional<z.ZodEnum<[
|
|
42
|
+
"before_char",
|
|
43
|
+
"after_char"
|
|
44
|
+
]>>;
|
|
45
|
+
extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
46
|
+
automation_id: z.ZodOptional<z.ZodString>;
|
|
47
|
+
role: z.ZodOptional<z.ZodEnum<[
|
|
48
|
+
"system",
|
|
49
|
+
"user",
|
|
50
|
+
"assistant"
|
|
51
|
+
]>>;
|
|
52
|
+
group: z.ZodOptional<z.ZodString>;
|
|
53
|
+
scan_frequency: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
probability: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
use_regex: z.ZodOptional<z.ZodBoolean>;
|
|
56
|
+
depth: z.ZodOptional<z.ZodNumber>;
|
|
57
|
+
selective_logic: z.ZodOptional<z.ZodEnum<[
|
|
58
|
+
"AND",
|
|
59
|
+
"NOT"
|
|
60
|
+
]>>;
|
|
61
|
+
}, "strip", z.ZodTypeAny, {
|
|
62
|
+
keys: string[];
|
|
63
|
+
content: string;
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
insertion_order: number;
|
|
66
|
+
name?: string | undefined;
|
|
67
|
+
extensions?: Record<string, unknown> | undefined;
|
|
68
|
+
case_sensitive?: boolean | undefined;
|
|
69
|
+
priority?: number | undefined;
|
|
70
|
+
id?: number | undefined;
|
|
71
|
+
comment?: string | undefined;
|
|
72
|
+
selective?: boolean | undefined;
|
|
73
|
+
secondary_keys?: string[] | undefined;
|
|
74
|
+
constant?: boolean | undefined;
|
|
75
|
+
position?: "before_char" | "after_char" | undefined;
|
|
76
|
+
automation_id?: string | undefined;
|
|
77
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
78
|
+
group?: string | undefined;
|
|
79
|
+
scan_frequency?: number | undefined;
|
|
80
|
+
probability?: number | undefined;
|
|
81
|
+
use_regex?: boolean | undefined;
|
|
82
|
+
depth?: number | undefined;
|
|
83
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
84
|
+
}, {
|
|
85
|
+
keys: string[];
|
|
86
|
+
content: string;
|
|
87
|
+
enabled: boolean;
|
|
88
|
+
insertion_order: number;
|
|
89
|
+
name?: string | undefined;
|
|
90
|
+
extensions?: Record<string, unknown> | undefined;
|
|
91
|
+
case_sensitive?: boolean | undefined;
|
|
92
|
+
priority?: number | undefined;
|
|
93
|
+
id?: number | undefined;
|
|
94
|
+
comment?: string | undefined;
|
|
95
|
+
selective?: boolean | undefined;
|
|
96
|
+
secondary_keys?: string[] | undefined;
|
|
97
|
+
constant?: boolean | undefined;
|
|
98
|
+
position?: "before_char" | "after_char" | undefined;
|
|
99
|
+
automation_id?: string | undefined;
|
|
100
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
101
|
+
group?: string | undefined;
|
|
102
|
+
scan_frequency?: number | undefined;
|
|
103
|
+
probability?: number | undefined;
|
|
104
|
+
use_regex?: boolean | undefined;
|
|
105
|
+
depth?: number | undefined;
|
|
106
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
107
|
+
}>, "many">;
|
|
108
|
+
}, "strip", z.ZodTypeAny, {
|
|
109
|
+
entries: {
|
|
110
|
+
keys: string[];
|
|
111
|
+
content: string;
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
insertion_order: number;
|
|
114
|
+
name?: string | undefined;
|
|
115
|
+
extensions?: Record<string, unknown> | undefined;
|
|
116
|
+
case_sensitive?: boolean | undefined;
|
|
117
|
+
priority?: number | undefined;
|
|
118
|
+
id?: number | undefined;
|
|
119
|
+
comment?: string | undefined;
|
|
120
|
+
selective?: boolean | undefined;
|
|
121
|
+
secondary_keys?: string[] | undefined;
|
|
122
|
+
constant?: boolean | undefined;
|
|
123
|
+
position?: "before_char" | "after_char" | undefined;
|
|
124
|
+
automation_id?: string | undefined;
|
|
125
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
126
|
+
group?: string | undefined;
|
|
127
|
+
scan_frequency?: number | undefined;
|
|
128
|
+
probability?: number | undefined;
|
|
129
|
+
use_regex?: boolean | undefined;
|
|
130
|
+
depth?: number | undefined;
|
|
131
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
132
|
+
}[];
|
|
133
|
+
name?: string | undefined;
|
|
134
|
+
description?: string | undefined;
|
|
135
|
+
scan_depth?: number | undefined;
|
|
136
|
+
token_budget?: number | undefined;
|
|
137
|
+
recursive_scanning?: boolean | undefined;
|
|
138
|
+
extensions?: Record<string, unknown> | undefined;
|
|
139
|
+
}, {
|
|
140
|
+
entries: {
|
|
141
|
+
keys: string[];
|
|
142
|
+
content: string;
|
|
143
|
+
enabled: boolean;
|
|
144
|
+
insertion_order: number;
|
|
145
|
+
name?: string | undefined;
|
|
146
|
+
extensions?: Record<string, unknown> | undefined;
|
|
147
|
+
case_sensitive?: boolean | undefined;
|
|
148
|
+
priority?: number | undefined;
|
|
149
|
+
id?: number | undefined;
|
|
150
|
+
comment?: string | undefined;
|
|
151
|
+
selective?: boolean | undefined;
|
|
152
|
+
secondary_keys?: string[] | undefined;
|
|
153
|
+
constant?: boolean | undefined;
|
|
154
|
+
position?: "before_char" | "after_char" | undefined;
|
|
155
|
+
automation_id?: string | undefined;
|
|
156
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
157
|
+
group?: string | undefined;
|
|
158
|
+
scan_frequency?: number | undefined;
|
|
159
|
+
probability?: number | undefined;
|
|
160
|
+
use_regex?: boolean | undefined;
|
|
161
|
+
depth?: number | undefined;
|
|
162
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
163
|
+
}[];
|
|
164
|
+
name?: string | undefined;
|
|
165
|
+
description?: string | undefined;
|
|
166
|
+
scan_depth?: number | undefined;
|
|
167
|
+
token_budget?: number | undefined;
|
|
168
|
+
recursive_scanning?: boolean | undefined;
|
|
169
|
+
extensions?: Record<string, unknown> | undefined;
|
|
170
|
+
}>>>;
|
|
171
|
+
extensions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
172
|
+
assets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
173
|
+
type: z.ZodEnum<[
|
|
174
|
+
"icon",
|
|
175
|
+
"background",
|
|
176
|
+
"emotion",
|
|
177
|
+
"user_icon",
|
|
178
|
+
"sound",
|
|
179
|
+
"video",
|
|
180
|
+
"custom",
|
|
181
|
+
"x-risu-asset"
|
|
182
|
+
]>;
|
|
183
|
+
uri: z.ZodString;
|
|
184
|
+
name: z.ZodString;
|
|
185
|
+
ext: z.ZodString;
|
|
186
|
+
}, "strip", z.ZodTypeAny, {
|
|
187
|
+
name: string;
|
|
188
|
+
type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
|
|
189
|
+
uri: string;
|
|
190
|
+
ext: string;
|
|
191
|
+
}, {
|
|
192
|
+
name: string;
|
|
193
|
+
type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
|
|
194
|
+
uri: string;
|
|
195
|
+
ext: string;
|
|
196
|
+
}>, "many">>;
|
|
197
|
+
nickname: z.ZodOptional<z.ZodString>;
|
|
198
|
+
creator_notes_multilingual: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
199
|
+
source: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
200
|
+
creation_date: z.ZodOptional<z.ZodNumber>;
|
|
201
|
+
modification_date: z.ZodOptional<z.ZodNumber>;
|
|
202
|
+
}, "strip", z.ZodTypeAny, {
|
|
203
|
+
name: string;
|
|
204
|
+
description: string;
|
|
205
|
+
personality: string | null;
|
|
206
|
+
scenario: string;
|
|
207
|
+
first_mes: string;
|
|
208
|
+
mes_example: string | null;
|
|
209
|
+
tags: string[];
|
|
210
|
+
creator: string;
|
|
211
|
+
character_version: string;
|
|
212
|
+
group_only_greetings: string[];
|
|
213
|
+
extensions?: Record<string, unknown> | undefined;
|
|
214
|
+
creator_notes?: string | undefined;
|
|
215
|
+
system_prompt?: string | undefined;
|
|
216
|
+
post_history_instructions?: string | undefined;
|
|
217
|
+
alternate_greetings?: string[] | undefined;
|
|
218
|
+
character_book?: {
|
|
219
|
+
entries: {
|
|
220
|
+
keys: string[];
|
|
221
|
+
content: string;
|
|
222
|
+
enabled: boolean;
|
|
223
|
+
insertion_order: number;
|
|
224
|
+
name?: string | undefined;
|
|
225
|
+
extensions?: Record<string, unknown> | undefined;
|
|
226
|
+
case_sensitive?: boolean | undefined;
|
|
227
|
+
priority?: number | undefined;
|
|
228
|
+
id?: number | undefined;
|
|
229
|
+
comment?: string | undefined;
|
|
230
|
+
selective?: boolean | undefined;
|
|
231
|
+
secondary_keys?: string[] | undefined;
|
|
232
|
+
constant?: boolean | undefined;
|
|
233
|
+
position?: "before_char" | "after_char" | undefined;
|
|
234
|
+
automation_id?: string | undefined;
|
|
235
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
236
|
+
group?: string | undefined;
|
|
237
|
+
scan_frequency?: number | undefined;
|
|
238
|
+
probability?: number | undefined;
|
|
239
|
+
use_regex?: boolean | undefined;
|
|
240
|
+
depth?: number | undefined;
|
|
241
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
242
|
+
}[];
|
|
243
|
+
name?: string | undefined;
|
|
244
|
+
description?: string | undefined;
|
|
245
|
+
scan_depth?: number | undefined;
|
|
246
|
+
token_budget?: number | undefined;
|
|
247
|
+
recursive_scanning?: boolean | undefined;
|
|
248
|
+
extensions?: Record<string, unknown> | undefined;
|
|
249
|
+
} | null | undefined;
|
|
250
|
+
assets?: {
|
|
251
|
+
name: string;
|
|
252
|
+
type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
|
|
253
|
+
uri: string;
|
|
254
|
+
ext: string;
|
|
255
|
+
}[] | undefined;
|
|
256
|
+
nickname?: string | undefined;
|
|
257
|
+
creator_notes_multilingual?: Record<string, string> | undefined;
|
|
258
|
+
source?: string[] | undefined;
|
|
259
|
+
creation_date?: number | undefined;
|
|
260
|
+
modification_date?: number | undefined;
|
|
261
|
+
}, {
|
|
262
|
+
name?: string | undefined;
|
|
263
|
+
description?: string | undefined;
|
|
264
|
+
extensions?: Record<string, unknown> | undefined;
|
|
265
|
+
personality?: string | null | undefined;
|
|
266
|
+
scenario?: string | undefined;
|
|
267
|
+
first_mes?: string | undefined;
|
|
268
|
+
mes_example?: string | null | undefined;
|
|
269
|
+
creator_notes?: string | undefined;
|
|
270
|
+
system_prompt?: string | undefined;
|
|
271
|
+
post_history_instructions?: string | undefined;
|
|
272
|
+
alternate_greetings?: string[] | undefined;
|
|
273
|
+
character_book?: {
|
|
274
|
+
entries: {
|
|
275
|
+
keys: string[];
|
|
276
|
+
content: string;
|
|
277
|
+
enabled: boolean;
|
|
278
|
+
insertion_order: number;
|
|
279
|
+
name?: string | undefined;
|
|
280
|
+
extensions?: Record<string, unknown> | undefined;
|
|
281
|
+
case_sensitive?: boolean | undefined;
|
|
282
|
+
priority?: number | undefined;
|
|
283
|
+
id?: number | undefined;
|
|
284
|
+
comment?: string | undefined;
|
|
285
|
+
selective?: boolean | undefined;
|
|
286
|
+
secondary_keys?: string[] | undefined;
|
|
287
|
+
constant?: boolean | undefined;
|
|
288
|
+
position?: "before_char" | "after_char" | undefined;
|
|
289
|
+
automation_id?: string | undefined;
|
|
290
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
291
|
+
group?: string | undefined;
|
|
292
|
+
scan_frequency?: number | undefined;
|
|
293
|
+
probability?: number | undefined;
|
|
294
|
+
use_regex?: boolean | undefined;
|
|
295
|
+
depth?: number | undefined;
|
|
296
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
297
|
+
}[];
|
|
298
|
+
name?: string | undefined;
|
|
299
|
+
description?: string | undefined;
|
|
300
|
+
scan_depth?: number | undefined;
|
|
301
|
+
token_budget?: number | undefined;
|
|
302
|
+
recursive_scanning?: boolean | undefined;
|
|
303
|
+
extensions?: Record<string, unknown> | undefined;
|
|
304
|
+
} | null | undefined;
|
|
305
|
+
tags?: string[] | undefined;
|
|
306
|
+
creator?: string | undefined;
|
|
307
|
+
character_version?: string | undefined;
|
|
308
|
+
group_only_greetings?: string[] | undefined;
|
|
309
|
+
assets?: {
|
|
310
|
+
name: string;
|
|
311
|
+
type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
|
|
312
|
+
uri: string;
|
|
313
|
+
ext: string;
|
|
314
|
+
}[] | undefined;
|
|
315
|
+
nickname?: string | undefined;
|
|
316
|
+
creator_notes_multilingual?: Record<string, string> | undefined;
|
|
317
|
+
source?: string[] | undefined;
|
|
318
|
+
creation_date?: number | undefined;
|
|
319
|
+
modification_date?: number | undefined;
|
|
320
|
+
}>;
|
|
321
|
+
}, "strip", z.ZodTypeAny, {
|
|
322
|
+
data: {
|
|
323
|
+
name: string;
|
|
324
|
+
description: string;
|
|
325
|
+
personality: string | null;
|
|
326
|
+
scenario: string;
|
|
327
|
+
first_mes: string;
|
|
328
|
+
mes_example: string | null;
|
|
329
|
+
tags: string[];
|
|
330
|
+
creator: string;
|
|
331
|
+
character_version: string;
|
|
332
|
+
group_only_greetings: string[];
|
|
333
|
+
extensions?: Record<string, unknown> | undefined;
|
|
334
|
+
creator_notes?: string | undefined;
|
|
335
|
+
system_prompt?: string | undefined;
|
|
336
|
+
post_history_instructions?: string | undefined;
|
|
337
|
+
alternate_greetings?: string[] | undefined;
|
|
338
|
+
character_book?: {
|
|
339
|
+
entries: {
|
|
340
|
+
keys: string[];
|
|
341
|
+
content: string;
|
|
342
|
+
enabled: boolean;
|
|
343
|
+
insertion_order: number;
|
|
344
|
+
name?: string | undefined;
|
|
345
|
+
extensions?: Record<string, unknown> | undefined;
|
|
346
|
+
case_sensitive?: boolean | undefined;
|
|
347
|
+
priority?: number | undefined;
|
|
348
|
+
id?: number | undefined;
|
|
349
|
+
comment?: string | undefined;
|
|
350
|
+
selective?: boolean | undefined;
|
|
351
|
+
secondary_keys?: string[] | undefined;
|
|
352
|
+
constant?: boolean | undefined;
|
|
353
|
+
position?: "before_char" | "after_char" | undefined;
|
|
354
|
+
automation_id?: string | undefined;
|
|
355
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
356
|
+
group?: string | undefined;
|
|
357
|
+
scan_frequency?: number | undefined;
|
|
358
|
+
probability?: number | undefined;
|
|
359
|
+
use_regex?: boolean | undefined;
|
|
360
|
+
depth?: number | undefined;
|
|
361
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
362
|
+
}[];
|
|
363
|
+
name?: string | undefined;
|
|
364
|
+
description?: string | undefined;
|
|
365
|
+
scan_depth?: number | undefined;
|
|
366
|
+
token_budget?: number | undefined;
|
|
367
|
+
recursive_scanning?: boolean | undefined;
|
|
368
|
+
extensions?: Record<string, unknown> | undefined;
|
|
369
|
+
} | null | undefined;
|
|
370
|
+
assets?: {
|
|
371
|
+
name: string;
|
|
372
|
+
type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
|
|
373
|
+
uri: string;
|
|
374
|
+
ext: string;
|
|
375
|
+
}[] | undefined;
|
|
376
|
+
nickname?: string | undefined;
|
|
377
|
+
creator_notes_multilingual?: Record<string, string> | undefined;
|
|
378
|
+
source?: string[] | undefined;
|
|
379
|
+
creation_date?: number | undefined;
|
|
380
|
+
modification_date?: number | undefined;
|
|
381
|
+
};
|
|
382
|
+
spec: "chara_card_v3";
|
|
383
|
+
spec_version: "3.0";
|
|
384
|
+
}, {
|
|
385
|
+
data: {
|
|
386
|
+
name?: string | undefined;
|
|
387
|
+
description?: string | undefined;
|
|
388
|
+
extensions?: Record<string, unknown> | undefined;
|
|
389
|
+
personality?: string | null | undefined;
|
|
390
|
+
scenario?: string | undefined;
|
|
391
|
+
first_mes?: string | undefined;
|
|
392
|
+
mes_example?: string | null | undefined;
|
|
393
|
+
creator_notes?: string | undefined;
|
|
394
|
+
system_prompt?: string | undefined;
|
|
395
|
+
post_history_instructions?: string | undefined;
|
|
396
|
+
alternate_greetings?: string[] | undefined;
|
|
397
|
+
character_book?: {
|
|
398
|
+
entries: {
|
|
399
|
+
keys: string[];
|
|
400
|
+
content: string;
|
|
401
|
+
enabled: boolean;
|
|
402
|
+
insertion_order: number;
|
|
403
|
+
name?: string | undefined;
|
|
404
|
+
extensions?: Record<string, unknown> | undefined;
|
|
405
|
+
case_sensitive?: boolean | undefined;
|
|
406
|
+
priority?: number | undefined;
|
|
407
|
+
id?: number | undefined;
|
|
408
|
+
comment?: string | undefined;
|
|
409
|
+
selective?: boolean | undefined;
|
|
410
|
+
secondary_keys?: string[] | undefined;
|
|
411
|
+
constant?: boolean | undefined;
|
|
412
|
+
position?: "before_char" | "after_char" | undefined;
|
|
413
|
+
automation_id?: string | undefined;
|
|
414
|
+
role?: "system" | "user" | "assistant" | undefined;
|
|
415
|
+
group?: string | undefined;
|
|
416
|
+
scan_frequency?: number | undefined;
|
|
417
|
+
probability?: number | undefined;
|
|
418
|
+
use_regex?: boolean | undefined;
|
|
419
|
+
depth?: number | undefined;
|
|
420
|
+
selective_logic?: "AND" | "NOT" | undefined;
|
|
421
|
+
}[];
|
|
422
|
+
name?: string | undefined;
|
|
423
|
+
description?: string | undefined;
|
|
424
|
+
scan_depth?: number | undefined;
|
|
425
|
+
token_budget?: number | undefined;
|
|
426
|
+
recursive_scanning?: boolean | undefined;
|
|
427
|
+
extensions?: Record<string, unknown> | undefined;
|
|
428
|
+
} | null | undefined;
|
|
429
|
+
tags?: string[] | undefined;
|
|
430
|
+
creator?: string | undefined;
|
|
431
|
+
character_version?: string | undefined;
|
|
432
|
+
group_only_greetings?: string[] | undefined;
|
|
433
|
+
assets?: {
|
|
434
|
+
name: string;
|
|
435
|
+
type: "custom" | "icon" | "background" | "emotion" | "user_icon" | "sound" | "video" | "x-risu-asset";
|
|
436
|
+
uri: string;
|
|
437
|
+
ext: string;
|
|
438
|
+
}[] | undefined;
|
|
439
|
+
nickname?: string | undefined;
|
|
440
|
+
creator_notes_multilingual?: Record<string, string> | undefined;
|
|
441
|
+
source?: string[] | undefined;
|
|
442
|
+
creation_date?: number | undefined;
|
|
443
|
+
modification_date?: number | undefined;
|
|
444
|
+
};
|
|
445
|
+
spec: "chara_card_v3";
|
|
446
|
+
spec_version: "3.0";
|
|
447
|
+
}>;
|
|
448
|
+
/**
|
|
449
|
+
* Character Card v3 full structure
|
|
450
|
+
*/
|
|
451
|
+
export type CCv3Data = z.infer<typeof CCv3DataSchema>;
|
|
452
|
+
/**
|
|
453
|
+
* Federation Types
|
|
454
|
+
*
|
|
455
|
+
* Core types for ActivityPub-based character card federation.
|
|
456
|
+
*/
|
|
457
|
+
/**
|
|
458
|
+
* Unique identifier for a federated card
|
|
459
|
+
* Format: {platform}:{id} or full URI
|
|
460
|
+
*/
|
|
461
|
+
export type FederatedCardId = string;
|
|
462
|
+
/**
|
|
463
|
+
* Platform identifier
|
|
464
|
+
*/
|
|
465
|
+
export type PlatformId = "archive" | "hub" | "editor" | "sillytavern" | "risu" | "chub" | "custom";
|
|
466
|
+
/**
|
|
467
|
+
* ActivityPub Actor representing a creator/user
|
|
468
|
+
*/
|
|
469
|
+
export interface FederatedActor {
|
|
470
|
+
/** ActivityPub context */
|
|
471
|
+
"@context"?: ActivityPubContext;
|
|
472
|
+
/** ActivityPub ID (URI) */
|
|
473
|
+
id: string;
|
|
474
|
+
/** Actor type */
|
|
475
|
+
type: "Person" | "Service" | "Application";
|
|
476
|
+
/** Display name */
|
|
477
|
+
name: string;
|
|
478
|
+
/** Username/handle */
|
|
479
|
+
preferredUsername: string;
|
|
480
|
+
/** Profile summary */
|
|
481
|
+
summary?: string;
|
|
482
|
+
/** Avatar URL */
|
|
483
|
+
icon?: string;
|
|
484
|
+
/** Inbox URL for receiving activities */
|
|
485
|
+
inbox: string;
|
|
486
|
+
/** Outbox URL for published activities */
|
|
487
|
+
outbox: string;
|
|
488
|
+
/** Followers collection URL */
|
|
489
|
+
followers?: string;
|
|
490
|
+
/** Following collection URL */
|
|
491
|
+
following?: string;
|
|
492
|
+
/** Public key for HTTP signatures */
|
|
493
|
+
publicKey?: {
|
|
494
|
+
id: string;
|
|
495
|
+
owner: string;
|
|
496
|
+
publicKeyPem: string;
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* ActivityPub context type - can be a string, or an array of strings and objects
|
|
501
|
+
*/
|
|
502
|
+
export type ActivityPubContext = string | (string | Record<string, unknown>)[];
|
|
503
|
+
/**
|
|
504
|
+
* ActivityPub Object representing a character card
|
|
505
|
+
*/
|
|
506
|
+
export interface FederatedCard {
|
|
507
|
+
/** ActivityPub context */
|
|
508
|
+
"@context": ActivityPubContext;
|
|
509
|
+
/** Unique ID (URI) */
|
|
510
|
+
id: string;
|
|
511
|
+
/** Object type */
|
|
512
|
+
type: "Note" | "Article" | "Document";
|
|
513
|
+
/** Card name/title */
|
|
514
|
+
name: string;
|
|
515
|
+
/** Card description/summary */
|
|
516
|
+
summary?: string;
|
|
517
|
+
/** Full card content (JSON stringified CCv3Data) */
|
|
518
|
+
content: string;
|
|
519
|
+
/** Content media type */
|
|
520
|
+
mediaType: "application/json";
|
|
521
|
+
/** Creator actor */
|
|
522
|
+
attributedTo: string;
|
|
523
|
+
/** Publication timestamp */
|
|
524
|
+
published: string;
|
|
525
|
+
/** Last update timestamp */
|
|
526
|
+
updated?: string;
|
|
527
|
+
/** Tags/hashtags */
|
|
528
|
+
tag?: Array<{
|
|
529
|
+
type: "Hashtag";
|
|
530
|
+
name: string;
|
|
531
|
+
href?: string;
|
|
532
|
+
}>;
|
|
533
|
+
/** Attached assets */
|
|
534
|
+
attachment?: Array<{
|
|
535
|
+
type: "Document" | "Image" | "Audio";
|
|
536
|
+
mediaType: string;
|
|
537
|
+
url: string;
|
|
538
|
+
name?: string;
|
|
539
|
+
}>;
|
|
540
|
+
/** Source platform */
|
|
541
|
+
source?: {
|
|
542
|
+
platform: PlatformId;
|
|
543
|
+
id: string;
|
|
544
|
+
url?: string;
|
|
545
|
+
};
|
|
546
|
+
/** Card version */
|
|
547
|
+
"character:version"?: string;
|
|
548
|
+
/** Card spec version */
|
|
549
|
+
"character:spec"?: string;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* ActivityPub Activity types for cards
|
|
553
|
+
*/
|
|
554
|
+
export type ActivityType = "Create" | "Update" | "Delete" | "Announce" | "Like" | "Follow" | "Undo" | "Fork" | "Install" | "Flag" | "Block";
|
|
555
|
+
/**
|
|
556
|
+
* Platform role in federation topology
|
|
557
|
+
*/
|
|
558
|
+
export type PlatformRole = "publisher" | "hub" | "architect" | "consumer";
|
|
559
|
+
/**
|
|
560
|
+
* Fork reference stored in card extensions
|
|
561
|
+
* Path: card.data.extensions['character-foundry'].forkedFrom
|
|
562
|
+
*/
|
|
563
|
+
export interface ForkReference {
|
|
564
|
+
/** Federated URI of the source card */
|
|
565
|
+
federatedId: string;
|
|
566
|
+
/** Source platform identifier */
|
|
567
|
+
platform: PlatformId;
|
|
568
|
+
/** Timestamp when forked */
|
|
569
|
+
forkedAt: string;
|
|
570
|
+
/** Source version hash at fork time */
|
|
571
|
+
sourceVersionHash?: string;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Fork notification received by source instance
|
|
575
|
+
*/
|
|
576
|
+
export interface ForkNotification {
|
|
577
|
+
/** Federated URI of the fork */
|
|
578
|
+
forkId: string;
|
|
579
|
+
/** Actor who created the fork */
|
|
580
|
+
actorId: string;
|
|
581
|
+
/** Platform where fork was created */
|
|
582
|
+
platform: PlatformId;
|
|
583
|
+
/** Timestamp of fork */
|
|
584
|
+
timestamp: string;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Install notification received by source instance
|
|
588
|
+
* Consumers (SillyTavern, Voxta) send these when a card is installed
|
|
589
|
+
*/
|
|
590
|
+
export interface InstallNotification {
|
|
591
|
+
/** Platform where card was installed */
|
|
592
|
+
platform: PlatformId;
|
|
593
|
+
/** Actor/user who installed (if known) */
|
|
594
|
+
actorId?: string;
|
|
595
|
+
/** Timestamp of installation */
|
|
596
|
+
timestamp: string;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Aggregated stats for a card
|
|
600
|
+
*/
|
|
601
|
+
export interface CardStats {
|
|
602
|
+
/** Total known installs across all platforms */
|
|
603
|
+
installCount: number;
|
|
604
|
+
/** Install counts per platform */
|
|
605
|
+
installsByPlatform: Partial<Record<PlatformId, number>>;
|
|
606
|
+
/** Fork count (separate from forkNotifications for efficiency) */
|
|
607
|
+
forkCount: number;
|
|
608
|
+
/** Like/favorite count */
|
|
609
|
+
likeCount: number;
|
|
610
|
+
/** Last time any stat was updated */
|
|
611
|
+
lastUpdated: string;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Install activity - consumer notifies hub about card installation
|
|
615
|
+
*/
|
|
616
|
+
export interface InstallActivity extends Omit<FederatedActivity, "type" | "object"> {
|
|
617
|
+
type: "Install";
|
|
618
|
+
/** Card URI that was installed */
|
|
619
|
+
object: string;
|
|
620
|
+
/** Platform where installed */
|
|
621
|
+
target?: {
|
|
622
|
+
type: "Application";
|
|
623
|
+
name: PlatformId;
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* ActivityPub Activity
|
|
628
|
+
*/
|
|
629
|
+
export interface FederatedActivity {
|
|
630
|
+
"@context": ActivityPubContext;
|
|
631
|
+
id: string;
|
|
632
|
+
type: ActivityType;
|
|
633
|
+
actor: string;
|
|
634
|
+
object: string | FederatedCard;
|
|
635
|
+
published: string;
|
|
636
|
+
to?: string[];
|
|
637
|
+
cc?: string[];
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Sync state for a card across platforms
|
|
641
|
+
*/
|
|
642
|
+
export interface CardSyncState {
|
|
643
|
+
/** Local card ID */
|
|
644
|
+
localId: string;
|
|
645
|
+
/** Federated card ID (ActivityPub URI) */
|
|
646
|
+
federatedId: string;
|
|
647
|
+
/** Platform-specific IDs (only set for synced platforms) */
|
|
648
|
+
platformIds: Partial<Record<PlatformId, string>>;
|
|
649
|
+
/** Last sync timestamp per platform (only set for synced platforms) */
|
|
650
|
+
lastSync: Partial<Record<PlatformId, string>>;
|
|
651
|
+
/** Current version hash */
|
|
652
|
+
versionHash: string;
|
|
653
|
+
/** Sync status */
|
|
654
|
+
status: "synced" | "pending" | "conflict" | "error";
|
|
655
|
+
/** Conflict details if any */
|
|
656
|
+
conflict?: {
|
|
657
|
+
localVersion: string;
|
|
658
|
+
remoteVersion: string;
|
|
659
|
+
remotePlatform: PlatformId;
|
|
660
|
+
};
|
|
661
|
+
/** Fork reference if this card is a fork */
|
|
662
|
+
forkedFrom?: ForkReference;
|
|
663
|
+
/** Count of known forks of this card */
|
|
664
|
+
forksCount?: number;
|
|
665
|
+
/** References to known forks (capped at 100 to prevent unbounded growth) */
|
|
666
|
+
forkNotifications?: ForkNotification[];
|
|
667
|
+
/** Aggregated stats for this card */
|
|
668
|
+
stats?: CardStats;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Sync operation
|
|
672
|
+
*/
|
|
673
|
+
export interface SyncOperation {
|
|
674
|
+
type: "push" | "pull" | "delete" | "resolve" | "fork";
|
|
675
|
+
cardId: FederatedCardId;
|
|
676
|
+
sourcePlatform: PlatformId;
|
|
677
|
+
targetPlatform: PlatformId;
|
|
678
|
+
timestamp: string;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Sync result
|
|
682
|
+
*/
|
|
683
|
+
export interface SyncResult {
|
|
684
|
+
success: boolean;
|
|
685
|
+
operation: SyncOperation;
|
|
686
|
+
newState?: CardSyncState;
|
|
687
|
+
error?: string;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Platform adapter interface
|
|
691
|
+
* Each platform implements this to enable federation
|
|
692
|
+
*/
|
|
693
|
+
export interface PlatformAdapter {
|
|
694
|
+
/** Platform identifier */
|
|
695
|
+
readonly platform: PlatformId;
|
|
696
|
+
/** Platform display name */
|
|
697
|
+
readonly displayName: string;
|
|
698
|
+
/** Whether the platform is currently available */
|
|
699
|
+
isAvailable(): Promise<boolean>;
|
|
700
|
+
/** Get a card by local ID */
|
|
701
|
+
getCard(localId: string): Promise<CCv3Data | null>;
|
|
702
|
+
/** List all cards (with optional pagination) */
|
|
703
|
+
listCards(options?: {
|
|
704
|
+
limit?: number;
|
|
705
|
+
offset?: number;
|
|
706
|
+
since?: string;
|
|
707
|
+
}): Promise<Array<{
|
|
708
|
+
id: string;
|
|
709
|
+
card: CCv3Data;
|
|
710
|
+
updatedAt: string;
|
|
711
|
+
}>>;
|
|
712
|
+
/** Save/update a card */
|
|
713
|
+
saveCard(card: CCv3Data, localId?: string): Promise<string>;
|
|
714
|
+
/** Delete a card */
|
|
715
|
+
deleteCard(localId: string): Promise<boolean>;
|
|
716
|
+
/** Get card assets */
|
|
717
|
+
getAssets(localId: string): Promise<Array<{
|
|
718
|
+
name: string;
|
|
719
|
+
type: string;
|
|
720
|
+
data: Uint8Array;
|
|
721
|
+
}>>;
|
|
722
|
+
/** Get the last modified timestamp for a card */
|
|
723
|
+
getLastModified(localId: string): Promise<string | null>;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Federation hub configuration
|
|
727
|
+
*/
|
|
728
|
+
export interface FederationConfig {
|
|
729
|
+
/** This instance's ActivityPub actor */
|
|
730
|
+
actor: FederatedActor;
|
|
731
|
+
/** Registered platform adapters */
|
|
732
|
+
platforms: Map<PlatformId, PlatformAdapter>;
|
|
733
|
+
/** Sync state storage */
|
|
734
|
+
stateStore: SyncStateStore;
|
|
735
|
+
/** HTTP signature keys */
|
|
736
|
+
keys?: {
|
|
737
|
+
privateKey: string;
|
|
738
|
+
publicKey: string;
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Sync state storage interface
|
|
743
|
+
*/
|
|
744
|
+
export interface SyncStateStore {
|
|
745
|
+
/** Get sync state for a card */
|
|
746
|
+
get(federatedId: string): Promise<CardSyncState | null>;
|
|
747
|
+
/** Save sync state */
|
|
748
|
+
set(state: CardSyncState): Promise<void>;
|
|
749
|
+
/** Delete sync state */
|
|
750
|
+
delete(federatedId: string): Promise<void>;
|
|
751
|
+
/** List all sync states */
|
|
752
|
+
list(): Promise<CardSyncState[]>;
|
|
753
|
+
/** Find by platform ID */
|
|
754
|
+
findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Federation event types
|
|
758
|
+
*/
|
|
759
|
+
export type FederationEventType = "card:created" | "card:updated" | "card:deleted" | "card:synced" | "card:conflict" | "card:forked" | "card:fork-received" | "card:installed" | "card:install-received" | "sync:started" | "sync:completed" | "sync:failed" | "sync:skipped" | "actor:followed" | "actor:unfollowed" | "moderation:report-received" | "moderation:action-taken" | "moderation:instance-blocked" | "moderation:instance-unblocked" | "moderation:policy-matched" | "moderation:policy-rejected";
|
|
760
|
+
/**
|
|
761
|
+
* Federation event
|
|
762
|
+
*/
|
|
763
|
+
export interface FederationEvent {
|
|
764
|
+
type: FederationEventType;
|
|
765
|
+
timestamp: string;
|
|
766
|
+
data: unknown;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Event listener
|
|
770
|
+
*/
|
|
771
|
+
export type FederationEventListener = (event: FederationEvent) => void | Promise<void>;
|
|
772
|
+
/**
|
|
773
|
+
* Fork activity - custom ActivityPub extension
|
|
774
|
+
*/
|
|
775
|
+
export interface ForkActivity extends Omit<FederatedActivity, "type" | "object"> {
|
|
776
|
+
type: "Fork";
|
|
777
|
+
/** Source card URI being forked */
|
|
778
|
+
object: string;
|
|
779
|
+
/** The newly created fork */
|
|
780
|
+
result: FederatedCard;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Fork operation result
|
|
784
|
+
*/
|
|
785
|
+
export interface ForkResult {
|
|
786
|
+
success: boolean;
|
|
787
|
+
operation: SyncOperation;
|
|
788
|
+
forkState?: CardSyncState;
|
|
789
|
+
sourceFederatedId?: string;
|
|
790
|
+
forkFederatedId?: string;
|
|
791
|
+
error?: string;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Inbox handler result
|
|
795
|
+
*/
|
|
796
|
+
export interface InboxResult {
|
|
797
|
+
accepted: boolean;
|
|
798
|
+
error?: string;
|
|
799
|
+
activityType?: ActivityType;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Inbox handler options
|
|
803
|
+
*/
|
|
804
|
+
export interface InboxHandlerOptions {
|
|
805
|
+
/** Fetch actor by ID for signature verification */
|
|
806
|
+
fetchActor: (actorId: string) => Promise<FederatedActor | null>;
|
|
807
|
+
/** Strict mode for signature validation */
|
|
808
|
+
strictMode?: boolean;
|
|
809
|
+
/** Maximum age for signatures in seconds (default 300) */
|
|
810
|
+
maxAge?: number;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* ActivityPub Utilities
|
|
814
|
+
*
|
|
815
|
+
* Convert character cards to/from ActivityPub format.
|
|
816
|
+
*/
|
|
817
|
+
/**
|
|
818
|
+
* ActivityPub context for character cards
|
|
819
|
+
*/
|
|
820
|
+
export declare const ACTIVITY_CONTEXT: (string | {
|
|
821
|
+
character: string;
|
|
822
|
+
"character:version": {
|
|
823
|
+
"@id": string;
|
|
824
|
+
};
|
|
825
|
+
"character:spec": {
|
|
826
|
+
"@id": string;
|
|
827
|
+
};
|
|
828
|
+
})[];
|
|
829
|
+
/**
|
|
830
|
+
* Extended ActivityPub context for Fork activities
|
|
831
|
+
* Includes custom character-foundry namespace for fork semantics
|
|
832
|
+
*/
|
|
833
|
+
export declare const FORK_ACTIVITY_CONTEXT: (string | {
|
|
834
|
+
character: string;
|
|
835
|
+
"character:version": {
|
|
836
|
+
"@id": string;
|
|
837
|
+
};
|
|
838
|
+
"character:spec": {
|
|
839
|
+
"@id": string;
|
|
840
|
+
};
|
|
841
|
+
Fork: string;
|
|
842
|
+
forkedFrom: {
|
|
843
|
+
"@id": string;
|
|
844
|
+
"@type": string;
|
|
845
|
+
};
|
|
846
|
+
})[];
|
|
847
|
+
/**
|
|
848
|
+
* Extended ActivityPub context for Install activities
|
|
849
|
+
* Used by consumers (SillyTavern, Voxta) to notify hub about installations
|
|
850
|
+
*/
|
|
851
|
+
export declare const INSTALL_ACTIVITY_CONTEXT: (string | {
|
|
852
|
+
character: string;
|
|
853
|
+
Install: string;
|
|
854
|
+
})[];
|
|
855
|
+
/**
|
|
856
|
+
* Generate a unique ID for a federated card
|
|
857
|
+
*
|
|
858
|
+
* @security localId is URI-encoded to prevent path traversal and query injection.
|
|
859
|
+
* Characters like '/', '?', '#', and spaces are safely escaped.
|
|
860
|
+
*/
|
|
861
|
+
export declare function generateCardId(baseUrl: string, localId: string): string;
|
|
862
|
+
/**
|
|
863
|
+
* Generate a unique ID for an activity using crypto-grade randomness
|
|
864
|
+
*/
|
|
865
|
+
export declare function generateActivityId(baseUrl: string): string;
|
|
866
|
+
/**
|
|
867
|
+
* Convert a CCv3 card to ActivityPub FederatedCard format
|
|
868
|
+
*/
|
|
869
|
+
export declare function cardToActivityPub(card: CCv3Data, options: {
|
|
870
|
+
id: string;
|
|
871
|
+
actorId: string;
|
|
872
|
+
published?: string;
|
|
873
|
+
updated?: string;
|
|
874
|
+
sourcePlatform?: PlatformId;
|
|
875
|
+
sourceId?: string;
|
|
876
|
+
sourceUrl?: string;
|
|
877
|
+
attachments?: Array<{
|
|
878
|
+
type: "Image" | "Audio" | "Document";
|
|
879
|
+
mediaType: string;
|
|
880
|
+
url: string;
|
|
881
|
+
name?: string;
|
|
882
|
+
}>;
|
|
883
|
+
}): FederatedCard;
|
|
884
|
+
/**
|
|
885
|
+
* Extract CCv3 card from ActivityPub FederatedCard
|
|
886
|
+
*/
|
|
887
|
+
export declare function cardFromActivityPub(federatedCard: FederatedCard): CCv3Data;
|
|
888
|
+
/**
|
|
889
|
+
* Create a Create activity for a new card
|
|
890
|
+
*/
|
|
891
|
+
export declare function createCreateActivity(card: FederatedCard, actorId: string, baseUrl: string, recipients?: {
|
|
892
|
+
to?: string[];
|
|
893
|
+
cc?: string[];
|
|
894
|
+
}): FederatedActivity;
|
|
895
|
+
/**
|
|
896
|
+
* Create an Update activity for a modified card
|
|
897
|
+
*/
|
|
898
|
+
export declare function createUpdateActivity(card: FederatedCard, actorId: string, baseUrl: string, recipients?: {
|
|
899
|
+
to?: string[];
|
|
900
|
+
cc?: string[];
|
|
901
|
+
}): FederatedActivity;
|
|
902
|
+
/**
|
|
903
|
+
* Create a Delete activity for a removed card
|
|
904
|
+
*/
|
|
905
|
+
export declare function createDeleteActivity(cardId: string, actorId: string, baseUrl: string, recipients?: {
|
|
906
|
+
to?: string[];
|
|
907
|
+
cc?: string[];
|
|
908
|
+
}): FederatedActivity;
|
|
909
|
+
/**
|
|
910
|
+
* Create an Announce (reshare) activity
|
|
911
|
+
*/
|
|
912
|
+
export declare function createAnnounceActivity(cardId: string, actorId: string, baseUrl: string, recipients?: {
|
|
913
|
+
to?: string[];
|
|
914
|
+
cc?: string[];
|
|
915
|
+
}): FederatedActivity;
|
|
916
|
+
/**
|
|
917
|
+
* Create a Like activity
|
|
918
|
+
*/
|
|
919
|
+
export declare function createLikeActivity(cardId: string, actorId: string, baseUrl: string): FederatedActivity;
|
|
920
|
+
/**
|
|
921
|
+
* Create an Undo activity
|
|
922
|
+
*/
|
|
923
|
+
export declare function createUndoActivity(originalActivityId: string, actorId: string, baseUrl: string): FederatedActivity;
|
|
924
|
+
/**
|
|
925
|
+
* Create a Fork activity for card derivation
|
|
926
|
+
*
|
|
927
|
+
* Fork activities notify the source instance that a card was forked.
|
|
928
|
+
* The forked card contains a reference to the source in its extensions.
|
|
929
|
+
*/
|
|
930
|
+
export declare function createForkActivity(sourceCardId: string, forkedCard: FederatedCard, actorId: string, baseUrl: string, recipients?: {
|
|
931
|
+
to?: string[];
|
|
932
|
+
cc?: string[];
|
|
933
|
+
}): ForkActivity;
|
|
934
|
+
/**
|
|
935
|
+
* Parse an incoming Fork activity
|
|
936
|
+
*
|
|
937
|
+
* @returns Parsed fork data or null if invalid
|
|
938
|
+
*/
|
|
939
|
+
export declare function parseForkActivity(activity: unknown): {
|
|
940
|
+
sourceCardId: string;
|
|
941
|
+
forkedCard: FederatedCard;
|
|
942
|
+
actor: string;
|
|
943
|
+
activityId: string;
|
|
944
|
+
} | null;
|
|
945
|
+
/**
|
|
946
|
+
* Create an Install activity for notifying hub about card installation
|
|
947
|
+
*
|
|
948
|
+
* Consumers (SillyTavern, Voxta) send this when a card is installed/saved.
|
|
949
|
+
*/
|
|
950
|
+
export declare function createInstallActivity(cardId: string, actorId: string, baseUrl: string, platform: PlatformId, recipients?: {
|
|
951
|
+
to?: string[];
|
|
952
|
+
cc?: string[];
|
|
953
|
+
}): InstallActivity;
|
|
954
|
+
/**
|
|
955
|
+
* Parse an incoming Install activity
|
|
956
|
+
*
|
|
957
|
+
* @returns Parsed install data or null if invalid
|
|
958
|
+
*/
|
|
959
|
+
export declare function parseInstallActivity(activity: unknown): {
|
|
960
|
+
cardId: string;
|
|
961
|
+
actor: string;
|
|
962
|
+
platform: PlatformId | null;
|
|
963
|
+
activityId: string;
|
|
964
|
+
} | null;
|
|
965
|
+
/**
|
|
966
|
+
* Create a minimal actor object
|
|
967
|
+
*/
|
|
968
|
+
export declare function createActor(options: {
|
|
969
|
+
id: string;
|
|
970
|
+
username: string;
|
|
971
|
+
displayName: string;
|
|
972
|
+
summary?: string;
|
|
973
|
+
icon?: string;
|
|
974
|
+
baseUrl: string;
|
|
975
|
+
publicKeyPem?: string;
|
|
976
|
+
}): FederatedActor;
|
|
977
|
+
/**
|
|
978
|
+
* Parse an incoming activity
|
|
979
|
+
*/
|
|
980
|
+
export declare function parseActivity(data: unknown): FederatedActivity;
|
|
981
|
+
/**
|
|
982
|
+
* Sync Engine
|
|
983
|
+
*
|
|
984
|
+
* Handles synchronization of character cards across platforms.
|
|
985
|
+
*/
|
|
986
|
+
/**
|
|
987
|
+
* Sync Engine Options
|
|
988
|
+
*/
|
|
989
|
+
export interface SyncEngineOptions {
|
|
990
|
+
/** Base URL for this federation instance */
|
|
991
|
+
baseUrl: string;
|
|
992
|
+
/** Actor ID for this instance */
|
|
993
|
+
actorId: string;
|
|
994
|
+
/** State storage */
|
|
995
|
+
stateStore: SyncStateStore;
|
|
996
|
+
/** Auto-sync interval in ms (0 to disable) */
|
|
997
|
+
autoSyncInterval?: number;
|
|
998
|
+
/**
|
|
999
|
+
* Use SHA-256 for change detection instead of fast 32-bit hash.
|
|
1000
|
+
*
|
|
1001
|
+
* Default: false (fast hash for backwards compatibility)
|
|
1002
|
+
* Recommended: true for federation sync across untrusted systems
|
|
1003
|
+
*
|
|
1004
|
+
* @security Fast hash has collision potential (~2^16 birthday bound).
|
|
1005
|
+
* SHA-256 is cryptographically secure but slightly slower.
|
|
1006
|
+
*/
|
|
1007
|
+
secureHashing?: boolean;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Sync Engine
|
|
1011
|
+
* Coordinates synchronization between registered platforms
|
|
1012
|
+
*/
|
|
1013
|
+
export declare class SyncEngine {
|
|
1014
|
+
private platforms;
|
|
1015
|
+
private stateStore;
|
|
1016
|
+
private baseUrl;
|
|
1017
|
+
private actorId;
|
|
1018
|
+
private listeners;
|
|
1019
|
+
private autoSyncTimer?;
|
|
1020
|
+
private secureHashing;
|
|
1021
|
+
/**
|
|
1022
|
+
* Mutex flag to prevent concurrent syncAll() executions.
|
|
1023
|
+
* When autoSyncInterval triggers while a sync is in progress,
|
|
1024
|
+
* the new sync is skipped to prevent race conditions.
|
|
1025
|
+
*/
|
|
1026
|
+
private syncInProgress;
|
|
1027
|
+
constructor(options: SyncEngineOptions);
|
|
1028
|
+
/**
|
|
1029
|
+
* Generate a hash for change detection.
|
|
1030
|
+
* Uses SHA-256 if secureHashing is enabled, otherwise fast 32-bit hash.
|
|
1031
|
+
*/
|
|
1032
|
+
private hashCard;
|
|
1033
|
+
/**
|
|
1034
|
+
* Register a platform adapter
|
|
1035
|
+
*/
|
|
1036
|
+
registerPlatform(adapter: PlatformAdapter): void;
|
|
1037
|
+
/**
|
|
1038
|
+
* Unregister a platform adapter
|
|
1039
|
+
*/
|
|
1040
|
+
unregisterPlatform(platform: PlatformId): void;
|
|
1041
|
+
/**
|
|
1042
|
+
* Get registered platforms
|
|
1043
|
+
*/
|
|
1044
|
+
getPlatforms(): PlatformId[];
|
|
1045
|
+
/**
|
|
1046
|
+
* Add event listener
|
|
1047
|
+
*/
|
|
1048
|
+
on(event: FederationEventType, listener: FederationEventListener): void;
|
|
1049
|
+
/**
|
|
1050
|
+
* Remove event listener
|
|
1051
|
+
*/
|
|
1052
|
+
off(event: FederationEventType, listener: FederationEventListener): void;
|
|
1053
|
+
/**
|
|
1054
|
+
* Emit an event
|
|
1055
|
+
*/
|
|
1056
|
+
private emit;
|
|
1057
|
+
/**
|
|
1058
|
+
* Push a card from one platform to another
|
|
1059
|
+
*/
|
|
1060
|
+
pushCard(sourcePlatform: PlatformId, sourceId: string, targetPlatform: PlatformId): Promise<SyncResult>;
|
|
1061
|
+
/**
|
|
1062
|
+
* Pull a card from a remote platform to local
|
|
1063
|
+
*/
|
|
1064
|
+
pullCard(remotePlatform: PlatformId, remoteId: string, localPlatform: PlatformId): Promise<SyncResult>;
|
|
1065
|
+
/**
|
|
1066
|
+
* Sync a card across all registered platforms
|
|
1067
|
+
*/
|
|
1068
|
+
syncCardToAll(sourcePlatform: PlatformId, sourceId: string): Promise<SyncResult[]>;
|
|
1069
|
+
/**
|
|
1070
|
+
* Sync all cards from one platform to another
|
|
1071
|
+
*/
|
|
1072
|
+
syncPlatform(sourcePlatform: PlatformId, targetPlatform: PlatformId): Promise<SyncResult[]>;
|
|
1073
|
+
/**
|
|
1074
|
+
* Sync all platforms with each other.
|
|
1075
|
+
*
|
|
1076
|
+
* Includes mutex protection to prevent concurrent executions when
|
|
1077
|
+
* triggered by autoSyncInterval. If a sync is already in progress,
|
|
1078
|
+
* subsequent calls are skipped and emit a 'sync:skipped' event.
|
|
1079
|
+
*/
|
|
1080
|
+
syncAll(): Promise<Map<string, SyncResult[]>>;
|
|
1081
|
+
/**
|
|
1082
|
+
* Get sync state for a card
|
|
1083
|
+
*/
|
|
1084
|
+
getSyncState(federatedId: string): Promise<CardSyncState | null>;
|
|
1085
|
+
/**
|
|
1086
|
+
* Find sync state by platform ID
|
|
1087
|
+
*/
|
|
1088
|
+
findSyncState(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
|
|
1089
|
+
/**
|
|
1090
|
+
* Resolve a sync conflict by choosing a version
|
|
1091
|
+
*/
|
|
1092
|
+
resolveConflict(federatedId: string, resolution: "local" | "remote" | "merge", mergedCard?: CCv3Data): Promise<SyncResult>;
|
|
1093
|
+
/**
|
|
1094
|
+
* Fork a card from a remote source
|
|
1095
|
+
*
|
|
1096
|
+
* Creates a local copy of a remote card with fork metadata stored in
|
|
1097
|
+
* the card's extensions and sync state. Optionally notifies the source
|
|
1098
|
+
* instance about the fork.
|
|
1099
|
+
*
|
|
1100
|
+
* @param sourceFederatedId - Federated URI of the source card
|
|
1101
|
+
* @param sourcePlatform - Platform where the source card resides
|
|
1102
|
+
* @param targetPlatform - Platform to save the fork to
|
|
1103
|
+
* @param options - Fork options
|
|
1104
|
+
*/
|
|
1105
|
+
forkCard(sourceFederatedId: string, sourcePlatform: PlatformId, targetPlatform: PlatformId, options?: {
|
|
1106
|
+
modifications?: Partial<CCv3Data["data"]>;
|
|
1107
|
+
notifySource?: boolean;
|
|
1108
|
+
sourceInbox?: string;
|
|
1109
|
+
}): Promise<ForkResult>;
|
|
1110
|
+
/**
|
|
1111
|
+
* Create a forked card with fork metadata in extensions
|
|
1112
|
+
*/
|
|
1113
|
+
private createForkedCard;
|
|
1114
|
+
/**
|
|
1115
|
+
* Handle incoming fork notification
|
|
1116
|
+
*
|
|
1117
|
+
* Called when another instance notifies us that they forked one of our cards.
|
|
1118
|
+
* Increments the fork count and stores the notification.
|
|
1119
|
+
*/
|
|
1120
|
+
handleForkNotification(activity: ForkActivity): Promise<void>;
|
|
1121
|
+
/**
|
|
1122
|
+
* Handle incoming install notification
|
|
1123
|
+
*
|
|
1124
|
+
* Called when a consumer (SillyTavern, Voxta) notifies us that they installed one of our cards.
|
|
1125
|
+
* Increments the install count and stores the notification.
|
|
1126
|
+
*/
|
|
1127
|
+
handleInstallNotification(activity: InstallActivity): Promise<void>;
|
|
1128
|
+
/**
|
|
1129
|
+
* Get stats for a card
|
|
1130
|
+
*/
|
|
1131
|
+
getCardStats(federatedId: string): Promise<CardStats | null>;
|
|
1132
|
+
/**
|
|
1133
|
+
* Get fork count for a card
|
|
1134
|
+
*/
|
|
1135
|
+
getForkCount(federatedId: string): Promise<number>;
|
|
1136
|
+
/**
|
|
1137
|
+
* Get install count for a card
|
|
1138
|
+
*/
|
|
1139
|
+
getInstallCount(federatedId: string): Promise<number>;
|
|
1140
|
+
/**
|
|
1141
|
+
* Find all local forks of a source card
|
|
1142
|
+
*/
|
|
1143
|
+
findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
|
|
1144
|
+
/**
|
|
1145
|
+
* Stop the sync engine
|
|
1146
|
+
*/
|
|
1147
|
+
dispose(): void;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Sync State Stores
|
|
1151
|
+
*
|
|
1152
|
+
* Implementations for storing sync state.
|
|
1153
|
+
*/
|
|
1154
|
+
/**
|
|
1155
|
+
* In-memory sync state store
|
|
1156
|
+
* Useful for testing and single-session sync
|
|
1157
|
+
*/
|
|
1158
|
+
export declare class MemorySyncStateStore implements SyncStateStore {
|
|
1159
|
+
private states;
|
|
1160
|
+
get(federatedId: string): Promise<CardSyncState | null>;
|
|
1161
|
+
set(state: CardSyncState): Promise<void>;
|
|
1162
|
+
delete(federatedId: string): Promise<void>;
|
|
1163
|
+
list(): Promise<CardSyncState[]>;
|
|
1164
|
+
findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
|
|
1165
|
+
/**
|
|
1166
|
+
* Clear all states (for testing)
|
|
1167
|
+
*/
|
|
1168
|
+
clear(): void;
|
|
1169
|
+
/**
|
|
1170
|
+
* Increment fork count and add notification
|
|
1171
|
+
*/
|
|
1172
|
+
incrementForkCount(federatedId: string, notification: ForkNotification): Promise<void>;
|
|
1173
|
+
/**
|
|
1174
|
+
* Get fork count for a card
|
|
1175
|
+
*/
|
|
1176
|
+
getForkCount(federatedId: string): Promise<number>;
|
|
1177
|
+
/**
|
|
1178
|
+
* Find all cards that are forks of a given source card
|
|
1179
|
+
*/
|
|
1180
|
+
findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* JSON file-based sync state store
|
|
1184
|
+
* Persists state to a JSON file
|
|
1185
|
+
*/
|
|
1186
|
+
export declare class FileSyncStateStore implements SyncStateStore {
|
|
1187
|
+
private states;
|
|
1188
|
+
private filePath;
|
|
1189
|
+
private saveDebounce?;
|
|
1190
|
+
private fs;
|
|
1191
|
+
constructor(filePath: string, fs: {
|
|
1192
|
+
readFile: (path: string, encoding: string) => Promise<string>;
|
|
1193
|
+
writeFile: (path: string, data: string) => Promise<void>;
|
|
1194
|
+
mkdir: (path: string, options: {
|
|
1195
|
+
recursive: boolean;
|
|
1196
|
+
}) => Promise<void>;
|
|
1197
|
+
});
|
|
1198
|
+
/**
|
|
1199
|
+
* Load state from file
|
|
1200
|
+
*/
|
|
1201
|
+
load(): Promise<void>;
|
|
1202
|
+
/**
|
|
1203
|
+
* Save state to file (debounced)
|
|
1204
|
+
*/
|
|
1205
|
+
private scheduleSave;
|
|
1206
|
+
/**
|
|
1207
|
+
* Save state to file immediately
|
|
1208
|
+
*/
|
|
1209
|
+
saveNow(): Promise<void>;
|
|
1210
|
+
get(federatedId: string): Promise<CardSyncState | null>;
|
|
1211
|
+
set(state: CardSyncState): Promise<void>;
|
|
1212
|
+
delete(federatedId: string): Promise<void>;
|
|
1213
|
+
list(): Promise<CardSyncState[]>;
|
|
1214
|
+
findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
|
|
1215
|
+
/**
|
|
1216
|
+
* Increment fork count and add notification
|
|
1217
|
+
*/
|
|
1218
|
+
incrementForkCount(federatedId: string, notification: ForkNotification): Promise<void>;
|
|
1219
|
+
/**
|
|
1220
|
+
* Get fork count for a card
|
|
1221
|
+
*/
|
|
1222
|
+
getForkCount(federatedId: string): Promise<number>;
|
|
1223
|
+
/**
|
|
1224
|
+
* Find all cards that are forks of a given source card
|
|
1225
|
+
*/
|
|
1226
|
+
findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Storage interface (compatible with Web Storage API)
|
|
1230
|
+
*/
|
|
1231
|
+
export interface StorageInterface {
|
|
1232
|
+
getItem(key: string): string | null;
|
|
1233
|
+
setItem(key: string, value: string): void;
|
|
1234
|
+
removeItem(key: string): void;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Create a state store backed by a Storage interface (e.g., localStorage in browser)
|
|
1238
|
+
*
|
|
1239
|
+
* @param key - Prefix key for storage entries
|
|
1240
|
+
* @param storage - Storage interface (pass localStorage in browser, or a polyfill in Node.js)
|
|
1241
|
+
*/
|
|
1242
|
+
export declare function createLocalStorageStore(key: string, storage: StorageInterface): SyncStateStore;
|
|
1243
|
+
/**
|
|
1244
|
+
* D1 Sync State Store
|
|
1245
|
+
*
|
|
1246
|
+
* Cloudflare D1-compatible implementation of SyncStateStore for production
|
|
1247
|
+
* federation support on Cloudflare Workers.
|
|
1248
|
+
*/
|
|
1249
|
+
/**
|
|
1250
|
+
* Minimal D1Database interface
|
|
1251
|
+
* Compatible with Cloudflare Workers D1 API
|
|
1252
|
+
*/
|
|
1253
|
+
export interface D1Database {
|
|
1254
|
+
prepare(query: string): D1PreparedStatement;
|
|
1255
|
+
exec(query: string): Promise<D1ExecResult>;
|
|
1256
|
+
batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
|
|
1257
|
+
}
|
|
1258
|
+
export interface D1PreparedStatement {
|
|
1259
|
+
bind(...values: unknown[]): D1PreparedStatement;
|
|
1260
|
+
first<T = unknown>(column?: string): Promise<T | null>;
|
|
1261
|
+
run(): Promise<D1Result>;
|
|
1262
|
+
all<T = unknown>(): Promise<D1Result<T>>;
|
|
1263
|
+
}
|
|
1264
|
+
export interface D1Result<T = unknown> {
|
|
1265
|
+
results: T[];
|
|
1266
|
+
success: boolean;
|
|
1267
|
+
meta: {
|
|
1268
|
+
duration: number;
|
|
1269
|
+
changes: number;
|
|
1270
|
+
last_row_id: number;
|
|
1271
|
+
served_by?: string;
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
export interface D1ExecResult {
|
|
1275
|
+
count: number;
|
|
1276
|
+
duration: number;
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* D1-compatible implementation of SyncStateStore
|
|
1280
|
+
*
|
|
1281
|
+
* Stores federation sync state in Cloudflare D1 (SQLite).
|
|
1282
|
+
*
|
|
1283
|
+
* @example
|
|
1284
|
+
* ```typescript
|
|
1285
|
+
* const store = new D1SyncStateStore(env.DB);
|
|
1286
|
+
* await store.init();
|
|
1287
|
+
*
|
|
1288
|
+
* // Use with SyncEngine
|
|
1289
|
+
* const engine = new SyncEngine({
|
|
1290
|
+
* stateStore: store,
|
|
1291
|
+
* // ...
|
|
1292
|
+
* });
|
|
1293
|
+
* ```
|
|
1294
|
+
*/
|
|
1295
|
+
export declare class D1SyncStateStore implements SyncStateStore {
|
|
1296
|
+
private db;
|
|
1297
|
+
private tableName;
|
|
1298
|
+
/**
|
|
1299
|
+
* Create a new D1SyncStateStore
|
|
1300
|
+
*
|
|
1301
|
+
* @param db - D1Database instance (from env.DB in Workers)
|
|
1302
|
+
* @param tableName - Table name for storing sync state (default: 'federation_sync_state')
|
|
1303
|
+
* @throws If tableName contains invalid characters (SQL injection prevention)
|
|
1304
|
+
*/
|
|
1305
|
+
constructor(db: D1Database, tableName?: string);
|
|
1306
|
+
/**
|
|
1307
|
+
* Initialize the database table
|
|
1308
|
+
*
|
|
1309
|
+
* Creates the sync state table if it doesn't exist.
|
|
1310
|
+
* Safe to call multiple times (idempotent).
|
|
1311
|
+
*/
|
|
1312
|
+
init(): Promise<void>;
|
|
1313
|
+
/**
|
|
1314
|
+
* Get sync state for a federated card ID
|
|
1315
|
+
*/
|
|
1316
|
+
get(federatedId: string): Promise<CardSyncState | null>;
|
|
1317
|
+
/**
|
|
1318
|
+
* Save or update sync state
|
|
1319
|
+
*/
|
|
1320
|
+
set(state: CardSyncState): Promise<void>;
|
|
1321
|
+
/**
|
|
1322
|
+
* Delete sync state for a federated card ID
|
|
1323
|
+
*/
|
|
1324
|
+
delete(federatedId: string): Promise<void>;
|
|
1325
|
+
/**
|
|
1326
|
+
* List all sync states
|
|
1327
|
+
*/
|
|
1328
|
+
list(): Promise<CardSyncState[]>;
|
|
1329
|
+
/**
|
|
1330
|
+
* Find sync state by platform-specific ID
|
|
1331
|
+
*
|
|
1332
|
+
* @param platform - Platform identifier
|
|
1333
|
+
* @param platformId - Platform-specific card ID
|
|
1334
|
+
* @returns Sync state if found, null otherwise
|
|
1335
|
+
*/
|
|
1336
|
+
findByPlatformId(platform: PlatformId, platformId: string): Promise<CardSyncState | null>;
|
|
1337
|
+
/**
|
|
1338
|
+
* Find sync state by local ID
|
|
1339
|
+
*
|
|
1340
|
+
* @param localId - Local card ID
|
|
1341
|
+
* @returns Sync state if found, null otherwise
|
|
1342
|
+
*/
|
|
1343
|
+
findByLocalId(localId: string): Promise<CardSyncState | null>;
|
|
1344
|
+
/**
|
|
1345
|
+
* Get count of all sync states
|
|
1346
|
+
*/
|
|
1347
|
+
count(): Promise<number>;
|
|
1348
|
+
/**
|
|
1349
|
+
* List sync states by status
|
|
1350
|
+
*
|
|
1351
|
+
* @param status - Status to filter by
|
|
1352
|
+
* @returns Array of sync states with matching status
|
|
1353
|
+
*/
|
|
1354
|
+
listByStatus(status: CardSyncState["status"]): Promise<CardSyncState[]>;
|
|
1355
|
+
/**
|
|
1356
|
+
* Clear all sync states (for testing)
|
|
1357
|
+
*
|
|
1358
|
+
* ⚠️ Use with caution - this deletes all data
|
|
1359
|
+
*/
|
|
1360
|
+
clear(): Promise<void>;
|
|
1361
|
+
/**
|
|
1362
|
+
* Increment fork count and add notification for a source card
|
|
1363
|
+
*
|
|
1364
|
+
* Used when receiving a Fork activity to track that someone forked this card.
|
|
1365
|
+
* Notifications are capped at 100 to prevent unbounded growth.
|
|
1366
|
+
*/
|
|
1367
|
+
incrementForkCount(federatedId: string, notification: ForkNotification): Promise<void>;
|
|
1368
|
+
/**
|
|
1369
|
+
* Get fork count for a card
|
|
1370
|
+
*/
|
|
1371
|
+
getForkCount(federatedId: string): Promise<number>;
|
|
1372
|
+
/**
|
|
1373
|
+
* Find all cards that are forks of a given source card
|
|
1374
|
+
*/
|
|
1375
|
+
findForks(sourceFederatedId: string): Promise<CardSyncState[]>;
|
|
1376
|
+
/**
|
|
1377
|
+
* Convert database row to CardSyncState
|
|
1378
|
+
*/
|
|
1379
|
+
private rowToState;
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Base Platform Adapter
|
|
1383
|
+
*
|
|
1384
|
+
* Abstract base class for platform adapters.
|
|
1385
|
+
*/
|
|
1386
|
+
/**
|
|
1387
|
+
* Card with metadata from adapter
|
|
1388
|
+
*/
|
|
1389
|
+
export interface AdapterCard {
|
|
1390
|
+
id: string;
|
|
1391
|
+
card: CCv3Data;
|
|
1392
|
+
updatedAt: string;
|
|
1393
|
+
createdAt?: string;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Asset from adapter
|
|
1397
|
+
*/
|
|
1398
|
+
export interface AdapterAsset {
|
|
1399
|
+
name: string;
|
|
1400
|
+
type: string;
|
|
1401
|
+
data: Uint8Array;
|
|
1402
|
+
mimeType?: string;
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Abstract base adapter with common functionality
|
|
1406
|
+
*/
|
|
1407
|
+
export declare abstract class BasePlatformAdapter implements PlatformAdapter {
|
|
1408
|
+
abstract readonly platform: PlatformId;
|
|
1409
|
+
abstract readonly displayName: string;
|
|
1410
|
+
/**
|
|
1411
|
+
* Check if platform is available
|
|
1412
|
+
*/
|
|
1413
|
+
abstract isAvailable(): Promise<boolean>;
|
|
1414
|
+
/**
|
|
1415
|
+
* Get a card by local ID
|
|
1416
|
+
*/
|
|
1417
|
+
abstract getCard(localId: string): Promise<CCv3Data | null>;
|
|
1418
|
+
/**
|
|
1419
|
+
* List all cards
|
|
1420
|
+
*/
|
|
1421
|
+
abstract listCards(options?: {
|
|
1422
|
+
limit?: number;
|
|
1423
|
+
offset?: number;
|
|
1424
|
+
since?: string;
|
|
1425
|
+
}): Promise<AdapterCard[]>;
|
|
1426
|
+
/**
|
|
1427
|
+
* Save/update a card
|
|
1428
|
+
*/
|
|
1429
|
+
abstract saveCard(card: CCv3Data, localId?: string): Promise<string>;
|
|
1430
|
+
/**
|
|
1431
|
+
* Delete a card
|
|
1432
|
+
*/
|
|
1433
|
+
abstract deleteCard(localId: string): Promise<boolean>;
|
|
1434
|
+
/**
|
|
1435
|
+
* Get card assets
|
|
1436
|
+
*/
|
|
1437
|
+
abstract getAssets(localId: string): Promise<AdapterAsset[]>;
|
|
1438
|
+
/**
|
|
1439
|
+
* Get last modified timestamp
|
|
1440
|
+
*/
|
|
1441
|
+
abstract getLastModified(localId: string): Promise<string | null>;
|
|
1442
|
+
/**
|
|
1443
|
+
* Generate a new local ID using crypto-grade randomness
|
|
1444
|
+
*/
|
|
1445
|
+
protected generateId(): string;
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* In-memory adapter for testing
|
|
1449
|
+
*/
|
|
1450
|
+
export declare class MemoryPlatformAdapter extends BasePlatformAdapter {
|
|
1451
|
+
readonly platform: PlatformId;
|
|
1452
|
+
readonly displayName: string;
|
|
1453
|
+
private cards;
|
|
1454
|
+
private assets;
|
|
1455
|
+
constructor(platform?: PlatformId, displayName?: string);
|
|
1456
|
+
isAvailable(): Promise<boolean>;
|
|
1457
|
+
getCard(localId: string): Promise<CCv3Data | null>;
|
|
1458
|
+
listCards(options?: {
|
|
1459
|
+
limit?: number;
|
|
1460
|
+
offset?: number;
|
|
1461
|
+
since?: string;
|
|
1462
|
+
}): Promise<AdapterCard[]>;
|
|
1463
|
+
saveCard(card: CCv3Data, localId?: string): Promise<string>;
|
|
1464
|
+
deleteCard(localId: string): Promise<boolean>;
|
|
1465
|
+
getAssets(localId: string): Promise<AdapterAsset[]>;
|
|
1466
|
+
getLastModified(localId: string): Promise<string | null>;
|
|
1467
|
+
/**
|
|
1468
|
+
* Set assets for a card (for testing)
|
|
1469
|
+
*/
|
|
1470
|
+
setAssets(localId: string, assets: AdapterAsset[]): void;
|
|
1471
|
+
/**
|
|
1472
|
+
* Clear all data (for testing)
|
|
1473
|
+
*/
|
|
1474
|
+
clear(): void;
|
|
1475
|
+
/**
|
|
1476
|
+
* Get card count
|
|
1477
|
+
*/
|
|
1478
|
+
count(): number;
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* HTTP Platform Adapter
|
|
1482
|
+
*
|
|
1483
|
+
* Adapter for platforms accessible via HTTP API.
|
|
1484
|
+
*/
|
|
1485
|
+
/**
|
|
1486
|
+
* HTTP fetch function type
|
|
1487
|
+
*/
|
|
1488
|
+
export type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
|
|
1489
|
+
/**
|
|
1490
|
+
* HTTP adapter configuration
|
|
1491
|
+
*/
|
|
1492
|
+
export interface HttpAdapterConfig {
|
|
1493
|
+
/** Platform ID */
|
|
1494
|
+
platform: PlatformId;
|
|
1495
|
+
/** Display name */
|
|
1496
|
+
displayName: string;
|
|
1497
|
+
/** Base URL of the API */
|
|
1498
|
+
baseUrl: string;
|
|
1499
|
+
/** API endpoints */
|
|
1500
|
+
endpoints: {
|
|
1501
|
+
/** List cards: GET */
|
|
1502
|
+
list: string;
|
|
1503
|
+
/** Get card: GET (append /:id) */
|
|
1504
|
+
get: string;
|
|
1505
|
+
/** Create card: POST */
|
|
1506
|
+
create: string;
|
|
1507
|
+
/** Update card: PUT/PATCH (append /:id) */
|
|
1508
|
+
update: string;
|
|
1509
|
+
/** Delete card: DELETE (append /:id) */
|
|
1510
|
+
delete: string;
|
|
1511
|
+
/** Get assets: GET (append /:id) */
|
|
1512
|
+
assets?: string;
|
|
1513
|
+
/** Health check: GET */
|
|
1514
|
+
health?: string;
|
|
1515
|
+
};
|
|
1516
|
+
/** Authentication header */
|
|
1517
|
+
auth?: {
|
|
1518
|
+
type: "bearer" | "api-key" | "basic";
|
|
1519
|
+
token: string;
|
|
1520
|
+
header?: string;
|
|
1521
|
+
};
|
|
1522
|
+
/** Custom fetch function (for Node.js or testing) */
|
|
1523
|
+
fetch?: FetchFn;
|
|
1524
|
+
/**
|
|
1525
|
+
* Request timeout in milliseconds.
|
|
1526
|
+
* Default: 30000 (30 seconds)
|
|
1527
|
+
*
|
|
1528
|
+
* @security Prevents hanging connections that could cause resource exhaustion.
|
|
1529
|
+
*/
|
|
1530
|
+
timeout?: number;
|
|
1531
|
+
/** Response transformers */
|
|
1532
|
+
transformers?: {
|
|
1533
|
+
/** Transform list response to AdapterCard[] */
|
|
1534
|
+
list?: (response: unknown) => AdapterCard[];
|
|
1535
|
+
/** Transform get response to CCv3Data */
|
|
1536
|
+
get?: (response: unknown) => CCv3Data;
|
|
1537
|
+
/** Transform card to create request body */
|
|
1538
|
+
create?: (card: CCv3Data) => unknown;
|
|
1539
|
+
/** Transform card to update request body */
|
|
1540
|
+
update?: (card: CCv3Data) => unknown;
|
|
1541
|
+
/** Extract ID from create response */
|
|
1542
|
+
extractId?: (response: unknown) => string;
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Error thrown when resource ID validation fails
|
|
1547
|
+
*/
|
|
1548
|
+
export declare class InvalidResourceIdError extends Error {
|
|
1549
|
+
readonly id: string;
|
|
1550
|
+
readonly reason: string;
|
|
1551
|
+
constructor(id: string, reason: string);
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* HTTP-based platform adapter
|
|
1555
|
+
*/
|
|
1556
|
+
export declare class HttpPlatformAdapter extends BasePlatformAdapter {
|
|
1557
|
+
readonly platform: PlatformId;
|
|
1558
|
+
readonly displayName: string;
|
|
1559
|
+
private config;
|
|
1560
|
+
private fetchFn;
|
|
1561
|
+
private timeoutMs;
|
|
1562
|
+
constructor(config: HttpAdapterConfig);
|
|
1563
|
+
/**
|
|
1564
|
+
* Execute fetch with timeout using AbortController
|
|
1565
|
+
*
|
|
1566
|
+
* @security Prevents hanging connections that could cause resource exhaustion
|
|
1567
|
+
*/
|
|
1568
|
+
private fetchWithTimeout;
|
|
1569
|
+
/**
|
|
1570
|
+
* Build headers for requests
|
|
1571
|
+
*/
|
|
1572
|
+
private buildHeaders;
|
|
1573
|
+
/**
|
|
1574
|
+
* Build full URL with optional resource ID.
|
|
1575
|
+
*
|
|
1576
|
+
* @security ID is validated and encoded to prevent SSRF/path traversal.
|
|
1577
|
+
* @throws InvalidResourceIdError if ID contains malicious patterns
|
|
1578
|
+
*/
|
|
1579
|
+
private buildUrl;
|
|
1580
|
+
isAvailable(): Promise<boolean>;
|
|
1581
|
+
getCard(localId: string): Promise<CCv3Data | null>;
|
|
1582
|
+
listCards(options?: {
|
|
1583
|
+
limit?: number;
|
|
1584
|
+
offset?: number;
|
|
1585
|
+
since?: string;
|
|
1586
|
+
}): Promise<AdapterCard[]>;
|
|
1587
|
+
saveCard(card: CCv3Data, localId?: string): Promise<string>;
|
|
1588
|
+
deleteCard(localId: string): Promise<boolean>;
|
|
1589
|
+
getAssets(localId: string): Promise<AdapterAsset[]>;
|
|
1590
|
+
getLastModified(localId: string): Promise<string | null>;
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Create an HTTP adapter for Character Archive API
|
|
1594
|
+
*/
|
|
1595
|
+
export declare function createArchiveAdapter(baseUrl: string, apiKey?: string): HttpPlatformAdapter;
|
|
1596
|
+
/**
|
|
1597
|
+
* Create an HTTP adapter for CardsHub API
|
|
1598
|
+
*/
|
|
1599
|
+
export declare function createHubAdapter(baseUrl: string, apiKey?: string): HttpPlatformAdapter;
|
|
1600
|
+
/**
|
|
1601
|
+
* SillyTavern Platform Adapter
|
|
1602
|
+
*
|
|
1603
|
+
* Adapter for SillyTavern integration via plugin.
|
|
1604
|
+
* This provides the interface that a SillyTavern plugin would implement.
|
|
1605
|
+
*/
|
|
1606
|
+
/**
|
|
1607
|
+
* SillyTavern character format (simplified)
|
|
1608
|
+
*/
|
|
1609
|
+
export interface STCharacter {
|
|
1610
|
+
/** Character filename/ID */
|
|
1611
|
+
name: string;
|
|
1612
|
+
/** Avatar filename */
|
|
1613
|
+
avatar: string;
|
|
1614
|
+
/** Character data (TavernCard format) */
|
|
1615
|
+
data: {
|
|
1616
|
+
name: string;
|
|
1617
|
+
description: string;
|
|
1618
|
+
personality: string;
|
|
1619
|
+
scenario: string;
|
|
1620
|
+
first_mes: string;
|
|
1621
|
+
mes_example: string;
|
|
1622
|
+
creator_notes?: string;
|
|
1623
|
+
system_prompt?: string;
|
|
1624
|
+
post_history_instructions?: string;
|
|
1625
|
+
alternate_greetings?: string[];
|
|
1626
|
+
character_book?: {
|
|
1627
|
+
entries: Array<{
|
|
1628
|
+
keys: string[];
|
|
1629
|
+
content: string;
|
|
1630
|
+
enabled: boolean;
|
|
1631
|
+
[key: string]: unknown;
|
|
1632
|
+
}>;
|
|
1633
|
+
[key: string]: unknown;
|
|
1634
|
+
};
|
|
1635
|
+
tags?: string[];
|
|
1636
|
+
creator?: string;
|
|
1637
|
+
character_version?: string;
|
|
1638
|
+
extensions?: Record<string, unknown>;
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Usage stats for a character in SillyTavern
|
|
1643
|
+
*/
|
|
1644
|
+
export interface STCharacterStats {
|
|
1645
|
+
/** Number of chats with this character */
|
|
1646
|
+
chatCount?: number;
|
|
1647
|
+
/** Total messages exchanged */
|
|
1648
|
+
messageCount?: number;
|
|
1649
|
+
/** Last time this character was used */
|
|
1650
|
+
lastUsed?: string;
|
|
1651
|
+
/** When the character was first added */
|
|
1652
|
+
installedAt?: string;
|
|
1653
|
+
}
|
|
1654
|
+
/**
|
|
1655
|
+
* Interface that SillyTavern plugin must implement
|
|
1656
|
+
*/
|
|
1657
|
+
export interface SillyTavernBridge {
|
|
1658
|
+
/** Get all characters */
|
|
1659
|
+
getCharacters(): Promise<STCharacter[]>;
|
|
1660
|
+
/** Get a character by name/ID */
|
|
1661
|
+
getCharacter(name: string): Promise<STCharacter | null>;
|
|
1662
|
+
/** Save a character */
|
|
1663
|
+
saveCharacter(character: STCharacter): Promise<string>;
|
|
1664
|
+
/** Delete a character */
|
|
1665
|
+
deleteCharacter(name: string): Promise<boolean>;
|
|
1666
|
+
/** Get character avatar */
|
|
1667
|
+
getAvatar(name: string): Promise<Uint8Array | null>;
|
|
1668
|
+
/** Check if SillyTavern is available */
|
|
1669
|
+
isConnected(): Promise<boolean>;
|
|
1670
|
+
/**
|
|
1671
|
+
* Get usage stats for a character (optional)
|
|
1672
|
+
* SillyTavern plugins can implement this to report usage data back to hub
|
|
1673
|
+
*/
|
|
1674
|
+
getCharacterStats?(name: string): Promise<STCharacterStats | null>;
|
|
1675
|
+
/**
|
|
1676
|
+
* Get stats for all characters (optional)
|
|
1677
|
+
* Returns a map of character name to stats
|
|
1678
|
+
*/
|
|
1679
|
+
getAllStats?(): Promise<Map<string, STCharacterStats>>;
|
|
1680
|
+
/**
|
|
1681
|
+
* Notify hub that a card was installed (optional)
|
|
1682
|
+
* Called when a new character is added from federation
|
|
1683
|
+
* @param federatedId - The federated URI of the installed card
|
|
1684
|
+
* @param hubInbox - The hub's inbox URL to notify
|
|
1685
|
+
*/
|
|
1686
|
+
notifyInstall?(federatedId: string, hubInbox: string): Promise<void>;
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Convert STCharacter to CCv3Data
|
|
1690
|
+
*/
|
|
1691
|
+
export declare function stCharacterToCCv3(st: STCharacter): CCv3Data;
|
|
1692
|
+
/**
|
|
1693
|
+
* Convert CCv3Data to STCharacter
|
|
1694
|
+
*/
|
|
1695
|
+
export declare function ccv3ToSTCharacter(card: CCv3Data, filename?: string): STCharacter;
|
|
1696
|
+
/**
|
|
1697
|
+
* SillyTavern adapter
|
|
1698
|
+
* Requires a bridge implementation from the SillyTavern plugin
|
|
1699
|
+
*/
|
|
1700
|
+
export declare class SillyTavernAdapter extends BasePlatformAdapter {
|
|
1701
|
+
readonly platform: PlatformId;
|
|
1702
|
+
readonly displayName = "SillyTavern";
|
|
1703
|
+
private bridge;
|
|
1704
|
+
constructor(bridge: SillyTavernBridge);
|
|
1705
|
+
isAvailable(): Promise<boolean>;
|
|
1706
|
+
getCard(localId: string): Promise<CCv3Data | null>;
|
|
1707
|
+
listCards(options?: {
|
|
1708
|
+
limit?: number;
|
|
1709
|
+
offset?: number;
|
|
1710
|
+
since?: string;
|
|
1711
|
+
}): Promise<AdapterCard[]>;
|
|
1712
|
+
saveCard(card: CCv3Data, localId?: string): Promise<string>;
|
|
1713
|
+
deleteCard(localId: string): Promise<boolean>;
|
|
1714
|
+
getAssets(localId: string): Promise<AdapterAsset[]>;
|
|
1715
|
+
getLastModified(localId: string): Promise<string | null>;
|
|
1716
|
+
/**
|
|
1717
|
+
* Get stats for a character (if bridge supports it)
|
|
1718
|
+
*/
|
|
1719
|
+
getStats(localId: string): Promise<STCharacterStats | null>;
|
|
1720
|
+
/**
|
|
1721
|
+
* Get stats for all characters (if bridge supports it)
|
|
1722
|
+
*/
|
|
1723
|
+
getAllStats(): Promise<Map<string, STCharacterStats> | null>;
|
|
1724
|
+
/**
|
|
1725
|
+
* Notify hub about installation (if bridge supports it)
|
|
1726
|
+
*/
|
|
1727
|
+
notifyInstall(federatedId: string, hubInbox: string): Promise<boolean>;
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Create a mock SillyTavern bridge for testing
|
|
1731
|
+
*/
|
|
1732
|
+
export declare function createMockSTBridge(): SillyTavernBridge & {
|
|
1733
|
+
characters: Map<string, STCharacter>;
|
|
1734
|
+
avatars: Map<string, Uint8Array>;
|
|
1735
|
+
stats: Map<string, STCharacterStats>;
|
|
1736
|
+
installNotifications: Array<{
|
|
1737
|
+
federatedId: string;
|
|
1738
|
+
hubInbox: string;
|
|
1739
|
+
}>;
|
|
1740
|
+
};
|
|
1741
|
+
export interface WebFingerLink {
|
|
1742
|
+
rel: string;
|
|
1743
|
+
type?: string;
|
|
1744
|
+
href: string;
|
|
1745
|
+
template?: string;
|
|
1746
|
+
}
|
|
1747
|
+
export interface WebFingerResponse {
|
|
1748
|
+
subject: string;
|
|
1749
|
+
aliases?: string[];
|
|
1750
|
+
properties?: Record<string, string>;
|
|
1751
|
+
links: WebFingerLink[];
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Handle WebFinger discovery request
|
|
1755
|
+
*
|
|
1756
|
+
* @param resource - The resource URI being requested (e.g. acct:user@domain)
|
|
1757
|
+
* @param config - Federation configuration containing actor details
|
|
1758
|
+
* @returns The WebFinger response object or null if not found
|
|
1759
|
+
*/
|
|
1760
|
+
export declare function handleWebFinger(resource: string, config: FederationConfig): WebFingerResponse | null;
|
|
1761
|
+
export interface NodeInfoLink {
|
|
1762
|
+
rel: string;
|
|
1763
|
+
href: string;
|
|
1764
|
+
}
|
|
1765
|
+
export interface NodeInfoDiscoveryResponse {
|
|
1766
|
+
links: NodeInfoLink[];
|
|
1767
|
+
}
|
|
1768
|
+
export interface NodeInfoUsage {
|
|
1769
|
+
users: {
|
|
1770
|
+
total: number;
|
|
1771
|
+
activeMonth?: number;
|
|
1772
|
+
activeHalfyear?: number;
|
|
1773
|
+
};
|
|
1774
|
+
localPosts?: number;
|
|
1775
|
+
localComments?: number;
|
|
1776
|
+
}
|
|
1777
|
+
export interface NodeInfoResponse {
|
|
1778
|
+
version: string;
|
|
1779
|
+
software: {
|
|
1780
|
+
name: string;
|
|
1781
|
+
version: string;
|
|
1782
|
+
};
|
|
1783
|
+
protocols: string[];
|
|
1784
|
+
services: {
|
|
1785
|
+
inbound: string[];
|
|
1786
|
+
outbound: string[];
|
|
1787
|
+
};
|
|
1788
|
+
openRegistrations: boolean;
|
|
1789
|
+
usage: NodeInfoUsage;
|
|
1790
|
+
metadata: Record<string, unknown>;
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Handle NodeInfo discovery request (/.well-known/nodeinfo)
|
|
1794
|
+
*
|
|
1795
|
+
* @param baseUrl - The base URL of the server (e.g. https://hub.example.com)
|
|
1796
|
+
* @returns The NodeInfo discovery response
|
|
1797
|
+
*/
|
|
1798
|
+
export declare function handleNodeInfoDiscovery(baseUrl: string): NodeInfoDiscoveryResponse;
|
|
1799
|
+
/**
|
|
1800
|
+
* Handle NodeInfo data request
|
|
1801
|
+
*
|
|
1802
|
+
* @param config - Federation configuration
|
|
1803
|
+
* @param version - NodeInfo version ('2.0' or '2.1')
|
|
1804
|
+
* @returns The NodeInfo response
|
|
1805
|
+
*/
|
|
1806
|
+
export declare function handleNodeInfo(config: FederationConfig, version?: "2.0" | "2.1"): NodeInfoResponse;
|
|
1807
|
+
/**
|
|
1808
|
+
* Handle Actor profile request
|
|
1809
|
+
*
|
|
1810
|
+
* @param config - Federation configuration
|
|
1811
|
+
* @returns The Actor JSON-LD object
|
|
1812
|
+
*/
|
|
1813
|
+
export declare function handleActor(config: FederationConfig): FederatedActor;
|
|
1814
|
+
/**
|
|
1815
|
+
* Moderation Types
|
|
1816
|
+
*
|
|
1817
|
+
* Core types for federated moderation infrastructure.
|
|
1818
|
+
* Provides tools for communities to self-moderate without imposing specific morality.
|
|
1819
|
+
*/
|
|
1820
|
+
/**
|
|
1821
|
+
* Flag activity for reporting content/actors
|
|
1822
|
+
* Per ActivityPub spec, Flag is delivered directly to target's inbox
|
|
1823
|
+
*/
|
|
1824
|
+
export interface FlagActivity {
|
|
1825
|
+
"@context": ActivityPubContext;
|
|
1826
|
+
id: string;
|
|
1827
|
+
type: "Flag";
|
|
1828
|
+
/** Reporter actor URI */
|
|
1829
|
+
actor: string;
|
|
1830
|
+
/** Reported content/actor URI(s) */
|
|
1831
|
+
object: string | string[];
|
|
1832
|
+
/** Report reason/description */
|
|
1833
|
+
content?: string;
|
|
1834
|
+
/** Report category (flexible string - communities define their own) */
|
|
1835
|
+
category?: string;
|
|
1836
|
+
published: string;
|
|
1837
|
+
/** Target instance inbox */
|
|
1838
|
+
to?: string[];
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Block activity for instance-level blocking
|
|
1842
|
+
*/
|
|
1843
|
+
export interface BlockActivity {
|
|
1844
|
+
"@context": ActivityPubContext;
|
|
1845
|
+
id: string;
|
|
1846
|
+
type: "Block";
|
|
1847
|
+
/** Admin actor URI */
|
|
1848
|
+
actor: string;
|
|
1849
|
+
/** Blocked instance domain or actor URI */
|
|
1850
|
+
object: string;
|
|
1851
|
+
/** Block reason */
|
|
1852
|
+
summary?: string;
|
|
1853
|
+
published: string;
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Report status - flexible string, communities define their own workflow
|
|
1857
|
+
* Common values: 'pending', 'reviewing', 'resolved', 'dismissed', 'escalated'
|
|
1858
|
+
*/
|
|
1859
|
+
export type ReportStatus = string;
|
|
1860
|
+
/**
|
|
1861
|
+
* Report category - flexible string, communities define their own categories
|
|
1862
|
+
* Examples: 'spam', 'harassment', 'nsfw_unmarked', 'copyright', 'illegal_content', etc.
|
|
1863
|
+
*/
|
|
1864
|
+
export type ReportCategory = string;
|
|
1865
|
+
/**
|
|
1866
|
+
* Moderation report record
|
|
1867
|
+
*/
|
|
1868
|
+
export interface ModerationReport {
|
|
1869
|
+
/** Unique report ID */
|
|
1870
|
+
id: string;
|
|
1871
|
+
/** Reporter actor URI */
|
|
1872
|
+
reporterActorId: string;
|
|
1873
|
+
/** Reporter's instance domain */
|
|
1874
|
+
reporterInstance: string;
|
|
1875
|
+
/** Reported content/actor URI(s) */
|
|
1876
|
+
targetIds: string[];
|
|
1877
|
+
/** Report category */
|
|
1878
|
+
category: ReportCategory;
|
|
1879
|
+
/** Detailed description */
|
|
1880
|
+
description: string;
|
|
1881
|
+
/** Report status */
|
|
1882
|
+
status: ReportStatus;
|
|
1883
|
+
/** Associated Flag activity ID */
|
|
1884
|
+
activityId: string;
|
|
1885
|
+
/** Timestamp when created */
|
|
1886
|
+
createdAt: string;
|
|
1887
|
+
/** Timestamp of last update */
|
|
1888
|
+
updatedAt: string;
|
|
1889
|
+
/** Instance where report was received */
|
|
1890
|
+
receivingInstance: string;
|
|
1891
|
+
/** Whether the report was federated to target instance */
|
|
1892
|
+
federatedToTarget: boolean;
|
|
1893
|
+
/** Any additional context/metadata */
|
|
1894
|
+
metadata?: Record<string, unknown>;
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Action type - flexible string, communities define their own actions
|
|
1898
|
+
* Examples: 'warn', 'delete', 'suspend', 'silence', 'ban', 'reject_report', 'escalate', 'restore'
|
|
1899
|
+
*/
|
|
1900
|
+
export type ActionType = string;
|
|
1901
|
+
/**
|
|
1902
|
+
* Moderation action record - audit trail for moderator decisions
|
|
1903
|
+
*/
|
|
1904
|
+
export interface ModerationAction {
|
|
1905
|
+
/** Unique action ID */
|
|
1906
|
+
id: string;
|
|
1907
|
+
/** Report this action addresses (optional - some actions are proactive) */
|
|
1908
|
+
reportId?: string;
|
|
1909
|
+
/** Moderator actor URI */
|
|
1910
|
+
moderatorActorId: string;
|
|
1911
|
+
/** Target of the action (actor or content URI) */
|
|
1912
|
+
targetId: string;
|
|
1913
|
+
/** Type of action taken */
|
|
1914
|
+
actionType: ActionType;
|
|
1915
|
+
/** Reason/justification for action */
|
|
1916
|
+
reason: string;
|
|
1917
|
+
/** When action was taken */
|
|
1918
|
+
timestamp: string;
|
|
1919
|
+
/** When action expires (for temporary actions like suspensions) */
|
|
1920
|
+
expiresAt?: string;
|
|
1921
|
+
/** Whether action is currently active */
|
|
1922
|
+
active: boolean;
|
|
1923
|
+
/** Previous action this reverses (for undo/restore) */
|
|
1924
|
+
reversesActionId?: string;
|
|
1925
|
+
/** Audit trail - who approved (for multi-mod workflows) */
|
|
1926
|
+
approvedBy?: string[];
|
|
1927
|
+
/** Additional metadata */
|
|
1928
|
+
metadata?: Record<string, unknown>;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Instance block level
|
|
1932
|
+
* - suspend: Full defederation, reject all activities
|
|
1933
|
+
* - silence: Don't show in public feeds, but allow existing follows
|
|
1934
|
+
* - reject_media: Accept text but reject media/assets
|
|
1935
|
+
*/
|
|
1936
|
+
export type InstanceBlockLevel = "suspend" | "silence" | "reject_media";
|
|
1937
|
+
/**
|
|
1938
|
+
* Instance block record for defederation
|
|
1939
|
+
*/
|
|
1940
|
+
export interface InstanceBlock {
|
|
1941
|
+
/** Unique block ID */
|
|
1942
|
+
id: string;
|
|
1943
|
+
/** Blocked instance domain (e.g., "evil.example.com") */
|
|
1944
|
+
blockedDomain: string;
|
|
1945
|
+
/** Block level */
|
|
1946
|
+
level: InstanceBlockLevel;
|
|
1947
|
+
/** Reason for block */
|
|
1948
|
+
reason: string;
|
|
1949
|
+
/** Admin who created block */
|
|
1950
|
+
createdBy: string;
|
|
1951
|
+
/** When block was created */
|
|
1952
|
+
createdAt: string;
|
|
1953
|
+
/** Whether block is active */
|
|
1954
|
+
active: boolean;
|
|
1955
|
+
/** Optional: public comment for transparency reports */
|
|
1956
|
+
publicComment?: string;
|
|
1957
|
+
/** Whether to announce block to federation peers */
|
|
1958
|
+
federate: boolean;
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Policy rule types
|
|
1962
|
+
* - keyword: Text contains keyword (case-insensitive)
|
|
1963
|
+
* - regex: Text matches regex pattern
|
|
1964
|
+
* - tag: Card has specific tag
|
|
1965
|
+
* - creator: Card from specific creator
|
|
1966
|
+
* - instance: Card from specific instance domain
|
|
1967
|
+
*/
|
|
1968
|
+
export type PolicyRuleType = "keyword" | "regex" | "tag" | "creator" | "instance";
|
|
1969
|
+
/**
|
|
1970
|
+
* Policy action on rule match
|
|
1971
|
+
* - allow: Explicitly allow (bypass other checks, whitelist)
|
|
1972
|
+
* - warn: Log warning, allow through
|
|
1973
|
+
* - review: Queue for manual review
|
|
1974
|
+
* - reject: Block entirely
|
|
1975
|
+
* - quarantine: Accept but limit visibility
|
|
1976
|
+
*/
|
|
1977
|
+
export type PolicyAction = "allow" | "warn" | "review" | "reject" | "quarantine";
|
|
1978
|
+
/**
|
|
1979
|
+
* Content policy rule
|
|
1980
|
+
*/
|
|
1981
|
+
export interface ContentPolicyRule {
|
|
1982
|
+
/** Unique rule ID */
|
|
1983
|
+
id: string;
|
|
1984
|
+
/** Rule name for admin reference */
|
|
1985
|
+
name: string;
|
|
1986
|
+
/** Rule type */
|
|
1987
|
+
type: PolicyRuleType;
|
|
1988
|
+
/** Pattern/value to match (interpretation depends on type) */
|
|
1989
|
+
pattern: string;
|
|
1990
|
+
/**
|
|
1991
|
+
* Fields to check (for keyword/regex)
|
|
1992
|
+
* Default: ['name', 'description', 'personality', 'scenario']
|
|
1993
|
+
*/
|
|
1994
|
+
targetFields?: string[];
|
|
1995
|
+
/** Action to take on match */
|
|
1996
|
+
action: PolicyAction;
|
|
1997
|
+
/** Priority (lower = checked first, allows for whitelist rules) */
|
|
1998
|
+
priority: number;
|
|
1999
|
+
/** Whether rule is enabled */
|
|
2000
|
+
enabled: boolean;
|
|
2001
|
+
/** Optional explanation for transparency */
|
|
2002
|
+
publicReason?: string;
|
|
2003
|
+
/** When rule was created */
|
|
2004
|
+
createdAt: string;
|
|
2005
|
+
/** Who created the rule */
|
|
2006
|
+
createdBy: string;
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Content policy (collection of rules)
|
|
2010
|
+
*/
|
|
2011
|
+
export interface ContentPolicy {
|
|
2012
|
+
/** Policy ID */
|
|
2013
|
+
id: string;
|
|
2014
|
+
/** Policy name */
|
|
2015
|
+
name: string;
|
|
2016
|
+
/** Description */
|
|
2017
|
+
description: string;
|
|
2018
|
+
/** Rules in this policy */
|
|
2019
|
+
rules: ContentPolicyRule[];
|
|
2020
|
+
/** Whether policy is active */
|
|
2021
|
+
enabled: boolean;
|
|
2022
|
+
/** Default action if no rules match */
|
|
2023
|
+
defaultAction: PolicyAction;
|
|
2024
|
+
/** When policy was last updated */
|
|
2025
|
+
updatedAt: string;
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Result of policy evaluation
|
|
2029
|
+
*/
|
|
2030
|
+
export interface PolicyEvaluationResult {
|
|
2031
|
+
/** Final action to take */
|
|
2032
|
+
action: PolicyAction;
|
|
2033
|
+
/** Rules that matched (for audit) */
|
|
2034
|
+
matchedRules: Array<{
|
|
2035
|
+
ruleId: string;
|
|
2036
|
+
ruleName: string;
|
|
2037
|
+
matchedField?: string;
|
|
2038
|
+
matchedValue?: string;
|
|
2039
|
+
}>;
|
|
2040
|
+
/** Whether any rules matched */
|
|
2041
|
+
hasMatch: boolean;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Rate limit bucket for an actor
|
|
2045
|
+
*/
|
|
2046
|
+
export interface RateLimitBucket {
|
|
2047
|
+
/** Actor ID */
|
|
2048
|
+
actorId: string;
|
|
2049
|
+
/** Current token count */
|
|
2050
|
+
tokens: number;
|
|
2051
|
+
/** Max tokens (bucket capacity) */
|
|
2052
|
+
maxTokens: number;
|
|
2053
|
+
/** Last time bucket was refilled (ISO timestamp) */
|
|
2054
|
+
lastRefill: string;
|
|
2055
|
+
/** Refill rate (tokens per hour) */
|
|
2056
|
+
refillRate: number;
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* Rate limit check result
|
|
2060
|
+
*/
|
|
2061
|
+
export interface RateLimitResult {
|
|
2062
|
+
/** Whether action is allowed */
|
|
2063
|
+
allowed: boolean;
|
|
2064
|
+
/** Remaining tokens */
|
|
2065
|
+
remaining: number;
|
|
2066
|
+
/** When bucket resets (ISO timestamp) */
|
|
2067
|
+
resetAt: string;
|
|
2068
|
+
/** Retry-After header value in seconds (if not allowed) */
|
|
2069
|
+
retryAfter?: number;
|
|
2070
|
+
}
|
|
2071
|
+
/**
|
|
2072
|
+
* Moderation store interface
|
|
2073
|
+
* Follows same pattern as SyncStateStore for consistency
|
|
2074
|
+
*/
|
|
2075
|
+
export interface ModerationStore {
|
|
2076
|
+
/** Create a new report */
|
|
2077
|
+
createReport(report: Omit<ModerationReport, "id">): Promise<ModerationReport>;
|
|
2078
|
+
/** Get report by ID */
|
|
2079
|
+
getReport(id: string): Promise<ModerationReport | null>;
|
|
2080
|
+
/** Update report */
|
|
2081
|
+
updateReport(id: string, updates: Partial<ModerationReport>): Promise<void>;
|
|
2082
|
+
/** List reports with optional filters */
|
|
2083
|
+
listReports(filters?: {
|
|
2084
|
+
status?: ReportStatus;
|
|
2085
|
+
category?: ReportCategory;
|
|
2086
|
+
targetId?: string;
|
|
2087
|
+
reporterActorId?: string;
|
|
2088
|
+
since?: string;
|
|
2089
|
+
limit?: number;
|
|
2090
|
+
offset?: number;
|
|
2091
|
+
}): Promise<ModerationReport[]>;
|
|
2092
|
+
/** Count reports */
|
|
2093
|
+
countReports(filters?: {
|
|
2094
|
+
status?: ReportStatus;
|
|
2095
|
+
}): Promise<number>;
|
|
2096
|
+
/** Create a moderation action */
|
|
2097
|
+
createAction(action: Omit<ModerationAction, "id">): Promise<ModerationAction>;
|
|
2098
|
+
/** Get action by ID */
|
|
2099
|
+
getAction(id: string): Promise<ModerationAction | null>;
|
|
2100
|
+
/** List actions with optional filters */
|
|
2101
|
+
listActions(filters?: {
|
|
2102
|
+
targetId?: string;
|
|
2103
|
+
moderatorActorId?: string;
|
|
2104
|
+
actionType?: ActionType;
|
|
2105
|
+
active?: boolean;
|
|
2106
|
+
since?: string;
|
|
2107
|
+
limit?: number;
|
|
2108
|
+
}): Promise<ModerationAction[]>;
|
|
2109
|
+
/** Deactivate an action (for reversal) */
|
|
2110
|
+
deactivateAction(id: string): Promise<void>;
|
|
2111
|
+
/** Create an instance block */
|
|
2112
|
+
createBlock(block: Omit<InstanceBlock, "id">): Promise<InstanceBlock>;
|
|
2113
|
+
/** Get block by ID */
|
|
2114
|
+
getBlock(id: string): Promise<InstanceBlock | null>;
|
|
2115
|
+
/** Get block by domain */
|
|
2116
|
+
getBlockByDomain(domain: string): Promise<InstanceBlock | null>;
|
|
2117
|
+
/** List blocks */
|
|
2118
|
+
listBlocks(filters?: {
|
|
2119
|
+
active?: boolean;
|
|
2120
|
+
}): Promise<InstanceBlock[]>;
|
|
2121
|
+
/** Update block */
|
|
2122
|
+
updateBlock(id: string, updates: Partial<InstanceBlock>): Promise<void>;
|
|
2123
|
+
/** Check if instance is blocked */
|
|
2124
|
+
isInstanceBlocked(domain: string): Promise<boolean>;
|
|
2125
|
+
/** Create a content policy */
|
|
2126
|
+
createPolicy(policy: Omit<ContentPolicy, "id">): Promise<ContentPolicy>;
|
|
2127
|
+
/** Get policy by ID */
|
|
2128
|
+
getPolicy(id: string): Promise<ContentPolicy | null>;
|
|
2129
|
+
/** List policies */
|
|
2130
|
+
listPolicies(filters?: {
|
|
2131
|
+
enabled?: boolean;
|
|
2132
|
+
}): Promise<ContentPolicy[]>;
|
|
2133
|
+
/** Update policy */
|
|
2134
|
+
updatePolicy(id: string, updates: Partial<ContentPolicy>): Promise<void>;
|
|
2135
|
+
/** Delete policy */
|
|
2136
|
+
deletePolicy(id: string): Promise<void>;
|
|
2137
|
+
/** Get rate limit bucket for actor */
|
|
2138
|
+
getRateLimitBucket(actorId: string): Promise<RateLimitBucket | null>;
|
|
2139
|
+
/** Update rate limit bucket */
|
|
2140
|
+
updateRateLimitBucket(bucket: RateLimitBucket): Promise<void>;
|
|
2141
|
+
/** Get audit log (alias for listActions with specific filters) */
|
|
2142
|
+
getAuditLog(filters?: {
|
|
2143
|
+
targetId?: string;
|
|
2144
|
+
actorId?: string;
|
|
2145
|
+
since?: string;
|
|
2146
|
+
until?: string;
|
|
2147
|
+
limit?: number;
|
|
2148
|
+
}): Promise<ModerationAction[]>;
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Moderation event types
|
|
2152
|
+
*/
|
|
2153
|
+
export type ModerationEventType = "report:created" | "report:updated" | "report:resolved" | "action:created" | "action:reverted" | "block:created" | "block:removed" | "policy:matched" | "policy:rejected";
|
|
2154
|
+
/**
|
|
2155
|
+
* Moderation event
|
|
2156
|
+
*/
|
|
2157
|
+
export interface ModerationEvent {
|
|
2158
|
+
type: ModerationEventType;
|
|
2159
|
+
timestamp: string;
|
|
2160
|
+
data: unknown;
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* Moderation event listener
|
|
2164
|
+
*/
|
|
2165
|
+
export type ModerationEventListener = (event: ModerationEvent) => void | Promise<void>;
|
|
2166
|
+
/**
|
|
2167
|
+
* Moderation ActivityPub Activities
|
|
2168
|
+
*
|
|
2169
|
+
* Create and parse Flag/Block activities per ActivityPub spec.
|
|
2170
|
+
*/
|
|
2171
|
+
/**
|
|
2172
|
+
* ActivityPub context for moderation activities
|
|
2173
|
+
*/
|
|
2174
|
+
export declare const MODERATION_ACTIVITY_CONTEXT: readonly [
|
|
2175
|
+
"https://www.w3.org/ns/activitystreams",
|
|
2176
|
+
{
|
|
2177
|
+
readonly moderation: "https://character-foundry.dev/ns/moderation#";
|
|
2178
|
+
readonly category: {
|
|
2179
|
+
readonly "@id": "moderation:category";
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
];
|
|
2183
|
+
/**
|
|
2184
|
+
* Create a Flag activity for reporting content/actors
|
|
2185
|
+
*
|
|
2186
|
+
* Per ActivityPub spec, Flag is delivered directly to the target's instance inbox,
|
|
2187
|
+
* NOT wrapped in a Create activity.
|
|
2188
|
+
*
|
|
2189
|
+
* @example
|
|
2190
|
+
* ```typescript
|
|
2191
|
+
* const flag = createFlagActivity(
|
|
2192
|
+
* 'https://myinstance.com/users/reporter',
|
|
2193
|
+
* 'https://other.com/cards/problematic-card',
|
|
2194
|
+
* 'https://myinstance.com',
|
|
2195
|
+
* {
|
|
2196
|
+
* content: 'This card contains spam',
|
|
2197
|
+
* category: 'spam',
|
|
2198
|
+
* to: ['https://other.com/inbox'],
|
|
2199
|
+
* }
|
|
2200
|
+
* );
|
|
2201
|
+
* ```
|
|
2202
|
+
*/
|
|
2203
|
+
export declare function createFlagActivity(reporterActorId: string, targetIds: string | string[], baseUrl: string, options?: {
|
|
2204
|
+
/** Report description/reason */
|
|
2205
|
+
content?: string;
|
|
2206
|
+
/** Report category (flexible string) */
|
|
2207
|
+
category?: ReportCategory;
|
|
2208
|
+
/** Target inbox(es) */
|
|
2209
|
+
to?: string[];
|
|
2210
|
+
}): FlagActivity;
|
|
2211
|
+
/**
|
|
2212
|
+
* Parse incoming Flag activity
|
|
2213
|
+
*
|
|
2214
|
+
* @returns Parsed flag data or null if invalid
|
|
2215
|
+
*/
|
|
2216
|
+
export declare function parseFlagActivity(activity: unknown): {
|
|
2217
|
+
actorId: string;
|
|
2218
|
+
targetIds: string[];
|
|
2219
|
+
content?: string;
|
|
2220
|
+
category?: string;
|
|
2221
|
+
activityId: string;
|
|
2222
|
+
} | null;
|
|
2223
|
+
/**
|
|
2224
|
+
* Validate a Flag activity has all required fields
|
|
2225
|
+
*/
|
|
2226
|
+
declare function validateFlagActivity(activity: unknown): {
|
|
2227
|
+
valid: boolean;
|
|
2228
|
+
error?: string;
|
|
2229
|
+
};
|
|
2230
|
+
/**
|
|
2231
|
+
* Create a Block activity for instance-level blocking
|
|
2232
|
+
*
|
|
2233
|
+
* Block activities are typically:
|
|
2234
|
+
* 1. Stored locally for enforcement
|
|
2235
|
+
* 2. Optionally announced to trusted federation peers
|
|
2236
|
+
*
|
|
2237
|
+
* @example
|
|
2238
|
+
* ```typescript
|
|
2239
|
+
* const block = createBlockActivity(
|
|
2240
|
+
* 'https://myinstance.com/actor',
|
|
2241
|
+
* 'evil.example.com',
|
|
2242
|
+
* 'https://myinstance.com',
|
|
2243
|
+
* { summary: 'Spam and harassment' }
|
|
2244
|
+
* );
|
|
2245
|
+
* ```
|
|
2246
|
+
*/
|
|
2247
|
+
export declare function createBlockActivity(adminActorId: string, blockedTarget: string, baseUrl: string, options?: {
|
|
2248
|
+
/** Block reason */
|
|
2249
|
+
summary?: string;
|
|
2250
|
+
/** Recipients for federation announcement */
|
|
2251
|
+
to?: string[];
|
|
2252
|
+
}): BlockActivity;
|
|
2253
|
+
/**
|
|
2254
|
+
* Parse incoming Block activity
|
|
2255
|
+
*
|
|
2256
|
+
* @returns Parsed block data or null if invalid
|
|
2257
|
+
*/
|
|
2258
|
+
export declare function parseBlockActivity(activity: unknown): {
|
|
2259
|
+
actorId: string;
|
|
2260
|
+
targetId: string;
|
|
2261
|
+
summary?: string;
|
|
2262
|
+
activityId: string;
|
|
2263
|
+
} | null;
|
|
2264
|
+
/**
|
|
2265
|
+
* Validate a Block activity has all required fields
|
|
2266
|
+
*/
|
|
2267
|
+
declare function validateBlockActivity(activity: unknown): {
|
|
2268
|
+
valid: boolean;
|
|
2269
|
+
error?: string;
|
|
2270
|
+
};
|
|
2271
|
+
/**
|
|
2272
|
+
* Inbox Handler
|
|
2273
|
+
*
|
|
2274
|
+
* Process incoming ActivityPub activities.
|
|
2275
|
+
*/
|
|
2276
|
+
/**
|
|
2277
|
+
* Handle incoming ActivityPub activities
|
|
2278
|
+
*
|
|
2279
|
+
* Parses and routes incoming activities to the appropriate handlers.
|
|
2280
|
+
* Currently supports Fork activities for fork notification handling.
|
|
2281
|
+
*
|
|
2282
|
+
* @example
|
|
2283
|
+
* ```typescript
|
|
2284
|
+
* // In your web framework (e.g., Hono, Express)
|
|
2285
|
+
* app.post('/inbox', async (req) => {
|
|
2286
|
+
* // IMPORTANT: Capture raw body BEFORE parsing for Digest verification
|
|
2287
|
+
* const rawBody = await req.text();
|
|
2288
|
+
* const body = JSON.parse(rawBody);
|
|
2289
|
+
*
|
|
2290
|
+
* const result = await handleInbox(body, req.headers, {
|
|
2291
|
+
* rawBody, // Required for body integrity verification
|
|
2292
|
+
* method: 'POST',
|
|
2293
|
+
* path: '/inbox', // Must match your actual route path
|
|
2294
|
+
* strictMode: true,
|
|
2295
|
+
* fetchActor: async (id) => fetchActorFromNetwork(id),
|
|
2296
|
+
* onFork: async (activity) => {
|
|
2297
|
+
* await syncEngine.handleForkNotification(activity);
|
|
2298
|
+
* },
|
|
2299
|
+
* });
|
|
2300
|
+
*
|
|
2301
|
+
* if (result.accepted) {
|
|
2302
|
+
* return new Response(null, { status: 202 });
|
|
2303
|
+
* } else {
|
|
2304
|
+
* return new Response(result.error, { status: 400 });
|
|
2305
|
+
* }
|
|
2306
|
+
* });
|
|
2307
|
+
* ```
|
|
2308
|
+
*/
|
|
2309
|
+
export declare function handleInbox(body: unknown, headers: Headers | Record<string, string>, options: InboxHandlerOptions & {
|
|
2310
|
+
/** HTTP method (required for signature verification in strictMode) */
|
|
2311
|
+
method?: string;
|
|
2312
|
+
/** Request path (required for signature verification in strictMode) */
|
|
2313
|
+
path?: string;
|
|
2314
|
+
/**
|
|
2315
|
+
* Raw request body for Digest header verification.
|
|
2316
|
+
*
|
|
2317
|
+
* @security REQUIRED in strictMode when Digest header is present.
|
|
2318
|
+
* Without this, an attacker could modify the JSON body while keeping a valid signature
|
|
2319
|
+
* (since signatures only cover headers, not the body).
|
|
2320
|
+
*
|
|
2321
|
+
* Pass the raw bytes before JSON parsing:
|
|
2322
|
+
* ```typescript
|
|
2323
|
+
* const rawBody = await req.text();
|
|
2324
|
+
* const body = JSON.parse(rawBody);
|
|
2325
|
+
* handleInbox(body, headers, { rawBody, ... });
|
|
2326
|
+
* ```
|
|
2327
|
+
*/
|
|
2328
|
+
rawBody?: string | ArrayBuffer;
|
|
2329
|
+
onFork?: (activity: ForkActivity) => Promise<void>;
|
|
2330
|
+
onInstall?: (activity: InstallActivity) => Promise<void>;
|
|
2331
|
+
onCreate?: (activity: FederatedActivity) => Promise<void>;
|
|
2332
|
+
onUpdate?: (activity: FederatedActivity) => Promise<void>;
|
|
2333
|
+
onDelete?: (activity: FederatedActivity) => Promise<void>;
|
|
2334
|
+
onLike?: (activity: FederatedActivity) => Promise<void>;
|
|
2335
|
+
onAnnounce?: (activity: FederatedActivity) => Promise<void>;
|
|
2336
|
+
onUndo?: (activity: FederatedActivity) => Promise<void>;
|
|
2337
|
+
onFlag?: (activity: FlagActivity, parsedReport: ReturnType<typeof parseFlagActivity>) => Promise<void>;
|
|
2338
|
+
onBlock?: (activity: BlockActivity, parsedBlock: ReturnType<typeof parseBlockActivity>) => Promise<void>;
|
|
2339
|
+
/**
|
|
2340
|
+
* Moderation store for instance blocking.
|
|
2341
|
+
* If provided, blocked instances are rejected BEFORE signature verification (saves CPU).
|
|
2342
|
+
*/
|
|
2343
|
+
moderationStore?: ModerationStore;
|
|
2344
|
+
}): Promise<InboxResult>;
|
|
2345
|
+
/**
|
|
2346
|
+
* Validate that a Fork activity has all required fields
|
|
2347
|
+
*/
|
|
2348
|
+
export declare function validateForkActivity(activity: unknown): {
|
|
2349
|
+
valid: boolean;
|
|
2350
|
+
error?: string;
|
|
2351
|
+
};
|
|
2352
|
+
/**
|
|
2353
|
+
* Validate that an Install activity has all required fields
|
|
2354
|
+
*/
|
|
2355
|
+
export declare function validateInstallActivity(activity: unknown): {
|
|
2356
|
+
valid: boolean;
|
|
2357
|
+
error?: string;
|
|
2358
|
+
};
|
|
2359
|
+
/**
|
|
2360
|
+
* HTTP Signatures
|
|
2361
|
+
*
|
|
2362
|
+
* Implements HTTP message signing per draft-cavage-http-signatures
|
|
2363
|
+
* for ActivityPub federation.
|
|
2364
|
+
*
|
|
2365
|
+
* Works in both Node.js and browser/Workers environments using
|
|
2366
|
+
* the Web Crypto API.
|
|
2367
|
+
*/
|
|
2368
|
+
/**
|
|
2369
|
+
* Parsed HTTP signature header
|
|
2370
|
+
*/
|
|
2371
|
+
export interface ParsedSignature {
|
|
2372
|
+
keyId: string;
|
|
2373
|
+
algorithm: string;
|
|
2374
|
+
headers: string[];
|
|
2375
|
+
signature: string;
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Required headers for strict signature validation.
|
|
2379
|
+
* These headers MUST be included in the signed header list when strictMode is enabled.
|
|
2380
|
+
*/
|
|
2381
|
+
export declare const REQUIRED_SIGNED_HEADERS: readonly [
|
|
2382
|
+
"(request-target)",
|
|
2383
|
+
"host",
|
|
2384
|
+
"date"
|
|
2385
|
+
];
|
|
2386
|
+
/**
|
|
2387
|
+
* Signature validation options
|
|
2388
|
+
*/
|
|
2389
|
+
export interface SignatureValidationOptions {
|
|
2390
|
+
/** HTTP method (GET, POST, etc.) */
|
|
2391
|
+
method: string;
|
|
2392
|
+
/** Request path (e.g., /inbox) */
|
|
2393
|
+
path: string;
|
|
2394
|
+
/** Function to fetch actor by ID */
|
|
2395
|
+
fetchActor: (actorId: string) => Promise<FederatedActor | null>;
|
|
2396
|
+
/** Maximum age in seconds for Date header. Default: 300 (5 minutes) */
|
|
2397
|
+
maxAge?: number;
|
|
2398
|
+
/**
|
|
2399
|
+
* Enable strict signature validation mode.
|
|
2400
|
+
*
|
|
2401
|
+
* When enabled, signatures MUST include these headers: (request-target), host, date.
|
|
2402
|
+
* This prevents replay attacks and cross-host request reuse.
|
|
2403
|
+
*
|
|
2404
|
+
* Default: false (for backwards compatibility with existing federation peers)
|
|
2405
|
+
* Recommended: true for new deployments
|
|
2406
|
+
*
|
|
2407
|
+
* @security Without strict mode, attackers can:
|
|
2408
|
+
* - Replay captured requests if Date is not signed
|
|
2409
|
+
* - Reuse signatures across different hosts if host is not signed
|
|
2410
|
+
*/
|
|
2411
|
+
strictMode?: boolean;
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Signature validation result
|
|
2415
|
+
*/
|
|
2416
|
+
export interface SignatureValidationResult {
|
|
2417
|
+
valid: boolean;
|
|
2418
|
+
error?: string;
|
|
2419
|
+
actor?: FederatedActor;
|
|
2420
|
+
keyId?: string;
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Request signing options
|
|
2424
|
+
*/
|
|
2425
|
+
export interface SigningOptions {
|
|
2426
|
+
/** Private key in PEM format */
|
|
2427
|
+
privateKeyPem: string;
|
|
2428
|
+
/** Key ID (usually actor#main-key) */
|
|
2429
|
+
keyId: string;
|
|
2430
|
+
/** HTTP method */
|
|
2431
|
+
method: string;
|
|
2432
|
+
/** Request path */
|
|
2433
|
+
path: string;
|
|
2434
|
+
/** Optional: target host header */
|
|
2435
|
+
host?: string;
|
|
2436
|
+
/** Optional: digest header */
|
|
2437
|
+
digest?: string;
|
|
2438
|
+
/** Optional: content-type header */
|
|
2439
|
+
contentType?: string;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Parse an HTTP Signature header
|
|
2443
|
+
*
|
|
2444
|
+
* Format: keyId="...",algorithm="...",headers="...",signature="..."
|
|
2445
|
+
*/
|
|
2446
|
+
export declare function parseSignatureHeader(header: string): ParsedSignature | null;
|
|
2447
|
+
/**
|
|
2448
|
+
* Successful signing string result
|
|
2449
|
+
*/
|
|
2450
|
+
export interface SigningStringSuccess {
|
|
2451
|
+
success: true;
|
|
2452
|
+
signingString: string;
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Failed signing string result (missing headers)
|
|
2456
|
+
*/
|
|
2457
|
+
export interface SigningStringFailure {
|
|
2458
|
+
success: false;
|
|
2459
|
+
error: string;
|
|
2460
|
+
missingHeaders: string[];
|
|
2461
|
+
}
|
|
2462
|
+
/**
|
|
2463
|
+
* Result of building a signing string
|
|
2464
|
+
*/
|
|
2465
|
+
export type SigningStringResult = SigningStringSuccess | SigningStringFailure;
|
|
2466
|
+
/**
|
|
2467
|
+
* Build the signing string from headers
|
|
2468
|
+
*
|
|
2469
|
+
* @security If a header is listed in headerNames but missing from the request,
|
|
2470
|
+
* this function returns an error. This prevents downgrade attacks where an
|
|
2471
|
+
* attacker claims to sign headers that aren't actually present.
|
|
2472
|
+
*
|
|
2473
|
+
* Synthetic headers ((request-target), (created), (expires)) are always allowed.
|
|
2474
|
+
*/
|
|
2475
|
+
export declare function buildSigningString(method: string, path: string, headers: Headers, headerNames: string[]): string;
|
|
2476
|
+
/**
|
|
2477
|
+
* Build signing string with strict header validation
|
|
2478
|
+
*
|
|
2479
|
+
* Returns an error if any non-synthetic header in headerNames is missing.
|
|
2480
|
+
* Use this for new code that wants strict validation.
|
|
2481
|
+
*/
|
|
2482
|
+
export declare function buildSigningStringStrict(method: string, path: string, headers: Headers, headerNames: string[]): SigningStringResult;
|
|
2483
|
+
/**
|
|
2484
|
+
* Options for HTTP signature verification
|
|
2485
|
+
*/
|
|
2486
|
+
export interface VerifyHttpSignatureOptions {
|
|
2487
|
+
/**
|
|
2488
|
+
* If true, verification fails when any claimed signed header is missing.
|
|
2489
|
+
* This prevents downgrade attacks where signatures claim to cover headers
|
|
2490
|
+
* that aren't actually present in the request.
|
|
2491
|
+
*
|
|
2492
|
+
* @security Enable this for production deployments to ensure signatures
|
|
2493
|
+
* actually cover all claimed headers.
|
|
2494
|
+
*/
|
|
2495
|
+
strictHeaders?: boolean;
|
|
2496
|
+
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Verify an HTTP signature using Web Crypto API
|
|
2499
|
+
*
|
|
2500
|
+
* @param options.strictHeaders - If true, fails when claimed headers are missing
|
|
2501
|
+
*/
|
|
2502
|
+
export declare function verifyHttpSignature(parsed: ParsedSignature, publicKeyPem: string, method: string, path: string, headers: Headers, options?: VerifyHttpSignatureOptions): Promise<boolean>;
|
|
2503
|
+
/**
|
|
2504
|
+
* Validate an incoming activity's HTTP signature
|
|
2505
|
+
*
|
|
2506
|
+
* @security Enable `strictMode` for production deployments to prevent:
|
|
2507
|
+
* - Replay attacks (requires Date header in signature)
|
|
2508
|
+
* - Cross-host request reuse (requires host header in signature)
|
|
2509
|
+
*/
|
|
2510
|
+
declare function validateActivitySignature(activity: FederatedActivity, headers: Headers, options: SignatureValidationOptions): Promise<SignatureValidationResult>;
|
|
2511
|
+
/**
|
|
2512
|
+
* Sign an outgoing HTTP request
|
|
2513
|
+
*
|
|
2514
|
+
* @param options.host - Required. The target host for the request.
|
|
2515
|
+
* @throws If host is not provided (required for valid signatures)
|
|
2516
|
+
*/
|
|
2517
|
+
export declare function signRequest(options: SigningOptions): Promise<{
|
|
2518
|
+
signature: string;
|
|
2519
|
+
date: string;
|
|
2520
|
+
host: string;
|
|
2521
|
+
}>;
|
|
2522
|
+
/**
|
|
2523
|
+
* Calculate SHA-256 digest for request body
|
|
2524
|
+
*/
|
|
2525
|
+
export declare function calculateDigest(body: string | ArrayBuffer): Promise<string>;
|
|
2526
|
+
/**
|
|
2527
|
+
* In-Memory Moderation Store
|
|
2528
|
+
*
|
|
2529
|
+
* Memory implementation for testing and single-session use.
|
|
2530
|
+
* Follows the same pattern as MemorySyncStateStore.
|
|
2531
|
+
*/
|
|
2532
|
+
/**
|
|
2533
|
+
* In-memory implementation of ModerationStore
|
|
2534
|
+
*
|
|
2535
|
+
* Suitable for testing, development, and single-session use cases.
|
|
2536
|
+
* Data is lost when the process ends.
|
|
2537
|
+
*/
|
|
2538
|
+
export declare class MemoryModerationStore implements ModerationStore {
|
|
2539
|
+
private reports;
|
|
2540
|
+
private actions;
|
|
2541
|
+
private blocks;
|
|
2542
|
+
private policies;
|
|
2543
|
+
private rateLimits;
|
|
2544
|
+
createReport(report: Omit<ModerationReport, "id">): Promise<ModerationReport>;
|
|
2545
|
+
getReport(id: string): Promise<ModerationReport | null>;
|
|
2546
|
+
updateReport(id: string, updates: Partial<ModerationReport>): Promise<void>;
|
|
2547
|
+
listReports(filters?: {
|
|
2548
|
+
status?: ReportStatus;
|
|
2549
|
+
category?: ReportCategory;
|
|
2550
|
+
targetId?: string;
|
|
2551
|
+
reporterActorId?: string;
|
|
2552
|
+
since?: string;
|
|
2553
|
+
limit?: number;
|
|
2554
|
+
offset?: number;
|
|
2555
|
+
}): Promise<ModerationReport[]>;
|
|
2556
|
+
countReports(filters?: {
|
|
2557
|
+
status?: ReportStatus;
|
|
2558
|
+
}): Promise<number>;
|
|
2559
|
+
createAction(action: Omit<ModerationAction, "id">): Promise<ModerationAction>;
|
|
2560
|
+
getAction(id: string): Promise<ModerationAction | null>;
|
|
2561
|
+
listActions(filters?: {
|
|
2562
|
+
targetId?: string;
|
|
2563
|
+
moderatorActorId?: string;
|
|
2564
|
+
actionType?: ActionType;
|
|
2565
|
+
active?: boolean;
|
|
2566
|
+
since?: string;
|
|
2567
|
+
limit?: number;
|
|
2568
|
+
}): Promise<ModerationAction[]>;
|
|
2569
|
+
deactivateAction(id: string): Promise<void>;
|
|
2570
|
+
createBlock(block: Omit<InstanceBlock, "id">): Promise<InstanceBlock>;
|
|
2571
|
+
getBlock(id: string): Promise<InstanceBlock | null>;
|
|
2572
|
+
getBlockByDomain(domain: string): Promise<InstanceBlock | null>;
|
|
2573
|
+
listBlocks(filters?: {
|
|
2574
|
+
active?: boolean;
|
|
2575
|
+
}): Promise<InstanceBlock[]>;
|
|
2576
|
+
updateBlock(id: string, updates: Partial<InstanceBlock>): Promise<void>;
|
|
2577
|
+
isInstanceBlocked(domain: string): Promise<boolean>;
|
|
2578
|
+
createPolicy(policy: Omit<ContentPolicy, "id">): Promise<ContentPolicy>;
|
|
2579
|
+
getPolicy(id: string): Promise<ContentPolicy | null>;
|
|
2580
|
+
listPolicies(filters?: {
|
|
2581
|
+
enabled?: boolean;
|
|
2582
|
+
}): Promise<ContentPolicy[]>;
|
|
2583
|
+
updatePolicy(id: string, updates: Partial<ContentPolicy>): Promise<void>;
|
|
2584
|
+
deletePolicy(id: string): Promise<void>;
|
|
2585
|
+
getRateLimitBucket(actorId: string): Promise<RateLimitBucket | null>;
|
|
2586
|
+
updateRateLimitBucket(bucket: RateLimitBucket): Promise<void>;
|
|
2587
|
+
getAuditLog(filters?: {
|
|
2588
|
+
targetId?: string;
|
|
2589
|
+
actorId?: string;
|
|
2590
|
+
since?: string;
|
|
2591
|
+
until?: string;
|
|
2592
|
+
limit?: number;
|
|
2593
|
+
}): Promise<ModerationAction[]>;
|
|
2594
|
+
/**
|
|
2595
|
+
* Clear all data (useful for testing)
|
|
2596
|
+
*/
|
|
2597
|
+
clear(): void;
|
|
2598
|
+
/**
|
|
2599
|
+
* Get counts for dashboard/stats
|
|
2600
|
+
*/
|
|
2601
|
+
getStats(): Promise<{
|
|
2602
|
+
reports: {
|
|
2603
|
+
total: number;
|
|
2604
|
+
pending: number;
|
|
2605
|
+
};
|
|
2606
|
+
actions: {
|
|
2607
|
+
total: number;
|
|
2608
|
+
active: number;
|
|
2609
|
+
};
|
|
2610
|
+
blocks: {
|
|
2611
|
+
total: number;
|
|
2612
|
+
active: number;
|
|
2613
|
+
};
|
|
2614
|
+
policies: {
|
|
2615
|
+
total: number;
|
|
2616
|
+
enabled: number;
|
|
2617
|
+
};
|
|
2618
|
+
}>;
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* D1 Moderation Store
|
|
2622
|
+
*
|
|
2623
|
+
* Cloudflare D1-compatible implementation of ModerationStore for production
|
|
2624
|
+
* moderation support on Cloudflare Workers.
|
|
2625
|
+
*/
|
|
2626
|
+
/**
|
|
2627
|
+
* D1-compatible implementation of ModerationStore
|
|
2628
|
+
*
|
|
2629
|
+
* @example
|
|
2630
|
+
* ```typescript
|
|
2631
|
+
* const store = new D1ModerationStore(env.DB);
|
|
2632
|
+
* await store.init();
|
|
2633
|
+
*
|
|
2634
|
+
* // Create a report
|
|
2635
|
+
* const report = await store.createReport({
|
|
2636
|
+
* reporterActorId: 'https://example.com/users/alice',
|
|
2637
|
+
* reporterInstance: 'example.com',
|
|
2638
|
+
* targetIds: ['https://bad.com/cards/spam'],
|
|
2639
|
+
* category: 'spam',
|
|
2640
|
+
* description: 'This card is spam',
|
|
2641
|
+
* status: 'pending',
|
|
2642
|
+
* activityId: 'https://example.com/activities/123',
|
|
2643
|
+
* createdAt: new Date().toISOString(),
|
|
2644
|
+
* updatedAt: new Date().toISOString(),
|
|
2645
|
+
* receivingInstance: 'bad.com',
|
|
2646
|
+
* federatedToTarget: true,
|
|
2647
|
+
* });
|
|
2648
|
+
* ```
|
|
2649
|
+
*/
|
|
2650
|
+
export declare class D1ModerationStore implements ModerationStore {
|
|
2651
|
+
private db;
|
|
2652
|
+
private prefix;
|
|
2653
|
+
constructor(db: D1Database, tablePrefix?: string);
|
|
2654
|
+
/**
|
|
2655
|
+
* Initialize all moderation tables
|
|
2656
|
+
*/
|
|
2657
|
+
init(): Promise<void>;
|
|
2658
|
+
createReport(report: Omit<ModerationReport, "id">): Promise<ModerationReport>;
|
|
2659
|
+
getReport(id: string): Promise<ModerationReport | null>;
|
|
2660
|
+
updateReport(id: string, updates: Partial<ModerationReport>): Promise<void>;
|
|
2661
|
+
listReports(filters?: {
|
|
2662
|
+
status?: ReportStatus;
|
|
2663
|
+
category?: ReportCategory;
|
|
2664
|
+
targetId?: string;
|
|
2665
|
+
reporterActorId?: string;
|
|
2666
|
+
since?: string;
|
|
2667
|
+
limit?: number;
|
|
2668
|
+
offset?: number;
|
|
2669
|
+
}): Promise<ModerationReport[]>;
|
|
2670
|
+
countReports(filters?: {
|
|
2671
|
+
status?: ReportStatus;
|
|
2672
|
+
}): Promise<number>;
|
|
2673
|
+
createAction(action: Omit<ModerationAction, "id">): Promise<ModerationAction>;
|
|
2674
|
+
getAction(id: string): Promise<ModerationAction | null>;
|
|
2675
|
+
listActions(filters?: {
|
|
2676
|
+
targetId?: string;
|
|
2677
|
+
moderatorActorId?: string;
|
|
2678
|
+
actionType?: ActionType;
|
|
2679
|
+
active?: boolean;
|
|
2680
|
+
since?: string;
|
|
2681
|
+
limit?: number;
|
|
2682
|
+
}): Promise<ModerationAction[]>;
|
|
2683
|
+
deactivateAction(id: string): Promise<void>;
|
|
2684
|
+
createBlock(block: Omit<InstanceBlock, "id">): Promise<InstanceBlock>;
|
|
2685
|
+
getBlock(id: string): Promise<InstanceBlock | null>;
|
|
2686
|
+
getBlockByDomain(domain: string): Promise<InstanceBlock | null>;
|
|
2687
|
+
listBlocks(filters?: {
|
|
2688
|
+
active?: boolean;
|
|
2689
|
+
}): Promise<InstanceBlock[]>;
|
|
2690
|
+
updateBlock(id: string, updates: Partial<InstanceBlock>): Promise<void>;
|
|
2691
|
+
isInstanceBlocked(domain: string): Promise<boolean>;
|
|
2692
|
+
createPolicy(policy: Omit<ContentPolicy, "id">): Promise<ContentPolicy>;
|
|
2693
|
+
getPolicy(id: string): Promise<ContentPolicy | null>;
|
|
2694
|
+
listPolicies(filters?: {
|
|
2695
|
+
enabled?: boolean;
|
|
2696
|
+
}): Promise<ContentPolicy[]>;
|
|
2697
|
+
updatePolicy(id: string, updates: Partial<ContentPolicy>): Promise<void>;
|
|
2698
|
+
deletePolicy(id: string): Promise<void>;
|
|
2699
|
+
getRateLimitBucket(actorId: string): Promise<RateLimitBucket | null>;
|
|
2700
|
+
updateRateLimitBucket(bucket: RateLimitBucket): Promise<void>;
|
|
2701
|
+
getAuditLog(filters?: {
|
|
2702
|
+
targetId?: string;
|
|
2703
|
+
actorId?: string;
|
|
2704
|
+
since?: string;
|
|
2705
|
+
until?: string;
|
|
2706
|
+
limit?: number;
|
|
2707
|
+
}): Promise<ModerationAction[]>;
|
|
2708
|
+
/**
|
|
2709
|
+
* Clear all moderation data (for testing)
|
|
2710
|
+
*/
|
|
2711
|
+
clear(): Promise<void>;
|
|
2712
|
+
/**
|
|
2713
|
+
* Get stats for dashboard
|
|
2714
|
+
*/
|
|
2715
|
+
getStats(): Promise<{
|
|
2716
|
+
reports: {
|
|
2717
|
+
total: number;
|
|
2718
|
+
pending: number;
|
|
2719
|
+
};
|
|
2720
|
+
actions: {
|
|
2721
|
+
total: number;
|
|
2722
|
+
active: number;
|
|
2723
|
+
};
|
|
2724
|
+
blocks: {
|
|
2725
|
+
total: number;
|
|
2726
|
+
active: number;
|
|
2727
|
+
};
|
|
2728
|
+
policies: {
|
|
2729
|
+
total: number;
|
|
2730
|
+
enabled: number;
|
|
2731
|
+
};
|
|
2732
|
+
}>;
|
|
2733
|
+
private rowToReport;
|
|
2734
|
+
private rowToAction;
|
|
2735
|
+
private rowToBlock;
|
|
2736
|
+
private rowToPolicy;
|
|
2737
|
+
private rowToRateLimit;
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Content Policy Engine
|
|
2741
|
+
*
|
|
2742
|
+
* Evaluates incoming cards against content policies.
|
|
2743
|
+
* Provides tools for community-defined moderation rules.
|
|
2744
|
+
*/
|
|
2745
|
+
/**
|
|
2746
|
+
* Check if a regex pattern might be vulnerable to ReDoS
|
|
2747
|
+
* Returns a warning message if the pattern looks dangerous, null if safe
|
|
2748
|
+
*
|
|
2749
|
+
* @security This is a heuristic check, not a guarantee.
|
|
2750
|
+
* Admin-only patterns should still be audited manually.
|
|
2751
|
+
*/
|
|
2752
|
+
export declare function checkRegexSafety(pattern: string): string | null;
|
|
2753
|
+
/**
|
|
2754
|
+
* Content Policy Engine
|
|
2755
|
+
*
|
|
2756
|
+
* Evaluates cards against configured policies and returns the appropriate action.
|
|
2757
|
+
*
|
|
2758
|
+
* @example
|
|
2759
|
+
* ```typescript
|
|
2760
|
+
* const engine = new PolicyEngine(moderationStore);
|
|
2761
|
+
*
|
|
2762
|
+
* const result = await engine.evaluateCard(card, 'source.example.com');
|
|
2763
|
+
* if (result.action === 'reject') {
|
|
2764
|
+
* return new Response('Content policy violation', { status: 403 });
|
|
2765
|
+
* }
|
|
2766
|
+
* if (result.action === 'review') {
|
|
2767
|
+
* await queueForReview(card, result.matchedRules);
|
|
2768
|
+
* }
|
|
2769
|
+
* ```
|
|
2770
|
+
*/
|
|
2771
|
+
export declare class PolicyEngine {
|
|
2772
|
+
private store;
|
|
2773
|
+
private compiledRegexCache;
|
|
2774
|
+
constructor(store: ModerationStore);
|
|
2775
|
+
/**
|
|
2776
|
+
* Evaluate a card against all active policies
|
|
2777
|
+
*
|
|
2778
|
+
* @param card - The CCv3 card to evaluate
|
|
2779
|
+
* @param sourceInstance - Optional source instance domain for instance rules
|
|
2780
|
+
* @returns Evaluation result with action and matched rules
|
|
2781
|
+
*/
|
|
2782
|
+
evaluateCard(card: CCv3Data, sourceInstance?: string): Promise<PolicyEvaluationResult>;
|
|
2783
|
+
/**
|
|
2784
|
+
* Evaluate card against a single policy
|
|
2785
|
+
*/
|
|
2786
|
+
private evaluateAgainstPolicy;
|
|
2787
|
+
/**
|
|
2788
|
+
* Evaluate a single rule against a card
|
|
2789
|
+
*/
|
|
2790
|
+
private evaluateRule;
|
|
2791
|
+
/**
|
|
2792
|
+
* Keyword rule - case-insensitive text search
|
|
2793
|
+
*/
|
|
2794
|
+
private evaluateKeywordRule;
|
|
2795
|
+
/**
|
|
2796
|
+
* Regex rule - pattern matching with compiled cache
|
|
2797
|
+
*
|
|
2798
|
+
* @security Input is truncated to MAX_REGEX_INPUT_LENGTH to prevent ReDoS.
|
|
2799
|
+
* Patterns should be admin-audited. Use checkRegexSafety() when creating rules.
|
|
2800
|
+
*/
|
|
2801
|
+
private evaluateRegexRule;
|
|
2802
|
+
/**
|
|
2803
|
+
* Tag rule - check card tags array
|
|
2804
|
+
*/
|
|
2805
|
+
private evaluateTagRule;
|
|
2806
|
+
/**
|
|
2807
|
+
* Creator rule - match card creator
|
|
2808
|
+
*/
|
|
2809
|
+
private evaluateCreatorRule;
|
|
2810
|
+
/**
|
|
2811
|
+
* Instance rule - match source domain
|
|
2812
|
+
* Supports wildcard patterns like "*.evil.com"
|
|
2813
|
+
*/
|
|
2814
|
+
private evaluateInstanceRule;
|
|
2815
|
+
/**
|
|
2816
|
+
* Get a field value from card data
|
|
2817
|
+
*/
|
|
2818
|
+
private getCardField;
|
|
2819
|
+
/**
|
|
2820
|
+
* Clear regex cache (call when rules change)
|
|
2821
|
+
*/
|
|
2822
|
+
clearCache(): void;
|
|
2823
|
+
/**
|
|
2824
|
+
* Pre-compile regexes for a policy (optimization)
|
|
2825
|
+
*
|
|
2826
|
+
* @returns errors for invalid patterns, warnings for potentially unsafe patterns
|
|
2827
|
+
*/
|
|
2828
|
+
precompilePolicy(policy: ContentPolicy): {
|
|
2829
|
+
errors: string[];
|
|
2830
|
+
warnings: string[];
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Rate Limiter
|
|
2835
|
+
*
|
|
2836
|
+
* Token bucket rate limiting for moderation actions.
|
|
2837
|
+
* Prevents abuse of report/flag functionality.
|
|
2838
|
+
*/
|
|
2839
|
+
/**
|
|
2840
|
+
* Rate limiter configuration
|
|
2841
|
+
*/
|
|
2842
|
+
export interface RateLimiterConfig {
|
|
2843
|
+
/** Max tokens per bucket (default: 5) */
|
|
2844
|
+
maxTokens: number;
|
|
2845
|
+
/** Tokens per hour refill rate (default: 1) */
|
|
2846
|
+
refillRate: number;
|
|
2847
|
+
}
|
|
2848
|
+
/**
|
|
2849
|
+
* Token bucket rate limiter
|
|
2850
|
+
*
|
|
2851
|
+
* @example
|
|
2852
|
+
* ```typescript
|
|
2853
|
+
* const limiter = new RateLimiter(moderationStore, {
|
|
2854
|
+
* maxTokens: 10,
|
|
2855
|
+
* refillRate: 2, // 2 tokens per hour
|
|
2856
|
+
* });
|
|
2857
|
+
*
|
|
2858
|
+
* const result = await limiter.checkAndConsume('https://example.com/users/alice');
|
|
2859
|
+
* if (!result.allowed) {
|
|
2860
|
+
* return new Response('Too many reports', {
|
|
2861
|
+
* status: 429,
|
|
2862
|
+
* headers: { 'Retry-After': String(result.retryAfter) },
|
|
2863
|
+
* });
|
|
2864
|
+
* }
|
|
2865
|
+
* ```
|
|
2866
|
+
*/
|
|
2867
|
+
export declare class RateLimiter {
|
|
2868
|
+
private store;
|
|
2869
|
+
private config;
|
|
2870
|
+
constructor(store: ModerationStore, config?: Partial<RateLimiterConfig>);
|
|
2871
|
+
/**
|
|
2872
|
+
* Check if action is allowed and consume a token if so
|
|
2873
|
+
*
|
|
2874
|
+
* @param actorId - Actor URI to check
|
|
2875
|
+
* @returns Rate limit result with remaining tokens and reset time
|
|
2876
|
+
*/
|
|
2877
|
+
checkAndConsume(actorId: string): Promise<RateLimitResult>;
|
|
2878
|
+
/**
|
|
2879
|
+
* Check remaining tokens without consuming
|
|
2880
|
+
*
|
|
2881
|
+
* @param actorId - Actor URI to check
|
|
2882
|
+
* @returns Current rate limit status
|
|
2883
|
+
*/
|
|
2884
|
+
check(actorId: string): Promise<RateLimitResult>;
|
|
2885
|
+
/**
|
|
2886
|
+
* Reset rate limit for an actor (admin function)
|
|
2887
|
+
*
|
|
2888
|
+
* @param actorId - Actor URI to reset
|
|
2889
|
+
*/
|
|
2890
|
+
reset(actorId: string): Promise<void>;
|
|
2891
|
+
/**
|
|
2892
|
+
* Refill tokens based on time elapsed since last refill
|
|
2893
|
+
*/
|
|
2894
|
+
private refillBucket;
|
|
2895
|
+
/**
|
|
2896
|
+
* Calculate when bucket will have at least 1 token
|
|
2897
|
+
*/
|
|
2898
|
+
private calculateResetTime;
|
|
2899
|
+
/**
|
|
2900
|
+
* Get current configuration
|
|
2901
|
+
*/
|
|
2902
|
+
getConfig(): RateLimiterConfig;
|
|
2903
|
+
}
|
|
2904
|
+
/**
|
|
2905
|
+
* @character-foundry/federation
|
|
2906
|
+
*
|
|
2907
|
+
* Federation layer for syncing character cards across platforms via ActivityPub.
|
|
2908
|
+
*
|
|
2909
|
+
* ⚠️ WARNING: This package is experimental and incomplete.
|
|
2910
|
+
*
|
|
2911
|
+
* Security-critical features (signature validation, inbox handling) are stubbed.
|
|
2912
|
+
* Do NOT use in production without explicit opt-in.
|
|
2913
|
+
*
|
|
2914
|
+
* To enable federation features, you MUST:
|
|
2915
|
+
* 1. Set FEDERATION_ENABLED=true in your environment (Node.js) or call enableFederation({ skipEnvCheck: true }) in browser/Workers
|
|
2916
|
+
* 2. Call enableFederation() before using SyncEngine or route handlers
|
|
2917
|
+
*
|
|
2918
|
+
* Both steps are required as a dual opt-in safety mechanism (except in environments without process.env).
|
|
2919
|
+
*/
|
|
2920
|
+
/**
|
|
2921
|
+
* Enable federation features. Must be called before using SyncEngine or route handlers.
|
|
2922
|
+
* This is a safety gate - federation is experimental and has incomplete security.
|
|
2923
|
+
*
|
|
2924
|
+
* @param options.skipEnvCheck - Skip FEDERATION_ENABLED env var check (required for browser/Workers)
|
|
2925
|
+
*
|
|
2926
|
+
* Note: In Node.js, both this call AND FEDERATION_ENABLED=true are required (dual opt-in).
|
|
2927
|
+
* In browser/Workers, use skipEnvCheck: true since env vars aren't available.
|
|
2928
|
+
*/
|
|
2929
|
+
export declare function enableFederation(options?: {
|
|
2930
|
+
skipEnvCheck?: boolean;
|
|
2931
|
+
}): void;
|
|
2932
|
+
/**
|
|
2933
|
+
* Check if federation is enabled
|
|
2934
|
+
*
|
|
2935
|
+
* In Node.js: requires BOTH env var AND enableFederation() call
|
|
2936
|
+
* In browser/Workers with skipEnvCheck: requires only enableFederation() call
|
|
2937
|
+
*/
|
|
2938
|
+
export declare function isFederationEnabled(): boolean;
|
|
2939
|
+
/**
|
|
2940
|
+
* Assert federation is enabled, throw if not
|
|
2941
|
+
* @internal
|
|
2942
|
+
*/
|
|
2943
|
+
export declare function assertFederationEnabled(feature: string): void;
|
|
2944
|
+
|
|
2945
|
+
export {
|
|
2946
|
+
validateActivitySignature as validateHttpSignature,
|
|
2947
|
+
validateBlockActivity as validateBlockActivityFields,
|
|
2948
|
+
validateFlagActivity as validateFlagActivityFields,
|
|
2949
|
+
};
|
|
2950
|
+
|
|
2951
|
+
export {};
|