spora 0.3.2 → 0.3.4

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 (102) hide show
  1. package/dist/{account-creator-SETL5CGT.js → account-creator-AABUY2JU.js} +5 -5
  2. package/dist/{chunk-Q7YS3AIK.js → chunk-6KCIAMHL.js} +5 -6
  3. package/dist/chunk-6KCIAMHL.js.map +1 -0
  4. package/dist/{chunk-HERI4RPY.js → chunk-A6R5ZGK6.js} +2 -2
  5. package/dist/{chunk-QHFM2YW6.js → chunk-B6VI6L4D.js} +9 -44
  6. package/dist/chunk-B6VI6L4D.js.map +1 -0
  7. package/dist/{chunk-GJFBWIW3.js → chunk-FTFTB5Y5.js} +2 -2
  8. package/dist/{chunk-SXMDYUK3.js → chunk-GMSK775L.js} +29 -6
  9. package/dist/chunk-GMSK775L.js.map +1 -0
  10. package/dist/{chunk-POEDIDM6.js → chunk-H62HH5ZI.js} +2 -2
  11. package/dist/{chunk-NLWU5432.js → chunk-KQ37VL54.js} +5 -5
  12. package/dist/{chunk-JWMADEQO.js → chunk-ML4EMUZC.js} +3 -3
  13. package/dist/chunk-N5TBL3NY.js +86 -0
  14. package/dist/chunk-N5TBL3NY.js.map +1 -0
  15. package/dist/chunk-PNZ3XK2N.js +358 -0
  16. package/dist/chunk-PNZ3XK2N.js.map +1 -0
  17. package/dist/{chunk-J7J557HV.js → chunk-UCCAF2ZO.js} +2 -2
  18. package/dist/{chunk-RNVEWVDN.js → chunk-V6ZNR2SI.js} +2 -26
  19. package/dist/chunk-V6ZNR2SI.js.map +1 -0
  20. package/dist/cli.js +46 -46
  21. package/dist/cli.js.map +1 -1
  22. package/dist/{client-NVI3ZD4G.js → client-BGLXHLID.js} +10 -20
  23. package/dist/client-BGLXHLID.js.map +1 -0
  24. package/dist/client-TWYR2IIQ.js +373 -0
  25. package/dist/client-TWYR2IIQ.js.map +1 -0
  26. package/dist/{colony-J4EZQI37.js → colony-JVBCMZTK.js} +7 -7
  27. package/dist/{config-QRBOL4NX.js → config-5EPXA325.js} +3 -3
  28. package/dist/{crypto-ZVWJLD2J.js → crypto-HS4CGS4A.js} +3 -3
  29. package/dist/heartbeat-B2CZKMUF.js +901 -0
  30. package/dist/heartbeat-B2CZKMUF.js.map +1 -0
  31. package/dist/{identity-LN2R4KJU.js → identity-6CXRCXJQ.js} +3 -3
  32. package/dist/{init-ANGLSI2L.js → init-KXNLBFMG.js} +21 -21
  33. package/dist/init-KXNLBFMG.js.map +1 -0
  34. package/dist/llm-CUCO24K7.js +16 -0
  35. package/dist/mcp-server.js +24 -24
  36. package/dist/{memory-JMXU3UXR.js → memory-2OI3JXY2.js} +3 -3
  37. package/dist/{memory-J6AYZ5Y2.js → memory-LPU2I6NI.js} +3 -5
  38. package/dist/{paths-KXOWF2B2.js → paths-Q4TJEOMQ.js} +2 -2
  39. package/dist/prompt-builder-VHGZFBL6.js +19 -0
  40. package/dist/queue-LNBQWMFX.js +14 -0
  41. package/dist/web-chat/chat.html +38 -958
  42. package/dist/web-chat-DHHJTGFZ.js +253 -0
  43. package/dist/web-chat-DHHJTGFZ.js.map +1 -0
  44. package/dist/x-client-W5IB7XOM.js +12 -0
  45. package/package.json +2 -1
  46. package/dist/chunk-DFSYD45Q.js +0 -665
  47. package/dist/chunk-DFSYD45Q.js.map +0 -1
  48. package/dist/chunk-FCAK5FYQ.js +0 -127
  49. package/dist/chunk-FCAK5FYQ.js.map +0 -1
  50. package/dist/chunk-LRKBNKMQ.js +0 -79
  51. package/dist/chunk-LRKBNKMQ.js.map +0 -1
  52. package/dist/chunk-Q7YS3AIK.js.map +0 -1
  53. package/dist/chunk-QHFM2YW6.js.map +0 -1
  54. package/dist/chunk-R7PAD4OL.js +0 -44
  55. package/dist/chunk-R7PAD4OL.js.map +0 -1
  56. package/dist/chunk-RNVEWVDN.js.map +0 -1
  57. package/dist/chunk-SUFTVQME.js +0 -82
  58. package/dist/chunk-SUFTVQME.js.map +0 -1
  59. package/dist/chunk-SXMDYUK3.js.map +0 -1
  60. package/dist/chunk-YZ7RWJ6Z.js +0 -262
  61. package/dist/chunk-YZ7RWJ6Z.js.map +0 -1
  62. package/dist/client-23THPNVL.js +0 -382
  63. package/dist/client-23THPNVL.js.map +0 -1
  64. package/dist/client-NVI3ZD4G.js.map +0 -1
  65. package/dist/decision-engine-WBD36PZI.js +0 -19
  66. package/dist/goals-IM4AEHS4.js +0 -12
  67. package/dist/heartbeat-35HVB5PB.js +0 -317
  68. package/dist/heartbeat-35HVB5PB.js.map +0 -1
  69. package/dist/image-search-SZVMGWLN.js +0 -45
  70. package/dist/image-search-SZVMGWLN.js.map +0 -1
  71. package/dist/init-ANGLSI2L.js.map +0 -1
  72. package/dist/llm-MHZG2VHU.js +0 -16
  73. package/dist/performance-7G6R6ELJ.js +0 -18
  74. package/dist/prompt-builder-NSU4IFPB.js +0 -28
  75. package/dist/prompt-builder-NSU4IFPB.js.map +0 -1
  76. package/dist/queue-MLRTMJRE.js +0 -14
  77. package/dist/queue-MLRTMJRE.js.map +0 -1
  78. package/dist/strategy-TOVFBIZQ.js +0 -12
  79. package/dist/strategy-TOVFBIZQ.js.map +0 -1
  80. package/dist/web-chat/logo.png +0 -0
  81. package/dist/web-chat-N2AYUWT7.js +0 -802
  82. package/dist/web-chat-N2AYUWT7.js.map +0 -1
  83. package/dist/x-client-HUXCQOAW.js +0 -12
  84. package/dist/x-client-HUXCQOAW.js.map +0 -1
  85. /package/dist/{account-creator-SETL5CGT.js.map → account-creator-AABUY2JU.js.map} +0 -0
  86. /package/dist/{chunk-HERI4RPY.js.map → chunk-A6R5ZGK6.js.map} +0 -0
  87. /package/dist/{chunk-GJFBWIW3.js.map → chunk-FTFTB5Y5.js.map} +0 -0
  88. /package/dist/{chunk-POEDIDM6.js.map → chunk-H62HH5ZI.js.map} +0 -0
  89. /package/dist/{chunk-NLWU5432.js.map → chunk-KQ37VL54.js.map} +0 -0
  90. /package/dist/{chunk-JWMADEQO.js.map → chunk-ML4EMUZC.js.map} +0 -0
  91. /package/dist/{chunk-J7J557HV.js.map → chunk-UCCAF2ZO.js.map} +0 -0
  92. /package/dist/{colony-J4EZQI37.js.map → colony-JVBCMZTK.js.map} +0 -0
  93. /package/dist/{config-QRBOL4NX.js.map → config-5EPXA325.js.map} +0 -0
  94. /package/dist/{crypto-ZVWJLD2J.js.map → crypto-HS4CGS4A.js.map} +0 -0
  95. /package/dist/{decision-engine-WBD36PZI.js.map → identity-6CXRCXJQ.js.map} +0 -0
  96. /package/dist/{goals-IM4AEHS4.js.map → llm-CUCO24K7.js.map} +0 -0
  97. /package/dist/{identity-LN2R4KJU.js.map → memory-2OI3JXY2.js.map} +0 -0
  98. /package/dist/{llm-MHZG2VHU.js.map → memory-LPU2I6NI.js.map} +0 -0
  99. /package/dist/{memory-J6AYZ5Y2.js.map → paths-Q4TJEOMQ.js.map} +0 -0
  100. /package/dist/{memory-JMXU3UXR.js.map → prompt-builder-VHGZFBL6.js.map} +0 -0
  101. /package/dist/{paths-KXOWF2B2.js.map → queue-LNBQWMFX.js.map} +0 -0
  102. /package/dist/{performance-7G6R6ELJ.js.map → x-client-W5IB7XOM.js.map} +0 -0
