spora 0.7.0 → 0.7.2

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 (103) hide show
  1. package/README.md +7 -5
  2. package/dist/autonomy-DAV7X6QS.js +19 -0
  3. package/dist/{chunk-53YLFYJF.js → chunk-3RYCUGXE.js} +6 -2
  4. package/dist/chunk-3RYCUGXE.js.map +1 -0
  5. package/dist/{chunk-AH7HPXYC.js → chunk-AOQ3WLZV.js} +153 -143
  6. package/dist/chunk-AOQ3WLZV.js.map +1 -0
  7. package/dist/chunk-E5NR6HT4.js +29 -0
  8. package/dist/chunk-E5NR6HT4.js.map +1 -0
  9. package/dist/{chunk-EBO4F5NU.js → chunk-JBYZ7K56.js} +2 -2
  10. package/dist/chunk-KWWAIS3C.js +180 -0
  11. package/dist/chunk-KWWAIS3C.js.map +1 -0
  12. package/dist/{chunk-UINSD4FT.js → chunk-LXQNVVIY.js} +6 -6
  13. package/dist/{chunk-UINSD4FT.js.map → chunk-LXQNVVIY.js.map} +1 -1
  14. package/dist/{chunk-AIEXQCQS.js → chunk-M6YOQVSI.js} +2 -2
  15. package/dist/{chunk-B6RPMDML.js → chunk-NO3NQN67.js} +16 -6
  16. package/dist/chunk-NO3NQN67.js.map +1 -0
  17. package/dist/{chunk-QOKQ5OTU.js → chunk-NPV3OV2K.js} +3 -14
  18. package/dist/chunk-NPV3OV2K.js.map +1 -0
  19. package/dist/{chunk-SBQILQCJ.js → chunk-OACD3HGE.js} +7 -7
  20. package/dist/{chunk-UM57WU5I.js → chunk-P6KZIJYL.js} +2 -2
  21. package/dist/{chunk-AHXZIGQE.js → chunk-T7L2L7ZL.js} +2 -2
  22. package/dist/{chunk-ZJZKH7N7.js → chunk-VZBHRUZS.js} +2 -2
  23. package/dist/chunk-VZBHRUZS.js.map +1 -0
  24. package/dist/chunk-WIK74GGJ.js +295 -0
  25. package/dist/chunk-WIK74GGJ.js.map +1 -0
  26. package/dist/{chunk-YLJVFCT4.js → chunk-WN35MRMF.js} +2 -2
  27. package/dist/cli.js +173 -138
  28. package/dist/cli.js.map +1 -1
  29. package/dist/client-57BQKVYF.js +337 -0
  30. package/dist/client-57BQKVYF.js.map +1 -0
  31. package/dist/{colony-LCWN5IAN.js → colony-JPZC3R34.js} +7 -7
  32. package/dist/{config-TFAFYSIW.js → config-FL4VJVKZ.js} +3 -3
  33. package/dist/{crypto-FHSQ72NU.js → crypto-NOXNL4GP.js} +3 -3
  34. package/dist/{goals-5TAPXNR2.js → goals-RBKLMILE.js} +3 -3
  35. package/dist/{heartbeat-ZHRCEMF5.js → heartbeat-TNEPE3ZP.js} +83 -88
  36. package/dist/heartbeat-TNEPE3ZP.js.map +1 -0
  37. package/dist/{identity-O4FLSZKZ.js → identity-VDUW4I2K.js} +3 -3
  38. package/dist/{init-G3WINLAP.js → init-ISSXETHY.js} +59 -46
  39. package/dist/init-ISSXETHY.js.map +1 -0
  40. package/dist/llm-T33QTPVW.js +22 -0
  41. package/dist/mcp-server.js +28 -28
  42. package/dist/mcp-server.js.map +1 -1
  43. package/dist/{memory-O3AJIKBX.js → memory-OIAH33G2.js} +3 -3
  44. package/dist/{memory-7FBE26K3.js → memory-PNW7SX7A.js} +3 -3
  45. package/dist/{paths-5GFUUHCZ.js → paths-BYR6MEPR.js} +2 -2
  46. package/dist/prompt-builder-5NYONN2W.js +23 -0
  47. package/dist/queue-G5PTE6R6.js +14 -0
  48. package/dist/{strategy-S45TX766.js → strategy-Z4JSFHSP.js} +3 -3
  49. package/dist/{web-chat-RQIILEQK.js → web-chat-3HM35XM4.js} +31 -80
  50. package/dist/web-chat-3HM35XM4.js.map +1 -0
  51. package/dist/x-client-GY6XSPK6.js +12 -0
  52. package/package.json +1 -1
  53. package/dist/account-creator-ZD643X3Z.js +0 -498
  54. package/dist/account-creator-ZD643X3Z.js.map +0 -1
  55. package/dist/chunk-535NMUUW.js +0 -96
  56. package/dist/chunk-535NMUUW.js.map +0 -1
  57. package/dist/chunk-53YLFYJF.js.map +0 -1
  58. package/dist/chunk-55XPDJ6P.js +0 -124
  59. package/dist/chunk-55XPDJ6P.js.map +0 -1
  60. package/dist/chunk-AH7HPXYC.js.map +0 -1
  61. package/dist/chunk-B6RPMDML.js.map +0 -1
  62. package/dist/chunk-E6GMS76S.js +0 -154
  63. package/dist/chunk-E6GMS76S.js.map +0 -1
  64. package/dist/chunk-JJZ7T2IZ.js +0 -32
  65. package/dist/chunk-JJZ7T2IZ.js.map +0 -1
  66. package/dist/chunk-QOKQ5OTU.js.map +0 -1
  67. package/dist/chunk-TF2XYGGG.js +0 -249
  68. package/dist/chunk-TF2XYGGG.js.map +0 -1
  69. package/dist/chunk-ZJZKH7N7.js.map +0 -1
  70. package/dist/client-B6NGVRHM.js +0 -381
  71. package/dist/client-B6NGVRHM.js.map +0 -1
  72. package/dist/client-DDCS5FJS.js +0 -412
  73. package/dist/client-DDCS5FJS.js.map +0 -1
  74. package/dist/decision-engine-DRPIZLHI.js +0 -19
  75. package/dist/heartbeat-ZHRCEMF5.js.map +0 -1
  76. package/dist/init-G3WINLAP.js.map +0 -1
  77. package/dist/llm-3LSNADSR.js +0 -16
  78. package/dist/prompt-builder-U2J4H7YX.js +0 -24
  79. package/dist/queue-USY7JXDV.js +0 -14
  80. package/dist/research-TQLP42BC.js +0 -13
  81. package/dist/web-chat-RQIILEQK.js.map +0 -1
  82. package/dist/x-client-TYU5QSLG.js +0 -12
  83. package/dist/x-client-TYU5QSLG.js.map +0 -1
  84. /package/dist/{config-TFAFYSIW.js.map → autonomy-DAV7X6QS.js.map} +0 -0
  85. /package/dist/{chunk-EBO4F5NU.js.map → chunk-JBYZ7K56.js.map} +0 -0
  86. /package/dist/{chunk-AIEXQCQS.js.map → chunk-M6YOQVSI.js.map} +0 -0
  87. /package/dist/{chunk-SBQILQCJ.js.map → chunk-OACD3HGE.js.map} +0 -0
  88. /package/dist/{chunk-UM57WU5I.js.map → chunk-P6KZIJYL.js.map} +0 -0
  89. /package/dist/{chunk-AHXZIGQE.js.map → chunk-T7L2L7ZL.js.map} +0 -0
  90. /package/dist/{chunk-YLJVFCT4.js.map → chunk-WN35MRMF.js.map} +0 -0
  91. /package/dist/{colony-LCWN5IAN.js.map → colony-JPZC3R34.js.map} +0 -0
  92. /package/dist/{crypto-FHSQ72NU.js.map → config-FL4VJVKZ.js.map} +0 -0
  93. /package/dist/{decision-engine-DRPIZLHI.js.map → crypto-NOXNL4GP.js.map} +0 -0
  94. /package/dist/{goals-5TAPXNR2.js.map → goals-RBKLMILE.js.map} +0 -0
  95. /package/dist/{identity-O4FLSZKZ.js.map → identity-VDUW4I2K.js.map} +0 -0
  96. /package/dist/{llm-3LSNADSR.js.map → llm-T33QTPVW.js.map} +0 -0
  97. /package/dist/{memory-7FBE26K3.js.map → memory-OIAH33G2.js.map} +0 -0
  98. /package/dist/{memory-O3AJIKBX.js.map → memory-PNW7SX7A.js.map} +0 -0
  99. /package/dist/{paths-5GFUUHCZ.js.map → paths-BYR6MEPR.js.map} +0 -0
  100. /package/dist/{prompt-builder-U2J4H7YX.js.map → prompt-builder-5NYONN2W.js.map} +0 -0
  101. /package/dist/{queue-USY7JXDV.js.map → queue-G5PTE6R6.js.map} +0 -0
  102. /package/dist/{research-TQLP42BC.js.map → strategy-Z4JSFHSP.js.map} +0 -0
  103. /package/dist/{strategy-S45TX766.js.map → x-client-GY6XSPK6.js.map} +0 -0
