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.
Files changed (131) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +293 -0
  3. package/dist/cli.d.ts +10 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +261 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/health-check.d.ts +7 -0
  8. package/dist/health-check.d.ts.map +1 -0
  9. package/dist/health-check.js +57 -0
  10. package/dist/health-check.js.map +1 -0
  11. package/dist/index.d.ts +122 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +612 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/prompts/index.d.ts +77 -0
  16. package/dist/prompts/index.d.ts.map +1 -0
  17. package/dist/prompts/index.js +186 -0
  18. package/dist/prompts/index.js.map +1 -0
  19. package/dist/resources/index.d.ts +75 -0
  20. package/dist/resources/index.d.ts.map +1 -0
  21. package/dist/resources/index.js +218 -0
  22. package/dist/resources/index.js.map +1 -0
  23. package/dist/test/setup.d.ts +78 -0
  24. package/dist/test/setup.d.ts.map +1 -0
  25. package/dist/test/setup.js +138 -0
  26. package/dist/test/setup.js.map +1 -0
  27. package/dist/tools/implementations/advanced-social-tools.d.ts +250 -0
  28. package/dist/tools/implementations/advanced-social-tools.d.ts.map +1 -0
  29. package/dist/tools/implementations/advanced-social-tools.js +380 -0
  30. package/dist/tools/implementations/advanced-social-tools.js.map +1 -0
  31. package/dist/tools/implementations/base-tool.d.ts +73 -0
  32. package/dist/tools/implementations/base-tool.d.ts.map +1 -0
  33. package/dist/tools/implementations/base-tool.js +225 -0
  34. package/dist/tools/implementations/base-tool.js.map +1 -0
  35. package/dist/tools/implementations/content-management-tools.d.ts +81 -0
  36. package/dist/tools/implementations/content-management-tools.d.ts.map +1 -0
  37. package/dist/tools/implementations/content-management-tools.js +236 -0
  38. package/dist/tools/implementations/content-management-tools.js.map +1 -0
  39. package/dist/tools/implementations/create-post-tool.d.ts +131 -0
  40. package/dist/tools/implementations/create-post-tool.d.ts.map +1 -0
  41. package/dist/tools/implementations/create-post-tool.js +182 -0
  42. package/dist/tools/implementations/create-post-tool.js.map +1 -0
  43. package/dist/tools/implementations/follow-user-tool.d.ts +69 -0
  44. package/dist/tools/implementations/follow-user-tool.d.ts.map +1 -0
  45. package/dist/tools/implementations/follow-user-tool.js +200 -0
  46. package/dist/tools/implementations/follow-user-tool.js.map +1 -0
  47. package/dist/tools/implementations/get-user-profile-tool.d.ts +61 -0
  48. package/dist/tools/implementations/get-user-profile-tool.d.ts.map +1 -0
  49. package/dist/tools/implementations/get-user-profile-tool.js +139 -0
  50. package/dist/tools/implementations/get-user-profile-tool.js.map +1 -0
  51. package/dist/tools/implementations/index.d.ts +22 -0
  52. package/dist/tools/implementations/index.d.ts.map +1 -0
  53. package/dist/tools/implementations/index.js +32 -0
  54. package/dist/tools/implementations/index.js.map +1 -0
  55. package/dist/tools/implementations/like-post-tool.d.ts +68 -0
  56. package/dist/tools/implementations/like-post-tool.d.ts.map +1 -0
  57. package/dist/tools/implementations/like-post-tool.js +184 -0
  58. package/dist/tools/implementations/like-post-tool.js.map +1 -0
  59. package/dist/tools/implementations/media-tools.d.ts +360 -0
  60. package/dist/tools/implementations/media-tools.d.ts.map +1 -0
  61. package/dist/tools/implementations/media-tools.js +444 -0
  62. package/dist/tools/implementations/media-tools.js.map +1 -0
  63. package/dist/tools/implementations/moderation-tools.d.ts +189 -0
  64. package/dist/tools/implementations/moderation-tools.d.ts.map +1 -0
  65. package/dist/tools/implementations/moderation-tools.js +289 -0
  66. package/dist/tools/implementations/moderation-tools.js.map +1 -0
  67. package/dist/tools/implementations/oauth-tools.d.ts +108 -0
  68. package/dist/tools/implementations/oauth-tools.d.ts.map +1 -0
  69. package/dist/tools/implementations/oauth-tools.js +183 -0
  70. package/dist/tools/implementations/oauth-tools.js.map +1 -0
  71. package/dist/tools/implementations/reply-to-post-tool.d.ts +52 -0
  72. package/dist/tools/implementations/reply-to-post-tool.d.ts.map +1 -0
  73. package/dist/tools/implementations/reply-to-post-tool.js +167 -0
  74. package/dist/tools/implementations/reply-to-post-tool.js.map +1 -0
  75. package/dist/tools/implementations/repost-tool.d.ts +76 -0
  76. package/dist/tools/implementations/repost-tool.d.ts.map +1 -0
  77. package/dist/tools/implementations/repost-tool.js +181 -0
  78. package/dist/tools/implementations/repost-tool.js.map +1 -0
  79. package/dist/tools/implementations/search-posts-tool.d.ts +95 -0
  80. package/dist/tools/implementations/search-posts-tool.d.ts.map +1 -0
  81. package/dist/tools/implementations/search-posts-tool.js +208 -0
  82. package/dist/tools/implementations/search-posts-tool.js.map +1 -0
  83. package/dist/tools/implementations/social-graph-tools.d.ts +119 -0
  84. package/dist/tools/implementations/social-graph-tools.d.ts.map +1 -0
  85. package/dist/tools/implementations/social-graph-tools.js +262 -0
  86. package/dist/tools/implementations/social-graph-tools.js.map +1 -0
  87. package/dist/tools/implementations/streaming-tools.d.ts +130 -0
  88. package/dist/tools/implementations/streaming-tools.d.ts.map +1 -0
  89. package/dist/tools/implementations/streaming-tools.js +241 -0
  90. package/dist/tools/implementations/streaming-tools.js.map +1 -0
  91. package/dist/tools/implementations/timeline-tools.d.ts +69 -0
  92. package/dist/tools/implementations/timeline-tools.d.ts.map +1 -0
  93. package/dist/tools/implementations/timeline-tools.js +194 -0
  94. package/dist/tools/implementations/timeline-tools.js.map +1 -0
  95. package/dist/tools/index.d.ts +23 -0
  96. package/dist/tools/index.d.ts.map +1 -0
  97. package/dist/tools/index.js +73 -0
  98. package/dist/tools/index.js.map +1 -0
  99. package/dist/types/index.d.ts +242 -0
  100. package/dist/types/index.d.ts.map +1 -0
  101. package/dist/types/index.js +73 -0
  102. package/dist/types/index.js.map +1 -0
  103. package/dist/utils/atp-client.d.ts +109 -0
  104. package/dist/utils/atp-client.d.ts.map +1 -0
  105. package/dist/utils/atp-client.js +451 -0
  106. package/dist/utils/atp-client.js.map +1 -0
  107. package/dist/utils/config.d.ts +74 -0
  108. package/dist/utils/config.d.ts.map +1 -0
  109. package/dist/utils/config.js +311 -0
  110. package/dist/utils/config.js.map +1 -0
  111. package/dist/utils/firehose-client.d.ts +96 -0
  112. package/dist/utils/firehose-client.d.ts.map +1 -0
  113. package/dist/utils/firehose-client.js +252 -0
  114. package/dist/utils/firehose-client.js.map +1 -0
  115. package/dist/utils/logger.d.ts +74 -0
  116. package/dist/utils/logger.d.ts.map +1 -0
  117. package/dist/utils/logger.js +142 -0
  118. package/dist/utils/logger.js.map +1 -0
  119. package/dist/utils/oauth-client.d.ts +61 -0
  120. package/dist/utils/oauth-client.d.ts.map +1 -0
  121. package/dist/utils/oauth-client.js +224 -0
  122. package/dist/utils/oauth-client.js.map +1 -0
  123. package/dist/utils/performance.d.ts +102 -0
  124. package/dist/utils/performance.d.ts.map +1 -0
  125. package/dist/utils/performance.js +302 -0
  126. package/dist/utils/performance.js.map +1 -0
  127. package/dist/utils/security.d.ts +154 -0
  128. package/dist/utils/security.d.ts.map +1 -0
  129. package/dist/utils/security.js +358 -0
  130. package/dist/utils/security.js.map +1 -0
  131. 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