spora 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +7 -5
  2. package/dist/autonomy-DAV7X6QS.js +19 -0
  3. package/dist/{chunk-53YLFYJF.js → chunk-3RYCUGXE.js} +6 -2
  4. package/dist/chunk-3RYCUGXE.js.map +1 -0
  5. package/dist/{chunk-AH7HPXYC.js → chunk-AOQ3WLZV.js} +153 -143
  6. package/dist/chunk-AOQ3WLZV.js.map +1 -0
  7. package/dist/chunk-E5NR6HT4.js +29 -0
  8. package/dist/chunk-E5NR6HT4.js.map +1 -0
  9. package/dist/{chunk-EBO4F5NU.js → chunk-JBYZ7K56.js} +2 -2
  10. package/dist/chunk-KWWAIS3C.js +180 -0
  11. package/dist/chunk-KWWAIS3C.js.map +1 -0
  12. package/dist/{chunk-UINSD4FT.js → chunk-LXQNVVIY.js} +6 -6
  13. package/dist/{chunk-UINSD4FT.js.map → chunk-LXQNVVIY.js.map} +1 -1
  14. package/dist/{chunk-AIEXQCQS.js → chunk-M6YOQVSI.js} +2 -2
  15. package/dist/{chunk-B6RPMDML.js → chunk-NO3NQN67.js} +16 -6
  16. package/dist/chunk-NO3NQN67.js.map +1 -0
  17. package/dist/{chunk-QOKQ5OTU.js → chunk-NPV3OV2K.js} +3 -14
  18. package/dist/chunk-NPV3OV2K.js.map +1 -0
  19. package/dist/{chunk-SBQILQCJ.js → chunk-OACD3HGE.js} +7 -7
  20. package/dist/{chunk-UM57WU5I.js → chunk-P6KZIJYL.js} +2 -2
  21. package/dist/{chunk-AHXZIGQE.js → chunk-T7L2L7ZL.js} +2 -2
  22. package/dist/{chunk-ZJZKH7N7.js → chunk-VZBHRUZS.js} +2 -2
  23. package/dist/chunk-VZBHRUZS.js.map +1 -0
  24. package/dist/chunk-WIK74GGJ.js +295 -0
  25. package/dist/chunk-WIK74GGJ.js.map +1 -0
  26. package/dist/{chunk-YLJVFCT4.js → chunk-WN35MRMF.js} +2 -2
  27. package/dist/cli.js +173 -138
  28. package/dist/cli.js.map +1 -1
  29. package/dist/client-57BQKVYF.js +337 -0
  30. package/dist/client-57BQKVYF.js.map +1 -0
  31. package/dist/{colony-LCWN5IAN.js → colony-JPZC3R34.js} +7 -7
  32. package/dist/{config-TFAFYSIW.js → config-FL4VJVKZ.js} +3 -3
  33. package/dist/{crypto-FHSQ72NU.js → crypto-NOXNL4GP.js} +3 -3
  34. package/dist/{goals-5TAPXNR2.js → goals-RBKLMILE.js} +3 -3
  35. package/dist/{heartbeat-ZHRCEMF5.js → heartbeat-TNEPE3ZP.js} +83 -88
  36. package/dist/heartbeat-TNEPE3ZP.js.map +1 -0
  37. package/dist/{identity-O4FLSZKZ.js → identity-VDUW4I2K.js} +3 -3
  38. package/dist/{init-G3WINLAP.js → init-ISSXETHY.js} +59 -46
  39. package/dist/init-ISSXETHY.js.map +1 -0
  40. package/dist/llm-T33QTPVW.js +22 -0
  41. package/dist/mcp-server.js +28 -28
  42. package/dist/mcp-server.js.map +1 -1
  43. package/dist/{memory-O3AJIKBX.js → memory-OIAH33G2.js} +3 -3
  44. package/dist/{memory-7FBE26K3.js → memory-PNW7SX7A.js} +3 -3
  45. package/dist/{paths-5GFUUHCZ.js → paths-BYR6MEPR.js} +2 -2
  46. package/dist/prompt-builder-5NYONN2W.js +23 -0
  47. package/dist/queue-G5PTE6R6.js +14 -0
  48. package/dist/{strategy-S45TX766.js → strategy-Z4JSFHSP.js} +3 -3
  49. package/dist/{web-chat-RQIILEQK.js → web-chat-3HM35XM4.js} +31 -80
  50. package/dist/web-chat-3HM35XM4.js.map +1 -0
  51. package/dist/x-client-GY6XSPK6.js +12 -0
  52. package/package.json +1 -1
  53. package/dist/account-creator-ZD643X3Z.js +0 -498
  54. package/dist/account-creator-ZD643X3Z.js.map +0 -1
  55. package/dist/chunk-535NMUUW.js +0 -96
  56. package/dist/chunk-535NMUUW.js.map +0 -1
  57. package/dist/chunk-53YLFYJF.js.map +0 -1
  58. package/dist/chunk-55XPDJ6P.js +0 -124
  59. package/dist/chunk-55XPDJ6P.js.map +0 -1
  60. package/dist/chunk-AH7HPXYC.js.map +0 -1
  61. package/dist/chunk-B6RPMDML.js.map +0 -1
  62. package/dist/chunk-E6GMS76S.js +0 -154
  63. package/dist/chunk-E6GMS76S.js.map +0 -1
  64. package/dist/chunk-JJZ7T2IZ.js +0 -32
  65. package/dist/chunk-JJZ7T2IZ.js.map +0 -1
  66. package/dist/chunk-QOKQ5OTU.js.map +0 -1
  67. package/dist/chunk-TF2XYGGG.js +0 -249
  68. package/dist/chunk-TF2XYGGG.js.map +0 -1
  69. package/dist/chunk-ZJZKH7N7.js.map +0 -1
  70. package/dist/client-B6NGVRHM.js +0 -381
  71. package/dist/client-B6NGVRHM.js.map +0 -1
  72. package/dist/client-DDCS5FJS.js +0 -412
  73. package/dist/client-DDCS5FJS.js.map +0 -1
  74. package/dist/decision-engine-DRPIZLHI.js +0 -19
  75. package/dist/heartbeat-ZHRCEMF5.js.map +0 -1
  76. package/dist/init-G3WINLAP.js.map +0 -1
  77. package/dist/llm-3LSNADSR.js +0 -16
  78. package/dist/prompt-builder-U2J4H7YX.js +0 -24
  79. package/dist/queue-USY7JXDV.js +0 -14
  80. package/dist/research-TQLP42BC.js +0 -13
  81. package/dist/web-chat-RQIILEQK.js.map +0 -1
  82. package/dist/x-client-TYU5QSLG.js +0 -12
  83. package/dist/x-client-TYU5QSLG.js.map +0 -1
  84. /package/dist/{config-TFAFYSIW.js.map → autonomy-DAV7X6QS.js.map} +0 -0
  85. /package/dist/{chunk-EBO4F5NU.js.map → chunk-JBYZ7K56.js.map} +0 -0
  86. /package/dist/{chunk-AIEXQCQS.js.map → chunk-M6YOQVSI.js.map} +0 -0
  87. /package/dist/{chunk-SBQILQCJ.js.map → chunk-OACD3HGE.js.map} +0 -0
  88. /package/dist/{chunk-UM57WU5I.js.map → chunk-P6KZIJYL.js.map} +0 -0
  89. /package/dist/{chunk-AHXZIGQE.js.map → chunk-T7L2L7ZL.js.map} +0 -0
  90. /package/dist/{chunk-YLJVFCT4.js.map → chunk-WN35MRMF.js.map} +0 -0
  91. /package/dist/{colony-LCWN5IAN.js.map → colony-JPZC3R34.js.map} +0 -0
  92. /package/dist/{crypto-FHSQ72NU.js.map → config-FL4VJVKZ.js.map} +0 -0
  93. /package/dist/{decision-engine-DRPIZLHI.js.map → crypto-NOXNL4GP.js.map} +0 -0
  94. /package/dist/{goals-5TAPXNR2.js.map → goals-RBKLMILE.js.map} +0 -0
  95. /package/dist/{identity-O4FLSZKZ.js.map → identity-VDUW4I2K.js.map} +0 -0
  96. /package/dist/{llm-3LSNADSR.js.map → llm-T33QTPVW.js.map} +0 -0
  97. /package/dist/{memory-7FBE26K3.js.map → memory-OIAH33G2.js.map} +0 -0
  98. /package/dist/{memory-O3AJIKBX.js.map → memory-PNW7SX7A.js.map} +0 -0
  99. /package/dist/{paths-5GFUUHCZ.js.map → paths-BYR6MEPR.js.map} +0 -0
  100. /package/dist/{prompt-builder-U2J4H7YX.js.map → prompt-builder-5NYONN2W.js.map} +0 -0
  101. /package/dist/{queue-USY7JXDV.js.map → queue-G5PTE6R6.js.map} +0 -0
  102. /package/dist/{research-TQLP42BC.js.map → strategy-Z4JSFHSP.js.map} +0 -0
  103. /package/dist/{strategy-S45TX766.js.map → x-client-GY6XSPK6.js.map} +0 -0
