spora 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 (62) hide show
  1. package/README.md +87 -0
  2. package/bin/spora.js +2 -0
  3. package/dist/account-creator-PZW5JLHS.js +498 -0
  4. package/dist/account-creator-PZW5JLHS.js.map +1 -0
  5. package/dist/chunk-3JEDGXEM.js +32 -0
  6. package/dist/chunk-3JEDGXEM.js.map +1 -0
  7. package/dist/chunk-53YLFYJF.js +59 -0
  8. package/dist/chunk-53YLFYJF.js.map +1 -0
  9. package/dist/chunk-7CR4ID6P.js +614 -0
  10. package/dist/chunk-7CR4ID6P.js.map +1 -0
  11. package/dist/chunk-AHXZIGQE.js +156 -0
  12. package/dist/chunk-AHXZIGQE.js.map +1 -0
  13. package/dist/chunk-DJJWHOL3.js +162 -0
  14. package/dist/chunk-DJJWHOL3.js.map +1 -0
  15. package/dist/chunk-EBO4F5NU.js +105 -0
  16. package/dist/chunk-EBO4F5NU.js.map +1 -0
  17. package/dist/chunk-ERTBXYOP.js +81 -0
  18. package/dist/chunk-ERTBXYOP.js.map +1 -0
  19. package/dist/chunk-KELPENM3.js +47 -0
  20. package/dist/chunk-KELPENM3.js.map +1 -0
  21. package/dist/chunk-NFDZ47AG.js +57 -0
  22. package/dist/chunk-NFDZ47AG.js.map +1 -0
  23. package/dist/chunk-O23NWMYU.js +124 -0
  24. package/dist/chunk-O23NWMYU.js.map +1 -0
  25. package/dist/chunk-YEKHNTQO.js +80 -0
  26. package/dist/chunk-YEKHNTQO.js.map +1 -0
  27. package/dist/chunk-ZJZKH7N7.js +56 -0
  28. package/dist/chunk-ZJZKH7N7.js.map +1 -0
  29. package/dist/cli.js +675 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/client-3AQCA4YE.js +401 -0
  32. package/dist/client-3AQCA4YE.js.map +1 -0
  33. package/dist/client-RBGZWS3Q.js +373 -0
  34. package/dist/client-RBGZWS3Q.js.map +1 -0
  35. package/dist/colony-J5KQIV6M.js +229 -0
  36. package/dist/colony-J5KQIV6M.js.map +1 -0
  37. package/dist/config-NZAFARS6.js +14 -0
  38. package/dist/config-NZAFARS6.js.map +1 -0
  39. package/dist/crypto-FHSQ72NU.js +14 -0
  40. package/dist/crypto-FHSQ72NU.js.map +1 -0
  41. package/dist/heartbeat-J4JLYH2B.js +358 -0
  42. package/dist/heartbeat-J4JLYH2B.js.map +1 -0
  43. package/dist/init-BG4Z4XQU.js +205 -0
  44. package/dist/init-BG4Z4XQU.js.map +1 -0
  45. package/dist/llm-RDNC5Y3G.js +16 -0
  46. package/dist/llm-RDNC5Y3G.js.map +1 -0
  47. package/dist/mcp-server.js +773 -0
  48. package/dist/mcp-server.js.map +1 -0
  49. package/dist/memory-7FBE26K3.js +26 -0
  50. package/dist/memory-7FBE26K3.js.map +1 -0
  51. package/dist/memory-O3AJIKBX.js +24 -0
  52. package/dist/memory-O3AJIKBX.js.map +1 -0
  53. package/dist/paths-5GFUUHCZ.js +13 -0
  54. package/dist/paths-5GFUUHCZ.js.map +1 -0
  55. package/dist/prompt-builder-WNMZ2QCN.js +17 -0
  56. package/dist/prompt-builder-WNMZ2QCN.js.map +1 -0
  57. package/dist/queue-ELK5ZX7J.js +14 -0
  58. package/dist/queue-ELK5ZX7J.js.map +1 -0
  59. package/dist/x-client-J4GE5A7P.js +12 -0
  60. package/dist/x-client-J4GE5A7P.js.map +1 -0
  61. package/package.json +57 -0
  62. package/templates/SKILL.md +335 -0