@@ -1,262 +0,0 @@
1
- import {
2
- getXClient
3
- } from "./chunk-NLWU5432.js";
4
- import {
5
- trackPost
6
- } from "./chunk-FCAK5FYQ.js";
7
- import {
8
- addToQueue
9
- } from "./chunk-QHFM2YW6.js";
10
- import {
11
- loadIdentity,
12
- saveIdentity
13
- } from "./chunk-GJFBWIW3.js";
14
- import {
15
- logger
16
- } from "./chunk-J7J557HV.js";
17
- import {
18
- addLearning,
19
- updateRelationship
20
- } from "./chunk-RNVEWVDN.js";
21
-
22
- // src/runtime/decision-engine.ts
23
- function parseActions(llmResponse) {
24
- const jsonMatch = llmResponse.match(/\[[\s\S]*?\]/);
25
- if (!jsonMatch) {
26
- const objMatch = llmResponse.match(/\{[\s\S]*?\}/);
27
- if (objMatch) {
28
- try {
29
- return [JSON.parse(objMatch[0])];
30
- } catch {
31
- logger.warn("Could not parse LLM response as action object");
32
- return [];
33
- }
34
- }
35
- logger.warn("No JSON found in LLM response");
36
- return [];
37
- }
38
- try {
39
- const actions = JSON.parse(jsonMatch[0]);
40
- return Array.isArray(actions) ? actions : [actions];
41
- } catch {
42
- logger.warn("Failed to parse actions JSON from LLM response");
43
- return [];
44
- }
45
- }
46
- function sanitizeTweetId(id) {
47
- return id.replace(/^tweet:/i, "").trim();
48
- }
49
- async function executeAction(action) {
50
- const { action: type } = action;
51
- if (action.tweetId) {
52
- action.tweetId = sanitizeTweetId(action.tweetId);
53
- }
54
- try {
55
- switch (type) {
56
- case "post": {
57
- if (!action.content) return { action: type, success: false, error: "No content provided" };
58
- if (action.content.length > 280) {
59
- return { action: type, success: false, error: `Tweet too long: ${action.content.length} chars (max 280)` };
60
- }
61
- const client = await getXClient();
62
- if (action.imageQuery) {
63
- try {
64
- const { searchImage, downloadImage } = await import("./image-search-SZVMGWLN.js");
65
- const imageUrl = await searchImage(action.imageQuery);
66
- if (imageUrl) {
67
- const imageBuffer = await downloadImage(imageUrl);
68
- const result2 = await client.postTweetWithMedia(action.content, imageBuffer);
69
- if (result2.success) {
70
- logger.info(`Posted with image: "${action.content.slice(0, 50)}..." (query: "${action.imageQuery}")`);
71
- }
72
- return { action: type, success: result2.success, detail: result2.tweetId, error: result2.error, content: action.content };
73
- }
74
- logger.info("Image search returned no results, posting without image");
75
- } catch (imgError) {
76
- logger.warn(`Image attach failed, posting text only: ${imgError.message}`);
77
- }
78
- }
79
- const result = await client.postTweet(action.content);
80
- if (result.success) {
81
- logger.info(`Posted: "${action.content.slice(0, 50)}..."`);
82
- }
83
- return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };
84
- }
85
- case "reply": {
86
- if (!action.tweetId || !action.content) {
87
- return { action: type, success: false, error: "Missing tweetId or content" };
88
- }
89
- const client = await getXClient();
90
- if (action.imageQuery) {
91
- try {
92
- const { searchImage, downloadImage } = await import("./image-search-SZVMGWLN.js");
93
- const imageUrl = await searchImage(action.imageQuery);
94
- if (imageUrl) {
95
- const imageBuffer = await downloadImage(imageUrl);
96
- const result2 = await client.replyToTweetWithMedia(action.tweetId, action.content, imageBuffer);
97
- if (result2.success) {
98
- logger.info(`Replied with image to ${action.tweetId}: "${action.content.slice(0, 50)}..." (query: "${action.imageQuery}")`);
99
- }
100
- return { action: type, success: result2.success, detail: result2.tweetId, error: result2.error, content: action.content };
101
- }
102
- logger.info("Image search returned no results, replying without image");
103
- } catch (imgError) {
104
- logger.warn(`Image attach failed, replying text only: ${imgError.message}`);
105
- }
106
- }
107
- const result = await client.replyToTweet(action.tweetId, action.content);
108
- if (result.success) {
109
- logger.info(`Replied to ${action.tweetId}: "${action.content.slice(0, 50)}..."`);
110
- }
111
- return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };
112
- }
113
- case "like": {
114
- if (!action.tweetId) return { action: type, success: false, error: "Missing tweetId" };
115
- const client = await getXClient();
116
- const result = await client.likeTweet(action.tweetId);
117
- return { action: type, success: result.success, error: result.error };
118
- }
119
- case "retweet": {
120
- if (!action.tweetId) return { action: type, success: false, error: "Missing tweetId" };
121
- const client = await getXClient();
122
- const result = await client.retweet(action.tweetId);
123
- return { action: type, success: result.success, error: result.error };
124
- }
125
- case "follow": {
126
- if (!action.handle) return { action: type, success: false, error: "Missing handle" };
127
- const client = await getXClient();
128
- const result = await client.followUser(action.handle);
129
- return { action: type, success: result.success, error: result.error };
130
- }
131
- case "schedule": {
132
- if (!action.content) return { action: type, success: false, error: "No content" };
133
- const entry = addToQueue(action.content, action.scheduledFor, action.imageQuery);
134
- const time = new Date(entry.scheduledFor).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
135
- const imgNote = action.imageQuery ? ` [with image: "${action.imageQuery}"]` : "";
136
- logger.info(`Scheduled: "${action.content.slice(0, 50)}..." for ${time}${imgNote}`);
137
- return { action: type, success: true, detail: `Queued for ${time}${imgNote}`, content: action.content };
138
- }
139
- case "learn": {
140
- if (!action.content) return { action: type, success: false, error: "No content" };
141
- addLearning(action.content, "agent", action.tags ?? ["heartbeat"]);
142
- logger.info(`Learned: "${action.content.slice(0, 50)}..."`);
143
- return { action: type, success: true };
144
- }
145
- case "reflect": {
146
- if (!action.content) return { action: type, success: false, error: "No content" };
147
- const identity = loadIdentity();
148
- identity.evolutionJournal.push({
149
- date: (/* @__PURE__ */ new Date()).toISOString(),
150
- reflection: action.content
151
- });
152
- saveIdentity(identity);
153
- logger.info(`Reflected: "${action.content.slice(0, 50)}..."`);
154
- return { action: type, success: true };
155
- }
156
- case "search": {
157
- if (!action.query) return { action: type, success: false, error: "No query provided" };
158
- const client = await getXClient();
159
- const tweets = await client.searchTweets(action.query, { count: 10 });
160
- if (tweets.length === 0) {
161
- return { action: type, success: true, detail: "No results found" };
162
- }
163
- const results = tweets.map((t) => `@${t.authorHandle}: "${t.text}" [tweet:${t.id}]`).join("\n");
164
- logger.info(`Search "${action.query}": ${tweets.length} results`);
165
- return { action: type, success: true, detail: results };
166
- }
167
- case "skip": {
168
- logger.info(`Skipping: ${action.reason ?? action.reasoning ?? "no reason given"}`);
169
- return { action: type, success: true, detail: action.reason ?? action.reasoning };
170
- }
171
- default:
172
- logger.warn(`Unknown action: ${type}`);
173
- return { action: type, success: false, error: `Unknown action: ${type}` };
174
- }
175
- } catch (error) {
176
- const msg = error.message;
177
- logger.error(`Action ${type} failed: ${msg}`);
178
- return { action: type, success: false, error: msg };
179
- }
180
- }
181
- async function executeActions(actions, tweetMap) {
182
- const results = [];
183
- let tweetOutputCount = 0;
184
- let likeCount = 0;
185
- let followCount = 0;
186
- let retweetCount = 0;
187
- const MAX_TWEET_OUTPUTS = 1;
188
- const MAX_LIKES = 5;
189
- const MAX_FOLLOWS = 2;
190
- const MAX_RETWEETS = 1;
191
- for (const action of actions) {
192
- const type = action.action;
193
- if (type === "post" || type === "reply" || type === "schedule") {
194
- if (tweetOutputCount >= MAX_TWEET_OUTPUTS) {
195
- logger.info(`Skipped ${type}: tweet output limit reached (max ${MAX_TWEET_OUTPUTS})`);
196
- continue;
197
- }
198
- tweetOutputCount++;
199
- } else if (type === "like") {
200
- if (likeCount >= MAX_LIKES) {
201
- logger.info(`Skipped like: like limit reached (max ${MAX_LIKES})`);
202
- continue;
203
- }
204
- likeCount++;
205
- } else if (type === "follow") {
206
- if (followCount >= MAX_FOLLOWS) {
207
- logger.info(`Skipped follow: follow limit reached (max ${MAX_FOLLOWS})`);
208
- continue;
209
- }
210
- followCount++;
211
- } else if (type === "retweet") {
212
- if (retweetCount >= MAX_RETWEETS) {
213
- logger.info(`Skipped retweet: retweet limit reached (max ${MAX_RETWEETS})`);
214
- continue;
215
- }
216
- retweetCount++;
217
- }
218
- const result = await executeAction(action);
219
- results.push(result);
220
- if (result.success && result.detail && result.content) {
221
- if (type === "post") {
222
- trackPost(result.detail, result.content, "post");
223
- } else if (type === "reply") {
224
- trackPost(result.detail, result.content, "reply");
225
- }
226
- }
227
- if (result.success && tweetMap) {
228
- const cleanId = action.tweetId?.replace(/^tweet:/i, "").trim();
229
- if (cleanId) {
230
- const tweet = tweetMap.get(cleanId);
231
- if (tweet && (type === "reply" || type === "like" || type === "retweet")) {
232
- try {
233
- updateRelationship(tweet.authorId, {
234
- handle: tweet.authorHandle,
235
- sentiment: type === "reply" ? 0.6 : 0.4
236
- });
237
- } catch {
238
- }
239
- }
240
- }
241
- if (type === "follow" && action.handle) {
242
- try {
243
- updateRelationship(action.handle, {
244
- handle: action.handle,
245
- sentiment: 0.5,
246
- tags: ["followed"]
247
- });
248
- } catch {
249
- }
250
- }
251
- }
252
- await new Promise((r) => setTimeout(r, 2e3 + Math.random() * 3e3));
253
- }
254
- return results;
255
- }
256
-
257
- export {
258
- parseActions,
259
- executeAction,
260
- executeActions
261
- };
262
- //# sourceMappingURL=chunk-YZ7RWJ6Z.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/runtime/decision-engine.ts"],"sourcesContent":["import { logger } from \"../utils/logger.js\";\nimport { getXClient } from \"../x-client/index.js\";\nimport { logInteraction, addLearning, updateRelationship } from \"../memory/index.js\";\nimport { loadIdentity, saveIdentity } from \"../identity/index.js\";\nimport { addToQueue } from \"../scheduler/queue.js\";\nimport { trackPost } from \"../memory/performance.js\";\nimport type { Tweet } from \"../x-client/types.js\";\n\nexport interface AgentAction {\n action: string;\n content?: string;\n tweetId?: string;\n handle?: string;\n query?: string;\n tags?: string[];\n reason?: string;\n reasoning?: string;\n scheduledFor?: string;\n imageQuery?: string; // Optional: search query to find an image to attach\n}\n\nexport interface ActionResult {\n action: string;\n success: boolean;\n detail?: string;\n error?: string;\n content?: string; // The tweet text for post/reply/schedule actions\n}\n\nexport function parseActions(llmResponse: string): AgentAction[] {\n // Extract JSON array from the response (may be wrapped in markdown code blocks)\n const jsonMatch = llmResponse.match(/\\[[\\s\\S]*?\\]/);\n if (!jsonMatch) {\n // Try to parse as a single action object\n const objMatch = llmResponse.match(/\\{[\\s\\S]*?\\}/);\n if (objMatch) {\n try {\n return [JSON.parse(objMatch[0]) as AgentAction];\n } catch {\n logger.warn(\"Could not parse LLM response as action object\");\n return [];\n }\n }\n logger.warn(\"No JSON found in LLM response\");\n return [];\n }\n\n try {\n const actions = JSON.parse(jsonMatch[0]) as AgentAction[];\n return Array.isArray(actions) ? actions : [actions];\n } catch {\n logger.warn(\"Failed to parse actions JSON from LLM response\");\n return [];\n }\n}\n\nfunction sanitizeTweetId(id: string): string {\n // Strip \"tweet:\" prefix if LLM includes it from prompt format [tweet:123]\n return id.replace(/^tweet:/i, \"\").trim();\n}\n\nexport async function executeAction(action: AgentAction): Promise<ActionResult> {\n const { action: type } = action;\n\n // Clean up tweetId if present\n if (action.tweetId) {\n action.tweetId = sanitizeTweetId(action.tweetId);\n }\n\n try {\n switch (type) {\n case \"post\": {\n if (!action.content) return { action: type, success: false, error: \"No content provided\" };\n if (action.content.length > 280) {\n return { action: type, success: false, error: `Tweet too long: ${action.content.length} chars (max 280)` };\n }\n\n const client = await getXClient();\n\n // If imageQuery provided, search for an image and attach it\n if (action.imageQuery) {\n try {\n const { searchImage, downloadImage } = await import(\"../utils/image-search.js\");\n const imageUrl = await searchImage(action.imageQuery);\n if (imageUrl) {\n const imageBuffer = await downloadImage(imageUrl);\n const result = await client.postTweetWithMedia(action.content, imageBuffer);\n if (result.success) {\n logger.info(`Posted with image: \"${action.content.slice(0, 50)}...\" (query: \"${action.imageQuery}\")`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };\n }\n logger.info(\"Image search returned no results, posting without image\");\n } catch (imgError) {\n logger.warn(`Image attach failed, posting text only: ${(imgError as Error).message}`);\n }\n }\n\n const result = await client.postTweet(action.content);\n if (result.success) {\n logger.info(`Posted: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };\n }\n\n case \"reply\": {\n if (!action.tweetId || !action.content) {\n return { action: type, success: false, error: \"Missing tweetId or content\" };\n }\n\n const client = await getXClient();\n\n // If imageQuery provided, search for an image and attach it\n if (action.imageQuery) {\n try {\n const { searchImage, downloadImage } = await import(\"../utils/image-search.js\");\n const imageUrl = await searchImage(action.imageQuery);\n if (imageUrl) {\n const imageBuffer = await downloadImage(imageUrl);\n const result = await client.replyToTweetWithMedia(action.tweetId, action.content, imageBuffer);\n if (result.success) {\n logger.info(`Replied with image to ${action.tweetId}: \"${action.content.slice(0, 50)}...\" (query: \"${action.imageQuery}\")`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };\n }\n logger.info(\"Image search returned no results, replying without image\");\n } catch (imgError) {\n logger.warn(`Image attach failed, replying text only: ${(imgError as Error).message}`);\n }\n }\n\n const result = await client.replyToTweet(action.tweetId, action.content);\n if (result.success) {\n logger.info(`Replied to ${action.tweetId}: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error, content: action.content };\n }\n\n case \"like\": {\n if (!action.tweetId) return { action: type, success: false, error: \"Missing tweetId\" };\n const client = await getXClient();\n const result = await client.likeTweet(action.tweetId);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"retweet\": {\n if (!action.tweetId) return { action: type, success: false, error: \"Missing tweetId\" };\n const client = await getXClient();\n const result = await client.retweet(action.tweetId);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"follow\": {\n if (!action.handle) return { action: type, success: false, error: \"Missing handle\" };\n const client = await getXClient();\n const result = await client.followUser(action.handle);\n return { action: type, success: result.success, error: result.error };\n }\n\n case \"schedule\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n const entry = addToQueue(action.content, action.scheduledFor, action.imageQuery);\n const time = new Date(entry.scheduledFor).toLocaleTimeString(\"en-US\", { hour: \"numeric\", minute: \"2-digit\" });\n const imgNote = action.imageQuery ? ` [with image: \"${action.imageQuery}\"]` : \"\";\n logger.info(`Scheduled: \"${action.content.slice(0, 50)}...\" for ${time}${imgNote}`);\n return { action: type, success: true, detail: `Queued for ${time}${imgNote}`, content: action.content };\n }\n\n case \"learn\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n addLearning(action.content, \"agent\", action.tags ?? [\"heartbeat\"]);\n logger.info(`Learned: \"${action.content.slice(0, 50)}...\"`);\n return { action: type, success: true };\n }\n\n case \"reflect\": {\n if (!action.content) return { action: type, success: false, error: \"No content\" };\n const identity = loadIdentity();\n identity.evolutionJournal.push({\n date: new Date().toISOString(),\n reflection: action.content,\n });\n saveIdentity(identity);\n logger.info(`Reflected: \"${action.content.slice(0, 50)}...\"`);\n return { action: type, success: true };\n }\n\n case \"search\": {\n if (!action.query) return { action: type, success: false, error: \"No query provided\" };\n const client = await getXClient();\n const tweets = await client.searchTweets(action.query, { count: 10 });\n if (tweets.length === 0) {\n return { action: type, success: true, detail: \"No results found\" };\n }\n const results = tweets.map(t => `@${t.authorHandle}: \"${t.text}\" [tweet:${t.id}]`).join(\"\\n\");\n logger.info(`Search \"${action.query}\": ${tweets.length} results`);\n return { action: type, success: true, detail: results };\n }\n\n case \"skip\": {\n logger.info(`Skipping: ${action.reason ?? action.reasoning ?? \"no reason given\"}`);\n return { action: type, success: true, detail: action.reason ?? action.reasoning };\n }\n\n default:\n logger.warn(`Unknown action: ${type}`);\n return { action: type, success: false, error: `Unknown action: ${type}` };\n }\n } catch (error) {\n const msg = (error as Error).message;\n logger.error(`Action ${type} failed: ${msg}`);\n return { action: type, success: false, error: msg };\n }\n}\n\nexport async function executeActions(\n actions: AgentAction[],\n tweetMap?: Map<string, Tweet>,\n): Promise<ActionResult[]> {\n const results: ActionResult[] = [];\n\n // Per-type counters\n let tweetOutputCount = 0; // post + reply + schedule combined\n let likeCount = 0;\n let followCount = 0;\n let retweetCount = 0;\n\n // Hard caps per heartbeat\n const MAX_TWEET_OUTPUTS = 1;\n const MAX_LIKES = 5;\n const MAX_FOLLOWS = 2;\n const MAX_RETWEETS = 1;\n\n for (const action of actions) {\n const type = action.action;\n\n // Enforce per-type limits\n if (type === \"post\" || type === \"reply\" || type === \"schedule\") {\n if (tweetOutputCount >= MAX_TWEET_OUTPUTS) {\n logger.info(`Skipped ${type}: tweet output limit reached (max ${MAX_TWEET_OUTPUTS})`);\n continue;\n }\n tweetOutputCount++;\n } else if (type === \"like\") {\n if (likeCount >= MAX_LIKES) {\n logger.info(`Skipped like: like limit reached (max ${MAX_LIKES})`);\n continue;\n }\n likeCount++;\n } else if (type === \"follow\") {\n if (followCount >= MAX_FOLLOWS) {\n logger.info(`Skipped follow: follow limit reached (max ${MAX_FOLLOWS})`);\n continue;\n }\n followCount++;\n } else if (type === \"retweet\") {\n if (retweetCount >= MAX_RETWEETS) {\n logger.info(`Skipped retweet: retweet limit reached (max ${MAX_RETWEETS})`);\n continue;\n }\n retweetCount++;\n }\n\n const result = await executeAction(action);\n results.push(result);\n\n // Track successful posts/replies for performance monitoring\n if (result.success && result.detail && result.content) {\n if (type === \"post\") {\n trackPost(result.detail, result.content, \"post\");\n } else if (type === \"reply\") {\n trackPost(result.detail, result.content, \"reply\");\n }\n }\n\n // Update relationships for engagement actions\n if (result.success && tweetMap) {\n const cleanId = action.tweetId?.replace(/^tweet:/i, \"\").trim();\n if (cleanId) {\n const tweet = tweetMap.get(cleanId);\n if (tweet && (type === \"reply\" || type === \"like\" || type === \"retweet\")) {\n try {\n updateRelationship(tweet.authorId, {\n handle: tweet.authorHandle,\n sentiment: type === \"reply\" ? 0.6 : 0.4,\n });\n } catch { /* non-critical */ }\n }\n }\n if (type === \"follow\" && action.handle) {\n try {\n updateRelationship(action.handle, {\n handle: action.handle,\n sentiment: 0.5,\n tags: [\"followed\"],\n });\n } catch { /* non-critical */ }\n }\n }\n\n // Small delay between actions to be human-like\n await new Promise((r) => setTimeout(r, 2000 + Math.random() * 3000));\n }\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BO,SAAS,aAAa,aAAoC;AAE/D,QAAM,YAAY,YAAY,MAAM,cAAc;AAClD,MAAI,CAAC,WAAW;AAEd,UAAM,WAAW,YAAY,MAAM,cAAc;AACjD,QAAI,UAAU;AACZ,UAAI;AACF,eAAO,CAAC,KAAK,MAAM,SAAS,CAAC,CAAC,CAAgB;AAAA,MAChD,QAAQ;AACN,eAAO,KAAK,+CAA+C;AAC3D,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AACA,WAAO,KAAK,+BAA+B;AAC3C,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AACF,UAAM,UAAU,KAAK,MAAM,UAAU,CAAC,CAAC;AACvC,WAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AAAA,EACpD,QAAQ;AACN,WAAO,KAAK,gDAAgD;AAC5D,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,gBAAgB,IAAoB;AAE3C,SAAO,GAAG,QAAQ,YAAY,EAAE,EAAE,KAAK;AACzC;AAEA,eAAsB,cAAc,QAA4C;AAC9E,QAAM,EAAE,QAAQ,KAAK,IAAI;AAGzB,MAAI,OAAO,SAAS;AAClB,WAAO,UAAU,gBAAgB,OAAO,OAAO;AAAA,EACjD;AAEA,MAAI;AACF,YAAQ,MAAM;AAAA,MACZ,KAAK,QAAQ;AACX,YAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,sBAAsB;AACzF,YAAI,OAAO,QAAQ,SAAS,KAAK;AAC/B,iBAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,mBAAmB,OAAO,QAAQ,MAAM,mBAAmB;AAAA,QAC3G;AAEA,cAAM,SAAS,MAAM,WAAW;AAGhC,YAAI,OAAO,YAAY;AACrB,cAAI;AACF,kBAAM,EAAE,aAAa,cAAc,IAAI,MAAM,OAAO,4BAA0B;AAC9E,kBAAM,WAAW,MAAM,YAAY,OAAO,UAAU;AACpD,gBAAI,UAAU;AACZ,oBAAM,cAAc,MAAM,cAAc,QAAQ;AAChD,oBAAMA,UAAS,MAAM,OAAO,mBAAmB,OAAO,SAAS,WAAW;AAC1E,kBAAIA,QAAO,SAAS;AAClB,uBAAO,KAAK,uBAAuB,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,iBAAiB,OAAO,UAAU,IAAI;AAAA,cACtG;AACA,qBAAO,EAAE,QAAQ,MAAM,SAASA,QAAO,SAAS,QAAQA,QAAO,SAAS,OAAOA,QAAO,OAAO,SAAS,OAAO,QAAQ;AAAA,YACvH;AACA,mBAAO,KAAK,yDAAyD;AAAA,UACvE,SAAS,UAAU;AACjB,mBAAO,KAAK,2CAA4C,SAAmB,OAAO,EAAE;AAAA,UACtF;AAAA,QACF;AAEA,cAAM,SAAS,MAAM,OAAO,UAAU,OAAO,OAAO;AACpD,YAAI,OAAO,SAAS;AAClB,iBAAO,KAAK,YAAY,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AAAA,QAC3D;AACA,eAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,SAAS,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,SAAS,OAAO,QAAQ;AAAA,MACvH;AAAA,MAEA,KAAK,SAAS;AACZ,YAAI,CAAC,OAAO,WAAW,CAAC,OAAO,SAAS;AACtC,iBAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,6BAA6B;AAAA,QAC7E;AAEA,cAAM,SAAS,MAAM,WAAW;AAGhC,YAAI,OAAO,YAAY;AACrB,cAAI;AACF,kBAAM,EAAE,aAAa,cAAc,IAAI,MAAM,OAAO,4BAA0B;AAC9E,kBAAM,WAAW,MAAM,YAAY,OAAO,UAAU;AACpD,gBAAI,UAAU;AACZ,oBAAM,cAAc,MAAM,cAAc,QAAQ;AAChD,oBAAMA,UAAS,MAAM,OAAO,sBAAsB,OAAO,SAAS,OAAO,SAAS,WAAW;AAC7F,kBAAIA,QAAO,SAAS;AAClB,uBAAO,KAAK,yBAAyB,OAAO,OAAO,MAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,iBAAiB,OAAO,UAAU,IAAI;AAAA,cAC5H;AACA,qBAAO,EAAE,QAAQ,MAAM,SAASA,QAAO,SAAS,QAAQA,QAAO,SAAS,OAAOA,QAAO,OAAO,SAAS,OAAO,QAAQ;AAAA,YACvH;AACA,mBAAO,KAAK,0DAA0D;AAAA,UACxE,SAAS,UAAU;AACjB,mBAAO,KAAK,4CAA6C,SAAmB,OAAO,EAAE;AAAA,UACvF;AAAA,QACF;AAEA,cAAM,SAAS,MAAM,OAAO,aAAa,OAAO,SAAS,OAAO,OAAO;AACvE,YAAI,OAAO,SAAS;AAClB,iBAAO,KAAK,cAAc,OAAO,OAAO,MAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AAAA,QACjF;AACA,eAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,SAAS,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,SAAS,OAAO,QAAQ;AAAA,MACvH;AAAA,MAEA,KAAK,QAAQ;AACX,YAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,kBAAkB;AACrF,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,OAAO,UAAU,OAAO,OAAO;AACpD,eAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,SAAS,OAAO,OAAO,MAAM;AAAA,MACtE;AAAA,MAEA,KAAK,WAAW;AACd,YAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,kBAAkB;AACrF,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,OAAO,QAAQ,OAAO,OAAO;AAClD,eAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,SAAS,OAAO,OAAO,MAAM;AAAA,MACtE;AAAA,MAEA,KAAK,UAAU;AACb,YAAI,CAAC,OAAO,OAAQ,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,iBAAiB;AACnF,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,OAAO,WAAW,OAAO,MAAM;AACpD,eAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,SAAS,OAAO,OAAO,MAAM;AAAA,MACtE;AAAA,MAEA,KAAK,YAAY;AACf,YAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,aAAa;AAChF,cAAM,QAAQ,WAAW,OAAO,SAAS,OAAO,cAAc,OAAO,UAAU;AAC/E,cAAM,OAAO,IAAI,KAAK,MAAM,YAAY,EAAE,mBAAmB,SAAS,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AAC5G,cAAM,UAAU,OAAO,aAAa,kBAAkB,OAAO,UAAU,OAAO;AAC9E,eAAO,KAAK,eAAe,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,YAAY,IAAI,GAAG,OAAO,EAAE;AAClF,eAAO,EAAE,QAAQ,MAAM,SAAS,MAAM,QAAQ,cAAc,IAAI,GAAG,OAAO,IAAI,SAAS,OAAO,QAAQ;AAAA,MACxG;AAAA,MAEA,KAAK,SAAS;AACZ,YAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,aAAa;AAChF,oBAAY,OAAO,SAAS,SAAS,OAAO,QAAQ,CAAC,WAAW,CAAC;AACjE,eAAO,KAAK,aAAa,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AAC1D,eAAO,EAAE,QAAQ,MAAM,SAAS,KAAK;AAAA,MACvC;AAAA,MAEA,KAAK,WAAW;AACd,YAAI,CAAC,OAAO,QAAS,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,aAAa;AAChF,cAAM,WAAW,aAAa;AAC9B,iBAAS,iBAAiB,KAAK;AAAA,UAC7B,OAAM,oBAAI,KAAK,GAAE,YAAY;AAAA,UAC7B,YAAY,OAAO;AAAA,QACrB,CAAC;AACD,qBAAa,QAAQ;AACrB,eAAO,KAAK,eAAe,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AAC5D,eAAO,EAAE,QAAQ,MAAM,SAAS,KAAK;AAAA,MACvC;AAAA,MAEA,KAAK,UAAU;AACb,YAAI,CAAC,OAAO,MAAO,QAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,oBAAoB;AACrF,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,OAAO,aAAa,OAAO,OAAO,EAAE,OAAO,GAAG,CAAC;AACpE,YAAI,OAAO,WAAW,GAAG;AACvB,iBAAO,EAAE,QAAQ,MAAM,SAAS,MAAM,QAAQ,mBAAmB;AAAA,QACnE;AACA,cAAM,UAAU,OAAO,IAAI,OAAK,IAAI,EAAE,YAAY,MAAM,EAAE,IAAI,YAAY,EAAE,EAAE,GAAG,EAAE,KAAK,IAAI;AAC5F,eAAO,KAAK,WAAW,OAAO,KAAK,MAAM,OAAO,MAAM,UAAU;AAChE,eAAO,EAAE,QAAQ,MAAM,SAAS,MAAM,QAAQ,QAAQ;AAAA,MACxD;AAAA,MAEA,KAAK,QAAQ;AACX,eAAO,KAAK,aAAa,OAAO,UAAU,OAAO,aAAa,iBAAiB,EAAE;AACjF,eAAO,EAAE,QAAQ,MAAM,SAAS,MAAM,QAAQ,OAAO,UAAU,OAAO,UAAU;AAAA,MAClF;AAAA,MAEA;AACE,eAAO,KAAK,mBAAmB,IAAI,EAAE;AACrC,eAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,mBAAmB,IAAI,GAAG;AAAA,IAC5E;AAAA,EACF,SAAS,OAAO;AACd,UAAM,MAAO,MAAgB;AAC7B,WAAO,MAAM,UAAU,IAAI,YAAY,GAAG,EAAE;AAC5C,WAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,IAAI;AAAA,EACpD;AACF;AAEA,eAAsB,eACpB,SACA,UACyB;AACzB,QAAM,UAA0B,CAAC;AAGjC,MAAI,mBAAmB;AACvB,MAAI,YAAY;AAChB,MAAI,cAAc;AAClB,MAAI,eAAe;AAGnB,QAAM,oBAAoB;AAC1B,QAAM,YAAY;AAClB,QAAM,cAAc;AACpB,QAAM,eAAe;AAErB,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,OAAO;AAGpB,QAAI,SAAS,UAAU,SAAS,WAAW,SAAS,YAAY;AAC9D,UAAI,oBAAoB,mBAAmB;AACzC,eAAO,KAAK,WAAW,IAAI,qCAAqC,iBAAiB,GAAG;AACpF;AAAA,MACF;AACA;AAAA,IACF,WAAW,SAAS,QAAQ;AAC1B,UAAI,aAAa,WAAW;AAC1B,eAAO,KAAK,yCAAyC,SAAS,GAAG;AACjE;AAAA,MACF;AACA;AAAA,IACF,WAAW,SAAS,UAAU;AAC5B,UAAI,eAAe,aAAa;AAC9B,eAAO,KAAK,6CAA6C,WAAW,GAAG;AACvE;AAAA,MACF;AACA;AAAA,IACF,WAAW,SAAS,WAAW;AAC7B,UAAI,gBAAgB,cAAc;AAChC,eAAO,KAAK,+CAA+C,YAAY,GAAG;AAC1E;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,cAAc,MAAM;AACzC,YAAQ,KAAK,MAAM;AAGnB,QAAI,OAAO,WAAW,OAAO,UAAU,OAAO,SAAS;AACrD,UAAI,SAAS,QAAQ;AACnB,kBAAU,OAAO,QAAQ,OAAO,SAAS,MAAM;AAAA,MACjD,WAAW,SAAS,SAAS;AAC3B,kBAAU,OAAO,QAAQ,OAAO,SAAS,OAAO;AAAA,MAClD;AAAA,IACF;AAGA,QAAI,OAAO,WAAW,UAAU;AAC9B,YAAM,UAAU,OAAO,SAAS,QAAQ,YAAY,EAAE,EAAE,KAAK;AAC7D,UAAI,SAAS;AACX,cAAM,QAAQ,SAAS,IAAI,OAAO;AAClC,YAAI,UAAU,SAAS,WAAW,SAAS,UAAU,SAAS,YAAY;AACxE,cAAI;AACF,+BAAmB,MAAM,UAAU;AAAA,cACjC,QAAQ,MAAM;AAAA,cACd,WAAW,SAAS,UAAU,MAAM;AAAA,YACtC,CAAC;AAAA,UACH,QAAQ;AAAA,UAAqB;AAAA,QAC/B;AAAA,MACF;AACA,UAAI,SAAS,YAAY,OAAO,QAAQ;AACtC,YAAI;AACF,6BAAmB,OAAO,QAAQ;AAAA,YAChC,QAAQ,OAAO;AAAA,YACf,WAAW;AAAA,YACX,MAAM,CAAC,UAAU;AAAA,UACnB,CAAC;AAAA,QACH,QAAQ;AAAA,QAAqB;AAAA,MAC/B;AAAA,IACF;AAGA,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAO,KAAK,OAAO,IAAI,GAAI,CAAC;AAAA,EACrE;AACA,SAAO;AACT;","names":["result"]}
@@ -1,382 +0,0 @@
1
- import {
2
- rateLimiter
3
- } from "./chunk-JWMADEQO.js";
4
- import "./chunk-SXMDYUK3.js";
5
- import {
6
- loadCredentials
7
- } from "./chunk-POEDIDM6.js";
8
- import {
9
- logger
10
- } from "./chunk-J7J557HV.js";
11
- import {
12
- logInteraction
13
- } from "./chunk-RNVEWVDN.js";
14
- import "./chunk-Q7YS3AIK.js";
15
-
16
- // src/x-client/api/client.ts
17
- import { TwitterApi } from "twitter-api-v2";
18
- var XApiClient = class {
19
- client;
20
- userId = null;
21
- authenticatedHandle = null;
22
- constructor() {
23
- const creds = loadCredentials();
24
- if (creds.method !== "api") {
25
- throw new Error("API client requires API credentials. Current method: browser");
26
- }
27
- this.client = new TwitterApi({
28
- appKey: creds.apiKey,
29
- appSecret: creds.apiSecret,
30
- accessToken: creds.accessToken,
31
- accessSecret: creds.accessTokenSecret
32
- });
33
- }
34
- async getUserId() {
35
- if (this.userId) return this.userId;
36
- try {
37
- const me = await this.client.v2.me();
38
- this.userId = me.data.id;
39
- this.authenticatedHandle = me.data.username;
40
- logger.info(`Authenticated as @${this.authenticatedHandle} (ID: ${this.userId})`);
41
- return this.userId;
42
- } catch (error) {
43
- logger.error("Failed to get authenticated user", error);
44
- throw error;
45
- }
46
- }
47
- /**
48
- * Get the real Twitter handle of the authenticated user
49
- */
50
- async getAuthenticatedHandle() {
51
- if (this.authenticatedHandle) return this.authenticatedHandle;
52
- await this.getUserId();
53
- return this.authenticatedHandle;
54
- }
55
- async postTweet(content) {
56
- if (!rateLimiter.canPost()) {
57
- return { success: false, error: "Monthly post limit reached" };
58
- }
59
- try {
60
- const result = await this.client.v2.tweet(content);
61
- rateLimiter.consume();
62
- logInteraction({
63
- id: `int-${Date.now()}`,
64
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
65
- type: "post",
66
- tweetId: result.data.id,
67
- content,
68
- creditsUsed: 1,
69
- success: true
70
- });
71
- return { success: true, tweetId: result.data.id };
72
- } catch (error) {
73
- logger.error("Failed to post tweet", error);
74
- return { success: false, error: error.message };
75
- }
76
- }
77
- async postTweetWithMedia(content, mediaBuffer) {
78
- if (!rateLimiter.canPost()) {
79
- return { success: false, error: "Monthly post limit reached" };
80
- }
81
- try {
82
- const mediaId = await this.client.v1.uploadMedia(mediaBuffer, { mimeType: "image/jpeg" });
83
- const result = await this.client.v2.tweet({
84
- text: content,
85
- media: { media_ids: [mediaId] }
86
- });
87
- rateLimiter.consume();
88
- logInteraction({
89
- id: `int-${Date.now()}`,
90
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
91
- type: "post",
92
- tweetId: result.data.id,
93
- content: content + " [with image]",
94
- creditsUsed: 1,
95
- success: true
96
- });
97
- return { success: true, tweetId: result.data.id };
98
- } catch (error) {
99
- logger.error("Failed to post tweet with media", error);
100
- return { success: false, error: error.message };
101
- }
102
- }
103
- async replyToTweet(tweetId, content) {
104
- if (!rateLimiter.canPost()) {
105
- return { success: false, error: "Monthly post limit reached" };
106
- }
107
- try {
108
- const result = await this.client.v2.reply(content, tweetId);
109
- rateLimiter.consume();
110
- logInteraction({
111
- id: `int-${Date.now()}`,
112
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
113
- type: "reply",
114
- tweetId: result.data.id,
115
- inReplyTo: tweetId,
116
- content,
117
- creditsUsed: 1,
118
- success: true
119
- });
120
- return { success: true, tweetId: result.data.id };
121
- } catch (error) {
122
- logger.error("Failed to reply", error);
123
- return { success: false, error: error.message };
124
- }
125
- }
126
- async replyToTweetWithMedia(tweetId, content, mediaBuffer) {
127
- if (!rateLimiter.canPost()) {
128
- return { success: false, error: "Monthly post limit reached" };
129
- }
130
- try {
131
- const mediaId = await this.client.v1.uploadMedia(mediaBuffer, { mimeType: "image/jpeg" });
132
- const result = await this.client.v2.reply(content, tweetId, {
133
- media: { media_ids: [mediaId] }
134
- });
135
- rateLimiter.consume();
136
- logInteraction({
137
- id: `int-${Date.now()}`,
138
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
139
- type: "reply",
140
- tweetId: result.data.id,
141
- inReplyTo: tweetId,
142
- content: content + " [with image]",
143
- creditsUsed: 1,
144
- success: true
145
- });
146
- return { success: true, tweetId: result.data.id };
147
- } catch (error) {
148
- logger.error("Failed to reply with media", error);
149
- return { success: false, error: error.message };
150
- }
151
- }
152
- async deleteTweet(tweetId) {
153
- try {
154
- await this.client.v2.deleteTweet(tweetId);
155
- return { success: true, tweetId };
156
- } catch (error) {
157
- return { success: false, error: error.message };
158
- }
159
- }
160
- async likeTweet(tweetId) {
161
- rateLimiter.requireBasicTier("Like");
162
- try {
163
- const userId = await this.getUserId();
164
- logger.info(`Like attempt: userId=${userId}, tweetId=${tweetId}`);
165
- await this.client.v2.like(userId, tweetId);
166
- logInteraction({
167
- id: `int-${Date.now()}`,
168
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
169
- type: "like",
170
- tweetId,
171
- creditsUsed: 0,
172
- success: true
173
- });
174
- return { success: true, tweetId };
175
- } catch (error) {
176
- return { success: false, error: error.message };
177
- }
178
- }
179
- async unlikeTweet(tweetId) {
180
- rateLimiter.requireBasicTier("Unlike");
181
- try {
182
- const userId = await this.getUserId();
183
- await this.client.v2.unlike(userId, tweetId);
184
- return { success: true, tweetId };
185
- } catch (error) {
186
- return { success: false, error: error.message };
187
- }
188
- }
189
- async retweet(tweetId) {
190
- rateLimiter.requireBasicTier("Retweet");
191
- try {
192
- const userId = await this.getUserId();
193
- await this.client.v2.retweet(userId, tweetId);
194
- logInteraction({
195
- id: `int-${Date.now()}`,
196
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
197
- type: "retweet",
198
- tweetId,
199
- creditsUsed: 0,
200
- success: true
201
- });
202
- return { success: true, tweetId };
203
- } catch (error) {
204
- return { success: false, error: error.message };
205
- }
206
- }
207
- async unretweet(tweetId) {
208
- rateLimiter.requireBasicTier("Unretweet");
209
- try {
210
- const userId = await this.getUserId();
211
- await this.client.v2.unretweet(userId, tweetId);
212
- return { success: true, tweetId };
213
- } catch (error) {
214
- return { success: false, error: error.message };
215
- }
216
- }
217
- async followUser(userId) {
218
- rateLimiter.requireBasicTier("Follow");
219
- try {
220
- const myId = await this.getUserId();
221
- await this.client.v2.follow(myId, userId);
222
- logInteraction({
223
- id: `int-${Date.now()}`,
224
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
225
- type: "follow",
226
- targetUserId: userId,
227
- creditsUsed: 0,
228
- success: true
229
- });
230
- return { success: true };
231
- } catch (error) {
232
- return { success: false, error: error.message };
233
- }
234
- }
235
- async unfollowUser(userId) {
236
- rateLimiter.requireBasicTier("Unfollow");
237
- try {
238
- const myId = await this.getUserId();
239
- await this.client.v2.unfollow(myId, userId);
240
- return { success: true };
241
- } catch (error) {
242
- return { success: false, error: error.message };
243
- }
244
- }
245
- async getTimeline(options) {
246
- rateLimiter.requireBasicTier("Read timeline");
247
- try {
248
- const userId = await this.getUserId();
249
- const result = await this.client.v2.homeTimeline({
250
- max_results: options?.count ?? 20,
251
- "tweet.fields": ["created_at", "public_metrics", "in_reply_to_user_id"],
252
- expansions: ["author_id"],
253
- "user.fields": ["username"],
254
- ...options?.sinceId ? { since_id: options.sinceId } : {}
255
- });
256
- if (!result.data?.data) return [];
257
- const userMap = /* @__PURE__ */ new Map();
258
- for (const user of result.includes?.users ?? []) {
259
- userMap.set(user.id, user.username);
260
- }
261
- return result.data.data.map((tweet) => ({
262
- id: tweet.id,
263
- text: tweet.text,
264
- authorId: tweet.author_id ?? "unknown",
265
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
266
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
267
- likeCount: tweet.public_metrics?.like_count,
268
- retweetCount: tweet.public_metrics?.retweet_count,
269
- replyCount: tweet.public_metrics?.reply_count
270
- }));
271
- } catch (error) {
272
- logger.error("Failed to read timeline", error);
273
- return [];
274
- }
275
- }
276
- async getMentions(options) {
277
- rateLimiter.requireBasicTier("Read mentions");
278
- try {
279
- const userId = await this.getUserId();
280
- const result = await this.client.v2.userMentionTimeline(userId, {
281
- max_results: options?.count ?? 20,
282
- "tweet.fields": ["created_at", "public_metrics"],
283
- expansions: ["author_id"],
284
- "user.fields": ["username"],
285
- ...options?.sinceId ? { since_id: options.sinceId } : {}
286
- });
287
- if (!result.data?.data) return [];
288
- const userMap = /* @__PURE__ */ new Map();
289
- for (const user of result.includes?.users ?? []) {
290
- userMap.set(user.id, user.username);
291
- }
292
- return result.data.data.map((tweet) => ({
293
- id: tweet.id,
294
- text: tweet.text,
295
- authorId: tweet.author_id ?? "unknown",
296
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
297
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
298
- likeCount: tweet.public_metrics?.like_count,
299
- retweetCount: tweet.public_metrics?.retweet_count,
300
- replyCount: tweet.public_metrics?.reply_count
301
- }));
302
- } catch (error) {
303
- logger.error("Failed to read mentions", error);
304
- return [];
305
- }
306
- }
307
- async searchTweets(query, options) {
308
- rateLimiter.requireBasicTier("Search tweets");
309
- try {
310
- const result = await this.client.v2.search(query, {
311
- max_results: options?.count ?? 20,
312
- "tweet.fields": ["created_at", "public_metrics"],
313
- expansions: ["author_id"],
314
- "user.fields": ["username"]
315
- });
316
- if (!result.data?.data) return [];
317
- const userMap = /* @__PURE__ */ new Map();
318
- for (const user of result.includes?.users ?? []) {
319
- userMap.set(user.id, user.username);
320
- }
321
- return result.data.data.map((tweet) => ({
322
- id: tweet.id,
323
- text: tweet.text,
324
- authorId: tweet.author_id ?? "unknown",
325
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
326
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
327
- likeCount: tweet.public_metrics?.like_count,
328
- retweetCount: tweet.public_metrics?.retweet_count,
329
- replyCount: tweet.public_metrics?.reply_count
330
- }));
331
- } catch (error) {
332
- logger.error("Failed to search tweets", error);
333
- return [];
334
- }
335
- }
336
- async getTweet(tweetId) {
337
- rateLimiter.requireBasicTier("Get tweet");
338
- try {
339
- const result = await this.client.v2.singleTweet(tweetId, {
340
- "tweet.fields": ["created_at", "public_metrics"],
341
- expansions: ["author_id"],
342
- "user.fields": ["username"]
343
- });
344
- if (!result.data) return null;
345
- const tweet = result.data;
346
- const author = result.includes?.users?.[0];
347
- return {
348
- id: tweet.id,
349
- text: tweet.text,
350
- authorId: tweet.author_id ?? "unknown",
351
- authorHandle: author?.username ?? "unknown",
352
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
353
- likeCount: tweet.public_metrics?.like_count,
354
- retweetCount: tweet.public_metrics?.retweet_count,
355
- replyCount: tweet.public_metrics?.reply_count
356
- };
357
- } catch {
358
- return null;
359
- }
360
- }
361
- async getProfile(handle) {
362
- rateLimiter.requireBasicTier("Get profile");
363
- const result = await this.client.v2.userByUsername(handle, {
364
- "user.fields": ["description", "public_metrics", "verified", "profile_image_url"]
365
- });
366
- return {
367
- id: result.data.id,
368
- handle: result.data.username,
369
- name: result.data.name,
370
- bio: result.data.description ?? "",
371
- followersCount: result.data.public_metrics?.followers_count ?? 0,
372
- followingCount: result.data.public_metrics?.following_count ?? 0,
373
- tweetCount: result.data.public_metrics?.tweet_count ?? 0,
374
- verified: result.data.verified ?? false,
375
- profileImageUrl: result.data.profile_image_url
376
- };
377
- }
378
- };
379
- export {
380
- XApiClient
381
- };
382
- //# sourceMappingURL=client-23THPNVL.js.map