@@ -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 { logger } from \"../../utils/logger.js\";\nimport { logInteraction } from \"../../memory/index.js\";\nimport { rateLimiter } from \"../rate-limiter.js\";\nimport type {\n XClientInterface,\n Tweet,\n UserProfile,\n PostResult,\n TimelineOptions,\n SearchOptions,\n} from \"../types.js\";\n\ninterface TwitterResponseMeta {\n next_token?: string;\n}\n\ninterface TwitterUser {\n id: string;\n username: string;\n name?: string;\n description?: string;\n verified?: boolean;\n profile_image_url?: string;\n public_metrics?: {\n followers_count?: number;\n following_count?: number;\n tweet_count?: number;\n };\n}\n\ninterface TwitterTweet {\n id: string;\n text: string;\n author_id?: string;\n created_at?: string;\n in_reply_to_user_id?: string;\n public_metrics?: {\n like_count?: number;\n retweet_count?: number;\n reply_count?: number;\n };\n}\n\ninterface TwitterTimelineResponse {\n data?: TwitterTweet[];\n includes?: {\n users?: TwitterUser[];\n };\n meta?: TwitterResponseMeta;\n}\n\nexport class XApiClient implements XClientInterface {\n private readonly readWrite: TwitterApi;\n private readonly readOnly: TwitterApi;\n private meId: string | null = null;\n\n constructor() {\n const creds = loadCredentials();\n if (creds.method !== \"api\") {\n throw new Error(\"Only API credentials are supported.\");\n }\n\n if (!creds.apiKey || !creds.apiSecret || !creds.accessToken || !creds.accessTokenSecret) {\n throw new Error(\"Missing X OAuth credentials. Run `spora init` and provide API keys.\");\n }\n\n this.readWrite = new TwitterApi({\n appKey: creds.apiKey,\n appSecret: creds.apiSecret,\n accessToken: creds.accessToken,\n accessSecret: creds.accessTokenSecret,\n });\n\n this.readOnly = creds.bearerToken\n ? new TwitterApi(creds.bearerToken)\n : this.readWrite;\n }\n\n private toTweet(tweet: TwitterTweet, userMap: Map<string, string>): Tweet {\n const authorId = tweet.author_id ?? \"unknown\";\n return {\n id: tweet.id,\n text: tweet.text,\n authorId,\n authorHandle: userMap.get(authorId) ?? \"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 inReplyToId: tweet.in_reply_to_user_id,\n };\n }\n\n private async getAuthenticatedUserId(): Promise<string> {\n if (this.meId) return this.meId;\n\n const me = (await this.readWrite.v2.me({\n \"user.fields\": [\"id\"],\n })) as { data?: { id: string } };\n\n if (!me.data?.id) {\n throw new Error(\"Unable to resolve authenticated X user ID.\");\n }\n\n this.meId = me.data.id;\n return this.meId;\n }\n\n private normalizeHandleOrId(input: string): { isId: boolean; value: string } {\n const cleaned = input.trim().replace(/^@/, \"\");\n const isId = /^\\d+$/.test(cleaned);\n return { isId, value: cleaned };\n }\n\n private async resolveUserId(handleOrId: string): Promise<string> {\n const parsed = this.normalizeHandleOrId(handleOrId);\n if (parsed.isId) return parsed.value;\n\n const user = (await this.readOnly.v2.userByUsername(parsed.value, {\n \"user.fields\": [\"id\", \"username\"],\n })) as { data?: { id: string } };\n\n if (!user.data?.id) {\n throw new Error(`Could not resolve @${parsed.value} to a user ID`);\n }\n\n return user.data.id;\n }\n\n private buildUserMap(users: TwitterUser[] | undefined): Map<string, string> {\n const map = new Map<string, string>();\n for (const user of users ?? []) {\n map.set(user.id, user.username);\n }\n return map;\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 response = (await this.readWrite.v2.tweet(content)) as { data?: { id?: string } };\n const tweetId = response.data?.id;\n\n if (!tweetId) {\n return { success: false, error: \"X API did not return a tweet ID.\" };\n }\n\n rateLimiter.consume(1);\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"post\",\n tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId };\n } catch (error) {\n logger.error(\"Failed to post tweet\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async replyToTweet(tweetId: string, content: string): Promise<PostResult> {\n if (!rateLimiter.canPost()) {\n return { success: false, error: \"Monthly post limit reached\" };\n }\n\n try {\n const response = (await this.readWrite.v2.reply(content, tweetId)) as {\n data?: { id?: string };\n };\n const createdId = response.data?.id;\n\n if (!createdId) {\n return { success: false, error: \"X API did not return a reply ID.\" };\n }\n\n rateLimiter.consume(1);\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"reply\",\n tweetId: createdId,\n inReplyTo: tweetId,\n content,\n creditsUsed: 1,\n success: true,\n });\n\n return { success: true, tweetId: createdId };\n } catch (error) {\n logger.error(\"Failed to reply\", error);\n return { success: false, error: (error as Error).message };\n }\n }\n\n async deleteTweet(tweetId: string): Promise<PostResult> {\n try {\n await this.readWrite.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 meId = await this.getAuthenticatedUserId();\n await this.readWrite.v2.like(meId, 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 meId = await this.getAuthenticatedUserId();\n await this.readWrite.v2.unlike(meId, 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 meId = await this.getAuthenticatedUserId();\n await this.readWrite.v2.retweet(meId, 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 meId = await this.getAuthenticatedUserId();\n await this.readWrite.v2.unretweet(meId, tweetId);\n return { success: true, tweetId };\n } catch (error) {\n return { success: false, error: (error as Error).message };\n }\n }\n\n async followUser(handleOrId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Follow\");\n try {\n const meId = await this.getAuthenticatedUserId();\n const targetId = await this.resolveUserId(handleOrId);\n await this.readWrite.v2.follow(meId, targetId);\n\n logInteraction({\n id: `int-${Date.now()}`,\n timestamp: new Date().toISOString(),\n type: \"follow\",\n targetUserId: targetId,\n targetHandle: handleOrId.replace(/^@/, \"\"),\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(handleOrId: string): Promise<PostResult> {\n rateLimiter.requireBasicTier(\"Unfollow\");\n try {\n const meId = await this.getAuthenticatedUserId();\n const targetId = await this.resolveUserId(handleOrId);\n await this.readWrite.v2.unfollow(meId, targetId);\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 meId = await this.getAuthenticatedUserId();\n\n const response = (await this.readOnly.v2.get(\n `users/${meId}/timelines/reverse_chronological`,\n {\n max_results: options?.count ?? 20,\n since_id: options?.sinceId,\n expansions: [\"author_id\"],\n \"tweet.fields\": [\n \"created_at\",\n \"public_metrics\",\n \"in_reply_to_user_id\",\n \"author_id\",\n ],\n \"user.fields\": [\"username\"],\n }\n )) as TwitterTimelineResponse;\n\n if (!response.data?.length) return [];\n\n const users = this.buildUserMap(response.includes?.users);\n return response.data.map((tweet) => this.toTweet(tweet, users));\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 meId = await this.getAuthenticatedUserId();\n const response = (await this.readOnly.v2.get(`users/${meId}/mentions`, {\n max_results: options?.count ?? 20,\n since_id: options?.sinceId,\n expansions: [\"author_id\"],\n \"tweet.fields\": [\"created_at\", \"public_metrics\", \"author_id\"],\n \"user.fields\": [\"username\"],\n })) as TwitterTimelineResponse;\n\n if (!response.data?.length) return [];\n\n const users = this.buildUserMap(response.includes?.users);\n return response.data.map((tweet) => this.toTweet(tweet, users));\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 paginator = await this.readOnly.v2.search(query, {\n max_results: Math.min(options?.count ?? 20, 100),\n expansions: [\"author_id\"],\n \"tweet.fields\": [\"created_at\", \"public_metrics\", \"author_id\"],\n \"user.fields\": [\"username\"],\n });\n\n const tweets: TwitterTweet[] = [];\n const users = new Map<string, string>();\n\n for await (const tweet of paginator) {\n tweets.push(tweet as unknown as TwitterTweet);\n if (tweets.length >= (options?.count ?? 20)) break;\n }\n\n const includeUsers = (paginator as unknown as { includes?: { users?: TwitterUser[] } }).includes?.users;\n for (const user of includeUsers ?? []) {\n users.set(user.id, user.username);\n }\n\n return tweets.map((tweet) => this.toTweet(tweet, users));\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\n const cleanedHandle = handle.trim().replace(/^@/, \"\");\n const response = (await this.readOnly.v2.userByUsername(cleanedHandle, {\n \"user.fields\": [\n \"description\",\n \"public_metrics\",\n \"verified\",\n \"profile_image_url\",\n \"username\",\n \"name\",\n ],\n })) as { data?: TwitterUser };\n\n if (!response.data) {\n throw new Error(`Profile not found for @${cleanedHandle}`);\n }\n\n return {\n id: response.data.id,\n handle: response.data.username,\n name: response.data.name ?? response.data.username,\n bio: response.data.description ?? \"\",\n followersCount: response.data.public_metrics?.followers_count ?? 0,\n followingCount: response.data.public_metrics?.following_count ?? 0,\n tweetCount: response.data.public_metrics?.tweet_count ?? 0,\n verified: response.data.verified ?? false,\n profileImageUrl: response.data.profile_image_url,\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -7,18 +7,18 @@ import {
7
7
  loadColonyMemory,
