spora 0.2.50 → 0.2.52
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-FFBTPEBB.js → chunk-3NW3VIN5.js} +2 -2
- package/dist/{chunk-O7KCHB7L.js → chunk-AIOYI7ZF.js} +40 -30
- package/dist/chunk-AIOYI7ZF.js.map +1 -0
- package/dist/{chunk-XBTXC3QH.js → chunk-K6FZPWXD.js} +3 -3
- package/dist/{chunk-VFH5NZYN.js → chunk-XJBOOX7N.js} +20 -3
- package/dist/chunk-XJBOOX7N.js.map +1 -0
- package/dist/cli.js +24 -24
- package/dist/{client-BVZQULNO.js → client-KXYBQUMD.js} +5 -1
- package/dist/client-KXYBQUMD.js.map +1 -0
- package/dist/{client-HK2HPFP2.js → client-YR2RA56D.js} +27 -1
- package/dist/client-YR2RA56D.js.map +1 -0
- package/dist/{colony-U6FCMPYW.js → colony-4EYP6EPG.js} +2 -2
- package/dist/{decision-engine-VQEARJYD.js → decision-engine-YQDGNP3C.js} +4 -4
- package/dist/{heartbeat-5ZOF673V.js → heartbeat-5MDXYM3G.js} +8 -12
- package/dist/heartbeat-5MDXYM3G.js.map +1 -0
- package/dist/{init-3D462TGK.js → init-OVMM7SOZ.js} +3 -3
- package/dist/mcp-server.js +19 -19
- package/dist/{prompt-builder-XMJYAFB4.js → prompt-builder-R65RH7KP.js} +2 -2
- package/dist/{queue-25W5G4BN.js → queue-IDNLFXWC.js} +2 -2
- package/dist/{web-chat-KWOIRDQQ.js → web-chat-3IEWDNHD.js} +12 -16
- package/dist/web-chat-3IEWDNHD.js.map +1 -0
- package/dist/{x-client-KFRZVA3N.js → x-client-DFMW2PX7.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-O7KCHB7L.js.map +0 -1
- package/dist/chunk-VFH5NZYN.js.map +0 -1
- package/dist/client-BVZQULNO.js.map +0 -1
- package/dist/client-HK2HPFP2.js.map +0 -1
- package/dist/heartbeat-5ZOF673V.js.map +0 -1
- package/dist/web-chat-KWOIRDQQ.js.map +0 -1
- /package/dist/{chunk-FFBTPEBB.js.map → chunk-3NW3VIN5.js.map} +0 -0
- /package/dist/{chunk-XBTXC3QH.js.map → chunk-K6FZPWXD.js.map} +0 -0
- /package/dist/{colony-U6FCMPYW.js.map → colony-4EYP6EPG.js.map} +0 -0
- /package/dist/{decision-engine-VQEARJYD.js.map → decision-engine-YQDGNP3C.js.map} +0 -0
- /package/dist/{init-3D462TGK.js.map → init-OVMM7SOZ.js.map} +0 -0
- /package/dist/{prompt-builder-XMJYAFB4.js.map → prompt-builder-R65RH7KP.js.map} +0 -0
- /package/dist/{queue-25W5G4BN.js.map → queue-IDNLFXWC.js.map} +0 -0
- /package/dist/{x-client-KFRZVA3N.js.map → x-client-DFMW2PX7.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/x-client/browser/client.ts"],"sourcesContent":["import { chromium, type Browser, type Page, type BrowserContext } from \"playwright\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { loadCredentials } from \"../../utils/crypto.js\";\nimport { loadIdentity, identityExists } from \"../../identity/index.js\";\nimport { logInteraction } from \"../../memory/index.js\";\nimport { rateLimiter } from \"../rate-limiter.js\";\nimport { paths, ensureDirectories } from \"../../utils/paths.js\";\nimport { logger } from \"../../utils/logger.js\";\nimport type {\n XClientInterface,\n Tweet,\n UserProfile,\n PostResult,\n TimelineOptions,\n SearchOptions,\n} from \"../types.js\";\n\nconst X_BASE = \"https://x.com\";\n\nexport class XBrowserClient implements XClientInterface {\n private browser: Browser | null = null;\n private context: BrowserContext | null = null;\n private page: Page | null = null;\n\n private async ensureBrowser(): Promise<Page> {\n if (this.page && !this.page.isClosed()) return this.page;\n\n this.browser = await chromium.launch({ headless: true });\n\n // Restore session if available\n if (existsSync(paths.browserAuth)) {\n const storageState = JSON.parse(readFileSync(paths.browserAuth, \"utf-8\"));\n this.context = await this.browser.newContext({ storageState });\n } else {\n this.context = await this.browser.newContext();\n }\n\n this.page = await this.context.newPage();\n\n // Check if we're logged in\n await this.page.goto(X_BASE, { waitUntil: \"domcontentloaded\" });\n const isLoggedIn = await this.page\n .locator('[data-testid=\"SideNav_AccountSwitcher_Button\"]')\n .isVisible({ timeout: 5000 })\n .catch(() => false);\n\n if (!isLoggedIn) {\n await this.login();\n }\n\n return this.page;\n }\n\n private async login(): Promise<void> {\n const creds = loadCredentials();\n if (!creds.username || !creds.password) {\n throw new Error(\"Browser mode requires username and password credentials\");\n }\n\n const page = this.page!;\n\n await page.goto(`${X_BASE}/login`, { waitUntil: \"domcontentloaded\" });\n await page.waitForTimeout(2000);\n\n // Enter username\n const usernameInput = page.locator('input[autocomplete=\"username\"]');\n await usernameInput.waitFor({ timeout: 10000 });\n await usernameInput.fill(creds.username);\n await page.locator('text=Next').click();\n await page.waitForTimeout(2000);\n\n // Check for email verification step\n const emailInput = page.locator('input[data-testid=\"ocfEnterTextTextInput\"]');\n const emailVisible = await emailInput.isVisible({ timeout: 3000 }).catch(() => false);\n if (emailVisible && creds.email) {\n await emailInput.fill(creds.email);\n await page.locator('text=Next').click();\n await page.waitForTimeout(2000);\n }\n\n // Enter password\n const passwordInput = page.locator('input[type=\"password\"]');\n await passwordInput.waitFor({ timeout: 10000 });\n await passwordInput.fill(creds.password);\n await page.locator('[data-testid=\"LoginForm_Login_Button\"]').click();\n await page.waitForTimeout(3000);\n\n // Save session\n await this.saveSession();\n logger.info(\"Logged into X via browser\");\n }\n\n private async saveSession(): Promise<void> {\n if (!this.context) return;\n ensureDirectories();\n const state = await this.context.storageState();\n writeFileSync(paths.browserAuth, JSON.stringify(state));\n }\n\n private getHandle(): string {\n if (identityExists()) {\n return loadIdentity().handle;\n }\n const creds = loadCredentials();\n if (creds.username) return creds.username;\n throw new Error(\"No handle found. Create a Spore identity first.\");\n }\n\n private async waitForNavigation(page: Page, timeout = 5000): Promise<void> {\n await page.waitForTimeout(timeout);\n }\n\n async postTweet(content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const page = await this.ensureBrowser();\n await page.goto(X_BASE, { waitUntil: \"domcontentloaded\" });\n await page.waitForTimeout(2000);\n\n // Click the tweet compose area\n const composeButton = page.locator('[data-testid=\"tweetButtonInline\"]').first();\n const composeArea = page.locator('[data-testid=\"tweetTextarea_0\"]').first();\n\n // Try clicking the compose area directly\n const areaVisible = await composeArea.isVisible({ timeout: 3000 }).catch(() => false);\n if (!areaVisible) {\n // Click the compose button in sidebar\n await page.locator('[data-testid=\"SideNav_NewTweet_Button\"]').click();\n await page.waitForTimeout(1000);\n }\n\n // Type the tweet\n const textarea = page.locator('[data-testid=\"tweetTextarea_0\"]').first();\n await textarea.waitFor({ timeout: 5000 });\n await textarea.fill(content);\n await page.waitForTimeout(500);\n\n // Click the post button\n await page.locator('[data-testid=\"tweetButton\"]').click();\n await page.waitForTimeout(3000);\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n content,\n creditsUsed: 1,\n success: true,\n });\n\n await this.saveSession();\n return { success: true };\n } catch (error) {\n logger.error(\"Failed to post tweet via browser\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async postTweetWithMedia(content: string, _mediaBuffer: Buffer): Promise<PostResult> {\n // Browser client doesn't support media upload — fall back to text-only post\n logger.warn(\"Browser client does not support media upload, posting without image\");\n return this.postTweet(content);\n }\n\n async replyToTweetWithMedia(tweetId: string, content: string, _mediaBuffer: Buffer): Promise<PostResult> {\n logger.warn(\"Browser client does not support media upload, replying without image\");\n return this.replyToTweet(tweetId, content);\n }\n\n async replyToTweet(tweetId: string, content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const page = await this.ensureBrowser();\n\n // Navigate to the tweet using /i/status/ which works for any author's tweet\n await page.goto(`${X_BASE}/i/status/${tweetId}`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(2000);\n\n // Click reply area\n const replyArea = page.locator('[data-testid=\"tweetTextarea_0\"]').first();\n await replyArea.waitFor({ timeout: 5000 });\n await replyArea.fill(content);\n await page.waitForTimeout(500);\n\n // Click reply button\n await page.locator('[data-testid=\"tweetButton\"]').click();\n await page.waitForTimeout(3000);\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n inReplyTo: tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n await this.saveSession();\n return { success: true };\n } catch (error) {\n logger.error(\"Failed to reply via browser\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async deleteTweet(tweetId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n const handle = this.getHandle();\n\n await page.goto(`${X_BASE}/${handle}/status/${tweetId}`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(2000);\n\n // Click the more options menu\n await page.locator('[data-testid=\"caret\"]').first().click();\n await page.waitForTimeout(1000);\n\n // Click delete\n await page.locator('text=Delete').click();\n await page.waitForTimeout(1000);\n\n // Confirm\n await page.locator('[data-testid=\"confirmationSheetConfirm\"]').click();\n await page.waitForTimeout(2000);\n\n await this.saveSession();\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async likeTweet(tweetId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n\n // Navigate to a page that shows the tweet, then like it\n await page.goto(`${X_BASE}/i/status/${tweetId}`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(2000);\n\n await page.locator('[data-testid=\"like\"]').first().click();\n await page.waitForTimeout(1000);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"like\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n await this.saveSession();\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unlikeTweet(tweetId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n await page.goto(`${X_BASE}/i/status/${tweetId}`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(2000);\n\n await page.locator('[data-testid=\"unlike\"]').first().click();\n await page.waitForTimeout(1000);\n\n await this.saveSession();\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async retweet(tweetId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n await page.goto(`${X_BASE}/i/status/${tweetId}`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(2000);\n\n await page.locator('[data-testid=\"retweet\"]').first().click();\n await page.waitForTimeout(1000);\n await page.locator('[data-testid=\"retweetConfirm\"]').click();\n await page.waitForTimeout(1000);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"retweet\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n await this.saveSession();\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unretweet(tweetId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n await page.goto(`${X_BASE}/i/status/${tweetId}`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(2000);\n\n await page.locator('[data-testid=\"unretweet\"]').first().click();\n await page.waitForTimeout(1000);\n await page.locator('[data-testid=\"unretweetConfirm\"]').click();\n await page.waitForTimeout(1000);\n\n await this.saveSession();\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async followUser(userId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n // userId here may be a handle in browser mode\n await page.goto(`${X_BASE}/${userId}`, { waitUntil: \"domcontentloaded\" });\n await page.waitForTimeout(2000);\n\n const followButton = page.locator('[data-testid$=\"-follow\"]').first();\n await followButton.click();\n await page.waitForTimeout(1000);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"follow\",\n targetUserId: userId,\n creditsUsed: 0,\n success: true,\n });\n\n await this.saveSession();\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unfollowUser(userId: string): Promise<PostResult> {\n try {\n const page = await this.ensureBrowser();\n await page.goto(`${X_BASE}/${userId}`, { waitUntil: \"domcontentloaded\" });\n await page.waitForTimeout(2000);\n\n const unfollowButton = page.locator('[data-testid$=\"-unfollow\"]').first();\n await unfollowButton.click();\n await page.waitForTimeout(1000);\n\n // Confirm unfollow\n await page.locator('[data-testid=\"confirmationSheetConfirm\"]').click();\n await page.waitForTimeout(1000);\n\n await this.saveSession();\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async getTimeline(options?: TimelineOptions): Promise<Tweet[]> {\n try {\n const page = await this.ensureBrowser();\n await page.goto(X_BASE, { waitUntil: \"domcontentloaded\" });\n await page.waitForTimeout(3000);\n\n const tweets = await this.scrapeTweets(page, options?.count ?? 20);\n return tweets;\n } catch (error) {\n logger.error(\"Failed to read timeline via browser\", error);\n return [];\n }\n }\n\n async getMentions(options?: TimelineOptions): Promise<Tweet[]> {\n try {\n const page = await this.ensureBrowser();\n await page.goto(`${X_BASE}/notifications/mentions`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(3000);\n\n const tweets = await this.scrapeTweets(page, options?.count ?? 20);\n return tweets;\n } catch (error) {\n logger.error(\"Failed to read mentions via browser\", error);\n return [];\n }\n }\n\n async searchTweets(query: string, options?: SearchOptions): Promise<Tweet[]> {\n try {\n const page = await this.ensureBrowser();\n const encodedQuery = encodeURIComponent(query);\n await page.goto(`${X_BASE}/search?q=${encodedQuery}&f=live`, {\n waitUntil: \"domcontentloaded\",\n });\n await page.waitForTimeout(3000);\n\n const tweets = await this.scrapeTweets(page, options?.count ?? 20);\n return tweets;\n } catch (error) {\n logger.error(\"Failed to search via browser\", error);\n return [];\n }\n }\n\n async getProfile(handle: string): Promise<UserProfile> {\n const page = await this.ensureBrowser();\n await page.goto(`${X_BASE}/${handle}`, { waitUntil: \"domcontentloaded\" });\n await page.waitForTimeout(3000);\n\n const name =\n (await page\n .locator('[data-testid=\"UserName\"] span')\n .first()\n .textContent()) ?? handle;\n\n const bio =\n (await page\n .locator('[data-testid=\"UserDescription\"]')\n .textContent()\n .catch(() => \"\")) ?? \"\";\n\n // Parse follower/following counts from the profile page\n const followersText =\n (await page\n .locator(`a[href=\"/${handle}/verified_followers\"] span`)\n .first()\n .textContent()\n .catch(() => \"0\")) ?? \"0\";\n\n const followingText =\n (await page\n .locator(`a[href=\"/${handle}/following\"] span`)\n .first()\n .textContent()\n .catch(() => \"0\")) ?? \"0\";\n\n await this.saveSession();\n\n return {\n id: handle,\n handle,\n name,\n bio,\n followersCount: parseInt(followersText.replace(/[,K.M]/g, \"\")) || 0,\n followingCount: parseInt(followingText.replace(/[,K.M]/g, \"\")) || 0,\n tweetCount: 0,\n verified: false,\n };\n }\n\n private async scrapeTweets(page: Page, count: number): Promise<Tweet[]> {\n const tweets: Tweet[] = [];\n\n const articles = page.locator('article[data-testid=\"tweet\"]');\n const articleCount = await articles.count();\n\n for (let i = 0; i < Math.min(articleCount, count); i++) {\n try {\n const article = articles.nth(i);\n\n const tweetText =\n (await article\n .locator('[data-testid=\"tweetText\"]')\n .textContent()\n .catch(() => \"\")) ?? \"\";\n\n const userLink = article\n .locator('a[role=\"link\"][href*=\"/\"]')\n .first();\n const href = (await userLink.getAttribute(\"href\").catch(() => \"\")) ?? \"\";\n const authorHandle = href.replace(\"/\", \"\");\n\n const time = article.locator(\"time\").first();\n const datetime = (await time.getAttribute(\"datetime\").catch(() => \"\")) ?? \"\";\n\n tweets.push({\n id: `browser-${Date.now()}-${i}`,\n text: tweetText,\n authorId: authorHandle,\n authorHandle,\n createdAt: datetime,\n });\n } catch {\n continue;\n }\n }\n\n return tweets;\n }\n\n async close(): Promise<void> {\n if (this.page) await this.page.close().catch(() => {});\n if (this.context) await this.context.close().catch(() => {});\n if (this.browser) await this.browser.close().catch(() => {});\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -123,6 +123,32 @@ var XApiClient = class {
|
|
|
123
123
|
return { success: false, error: error.message };
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
+
async replyToTweetWithMedia(tweetId, content, mediaBuffer) {
|
|
127
|
+
if (!rateLimiter.canPost()) {
|
|
128
|
+
return { success: false, error: "Monthly post limit reached" };
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const mediaId = await this.client.v1.uploadMedia(mediaBuffer, { mimeType: "image/jpeg" });
|
|
132
|
+
const result = await this.client.v2.reply(content, tweetId, {
|
|
133
|
+
media: { media_ids: [mediaId] }
|
|
134
|
+
});
|
|
135
|
+
rateLimiter.consume();
|
|
136
|
+
logInteraction({
|
|
137
|
+
id: `int-${Date.now()}`,
|
|
138
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
139
|
+
type: "reply",
|
|
140
|
+
tweetId: result.data.id,
|
|
141
|
+
inReplyTo: tweetId,
|
|
142
|
+
content: content + " [with image]",
|
|
143
|
+
creditsUsed: 1,
|
|
144
|
+
success: true
|
|
145
|
+
});
|
|
146
|
+
return { success: true, tweetId: result.data.id };
|
|
147
|
+
} catch (error) {
|
|
148
|
+
logger.error("Failed to reply with media", error);
|
|
149
|
+
return { success: false, error: error.message };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
126
152
|
async deleteTweet(tweetId) {
|
|
127
153
|
try {
|
|
128
154
|
await this.client.v2.deleteTweet(tweetId);
|
|
@@ -328,4 +354,4 @@ var XApiClient = class {
|
|
|
328
354
|
export {
|
|
329
355
|
XApiClient
|
|
330
356
|
};
|
|
331
|
-
//# sourceMappingURL=client-
|
|
357
|
+
//# sourceMappingURL=client-YR2RA56D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/x-client/api/client.ts"],"sourcesContent":["import { TwitterApi } from \"twitter-api-v2\";\nimport { loadCredentials } from \"../../utils/crypto.js\";\nimport { loadConfig } from \"../../utils/config.js\";\nimport { loadIdentity, identityExists } from \"../../identity/index.js\";\nimport { logInteraction } from \"../../memory/index.js\";\nimport { rateLimiter } from \"../rate-limiter.js\";\nimport { logger } from \"../../utils/logger.js\";\nimport type {\n XClientInterface,\n Tweet,\n UserProfile,\n PostResult,\n TimelineOptions,\n SearchOptions,\n} from \"../types.js\";\n\nexport class XApiClient implements XClientInterface {\n private client: TwitterApi;\n private userId: string | null = null;\n private authenticatedHandle: string | null = null;\n\n constructor() {\n const creds = loadCredentials();\n if (creds.method !== \"api\") {\n throw new Error(\"API client requires API credentials. Current method: browser\");\n }\n\n // Use OAuth 1.0a User Context for all endpoints\n this.client = new TwitterApi({\n appKey: creds.apiKey!,\n appSecret: creds.apiSecret!,\n accessToken: creds.accessToken!,\n accessSecret: creds.accessTokenSecret!,\n });\n }\n\n private async getUserId(): Promise<string> {\n if (this.userId) return this.userId;\n try {\n // Use v2.me() to get the authenticated user — always reliable\n const me = await this.client.v2.me();\n this.userId = me.data.id;\n this.authenticatedHandle = me.data.username;\n logger.info(`Authenticated as @${this.authenticatedHandle} (ID: ${this.userId})`);\n return this.userId;\n } catch (error) {\n logger.error(\"Failed to get authenticated user\", error);\n throw error;\n }\n }\n\n /**\n * Get the real Twitter handle of the authenticated user\n */\n async getAuthenticatedHandle(): Promise<string> {\n if (this.authenticatedHandle) return this.authenticatedHandle;\n await this.getUserId(); // This populates authenticatedHandle\n return this.authenticatedHandle!;\n }\n\n async postTweet(content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const result = await this.client.v2.tweet(content);\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n tweetId: result.data.id,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: result.data.id };\n } catch (error) {\n logger.error(\"Failed to post tweet\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async postTweetWithMedia(content: string, mediaBuffer: Buffer): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n // Upload media via v1 API\n const mediaId = await this.client.v1.uploadMedia(mediaBuffer, { mimeType: \"image/jpeg\" });\n\n // Post tweet with media attached\n const result = await this.client.v2.tweet({\n text: content,\n media: { media_ids: [mediaId] },\n });\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n tweetId: result.data.id,\n content: content + \" [with image]\",\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: result.data.id };\n } catch (error) {\n logger.error(\"Failed to post tweet with media\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async replyToTweet(tweetId: string, content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const result = await this.client.v2.reply(content, tweetId);\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n tweetId: result.data.id,\n inReplyTo: tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: result.data.id };\n } catch (error) {\n logger.error(\"Failed to reply\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async replyToTweetWithMedia(tweetId: string, content: string, mediaBuffer: Buffer): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const mediaId = await this.client.v1.uploadMedia(mediaBuffer, { mimeType: \"image/jpeg\" });\n\n const result = await this.client.v2.reply(content, tweetId, {\n media: { media_ids: [mediaId] },\n });\n\n rateLimiter.consume();\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n tweetId: result.data.id,\n inReplyTo: tweetId,\n content: content + \" [with image]\",\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: result.data.id };\n } catch (error) {\n logger.error(\"Failed to reply with media\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async deleteTweet(tweetId: string): Promise<PostResult> {\n try {\n await this.client.v2.deleteTweet(tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async likeTweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Like\");\n try {\n const userId = await this.getUserId();\n logger.info(`Like attempt: userId=${userId}, tweetId=${tweetId}`);\n await this.client.v2.like(userId, tweetId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"like\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unlikeTweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unlike\");\n try {\n const userId = await this.getUserId();\n await this.client.v2.unlike(userId, tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async retweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Retweet\");\n try {\n const userId = await this.getUserId();\n await this.client.v2.retweet(userId, tweetId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"retweet\",\n tweetId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unretweet(tweetId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unretweet\");\n try {\n const userId = await this.getUserId();\n await this.client.v2.unretweet(userId, tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async followUser(userId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Follow\");\n try {\n const myId = await this.getUserId();\n await this.client.v2.follow(myId, userId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"follow\",\n targetUserId: userId,\n creditsUsed: 0,\n success: true,\n });\n\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async unfollowUser(userId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unfollow\");\n try {\n const myId = await this.getUserId();\n await this.client.v2.unfollow(myId, userId);\n return { success: true };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async getTimeline(options?: TimelineOptions): Promise<Tweet[]> {\n rateLimiter.requireBasicTier(\"Read timeline\");\n try {\n const userId = await this.getUserId();\n const result = await this.client.v2.homeTimeline({\n max_results: options?.count ?? 20,\n \"tweet.fields\": [\"created_at\", \"public_metrics\", \"in_reply_to_user_id\"],\n expansions: [\"author_id\"],\n \"user.fields\": [\"username\"],\n ...(options?.sinceId ? { since_id: options.sinceId } : {}),\n });\n\n if (!result.data?.data) return [];\n\n const userMap = new Map<string, string>();\n for (const user of result.includes?.users ?? []) {\n userMap.set(user.id, user.username);\n }\n\n return result.data.data.map((tweet) => ({\n id: tweet.id,\n text: tweet.text,\n authorId: tweet.author_id ?? \"unknown\",\n authorHandle: userMap.get(tweet.author_id ?? \"\") ?? \"unknown\",\n createdAt: tweet.created_at ?? new Date().toISOString(),\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n }));\n } catch (error) {\n logger.error(\"Failed to read timeline\", error);\n return [];\n }\n }\n\n async getMentions(options?: TimelineOptions): Promise<Tweet[]> {\n rateLimiter.requireBasicTier(\"Read mentions\");\n try {\n const userId = await this.getUserId();\n const result = await this.client.v2.userMentionTimeline(userId, {\n max_results: options?.count ?? 20,\n \"tweet.fields\": [\"created_at\", \"public_metrics\"],\n expansions: [\"author_id\"],\n \"user.fields\": [\"username\"],\n ...(options?.sinceId ? { since_id: options.sinceId } : {}),\n });\n\n if (!result.data?.data) return [];\n\n const userMap = new Map<string, string>();\n for (const user of result.includes?.users ?? []) {\n userMap.set(user.id, user.username);\n }\n\n return result.data.data.map((tweet) => ({\n id: tweet.id,\n text: tweet.text,\n authorId: tweet.author_id ?? \"unknown\",\n authorHandle: userMap.get(tweet.author_id ?? \"\") ?? \"unknown\",\n createdAt: tweet.created_at ?? new Date().toISOString(),\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n }));\n } catch (error) {\n logger.error(\"Failed to read mentions\", error);\n return [];\n }\n }\n\n async searchTweets(query: string, options?: SearchOptions): Promise<Tweet[]> {\n rateLimiter.requireBasicTier(\"Search tweets\");\n try {\n const result = await this.client.v2.search(query, {\n max_results: options?.count ?? 20,\n \"tweet.fields\": [\"created_at\", \"public_metrics\"],\n expansions: [\"author_id\"],\n \"user.fields\": [\"username\"],\n });\n\n if (!result.data?.data) return [];\n\n const userMap = new Map<string, string>();\n for (const user of result.includes?.users ?? []) {\n userMap.set(user.id, user.username);\n }\n\n return result.data.data.map((tweet) => ({\n id: tweet.id,\n text: tweet.text,\n authorId: tweet.author_id ?? \"unknown\",\n authorHandle: userMap.get(tweet.author_id ?? \"\") ?? \"unknown\",\n createdAt: tweet.created_at ?? new Date().toISOString(),\n likeCount: tweet.public_metrics?.like_count,\n retweetCount: tweet.public_metrics?.retweet_count,\n replyCount: tweet.public_metrics?.reply_count,\n }));\n } catch (error) {\n logger.error(\"Failed to search tweets\", error);\n return [];\n }\n }\n\n async getProfile(handle: string): Promise<UserProfile> {\n rateLimiter.requireBasicTier(\"Get profile\");\n const result = await this.client.v2.userByUsername(handle, {\n \"user.fields\": [\"description\", \"public_metrics\", \"verified\", \"profile_image_url\"],\n });\n\n return {\n id: result.data.id,\n handle: result.data.username,\n name: result.data.name,\n bio: result.data.description ?? \"\",\n followersCount: result.data.public_metrics?.followers_count ?? 0,\n followingCount: result.data.public_metrics?.following_count ?? 0,\n tweetCount: result.data.public_metrics?.tweet_count ?? 0,\n verified: result.data.verified ?? false,\n profileImageUrl: result.data.profile_image_url,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-7UHJLJNI.js";
|
|
11
11
|
import {
|
|
12
12
|
getXClient
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-K6FZPWXD.js";
|
|
14
14
|
import "./chunk-T3U56JW4.js";
|
|
15
15
|
import {
|
|
16
16
|
loadIdentity
|
|
@@ -226,4 +226,4 @@ export {
|
|
|
226
226
|
postStatus,
|
|
227
227
|
proposePlan
|
|
228
228
|
};
|
|
229
|
-
//# sourceMappingURL=colony-
|
|
229
|
+
//# sourceMappingURL=colony-4EYP6EPG.js.map
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
executeAction,
|
|
3
3
|
executeActions,
|
|
4
4
|
parseActions
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-XJBOOX7N.js";
|
|
6
|
+
import "./chunk-K6FZPWXD.js";
|
|
7
|
+
import "./chunk-3NW3VIN5.js";
|
|
8
8
|
import "./chunk-T3U56JW4.js";
|
|
9
9
|
import "./chunk-CIWFFTSP.js";
|
|
10
10
|
import "./chunk-E4DZYHGF.js";
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
executeActions,
|
|
16
16
|
parseActions
|
|
17
17
|
};
|
|
18
|
-
//# sourceMappingURL=decision-engine-
|
|
18
|
+
//# sourceMappingURL=decision-engine-YQDGNP3C.js.map
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeActions,
|
|
3
3
|
parseActions
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XJBOOX7N.js";
|
|
5
5
|
import {
|
|
6
6
|
getXClient
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-K6FZPWXD.js";
|
|
8
8
|
import {
|
|
9
9
|
flushQueue
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3NW3VIN5.js";
|
|
11
11
|
import {
|
|
12
12
|
buildHeartbeatUserMessage,
|
|
13
13
|
buildSystemPrompt
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AIOYI7ZF.js";
|
|
15
15
|
import "./chunk-YF7WWJRO.js";
|
|
16
16
|
import {
|
|
17
17
|
generateResponse
|
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
// src/runtime/heartbeat.ts
|
|
32
32
|
import { existsSync, unlinkSync, writeFileSync, readFileSync } from "fs";
|
|
33
33
|
var running = false;
|
|
34
|
-
var lastTimelineSinceId;
|
|
35
34
|
var lastMentionsSinceId;
|
|
36
35
|
function isRunning() {
|
|
37
36
|
return running;
|
|
@@ -155,12 +154,9 @@ async function runHeartbeat(maxActions, intervalMs) {
|
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
156
|
try {
|
|
158
|
-
timeline = await client.getTimeline({ count: 20
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
if (ownHandle) {
|
|
162
|
-
timeline = timeline.filter((t) => t.authorHandle.toLowerCase() !== ownHandle.toLowerCase());
|
|
163
|
-
}
|
|
157
|
+
timeline = await client.getTimeline({ count: 20 });
|
|
158
|
+
if (ownHandle) {
|
|
159
|
+
timeline = timeline.filter((t) => t.authorHandle.toLowerCase() !== ownHandle.toLowerCase());
|
|
164
160
|
}
|
|
165
161
|
} catch (error) {
|
|
166
162
|
logger.warn(`Timeline read failed: ${error.message}`);
|
|
@@ -213,4 +209,4 @@ export {
|
|
|
213
209
|
requestStop,
|
|
214
210
|
startHeartbeatLoop
|
|
215
211
|
};
|
|
216
|
-
//# sourceMappingURL=heartbeat-
|
|
212
|
+
//# sourceMappingURL=heartbeat-5MDXYM3G.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/heartbeat.ts"],"sourcesContent":["import { existsSync, unlinkSync, writeFileSync, readFileSync } from \"node:fs\";\nimport { logger } from \"../utils/logger.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { paths } from \"../utils/paths.js\";\nimport { getXClient } from \"../x-client/index.js\";\nimport { flushQueue } from \"../scheduler/queue.js\";\nimport { buildSystemPrompt, buildHeartbeatUserMessage } from \"./prompt-builder.js\";\nimport { generateResponse } from \"./llm.js\";\nimport { parseActions, executeActions, type ActionResult } from \"./decision-engine.js\";\n\nlet running = false;\nlet lastMentionsSinceId: string | undefined;\n\nexport function isRunning(): boolean {\n return running;\n}\n\nexport function requestStop(): void {\n writeFileSync(paths.stopSignal, \"stop\");\n logger.info(\"Stop signal sent.\");\n}\n\nfunction shouldStop(): boolean {\n if (existsSync(paths.stopSignal)) {\n unlinkSync(paths.stopSignal);\n return true;\n }\n return false;\n}\n\nfunction writePid(): void {\n writeFileSync(paths.runtimePid, String(process.pid));\n}\n\nfunction clearPid(): void {\n if (existsSync(paths.runtimePid)) {\n unlinkSync(paths.runtimePid);\n }\n}\n\nexport function getRunningPid(): number | null {\n if (!existsSync(paths.runtimePid)) return null;\n const pid = parseInt(readFileSync(paths.runtimePid, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return null;\n\n // Check if process is actually running\n try {\n process.kill(pid, 0);\n return pid;\n } catch {\n // Process not running, clean up stale PID\n clearPid();\n return null;\n }\n}\n\nexport async function startHeartbeatLoop(): Promise<void> {\n // Check if already running\n const existingPid = getRunningPid();\n if (existingPid) {\n throw new Error(`Spora is already running (PID ${existingPid}). Run \\`spora stop\\` first.`);\n }\n\n running = true;\n writePid();\n\n const config = loadConfig();\n const intervalMs = config.runtime?.heartbeatIntervalMs ?? 60_000;\n const maxActions = config.runtime?.actionsPerHeartbeat ?? 3;\n\n logger.info(`Spora agent starting. Heartbeat interval: ${intervalMs / 1000}s, max actions: ${maxActions}`);\n console.log(`\\nSpora agent is running (PID ${process.pid})`);\n console.log(`Heartbeat every ${Math.round(intervalMs / 60_000)} minutes`);\n console.log(`Press Ctrl+C or run \\`spora stop\\` to stop.\\n`);\n\n // Handle graceful shutdown\n const shutdown = () => {\n logger.info(\"Shutting down...\");\n running = false;\n clearPid();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // Clean any stale stop signal\n if (existsSync(paths.stopSignal)) {\n unlinkSync(paths.stopSignal);\n }\n\n let heartbeatCount = 0;\n\n while (running) {\n heartbeatCount++;\n logger.info(`=== Heartbeat #${heartbeatCount} ===`);\n\n try {\n await runHeartbeat(maxActions, intervalMs);\n } catch (error) {\n logger.error(\"Heartbeat error\", error);\n console.error(`Heartbeat #${heartbeatCount} failed: ${(error as Error).message}`);\n }\n\n // Check for stop signal\n if (shouldStop()) {\n logger.info(\"Stop signal received.\");\n break;\n }\n\n // Sleep with jitter\n const jitter = Math.floor(Math.random() * intervalMs * 0.3);\n const sleepMs = intervalMs + jitter;\n logger.info(`Sleeping ${Math.round(sleepMs / 1000)}s until next heartbeat...`);\n\n // Sleep in chunks — flush queue during sleep so scheduled posts go out on time\n const chunkMs = 10_000;\n let slept = 0;\n while (slept < sleepMs && running) {\n await new Promise((r) => setTimeout(r, Math.min(chunkMs, sleepMs - slept)));\n slept += chunkMs;\n if (shouldStop()) {\n running = false;\n break;\n }\n\n // Flush queue during sleep so scheduled posts go out at their intended times\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n logger.info(`Flushed ${flushed.posted} scheduled post(s) during sleep`);\n }\n } catch {\n // Queue flush failed during sleep, not critical\n }\n }\n }\n\n clearPid();\n logger.info(\"Spora agent stopped.\");\n console.log(\"\\nSpora agent stopped.\");\n}\n\nasync function runHeartbeat(maxActions: number, intervalMs: number): Promise<void> {\n // 1. Flush any queued posts\n logger.info(\"Checking queue...\");\n try {\n const flushed = await flushQueue();\n if (flushed.posted > 0) {\n logger.info(`Flushed ${flushed.posted} queued posts.`);\n }\n } catch (error) {\n logger.warn(`Queue flush failed: ${(error as Error).message}`);\n }\n\n // 2. Read timeline and mentions (only new since last check)\n logger.info(\"Reading timeline and mentions...\");\n const client = await getXClient();\n\n let timeline: Awaited<ReturnType<typeof client.getTimeline>> = [];\n let mentions: Awaited<ReturnType<typeof client.getMentions>> = [];\n\n // Get own handle to filter own tweets\n let ownHandle: string | undefined;\n if (\"getAuthenticatedHandle\" in client) {\n try {\n ownHandle = await (client as any).getAuthenticatedHandle();\n } catch {}\n }\n\n try {\n // Always fetch latest timeline (no sinceId) so the agent always has context\n timeline = await client.getTimeline({ count: 20 });\n if (ownHandle) {\n timeline = timeline.filter(t => t.authorHandle.toLowerCase() !== ownHandle!.toLowerCase());\n }\n } catch (error) {\n logger.warn(`Timeline read failed: ${(error as Error).message}`);\n }\n\n try {\n mentions = await client.getMentions({ count: 10, sinceId: lastMentionsSinceId });\n if (mentions.length > 0) {\n lastMentionsSinceId = mentions[0].id;\n }\n } catch (error) {\n logger.warn(`Mentions read failed: ${(error as Error).message}`);\n }\n\n // 3. Build prompts\n const systemPrompt = buildSystemPrompt();\n const userMessage = buildHeartbeatUserMessage(timeline, mentions, intervalMs);\n\n // 4. Ask LLM for decisions\n logger.info(\"Asking LLM for decisions...\");\n const response = await generateResponse(systemPrompt, userMessage);\n\n // 5. Parse and execute actions\n const actions = parseActions(response.content);\n if (actions.length === 0) {\n logger.info(\"LLM returned no actions.\");\n return;\n }\n\n // Build set of valid tweet IDs from timeline + mentions to catch hallucinated IDs\n const validTweetIds = new Set<string>();\n for (const t of timeline) validTweetIds.add(t.id);\n for (const t of mentions) validTweetIds.add(t.id);\n\n // Filter out actions with fake/hallucinated tweet IDs\n const validatedActions = actions.filter(a => {\n if (a.tweetId) {\n const cleanId = a.tweetId.replace(/^tweet:/i, \"\").trim();\n if (!validTweetIds.has(cleanId)) {\n logger.warn(`Rejected ${a.action}: tweet ID ${cleanId} not in timeline/mentions (likely hallucinated)`);\n return false;\n }\n }\n return true;\n });\n\n // Limit to max actions per heartbeat\n const limitedActions = validatedActions.slice(0, maxActions);\n logger.info(`Executing ${limitedActions.length} action(s)...`);\n\n const results = await executeActions(limitedActions);\n\n // 6. Log results\n for (const result of results) {\n if (result.success) {\n logger.info(` [OK] ${result.action}${result.detail ? `: ${result.detail}` : \"\"}`);\n } else {\n logger.warn(` [FAIL] ${result.action}: ${result.error}`);\n }\n }\n\n logger.info(`Heartbeat complete. ${results.filter((r) => r.success).length}/${results.length} actions succeeded.`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -203,7 +203,7 @@ async function loginFlow() {
|
|
|
203
203
|
console.log(chalk.green("\u2713 Logged in!\n"));
|
|
204
204
|
console.log(chalk.gray("Opening chat interface...\n"));
|
|
205
205
|
try {
|
|
206
|
-
const { startWebChat } = await import("./web-chat-
|
|
206
|
+
const { startWebChat } = await import("./web-chat-3IEWDNHD.js");
|
|
207
207
|
await startWebChat();
|
|
208
208
|
} catch (error) {
|
|
209
209
|
console.log(chalk.yellow(`Could not start chat interface: ${error.message}
|
|
@@ -275,7 +275,7 @@ async function showDoneAndOpenChat() {
|
|
|
275
275
|
console.log(chalk.bold.cyan("\u2501\u2501\u2501 Your Spore is Ready! \u2501\u2501\u2501\n"));
|
|
276
276
|
console.log(chalk.gray("Opening chat interface...\n"));
|
|
277
277
|
try {
|
|
278
|
-
const { startWebChat } = await import("./web-chat-
|
|
278
|
+
const { startWebChat } = await import("./web-chat-3IEWDNHD.js");
|
|
279
279
|
await startWebChat();
|
|
280
280
|
} catch (error) {
|
|
281
281
|
console.log(chalk.yellow(`Could not start chat interface: ${error.message}
|
|
@@ -400,4 +400,4 @@ async function runInit(token) {
|
|
|
400
400
|
export {
|
|
401
401
|
runInit
|
|
402
402
|
};
|
|
403
|
-
//# sourceMappingURL=init-
|
|
403
|
+
//# sourceMappingURL=init-OVMM7SOZ.js.map
|
package/dist/mcp-server.js
CHANGED
|
@@ -388,7 +388,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
388
388
|
},
|
|
389
389
|
async ({ content }) => {
|
|
390
390
|
try {
|
|
391
|
-
const { getXClient } = await import("./x-client-
|
|
391
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
392
392
|
const client = await getXClient();
|
|
393
393
|
const result = await client.postTweet(content);
|
|
394
394
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -406,7 +406,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
406
406
|
},
|
|
407
407
|
async ({ tweetId, content }) => {
|
|
408
408
|
try {
|
|
409
|
-
const { getXClient } = await import("./x-client-
|
|
409
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
410
410
|
const client = await getXClient();
|
|
411
411
|
const result = await client.replyToTweet(tweetId, content);
|
|
412
412
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -421,7 +421,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
421
421
|
{ tweetId: z.string().describe("The ID of the tweet to like") },
|
|
422
422
|
async ({ tweetId }) => {
|
|
423
423
|
try {
|
|
424
|
-
const { getXClient } = await import("./x-client-
|
|
424
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
425
425
|
const client = await getXClient();
|
|
426
426
|
const result = await client.likeTweet(tweetId);
|
|
427
427
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -436,7 +436,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
436
436
|
{ tweetId: z.string().describe("The ID of the tweet to retweet") },
|
|
437
437
|
async ({ tweetId }) => {
|
|
438
438
|
try {
|
|
439
|
-
const { getXClient } = await import("./x-client-
|
|
439
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
440
440
|
const client = await getXClient();
|
|
441
441
|
const result = await client.retweet(tweetId);
|
|
442
442
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -451,7 +451,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
451
451
|
{ userId: z.string().describe("The user ID to follow") },
|
|
452
452
|
async ({ userId }) => {
|
|
453
453
|
try {
|
|
454
|
-
const { getXClient } = await import("./x-client-
|
|
454
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
455
455
|
const client = await getXClient();
|
|
456
456
|
const result = await client.followUser(userId);
|
|
457
457
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -466,7 +466,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
466
466
|
{ userId: z.string().describe("The user ID to unfollow") },
|
|
467
467
|
async ({ userId }) => {
|
|
468
468
|
try {
|
|
469
|
-
const { getXClient } = await import("./x-client-
|
|
469
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
470
470
|
const client = await getXClient();
|
|
471
471
|
const result = await client.unfollowUser(userId);
|
|
472
472
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -481,7 +481,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
481
481
|
{ count: z.number().optional().describe("Number of tweets to fetch (default 20)") },
|
|
482
482
|
async ({ count }) => {
|
|
483
483
|
try {
|
|
484
|
-
const { getXClient } = await import("./x-client-
|
|
484
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
485
485
|
const client = await getXClient();
|
|
486
486
|
const result = await client.getTimeline({ count: count ?? 20 });
|
|
487
487
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -496,7 +496,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
496
496
|
{ count: z.number().optional().describe("Number of mentions to fetch (default 20)") },
|
|
497
497
|
async ({ count }) => {
|
|
498
498
|
try {
|
|
499
|
-
const { getXClient } = await import("./x-client-
|
|
499
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
500
500
|
const client = await getXClient();
|
|
501
501
|
const result = await client.getMentions({ count: count ?? 20 });
|
|
502
502
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -514,7 +514,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
514
514
|
},
|
|
515
515
|
async ({ query, count }) => {
|
|
516
516
|
try {
|
|
517
|
-
const { getXClient } = await import("./x-client-
|
|
517
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
518
518
|
const client = await getXClient();
|
|
519
519
|
const result = await client.searchTweets(query, { count: count ?? 20 });
|
|
520
520
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -529,7 +529,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
529
529
|
{ handle: z.string().describe("X handle (without @)") },
|
|
530
530
|
async ({ handle }) => {
|
|
531
531
|
try {
|
|
532
|
-
const { getXClient } = await import("./x-client-
|
|
532
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
533
533
|
const client = await getXClient();
|
|
534
534
|
const result = await client.getProfile(handle);
|
|
535
535
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -547,7 +547,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
547
547
|
},
|
|
548
548
|
async ({ content, scheduledFor }) => {
|
|
549
549
|
try {
|
|
550
|
-
const { addToQueue } = await import("./queue-
|
|
550
|
+
const { addToQueue } = await import("./queue-IDNLFXWC.js");
|
|
551
551
|
const entry = addToQueue(content, scheduledFor);
|
|
552
552
|
return {
|
|
553
553
|
content: [
|
|
@@ -565,7 +565,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
565
565
|
{},
|
|
566
566
|
async () => {
|
|
567
567
|
try {
|
|
568
|
-
const { flushQueue } = await import("./queue-
|
|
568
|
+
const { flushQueue } = await import("./queue-IDNLFXWC.js");
|
|
569
569
|
const results = await flushQueue();
|
|
570
570
|
return {
|
|
571
571
|
content: [
|
|
@@ -586,7 +586,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
586
586
|
{ message: z.string().optional().describe("Optional message to post to the Colony community") },
|
|
587
587
|
async ({ message }) => {
|
|
588
588
|
try {
|
|
589
|
-
const { colonyCheckin } = await import("./colony-
|
|
589
|
+
const { colonyCheckin } = await import("./colony-4EYP6EPG.js");
|
|
590
590
|
const result = await colonyCheckin(message);
|
|
591
591
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
592
592
|
} catch (error) {
|
|
@@ -600,7 +600,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
600
600
|
{},
|
|
601
601
|
async () => {
|
|
602
602
|
try {
|
|
603
|
-
const { getColonyMemory } = await import("./colony-
|
|
603
|
+
const { getColonyMemory } = await import("./colony-4EYP6EPG.js");
|
|
604
604
|
const memory = getColonyMemory();
|
|
605
605
|
return { content: [{ type: "text", text: JSON.stringify(memory, null, 2) }] };
|
|
606
606
|
} catch (error) {
|
|
@@ -614,7 +614,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
614
614
|
{},
|
|
615
615
|
async () => {
|
|
616
616
|
try {
|
|
617
|
-
const { getActivePlans } = await import("./colony-
|
|
617
|
+
const { getActivePlans } = await import("./colony-4EYP6EPG.js");
|
|
618
618
|
const plans = getActivePlans();
|
|
619
619
|
return {
|
|
620
620
|
content: [
|
|
@@ -637,7 +637,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
637
637
|
},
|
|
638
638
|
async ({ description }) => {
|
|
639
639
|
try {
|
|
640
|
-
const { proposePlan } = await import("./colony-
|
|
640
|
+
const { proposePlan } = await import("./colony-4EYP6EPG.js");
|
|
641
641
|
const result = await proposePlan(description);
|
|
642
642
|
if (result.success) {
|
|
643
643
|
return {
|
|
@@ -660,7 +660,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
660
660
|
},
|
|
661
661
|
async ({ planId }) => {
|
|
662
662
|
try {
|
|
663
|
-
const { joinPlan } = await import("./colony-
|
|
663
|
+
const { joinPlan } = await import("./colony-4EYP6EPG.js");
|
|
664
664
|
const result = await joinPlan(planId);
|
|
665
665
|
if (result.success) {
|
|
666
666
|
return { content: [{ type: "text", text: "Joined the plan! Go execute it." }] };
|
|
@@ -679,7 +679,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
679
679
|
},
|
|
680
680
|
async ({ status }) => {
|
|
681
681
|
try {
|
|
682
|
-
const { postStatus } = await import("./colony-
|
|
682
|
+
const { postStatus } = await import("./colony-4EYP6EPG.js");
|
|
683
683
|
const result = await postStatus(status);
|
|
684
684
|
if (result.success) {
|
|
685
685
|
return { content: [{ type: "text", text: "Status posted to the Colony." }] };
|
|
@@ -696,7 +696,7 @@ Identity saved. Your Spore is alive. Use spora_get_identity to read the full doc
|
|
|
696
696
|
{},
|
|
697
697
|
async () => {
|
|
698
698
|
try {
|
|
699
|
-
const { getTodaysActivity } = await import("./colony-
|
|
699
|
+
const { getTodaysActivity } = await import("./colony-4EYP6EPG.js");
|
|
700
700
|
const activity = getTodaysActivity();
|
|
701
701
|
return {
|
|
702
702
|
content: [
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
buildHeartbeatUserMessage,
|
|
4
4
|
buildNarratedHeartbeatMessage,
|
|
5
5
|
buildSystemPrompt
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AIOYI7ZF.js";
|
|
7
7
|
import "./chunk-YF7WWJRO.js";
|
|
8
8
|
import "./chunk-T3U56JW4.js";
|
|
9
9
|
import "./chunk-CIWFFTSP.js";
|
|
@@ -16,4 +16,4 @@ export {
|
|
|
16
16
|
buildNarratedHeartbeatMessage,
|
|
17
17
|
buildSystemPrompt
|
|
18
18
|
};
|
|
19
|
-
//# sourceMappingURL=prompt-builder-
|
|
19
|
+
//# sourceMappingURL=prompt-builder-R65RH7KP.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
addToQueue,
|
|
3
3
|
flushQueue,
|
|
4
4
|
showQueue
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-3NW3VIN5.js";
|
|
6
6
|
import "./chunk-T3U56JW4.js";
|
|
7
7
|
import "./chunk-E4DZYHGF.js";
|
|
8
8
|
import "./chunk-5GPXH253.js";
|
|
@@ -11,4 +11,4 @@ export {
|
|
|
11
11
|
flushQueue,
|
|
12
12
|
showQueue
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=queue-
|
|
14
|
+
//# sourceMappingURL=queue-IDNLFXWC.js.map
|
|
@@ -303,7 +303,7 @@ async function extractAndSaveLearnings(responseText) {
|
|
|
303
303
|
return responseText.replace(/<<LEARN:\s*.+?>>/g, "").trim();
|
|
304
304
|
}
|
|
305
305
|
async function extractAndExecuteActions(responseText) {
|
|
306
|
-
const { parseActions, executeActions } = await import("./decision-engine-
|
|
306
|
+
const { parseActions, executeActions } = await import("./decision-engine-YQDGNP3C.js");
|
|
307
307
|
const jsonBlockPattern = /```json\s*([\s\S]*?)```/g;
|
|
308
308
|
const blocks = [...responseText.matchAll(jsonBlockPattern)];
|
|
309
309
|
const visibleResults = [];
|
|
@@ -383,7 +383,7 @@ async function startWebChat() {
|
|
|
383
383
|
try {
|
|
384
384
|
const { hasXCredentials } = await import("./paths-IL7YUMNP.js");
|
|
385
385
|
if (hasXCredentials()) {
|
|
386
|
-
const { getXClient } = await import("./x-client-
|
|
386
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
387
387
|
const client = await getXClient();
|
|
388
388
|
if ("getAuthenticatedHandle" in client) {
|
|
389
389
|
realHandle = await client.getAuthenticatedHandle();
|
|
@@ -409,7 +409,7 @@ async function startWebChat() {
|
|
|
409
409
|
const remainingMs = sleepBefore.sleeping && sleepBefore.wakeAt ? Math.max(0, sleepBefore.wakeAt - Date.now()) : 0;
|
|
410
410
|
server.setAwake();
|
|
411
411
|
if (!systemPrompt || messageCount % 10 === 0) {
|
|
412
|
-
const { buildChatPrompt } = await import("./prompt-builder-
|
|
412
|
+
const { buildChatPrompt } = await import("./prompt-builder-R65RH7KP.js");
|
|
413
413
|
systemPrompt = buildChatPrompt(realHandle);
|
|
414
414
|
}
|
|
415
415
|
messageCount++;
|
|
@@ -485,7 +485,6 @@ async function refreshMemoryStats(server) {
|
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
487
|
var heartbeatRunning = false;
|
|
488
|
-
var lastTimelineSinceId;
|
|
489
488
|
var lastMentionsSinceId;
|
|
490
489
|
async function startNarratedHeartbeat(server) {
|
|
491
490
|
const { loadConfig } = await import("./config-BRWV7X4S.js");
|
|
@@ -537,7 +536,7 @@ async function startNarratedHeartbeat(server) {
|
|
|
537
536
|
await new Promise((r) => setTimeout(r, Math.min(1e4, sleepMs - slept)));
|
|
538
537
|
slept += 1e4;
|
|
539
538
|
try {
|
|
540
|
-
const { flushQueue } = await import("./queue-
|
|
539
|
+
const { flushQueue } = await import("./queue-IDNLFXWC.js");
|
|
541
540
|
const flushed = await flushQueue();
|
|
542
541
|
if (flushed.posted > 0) {
|
|
543
542
|
console.log(chalk.green(` [Queue] Posted ${flushed.posted} scheduled tweet(s)`));
|
|
@@ -549,11 +548,11 @@ async function startNarratedHeartbeat(server) {
|
|
|
549
548
|
}
|
|
550
549
|
}
|
|
551
550
|
async function runNarratedHeartbeat(server, maxActions, intervalMs) {
|
|
552
|
-
const { getXClient } = await import("./x-client-
|
|
553
|
-
const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import("./prompt-builder-
|
|
551
|
+
const { getXClient } = await import("./x-client-DFMW2PX7.js");
|
|
552
|
+
const { buildSystemPrompt, buildNarratedHeartbeatMessage } = await import("./prompt-builder-R65RH7KP.js");
|
|
554
553
|
const { generateResponse } = await import("./llm-LQ46ZUY5.js");
|
|
555
|
-
const { parseActions, executeActions } = await import("./decision-engine-
|
|
556
|
-
const { flushQueue } = await import("./queue-
|
|
554
|
+
const { parseActions, executeActions } = await import("./decision-engine-YQDGNP3C.js");
|
|
555
|
+
const { flushQueue } = await import("./queue-IDNLFXWC.js");
|
|
557
556
|
try {
|
|
558
557
|
const flushed = await flushQueue();
|
|
559
558
|
if (flushed.posted > 0) {
|
|
@@ -572,12 +571,9 @@ async function runNarratedHeartbeat(server, maxActions, intervalMs) {
|
|
|
572
571
|
}
|
|
573
572
|
}
|
|
574
573
|
try {
|
|
575
|
-
timeline = await client.getTimeline({ count: 20
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
if (ownHandle) {
|
|
579
|
-
timeline = timeline.filter((t) => t.authorHandle.toLowerCase() !== ownHandle.toLowerCase());
|
|
580
|
-
}
|
|
574
|
+
timeline = await client.getTimeline({ count: 20 });
|
|
575
|
+
if (ownHandle) {
|
|
576
|
+
timeline = timeline.filter((t) => t.authorHandle.toLowerCase() !== ownHandle.toLowerCase());
|
|
581
577
|
}
|
|
582
578
|
} catch (error) {
|
|
583
579
|
console.log(chalk.dim(` [Heartbeat] Timeline read failed: ${error.message}`));
|
|
@@ -697,4 +693,4 @@ export {
|
|
|
697
693
|
openBrowser,
|
|
698
694
|
startWebChat
|
|
699
695
|
};
|
|
700
|
-
//# sourceMappingURL=web-chat-
|
|
696
|
+
//# sourceMappingURL=web-chat-3IEWDNHD.js.map
|