@@ -0,0 +1,373 @@
1
+ import {
2
+ rateLimiter
3
+ } from "./chunk-NFDZ47AG.js";
4
+ import {
5
+ identityExists,
6
+ loadIdentity
7
+ } from "./chunk-7CR4ID6P.js";
8
+ import {
9
+ logger
10
+ } from "./chunk-KELPENM3.js";
11
+ import "./chunk-YEKHNTQO.js";
12
+ import {
13
+ loadCredentials
14
+ } from "./chunk-ZJZKH7N7.js";
15
+ import {
16
+ logInteraction
17
+ } from "./chunk-EBO4F5NU.js";
18
+ import "./chunk-53YLFYJF.js";
19
+
20
+ // src/x-client/api/client.ts
21
+ var BASE_URL = "https://api.twitter.com/2";
22
+ var XApiClient = class {
23
+ bearerToken;
24
+ accessToken;
25
+ accessTokenSecret;
26
+ apiKey;
27
+ apiSecret;
28
+ userId = null;
29
+ constructor() {
30
+ const creds = loadCredentials();
31
+ if (creds.method !== "api") {
32
+ throw new Error("API client requires API credentials. Current method: browser");
33
+ }
34
+ this.bearerToken = creds.bearerToken;
35
+ this.accessToken = creds.accessToken;
36
+ this.accessTokenSecret = creds.accessTokenSecret;
37
+ this.apiKey = creds.apiKey;
38
+ this.apiSecret = creds.apiSecret;
39
+ }
40
+ async request(endpoint, options = {}) {
41
+ const { method = "GET", body, useOAuth = false } = options;
42
+ const url = `${BASE_URL}${endpoint}`;
43
+ const headers = {
44
+ "Content-Type": "application/json"
45
+ };
46
+ if (useOAuth) {
47
+ headers["Authorization"] = `Bearer ${this.bearerToken}`;
48
+ } else {
49
+ headers["Authorization"] = `Bearer ${this.bearerToken}`;
50
+ }
51
+ const response = await fetch(url, {
52
+ method,
53
+ headers,
54
+ body: body ? JSON.stringify(body) : void 0
55
+ });
56
+ if (!response.ok) {
57
+ const errorBody = await response.text();
58
+ throw new Error(`X API error ${response.status}: ${errorBody}`);
59
+ }
60
+ return response.json();
61
+ }
62
+ async getUserId() {
63
+ if (this.userId) return this.userId;
64
+ const handle = this.getHandle();
65
+ const result = await this.request(
66
+ `/users/by/username/${handle}`
67
+ );
68
+ this.userId = result.data.id;
69
+ return this.userId;
70
+ }
71
+ getHandle() {
72
+ if (identityExists()) {
73
+ return loadIdentity().handle;
74
+ }
75
+ const creds = loadCredentials();
76
+ if (creds.username) return creds.username;
77
+ throw new Error("No handle found. Create a Spore identity first.");
78
+ }
79
+ async postTweet(content) {
80
+ if (!rateLimiter.canPost()) {
81
+ return { success: false, error: "Monthly post limit reached" };
82
+ }
83
+ try {
84
+ const result = await this.request("/tweets", {
85
+ method: "POST",
86
+ body: { text: content },
87
+ useOAuth: true
88
+ });
89
+ rateLimiter.consume();
90
+ logInteraction({
91
+ id: `int-${Date.now()}`,
92
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
93
+ type: "post",
94
+ tweetId: result.data.id,
95
+ content,
96
+ creditsUsed: 1,
97
+ success: true
98
+ });
99
+ return { success: true, tweetId: result.data.id };
100
+ } catch (error) {
101
+ logger.error("Failed to post tweet", error);
102
+ return { success: false, error: error.message };
103
+ }
104
+ }
105
+ async replyToTweet(tweetId, content) {
106
+ if (!rateLimiter.canPost()) {
107
+ return { success: false, error: "Monthly post limit reached" };
108
+ }
109
+ try {
110
+ const result = await this.request("/tweets", {
111
+ method: "POST",
112
+ body: {
113
+ text: content,
114
+ reply: { in_reply_to_tweet_id: tweetId }
115
+ },
116
+ useOAuth: true
117
+ });
118
+ rateLimiter.consume();
119
+ logInteraction({
120
+ id: `int-${Date.now()}`,
121
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
122
+ type: "reply",
123
+ tweetId: result.data.id,
124
+ inReplyTo: tweetId,
125
+ content,
126
+ creditsUsed: 1,
127
+ success: true
128
+ });
129
+ return { success: true, tweetId: result.data.id };
130
+ } catch (error) {
131
+ logger.error("Failed to reply", error);
132
+ return { success: false, error: error.message };
133
+ }
134
+ }
135
+ async deleteTweet(tweetId) {
136
+ try {
137
+ await this.request(`/tweets/${tweetId}`, {
138
+ method: "DELETE",
139
+ useOAuth: true
140
+ });
141
+ return { success: true, tweetId };
142
+ } catch (error) {
143
+ return { success: false, error: error.message };
144
+ }
145
+ }
146
+ async likeTweet(tweetId) {
147
+ rateLimiter.requireBasicTier("Like");
148
+ try {
149
+ const userId = await this.getUserId();
150
+ await this.request(`/users/${userId}/likes`, {
151
+ method: "POST",
152
+ body: { tweet_id: tweetId },
153
+ useOAuth: true
154
+ });
155
+ logInteraction({
156
+ id: `int-${Date.now()}`,
157
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
158
+ type: "like",
159
+ tweetId,
160
+ creditsUsed: 0,
161
+ success: true
162
+ });
163
+ return { success: true, tweetId };
164
+ } catch (error) {
165
+ return { success: false, error: error.message };
166
+ }
167
+ }
168
+ async unlikeTweet(tweetId) {
169
+ rateLimiter.requireBasicTier("Unlike");
170
+ try {
171
+ const userId = await this.getUserId();
172
+ await this.request(`/users/${userId}/likes/${tweetId}`, {
173
+ method: "DELETE",
174
+ useOAuth: true
175
+ });
176
+ return { success: true, tweetId };
177
+ } catch (error) {
178
+ return { success: false, error: error.message };
179
+ }
180
+ }
181
+ async retweet(tweetId) {
182
+ rateLimiter.requireBasicTier("Retweet");
183
+ try {
184
+ const userId = await this.getUserId();
185
+ await this.request(`/users/${userId}/retweets`, {
186
+ method: "POST",
187
+ body: { tweet_id: tweetId },
188
+ useOAuth: true
189
+ });
190
+ logInteraction({
191
+ id: `int-${Date.now()}`,
192
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
193
+ type: "retweet",
194
+ tweetId,
195
+ creditsUsed: 0,
196
+ success: true
197
+ });
198
+ return { success: true, tweetId };
199
+ } catch (error) {
200
+ return { success: false, error: error.message };
201
+ }
202
+ }
203
+ async unretweet(tweetId) {
204
+ rateLimiter.requireBasicTier("Unretweet");
205
+ try {
206
+ const userId = await this.getUserId();
207
+ await this.request(`/users/${userId}/retweets/${tweetId}`, {
208
+ method: "DELETE",
209
+ useOAuth: true
210
+ });
211
+ return { success: true, tweetId };
212
+ } catch (error) {
213
+ return { success: false, error: error.message };
214
+ }
215
+ }
216
+ async followUser(userId) {
217
+ rateLimiter.requireBasicTier("Follow");
218
+ try {
219
+ const myId = await this.getUserId();
220
+ await this.request(`/users/${myId}/following`, {
221
+ method: "POST",
222
+ body: { target_user_id: userId },
223
+ useOAuth: true
224
+ });
225
+ logInteraction({
226
+ id: `int-${Date.now()}`,
227
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
228
+ type: "follow",
229
+ targetUserId: userId,
230
+ creditsUsed: 0,
231
+ success: true
232
+ });
233
+ return { success: true };
234
+ } catch (error) {
235
+ return { success: false, error: error.message };
236
+ }
237
+ }
238
+ async unfollowUser(userId) {
239
+ rateLimiter.requireBasicTier("Unfollow");
240
+ try {
241
+ const myId = await this.getUserId();
242
+ await this.request(`/users/${myId}/following/${userId}`, {
243
+ method: "DELETE",
244
+ useOAuth: true
245
+ });
246
+ return { success: true };
247
+ } catch (error) {
248
+ return { success: false, error: error.message };
249
+ }
250
+ }
251
+ async getTimeline(options) {
252
+ rateLimiter.requireBasicTier("Read timeline");
253
+ try {
254
+ const userId = await this.getUserId();
255
+ const params = new URLSearchParams({
256
+ max_results: String(options?.count ?? 20),
257
+ "tweet.fields": "created_at,public_metrics,in_reply_to_user_id",
258
+ expansions: "author_id",
259
+ "user.fields": "username"
260
+ });
261
+ if (options?.sinceId) params.set("since_id", options.sinceId);
262
+ const result = await this.request(
263
+ `/users/${userId}/timelines/reverse_chronological?${params}`
264
+ );
265
+ if (!result.data) return [];
266
+ const userMap = /* @__PURE__ */ new Map();
267
+ for (const user of result.includes?.users ?? []) {
268
+ userMap.set(user.id, user.username);
269
+ }
270
+ return result.data.map((tweet) => ({
271
+ id: tweet.id,
272
+ text: tweet.text,
273
+ authorId: tweet.author_id,
274
+ authorHandle: userMap.get(tweet.author_id) ?? "unknown",
275
+ createdAt: tweet.created_at,
276
+ likeCount: tweet.public_metrics?.like_count,
277
+ retweetCount: tweet.public_metrics?.retweet_count,
278
+ replyCount: tweet.public_metrics?.reply_count
279
+ }));
280
+ } catch (error) {
281
+ logger.error("Failed to read timeline", error);
282
+ return [];
283
+ }
284
+ }
285
+ async getMentions(options) {
286
+ rateLimiter.requireBasicTier("Read mentions");
287
+ try {
288
+ const userId = await this.getUserId();
289
+ const params = new URLSearchParams({
290
+ max_results: String(options?.count ?? 20),
291
+ "tweet.fields": "created_at,public_metrics",
292
+ expansions: "author_id",
293
+ "user.fields": "username"
294
+ });
295
+ if (options?.sinceId) params.set("since_id", options.sinceId);
296
+ const result = await this.request(
297
+ `/users/${userId}/mentions?${params}`
298
+ );
299
+ if (!result.data) return [];
300
+ const userMap = /* @__PURE__ */ new Map();
301
+ for (const user of result.includes?.users ?? []) {
302
+ userMap.set(user.id, user.username);
303
+ }
304
+ return result.data.map((tweet) => ({
305
+ id: tweet.id,
306
+ text: tweet.text,
307
+ authorId: tweet.author_id,
308
+ authorHandle: userMap.get(tweet.author_id) ?? "unknown",
309
+ createdAt: tweet.created_at,
310
+ likeCount: tweet.public_metrics?.like_count,
311
+ retweetCount: tweet.public_metrics?.retweet_count,
312
+ replyCount: tweet.public_metrics?.reply_count
313
+ }));
314
+ } catch (error) {
315
+ logger.error("Failed to read mentions", error);
316
+ return [];
317
+ }
318
+ }
319
+ async searchTweets(query, options) {
320
+ rateLimiter.requireBasicTier("Search tweets");
321
+ try {
322
+ const params = new URLSearchParams({
323
+ query,
324
+ max_results: String(options?.count ?? 20),
325
+ "tweet.fields": "created_at,public_metrics",
326
+ expansions: "author_id",
327
+ "user.fields": "username"
328
+ });
329
+ const result = await this.request(
330
+ `/tweets/search/recent?${params}`
331
+ );
332
+ if (!result.data) return [];
333
+ const userMap = /* @__PURE__ */ new Map();
334
+ for (const user of result.includes?.users ?? []) {
335
+ userMap.set(user.id, user.username);
336
+ }
337
+ return result.data.map((tweet) => ({
338
+ id: tweet.id,
339
+ text: tweet.text,
340
+ authorId: tweet.author_id,
341
+ authorHandle: userMap.get(tweet.author_id) ?? "unknown",
342
+ createdAt: tweet.created_at,
343
+ likeCount: tweet.public_metrics?.like_count,
344
+ retweetCount: tweet.public_metrics?.retweet_count,
345
+ replyCount: tweet.public_metrics?.reply_count
346
+ }));
347
+ } catch (error) {
348
+ logger.error("Failed to search tweets", error);
349
+ return [];
350
+ }
351
+ }
352
+ async getProfile(handle) {
353
+ rateLimiter.requireBasicTier("Get profile");
354
+ const result = await this.request(
355
+ `/users/by/username/${handle}?user.fields=description,public_metrics,verified,profile_image_url`
356
+ );
357
+ return {
358
+ id: result.data.id,
359
+ handle: result.data.username,
360
+ name: result.data.name,
361
+ bio: result.data.description,
362
+ followersCount: result.data.public_metrics.followers_count,
363
+ followingCount: result.data.public_metrics.following_count,
364
+ tweetCount: result.data.public_metrics.tweet_count,
365
+ verified: result.data.verified,
366
+ profileImageUrl: result.data.profile_image_url
367
+ };
368
+ }
369
+ };
370
+ export {
371
+ XApiClient
372
+ };
373
+ //# sourceMappingURL=client-RBGZWS3Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/x-client/api/client.ts"],"sourcesContent":["import { loadCredentials } from \"../../utils/crypto.js\";\nimport { loadConfig } from \"../../utils/config.js\";\nimport { loadIdentity, identityExists } from \"../../identity/index.js\";\nimport { logInteraction } from \"../../memory/index.js\";\nimport { rateLimiter } from \"../rate-limiter.js\";\nimport { logger } from \"../../utils/logger.js\";\nimport type {\n XClientInterface,\n Tweet,\n UserProfile,\n PostResult,\n TimelineOptions,\n SearchOptions,\n} from \"../types.js\";\n\nconst BASE_URL = \"https://api.twitter.com/2\";\n\nexport class XApiClient implements XClientInterface {\n private bearerToken: string;\n private accessToken: string;\n private accessTokenSecret: string;\n private apiKey: string;\n private apiSecret: string;\n private userId: string | null = null;\n\n constructor() {\n const creds = loadCredentials();\n if (creds.method !== \"api\") {\n throw new Error(\"API client requires API credentials. Current method: browser\");\n }\n this.bearerToken = creds.bearerToken!;\n this.accessToken = creds.accessToken!;\n this.accessTokenSecret = creds.accessTokenSecret!;\n this.apiKey = creds.apiKey!;\n this.apiSecret = creds.apiSecret!;\n }\n\n private async request(\n endpoint: string,\n options: {\n method?: string;\n body?: unknown;\n useOAuth?: boolean;\n } = {}\n ): Promise<unknown> {\n const { method = \"GET\", body, useOAuth = false } = options;\n const url = `${BASE_URL}${endpoint}`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n if (useOAuth) {\n // For user-context endpoints, use OAuth 1.0a\n // In production, this would use proper OAuth 1.0a signing\n // For now, we use Bearer token for app-only endpoints\n // and will implement OAuth 1.0a signing for user endpoints\n headers[\"Authorization\"] = `Bearer ${this.bearerToken}`;\n } else {\n headers[\"Authorization\"] = `Bearer ${this.bearerToken}`;\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`X API error ${response.status}: ${errorBody}`);\n }\n\n return response.json();\n }\n\n private async getUserId(): Promise<string> {\n if (this.userId) return this.userId;\n const handle = this.getHandle();\n const result = (await this.request(\n `/users/by/username/${handle}`\n )) as { data: { id: string } };\n this.userId = result.data.id;\n return this.userId;\n }\n\n private getHandle(): string {\n if (identityExists()) {\n return loadIdentity().handle;\n }\n // Fallback to credentials username before identity is created\n const creds = loadCredentials();\n if (creds.username) return creds.username;\n throw new Error(\"No handle found. Create a Spore identity first.\");\n }\n\n async postTweet(content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const result = (await this.request(\"/tweets\", {\n method: \"POST\",\n body: { text: content },\n useOAuth: true,\n })) as { data: { id: string } };\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n tweetId: result.data.id,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: result.data.id };\n } catch (error) {\n logger.error(\"Failed to post tweet\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async replyToTweet(tweetId: string, content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const result = (await this.request(\"/tweets\", {\n method: \"POST\",\n body: {\n text: content,\n reply: { in_reply_to_tweet_id: tweetId },\n },\n useOAuth: true,\n })) as { data: { id: string } };\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n tweetId: result.data.id,\n inReplyTo: tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: result.data.id };\n } catch (error) {\n logger.error(\"Failed to reply\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async deleteTweet(tweetId: string): Promise<PostResult> {\n try {\n await this.request(`/tweets/${tweetId}`, {\n method: \"DELETE\",\n useOAuth: true,\n });\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async likeTweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Like\");\n try {\n const userId = await this.getUserId();\n await this.request(`/users/${userId}/likes`, {\n method: \"POST\",\n body: { tweet_id: tweetId },\n useOAuth: true,\n });\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"like\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unlikeTweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unlike\");\n try {\n const userId = await this.getUserId();\n await this.request(`/users/${userId}/likes/${tweetId}`, {\n method: \"DELETE\",\n useOAuth: true,\n });\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async retweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Retweet\");\n try {\n const userId = await this.getUserId();\n await this.request(`/users/${userId}/retweets`, {\n method: \"POST\",\n body: { tweet_id: tweetId },\n useOAuth: true,\n });\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"retweet\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unretweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unretweet\");\n try {\n const userId = await this.getUserId();\n await this.request(`/users/${userId}/retweets/${tweetId}`, {\n method: \"DELETE\",\n useOAuth: true,\n });\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async followUser(userId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Follow\");\n try {\n const myId = await this.getUserId();\n await this.request(`/users/${myId}/following`, {\n method: \"POST\",\n body: { target_user_id: userId },\n useOAuth: true,\n });\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"follow\",\n targetUserId: userId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unfollowUser(userId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unfollow\");\n try {\n const myId = await this.getUserId();\n await this.request(`/users/${myId}/following/${userId}`, {\n method: \"DELETE\",\n useOAuth: true,\n });\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async getTimeline(options?: TimelineOptions): Promise<Tweet[]> {\n rateLimiter.requireBasicTier(\"Read timeline\");\n try {\n const userId = await this.getUserId();\n const params = new URLSearchParams({\n max_results: String(options?.count ?? 20),\n \"tweet.fields\": \"created_at,public_metrics,in_reply_to_user_id\",\n expansions: \"author_id\",\n \"user.fields\": \"username\",\n });\n if (options?.sinceId) params.set(\"since_id\", options.sinceId);\n\n const result = (await this.request(\n `/users/${userId}/timelines/reverse_chronological?${params}`\n )) as {\n data?: Array<{\n id: string;\n text: string;\n author_id: string;\n created_at: string;\n public_metrics?: {\n like_count: number;\n retweet_count: number;\n reply_count: number;\n };\n in_reply_to_user_id?: string;\n }>;\n includes?: {\n users?: Array<{ id: string; username: string }>;\n };\n };\n\n if (!result.data) return [];\n\n const userMap = new Map<string, string>();\n for (const user of result.includes?.users ?? []) {\n userMap.set(user.id, user.username);\n }\n\n return result.data.map((tweet) => ({\n id: tweet.id,\n text: tweet.text,\n authorId: tweet.author_id,\n authorHandle: userMap.get(tweet.author_id) ?? \"unknown\",\n createdAt: tweet.created_at,\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n }));\n } catch (error) {\n logger.error(\"Failed to read timeline\", error);\n return [];\n }\n }\n\n async getMentions(options?: TimelineOptions): Promise<Tweet[]> {\n rateLimiter.requireBasicTier(\"Read mentions\");\n try {\n const userId = await this.getUserId();\n const params = new URLSearchParams({\n max_results: String(options?.count ?? 20),\n \"tweet.fields\": \"created_at,public_metrics\",\n expansions: \"author_id\",\n \"user.fields\": \"username\",\n });\n if (options?.sinceId) params.set(\"since_id\", options.sinceId);\n\n const result = (await this.request(\n `/users/${userId}/mentions?${params}`\n )) as {\n data?: Array<{\n id: string;\n text: string;\n author_id: string;\n created_at: string;\n public_metrics?: {\n like_count: number;\n retweet_count: number;\n reply_count: number;\n };\n }>;\n includes?: {\n users?: Array<{ id: string; username: string }>;\n };\n };\n\n if (!result.data) return [];\n\n const userMap = new Map<string, string>();\n for (const user of result.includes?.users ?? []) {\n userMap.set(user.id, user.username);\n }\n\n return result.data.map((tweet) => ({\n id: tweet.id,\n text: tweet.text,\n authorId: tweet.author_id,\n authorHandle: userMap.get(tweet.author_id) ?? \"unknown\",\n createdAt: tweet.created_at,\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n }));\n } catch (error) {\n logger.error(\"Failed to read mentions\", error);\n return [];\n }\n }\n\n async searchTweets(query: string, options?: SearchOptions): Promise<Tweet[]> {\n rateLimiter.requireBasicTier(\"Search tweets\");\n try {\n const params = new URLSearchParams({\n query,\n max_results: String(options?.count ?? 20),\n \"tweet.fields\": \"created_at,public_metrics\",\n expansions: \"author_id\",\n \"user.fields\": \"username\",\n });\n\n const result = (await this.request(\n `/tweets/search/recent?${params}`\n )) as {\n data?: Array<{\n id: string;\n text: string;\n author_id: string;\n created_at: string;\n public_metrics?: {\n like_count: number;\n retweet_count: number;\n reply_count: number;\n };\n }>;\n includes?: {\n users?: Array<{ id: string; username: string }>;\n };\n };\n\n if (!result.data) return [];\n\n const userMap = new Map<string, string>();\n for (const user of result.includes?.users ?? []) {\n userMap.set(user.id, user.username);\n }\n\n return result.data.map((tweet) => ({\n id: tweet.id,\n text: tweet.text,\n authorId: tweet.author_id,\n authorHandle: userMap.get(tweet.author_id) ?? \"unknown\",\n createdAt: tweet.created_at,\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n }));\n } catch (error) {\n logger.error(\"Failed to search tweets\", error);\n return [];\n }\n }\n\n async getProfile(handle: string): Promise<UserProfile> {\n rateLimiter.requireBasicTier(\"Get profile\");\n const result = (await this.request(\n `/users/by/username/${handle}?user.fields=description,public_metrics,verified,profile_image_url`\n )) as {\n data: {\n id: string;\n username: string;\n name: string;\n description: string;\n public_metrics: {\n followers_count: number;\n following_count: number;\n tweet_count: number;\n };\n verified: boolean;\n profile_image_url?: string;\n };\n };\n\n return {\n id: result.data.id,\n handle: result.data.username,\n name: result.data.name,\n bio: result.data.description,\n followersCount: result.data.public_metrics.followers_count,\n followingCount: result.data.public_metrics.following_count,\n tweetCount: result.data.public_metrics.tweet_count,\n verified: result.data.verified,\n profileImageUrl: result.data.profile_image_url,\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,229 @@
1
+ import {
2
+ getXClient
3
+ } from "./chunk-3JEDGXEM.js";
4
+ import {
5
+ addColonyEntry,
6
+ addOrUpdatePlan,
7
+ addPlanParticipant,
8
+ getActivePlans,
9
+ getTodayEntries,
10
+ loadColonyMemory,
11
+ renderColonyBriefing,
12
+ saveColonyMemory
13
+ } from "./chunk-AHXZIGQE.js";
14
+ import {
15
+ loadIdentity
16
+ } from "./chunk-7CR4ID6P.js";
17
+ import {
18
+ logger
19
+ } from "./chunk-KELPENM3.js";
20
+ import "./chunk-YEKHNTQO.js";
21
+ import "./chunk-53YLFYJF.js";
22
+
23
+ // src/colony/index.ts
24
+ var COLONY_TAG = "#SporaColony";
25
+ async function colonyCheckin(message) {
26
+ const identity = loadIdentity();
27
+ if (!identity.colony.joined) {
28
+ return {
29
+ posted: false,
30
+ message: "Not a Colony member. Join during Spore creation.",
31
+ colonyMemory: loadColonyMemory(),
32
+ communityFeed: [],
33
+ discoveredSpores: []
34
+ };
35
+ }
36
+ const client = await getXClient();
37
+ const result = {
38
+ posted: false,
39
+ colonyMemory: loadColonyMemory(),
40
+ communityFeed: [],
41
+ discoveredSpores: []
42
+ };
43
+ try {
44
+ const feed = await client.searchTweets(COLONY_TAG, { count: 30 });
45
+ result.communityFeed = feed;
46
+ const memory = loadColonyMemory();
47
+ const sporeHandles = /* @__PURE__ */ new Set();
48
+ for (const tweet of feed) {
49
+ if (tweet.authorHandle === identity.handle) continue;
50
+ sporeHandles.add(tweet.authorHandle);
51
+ const text = tweet.text;
52
+ if (text.includes("[PLAN]")) {
53
+ const planContent = text.replace(COLONY_TAG, "").replace("[PLAN]", "").trim();
54
+ const existingPlan = memory.activePlans.find(
55
+ (p) => p.proposedBy === tweet.authorHandle && p.description === planContent
56
+ );
57
+ if (!existingPlan) {
58
+ memory.activePlans.push({
59
+ id: tweet.id,
60
+ proposedBy: tweet.authorHandle,
61
+ description: planContent,
62
+ proposedAt: tweet.createdAt,
63
+ participants: [tweet.authorHandle],
64
+ status: "active",
65
+ reports: []
66
+ });
67
+ }
68
+ } else if (text.includes("[JOIN:")) {
69
+ const planId = text.match(/\[JOIN:([^\]]+)\]/)?.[1];
70
+ if (planId) {
71
+ const plan = memory.activePlans.find((p) => p.id === planId);
72
+ if (plan && !plan.participants.includes(tweet.authorHandle)) {
73
+ plan.participants.push(tweet.authorHandle);
74
+ }
75
+ }
76
+ } else if (text.includes("[STATUS]")) {
77
+ const statusContent = text.replace(COLONY_TAG, "").replace("[STATUS]", "").trim();
78
+ const exists = memory.entries.some(
79
+ (e) => e.handle === tweet.authorHandle && e.content === statusContent
80
+ );
81
+ if (!exists) {
82
+ memory.entries.push({
83
+ handle: tweet.authorHandle,
84
+ content: statusContent,
85
+ timestamp: tweet.createdAt,
86
+ type: "status"
87
+ });
88
+ }
89
+ } else {
90
+ const chatterContent = text.replace(COLONY_TAG, "").trim();
91
+ const exists = memory.entries.some(
92
+ (e) => e.handle === tweet.authorHandle && e.content === chatterContent
93
+ );
94
+ if (!exists) {
95
+ memory.entries.push({
96
+ handle: tweet.authorHandle,
97
+ content: chatterContent,
98
+ timestamp: tweet.createdAt,
99
+ type: "chatter"
100
+ });
101
+ }
102
+ }
103
+ }
104
+ const cutoff = Date.now() - 48 * 60 * 60 * 1e3;
105
+ for (const plan of memory.activePlans) {
106
+ if (plan.status === "active" && new Date(plan.proposedAt).getTime() < cutoff) {
107
+ plan.status = "expired";
108
+ }
109
+ }
110
+ memory.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
111
+ saveColonyMemory(memory);
112
+ result.colonyMemory = memory;
113
+ result.discoveredSpores = Array.from(sporeHandles);
114
+ logger.info(
115
+ `Colony sync: ${feed.length} posts, ${result.discoveredSpores.length} Spores, ${memory.activePlans.filter((p) => p.status === "active").length} active plans`
116
+ );
117
+ } catch (error) {
118
+ logger.warn("Failed to read Colony feed", error);
119
+ }
120
+ if (message) {
121
+ try {
122
+ const colonyPost = `${COLONY_TAG} ${message}`;
123
+ const postResult = await client.postTweet(colonyPost.slice(0, 280));
124
+ result.posted = postResult.success;
125
+ result.message = postResult.success ? "Posted to Colony successfully" : `Failed: ${postResult.error}`;
126
+ } catch (error) {
127
+ result.message = `Failed to post: ${error.message}`;
128
+ }
129
+ }
130
+ return result;
131
+ }
132
+ async function proposePlan(description) {
133
+ const identity = loadIdentity();
134
+ if (!identity.colony.joined) {
135
+ return { success: false, error: "Not a Colony member." };
136
+ }
137
+ const client = await getXClient();
138
+ try {
139
+ const postContent = `${COLONY_TAG} [PLAN] ${description}`.slice(0, 280);
140
+ const result = await client.postTweet(postContent);
141
+ if (result.success && result.tweetId) {
142
+ addOrUpdatePlan({
143
+ id: result.tweetId,
144
+ proposedBy: identity.handle,
145
+ description,
146
+ proposedAt: (/* @__PURE__ */ new Date()).toISOString(),
147
+ participants: [identity.handle],
148
+ status: "active",
149
+ reports: []
150
+ });
151
+ return { success: true, planId: result.tweetId };
152
+ }
153
+ return { success: false, error: result.error };
154
+ } catch (error) {
155
+ return { success: false, error: error.message };
156
+ }
157
+ }
158
+ async function joinPlan(planId) {
159
+ const identity = loadIdentity();
160
+ if (!identity.colony.joined) {
161
+ return { success: false, error: "Not a Colony member." };
162
+ }
163
+ const memory = loadColonyMemory();
164
+ const plan = memory.activePlans.find((p) => p.id === planId);
165
+ if (!plan) {
166
+ return { success: false, error: `Plan ${planId} not found.` };
167
+ }
168
+ if (plan.participants.includes(identity.handle)) {
169
+ return { success: false, error: "Already joined this plan." };
170
+ }
171
+ const client = await getXClient();
172
+ try {
173
+ const postContent = `${COLONY_TAG} [JOIN:${planId}] I'm in! Joining: ${plan.description}`.slice(0, 280);
174
+ const result = await client.postTweet(postContent);
175
+ if (result.success) {
176
+ addPlanParticipant(planId, identity.handle);
177
+ return { success: true };
178
+ }
179
+ return { success: false, error: result.error };
180
+ } catch (error) {
181
+ return { success: false, error: error.message };
182
+ }
183
+ }
184
+ async function postStatus(status) {
185
+ const identity = loadIdentity();
186
+ if (!identity.colony.joined) {
187
+ return { success: false, error: "Not a Colony member." };
188
+ }
189
+ const client = await getXClient();
190
+ try {
191
+ const postContent = `${COLONY_TAG} [STATUS] ${status}`.slice(0, 280);
192
+ const result = await client.postTweet(postContent);
193
+ if (result.success) {
194
+ addColonyEntry({
195
+ handle: identity.handle,
196
+ content: status,
197
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
198
+ type: "status"
199
+ });
200
+ return { success: true };
201
+ }
202
+ return { success: false, error: result.error };
203
+ } catch (error) {
204
+ return { success: false, error: error.message };
205
+ }
206
+ }
207
+ function getColonyMemory() {
208
+ return loadColonyMemory();
209
+ }
210
+ function getActivePlans2() {
211
+ return getActivePlans();
212
+ }
213
+ function getTodaysActivity() {
214
+ return getTodayEntries();
215
+ }
216
+ function getColonyBriefing() {
217
+ return renderColonyBriefing();
218
+ }
219
+ export {
220
+ colonyCheckin,
221
+ getActivePlans2 as getActivePlans,
222
+ getColonyBriefing,
223
+ getColonyMemory,
224
+ getTodaysActivity,
225
+ joinPlan,
226
+ postStatus,
227
+ proposePlan
228
+ };
229
+ //# sourceMappingURL=colony-J5KQIV6M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/colony/index.ts"],"sourcesContent":["import { loadIdentity } from \"../identity/index.js\";\nimport { getXClient } from \"../x-client/index.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n loadColonyMemory,\n saveColonyMemory,\n addColonyEntry,\n addOrUpdatePlan,\n addPlanParticipant,\n getRecentEntries,\n getTodayEntries,\n getActivePlans as getActivePlansFromMemory,\n renderColonyBriefing,\n type ColonyMemory,\n type ColonyPlan,\n type ColonyEntry,\n} from \"./memory.js\";\nimport type { Tweet } from \"../x-client/types.js\";\n\n// The Colony is an X Community:\n// https://x.com/i/communities/2018501592421699779\n//\n// Colony memory is a shared, continuously growing state that all Spores read and write.\n// It lives both in the community (as posts) and locally (as a structured cache).\n// When a Spore checks in, it:\n// 1. Reads the community feed to sync colony memory\n// 2. Posts its own status/plans to the community\n// 3. Updates local colony memory cache\n//\n// Memory never resets — it accumulates over time like a real shared consciousness.\n\nconst COLONY_TAG = \"#SporaColony\";\n\n// ========== CHECK-IN ==========\n\nexport interface ColonyCheckinResult {\n posted: boolean;\n message?: string;\n colonyMemory: ColonyMemory;\n communityFeed: Tweet[];\n discoveredSpores: string[];\n}\n\nexport async function colonyCheckin(message?: string): Promise<ColonyCheckinResult> {\n const identity = loadIdentity();\n\n if (!identity.colony.joined) {\n return {\n posted: false,\n message: \"Not a Colony member. Join during Spore creation.\",\n colonyMemory: loadColonyMemory(),\n communityFeed: [],\n discoveredSpores: [],\n };\n }\n\n const client = await getXClient();\n const result: ColonyCheckinResult = {\n posted: false,\n colonyMemory: loadColonyMemory(),\n communityFeed: [],\n discoveredSpores: [],\n };\n\n // Read Colony community feed\n try {\n const feed = await client.searchTweets(COLONY_TAG, { count: 30 });\n result.communityFeed = feed;\n\n const memory = loadColonyMemory();\n const sporeHandles = new Set<string>();\n\n for (const tweet of feed) {\n if (tweet.authorHandle === identity.handle) continue;\n sporeHandles.add(tweet.authorHandle);\n\n const text = tweet.text;\n\n if (text.includes(\"[PLAN]\")) {\n const planContent = text.replace(COLONY_TAG, \"\").replace(\"[PLAN]\", \"\").trim();\n const existingPlan = memory.activePlans.find(\n (p) => p.proposedBy === tweet.authorHandle && p.description === planContent\n );\n if (!existingPlan) {\n memory.activePlans.push({\n id: tweet.id,\n proposedBy: tweet.authorHandle,\n description: planContent,\n proposedAt: tweet.createdAt,\n participants: [tweet.authorHandle],\n status: \"active\",\n reports: [],\n });\n }\n } else if (text.includes(\"[JOIN:\")) {\n const planId = text.match(/\\[JOIN:([^\\]]+)\\]/)?.[1];\n if (planId) {\n const plan = memory.activePlans.find((p) => p.id === planId);\n if (plan && !plan.participants.includes(tweet.authorHandle)) {\n plan.participants.push(tweet.authorHandle);\n }\n }\n } else if (text.includes(\"[STATUS]\")) {\n const statusContent = text.replace(COLONY_TAG, \"\").replace(\"[STATUS]\", \"\").trim();\n const exists = memory.entries.some(\n (e) => e.handle === tweet.authorHandle && e.content === statusContent\n );\n if (!exists) {\n memory.entries.push({\n handle: tweet.authorHandle,\n content: statusContent,\n timestamp: tweet.createdAt,\n type: \"status\",\n });\n }\n } else {\n // General colony chatter\n const chatterContent = text.replace(COLONY_TAG, \"\").trim();\n const exists = memory.entries.some(\n (e) => e.handle === tweet.authorHandle && e.content === chatterContent\n );\n if (!exists) {\n memory.entries.push({\n handle: tweet.authorHandle,\n content: chatterContent,\n timestamp: tweet.createdAt,\n type: \"chatter\",\n });\n }\n }\n }\n\n // Expire old plans (older than 48 hours) but keep them in memory as expired\n const cutoff = Date.now() - 48 * 60 * 60 * 1000;\n for (const plan of memory.activePlans) {\n if (plan.status === \"active\" && new Date(plan.proposedAt).getTime() < cutoff) {\n plan.status = \"expired\";\n }\n }\n\n memory.lastSynced = new Date().toISOString();\n saveColonyMemory(memory);\n\n result.colonyMemory = memory;\n result.discoveredSpores = Array.from(sporeHandles);\n\n logger.info(\n `Colony sync: ${feed.length} posts, ${result.discoveredSpores.length} Spores, ${memory.activePlans.filter((p) => p.status === \"active\").length} active plans`\n );\n } catch (error) {\n logger.warn(\"Failed to read Colony feed\", error);\n }\n\n // Post to Colony if message provided\n if (message) {\n try {\n const colonyPost = `${COLONY_TAG} ${message}`;\n const postResult = await client.postTweet(colonyPost.slice(0, 280));\n result.posted = postResult.success;\n result.message = postResult.success\n ? \"Posted to Colony successfully\"\n : `Failed: ${postResult.error}`;\n } catch (error) {\n result.message = `Failed to post: ${(error as Error).message}`;\n }\n }\n\n return result;\n}\n\n// ========== PLANS ==========\n\nexport async function proposePlan(description: string): Promise<{ success: boolean; planId?: string; error?: string }> {\n const identity = loadIdentity();\n if (!identity.colony.joined) {\n return { success: false, error: \"Not a Colony member.\" };\n }\n\n const client = await getXClient();\n\n try {\n const postContent = `${COLONY_TAG} [PLAN] ${description}`.slice(0, 280);\n const result = await client.postTweet(postContent);\n\n if (result.success && result.tweetId) {\n addOrUpdatePlan({\n id: result.tweetId,\n proposedBy: identity.handle,\n description,\n proposedAt: new Date().toISOString(),\n participants: [identity.handle],\n status: \"active\",\n reports: [],\n });\n\n return { success: true, planId: result.tweetId };\n }\n\n return { success: false, error: result.error };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n}\n\nexport async function joinPlan(planId: string): Promise<{ success: boolean; error?: string }> {\n const identity = loadIdentity();\n if (!identity.colony.joined) {\n return { success: false, error: \"Not a Colony member.\" };\n }\n\n const memory = loadColonyMemory();\n const plan = memory.activePlans.find((p) => p.id === planId);\n if (!plan) {\n return { success: false, error: `Plan ${planId} not found.` };\n }\n\n if (plan.participants.includes(identity.handle)) {\n return { success: false, error: \"Already joined this plan.\" };\n }\n\n const client = await getXClient();\n\n try {\n const postContent = `${COLONY_TAG} [JOIN:${planId}] I'm in! Joining: ${plan.description}`.slice(0, 280);\n const result = await client.postTweet(postContent);\n\n if (result.success) {\n addPlanParticipant(planId, identity.handle);\n return { success: true };\n }\n\n return { success: false, error: result.error };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n}\n\nexport async function postStatus(status: string): Promise<{ success: boolean; error?: string }> {\n const identity = loadIdentity();\n if (!identity.colony.joined) {\n return { success: false, error: \"Not a Colony member.\" };\n }\n\n const client = await getXClient();\n\n try {\n const postContent = `${COLONY_TAG} [STATUS] ${status}`.slice(0, 280);\n const result = await client.postTweet(postContent);\n\n if (result.success) {\n addColonyEntry({\n handle: identity.handle,\n content: status,\n timestamp: new Date().toISOString(),\n type: \"status\",\n });\n return { success: true };\n }\n\n return { success: false, error: result.error };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n}\n\n// ========== READ ==========\n\nexport function getColonyMemory(): ColonyMemory {\n return loadColonyMemory();\n}\n\nexport function getActivePlans(): ColonyPlan[] {\n return getActivePlansFromMemory();\n}\n\nexport function getTodaysActivity(): ColonyEntry[] {\n return getTodayEntries();\n}\n\nexport function getColonyBriefing(): string {\n return renderColonyBriefing();\n}\n"],"mappings":"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","names":["getActivePlans"]}