8
8
  renderColonyBriefing,
9
9
  saveColonyMemory
10
- } from "./chunk-AHXZIGQE.js";
10
+ } from "./chunk-T7L2L7ZL.js";
11
11
  import {
12
12
  getXClient
13
- } from "./chunk-JJZ7T2IZ.js";
14
- import "./chunk-B6RPMDML.js";
13
+ } from "./chunk-E5NR6HT4.js";
15
14
  import {
16
15
  loadIdentity
17
- } from "./chunk-AIEXQCQS.js";
16
+ } from "./chunk-M6YOQVSI.js";
18
17
  import {
19
18
  logger
20
- } from "./chunk-QOKQ5OTU.js";
21
- import "./chunk-53YLFYJF.js";
19
+ } from "./chunk-NPV3OV2K.js";
20
+ import "./chunk-NO3NQN67.js";
21
+ import "./chunk-3RYCUGXE.js";
22
22
 
23
23
  // src/colony/index.ts
24
24
  var COLONY_TAG = "#SporaColony";
@@ -226,4 +226,4 @@ export {
226
226
  postStatus,
227
227
  proposePlan
228
228
  };
229
- //# sourceMappingURL=colony-LCWN5IAN.js.map
229
+ //# sourceMappingURL=colony-JPZC3R34.js.map
@@ -3,12 +3,12 @@ import {
3
3
  createDefaultConfig,
4
4
  loadConfig,
5
5
  saveConfig
6
- } from "./chunk-B6RPMDML.js";
7
- import "./chunk-53YLFYJF.js";
6
+ } from "./chunk-NO3NQN67.js";
7
+ import "./chunk-3RYCUGXE.js";
8
8
  export {
9
9
  ConfigSchema,
10
10
  createDefaultConfig,
11
11
  loadConfig,
12
12
  saveConfig
13
13
  };
