atproto-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +293 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +261 -0
- package/dist/cli.js.map +1 -0
- package/dist/health-check.d.ts +7 -0
- package/dist/health-check.d.ts.map +1 -0
- package/dist/health-check.js +57 -0
- package/dist/health-check.js.map +1 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +612 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/index.d.ts +77 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +186 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/index.d.ts +75 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +218 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/test/setup.d.ts +78 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +138 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/tools/implementations/advanced-social-tools.d.ts +250 -0
- package/dist/tools/implementations/advanced-social-tools.d.ts.map +1 -0
- package/dist/tools/implementations/advanced-social-tools.js +380 -0
- package/dist/tools/implementations/advanced-social-tools.js.map +1 -0
- package/dist/tools/implementations/base-tool.d.ts +73 -0
- package/dist/tools/implementations/base-tool.d.ts.map +1 -0
- package/dist/tools/implementations/base-tool.js +225 -0
- package/dist/tools/implementations/base-tool.js.map +1 -0
- package/dist/tools/implementations/content-management-tools.d.ts +81 -0
- package/dist/tools/implementations/content-management-tools.d.ts.map +1 -0
- package/dist/tools/implementations/content-management-tools.js +236 -0
- package/dist/tools/implementations/content-management-tools.js.map +1 -0
- package/dist/tools/implementations/create-post-tool.d.ts +131 -0
- package/dist/tools/implementations/create-post-tool.d.ts.map +1 -0
- package/dist/tools/implementations/create-post-tool.js +182 -0
- package/dist/tools/implementations/create-post-tool.js.map +1 -0
- package/dist/tools/implementations/follow-user-tool.d.ts +69 -0
- package/dist/tools/implementations/follow-user-tool.d.ts.map +1 -0
- package/dist/tools/implementations/follow-user-tool.js +200 -0
- package/dist/tools/implementations/follow-user-tool.js.map +1 -0
- package/dist/tools/implementations/get-user-profile-tool.d.ts +61 -0
- package/dist/tools/implementations/get-user-profile-tool.d.ts.map +1 -0
- package/dist/tools/implementations/get-user-profile-tool.js +139 -0
- package/dist/tools/implementations/get-user-profile-tool.js.map +1 -0
- package/dist/tools/implementations/index.d.ts +22 -0
- package/dist/tools/implementations/index.d.ts.map +1 -0
- package/dist/tools/implementations/index.js +32 -0
- package/dist/tools/implementations/index.js.map +1 -0
- package/dist/tools/implementations/like-post-tool.d.ts +68 -0
- package/dist/tools/implementations/like-post-tool.d.ts.map +1 -0
- package/dist/tools/implementations/like-post-tool.js +184 -0
- package/dist/tools/implementations/like-post-tool.js.map +1 -0
- package/dist/tools/implementations/media-tools.d.ts +360 -0
- package/dist/tools/implementations/media-tools.d.ts.map +1 -0
- package/dist/tools/implementations/media-tools.js +444 -0
- package/dist/tools/implementations/media-tools.js.map +1 -0
- package/dist/tools/implementations/moderation-tools.d.ts +189 -0
- package/dist/tools/implementations/moderation-tools.d.ts.map +1 -0
- package/dist/tools/implementations/moderation-tools.js +289 -0
- package/dist/tools/implementations/moderation-tools.js.map +1 -0
- package/dist/tools/implementations/oauth-tools.d.ts +108 -0
- package/dist/tools/implementations/oauth-tools.d.ts.map +1 -0
- package/dist/tools/implementations/oauth-tools.js +183 -0
- package/dist/tools/implementations/oauth-tools.js.map +1 -0
- package/dist/tools/implementations/reply-to-post-tool.d.ts +52 -0
- package/dist/tools/implementations/reply-to-post-tool.d.ts.map +1 -0
- package/dist/tools/implementations/reply-to-post-tool.js +167 -0
- package/dist/tools/implementations/reply-to-post-tool.js.map +1 -0
- package/dist/tools/implementations/repost-tool.d.ts +76 -0
- package/dist/tools/implementations/repost-tool.d.ts.map +1 -0
- package/dist/tools/implementations/repost-tool.js +181 -0
- package/dist/tools/implementations/repost-tool.js.map +1 -0
- package/dist/tools/implementations/search-posts-tool.d.ts +95 -0
- package/dist/tools/implementations/search-posts-tool.d.ts.map +1 -0
- package/dist/tools/implementations/search-posts-tool.js +208 -0
- package/dist/tools/implementations/search-posts-tool.js.map +1 -0
- package/dist/tools/implementations/social-graph-tools.d.ts +119 -0
- package/dist/tools/implementations/social-graph-tools.d.ts.map +1 -0
- package/dist/tools/implementations/social-graph-tools.js +262 -0
- package/dist/tools/implementations/social-graph-tools.js.map +1 -0
- package/dist/tools/implementations/streaming-tools.d.ts +130 -0
- package/dist/tools/implementations/streaming-tools.d.ts.map +1 -0
- package/dist/tools/implementations/streaming-tools.js +241 -0
- package/dist/tools/implementations/streaming-tools.js.map +1 -0
- package/dist/tools/implementations/timeline-tools.d.ts +69 -0
- package/dist/tools/implementations/timeline-tools.d.ts.map +1 -0
- package/dist/tools/implementations/timeline-tools.js +194 -0
- package/dist/tools/implementations/timeline-tools.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +73 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +242 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +73 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/atp-client.d.ts +109 -0
- package/dist/utils/atp-client.d.ts.map +1 -0
- package/dist/utils/atp-client.js +451 -0
- package/dist/utils/atp-client.js.map +1 -0
- package/dist/utils/config.d.ts +74 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +311 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/firehose-client.d.ts +96 -0
- package/dist/utils/firehose-client.d.ts.map +1 -0
- package/dist/utils/firehose-client.js +252 -0
- package/dist/utils/firehose-client.js.map +1 -0
- package/dist/utils/logger.d.ts +74 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +142 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/oauth-client.d.ts +61 -0
- package/dist/utils/oauth-client.d.ts.map +1 -0
- package/dist/utils/oauth-client.js +224 -0
- package/dist/utils/oauth-client.js.map +1 -0
- package/dist/utils/performance.d.ts +102 -0
- package/dist/utils/performance.d.ts.map +1 -0
- package/dist/utils/performance.js +302 -0
- package/dist/utils/performance.js.map +1 -0
- package/dist/utils/security.d.ts +154 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +358 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management system for the AT Protocol MCP Server
|
|
3
|
+
* Handles environment variables, command-line arguments, and default settings
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { ConfigurationError } from '../types/index.js';
|
|
7
|
+
import { Logger } from './logger.js';
|
|
8
|
+
const logger = new Logger('Config');
|
|
9
|
+
/**
|
|
10
|
+
* Zod schema for AT Protocol configuration validation
|
|
11
|
+
* Authentication credentials are now optional to support unauthenticated mode
|
|
12
|
+
*/
|
|
13
|
+
const AtpConfigSchema = z.object({
|
|
14
|
+
service: z.string().url('AT Protocol service must be a valid URL'),
|
|
15
|
+
identifier: z.string().optional(),
|
|
16
|
+
password: z.string().optional(),
|
|
17
|
+
clientId: z.string().optional(),
|
|
18
|
+
clientSecret: z.string().optional(),
|
|
19
|
+
authMethod: z.enum(['app-password', 'oauth']).optional(),
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Zod schema for MCP server configuration validation
|
|
23
|
+
*/
|
|
24
|
+
const McpServerConfigSchema = z.object({
|
|
25
|
+
port: z.number().int().min(1).max(65535),
|
|
26
|
+
host: z.string().min(1),
|
|
27
|
+
name: z.string().min(1),
|
|
28
|
+
version: z.string().min(1),
|
|
29
|
+
description: z.string().min(1),
|
|
30
|
+
atproto: AtpConfigSchema,
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Default configuration values
|
|
34
|
+
* Authentication is now optional - server works in unauthenticated mode by default
|
|
35
|
+
*/
|
|
36
|
+
function createDefaultConfig() {
|
|
37
|
+
// Determine if we have authentication credentials
|
|
38
|
+
const hasAppPasswordCreds = !!(process.env['ATPROTO_IDENTIFIER'] != null &&
|
|
39
|
+
process.env['ATPROTO_IDENTIFIER'] !== '' &&
|
|
40
|
+
process.env['ATPROTO_PASSWORD'] != null &&
|
|
41
|
+
process.env['ATPROTO_PASSWORD'] !== '');
|
|
42
|
+
const hasOAuthCreds = !!(process.env['ATPROTO_CLIENT_ID'] != null &&
|
|
43
|
+
process.env['ATPROTO_CLIENT_ID'] !== '' &&
|
|
44
|
+
process.env['ATPROTO_CLIENT_SECRET'] != null &&
|
|
45
|
+
process.env['ATPROTO_CLIENT_SECRET'] !== '');
|
|
46
|
+
// Default to app-password if we have those credentials, otherwise no auth method
|
|
47
|
+
let defaultAuthMethod;
|
|
48
|
+
if (hasAppPasswordCreds) {
|
|
49
|
+
defaultAuthMethod = 'app-password';
|
|
50
|
+
}
|
|
51
|
+
else if (hasOAuthCreds) {
|
|
52
|
+
defaultAuthMethod = 'oauth';
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
port: 3000,
|
|
56
|
+
host: 'localhost',
|
|
57
|
+
name: 'atproto-mcp',
|
|
58
|
+
version: '0.1.0',
|
|
59
|
+
description: 'AT Protocol MCP Server - Comprehensive interface for LLMs to interact with AT Protocol (supports both authenticated and unauthenticated modes)',
|
|
60
|
+
atproto: {
|
|
61
|
+
service: 'https://bsky.social',
|
|
62
|
+
// Only set auth method if we have credentials
|
|
63
|
+
...(defaultAuthMethod != null && { authMethod: defaultAuthMethod }),
|
|
64
|
+
// Include credentials if available
|
|
65
|
+
...(process.env['ATPROTO_IDENTIFIER'] != null &&
|
|
66
|
+
process.env['ATPROTO_IDENTIFIER'] !== '' && {
|
|
67
|
+
identifier: process.env['ATPROTO_IDENTIFIER'],
|
|
68
|
+
}),
|
|
69
|
+
...(process.env['ATPROTO_PASSWORD'] != null &&
|
|
70
|
+
process.env['ATPROTO_PASSWORD'] !== '' && { password: process.env['ATPROTO_PASSWORD'] }),
|
|
71
|
+
...(process.env['ATPROTO_CLIENT_ID'] != null &&
|
|
72
|
+
process.env['ATPROTO_CLIENT_ID'] !== '' && { clientId: process.env['ATPROTO_CLIENT_ID'] }),
|
|
73
|
+
...(process.env['ATPROTO_CLIENT_SECRET'] != null &&
|
|
74
|
+
process.env['ATPROTO_CLIENT_SECRET'] !== '' && {
|
|
75
|
+
clientSecret: process.env['ATPROTO_CLIENT_SECRET'],
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Environment variable mappings
|
|
82
|
+
*/
|
|
83
|
+
const ENV_MAPPINGS = {
|
|
84
|
+
MCP_SERVER_PORT: 'port',
|
|
85
|
+
MCP_SERVER_HOST: 'host',
|
|
86
|
+
MCP_SERVER_NAME: 'name',
|
|
87
|
+
ATPROTO_SERVICE: 'atproto.service',
|
|
88
|
+
ATPROTO_IDENTIFIER: 'atproto.identifier',
|
|
89
|
+
ATPROTO_PASSWORD: 'atproto.password',
|
|
90
|
+
ATPROTO_CLIENT_ID: 'atproto.clientId',
|
|
91
|
+
ATPROTO_CLIENT_SECRET: 'atproto.clientSecret',
|
|
92
|
+
ATPROTO_AUTH_METHOD: 'atproto.authMethod',
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Configuration manager class
|
|
96
|
+
*/
|
|
97
|
+
export class ConfigManager {
|
|
98
|
+
config;
|
|
99
|
+
constructor(overrides = {}) {
|
|
100
|
+
this.config = this.buildConfig(overrides);
|
|
101
|
+
this.validateConfig();
|
|
102
|
+
this.logConfiguration();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build configuration from defaults, environment variables, and overrides
|
|
106
|
+
*/
|
|
107
|
+
buildConfig(overrides) {
|
|
108
|
+
// Start with defaults
|
|
109
|
+
let config = createDefaultConfig();
|
|
110
|
+
// Apply environment variables
|
|
111
|
+
config = this.applyEnvironmentVariables(config);
|
|
112
|
+
// Apply overrides (highest priority)
|
|
113
|
+
config = this.mergeConfig(config, overrides);
|
|
114
|
+
return config;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Apply environment variables to configuration
|
|
118
|
+
*/
|
|
119
|
+
applyEnvironmentVariables(config) {
|
|
120
|
+
const envConfig = {};
|
|
121
|
+
for (const [envVar, configPath] of Object.entries(ENV_MAPPINGS)) {
|
|
122
|
+
const value = process.env[envVar];
|
|
123
|
+
if (value !== undefined) {
|
|
124
|
+
this.setNestedProperty(envConfig, configPath, this.parseEnvValue(value));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return this.mergeConfig(config, envConfig);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse environment variable value to appropriate type
|
|
131
|
+
*/
|
|
132
|
+
parseEnvValue(value) {
|
|
133
|
+
// Try to parse as number
|
|
134
|
+
const numValue = Number(value);
|
|
135
|
+
if (!isNaN(numValue) && isFinite(numValue)) {
|
|
136
|
+
return numValue;
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Set nested property using dot notation
|
|
142
|
+
*/
|
|
143
|
+
setNestedProperty(obj, path, value) {
|
|
144
|
+
const keys = path.split('.');
|
|
145
|
+
let current = obj;
|
|
146
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
147
|
+
const key = keys[i];
|
|
148
|
+
if (!(key in current) || typeof current[key] !== 'object') {
|
|
149
|
+
current[key] = {};
|
|
150
|
+
}
|
|
151
|
+
current = current[key];
|
|
152
|
+
}
|
|
153
|
+
const finalKey = keys[keys.length - 1];
|
|
154
|
+
current[finalKey] = value;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Deep merge configuration objects
|
|
158
|
+
*/
|
|
159
|
+
mergeConfig(base, override) {
|
|
160
|
+
const result = { ...base };
|
|
161
|
+
for (const [key, value] of Object.entries(override)) {
|
|
162
|
+
if (value !== undefined) {
|
|
163
|
+
if (key === 'atproto' && typeof value === 'object' && value !== null) {
|
|
164
|
+
result.atproto = { ...result.atproto, ...value };
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
result[key] = value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Validate configuration using Zod schema
|
|
175
|
+
*/
|
|
176
|
+
validateConfig() {
|
|
177
|
+
try {
|
|
178
|
+
McpServerConfigSchema.parse(this.config);
|
|
179
|
+
this.validateAuthConfiguration();
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (error instanceof z.ZodError) {
|
|
183
|
+
const issues = error.issues.map(issue => `${issue.path.join('.')}: ${issue.message}`);
|
|
184
|
+
throw new ConfigurationError(`Configuration validation failed: ${issues.join(', ')}`, {
|
|
185
|
+
issues,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Validate authentication configuration
|
|
193
|
+
* Authentication is now optional - only validate if auth method is specified
|
|
194
|
+
*/
|
|
195
|
+
validateAuthConfiguration() {
|
|
196
|
+
const { atproto } = this.config;
|
|
197
|
+
// Skip validation in test environment if credentials are not provided
|
|
198
|
+
const isTestEnv = process.env['NODE_ENV'] === 'test';
|
|
199
|
+
// If no auth method is specified, we're in unauthenticated mode - no validation needed
|
|
200
|
+
if (!atproto.authMethod) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (atproto.authMethod === 'app-password') {
|
|
204
|
+
if (!isTestEnv && (!atproto.identifier || !atproto.password)) {
|
|
205
|
+
throw new ConfigurationError('App password authentication requires both identifier and password', { authMethod: atproto.authMethod });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (atproto.authMethod === 'oauth') {
|
|
209
|
+
if (!isTestEnv && (!atproto.clientId || !atproto.clientSecret)) {
|
|
210
|
+
throw new ConfigurationError('OAuth authentication requires both clientId and clientSecret', { authMethod: atproto.authMethod });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Log configuration (excluding sensitive data)
|
|
216
|
+
*/
|
|
217
|
+
logConfiguration() {
|
|
218
|
+
const safeConfig = {
|
|
219
|
+
...this.config,
|
|
220
|
+
atproto: {
|
|
221
|
+
...this.config.atproto,
|
|
222
|
+
password: this.config.atproto.password ? '[REDACTED]' : undefined,
|
|
223
|
+
clientSecret: this.config.atproto.clientSecret ? '[REDACTED]' : undefined,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
logger.info('Configuration loaded', safeConfig);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get the current configuration
|
|
230
|
+
*/
|
|
231
|
+
getConfig() {
|
|
232
|
+
return { ...this.config };
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get AT Protocol configuration
|
|
236
|
+
*/
|
|
237
|
+
getAtpConfig() {
|
|
238
|
+
return { ...this.config.atproto };
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Update configuration at runtime
|
|
242
|
+
*/
|
|
243
|
+
updateConfig(updates) {
|
|
244
|
+
const newConfig = this.mergeConfig(this.config, updates);
|
|
245
|
+
// Validate the new configuration
|
|
246
|
+
try {
|
|
247
|
+
McpServerConfigSchema.parse(newConfig);
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
if (error instanceof z.ZodError) {
|
|
251
|
+
const issues = error.issues.map(issue => `${issue.path.join('.')}: ${issue.message}`);
|
|
252
|
+
throw new ConfigurationError(`Configuration update validation failed: ${issues.join(', ')}`, { issues });
|
|
253
|
+
}
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
this.config = newConfig;
|
|
257
|
+
logger.info('Configuration updated');
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check if configuration is valid for the specified auth method
|
|
261
|
+
*/
|
|
262
|
+
isValidForAuth(authMethod) {
|
|
263
|
+
try {
|
|
264
|
+
const testConfig = { ...this.config, atproto: { ...this.config.atproto, authMethod } };
|
|
265
|
+
McpServerConfigSchema.parse(testConfig);
|
|
266
|
+
// Additional validation for auth method requirements
|
|
267
|
+
if (authMethod === 'app-password') {
|
|
268
|
+
return !!(testConfig.atproto.identifier && testConfig.atproto.password);
|
|
269
|
+
}
|
|
270
|
+
else if (authMethod === 'oauth') {
|
|
271
|
+
return !!(testConfig.atproto.clientId && testConfig.atproto.clientSecret);
|
|
272
|
+
}
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Check if authentication is configured
|
|
281
|
+
*/
|
|
282
|
+
hasAuthentication() {
|
|
283
|
+
const { atproto } = this.config;
|
|
284
|
+
if (!atproto.authMethod) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
if (atproto.authMethod === 'app-password') {
|
|
288
|
+
return !!(atproto.identifier && atproto.password);
|
|
289
|
+
}
|
|
290
|
+
else if (atproto.authMethod === 'oauth') {
|
|
291
|
+
return !!(atproto.clientId && atproto.clientSecret);
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get authentication mode description
|
|
297
|
+
*/
|
|
298
|
+
getAuthMode() {
|
|
299
|
+
if (!this.hasAuthentication()) {
|
|
300
|
+
return 'unauthenticated';
|
|
301
|
+
}
|
|
302
|
+
return this.config.atproto.authMethod;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Create a configuration manager instance
|
|
307
|
+
*/
|
|
308
|
+
export function createConfig(overrides = {}) {
|
|
309
|
+
return new ConfigManager(overrides);
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT Protocol Firehose Client for real-time streaming
|
|
3
|
+
*/
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import type { IAtpConfig } from '../types/index.js';
|
|
6
|
+
export interface IFirehoseEvent {
|
|
7
|
+
type: 'commit' | 'handle' | 'migrate' | 'tombstone';
|
|
8
|
+
seq: number;
|
|
9
|
+
time: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
commit?: {
|
|
12
|
+
rev: string;
|
|
13
|
+
operation: 'create' | 'update' | 'delete';
|
|
14
|
+
collection: string;
|
|
15
|
+
rkey: string;
|
|
16
|
+
record?: any;
|
|
17
|
+
cid?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface IFirehoseSubscription {
|
|
21
|
+
id: string;
|
|
22
|
+
collections?: string[];
|
|
23
|
+
onEvent: (event: IFirehoseEvent) => void;
|
|
24
|
+
onError?: (error: Error) => void;
|
|
25
|
+
}
|
|
26
|
+
export declare class FirehoseClient extends EventEmitter {
|
|
27
|
+
private ws;
|
|
28
|
+
private logger;
|
|
29
|
+
private config;
|
|
30
|
+
private subscriptions;
|
|
31
|
+
private reconnectAttempts;
|
|
32
|
+
private maxReconnectAttempts;
|
|
33
|
+
private reconnectDelay;
|
|
34
|
+
private isConnecting;
|
|
35
|
+
private isShuttingDown;
|
|
36
|
+
private heartbeatInterval;
|
|
37
|
+
private lastSeq;
|
|
38
|
+
constructor(config: IAtpConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Connect to the AT Protocol firehose
|
|
41
|
+
*/
|
|
42
|
+
connect(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Disconnect from the firehose
|
|
45
|
+
*/
|
|
46
|
+
disconnect(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to firehose events
|
|
49
|
+
*/
|
|
50
|
+
subscribe(subscription: IFirehoseSubscription): void;
|
|
51
|
+
/**
|
|
52
|
+
* Unsubscribe from firehose events
|
|
53
|
+
*/
|
|
54
|
+
unsubscribe(subscriptionId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Get connection status
|
|
57
|
+
*/
|
|
58
|
+
isConnected(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Get the last processed sequence number
|
|
61
|
+
*/
|
|
62
|
+
getLastSeq(): number | null;
|
|
63
|
+
/**
|
|
64
|
+
* Get firehose WebSocket URL
|
|
65
|
+
*/
|
|
66
|
+
private getFirehoseUrl;
|
|
67
|
+
/**
|
|
68
|
+
* Handle incoming firehose message
|
|
69
|
+
*/
|
|
70
|
+
private handleMessage;
|
|
71
|
+
/**
|
|
72
|
+
* Parse firehose message (simplified implementation)
|
|
73
|
+
*/
|
|
74
|
+
private parseFirehoseMessage;
|
|
75
|
+
/**
|
|
76
|
+
* Process firehose event and notify subscribers
|
|
77
|
+
*/
|
|
78
|
+
private processEvent;
|
|
79
|
+
/**
|
|
80
|
+
* Check if event matches subscription criteria
|
|
81
|
+
*/
|
|
82
|
+
private matchesSubscription;
|
|
83
|
+
/**
|
|
84
|
+
* Schedule reconnection attempt
|
|
85
|
+
*/
|
|
86
|
+
private scheduleReconnect;
|
|
87
|
+
/**
|
|
88
|
+
* Start heartbeat to keep connection alive
|
|
89
|
+
*/
|
|
90
|
+
private startHeartbeat;
|
|
91
|
+
/**
|
|
92
|
+
* Cleanup resources
|
|
93
|
+
*/
|
|
94
|
+
private cleanup;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=firehose-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firehose-client.d.ts","sourceRoot":"","sources":["../../src/utils/firehose-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;QAC1C,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,GAAG,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,aAAa,CAA4C;IACjE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,OAAO,CAAuB;gBAE1B,MAAM,EAAE,UAAU;IAM9B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA+D9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC;;OAEG;IACH,SAAS,CAAC,YAAY,EAAE,qBAAqB,GAAG,IAAI;IAQpD;;OAEG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAKzC;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,UAAU,IAAI,MAAM,GAAG,IAAI;IAI3B;;OAEG;IACH,OAAO,CAAC,cAAc;IAKtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA8B5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAmBpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,OAAO;CAQhB"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT Protocol Firehose Client for real-time streaming
|
|
3
|
+
*/
|
|
4
|
+
import { WebSocket } from 'ws';
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
import { Logger } from './logger.js';
|
|
7
|
+
export class FirehoseClient extends EventEmitter {
|
|
8
|
+
ws = null;
|
|
9
|
+
logger;
|
|
10
|
+
config;
|
|
11
|
+
subscriptions = new Map();
|
|
12
|
+
reconnectAttempts = 0;
|
|
13
|
+
maxReconnectAttempts = 10;
|
|
14
|
+
reconnectDelay = 1000; // Start with 1 second
|
|
15
|
+
isConnecting = false;
|
|
16
|
+
isShuttingDown = false;
|
|
17
|
+
heartbeatInterval = null;
|
|
18
|
+
lastSeq = null;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
super();
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.logger = new Logger('FirehoseClient');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Connect to the AT Protocol firehose
|
|
26
|
+
*/
|
|
27
|
+
async connect() {
|
|
28
|
+
if (this.isConnecting || this.ws?.readyState === WebSocket.OPEN) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.isConnecting = true;
|
|
32
|
+
this.isShuttingDown = false;
|
|
33
|
+
try {
|
|
34
|
+
const firehoseUrl = this.getFirehoseUrl();
|
|
35
|
+
this.logger.info('Connecting to AT Protocol firehose', { url: firehoseUrl });
|
|
36
|
+
this.ws = new WebSocket(firehoseUrl);
|
|
37
|
+
this.ws.on('open', () => {
|
|
38
|
+
this.logger.info('Firehose connection established');
|
|
39
|
+
this.isConnecting = false;
|
|
40
|
+
this.reconnectAttempts = 0;
|
|
41
|
+
this.reconnectDelay = 1000;
|
|
42
|
+
this.startHeartbeat();
|
|
43
|
+
this.emit('connected');
|
|
44
|
+
});
|
|
45
|
+
this.ws.on('message', (data) => {
|
|
46
|
+
try {
|
|
47
|
+
this.handleMessage(data);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
this.logger.error('Error handling firehose message', error);
|
|
51
|
+
this.emit('error', error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
this.ws.on('close', (code, reason) => {
|
|
55
|
+
this.logger.warn('Firehose connection closed', {
|
|
56
|
+
code,
|
|
57
|
+
reason: reason.toString(),
|
|
58
|
+
reconnectAttempts: this.reconnectAttempts,
|
|
59
|
+
});
|
|
60
|
+
this.cleanup();
|
|
61
|
+
this.emit('disconnected', { code, reason: reason.toString() });
|
|
62
|
+
if (!this.isShuttingDown && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
63
|
+
this.scheduleReconnect();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.ws.on('error', (error) => {
|
|
67
|
+
this.logger.error('Firehose connection error', error);
|
|
68
|
+
this.cleanup();
|
|
69
|
+
this.emit('error', error);
|
|
70
|
+
if (!this.isShuttingDown && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
71
|
+
this.scheduleReconnect();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.isConnecting = false;
|
|
77
|
+
this.logger.error('Failed to connect to firehose', error);
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Disconnect from the firehose
|
|
83
|
+
*/
|
|
84
|
+
async disconnect() {
|
|
85
|
+
this.isShuttingDown = true;
|
|
86
|
+
this.cleanup();
|
|
87
|
+
if (this.ws) {
|
|
88
|
+
this.ws.close();
|
|
89
|
+
this.ws = null;
|
|
90
|
+
}
|
|
91
|
+
this.logger.info('Firehose client disconnected');
|
|
92
|
+
this.emit('disconnected', { code: 1000, reason: 'Client disconnect' });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Subscribe to firehose events
|
|
96
|
+
*/
|
|
97
|
+
subscribe(subscription) {
|
|
98
|
+
this.subscriptions.set(subscription.id, subscription);
|
|
99
|
+
this.logger.info('Added firehose subscription', {
|
|
100
|
+
id: subscription.id,
|
|
101
|
+
collections: subscription.collections,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Unsubscribe from firehose events
|
|
106
|
+
*/
|
|
107
|
+
unsubscribe(subscriptionId) {
|
|
108
|
+
this.subscriptions.delete(subscriptionId);
|
|
109
|
+
this.logger.info('Removed firehose subscription', { id: subscriptionId });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get connection status
|
|
113
|
+
*/
|
|
114
|
+
isConnected() {
|
|
115
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the last processed sequence number
|
|
119
|
+
*/
|
|
120
|
+
getLastSeq() {
|
|
121
|
+
return this.lastSeq;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get firehose WebSocket URL
|
|
125
|
+
*/
|
|
126
|
+
getFirehoseUrl() {
|
|
127
|
+
const baseUrl = this.config.service.replace(/^https?:\/\//, '');
|
|
128
|
+
return `wss://${baseUrl}/xrpc/com.atproto.sync.subscribeRepos`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Handle incoming firehose message
|
|
132
|
+
*/
|
|
133
|
+
handleMessage(data) {
|
|
134
|
+
try {
|
|
135
|
+
// Parse CAR (Content Addressable aRchive) format
|
|
136
|
+
// This is a simplified parser - in production, use proper CAR parsing library
|
|
137
|
+
const event = this.parseFirehoseMessage(data);
|
|
138
|
+
if (event) {
|
|
139
|
+
this.lastSeq = event.seq;
|
|
140
|
+
this.processEvent(event);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
this.logger.error('Failed to parse firehose message', error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Parse firehose message (simplified implementation)
|
|
149
|
+
*/
|
|
150
|
+
parseFirehoseMessage(_data) {
|
|
151
|
+
try {
|
|
152
|
+
// This is a simplified parser for demonstration
|
|
153
|
+
// In production, use @atproto/lexicon and proper CAR parsing
|
|
154
|
+
// For now, create a mock event structure
|
|
155
|
+
const mockEvent = {
|
|
156
|
+
type: 'commit',
|
|
157
|
+
seq: Date.now(),
|
|
158
|
+
time: new Date().toISOString(),
|
|
159
|
+
repo: 'did:plc:example',
|
|
160
|
+
commit: {
|
|
161
|
+
rev: 'rev123',
|
|
162
|
+
operation: 'create',
|
|
163
|
+
collection: 'app.bsky.feed.post',
|
|
164
|
+
rkey: 'rkey123',
|
|
165
|
+
record: {
|
|
166
|
+
text: 'Example post from firehose',
|
|
167
|
+
createdAt: new Date().toISOString(),
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
return mockEvent;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
this.logger.error('Failed to parse firehose message', error);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Process firehose event and notify subscribers
|
|
180
|
+
*/
|
|
181
|
+
processEvent(event) {
|
|
182
|
+
this.emit('event', event);
|
|
183
|
+
// Notify matching subscriptions
|
|
184
|
+
for (const subscription of this.subscriptions.values()) {
|
|
185
|
+
try {
|
|
186
|
+
// Check if subscription matches the event
|
|
187
|
+
if (this.matchesSubscription(event, subscription)) {
|
|
188
|
+
subscription.onEvent(event);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
this.logger.error('Error in subscription handler', error);
|
|
193
|
+
if (subscription.onError) {
|
|
194
|
+
subscription.onError(error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if event matches subscription criteria
|
|
201
|
+
*/
|
|
202
|
+
matchesSubscription(event, subscription) {
|
|
203
|
+
// If no collections specified, match all events
|
|
204
|
+
if (!subscription.collections || subscription.collections.length === 0) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
// Check if event collection matches subscription
|
|
208
|
+
if (event.commit?.collection) {
|
|
209
|
+
return subscription.collections.includes(event.commit.collection);
|
|
210
|
+
}
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Schedule reconnection attempt
|
|
215
|
+
*/
|
|
216
|
+
scheduleReconnect() {
|
|
217
|
+
this.reconnectAttempts++;
|
|
218
|
+
const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1), 30000);
|
|
219
|
+
this.logger.info('Scheduling firehose reconnection', {
|
|
220
|
+
attempt: this.reconnectAttempts,
|
|
221
|
+
delay,
|
|
222
|
+
});
|
|
223
|
+
setTimeout(() => {
|
|
224
|
+
if (!this.isShuttingDown) {
|
|
225
|
+
this.connect().catch(error => {
|
|
226
|
+
this.logger.error('Reconnection attempt failed', error);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}, delay);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Start heartbeat to keep connection alive
|
|
233
|
+
*/
|
|
234
|
+
startHeartbeat() {
|
|
235
|
+
this.heartbeatInterval = setInterval(() => {
|
|
236
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
237
|
+
this.ws.ping();
|
|
238
|
+
}
|
|
239
|
+
}, 30000); // Ping every 30 seconds
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Cleanup resources
|
|
243
|
+
*/
|
|
244
|
+
cleanup() {
|
|
245
|
+
this.isConnecting = false;
|
|
246
|
+
if (this.heartbeatInterval) {
|
|
247
|
+
clearInterval(this.heartbeatInterval);
|
|
248
|
+
this.heartbeatInterval = null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=firehose-client.js.map
|