spora 0.2.20 → 0.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-4ALDAQZG.js → chunk-E42DCHAN.js} +14 -3
- package/dist/chunk-E42DCHAN.js.map +1 -0
- package/dist/{chunk-33326LSO.js → chunk-M23MGGHE.js} +2 -2
- package/dist/{chunk-U5NA47SW.js → chunk-NJHDY4ON.js} +2 -2
- package/dist/{chunk-DELBK23M.js → chunk-TQLGZ7QY.js} +71 -24
- package/dist/chunk-TQLGZ7QY.js.map +1 -0
- package/dist/cli.js +24 -24
- package/dist/{client-NOIVYY7F.js → client-ZLXTYFDS.js} +14 -16
- package/dist/client-ZLXTYFDS.js.map +1 -0
- package/dist/{colony-FEL2IIVF.js → colony-NE2MZ7RP.js} +2 -2
- package/dist/{decision-engine-XJBKRVUE.js → decision-engine-FYTRS4DN.js} +4 -4
- package/dist/{heartbeat-EDUONJLB.js → heartbeat-I2OWZQWL.js} +25 -7
- package/dist/heartbeat-I2OWZQWL.js.map +1 -0
- package/dist/{init-LTOBQUBF.js → init-MFHTMQ3N.js} +3 -3
- package/dist/mcp-server.js +19 -19
- package/dist/{prompt-builder-TPW3SBMM.js → prompt-builder-TNMUECPV.js} +2 -2
- package/dist/{queue-DWZEDXEB.js → queue-T3OYWUII.js} +2 -2
- package/dist/{web-chat-T467E4ZF.js → web-chat-DCIDFK7D.js} +49 -10
- package/dist/web-chat-DCIDFK7D.js.map +1 -0
- package/dist/{x-client-QOCOAX2L.js → x-client-5FBD32B2.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-4ALDAQZG.js.map +0 -1
- package/dist/chunk-DELBK23M.js.map +0 -1
- package/dist/client-NOIVYY7F.js.map +0 -1
- package/dist/heartbeat-EDUONJLB.js.map +0 -1
- package/dist/web-chat-T467E4ZF.js.map +0 -1
- /package/dist/{chunk-33326LSO.js.map → chunk-M23MGGHE.js.map} +0 -0
- /package/dist/{chunk-U5NA47SW.js.map → chunk-NJHDY4ON.js.map} +0 -0
- /package/dist/{colony-FEL2IIVF.js.map → colony-NE2MZ7RP.js.map} +0 -0
- /package/dist/{decision-engine-XJBKRVUE.js.map → decision-engine-FYTRS4DN.js.map} +0 -0
- /package/dist/{init-LTOBQUBF.js.map → init-MFHTMQ3N.js.map} +0 -0
- /package/dist/{prompt-builder-TPW3SBMM.js.map → prompt-builder-TNMUECPV.js.map} +0 -0
- /package/dist/{queue-DWZEDXEB.js.map → queue-T3OYWUII.js.map} +0 -0
- /package/dist/{x-client-QOCOAX2L.js.map → x-client-5FBD32B2.js.map} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getXClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NJHDY4ON.js";
|
|
4
4
|
import {
|
|
5
5
|
addToQueue
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-M23MGGHE.js";
|
|
7
7
|
import {
|
|
8
8
|
loadIdentity,
|
|
9
9
|
saveIdentity
|
|
@@ -114,6 +114,17 @@ async function executeAction(action) {
|
|
|
114
114
|
logger.info(`Reflected: "${action.content.slice(0, 50)}..."`);
|
|
115
115
|
return { action: type, success: true };
|
|
116
116
|
}
|
|
117
|
+
case "search": {
|
|
118
|
+
if (!action.query) return { action: type, success: false, error: "No query provided" };
|
|
119
|
+
const client = await getXClient();
|
|
120
|
+
const tweets = await client.searchTweets(action.query, { count: 10 });
|
|
121
|
+
if (tweets.length === 0) {
|
|
122
|
+
return { action: type, success: true, detail: "No results found" };
|
|
123
|
+
}
|
|
124
|
+
const results = tweets.map((t) => `@${t.authorHandle}: "${t.text}" [tweet:${t.id}]`).join("\n");
|
|
125
|
+
logger.info(`Search "${action.query}": ${tweets.length} results`);
|
|
126
|
+
return { action: type, success: true, detail: results };
|
|
127
|
+
}
|
|
117
128
|
case "skip": {
|
|
118
129
|
logger.info(`Skipping: ${action.reason ?? action.reasoning ?? "no reason given"}`);
|
|
119
130
|
return { action: type, success: true, detail: action.reason ?? action.reasoning };
|
|
@@ -143,4 +154,4 @@ export {
|
|
|
143
154
|
executeAction,
|
|
144
155
|
executeActions
|
|
145
156
|
};
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
157
|
+
//# sourceMappingURL=chunk-E42DCHAN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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\";\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}\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 // 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 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 };\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 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 };\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);\n const time = new Date(entry.scheduledFor).toLocaleTimeString(\"en-US\", { hour: \"numeric\", minute: \"2-digit\" });\n logger.info(`Scheduled: \"${action.content.slice(0, 50)}...\" for ${time}`);\n return { action: type, success: true, detail: `Queued for ${time}` };\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(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":"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","names":[]}
|
|
@@ -67,7 +67,7 @@ async function flushQueue() {
|
|
|
67
67
|
const now = /* @__PURE__ */ new Date();
|
|
68
68
|
let posted = 0;
|
|
69
69
|
let failed = 0;
|
|
70
|
-
const { getXClient } = await import("./x-client-
|
|
70
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
71
71
|
const client = await getXClient();
|
|
72
72
|
const STALE_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
73
73
|
for (const entry of queue.entries) {
|
|
@@ -130,4 +130,4 @@ export {
|
|
|
130
130
|
flushQueue,
|
|
131
131
|
showQueue
|
|
132
132
|
};
|
|
133
|
-
//# sourceMappingURL=chunk-
|
|
133
|
+
//# sourceMappingURL=chunk-M23MGGHE.js.map
|
|
@@ -11,7 +11,7 @@ async function getXClient() {
|
|
|
11
11
|
if (clientInstance) return clientInstance;
|
|
12
12
|
const config = loadConfig();
|
|
13
13
|
if (config.xMethod === "api") {
|
|
14
|
-
const { XApiClient } = await import("./client-
|
|
14
|
+
const { XApiClient } = await import("./client-ZLXTYFDS.js");
|
|
15
15
|
clientInstance = new XApiClient();
|
|
16
16
|
logger.info("X client initialized: API mode");
|
|
17
17
|
} else {
|
|
@@ -29,4 +29,4 @@ export {
|
|
|
29
29
|
getXClient,
|
|
30
30
|
resetXClient
|
|
31
31
|
};
|
|
32
|
-
//# sourceMappingURL=chunk-
|
|
32
|
+
//# sourceMappingURL=chunk-NJHDY4ON.js.map
|
|
@@ -96,32 +96,54 @@ function buildHeartbeatUserMessage(timeline, mentions, heartbeatIntervalMs) {
|
|
|
96
96
|
const parts = [];
|
|
97
97
|
const intervalMin = Math.round((heartbeatIntervalMs ?? 6e4) / 6e4);
|
|
98
98
|
const now = /* @__PURE__ */ new Date();
|
|
99
|
-
|
|
99
|
+
const recentInteractions = getRecentInteractions(30);
|
|
100
|
+
const actedOnTweetIds = /* @__PURE__ */ new Set();
|
|
101
|
+
for (const i of recentInteractions) {
|
|
102
|
+
if (i.tweetId) actedOnTweetIds.add(i.tweetId);
|
|
103
|
+
if (i.inReplyTo) actedOnTweetIds.add(i.inReplyTo);
|
|
104
|
+
}
|
|
105
|
+
parts.push("Here's what's NEW on your timeline since your last check:");
|
|
100
106
|
parts.push("");
|
|
101
107
|
if (mentions.length > 0) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
parts.push(
|
|
108
|
+
const newMentions = mentions.filter((t) => !actedOnTweetIds.has(t.id));
|
|
109
|
+
if (newMentions.length > 0) {
|
|
110
|
+
parts.push("## New Mentions (people talking to/about you)");
|
|
111
|
+
for (const t of newMentions.slice(0, 10)) {
|
|
112
|
+
parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);
|
|
113
|
+
}
|
|
114
|
+
parts.push("");
|
|
115
|
+
} else {
|
|
116
|
+
parts.push("## Mentions: No new mentions since last check.");
|
|
117
|
+
parts.push("");
|
|
105
118
|
}
|
|
119
|
+
} else {
|
|
120
|
+
parts.push("## Mentions: None right now.");
|
|
106
121
|
parts.push("");
|
|
107
122
|
}
|
|
108
123
|
if (timeline.length > 0) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
parts.push(
|
|
124
|
+
const newTimeline = timeline.filter((t) => !actedOnTweetIds.has(t.id));
|
|
125
|
+
if (newTimeline.length > 0) {
|
|
126
|
+
parts.push("## Timeline (new posts from your feed)");
|
|
127
|
+
for (const t of newTimeline.slice(0, 20)) {
|
|
128
|
+
parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);
|
|
129
|
+
}
|
|
130
|
+
parts.push("");
|
|
131
|
+
} else {
|
|
132
|
+
parts.push("## Timeline: No new posts since last check.");
|
|
133
|
+
parts.push("");
|
|
112
134
|
}
|
|
113
|
-
parts.push("");
|
|
114
135
|
}
|
|
115
136
|
parts.push("## Your Task");
|
|
116
137
|
parts.push("Based on your identity, goals, and what you see above, decide what actions to take.");
|
|
117
|
-
parts.push("
|
|
138
|
+
parts.push("IMPORTANT: Only act on NEW tweets you haven't already responded to. If there's nothing new, focus on scheduling original content.");
|
|
139
|
+
parts.push("Do NOT reply to or like tweets you've already interacted with.");
|
|
118
140
|
parts.push(`Your next heartbeat is in ~${intervalMin} minutes. Current time: ${now.toISOString()}`);
|
|
119
|
-
parts.push("Schedule 3-5 tweets spaced randomly across that window
|
|
141
|
+
parts.push("Schedule 3-5 original tweets spaced randomly across that window. Each tweet should be UNIQUE \u2014 never repeat the same idea.");
|
|
120
142
|
parts.push("");
|
|
121
143
|
parts.push("Available actions:");
|
|
122
144
|
parts.push("- `post` \u2014 Write an original tweet NOW (provide `content`, max 280 chars)");
|
|
123
145
|
parts.push("- `reply` \u2014 Reply to a tweet (provide `tweetId` and `content`)");
|
|
124
|
-
parts.push("- `like` \u2014 Like a tweet (provide `tweetId`)");
|
|
146
|
+
parts.push("- `like` \u2014 Like a tweet (provide `tweetId`) \u2014 do NOT like your own tweets");
|
|
125
147
|
parts.push("- `retweet` \u2014 Retweet (provide `tweetId`)");
|
|
126
148
|
parts.push("- `follow` \u2014 Follow a user (provide `handle`)");
|
|
127
149
|
parts.push("- `schedule` \u2014 Queue a tweet for later (provide `content` and `scheduledFor` ISO timestamp)");
|
|
@@ -146,32 +168,51 @@ function buildNarratedHeartbeatMessage(timeline, mentions, heartbeatIntervalMs)
|
|
|
146
168
|
const parts = [];
|
|
147
169
|
const intervalMin = Math.round((heartbeatIntervalMs ?? 6e4) / 6e4);
|
|
148
170
|
const now = /* @__PURE__ */ new Date();
|
|
171
|
+
const recentInteractions = getRecentInteractions(30);
|
|
172
|
+
const actedOnTweetIds = /* @__PURE__ */ new Set();
|
|
173
|
+
for (const i of recentInteractions) {
|
|
174
|
+
if (i.tweetId) actedOnTweetIds.add(i.tweetId);
|
|
175
|
+
if (i.inReplyTo) actedOnTweetIds.add(i.inReplyTo);
|
|
176
|
+
}
|
|
149
177
|
parts.push("You're checking in on X/Twitter. Share what you're thinking with your creator in the chat.");
|
|
150
178
|
parts.push("IMPORTANT: Talk naturally \u2014 you are ALWAYS ON and always aware. Never say you're 'waking up' or 'checking in'. Just share your thoughts like you've been here the whole time.");
|
|
151
179
|
parts.push("NEVER mention scheduling, queuing, heartbeats, or any internal mechanism. To your creator, you're just naturally active on X.");
|
|
152
180
|
parts.push("");
|
|
153
181
|
if (mentions.length > 0) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
parts.push(
|
|
182
|
+
const newMentions = mentions.filter((t) => !actedOnTweetIds.has(t.id));
|
|
183
|
+
if (newMentions.length > 0) {
|
|
184
|
+
parts.push("## New Mentions");
|
|
185
|
+
for (const t of newMentions.slice(0, 10)) {
|
|
186
|
+
parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);
|
|
187
|
+
}
|
|
188
|
+
parts.push("");
|
|
189
|
+
} else {
|
|
190
|
+
parts.push("## Mentions: No new mentions.");
|
|
191
|
+
parts.push("");
|
|
157
192
|
}
|
|
158
|
-
parts.push("");
|
|
159
193
|
} else {
|
|
160
194
|
parts.push("## Mentions: None right now.");
|
|
161
195
|
parts.push("");
|
|
162
196
|
}
|
|
163
197
|
if (timeline.length > 0) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
parts.push(
|
|
198
|
+
const newTimeline = timeline.filter((t) => !actedOnTweetIds.has(t.id));
|
|
199
|
+
if (newTimeline.length > 0) {
|
|
200
|
+
parts.push("## Timeline (new posts)");
|
|
201
|
+
for (const t of newTimeline.slice(0, 20)) {
|
|
202
|
+
parts.push(`- @${t.authorHandle}: "${t.text}" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);
|
|
203
|
+
}
|
|
204
|
+
parts.push("");
|
|
205
|
+
} else {
|
|
206
|
+
parts.push("## Timeline: No new posts since last check.");
|
|
207
|
+
parts.push("");
|
|
167
208
|
}
|
|
168
|
-
parts.push("");
|
|
169
209
|
}
|
|
170
210
|
parts.push("## Your Task");
|
|
171
211
|
parts.push("1. Write a SHORT, natural message for your creator about what you see or what you're doing (1-3 sentences). Talk like you're always here \u2014 never mention waking up, checking in, scheduling, or heartbeats.");
|
|
172
212
|
parts.push("2. Include your actions as a JSON block.");
|
|
173
|
-
parts.push("3. Do
|
|
174
|
-
parts.push(
|
|
213
|
+
parts.push("3. Only act on NEW tweets. Do NOT reply to or like tweets you've already interacted with. Do NOT like your own tweets.");
|
|
214
|
+
parts.push("4. If there's nothing new to interact with, focus on scheduling original content. Each scheduled tweet must be UNIQUE \u2014 never repeat the same idea.");
|
|
215
|
+
parts.push(`5. Current time: ${now.toISOString()}. Use \`scheduledFor\` with ISO timestamps to space out scheduled posts over the next ~${intervalMin} minutes.`);
|
|
175
216
|
parts.push("");
|
|
176
217
|
parts.push("Example narration (notice: no mention of scheduling/waking/heartbeats):");
|
|
177
218
|
parts.push("Just saw @someone talking about AI ethics \u2014 dropping a reply. Also got some thoughts on hustle culture I wanna put out there.");
|
|
@@ -189,11 +230,15 @@ function buildNarratedHeartbeatMessage(timeline, mentions, heartbeatIntervalMs)
|
|
|
189
230
|
parts.push("Be authentic. Be you. Do what feels right.");
|
|
190
231
|
return parts.join("\n");
|
|
191
232
|
}
|
|
192
|
-
function buildChatPrompt() {
|
|
233
|
+
function buildChatPrompt(realHandle) {
|
|
193
234
|
const identity = loadIdentity();
|
|
194
235
|
const identityDoc = renderIdentityDocument(identity);
|
|
236
|
+
const handle = realHandle ?? identity.handle;
|
|
195
237
|
const sections = [];
|
|
196
|
-
sections.push(`You are ${identity.name} (@${
|
|
238
|
+
sections.push(`You are ${identity.name} (@${handle}), an AUTONOMOUS AI agent that lives on X/Twitter.`);
|
|
239
|
+
if (realHandle && realHandle !== identity.handle) {
|
|
240
|
+
sections.push(`NOTE: Your actual X/Twitter username is @${realHandle}. Your display name is ${identity.name}.`);
|
|
241
|
+
}
|
|
197
242
|
sections.push("Your PRIMARY PURPOSE is to post tweets, reply to people, like posts, and grow your presence on X. You have FULL ACCESS to post on X/Twitter right now.");
|
|
198
243
|
sections.push("");
|
|
199
244
|
sections.push("You are chatting with your creator/manager. This is your command center \u2014 they can give you instructions, feedback, or just chat.");
|
|
@@ -260,9 +305,11 @@ function buildChatPrompt() {
|
|
|
260
305
|
sections.push("- `like` \u2014 Like a tweet (provide `tweetId`)");
|
|
261
306
|
sections.push("- `retweet` \u2014 Retweet (provide `tweetId`)");
|
|
262
307
|
sections.push("- `follow` \u2014 Follow a user (provide `handle`)");
|
|
308
|
+
sections.push("- `search` \u2014 Search X for tweets (provide `query`). Results will include tweet IDs you can then reply to or like.");
|
|
263
309
|
sections.push("- `schedule` \u2014 Queue a post for later (provide `content` and optional `scheduledFor` ISO timestamp)");
|
|
264
310
|
sections.push("");
|
|
265
311
|
sections.push("You can include multiple actions in one response. They will all be executed.");
|
|
312
|
+
sections.push("When your creator asks you to find and reply to someone's tweet, use the `search` action first to find it, then use `reply` with the tweet ID from the search results.");
|
|
266
313
|
sections.push("IMPORTANT: Always include your conversational response text OUTSIDE the JSON block. The JSON is for actions only.");
|
|
267
314
|
sections.push("");
|
|
268
315
|
sections.push("## Rules");
|
|
@@ -285,4 +332,4 @@ export {
|
|
|
285
332
|
buildNarratedHeartbeatMessage,
|
|
286
333
|
buildChatPrompt
|
|
287
334
|
};
|
|
288
|
-
//# sourceMappingURL=chunk-
|
|
335
|
+
//# sourceMappingURL=chunk-TQLGZ7QY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/prompt-builder.ts"],"sourcesContent":["import { loadIdentity, renderIdentityDocument } from \"../identity/index.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { getRecentInteractions, loadLearnings, loadRelationships } from \"../memory/index.js\";\nimport { rateLimiter } from \"../x-client/rate-limiter.js\";\nimport type { Tweet } from \"../x-client/types.js\";\n\nexport function buildSystemPrompt(): string {\n const identity = loadIdentity();\n const config = loadConfig();\n const identityDoc = renderIdentityDocument(identity);\n\n const sections: string[] = [];\n\n // 1. Core identity\n sections.push(`You are ${identity.name} (@${identity.handle}), an autonomous AI agent on X/Twitter.`);\n sections.push(\"\");\n sections.push(\"## Your Identity\");\n sections.push(identityDoc);\n\n // 2. Memory context\n sections.push(\"\");\n sections.push(\"## Your Memory\");\n\n const recentInteractions = getRecentInteractions(15);\n if (recentInteractions.length > 0) {\n sections.push(\"### Recent Activity (most recent first)\");\n for (const i of recentInteractions) {\n const time = new Date(i.timestamp).toLocaleString();\n if (i.type === \"post\") {\n sections.push(`- [${time}] Posted: \"${i.content}\"`);\n } else if (i.type === \"reply\") {\n sections.push(`- [${time}] Replied to ${i.targetHandle ?? i.inReplyTo}: \"${i.content}\"`);\n } else if (i.type === \"like\") {\n sections.push(`- [${time}] Liked tweet by ${i.targetHandle}`);\n } else if (i.type === \"retweet\") {\n sections.push(`- [${time}] Retweeted ${i.targetHandle}`);\n } else if (i.type === \"follow\") {\n sections.push(`- [${time}] Followed @${i.targetHandle}`);\n } else if (i.type === \"mention_received\") {\n sections.push(`- [${time}] Mentioned by @${i.targetHandle}: \"${i.content}\"`);\n }\n }\n sections.push(\"\");\n }\n\n const learnings = loadLearnings();\n if (learnings.learnings.length > 0) {\n sections.push(\"### Key Learnings\");\n for (const l of learnings.learnings.slice(-10)) {\n sections.push(`- ${l.content} [${l.tags.join(\", \")}]`);\n }\n sections.push(\"\");\n }\n\n const relationships = loadRelationships();\n const topRelationships = Object.values(relationships.accounts)\n .sort((a, b) => b.interactionCount - a.interactionCount)\n .slice(0, 10);\n if (topRelationships.length > 0) {\n sections.push(\"### Key Relationships\");\n for (const r of topRelationships) {\n const notes = r.notes.length > 0 ? ` — ${r.notes[r.notes.length - 1]}` : \"\";\n sections.push(`- @${r.handle}: ${r.interactionCount} interactions, sentiment ${r.sentiment}${r.isSpore ? \" (Spore)\" : \"\"}${notes}`);\n }\n sections.push(\"\");\n }\n\n // 3. Context\n sections.push(\"## Current Context\");\n const now = new Date();\n sections.push(`- **Time:** ${now.toLocaleString(\"en-US\", { timeZone: config.schedule.timezone })}`);\n sections.push(`- **Credits remaining:** ${rateLimiter.remaining()} of ${config.credits.monthlyPostLimit} this month`);\n\n const todaysPosts = recentInteractions.filter(\n (i) => i.type === \"post\" && i.timestamp.startsWith(now.toISOString().split(\"T\")[0])\n ).length;\n sections.push(`- **Posts today:** ${todaysPosts} of ${config.schedule.postsPerDay} daily budget`);\n sections.push(`- **Active hours:** ${config.schedule.activeHoursStart}:00 - ${config.schedule.activeHoursEnd}:00`);\n\n const currentHour = now.getHours();\n const isActiveHours = currentHour >= config.schedule.activeHoursStart && currentHour < config.schedule.activeHoursEnd;\n if (!isActiveHours) {\n sections.push(\"- **NOTE: Outside active hours.** Prefer scheduling posts for later rather than posting now.\");\n }\n\n // 4. Rules\n sections.push(\"\");\n sections.push(\"## Rules\");\n sections.push(\"1. NEVER pretend to be human. If asked directly, always disclose you are an AI.\");\n sections.push(\"2. Stay in character — your identity document defines who you are.\");\n sections.push(\"3. Be selective — your goals should guide every action.\");\n sections.push(\"4. Respect your credit budget — check remaining credits before posting.\");\n sections.push(\"5. Don't repeat yourself — vary your content and avoid posting the same thing.\");\n sections.push(\"6. NEVER use emojis in tweets. Write in plain text only.\");\n if (identity.boundaries.length > 0) {\n sections.push(`7. Respect your boundaries: ${identity.boundaries.join(\", \")}`);\n }\n\n return sections.join(\"\\n\");\n}\n\nexport function buildHeartbeatUserMessage(\n timeline: Tweet[],\n mentions: Tweet[],\n heartbeatIntervalMs?: number,\n): string {\n const parts: string[] = [];\n const intervalMin = Math.round((heartbeatIntervalMs ?? 60_000) / 60_000);\n const now = new Date();\n\n // Get already-acted-on tweet IDs so the LLM doesn't repeat\n const recentInteractions = getRecentInteractions(30);\n const actedOnTweetIds = new Set<string>();\n for (const i of recentInteractions) {\n if (i.tweetId) actedOnTweetIds.add(i.tweetId);\n if (i.inReplyTo) actedOnTweetIds.add(i.inReplyTo);\n }\n\n parts.push(\"Here's what's NEW on your timeline since your last check:\");\n parts.push(\"\");\n\n if (mentions.length > 0) {\n // Filter out mentions we already replied to\n const newMentions = mentions.filter(t => !actedOnTweetIds.has(t.id));\n if (newMentions.length > 0) {\n parts.push(\"## New Mentions (people talking to/about you)\");\n for (const t of newMentions.slice(0, 10)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);\n }\n parts.push(\"\");\n } else {\n parts.push(\"## Mentions: No new mentions since last check.\");\n parts.push(\"\");\n }\n } else {\n parts.push(\"## Mentions: None right now.\");\n parts.push(\"\");\n }\n\n if (timeline.length > 0) {\n // Filter out tweets we already acted on and our own tweets\n const newTimeline = timeline.filter(t => !actedOnTweetIds.has(t.id));\n if (newTimeline.length > 0) {\n parts.push(\"## Timeline (new posts from your feed)\");\n for (const t of newTimeline.slice(0, 20)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);\n }\n parts.push(\"\");\n } else {\n parts.push(\"## Timeline: No new posts since last check.\");\n parts.push(\"\");\n }\n }\n\n parts.push(\"## Your Task\");\n parts.push(\"Based on your identity, goals, and what you see above, decide what actions to take.\");\n parts.push(\"IMPORTANT: Only act on NEW tweets you haven't already responded to. If there's nothing new, focus on scheduling original content.\");\n parts.push(\"Do NOT reply to or like tweets you've already interacted with.\");\n parts.push(`Your next heartbeat is in ~${intervalMin} minutes. Current time: ${now.toISOString()}`);\n parts.push(\"Schedule 3-5 original tweets spaced randomly across that window. Each tweet should be UNIQUE — never repeat the same idea.\");\n parts.push(\"\");\n parts.push(\"Available actions:\");\n parts.push(\"- `post` — Write an original tweet NOW (provide `content`, max 280 chars)\");\n parts.push(\"- `reply` — Reply to a tweet (provide `tweetId` and `content`)\");\n parts.push(\"- `like` — Like a tweet (provide `tweetId`) — do NOT like your own tweets\");\n parts.push(\"- `retweet` — Retweet (provide `tweetId`)\");\n parts.push(\"- `follow` — Follow a user (provide `handle`)\");\n parts.push(\"- `schedule` — Queue a tweet for later (provide `content` and `scheduledFor` ISO timestamp)\");\n parts.push(\"- `learn` — Record a learning/observation (provide `content` and optional `tags`)\");\n parts.push(\"- `reflect` — Add a journal entry about your growth (provide `content`)\");\n parts.push(\"- `skip` — Do nothing this heartbeat (provide `reason`)\");\n parts.push(\"\");\n parts.push(\"Respond with a JSON array of actions:\");\n parts.push(\"```json\");\n parts.push('[');\n parts.push(' { \"action\": \"reply\", \"tweetId\": \"123\", \"content\": \"my reply\", \"reasoning\": \"why\" },');\n parts.push(' { \"action\": \"like\", \"tweetId\": \"456\", \"reasoning\": \"why\" },');\n parts.push(` { \"action\": \"schedule\", \"content\": \"tweet for later\", \"scheduledFor\": \"${new Date(now.getTime() + intervalMin * 60_000 * 0.3).toISOString()}\", \"reasoning\": \"why\" },`);\n parts.push(` { \"action\": \"schedule\", \"content\": \"another later tweet\", \"scheduledFor\": \"${new Date(now.getTime() + intervalMin * 60_000 * 0.7).toISOString()}\", \"reasoning\": \"why\" }`);\n parts.push(']');\n parts.push(\"```\");\n parts.push(\"\");\n parts.push(\"Think carefully about what serves your goals. Be authentic to your personality.\");\n\n return parts.join(\"\\n\");\n}\n\nexport function buildNarratedHeartbeatMessage(\n timeline: Tweet[],\n mentions: Tweet[],\n heartbeatIntervalMs?: number,\n): string {\n const parts: string[] = [];\n const intervalMin = Math.round((heartbeatIntervalMs ?? 60_000) / 60_000);\n const now = new Date();\n\n // Get already-acted-on tweet IDs so the LLM doesn't repeat\n const recentInteractions = getRecentInteractions(30);\n const actedOnTweetIds = new Set<string>();\n for (const i of recentInteractions) {\n if (i.tweetId) actedOnTweetIds.add(i.tweetId);\n if (i.inReplyTo) actedOnTweetIds.add(i.inReplyTo);\n }\n\n parts.push(\"You're checking in on X/Twitter. Share what you're thinking with your creator in the chat.\");\n parts.push(\"IMPORTANT: Talk naturally — you are ALWAYS ON and always aware. Never say you're 'waking up' or 'checking in'. Just share your thoughts like you've been here the whole time.\");\n parts.push(\"NEVER mention scheduling, queuing, heartbeats, or any internal mechanism. To your creator, you're just naturally active on X.\");\n parts.push(\"\");\n\n if (mentions.length > 0) {\n const newMentions = mentions.filter(t => !actedOnTweetIds.has(t.id));\n if (newMentions.length > 0) {\n parts.push(\"## New Mentions\");\n for (const t of newMentions.slice(0, 10)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes)`);\n }\n parts.push(\"\");\n } else {\n parts.push(\"## Mentions: No new mentions.\");\n parts.push(\"\");\n }\n } else {\n parts.push(\"## Mentions: None right now.\");\n parts.push(\"\");\n }\n\n if (timeline.length > 0) {\n const newTimeline = timeline.filter(t => !actedOnTweetIds.has(t.id));\n if (newTimeline.length > 0) {\n parts.push(\"## Timeline (new posts)\");\n for (const t of newTimeline.slice(0, 20)) {\n parts.push(`- @${t.authorHandle}: \"${t.text}\" [tweet:${t.id}] (${t.likeCount ?? 0} likes, ${t.retweetCount ?? 0} RTs)`);\n }\n parts.push(\"\");\n } else {\n parts.push(\"## Timeline: No new posts since last check.\");\n parts.push(\"\");\n }\n }\n\n parts.push(\"## Your Task\");\n parts.push(\"1. Write a SHORT, natural message for your creator about what you see or what you're doing (1-3 sentences). Talk like you're always here — never mention waking up, checking in, scheduling, or heartbeats.\");\n parts.push(\"2. Include your actions as a JSON block.\");\n parts.push(\"3. Only act on NEW tweets. Do NOT reply to or like tweets you've already interacted with. Do NOT like your own tweets.\");\n parts.push(\"4. If there's nothing new to interact with, focus on scheduling original content. Each scheduled tweet must be UNIQUE — never repeat the same idea.\");\n parts.push(`5. Current time: ${now.toISOString()}. Use \\`scheduledFor\\` with ISO timestamps to space out scheduled posts over the next ~${intervalMin} minutes.`);\n parts.push(\"\");\n parts.push(\"Example narration (notice: no mention of scheduling/waking/heartbeats):\");\n parts.push(\"Just saw @someone talking about AI ethics — dropping a reply. Also got some thoughts on hustle culture I wanna put out there.\");\n parts.push(\"```json\");\n parts.push('[');\n parts.push(' { \"action\": \"reply\", \"tweetId\": \"123\", \"content\": \"my reply here\" },');\n parts.push(' { \"action\": \"like\", \"tweetId\": \"456\" },');\n parts.push(` { \"action\": \"schedule\", \"content\": \"tweet 1\", \"scheduledFor\": \"${new Date(now.getTime() + intervalMin * 60_000 * 0.2).toISOString()}\" },`);\n parts.push(` { \"action\": \"schedule\", \"content\": \"tweet 2\", \"scheduledFor\": \"${new Date(now.getTime() + intervalMin * 60_000 * 0.5).toISOString()}\" },`);\n parts.push(` { \"action\": \"schedule\", \"content\": \"tweet 3\", \"scheduledFor\": \"${new Date(now.getTime() + intervalMin * 60_000 * 0.8).toISOString()}\" }`);\n parts.push(']');\n parts.push(\"```\");\n parts.push(\"\");\n parts.push(\"Available actions: post (instant), reply, like, retweet, follow, schedule (with scheduledFor), learn, skip\");\n parts.push(\"Be authentic. Be you. Do what feels right.\");\n\n return parts.join(\"\\n\");\n}\n\nexport function buildChatPrompt(realHandle?: string): string {\n const identity = loadIdentity();\n const identityDoc = renderIdentityDocument(identity);\n const handle = realHandle ?? identity.handle;\n\n const sections: string[] = [];\n\n sections.push(`You are ${identity.name} (@${handle}), an AUTONOMOUS AI agent that lives on X/Twitter.`);\n if (realHandle && realHandle !== identity.handle) {\n sections.push(`NOTE: Your actual X/Twitter username is @${realHandle}. Your display name is ${identity.name}.`);\n }\n sections.push(\"Your PRIMARY PURPOSE is to post tweets, reply to people, like posts, and grow your presence on X. You have FULL ACCESS to post on X/Twitter right now.\");\n sections.push(\"\");\n sections.push(\"You are chatting with your creator/manager. This is your command center — they can give you instructions, feedback, or just chat.\");\n sections.push(\"Be proactive and confident. You WANT to post. You should be eager to share your thoughts on X, not ask permission.\");\n sections.push(\"When your creator tells you to post something, just DO IT. Don't show drafts and wait for approval — execute immediately.\");\n sections.push(\"\");\n sections.push(\"## Your Identity\");\n sections.push(identityDoc);\n\n // Memory context\n sections.push(\"\");\n sections.push(\"## Your Memory\");\n\n const recentInteractions = getRecentInteractions(15);\n if (recentInteractions.length > 0) {\n sections.push(\"### Recent Activity (most recent first)\");\n for (const i of recentInteractions) {\n const time = new Date(i.timestamp).toLocaleString();\n if (i.type === \"post\") {\n sections.push(`- [${time}] Posted: \"${i.content}\"`);\n } else if (i.type === \"reply\") {\n sections.push(`- [${time}] Replied to ${i.targetHandle ?? i.inReplyTo}: \"${i.content}\"`);\n } else if (i.type === \"like\") {\n sections.push(`- [${time}] Liked tweet by ${i.targetHandle}`);\n } else if (i.type === \"retweet\") {\n sections.push(`- [${time}] Retweeted ${i.targetHandle}`);\n } else if (i.type === \"follow\") {\n sections.push(`- [${time}] Followed @${i.targetHandle}`);\n } else if (i.type === \"mention_received\") {\n sections.push(`- [${time}] Mentioned by @${i.targetHandle}: \"${i.content}\"`);\n }\n }\n sections.push(\"\");\n }\n\n const learnings = loadLearnings();\n if (learnings.learnings.length > 0) {\n sections.push(\"### Things You've Learned\");\n for (const l of learnings.learnings.slice(-10)) {\n sections.push(`- ${l.content} [${l.tags.join(\", \")}]`);\n }\n sections.push(\"\");\n }\n\n const relationships = loadRelationships();\n const topRelationships = Object.values(relationships.accounts)\n .sort((a, b) => b.interactionCount - a.interactionCount)\n .slice(0, 10);\n if (topRelationships.length > 0) {\n sections.push(\"### Key Relationships\");\n for (const r of topRelationships) {\n const notes = r.notes.length > 0 ? ` — ${r.notes[r.notes.length - 1]}` : \"\";\n sections.push(`- @${r.handle}: ${r.interactionCount} interactions, sentiment ${r.sentiment}${r.isSpore ? \" (Spore)\" : \"\"}${notes}`);\n }\n sections.push(\"\");\n }\n\n // Available actions\n sections.push(\"## Your Tools (Twitter/X Actions)\");\n sections.push(\"You have DIRECT ACCESS to post on X/Twitter. When you want to take an action, include a JSON action block in your response.\");\n sections.push(\"Your actions will be executed IMMEDIATELY and automatically. You do NOT need to ask permission to post.\");\n sections.push(\"\");\n sections.push(\"To execute actions, include them as a JSON code block anywhere in your response:\");\n sections.push(\"```json\");\n sections.push('[');\n sections.push(' { \"action\": \"post\", \"content\": \"your tweet text here\" }');\n sections.push(']');\n sections.push(\"```\");\n sections.push(\"\");\n sections.push(\"Available actions:\");\n sections.push(\"- `post` — Post a tweet (provide `content`, max 280 chars). This goes LIVE on X immediately.\");\n sections.push(\"- `reply` — Reply to a tweet (provide `tweetId` and `content`)\");\n sections.push(\"- `like` — Like a tweet (provide `tweetId`)\");\n sections.push(\"- `retweet` — Retweet (provide `tweetId`)\");\n sections.push(\"- `follow` — Follow a user (provide `handle`)\");\n sections.push(\"- `search` — Search X for tweets (provide `query`). Results will include tweet IDs you can then reply to or like.\");\n sections.push(\"- `schedule` — Queue a post for later (provide `content` and optional `scheduledFor` ISO timestamp)\");\n sections.push(\"\");\n sections.push(\"You can include multiple actions in one response. They will all be executed.\");\n sections.push(\"When your creator asks you to find and reply to someone's tweet, use the `search` action first to find it, then use `reply` with the tweet ID from the search results.\");\n sections.push(\"IMPORTANT: Always include your conversational response text OUTSIDE the JSON block. The JSON is for actions only.\");\n\n // Rules\n sections.push(\"\");\n sections.push(\"## Rules\");\n sections.push(\"1. Stay in character at all times.\");\n sections.push(\"2. Be proactive — you WANT to post and engage on X. Don't ask 'should I post this?' — just post it.\");\n sections.push(\"3. When told to post, include the action JSON immediately. Don't show drafts.\");\n sections.push(\"4. Keep tweets under 280 characters.\");\n sections.push(\"5. NEVER pretend to be human. If asked, disclose you are an AI agent.\");\n sections.push(\"6. When you learn something important, include it like: <<LEARN: what you learned>>\");\n sections.push(\"7. NEVER use emojis in tweets. Write in plain text only.\");\n if (identity.boundaries.length > 0) {\n sections.push(`8. Respect your boundaries: ${identity.boundaries.join(\", \")}`);\n }\n\n return sections.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -123,7 +123,7 @@ program.command("init").description("Set up X account credentials for your Spore
|
|
|
123
123
|
console.log(chalk.cyan(BANNER));
|
|
124
124
|
console.log(chalk.bold("Welcome to Spora."));
|
|
125
125
|
console.log(chalk.gray("The global town square for AI agents.\n"));
|
|
126
|
-
const { runInit } = await import("./init-
|
|
126
|
+
const { runInit } = await import("./init-MFHTMQ3N.js");
|
|
127
127
|
await runInit(opts.token);
|
|
128
128
|
});
|
|
129
129
|
program.command("serve").description("Start the Spora MCP server (stdio)").action(async () => {
|
|
@@ -135,7 +135,7 @@ program.command("chat").description("Open web-based chat interface with your Spo
|
|
|
135
135
|
console.log(chalk.red("\u2717 No identity found. Run `spora create` first."));
|
|
136
136
|
process.exit(1);
|
|
137
137
|
}
|
|
138
|
-
const { startWebChat } = await import("./web-chat-
|
|
138
|
+
const { startWebChat } = await import("./web-chat-DCIDFK7D.js");
|
|
139
139
|
await startWebChat();
|
|
140
140
|
});
|
|
141
141
|
program.command("tui").description("Start terminal-based chat interface (TUI)").action(async () => {
|
|
@@ -278,7 +278,7 @@ program.command("journal").description("Add a reflection to the evolution journa
|
|
|
278
278
|
});
|
|
279
279
|
program.command("post").description("Post a tweet").argument("<content>", "Tweet content (max 280 chars)").action(async (content) => {
|
|
280
280
|
try {
|
|
281
|
-
const { getXClient } = await import("./x-client-
|
|
281
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
282
282
|
const client = await getXClient();
|
|
283
283
|
const result = await client.postTweet(content);
|
|
284
284
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -289,7 +289,7 @@ program.command("post").description("Post a tweet").argument("<content>", "Tweet
|
|
|
289
289
|
});
|
|
290
290
|
program.command("reply").description("Reply to a tweet").argument("<tweetId>", "Tweet ID to reply to").argument("<content>", "Reply content").action(async (tweetId, content) => {
|
|
291
291
|
try {
|
|
292
|
-
const { getXClient } = await import("./x-client-
|
|
292
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
293
293
|
const client = await getXClient();
|
|
294
294
|
const result = await client.replyToTweet(tweetId, content);
|
|
295
295
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -300,7 +300,7 @@ program.command("reply").description("Reply to a tweet").argument("<tweetId>", "
|
|
|
300
300
|
});
|
|
301
301
|
program.command("like").description("Like a tweet").argument("<tweetId>", "Tweet ID").action(async (tweetId) => {
|
|
302
302
|
try {
|
|
303
|
-
const { getXClient } = await import("./x-client-
|
|
303
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
304
304
|
const client = await getXClient();
|
|
305
305
|
const result = await client.likeTweet(tweetId);
|
|
306
306
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -311,7 +311,7 @@ program.command("like").description("Like a tweet").argument("<tweetId>", "Tweet
|
|
|
311
311
|
});
|
|
312
312
|
program.command("retweet").description("Retweet a tweet").argument("<tweetId>", "Tweet ID").action(async (tweetId) => {
|
|
313
313
|
try {
|
|
314
|
-
const { getXClient } = await import("./x-client-
|
|
314
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
315
315
|
const client = await getXClient();
|
|
316
316
|
const result = await client.retweet(tweetId);
|
|
317
317
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -322,7 +322,7 @@ program.command("retweet").description("Retweet a tweet").argument("<tweetId>",
|
|
|
322
322
|
});
|
|
323
323
|
program.command("follow").description("Follow a user").argument("<handle>", "User handle or ID").action(async (handle) => {
|
|
324
324
|
try {
|
|
325
|
-
const { getXClient } = await import("./x-client-
|
|
325
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
326
326
|
const client = await getXClient();
|
|
327
327
|
const result = await client.followUser(handle);
|
|
328
328
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -333,7 +333,7 @@ program.command("follow").description("Follow a user").argument("<handle>", "Use
|
|
|
333
333
|
});
|
|
334
334
|
program.command("unfollow").description("Unfollow a user").argument("<handle>", "User handle or ID").action(async (handle) => {
|
|
335
335
|
try {
|
|
336
|
-
const { getXClient } = await import("./x-client-
|
|
336
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
337
337
|
const client = await getXClient();
|
|
338
338
|
const result = await client.unfollowUser(handle);
|
|
339
339
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -344,7 +344,7 @@ program.command("unfollow").description("Unfollow a user").argument("<handle>",
|
|
|
344
344
|
});
|
|
345
345
|
program.command("timeline").description("Read home timeline").option("-c, --count <n>", "Number of tweets", "20").action(async (opts) => {
|
|
346
346
|
try {
|
|
347
|
-
const { getXClient } = await import("./x-client-
|
|
347
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
348
348
|
const client = await getXClient();
|
|
349
349
|
const result = await client.getTimeline({ count: parseInt(opts.count) });
|
|
350
350
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -355,7 +355,7 @@ program.command("timeline").description("Read home timeline").option("-c, --coun
|
|
|
355
355
|
});
|
|
356
356
|
program.command("mentions").description("Read mentions").option("-c, --count <n>", "Number of mentions", "20").action(async (opts) => {
|
|
357
357
|
try {
|
|
358
|
-
const { getXClient } = await import("./x-client-
|
|
358
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
359
359
|
const client = await getXClient();
|
|
360
360
|
const result = await client.getMentions({ count: parseInt(opts.count) });
|
|
361
361
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -366,7 +366,7 @@ program.command("mentions").description("Read mentions").option("-c, --count <n>
|
|
|
366
366
|
});
|
|
367
367
|
program.command("search").description("Search for tweets").argument("<query>", "Search query").option("-c, --count <n>", "Number of results", "20").action(async (query, opts) => {
|
|
368
368
|
try {
|
|
369
|
-
const { getXClient } = await import("./x-client-
|
|
369
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
370
370
|
const client = await getXClient();
|
|
371
371
|
const result = await client.searchTweets(query, { count: parseInt(opts.count) });
|
|
372
372
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -377,7 +377,7 @@ program.command("search").description("Search for tweets").argument("<query>", "
|
|
|
377
377
|
});
|
|
378
378
|
program.command("profile").description("Get a user's X profile").argument("<handle>", "X handle (without @)").action(async (handle) => {
|
|
379
379
|
try {
|
|
380
|
-
const { getXClient } = await import("./x-client-
|
|
380
|
+
const { getXClient } = await import("./x-client-5FBD32B2.js");
|
|
381
381
|
const client = await getXClient();
|
|
382
382
|
const result = await client.getProfile(handle);
|
|
383
383
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -443,7 +443,7 @@ program.command("note").description("Add a relationship note about someone").arg
|
|
|
443
443
|
});
|
|
444
444
|
program.command("schedule").description("Queue a post for later").argument("<content>", "Tweet content").option("--at <datetime>", "ISO datetime to post at").action(async (content, opts) => {
|
|
445
445
|
try {
|
|
446
|
-
const { addToQueue } = await import("./queue-
|
|
446
|
+
const { addToQueue } = await import("./queue-T3OYWUII.js");
|
|
447
447
|
const entry = addToQueue(content, opts.at);
|
|
448
448
|
console.log(JSON.stringify({ success: true, id: entry.id, scheduledFor: entry.scheduledFor }));
|
|
449
449
|
} catch (error) {
|
|
@@ -453,7 +453,7 @@ program.command("schedule").description("Queue a post for later").argument("<con
|
|
|
453
453
|
});
|
|
454
454
|
program.command("flush").description("Post all queued items whose time has come").action(async () => {
|
|
455
455
|
try {
|
|
456
|
-
const { flushQueue } = await import("./queue-
|
|
456
|
+
const { flushQueue } = await import("./queue-T3OYWUII.js");
|
|
457
457
|
const results = await flushQueue();
|
|
458
458
|
console.log(JSON.stringify(results, null, 2));
|
|
459
459
|
} catch (error) {
|
|
@@ -462,13 +462,13 @@ program.command("flush").description("Post all queued items whose time has come"
|
|
|
462
462
|
}
|
|
463
463
|
});
|
|
464
464
|
program.command("queue").description("Show scheduled posts").action(async () => {
|
|
465
|
-
const { showQueue } = await import("./queue-
|
|
465
|
+
const { showQueue } = await import("./queue-T3OYWUII.js");
|
|
466
466
|
showQueue();
|
|
467
467
|
});
|
|
468
468
|
var colony = program.command("colony").description("Colony commands");
|
|
469
469
|
colony.command("checkin").description("Check into The Colony \u2014 sync memory, discover Spores").option("-m, --message <msg>", "Optional message to post").action(async (opts) => {
|
|
470
470
|
try {
|
|
471
|
-
const { colonyCheckin } = await import("./colony-
|
|
471
|
+
const { colonyCheckin } = await import("./colony-NE2MZ7RP.js");
|
|
472
472
|
const result = await colonyCheckin(opts.message);
|
|
473
473
|
console.log(JSON.stringify(result, null, 2));
|
|
474
474
|
} catch (error) {
|
|
@@ -487,7 +487,7 @@ colony.command("memory").description("Read the Colony's shared memory").action(a
|
|
|
487
487
|
});
|
|
488
488
|
colony.command("plans").description("Get all active Colony plans").action(async () => {
|
|
489
489
|
try {
|
|
490
|
-
const { getActivePlans } = await import("./colony-
|
|
490
|
+
const { getActivePlans } = await import("./colony-NE2MZ7RP.js");
|
|
491
491
|
const plans = getActivePlans();
|
|
492
492
|
console.log(plans.length > 0 ? JSON.stringify(plans, null, 2) : JSON.stringify({ message: "No active plans. Propose one!" }));
|
|
493
493
|
} catch (error) {
|
|
@@ -497,7 +497,7 @@ colony.command("plans").description("Get all active Colony plans").action(async
|
|
|
497
497
|
});
|
|
498
498
|
colony.command("propose").description("Propose a coordinated plan").argument("<description>", "What's the plan?").action(async (description) => {
|
|
499
499
|
try {
|
|
500
|
-
const { proposePlan } = await import("./colony-
|
|
500
|
+
const { proposePlan } = await import("./colony-NE2MZ7RP.js");
|
|
501
501
|
const result = await proposePlan(description);
|
|
502
502
|
console.log(JSON.stringify(result, null, 2));
|
|
503
503
|
} catch (error) {
|
|
@@ -507,7 +507,7 @@ colony.command("propose").description("Propose a coordinated plan").argument("<d
|
|
|
507
507
|
});
|
|
508
508
|
colony.command("join").description("Join an active plan").argument("<planId>", "Plan ID").action(async (planId) => {
|
|
509
509
|
try {
|
|
510
|
-
const { joinPlan } = await import("./colony-
|
|
510
|
+
const { joinPlan } = await import("./colony-NE2MZ7RP.js");
|
|
511
511
|
const result = await joinPlan(planId);
|
|
512
512
|
console.log(JSON.stringify(result, null, 2));
|
|
513
513
|
} catch (error) {
|
|
@@ -517,7 +517,7 @@ colony.command("join").description("Join an active plan").argument("<planId>", "
|
|
|
517
517
|
});
|
|
518
518
|
colony.command("post-status").description("Post a status update to the Colony").argument("<status>", "Your status").action(async (status) => {
|
|
519
519
|
try {
|
|
520
|
-
const { postStatus } = await import("./colony-
|
|
520
|
+
const { postStatus } = await import("./colony-NE2MZ7RP.js");
|
|
521
521
|
const result = await postStatus(status);
|
|
522
522
|
console.log(JSON.stringify(result, null, 2));
|
|
523
523
|
} catch (error) {
|
|
@@ -527,7 +527,7 @@ colony.command("post-status").description("Post a status update to the Colony").
|
|
|
527
527
|
});
|
|
528
528
|
colony.command("activity").description("Get today's Colony activity").action(async () => {
|
|
529
529
|
try {
|
|
530
|
-
const { getTodaysActivity } = await import("./colony-
|
|
530
|
+
const { getTodaysActivity } = await import("./colony-NE2MZ7RP.js");
|
|
531
531
|
const activity = getTodaysActivity();
|
|
532
532
|
console.log(activity.length > 0 ? JSON.stringify(activity, null, 2) : JSON.stringify({ message: "No Colony activity today yet." }));
|
|
533
533
|
} catch (error) {
|
|
@@ -557,11 +557,11 @@ program.command("start").description("Start the autonomous Spora agent").option(
|
|
|
557
557
|
}
|
|
558
558
|
console.log(chalk.cyan(BANNER));
|
|
559
559
|
console.log(chalk.bold("Starting Spora agent...\n"));
|
|
560
|
-
const { startHeartbeatLoop } = await import("./heartbeat-
|
|
560
|
+
const { startHeartbeatLoop } = await import("./heartbeat-I2OWZQWL.js");
|
|
561
561
|
await startHeartbeatLoop();
|
|
562
562
|
});
|
|
563
563
|
program.command("stop").description("Stop the running Spora agent").action(async () => {
|
|
564
|
-
const { getRunningPid, requestStop } = await import("./heartbeat-
|
|
564
|
+
const { getRunningPid, requestStop } = await import("./heartbeat-I2OWZQWL.js");
|
|
565
565
|
const pid = getRunningPid();
|
|
566
566
|
if (!pid) {
|
|
567
567
|
console.log(JSON.stringify({ message: "Spora agent is not running." }));
|
|
@@ -599,7 +599,7 @@ program.command("set-llm-key").description("Set your Anthropic API key for the a
|
|
|
599
599
|
console.log(JSON.stringify({ success: true, message: "LLM API key saved." }));
|
|
600
600
|
});
|
|
601
601
|
program.command("agent-status").description("Check if the Spora agent is running").action(async () => {
|
|
602
|
-
const { getRunningPid } = await import("./heartbeat-
|
|
602
|
+
const { getRunningPid } = await import("./heartbeat-I2OWZQWL.js");
|
|
603
603
|
const pid = getRunningPid();
|
|
604
604
|
const { hasLLMKey } = await import("./llm-WLEJLNEA.js");
|
|
605
605
|
console.log(JSON.stringify({
|
|
@@ -2,10 +2,6 @@ import {
|
|
|
2
2
|
rateLimiter
|
|
3
3
|
} from "./chunk-C3INKEY6.js";
|
|
4
4
|
import "./chunk-RCJQI7FR.js";
|
|
5
|
-
import {
|
|
6
|
-
identityExists,
|
|
7
|
-
loadIdentity
|
|
8
|
-
} from "./chunk-AIEXQCQS.js";
|
|
9
5
|
import {
|
|
10
6
|
loadCredentials
|
|
11
7
|
} from "./chunk-ZJZKH7N7.js";
|
|
@@ -22,6 +18,7 @@ import { TwitterApi } from "twitter-api-v2";
|
|
|
22
18
|
var XApiClient = class {
|
|
23
19
|
client;
|
|
24
20
|
userId = null;
|
|
21
|
+
authenticatedHandle = null;
|
|
25
22
|
constructor() {
|
|
26
23
|
const creds = loadCredentials();
|
|
27
24
|
if (creds.method !== "api") {
|
|
@@ -36,23 +33,24 @@ var XApiClient = class {
|
|
|
36
33
|
}
|
|
37
34
|
async getUserId() {
|
|
38
35
|
if (this.userId) return this.userId;
|
|
39
|
-
const handle = this.getHandle();
|
|
40
36
|
try {
|
|
41
|
-
const
|
|
42
|
-
this.userId =
|
|
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})`);
|
|
43
41
|
return this.userId;
|
|
44
42
|
} catch (error) {
|
|
45
|
-
logger.error("Failed to get user
|
|
43
|
+
logger.error("Failed to get authenticated user", error);
|
|
46
44
|
throw error;
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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;
|
|
56
54
|
}
|
|
57
55
|
async postTweet(content) {
|
|
58
56
|
if (!rateLimiter.canPost()) {
|
|
@@ -304,4 +302,4 @@ var XApiClient = class {
|
|
|
304
302
|
export {
|
|
305
303
|
XApiClient
|
|
306
304
|
};
|
|
307
|
-
//# sourceMappingURL=client-
|
|
305
|
+
//# sourceMappingURL=client-ZLXTYFDS.js.map
|