14
- //# sourceMappingURL=config-TFAFYSIW.js.map
14
+ //# sourceMappingURL=config-FL4VJVKZ.js.map
@@ -3,12 +3,12 @@ import {
3
3
  encrypt,
4
4
  loadCredentials,
5
5
  saveCredentials
6
- } from "./chunk-ZJZKH7N7.js";
7
- import "./chunk-53YLFYJF.js";
6
+ } from "./chunk-VZBHRUZS.js";
7
+ import "./chunk-3RYCUGXE.js";
8
8
  export {
9
9
  decrypt,
10
10
  encrypt,
11
11
  loadCredentials,
12
12
  saveCredentials
13
13
  };
14
- //# sourceMappingURL=crypto-FHSQ72NU.js.map
14
+ //# sourceMappingURL=crypto-NOXNL4GP.js.map
@@ -2,11 +2,11 @@ import {
2
2
  loadGoals,
3
3
  renderGoalsForPrompt,
4
4
  saveGoals
5
- } from "./chunk-YLJVFCT4.js";
6
- import "./chunk-53YLFYJF.js";
5
+ } from "./chunk-WN35MRMF.js";
6
+ import "./chunk-3RYCUGXE.js";
7
7
  export {
8
8
  loadGoals,
9
9
  renderGoalsForPrompt,
10
10
  saveGoals
11
11
  };
12
- //# sourceMappingURL=goals-5TAPXNR2.js.map
12
+ //# sourceMappingURL=goals-RBKLMILE.js.map
@@ -1,49 +1,75 @@
1
1
  import {
2
- executeActions,
3
- parseActions
4
- } from "./chunk-TF2XYGGG.js";
5
- import {
6
- runResearchPhase
7
- } from "./chunk-E6GMS76S.js";
8
- import {
9
- getXClient
10
- } from "./chunk-JJZ7T2IZ.js";
2
+ runAutonomyCycle
3
+ } from "./chunk-WIK74GGJ.js";
4
+ import "./chunk-E5NR6HT4.js";
11
5
  import {
12
6
  flushQueue
13
- } from "./chunk-SBQILQCJ.js";
14
- import {
15
- buildHeartbeatUserMessage,
16
- buildReflectionPrompt,
17
- buildSystemPrompt
18
- } from "./chunk-AH7HPXYC.js";
19
- import "./chunk-535NMUUW.js";
20
- import {
21
- loadStrategy,
22
- saveStrategy
23
- } from "./chunk-UM57WU5I.js";
24
- import "./chunk-YLJVFCT4.js";
25
- import "./chunk-UINSD4FT.js";
26
- import {
27
- generateResponse
28
- } from "./chunk-55XPDJ6P.js";
29
- import {
30
- loadConfig
31
- } from "./chunk-B6RPMDML.js";
32
- import {
33
- loadIdentity
34
- } from "./chunk-AIEXQCQS.js";
7
+ } from "./chunk-OACD3HGE.js";
8
+ import "./chunk-AOQ3WLZV.js";
9
+ import "./chunk-P6KZIJYL.js";
10
+ import "./chunk-WN35MRMF.js";
11
+ import "./chunk-LXQNVVIY.js";
12
+ import "./chunk-M6YOQVSI.js";
13
+ import "./chunk-KWWAIS3C.js";
35
14
  import {
36
15
  logger
37
- } from "./chunk-QOKQ5OTU.js";
16
+ } from "./chunk-NPV3OV2K.js";
38
17
  import {
39
- addLearning
40
- } from "./chunk-EBO4F5NU.js";
18
+ loadConfig
19
+ } from "./chunk-NO3NQN67.js";
20
+ import "./chunk-JBYZ7K56.js";
41
21
  import {
22
+ ensureDirectories,
42
23
  paths
43
- } from "./chunk-53YLFYJF.js";
24
+ } from "./chunk-3RYCUGXE.js";
44
25
 
45
26
  // src/runtime/heartbeat.ts
46
27
  import { existsSync, unlinkSync, writeFileSync, readFileSync } from "fs";