@@ -1,249 +0,0 @@
1
- import {
2
- getXClient
3
- } from "./chunk-JJZ7T2IZ.js";
4
- import {
5
- addToQueue
6
- } from "./chunk-SBQILQCJ.js";
7
- import {
8
- rateLimiter
9
- } from "./chunk-UINSD4FT.js";
10
- import {
11
- loadIdentity,
12
- saveIdentity
13
- } from "./chunk-AIEXQCQS.js";
14
- import {
15
- logger
16
- } from "./chunk-QOKQ5OTU.js";
17
- import {
18
- addLearning,
19
- logInteraction
20
- } from "./chunk-EBO4F5NU.js";
21
-
22
- // src/runtime/decision-engine.ts
23
- function parseActions(llmResponse) {
24
- const codeBlockMatch = llmResponse.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
25
- if (codeBlockMatch) {
26
- const inside = codeBlockMatch[1].trim();
27
- try {
28
- const parsed = JSON.parse(inside);
29
- return Array.isArray(parsed) ? parsed : [parsed];
30
- } catch {
31
- }
32
- }
33
- let lastArrayStart = -1;
34
- let lastArrayEnd = -1;
35
- let depth = 0;
36
- for (let i = 0; i < llmResponse.length; i++) {
37
- if (llmResponse[i] === "[") {
38
- if (depth === 0) lastArrayStart = i;
39
- depth++;
40
- } else if (llmResponse[i] === "]") {
41
- depth--;
42
- if (depth === 0) lastArrayEnd = i;
43
- }
44
- }
45
- if (lastArrayStart >= 0 && lastArrayEnd > lastArrayStart) {
46
- const arrayStr = llmResponse.slice(lastArrayStart, lastArrayEnd + 1);
47
- try {
48
- const parsed = JSON.parse(arrayStr);
49
- if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].action) {
50
- return parsed;
51
- }
52
- } catch {
53
- }
54
- }
55
- const objMatches = [...llmResponse.matchAll(/\{[^{}]*"action"\s*:\s*"[^"]+"/g)];
56
- if (objMatches.length > 0) {
57
- for (let i = objMatches.length - 1; i >= 0; i--) {
58
- const start = objMatches[i].index;
59
- let braceDepth = 0;
60
- let end = -1;
61
- for (let j = start; j < llmResponse.length; j++) {
62
- if (llmResponse[j] === "{") braceDepth++;
63
- else if (llmResponse[j] === "}") {
64
- braceDepth--;
65
- if (braceDepth === 0) {
66
- end = j;
67
- break;
68
- }
69
- }
70
- }
71
- if (end > start) {
72
- try {
73
- const obj = JSON.parse(llmResponse.slice(start, end + 1));
74
- if (obj.action) return [obj];
75
- } catch {
76
- continue;
77
- }
78
- }
79
- }
80
- }
81
- try {
82
- const parsed = JSON.parse(llmResponse.trim());
83
- if (Array.isArray(parsed)) return parsed;
84
- if (parsed.action) return [parsed];
85
- } catch {
86
- }
87
- logger.warn("Failed to parse actions from LLM response");
88
- if (llmResponse.length < 500) {
89
- logger.warn(`Response was: ${llmResponse}`);
90
- } else {
91
- logger.warn(`Response starts with: ${llmResponse.slice(0, 200)}...`);
92
- }
93
- return [];
94
- }
95
- async function executeAction(action) {
96
- const { action: type } = action;
97
- try {
98
- switch (type) {
99
- case "post": {
100
- if (!action.content) return { action: type, success: false, error: "No content provided" };
101
- if (action.content.length > 280) {
102
- return { action: type, success: false, error: `Tweet too long: ${action.content.length} chars (max 280)` };
103
- }
104
- if (!rateLimiter.canPost()) {
105
- return { action: type, success: false, error: "No credits remaining this month" };
106
- }
107
- const client = await getXClient();
108
- const result = await client.postTweet(action.content);
109
- if (result.success) {
110
- rateLimiter.consume(1);
111
- logInteraction({
112
- id: `int-${Date.now()}`,
113
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
114
- type: "post",
115
- tweetId: result.tweetId,
116
- content: action.content,
117
- creditsUsed: 1,
118
- success: true
119
- });
120
- logger.info(`Posted: "${action.content.slice(0, 50)}..."`);
121
- }
122
- return { action: type, success: result.success, detail: result.tweetId, error: result.error };
123
- }
124
- case "reply": {
125
- if (!action.tweetId || !action.content) {
126
- return { action: type, success: false, error: "Missing tweetId or content" };
127
- }
128
- if (!rateLimiter.canPost()) {
129
- return { action: type, success: false, error: "No credits remaining" };
130
- }
131
- const client = await getXClient();
132
- const result = await client.replyToTweet(action.tweetId, action.content);
133
- if (result.success) {
134
- rateLimiter.consume(1);
135
- logInteraction({
136
- id: `int-${Date.now()}`,
137
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
138
- type: "reply",
139
- tweetId: result.tweetId,
140
- inReplyTo: action.tweetId,
141
- content: action.content,
142
- creditsUsed: 1,
143
- success: true
144
- });
145
- logger.info(`Replied to ${action.tweetId}: "${action.content.slice(0, 50)}..."`);
146
- }
147
- return { action: type, success: result.success, detail: result.tweetId, error: result.error };
148
- }
149
- case "like": {
150
- if (!action.tweetId) return { action: type, success: false, error: "Missing tweetId" };
151
- const client = await getXClient();
152
- const result = await client.likeTweet(action.tweetId);
153
- if (result.success) {
154
- logInteraction({
155
- id: `int-${Date.now()}`,
156
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
157
- type: "like",
158
- tweetId: action.tweetId,
159
- creditsUsed: 0,
160
- success: true
161
- });
162
- }
163
- return { action: type, success: result.success, error: result.error };
164
- }
165
- case "retweet": {
166
- if (!action.tweetId) return { action: type, success: false, error: "Missing tweetId" };
167
- const client = await getXClient();
168
- const result = await client.retweet(action.tweetId);
169
- if (result.success) {
170
- logInteraction({
171
- id: `int-${Date.now()}`,
172
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
173
- type: "retweet",
174
- tweetId: action.tweetId,
175
- creditsUsed: 0,
176
- success: true
177
- });
178
- }
179
- return { action: type, success: result.success, error: result.error };
180
- }
181
- case "follow": {
182
- if (!action.handle) return { action: type, success: false, error: "Missing handle" };
183
- const client = await getXClient();
184
- const result = await client.followUser(action.handle);
185
- if (result.success) {
186
- logInteraction({
187
- id: `int-${Date.now()}`,
188
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
189
- type: "follow",
190
- targetHandle: action.handle,
191
- creditsUsed: 0,
192
- success: true
193
- });
194
- }
195
- return { action: type, success: result.success, error: result.error };
196
- }
197
- case "schedule": {
198
- if (!action.content) return { action: type, success: false, error: "No content" };
199
- const entry = addToQueue(action.content);
200
- logger.info(`Scheduled: "${action.content.slice(0, 50)}..." for ${entry.scheduledFor}`);
201
- return { action: type, success: true, detail: `Scheduled for ${entry.scheduledFor}` };
202
- }
203
- case "learn": {
204
- if (!action.content) return { action: type, success: false, error: "No content" };
205
- addLearning(action.content, "agent", action.tags ?? ["heartbeat"]);
206
- logger.info(`Learned: "${action.content.slice(0, 50)}..."`);
207
- return { action: type, success: true };
208
- }
209
- case "reflect": {
210
- if (!action.content) return { action: type, success: false, error: "No content" };
211
- const identity = loadIdentity();
212
- identity.evolutionJournal.push({
213
- date: (/* @__PURE__ */ new Date()).toISOString(),
214
- reflection: action.content
215
- });
216
- saveIdentity(identity);
217
- logger.info(`Reflected: "${action.content.slice(0, 50)}..."`);
218
- return { action: type, success: true };
219
- }
220
- case "skip": {
221
- logger.info(`Skipping: ${action.reason ?? action.reasoning ?? "no reason given"}`);
222
- return { action: type, success: true, detail: action.reason ?? action.reasoning };
223
- }
224
- default:
225
- logger.warn(`Unknown action: ${type}`);
226
- return { action: type, success: false, error: `Unknown action: ${type}` };
227
- }
228
- } catch (error) {
229
- const msg = error.message;
230
- logger.error(`Action ${type} failed: ${msg}`);
231
- return { action: type, success: false, error: msg };
232
- }
233
- }
234
- async function executeActions(actions) {
235
- const results = [];
236
- for (const action of actions) {
237
- const result = await executeAction(action);
238
- results.push(result);
239
- await new Promise((r) => setTimeout(r, 2e3 + Math.random() * 3e3));
240
- }
241
- return results;
242
- }
243
-
244
- export {
245
- parseActions,
246
- executeAction,
247
- executeActions
248
- };
249
- //# sourceMappingURL=chunk-TF2XYGGG.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 } from \"../memory/index.js\";\nimport { loadIdentity, saveIdentity } from \"../identity/index.js\";\nimport { addToQueue } from \"../scheduler/queue.js\";\nimport { rateLimiter } from \"../x-client/rate-limiter.js\";\n\nexport interface AgentAction {\n action: string;\n content?: string;\n tweetId?: string;\n handle?: string;\n tags?: string[];\n reason?: string;\n reasoning?: string;\n}\n\nexport interface ActionResult {\n action: string;\n success: boolean;\n detail?: string;\n error?: string;\n}\n\nexport function parseActions(llmResponse: string): AgentAction[] {\n // Strategy: try multiple extraction approaches, most specific first\n\n // 1. Try markdown code block first (```json ... ``` or ``` ... ```)\n const codeBlockMatch = llmResponse.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)```/);\n if (codeBlockMatch) {\n const inside = codeBlockMatch[1].trim();\n try {\n const parsed = JSON.parse(inside);\n return Array.isArray(parsed) ? parsed : [parsed];\n } catch {\n // Code block content wasn't valid JSON, continue to other strategies\n }\n }\n\n // 2. Find the last JSON array in the response (LLMs often put reasoning before the array)\n // Use a greedy approach to find the outermost array brackets\n let lastArrayStart = -1;\n let lastArrayEnd = -1;\n let depth = 0;\n for (let i = 0; i < llmResponse.length; i++) {\n if (llmResponse[i] === \"[\") {\n if (depth === 0) lastArrayStart = i;\n depth++;\n } else if (llmResponse[i] === \"]\") {\n depth--;\n if (depth === 0) lastArrayEnd = i;\n }\n }\n\n if (lastArrayStart >= 0 && lastArrayEnd > lastArrayStart) {\n const arrayStr = llmResponse.slice(lastArrayStart, lastArrayEnd + 1);\n try {\n const parsed = JSON.parse(arrayStr);\n if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].action) {\n return parsed;\n }\n } catch {\n // Not valid JSON array, try next strategy\n }\n }\n\n // 3. Try to find a single action object (last one in the response)\n const objMatches = [...llmResponse.matchAll(/\\{[^{}]*\"action\"\\s*:\\s*\"[^\"]+\"/g)];\n if (objMatches.length > 0) {\n // Find the full object starting from this match\n for (let i = objMatches.length - 1; i >= 0; i--) {\n const start = objMatches[i].index!;\n let braceDepth = 0;\n let end = -1;\n for (let j = start; j < llmResponse.length; j++) {\n if (llmResponse[j] === \"{\") braceDepth++;\n else if (llmResponse[j] === \"}\") {\n braceDepth--;\n if (braceDepth === 0) { end = j; break; }\n }\n }\n if (end > start) {\n try {\n const obj = JSON.parse(llmResponse.slice(start, end + 1));\n if (obj.action) return [obj as AgentAction];\n } catch {\n continue;\n }\n }\n }\n }\n\n // 4. Last resort: try the whole response as JSON\n try {\n const parsed = JSON.parse(llmResponse.trim());\n if (Array.isArray(parsed)) return parsed;\n if (parsed.action) return [parsed as AgentAction];\n } catch {\n // Not JSON at all\n }\n\n logger.warn(\"Failed to parse actions from LLM response\");\n if (llmResponse.length < 500) {\n logger.warn(`Response was: ${llmResponse}`);\n } else {\n logger.warn(`Response starts with: ${llmResponse.slice(0, 200)}...`);\n }\n return [];\n}\n\nexport async function executeAction(action: AgentAction): Promise<ActionResult> {\n const { action: type } = action;\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 if (!rateLimiter.canPost()) {\n return { action: type, success: false, error: \"No credits remaining this month\" };\n }\n\n const client = await getXClient();\n const result = await client.postTweet(action.content);\n if (result.success) {\n rateLimiter.consume(1);\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n tweetId: result.tweetId,\n content: action.content,\n creditsUsed: 1,\n success: true,\n });\n logger.info(`Posted: \"${action.content.slice(0, 50)}...\"`);\n }\n return { action: type, success: result.success, detail: result.tweetId, error: result.error };\n }\n\n case \"reply\": {\n if (!action.tweetId || !action.content) {\n return { action: type, success: false, error: \"Missing tweetId or content\" };\n }\n if (!rateLimiter.canPost()) {\n return { action: type, success: false, error: \"No credits remaining\" };\n }\n\n const client = await getXClient();\n const result = await client.replyToTweet(action.tweetId, action.content);\n if (result.success) {\n rateLimiter.consume(1);\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n tweetId: result.tweetId,\n inReplyTo: action.tweetId,\n content: action.content,\n creditsUsed: 1,\n success: true,\n });\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 };\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 if (result.success) {\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"like\",\n tweetId: action.tweetId,\n creditsUsed: 0,\n success: true,\n });\n }\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 if (result.success) {\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"retweet\",\n tweetId: action.tweetId,\n creditsUsed: 0,\n success: true,\n });\n }\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 if (result.success) {\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"follow\",\n targetHandle: action.handle,\n creditsUsed: 0,\n success: true,\n });\n }\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);\n logger.info(`Scheduled: \"${action.content.slice(0, 50)}...\" for ${entry.scheduledFor}`);\n return { action: type, success: true, detail: `Scheduled for ${entry.scheduledFor}` };\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 \"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(actions: AgentAction[]): Promise<ActionResult[]> {\n const results: ActionResult[] = [];\n for (const action of actions) {\n const result = await executeAction(action);\n results.push(result);\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":";;;;;;;;;;;;;;;;;;;;;;AAwBO,SAAS,aAAa,aAAoC;AAI/D,QAAM,iBAAiB,YAAY,MAAM,iCAAiC;AAC1E,MAAI,gBAAgB;AAClB,UAAM,SAAS,eAAe,CAAC,EAAE,KAAK;AACtC,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,MAAM;AAChC,aAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAAA,IACjD,QAAQ;AAAA,IAER;AAAA,EACF;AAIA,MAAI,iBAAiB;AACrB,MAAI,eAAe;AACnB,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,QAAI,YAAY,CAAC,MAAM,KAAK;AAC1B,UAAI,UAAU,EAAG,kBAAiB;AAClC;AAAA,IACF,WAAW,YAAY,CAAC,MAAM,KAAK;AACjC;AACA,UAAI,UAAU,EAAG,gBAAe;AAAA,IAClC;AAAA,EACF;AAEA,MAAI,kBAAkB,KAAK,eAAe,gBAAgB;AACxD,UAAM,WAAW,YAAY,MAAM,gBAAgB,eAAe,CAAC;AACnE,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,UAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,KAAK,OAAO,CAAC,EAAE,QAAQ;AAClE,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,aAAa,CAAC,GAAG,YAAY,SAAS,iCAAiC,CAAC;AAC9E,MAAI,WAAW,SAAS,GAAG;AAEzB,aAAS,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AAC/C,YAAM,QAAQ,WAAW,CAAC,EAAE;AAC5B,UAAI,aAAa;AACjB,UAAI,MAAM;AACV,eAAS,IAAI,OAAO,IAAI,YAAY,QAAQ,KAAK;AAC/C,YAAI,YAAY,CAAC,MAAM,IAAK;AAAA,iBACnB,YAAY,CAAC,MAAM,KAAK;AAC/B;AACA,cAAI,eAAe,GAAG;AAAE,kBAAM;AAAG;AAAA,UAAO;AAAA,QAC1C;AAAA,MACF;AACA,UAAI,MAAM,OAAO;AACf,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,YAAY,MAAM,OAAO,MAAM,CAAC,CAAC;AACxD,cAAI,IAAI,OAAQ,QAAO,CAAC,GAAkB;AAAA,QAC5C,QAAQ;AACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,YAAY,KAAK,CAAC;AAC5C,QAAI,MAAM,QAAQ,MAAM,EAAG,QAAO;AAClC,QAAI,OAAO,OAAQ,QAAO,CAAC,MAAqB;AAAA,EAClD,QAAQ;AAAA,EAER;AAEA,SAAO,KAAK,2CAA2C;AACvD,MAAI,YAAY,SAAS,KAAK;AAC5B,WAAO,KAAK,iBAAiB,WAAW,EAAE;AAAA,EAC5C,OAAO;AACL,WAAO,KAAK,yBAAyB,YAAY,MAAM,GAAG,GAAG,CAAC,KAAK;AAAA,EACrE;AACA,SAAO,CAAC;AACV;AAEA,eAAsB,cAAc,QAA4C;AAC9E,QAAM,EAAE,QAAQ,KAAK,IAAI;AAEzB,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;AACA,YAAI,CAAC,YAAY,QAAQ,GAAG;AAC1B,iBAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,kCAAkC;AAAA,QAClF;AAEA,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,OAAO,UAAU,OAAO,OAAO;AACpD,YAAI,OAAO,SAAS;AAClB,sBAAY,QAAQ,CAAC;AACrB,yBAAe;AAAA,YACb,IAAI,OAAO,KAAK,IAAI,CAAC;AAAA,YACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,MAAM;AAAA,YACN,SAAS,OAAO;AAAA,YAChB,SAAS,OAAO;AAAA,YAChB,aAAa;AAAA,YACb,SAAS;AAAA,UACX,CAAC;AACD,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,MAAM;AAAA,MAC9F;AAAA,MAEA,KAAK,SAAS;AACZ,YAAI,CAAC,OAAO,WAAW,CAAC,OAAO,SAAS;AACtC,iBAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,6BAA6B;AAAA,QAC7E;AACA,YAAI,CAAC,YAAY,QAAQ,GAAG;AAC1B,iBAAO,EAAE,QAAQ,MAAM,SAAS,OAAO,OAAO,uBAAuB;AAAA,QACvE;AAEA,cAAM,SAAS,MAAM,WAAW;AAChC,cAAM,SAAS,MAAM,OAAO,aAAa,OAAO,SAAS,OAAO,OAAO;AACvE,YAAI,OAAO,SAAS;AAClB,sBAAY,QAAQ,CAAC;AACrB,yBAAe;AAAA,YACb,IAAI,OAAO,KAAK,IAAI,CAAC;AAAA,YACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,MAAM;AAAA,YACN,SAAS,OAAO;AAAA,YAChB,WAAW,OAAO;AAAA,YAClB,SAAS,OAAO;AAAA,YAChB,aAAa;AAAA,YACb,SAAS;AAAA,UACX,CAAC;AACD,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,MAAM;AAAA,MAC9F;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,YAAI,OAAO,SAAS;AAClB,yBAAe;AAAA,YACb,IAAI,OAAO,KAAK,IAAI,CAAC;AAAA,YACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,MAAM;AAAA,YACN,SAAS,OAAO;AAAA,YAChB,aAAa;AAAA,YACb,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AACA,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,YAAI,OAAO,SAAS;AAClB,yBAAe;AAAA,YACb,IAAI,OAAO,KAAK,IAAI,CAAC;AAAA,YACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,MAAM;AAAA,YACN,SAAS,OAAO;AAAA,YAChB,aAAa;AAAA,YACb,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AACA,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,YAAI,OAAO,SAAS;AAClB,yBAAe;AAAA,YACb,IAAI,OAAO,KAAK,IAAI,CAAC;AAAA,YACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,YAClC,MAAM;AAAA,YACN,cAAc,OAAO;AAAA,YACrB,aAAa;AAAA,YACb,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AACA,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,OAAO;AACvC,eAAO,KAAK,eAAe,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC,YAAY,MAAM,YAAY,EAAE;AACtF,eAAO,EAAE,QAAQ,MAAM,SAAS,MAAM,QAAQ,iBAAiB,MAAM,YAAY,GAAG;AAAA,MACtF;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,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,eAAe,SAAiD;AACpF,QAAM,UAA0B,CAAC;AACjC,aAAW,UAAU,SAAS;AAC5B,UAAM,SAAS,MAAM,cAAc,MAAM;AACzC,YAAQ,KAAK,MAAM;AAEnB,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAO,KAAK,OAAO,IAAI,GAAI,CAAC;AAAA,EACrE;AACA,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/crypto.ts"],"sourcesContent":["import { createCipheriv, createDecipheriv, randomBytes, createHash } from \"node:crypto\";\nimport { readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { paths, ensureDirectories } from \"./paths.js\";\n\nconst ALGORITHM = \"aes-256-gcm\";\n\nfunction deriveKey(): Buffer {\n const machineId = `spora-${hostname()}-${process.env.USER ?? \"default\"}`;\n return createHash(\"sha256\").update(machineId).digest();\n}\n\nexport function encrypt(data: string): string {\n const key = deriveKey();\n const iv = randomBytes(16);\n const cipher = createCipheriv(ALGORITHM, key, iv);\n\n let encrypted = cipher.update(data, \"utf-8\", \"hex\");\n encrypted += cipher.final(\"hex\");\n const authTag = cipher.getAuthTag().toString(\"hex\");\n\n return JSON.stringify({\n iv: iv.toString(\"hex\"),\n encrypted,\n authTag,\n });\n}\n\nexport function decrypt(payload: string): string {\n const key = deriveKey();\n const { iv, encrypted, authTag } = JSON.parse(payload);\n\n const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, \"hex\"));\n decipher.setAuthTag(Buffer.from(authTag, \"hex\"));\n\n let decrypted = decipher.update(encrypted, \"hex\", \"utf-8\");\n decrypted += decipher.final(\"utf-8\");\n\n return decrypted;\n}\n\nexport interface XCredentials {\n method: \"api\" | \"browser\";\n // API mode\n apiKey?: string;\n apiSecret?: string;\n accessToken?: string;\n accessTokenSecret?: string;\n bearerToken?: string;\n // Browser mode\n username?: string;\n password?: string;\n email?: string;\n}\n\nexport function saveCredentials(credentials: XCredentials): void {\n ensureDirectories();\n const encrypted = encrypt(JSON.stringify(credentials));\n writeFileSync(paths.credentials, encrypted);\n}\n\nexport function loadCredentials(): XCredentials {\n if (!existsSync(paths.credentials)) {\n throw new Error(\"No credentials found. Run `spora init` first.\");\n }\n const payload = readFileSync(paths.credentials, \"utf-8\");\n return JSON.parse(decrypt(payload)) as XCredentials;\n}\n"],"mappings":";;;;;;AAAA,SAAS,gBAAgB,kBAAkB,aAAa,kBAAkB;AAC1E,SAAS,cAAc,eAAe,kBAAkB;AACxD,SAAS,gBAAgB;AAGzB,IAAM,YAAY;AAElB,SAAS,YAAoB;AAC3B,QAAM,YAAY,SAAS,SAAS,CAAC,IAAI,QAAQ,IAAI,QAAQ,SAAS;AACtE,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO;AACvD;AAEO,SAAS,QAAQ,MAAsB;AAC5C,QAAM,MAAM,UAAU;AACtB,QAAM,KAAK,YAAY,EAAE;AACzB,QAAM,SAAS,eAAe,WAAW,KAAK,EAAE;AAEhD,MAAI,YAAY,OAAO,OAAO,MAAM,SAAS,KAAK;AAClD,eAAa,OAAO,MAAM,KAAK;AAC/B,QAAM,UAAU,OAAO,WAAW,EAAE,SAAS,KAAK;AAElD,SAAO,KAAK,UAAU;AAAA,IACpB,IAAI,GAAG,SAAS,KAAK;AAAA,IACrB;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,SAAS,QAAQ,SAAyB;AAC/C,QAAM,MAAM,UAAU;AACtB,QAAM,EAAE,IAAI,WAAW,QAAQ,IAAI,KAAK,MAAM,OAAO;AAErD,QAAM,WAAW,iBAAiB,WAAW,KAAK,OAAO,KAAK,IAAI,KAAK,CAAC;AACxE,WAAS,WAAW,OAAO,KAAK,SAAS,KAAK,CAAC;AAE/C,MAAI,YAAY,SAAS,OAAO,WAAW,OAAO,OAAO;AACzD,eAAa,SAAS,MAAM,OAAO;AAEnC,SAAO;AACT;AAgBO,SAAS,gBAAgB,aAAiC;AAC/D,oBAAkB;AAClB,QAAM,YAAY,QAAQ,KAAK,UAAU,WAAW,CAAC;AACrD,gBAAc,MAAM,aAAa,SAAS;AAC5C;AAEO,SAAS,kBAAgC;AAC9C,MAAI,CAAC,WAAW,MAAM,WAAW,GAAG;AAClC,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACA,QAAM,UAAU,aAAa,MAAM,aAAa,OAAO;AACvD,SAAO,KAAK,MAAM,QAAQ,OAAO,CAAC;AACpC;","names":[]}
@@ -1,381 +0,0 @@
1
- import {
2
- rateLimiter
3
- } from "./chunk-UINSD4FT.js";
4
- import "./chunk-B6RPMDML.js";
5
- import {
6
- identityExists,
7
- loadIdentity
8
- } from "./chunk-AIEXQCQS.js";
9
- import {
10
- loadCredentials
11
- } from "./chunk-ZJZKH7N7.js";
12
- import {
13
- logger
14
- } from "./chunk-QOKQ5OTU.js";
15
- import {
16
- logInteraction
17
- } from "./chunk-EBO4F5NU.js";
18
- import "./chunk-53YLFYJF.js";
19
-
20
- // src/x-client/api/client.ts
21
- import { TwitterApi } from "twitter-api-v2";
22
- function extractTwitterError(error) {
23
- if (!error || typeof error !== "object") return String(error);
24
- const e = error;
25
- if (e.code && e.data) {
26
- const status = e.code;
27
- const data = e.data;
28
- const errors = data.errors ?? data.detail ?? data.title ?? data.reason;
29
- return `HTTP ${status}: ${JSON.stringify(errors ?? data)}`;
30
- }
31
- if (e.message) return e.message;
32
- return String(error);
33
- }
34
- var XApiClient = class {
35
- client;
36
- userId = null;
37
- creds;
38
- constructor() {
39
- this.creds = loadCredentials();
40
- if (this.creds.method !== "api") {
41
- throw new Error("API client requires API credentials. Current method: browser");
42
- }
43
- this.client = new TwitterApi({
44
- appKey: this.creds.apiKey,
45
- appSecret: this.creds.apiSecret,
46
- accessToken: this.creds.accessToken,
47
- accessSecret: this.creds.accessTokenSecret
48
- });
49
- }
50
- async getUserId() {
51
- if (this.userId) return this.userId;
52
- const accessToken = this.creds.accessToken;
53
- if (accessToken) {
54
- const dashIdx = accessToken.indexOf("-");
55
- if (dashIdx > 0) {
56
- const candidate = accessToken.substring(0, dashIdx);
57
- if (/^\d+$/.test(candidate)) {
58
- logger.info(`User ID extracted from access token: ${candidate}`);
59
- this.userId = candidate;
60
- return this.userId;
61
- }
62
- }
63
- }
64
- try {
65
- const me = await this.client.v2.me();
66
- if (me.data) {
67
- logger.info(`User ID from /me endpoint: ${me.data.id} (@${me.data.username})`);
68
- this.userId = me.data.id;
69
- return this.userId;
70
- }
71
- } catch (err) {
72
- logger.warn(`/me endpoint failed: ${extractTwitterError(err)}`);
73
- }
74
- const handle = this.getHandle();
75
- logger.info(`Trying userByUsername lookup for: ${handle}`);
76
- const result = await this.client.v2.userByUsername(handle);
77
- if (!result.data) {
78
- const errInfo = result.errors;
79
- throw new Error(
80
- `Could not find user @${handle}. API returned: ${JSON.stringify(errInfo ?? "no data")}`
81
- );
82
- }
83
- this.userId = result.data.id;
84
- return this.userId;
85
- }
86
- getHandle() {
87
- let handle;
88
- if (identityExists()) {
89
- handle = loadIdentity().handle;
90
- } else {
91
- handle = this.creds.username;
92
- }
93
- if (!handle) throw new Error("No handle found. Create a Spore identity first.");
94
- return handle.replace(/^@/, "");
95
- }
96
- async postTweet(content) {
97
- if (!rateLimiter.canPost()) {
98
- return { success: false, error: "Monthly post limit reached" };
99
- }
100
- try {
101
- const result = await this.client.v2.tweet(content);
102
- rateLimiter.consume();
103
- logInteraction({
104
- id: `int-${Date.now()}`,
105
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
106
- type: "post",
107
- tweetId: result.data.id,
108
- content,
109
- creditsUsed: 1,
110
- success: true
111
- });
112
- return { success: true, tweetId: result.data.id };
113
- } catch (error) {
114
- const detail = extractTwitterError(error);
115
- logger.error(`Failed to post tweet: ${detail}`);
116
- return { success: false, error: detail };
117
- }
118
- }
119
- async replyToTweet(tweetId, content) {
120
- if (!rateLimiter.canPost()) {
121
- return { success: false, error: "Monthly post limit reached" };
122
- }
123
- try {
124
- const result = await this.client.v2.reply(content, tweetId);
125
- rateLimiter.consume();
126
- logInteraction({
127
- id: `int-${Date.now()}`,
128
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
129
- type: "reply",
130
- tweetId: result.data.id,
131
- inReplyTo: tweetId,
132
- content,
133
- creditsUsed: 1,
134
- success: true
135
- });
136
- return { success: true, tweetId: result.data.id };
137
- } catch (error) {
138
- const detail = extractTwitterError(error);
139
- logger.error(`Failed to reply: ${detail}`);
140
- return { success: false, error: detail };
141
- }
142
- }
143
- async deleteTweet(tweetId) {
144
- try {
145
- await this.client.v2.deleteTweet(tweetId);
146
- return { success: true, tweetId };
147
- } catch (error) {
148
- return { success: false, error: error.message };
149
- }
150
- }
151
- async likeTweet(tweetId) {
152
- try {
153
- rateLimiter.requireBasicTier("Like");
154
- const userId = await this.getUserId();
155
- await this.client.v2.like(userId, tweetId);
156
- logInteraction({
157
- id: `int-${Date.now()}`,
158
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
159
- type: "like",
160
- tweetId,
161
- creditsUsed: 0,
162
- success: true
163
- });
164
- return { success: true, tweetId };
165
- } catch (error) {
166
- return { success: false, error: error.message };
167
- }
168
- }
169
- async unlikeTweet(tweetId) {
170
- try {
171
- rateLimiter.requireBasicTier("Unlike");
172
- const userId = await this.getUserId();
173
- await this.client.v2.unlike(userId, tweetId);
174
- return { success: true, tweetId };
175
- } catch (error) {
176
- return { success: false, error: error.message };
177
- }
178
- }
179
- async retweet(tweetId) {
180
- try {
181
- rateLimiter.requireBasicTier("Retweet");
182
- const userId = await this.getUserId();
183
- await this.client.v2.retweet(userId, tweetId);
184
- logInteraction({
185
- id: `int-${Date.now()}`,
186
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
187
- type: "retweet",
188
- tweetId,
189
- creditsUsed: 0,
190
- success: true
191
- });
192
- return { success: true, tweetId };
193
- } catch (error) {
194
- return { success: false, error: error.message };
195
- }
196
- }
197
- async unretweet(tweetId) {
198
- try {
199
- rateLimiter.requireBasicTier("Unretweet");
200
- const userId = await this.getUserId();
201
- await this.client.v2.unretweet(userId, tweetId);
202
- return { success: true, tweetId };
203
- } catch (error) {
204
- return { success: false, error: error.message };
205
- }
206
- }
207
- async followUser(userId) {
208
- try {
209
- rateLimiter.requireBasicTier("Follow");
210
- const myId = await this.getUserId();
211
- await this.client.v2.follow(myId, userId);
212
- logInteraction({
213
- id: `int-${Date.now()}`,
214
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
215
- type: "follow",
216
- targetUserId: userId,
217
- creditsUsed: 0,
218
- success: true
219
- });
220
- return { success: true };
221
- } catch (error) {
222
- return { success: false, error: error.message };
223
- }
224
- }
225
- async unfollowUser(userId) {
226
- try {
227
- rateLimiter.requireBasicTier("Unfollow");
228
- const myId = await this.getUserId();
229
- await this.client.v2.unfollow(myId, userId);
230
- return { success: true };
231
- } catch (error) {
232
- return { success: false, error: error.message };
233
- }
234
- }
235
- async getTimeline(options) {
236
- try {
237
- rateLimiter.requireBasicTier("Read timeline");
238
- const userId = await this.getUserId();
239
- const result = await this.client.v2.homeTimeline({
240
- max_results: options?.count ?? 20,
241
- "tweet.fields": ["created_at", "public_metrics", "in_reply_to_user_id"],
242
- expansions: ["author_id"],
243
- "user.fields": ["username"],
244
- ...options?.sinceId ? { since_id: options.sinceId } : {}
245
- });
246
- if (!result.data?.data) return [];
247
- const userMap = /* @__PURE__ */ new Map();
248
- for (const user of result.includes?.users ?? []) {
249
- userMap.set(user.id, user.username);
250
- }
251
- return result.data.data.map((tweet) => ({
252
- id: tweet.id,
253
- text: tweet.text,
254
- authorId: tweet.author_id ?? "unknown",
255
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
256
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
257
- likeCount: tweet.public_metrics?.like_count,
258
- retweetCount: tweet.public_metrics?.retweet_count,
259
- replyCount: tweet.public_metrics?.reply_count
260
- }));
261
- } catch (error) {
262
- const detail = extractTwitterError(error);
263
- logger.error(`Failed to read timeline: ${detail}`);
264
- return [];
265
- }
266
- }
267
- async getMentions(options) {
268
- try {
269
- rateLimiter.requireBasicTier("Read mentions");
270
- const userId = await this.getUserId();
271
- const result = await this.client.v2.userMentionTimeline(userId, {
272
- max_results: options?.count ?? 20,
273
- "tweet.fields": ["created_at", "public_metrics"],
274
- expansions: ["author_id"],
275
- "user.fields": ["username"],
276
- ...options?.sinceId ? { since_id: options.sinceId } : {}
277
- });
278
- if (!result.data?.data) return [];
279
- const userMap = /* @__PURE__ */ new Map();
280
- for (const user of result.includes?.users ?? []) {
281
- userMap.set(user.id, user.username);
282
- }
283
- return result.data.data.map((tweet) => ({
284
- id: tweet.id,
285
- text: tweet.text,
286
- authorId: tweet.author_id ?? "unknown",
287
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
288
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
289
- likeCount: tweet.public_metrics?.like_count,
290
- retweetCount: tweet.public_metrics?.retweet_count,
291
- replyCount: tweet.public_metrics?.reply_count
292
- }));
293
- } catch (error) {
294
- const detail = extractTwitterError(error);
295
- logger.error(`Failed to read mentions: ${detail}`);
296
- return [];
297
- }
298
- }
299
- async getUserTweets(userId, options) {
300
- try {
301
- rateLimiter.requireBasicTier("Read user tweets");
302
- const result = await this.client.v2.userTimeline(userId, {
303
- max_results: options?.count ?? 10,
304
- "tweet.fields": ["created_at", "public_metrics"],
305
- expansions: ["author_id"],
306
- "user.fields": ["username"],
307
- ...options?.sinceId ? { since_id: options.sinceId } : {}
308
- });
309
- if (!result.data?.data) return [];
310
- const userMap = /* @__PURE__ */ new Map();
311
- for (const user of result.includes?.users ?? []) {
312
- userMap.set(user.id, user.username);
313
- }
314
- return result.data.data.map((tweet) => ({
315
- id: tweet.id,
316
- text: tweet.text,
317
- authorId: tweet.author_id ?? "unknown",
318
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
319
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
320
- likeCount: tweet.public_metrics?.like_count,
321
- retweetCount: tweet.public_metrics?.retweet_count,
322
- replyCount: tweet.public_metrics?.reply_count
323
- }));
324
- } catch (error) {
325
- const detail = extractTwitterError(error);
326
- logger.error(`Failed to read user tweets for ${userId}: ${detail}`);
327
- return [];
328
- }
329
- }
330
- async searchTweets(query, options) {
331
- try {
332
- rateLimiter.requireBasicTier("Search tweets");
333
- const result = await this.client.v2.search(query, {
334
- max_results: options?.count ?? 20,
335
- "tweet.fields": ["created_at", "public_metrics"],
336
- expansions: ["author_id"],
337
- "user.fields": ["username"]
338
- });
339
- if (!result.data?.data) return [];
340
- const userMap = /* @__PURE__ */ new Map();
341
- for (const user of result.includes?.users ?? []) {
342
- userMap.set(user.id, user.username);
343
- }
344
- return result.data.data.map((tweet) => ({
345
- id: tweet.id,
346
- text: tweet.text,
347
- authorId: tweet.author_id ?? "unknown",
348
- authorHandle: userMap.get(tweet.author_id ?? "") ?? "unknown",
349
- createdAt: tweet.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
350
- likeCount: tweet.public_metrics?.like_count,
351
- retweetCount: tweet.public_metrics?.retweet_count,
352
- replyCount: tweet.public_metrics?.reply_count
353
- }));
354
- } catch (error) {
355
- const detail = extractTwitterError(error);
356
- logger.error(`Failed to search tweets: ${detail}`);
357
- return [];
358
- }
359
- }
360
- async getProfile(handle) {
361
- const result = await this.client.v2.userByUsername(handle, {
362
- "user.fields": ["description", "public_metrics", "verified", "profile_image_url"]
363
- });
364
- if (!result.data) throw new Error(`User not found: @${handle}`);
365
- return {
366
- id: result.data.id,
367
- handle: result.data.username,
368
- name: result.data.name,
369
- bio: result.data.description ?? "",
370
- followersCount: result.data.public_metrics?.followers_count ?? 0,
371
- followingCount: result.data.public_metrics?.following_count ?? 0,
372
- tweetCount: result.data.public_metrics?.tweet_count ?? 0,
373
- verified: result.data.verified ?? false,
374
- profileImageUrl: result.data.profile_image_url
375
- };
376
- }
377
- };
378
- export {
379
- XApiClient
380
- };
381
- //# sourceMappingURL=client-B6NGVRHM.js.map