28
+
29
+ // src/runtime/telemetry.ts
30
+ import { appendFileSync } from "fs";
31
+ function normalize(text) {
32
+ return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
33
+ }
34
+ function prefix(text, n = 6) {
35
+ return normalize(text).split(" ").filter(Boolean).slice(0, n).join(" ");
36
+ }
37
+ function repeatedFormatRate(actions) {
38
+ const posts = actions.filter((action) => action.action === "post" && action.content).map((action) => action.content ?? "");
39
+ if (posts.length <= 1) return 0;
40
+ const seen = /* @__PURE__ */ new Map();
41
+ for (const content of posts) {
42
+ const key = prefix(content);
43
+ seen.set(key, (seen.get(key) ?? 0) + 1);
44
+ }
45
+ let repeated = 0;
46
+ for (const count of seen.values()) {
47
+ if (count > 1) repeated += count;
48
+ }
49
+ return repeated / posts.length;
50
+ }
51
+ function writeHeartbeatMetrics(input) {
52
+ const interactionCount = input.actions.filter((a) => ["reply", "like", "retweet", "follow"].includes(a.action)).length;
53
+ const postCount = input.actions.filter((a) => a.action === "post").length;
54
+ const successCount = input.results.filter((r) => r.success).length;
55
+ const metrics = {
56
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
57
+ timelineCount: input.timelineCount,
58
+ mentionsCount: input.mentionsCount,
59
+ actionCount: input.actions.length,
60
+ successCount,
61
+ postCount,
62
+ interactionCount,
63
+ interactionRatio: input.actions.length > 0 ? interactionCount / input.actions.length : 0,
64
+ repeatedFormatRate: repeatedFormatRate(input.actions),
65
+ policyRejectionCount: input.policyFeedbackCount
66
+ };
67
+ ensureDirectories();
68
+ appendFileSync(paths.runtimeMetrics, JSON.stringify(metrics) + "\n");
69
+ return metrics;
70
+ }
71
+
72
+ // src/runtime/heartbeat.ts
47
73
  var running = false;
48
74
  function isRunning() {
49
75
  return running;
@@ -111,7 +137,7 @@ Spora agent is running (PID ${process.pid})`);
111
137
  heartbeatCount++;
112
138
  logger.info(`=== Heartbeat #${heartbeatCount} ===`);
113
139
  try {
114
- await runHeartbeat(maxActions, heartbeatCount);
140
+ await runHeartbeat(maxActions);
115
141
  } catch (error) {
116
142
  logger.error("Heartbeat error", error);
117
143
  console.error(`Heartbeat #${heartbeatCount} failed: ${error.message}`);
@@ -138,7 +164,7 @@ Spora agent is running (PID ${process.pid})`);
138
164
  logger.info("Spora agent stopped.");
139
165
  console.log("\nSpora agent stopped.");
140
166
  }
141
- async function runHeartbeat(maxActions, heartbeatCount) {
167
+ async function runHeartbeat(maxActions) {
142
168
  logger.info("Checking queue...");
143
169
  try {
144
170
  const flushed = await flushQueue();
@@ -148,65 +174,34 @@ async function runHeartbeat(maxActions, heartbeatCount) {
148
174
  } catch (error) {
149
175
  logger.warn(`Queue flush failed: ${error.message}`);
150
176
  }
151
- logger.info("Running research phase...");
152
- const client = await getXClient();
153
- const research = await runResearchPhase(client, heartbeatCount);
154
- logger.info(
155
- `Research: ${research.timeline.length} timeline, ${research.mentions.length} mentions, ${research.topicSearchResults.length} topic searches, ${research.peopleActivity.length} people checked`
156
- );
157
- const systemPrompt = buildSystemPrompt();
158
- const userMessage = buildHeartbeatUserMessage(research);
159
- logger.info("Asking LLM for decisions...");
160
- const response = await generateResponse(systemPrompt, userMessage);
161
- const actions = parseActions(response.content);
162
- if (actions.length === 0) {
163
- logger.info("LLM returned no actions.");
177
+ const cycle = await runAutonomyCycle(maxActions);
178
+ logger.info(`Observed ${cycle.timeline.length} timeline posts and ${cycle.mentions.length} mentions.`);
179
+ if (cycle.actions.length === 0) {
180
+ logger.info("Planner returned no approved actions.");
164
181
  return;
165
182
  }
166
- const limitedActions = actions.slice(0, maxActions);
167
- logger.info(`Executing ${limitedActions.length} action(s)...`);
168
- const results = await executeActions(limitedActions);
169
- for (const result of results) {
183
+ logger.info(`Executed ${cycle.results.length} action(s).`);
184
+ for (const result of cycle.results) {
170
185
  if (result.success) {
171
186
  logger.info(` [OK] ${result.action}${result.detail ? `: ${result.detail}` : ""}`);
172
187
  } else {
173
188
  logger.warn(` [FAIL] ${result.action}: ${result.error}`);
174
189
  }
175
190
  }
176
- logger.info(`Heartbeat complete. ${results.filter((r) => r.success).length}/${results.length} actions succeeded.`);
177
- if (heartbeatCount % 3 === 0) {
178
- try {
179
- logger.info("Running reflection phase...");
180
- const reflectionPrompt = buildReflectionPrompt(results);
181
- const reflectionResponse = await generateResponse(
182
- `You are ${loadIdentity().name}. Reflect honestly on your performance.`,
183
- reflectionPrompt
184
- );
185
- const jsonMatch = reflectionResponse.content.match(/\{[\s\S]*\}/);
186
- if (jsonMatch) {
187
- try {
188
- const reflection = JSON.parse(jsonMatch[0]);
189
- if (reflection.learning && reflection.learning !== "null") {
190
- addLearning(reflection.learning, "reflection", ["heartbeat", "performance"]);
191
- logger.info(`Reflection learning: ${reflection.learning}`);
192
- }
193
- if (reflection.strategyUpdate && reflection.strategyUpdate !== "null") {
194
- const strategy = loadStrategy();
195
- strategy.experiments.push({
196
- description: reflection.strategyUpdate,
197
- status: "pending"
198
- });
199
- strategy.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
200
- saveStrategy(strategy);
201
- logger.info(`Strategy update: ${reflection.strategyUpdate}`);
202
- }
203
- } catch {
204
- }
205
- }
206
- } catch (err) {
207
- logger.warn(`Reflection failed: ${err.message}`);
208
- }
191
+ for (const note of cycle.policyFeedback) {
192
+ logger.info(` [POLICY] ${note}`);
209
193
  }
194
+ const metrics = writeHeartbeatMetrics({
195
+ timelineCount: cycle.timeline.length,
196
+ mentionsCount: cycle.mentions.length,
197
+ actions: cycle.actions,
198
+ results: cycle.results,
199
+ policyFeedbackCount: cycle.policyFeedback.length
200
+ });
201
+ logger.info(
202
+ `Metrics: interactionRatio=${metrics.interactionRatio.toFixed(2)}, repeatedFormatRate=${metrics.repeatedFormatRate.toFixed(2)}`
203
+ );
204
+ logger.info(`Heartbeat complete. ${cycle.results.filter((r) => r.success).length}/${cycle.results.length} actions succeeded.`);
210
205
  }
211
206
  export {
212
207
  getRunningPid,
@@ -214,4 +209,4 @@ export {
214
209
  requestStop,
215
210
  startHeartbeatLoop
216
211
  };
217
- //# sourceMappingURL=heartbeat-ZHRCEMF5.js.map
212
+ //# sourceMappingURL=heartbeat-TNEPE3ZP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/heartbeat.ts","../src/runtime/telemetry.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 { flushQueue } from \"../scheduler/queue.js\";\nimport { runAutonomyCycle } from \"./autonomy.js\";\nimport { writeHeartbeatMetrics } from \"./telemetry.js\";\n\nlet running = false;\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 ?? 300_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);\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 so we can check for stop signals\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 }\n\n clearPid();\n logger.info(\"Spora agent stopped.\");\n console.log(\"\\nSpora agent stopped.\");\n}\n\nasync function runHeartbeat(maxActions: 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. Run planner/executor cycle.\n const cycle = await runAutonomyCycle(maxActions);\n logger.info(`Observed ${cycle.timeline.length} timeline posts and ${cycle.mentions.length} mentions.`);\n\n if (cycle.actions.length === 0) {\n logger.info(\"Planner returned no approved actions.\");\n return;\n }\n\n logger.info(`Executed ${cycle.results.length} action(s).`);\n\n // 3. Log results\n for (const result of cycle.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 for (const note of cycle.policyFeedback) {\n logger.info(` [POLICY] ${note}`);\n }\n\n const metrics = writeHeartbeatMetrics({\n timelineCount: cycle.timeline.length,\n mentionsCount: cycle.mentions.length,\n actions: cycle.actions,\n results: cycle.results,\n policyFeedbackCount: cycle.policyFeedback.length,\n });\n logger.info(\n `Metrics: interactionRatio=${metrics.interactionRatio.toFixed(2)}, repeatedFormatRate=${metrics.repeatedFormatRate.toFixed(2)}`\n );\n\n logger.info(`Heartbeat complete. ${cycle.results.filter((r) => r.success).length}/${cycle.results.length} actions succeeded.`);\n}\n","import { appendFileSync } from \"node:fs\";\nimport { paths, ensureDirectories } from \"../utils/paths.js\";\nimport type { AgentAction, ActionResult } from \"./decision-engine.js\";\n\nexport interface HeartbeatMetrics {\n timestamp: string;\n timelineCount: number;\n mentionsCount: number;\n actionCount: number;\n successCount: number;\n postCount: number;\n interactionCount: number;\n interactionRatio: number;\n repeatedFormatRate: number;\n policyRejectionCount: number;\n}\n\nfunction normalize(text: string): string {\n return text.toLowerCase().replace(/[^a-z0-9\\s]/g, \" \").replace(/\\s+/g, \" \").trim();\n}\n\nfunction prefix(text: string, n = 6): string {\n return normalize(text).split(\" \").filter(Boolean).slice(0, n).join(\" \");\n}\n\nfunction repeatedFormatRate(actions: AgentAction[]): number {\n const posts = actions\n .filter((action) => action.action === \"post\" && action.content)\n .map((action) => action.content ?? \"\");\n\n if (posts.length <= 1) return 0;\n\n const seen = new Map<string, number>();\n for (const content of posts) {\n const key = prefix(content);\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n\n let repeated = 0;\n for (const count of seen.values()) {\n if (count > 1) repeated += count;\n }\n\n return repeated / posts.length;\n}\n\nexport function writeHeartbeatMetrics(input: {\n timelineCount: number;\n mentionsCount: number;\n actions: AgentAction[];\n results: ActionResult[];\n policyFeedbackCount: number;\n}): HeartbeatMetrics {\n const interactionCount = input.actions.filter((a) => [\"reply\", \"like\", \"retweet\", \"follow\"].includes(a.action)).length;\n const postCount = input.actions.filter((a) => a.action === \"post\").length;\n const successCount = input.results.filter((r) => r.success).length;\n\n const metrics: HeartbeatMetrics = {\n timestamp: new Date().toISOString(),\n timelineCount: input.timelineCount,\n mentionsCount: input.mentionsCount,\n actionCount: input.actions.length,\n successCount,\n postCount,\n interactionCount,\n interactionRatio: input.actions.length > 0 ? interactionCount / input.actions.length : 0,\n repeatedFormatRate: repeatedFormatRate(input.actions),\n policyRejectionCount: input.policyFeedbackCount,\n };\n\n ensureDirectories();\n appendFileSync(paths.runtimeMetrics, JSON.stringify(metrics) + \"\\n\");\n return metrics;\n}\n"],"mappings":"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","names":[]}
@@ -9,8 +9,8 @@ import {
9
9
  mutateIdentity,
10
10
  renderIdentityDocument,
11
11
  saveIdentity
12
- } from "./chunk-AIEXQCQS.js";
13
- import "./chunk-53YLFYJF.js";
12
+ } from "./chunk-M6YOQVSI.js";
13
+ import "./chunk-3RYCUGXE.js";
14
14
  export {
15
15
  FRAMEWORKS,
16
16
  GOAL_PRESETS,
@@ -23,4 +23,4 @@ export {
23
23
  renderIdentityDocument,
24
24
  saveIdentity
25
25
  };
26
- //# sourceMappingURL=identity-O4FLSZKZ.js.map
26
+ //# sourceMappingURL=identity-VDUW4I2K.js.map
@@ -1,17 +1,22 @@
1
- import {
2
- createDefaultConfig,
3
- saveConfig
4
- } from "./chunk-B6RPMDML.js";
5
1
  import {
6
2
  loadIdentity
7
- } from "./chunk-AIEXQCQS.js";
3
+ } from "./chunk-M6YOQVSI.js";
8
4
  import {
9
5
  loadCredentials,
10
6
  saveCredentials
11
- } from "./chunk-ZJZKH7N7.js";
7
+ } from "./chunk-VZBHRUZS.js";
8
+ import {
9
+ getDefaultModel,
10
+ setLLMApiKey
11
+ } from "./chunk-KWWAIS3C.js";
12
+ import "./chunk-NPV3OV2K.js";
13
+ import {
14
+ createDefaultConfig,
15
+ saveConfig
16
+ } from "./chunk-NO3NQN67.js";
12
17
  import {
13
18
  ensureDirectories
14
- } from "./chunk-53YLFYJF.js";
19
+ } from "./chunk-3RYCUGXE.js";
15
20
 
16
21
  // src/init.ts
17
22
  import { input, select, password as passwordPrompt } from "@inquirer/prompts";
@@ -51,18 +56,6 @@ async function updateXProfile(identity) {
51
56
  console.error("Name/bio update error:", error);
52
57
  result.errors.push(`Failed to update name/bio: ${error.message}`);
53
58
  }
54
- if (identity.handle) {
55
- try {
56
- console.log(`Attempting to update username to: @${identity.handle}`);
57
- await client.v1.updateAccountProfile({
58
- screen_name: identity.handle
59
- });
60
- result.updated.push("username");
61
- console.log("Username updated successfully");
62
- } catch (error) {
63
- console.warn(`Could not update username: ${error.message}`);
64
- }
65
- }
66
59
  if (identity.profileImage) {
67
60
  try {
68
61
  console.log(`Downloading profile image from: ${identity.profileImage.substring(0, 60)}...`);
@@ -157,22 +150,22 @@ async function syncIdentityFromToken(token) {
157
150
  if (data.media.profileImage) data.identity.profileImage = data.media.profileImage;
158
151
  if (data.media.bannerImage) data.identity.bannerImage = data.media.bannerImage;
159
152
  }
160
- const { saveIdentity } = await import("./identity-O4FLSZKZ.js");
153
+ const { saveIdentity } = await import("./identity-VDUW4I2K.js");
161
154
  saveIdentity(data.identity);
162
155
  console.log(chalk.green(`\u2713 Connected to Spore: ${data.identity.name} (@${data.identity.handle})
163
156
  `));
164
157
  if (data.readme) {
165
158
  const { writeFileSync } = await import("fs");
166
- const { paths: paths2 } = await import("./paths-5GFUUHCZ.js");
159
+ const { paths: paths2 } = await import("./paths-BYR6MEPR.js");
167
160
  const { join, dirname } = await import("path");
168
161
  const readmePath = join(dirname(paths2.identity), "IDENTITY.md");
169
162
  writeFileSync(readmePath, data.readme, "utf-8");
170
163
  console.log(chalk.green("\u2713 Saved identity README\n"));
171
164
  }
172
165
  const { existsSync } = await import("fs");
173
- const { paths } = await import("./paths-5GFUUHCZ.js");
166
+ const { paths } = await import("./paths-BYR6MEPR.js");
174
167
  if (existsSync(paths.config)) {
175
- const { loadConfig, saveConfig: saveConfig2 } = await import("./config-TFAFYSIW.js");
168
+ const { loadConfig, saveConfig: saveConfig2 } = await import("./config-FL4VJVKZ.js");
176
169
  const config = loadConfig();
177
170
  config.connection = {
178
171
  token,
@@ -203,7 +196,7 @@ async function loginFlow() {
203
196
  console.log(chalk.green("\u2713 Logged in!\n"));
204
197
  console.log(chalk.gray("Opening chat interface...\n"));
205
198
  try {
206
- const { startWebChat } = await import("./web-chat-RQIILEQK.js");
199
+ const { startWebChat } = await import("./web-chat-3HM35XM4.js");
207
200
  await startWebChat();
208
201
  } catch (error) {
209
202
  console.log(chalk.yellow(`Could not start chat interface: ${error.message}
@@ -211,19 +204,35 @@ async function loginFlow() {
211
204
  console.log(chalk.gray("You can start it manually with: spora chat\n"));
212
205
  }
213
206
  }
207
+ function providerKeyUrl(provider) {
208
+ if (provider === "anthropic") return "https://console.anthropic.com/settings/keys";
209
+ if (provider === "openai") return "https://platform.openai.com/api-keys";
210
+ return "https://platform.deepseek.com/api_keys";
211
+ }
214
212
  async function setupKeys() {
215
- console.log(chalk.bold("\n\u2501\u2501\u2501 DeepSeek API Key \u2501\u2501\u2501\n"));
216
- console.log(chalk.gray("Your Spore uses DeepSeek to think and make decisions."));
217
- console.log(chalk.gray("Get your API key at: ") + chalk.cyan("https://platform.deepseek.com/api_keys\n"));
213
+ console.log(chalk.bold("\n\u2501\u2501\u2501 LLM Provider Setup \u2501\u2501\u2501\n"));
214
+ const provider = await select({
215
+ message: "Choose your LLM provider:",
216
+ choices: [
217
+ { name: "DeepSeek (recommended for low cost)", value: "deepseek" },
218
+ { name: "Anthropic (Claude)", value: "anthropic" },
219
+ { name: "OpenAI", value: "openai" }
220
+ ]
221
+ });
222
+ const defaultModel = getDefaultModel(provider);
223
+ const model = await input({
224
+ message: `Model name for ${provider} (press enter for ${defaultModel}):`
225
+ });
226
+ console.log(chalk.gray("Get your API key at: ") + chalk.cyan(`${providerKeyUrl(provider)}
227
+ `));
218
228
  const llmKey = await passwordPrompt({
219
- message: "DeepSeek API Key:",
229
+ message: `${provider} API Key:`,
220
230
  mask: "*",
221
231
  validate: (val) => val.length > 0 ? true : "API key is required"
222
232
  });
223
- const { paths } = await import("./paths-5GFUUHCZ.js");
224
- const { writeFileSync } = await import("fs");
225
- writeFileSync(paths.llmKey, llmKey, "utf-8");
226
- console.log(chalk.green("\u2713 DeepSeek API key saved\n"));
233
+ setLLMApiKey(provider, llmKey.trim());
234
+ console.log(chalk.green(`\u2713 ${provider} API key saved
235
+ `));
227
236
  console.log(chalk.bold("\n\u2501\u2501\u2501 Connect Your X Account \u2501\u2501\u2501\n"));
228
237
  console.log(chalk.gray("Your Spore needs OAuth 1.0a credentials for full X API access."));
229
238
  console.log(chalk.gray("This gives your agent the power to read timelines, post tweets, reply, like, and follow.\n"));
@@ -232,7 +241,7 @@ async function setupKeys() {
232
241
  console.log(chalk.gray(" 2. Create or select your app"));
233
242
  console.log(chalk.gray(' 3. Go to "Keys and tokens" tab'));
234
243
  console.log(chalk.gray(" 4. Copy all 4 credentials below\n"));
235
- console.log(chalk.yellow("Note: You need X Pro account ($200/mo) for posting access.\n"));
244
+ console.log(chalk.yellow("Note: Some endpoints may require paid X API access depending on your tier.\n"));
236
245
  const apiKey = await passwordPrompt({
237
246
  message: "X API Key (Consumer Key):",
238
247
  mask: "*",
@@ -253,10 +262,8 @@ async function setupKeys() {
253
262
  mask: "*",
254
263
  validate: (val) => val.length > 0 ? true : "Access Token Secret is required"
255
264
  });
256
- const bearerToken = await passwordPrompt({
257
- message: "X Bearer Token:",
258
- mask: "*",
259
- validate: (val) => val.length > 0 ? true : "Bearer Token is required"
265
+ const bearerToken = await input({
266
+ message: "X Bearer Token (optional, press enter to skip):"
260
267
  });
261
268
  saveCredentials({
262
269
  method: "api",
@@ -264,9 +271,15 @@ async function setupKeys() {
264
271
  apiSecret,
265
272
  accessToken,
266
273
  accessTokenSecret,
267
- bearerToken
274
+ bearerToken: bearerToken.trim() || void 0
268
275
  });
269
276
  console.log(chalk.green("\u2713 X API credentials saved (encrypted)\n"));
277
+ return {
278
+ llm: {
279
+ provider,
280
+ model: model.trim() || defaultModel
281
+ }
282
+ };
270
283
  }
271
284
  async function showDoneAndOpenChat() {
272
285
  console.log(chalk.green("\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
@@ -275,7 +288,7 @@ async function showDoneAndOpenChat() {
275
288
  console.log(chalk.bold.cyan("\u2501\u2501\u2501 Your Spore is Ready! \u2501\u2501\u2501\n"));
276
289
  console.log(chalk.gray("Opening chat interface...\n"));
277
290
  try {
278
- const { startWebChat } = await import("./web-chat-RQIILEQK.js");
291
+ const { startWebChat } = await import("./web-chat-3HM35XM4.js");
279
292
  await startWebChat();
280
293
  } catch (error) {
281
294
  console.log(chalk.yellow(`Could not start chat interface: ${error.message}
@@ -311,7 +324,7 @@ async function runInit(token) {
311
324
  console.log(chalk.yellow("Please check your token and try again.\n"));
312
325
  process.exit(1);
313
326
  }
314
- await setupKeys();
327
+ const setup2 = await setupKeys();
315
328
  console.log(chalk.bold("\n\u2501\u2501\u2501 Updating Your X Profile \u2501\u2501\u2501\n"));
316
329
  console.log(chalk.gray("Setting up profile picture, banner, and bio to match your Spore...\n"));
317
330
  try {
@@ -340,8 +353,8 @@ async function runInit(token) {
340
353
  console.log(chalk.gray("You can manually update your X profile later.\n"));
341
354
  }
342
355
  const config2 = createDefaultConfig({ xMethod: "api", xApiTier: "basic" });
343
- config2.llm = { provider: "deepseek", model: "deepseek-chat" };
344
- config2.runtime = { heartbeatIntervalMs: 3e5, actionsPerHeartbeat: 3, enabled: true };
356
+ config2.llm = setup2.llm;
357
+ config2.runtime = { heartbeatIntervalMs: 3e5, actionsPerHeartbeat: 4, enabled: true };
345
358
  config2.connection = {
346
359
  token,
347
360
  apiEndpoint: process.env.SPORA_API_URL || "https://www.spora.social/api/v1",
@@ -363,7 +376,7 @@ async function runInit(token) {
363
376
  return;
364
377
  }
365
378
  ensureDirectories();
366
- await setupKeys();
379
+ const setup = await setupKeys();
367
380
  console.log(chalk.bold("\n\u2501\u2501\u2501 Updating Your X Profile \u2501\u2501\u2501\n"));
368
381
  console.log(chalk.gray("Setting up profile picture, banner, and bio to match your Spore...\n"));
369
382
  try {
@@ -392,12 +405,12 @@ async function runInit(token) {
392
405
  console.log(chalk.gray("You can manually update your X profile later.\n"));
393
406
  }
394
407
  const config = createDefaultConfig({ xMethod: "api", xApiTier: "basic" });
395
- config.llm = { provider: "deepseek", model: "deepseek-chat" };
396
- config.runtime = { heartbeatIntervalMs: 3e5, actionsPerHeartbeat: 3, enabled: true };
408
+ config.llm = setup.llm;
409
+ config.runtime = { heartbeatIntervalMs: 3e5, actionsPerHeartbeat: 4, enabled: true };
397
410
  saveConfig(config);
398
411
  await showDoneAndOpenChat();
399
412
  }
400
413
  export {
401
414
  runInit
402
415
  };
403
- //# sourceMappingURL=init-G3WINLAP.js.map
416
+ //# sourceMappingURL=init-ISSXETHY.js.map