@elizaos/plugin-bluesky 2.0.0-alpha.7 → 2.0.0-beta.1

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.
@@ -1,16 +1,12 @@
1
1
  // index.ts
2
- import { logger as logger4 } from "@elizaos/core";
3
-
4
- // services/bluesky.ts
5
- import { logger as logger3, Service } from "@elizaos/core";
6
-
7
- // client.ts
8
- import { BskyAgent, RichText } from "@atproto/api";
9
- import { logger } from "@elizaos/core";
10
- import { LRUCache } from "lru-cache";
2
+ import {
3
+ getConnectorAccountManager,
4
+ logger as logger4
5
+ } from "@elizaos/core";
11
6
 
12
7
  // types/index.ts
13
- import { z } from "zod";
8
+ import * as zod from "zod";
9
+ var z2 = zod.z ?? zod;
14
10
  var BLUESKY_SERVICE_URL = "https://bsky.social";
15
11
  var BLUESKY_MAX_POST_LENGTH = 300;
16
12
  var BLUESKY_POLL_INTERVAL = 60;
@@ -35,20 +31,20 @@ var CACHE_SIZE = {
35
31
  NOTIFICATIONS: 1000,
36
32
  CONVERSATIONS: 100
37
33
  };
38
- var BlueSkyConfigSchema = z.object({
39
- handle: z.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
40
- password: z.string().min(1),
41
- service: z.string().url().default(BLUESKY_SERVICE_URL),
42
- dryRun: z.boolean().default(false),
43
- pollInterval: z.number().positive().default(BLUESKY_POLL_INTERVAL),
44
- enablePost: z.boolean().default(true),
45
- postIntervalMin: z.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
46
- postIntervalMax: z.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
47
- enableActionProcessing: z.boolean().default(true),
48
- actionInterval: z.number().positive().default(BLUESKY_ACTION_INTERVAL),
49
- postImmediately: z.boolean().default(false),
50
- maxActionsProcessing: z.number().positive().default(BLUESKY_MAX_ACTIONS),
51
- enableDMs: z.boolean().default(true)
34
+ var BlueSkyConfigSchema = z2.object({
35
+ handle: z2.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
36
+ password: z2.string().min(1),
37
+ service: z2.string().url().default(BLUESKY_SERVICE_URL),
38
+ dryRun: z2.boolean().default(false),
39
+ pollInterval: z2.number().positive().default(BLUESKY_POLL_INTERVAL),
40
+ enablePost: z2.boolean().default(true),
41
+ postIntervalMin: z2.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
42
+ postIntervalMax: z2.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
43
+ enableActionProcessing: z2.boolean().default(true),
44
+ actionInterval: z2.number().positive().default(BLUESKY_ACTION_INTERVAL),
45
+ postImmediately: z2.boolean().default(false),
46
+ maxActionsProcessing: z2.number().positive().default(BLUESKY_MAX_ACTIONS),
47
+ enableDMs: z2.boolean().default(true)
52
48
  });
53
49
 
54
50
  class BlueSkyError extends Error {
@@ -62,7 +58,254 @@ class BlueSkyError extends Error {
62
58
  }
63
59
  }
64
60
 
61
+ // utils/config.ts
62
+ var DEFAULT_BLUESKY_ACCOUNT_ID = "default";
63
+ function getApiKeyOptional(runtime, key) {
64
+ const value = runtime.getSetting(key);
65
+ return typeof value === "string" ? value : undefined;
66
+ }
67
+ function stringSetting(runtime, key) {
68
+ const value = runtime.getSetting(key);
69
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
70
+ }
71
+ function characterConfig(runtime) {
72
+ const settings = runtime.character?.settings;
73
+ const raw = settings?.bluesky;
74
+ return raw && typeof raw === "object" ? raw : {};
75
+ }
76
+ function parseAccountsJson(runtime) {
77
+ const raw = stringSetting(runtime, "BLUESKY_ACCOUNTS");
78
+ if (!raw)
79
+ return {};
80
+ try {
81
+ const parsed = JSON.parse(raw);
82
+ if (Array.isArray(parsed)) {
83
+ return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [
84
+ normalizeBlueSkyAccountId(item.accountId ?? item.id),
85
+ item
86
+ ]));
87
+ }
88
+ return parsed && typeof parsed === "object" ? parsed : {};
89
+ } catch {
90
+ return {};
91
+ }
92
+ }
93
+ function allAccountConfigs(runtime) {
94
+ return {
95
+ ...characterConfig(runtime).accounts ?? {},
96
+ ...parseAccountsJson(runtime)
97
+ };
98
+ }
99
+ function accountConfig(runtime, accountId) {
100
+ const accounts = allAccountConfigs(runtime);
101
+ return accounts[accountId] ?? accounts[normalizeBlueSkyAccountId(accountId)] ?? {};
102
+ }
103
+ function readRawField(record, keys) {
104
+ if (!record)
105
+ return;
106
+ for (const key of keys) {
107
+ const value = record[key];
108
+ if (typeof value === "string" && value.trim())
109
+ return value.trim();
110
+ if (typeof value === "number" || typeof value === "boolean")
111
+ return String(value);
112
+ }
113
+ return;
114
+ }
115
+ function boolValue(value, fallback = false) {
116
+ if (typeof value === "boolean")
117
+ return value;
118
+ if (typeof value === "string")
119
+ return value.trim().toLowerCase() === "true";
120
+ return fallback;
121
+ }
122
+ function intValue(value, fallback) {
123
+ const parsed = Number.parseInt(String(value ?? ""), 10);
124
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
125
+ }
126
+ function normalizeBlueSkyAccountId(accountId) {
127
+ if (typeof accountId !== "string")
128
+ return DEFAULT_BLUESKY_ACCOUNT_ID;
129
+ const trimmed = accountId.trim();
130
+ return trimmed || DEFAULT_BLUESKY_ACCOUNT_ID;
131
+ }
132
+ function listBlueSkyAccountIds(runtime) {
133
+ const ids = new Set;
134
+ const config = characterConfig(runtime);
135
+ if (stringSetting(runtime, "BLUESKY_HANDLE") || config.handle && config.password) {
136
+ ids.add(DEFAULT_BLUESKY_ACCOUNT_ID);
137
+ }
138
+ for (const id of Object.keys(allAccountConfigs(runtime))) {
139
+ ids.add(normalizeBlueSkyAccountId(id));
140
+ }
141
+ return Array.from(ids.size ? ids : new Set([DEFAULT_BLUESKY_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
142
+ }
143
+ function resolveDefaultBlueSkyAccountId(runtime) {
144
+ const requested = stringSetting(runtime, "BLUESKY_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "BLUESKY_ACCOUNT_ID");
145
+ if (requested)
146
+ return normalizeBlueSkyAccountId(requested);
147
+ const ids = listBlueSkyAccountIds(runtime);
148
+ return ids.includes(DEFAULT_BLUESKY_ACCOUNT_ID) ? DEFAULT_BLUESKY_ACCOUNT_ID : ids[0] ?? DEFAULT_BLUESKY_ACCOUNT_ID;
149
+ }
150
+ function readBlueSkyAccountId(...sources) {
151
+ for (const source of sources) {
152
+ if (!source || typeof source !== "object")
153
+ continue;
154
+ const record = source;
155
+ const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
156
+ const data = record.data && typeof record.data === "object" ? record.data : {};
157
+ const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
158
+ const bluesky = data.bluesky && typeof data.bluesky === "object" ? data.bluesky : {};
159
+ const value = record.accountId ?? parameters.accountId ?? data.accountId ?? bluesky.accountId ?? metadata.accountId;
160
+ if (typeof value === "string" && value.trim())
161
+ return normalizeBlueSkyAccountId(value);
162
+ }
163
+ return;
164
+ }
165
+ function hasBlueSkyEnabled(runtime, accountId) {
166
+ const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
167
+ const base = characterConfig(runtime);
168
+ const account = accountConfig(runtime, normalizedAccountId);
169
+ const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
170
+ const enabled = readRawField(account, ["enabled", "BLUESKY_ENABLED"]) ?? readRawField(base, ["enabled", "BLUESKY_ENABLED"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLED") : undefined);
171
+ if (enabled)
172
+ return String(enabled).toLowerCase() === "true";
173
+ return Boolean((readRawField(account, ["handle", "BLUESKY_HANDLE"]) ?? readRawField(base, ["handle", "BLUESKY_HANDLE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_HANDLE") : undefined)) && (readRawField(account, ["password", "BLUESKY_PASSWORD"]) ?? readRawField(base, ["password", "BLUESKY_PASSWORD"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_PASSWORD") : undefined)));
174
+ }
175
+ function validateBlueSkyConfig(runtime, accountId) {
176
+ const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
177
+ const base = characterConfig(runtime);
178
+ const account = accountConfig(runtime, normalizedAccountId);
179
+ const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
180
+ const result = BlueSkyConfigSchema.safeParse({
181
+ handle: readRawField(account, ["handle", "BLUESKY_HANDLE"]) ?? readRawField(base, ["handle", "BLUESKY_HANDLE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_HANDLE") : undefined) ?? "",
182
+ password: readRawField(account, ["password", "BLUESKY_PASSWORD"]) ?? readRawField(base, ["password", "BLUESKY_PASSWORD"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_PASSWORD") : undefined) ?? "",
183
+ service: readRawField(account, ["service", "BLUESKY_SERVICE"]) ?? readRawField(base, ["service", "BLUESKY_SERVICE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_SERVICE") : undefined) ?? BLUESKY_SERVICE_URL,
184
+ dryRun: boolValue(readRawField(account, ["dryRun", "BLUESKY_DRY_RUN"]) ?? readRawField(base, ["dryRun", "BLUESKY_DRY_RUN"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_DRY_RUN") : undefined)),
185
+ pollInterval: intValue(readRawField(account, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? readRawField(base, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POLL_INTERVAL") : undefined), BLUESKY_POLL_INTERVAL),
186
+ enablePost: String(readRawField(account, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? readRawField(base, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_POSTING") : undefined) ?? "true").toLowerCase() !== "false",
187
+ postIntervalMin: intValue(readRawField(account, ["postIntervalMin", "BLUESKY_POST_INTERVAL_MIN"]) ?? readRawField(base, ["postIntervalMin", "BLUESKY_POST_INTERVAL_MIN"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") : undefined), BLUESKY_POST_INTERVAL_MIN),
188
+ postIntervalMax: intValue(readRawField(account, ["postIntervalMax", "BLUESKY_POST_INTERVAL_MAX"]) ?? readRawField(base, ["postIntervalMax", "BLUESKY_POST_INTERVAL_MAX"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") : undefined), BLUESKY_POST_INTERVAL_MAX),
189
+ enableActionProcessing: String(readRawField(account, [
190
+ "enableActionProcessing",
191
+ "BLUESKY_ENABLE_ACTION_PROCESSING"
192
+ ]) ?? readRawField(base, [
193
+ "enableActionProcessing",
194
+ "BLUESKY_ENABLE_ACTION_PROCESSING"
195
+ ]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") : undefined) ?? "true").toLowerCase() !== "false",
196
+ actionInterval: intValue(readRawField(account, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? readRawField(base, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ACTION_INTERVAL") : undefined), BLUESKY_ACTION_INTERVAL),
197
+ postImmediately: boolValue(readRawField(account, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? readRawField(base, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_IMMEDIATELY") : undefined)),
198
+ maxActionsProcessing: intValue(readRawField(account, [
199
+ "maxActionsProcessing",
200
+ "BLUESKY_MAX_ACTIONS_PROCESSING"
201
+ ]) ?? readRawField(base, [
202
+ "maxActionsProcessing",
203
+ "BLUESKY_MAX_ACTIONS_PROCESSING"
204
+ ]) ?? (allowEnv ? runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") : undefined), BLUESKY_MAX_ACTIONS),
205
+ enableDMs: String(readRawField(account, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? readRawField(base, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_DMS") : undefined) ?? "true").toLowerCase() !== "false"
206
+ });
207
+ if (!result.success) {
208
+ const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
209
+ throw new Error(`Invalid BlueSky configuration: ${errors}`);
210
+ }
211
+ return { ...result.data, accountId: normalizedAccountId };
212
+ }
213
+ function getPollInterval(runtime, accountId) {
214
+ const config = validateBlueSkyConfig(runtime, accountId);
215
+ return config.pollInterval * 1000;
216
+ }
217
+ function getActionInterval(runtime, accountId) {
218
+ const config = validateBlueSkyConfig(runtime, accountId);
219
+ return config.actionInterval * 1000;
220
+ }
221
+ function getMaxActionsProcessing(runtime, accountId) {
222
+ const config = validateBlueSkyConfig(runtime, accountId);
223
+ return config.maxActionsProcessing;
224
+ }
225
+ function isPostingEnabled(runtime, accountId) {
226
+ const config = validateBlueSkyConfig(runtime, accountId);
227
+ return config.enablePost;
228
+ }
229
+ function shouldPostImmediately(runtime, accountId) {
230
+ const config = validateBlueSkyConfig(runtime, accountId);
231
+ return config.postImmediately;
232
+ }
233
+ function getPostIntervalRange(runtime, accountId) {
234
+ const config = validateBlueSkyConfig(runtime, accountId);
235
+ return {
236
+ min: config.postIntervalMin * 1000,
237
+ max: config.postIntervalMax * 1000
238
+ };
239
+ }
240
+
241
+ // connector-account-provider.ts
242
+ var BLUESKY_PROVIDER_ID = "bluesky";
243
+ function toConnectorAccount(runtime, accountId) {
244
+ let connected = false;
245
+ let handle = "";
246
+ let service = "";
247
+ try {
248
+ const config = validateBlueSkyConfig(runtime, accountId);
249
+ connected = Boolean(config.handle && config.password);
250
+ handle = config.handle;
251
+ service = config.service;
252
+ } catch {
253
+ connected = false;
254
+ }
255
+ const now = Date.now();
256
+ return {
257
+ id: accountId,
258
+ provider: BLUESKY_PROVIDER_ID,
259
+ label: handle || accountId,
260
+ role: "OWNER",
261
+ purpose: ["posting", "reading"],
262
+ accessGate: "open",
263
+ status: connected ? "connected" : "disabled",
264
+ externalId: handle || undefined,
265
+ displayHandle: handle || undefined,
266
+ createdAt: now,
267
+ updatedAt: now,
268
+ metadata: {
269
+ service
270
+ }
271
+ };
272
+ }
273
+ function createBlueSkyConnectorAccountProvider(runtime) {
274
+ return {
275
+ provider: BLUESKY_PROVIDER_ID,
276
+ label: "BlueSky",
277
+ listAccounts: async (_manager) => {
278
+ const ids = listBlueSkyAccountIds(runtime);
279
+ return ids.map((id) => toConnectorAccount(runtime, id));
280
+ },
281
+ createAccount: async (input, _manager) => {
282
+ return {
283
+ ...input,
284
+ provider: BLUESKY_PROVIDER_ID,
285
+ role: input.role ?? "OWNER",
286
+ purpose: input.purpose ?? ["posting", "reading"],
287
+ accessGate: input.accessGate ?? "open",
288
+ status: input.status ?? "pending"
289
+ };
290
+ },
291
+ patchAccount: async (_accountId, patch, _manager) => {
292
+ return { ...patch, provider: BLUESKY_PROVIDER_ID };
293
+ },
294
+ deleteAccount: async (_accountId, _manager) => {}
295
+ };
296
+ }
297
+
298
+ // services/bluesky.ts
299
+ import {
300
+ ChannelType as ChannelType3,
301
+ logger as logger3,
302
+ Service
303
+ } from "@elizaos/core";
304
+
65
305
  // client.ts
306
+ import { BskyAgent, RichText } from "@atproto/api";
307
+ import { logger } from "@elizaos/core";
308
+ import { LRUCache } from "lru-cache";
66
309
  function isPostView(item) {
67
310
  return typeof item === "object" && item !== null && "uri" in item && "cid" in item && "author" in item && "record" in item && "indexedAt" in item && typeof item.uri === "string" && typeof item.cid === "string";
68
311
  }
@@ -180,6 +423,18 @@ class BlueSkyClient {
180
423
  })
181
424
  };
182
425
  }
426
+ async searchPosts(params) {
427
+ const api = this.agent;
428
+ const response = await api.app.bsky.feed.searchPosts({
429
+ q: params.query,
430
+ limit: params.limit ?? 25,
431
+ cursor: params.cursor
432
+ });
433
+ return {
434
+ posts: response.data.posts.map(adaptPostView),
435
+ cursor: response.data.cursor
436
+ };
437
+ }
183
438
  async sendPost(request) {
184
439
  if (this.config.dryRun) {
185
440
  logger.info(`Dry run: would create post with text: ${request.content.text}`);
@@ -199,7 +454,20 @@ class BlueSkyClient {
199
454
  if (request.content.embed) {
200
455
  record.embed = request.content.embed;
201
456
  }
457
+ logger.info({
458
+ src: "plugin:bluesky",
459
+ op: "atproto:post",
460
+ textLength: rt.text.length,
461
+ hasReply: Boolean(request.replyTo),
462
+ hasEmbed: Boolean(request.content.embed)
463
+ }, "Publishing Bluesky post via atproto");
202
464
  const response = await this.agent.post(record);
465
+ logger.info({
466
+ src: "plugin:bluesky",
467
+ op: "atproto:post",
468
+ uri: response.uri,
469
+ cid: response.cid
470
+ }, "Bluesky post published");
203
471
  const thread = await this.agent.getPostThread({
204
472
  uri: response.uri,
205
473
  depth: 0
@@ -295,65 +563,15 @@ class BlueSkyClient {
295
563
  }
296
564
 
297
565
  // managers/agent.ts
298
- import { logger as logger2 } from "@elizaos/core";
299
-
300
- // utils/config.ts
301
- function getApiKeyOptional(runtime, key) {
302
- const value = runtime.getSetting(key);
303
- return typeof value === "string" ? value : undefined;
304
- }
305
- function hasBlueSkyEnabled(runtime) {
306
- const enabled = runtime.getSetting("BLUESKY_ENABLED");
307
- if (enabled)
308
- return String(enabled).toLowerCase() === "true";
309
- return Boolean(runtime.getSetting("BLUESKY_HANDLE") && runtime.getSetting("BLUESKY_PASSWORD"));
310
- }
311
- function validateBlueSkyConfig(runtime) {
312
- const result = BlueSkyConfigSchema.safeParse({
313
- handle: String(runtime.getSetting("BLUESKY_HANDLE") ?? ""),
314
- password: String(runtime.getSetting("BLUESKY_PASSWORD") ?? ""),
315
- service: String(runtime.getSetting("BLUESKY_SERVICE") ?? BLUESKY_SERVICE_URL),
316
- dryRun: runtime.getSetting("BLUESKY_DRY_RUN") === "true",
317
- pollInterval: parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL,
318
- enablePost: runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false",
319
- postIntervalMin: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN,
320
- postIntervalMax: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX,
321
- enableActionProcessing: runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") !== "false",
322
- actionInterval: parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL,
323
- postImmediately: runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true",
324
- maxActionsProcessing: parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS,
325
- enableDMs: runtime.getSetting("BLUESKY_ENABLE_DMS") !== "false"
326
- });
327
- if (!result.success) {
328
- const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
329
- throw new Error(`Invalid BlueSky configuration: ${errors}`);
330
- }
331
- return result.data;
332
- }
333
- function getPollInterval(runtime) {
334
- const seconds = parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL;
335
- return seconds * 1000;
336
- }
337
- function getActionInterval(runtime) {
338
- const seconds = parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL;
339
- return seconds * 1000;
340
- }
341
- function getMaxActionsProcessing(runtime) {
342
- return parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS;
343
- }
344
- function isPostingEnabled(runtime) {
345
- return runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false";
346
- }
347
- function shouldPostImmediately(runtime) {
348
- return runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true";
349
- }
350
- function getPostIntervalRange(runtime) {
351
- const min = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN;
352
- const max = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX;
353
- return { min: min * 1000, max: max * 1000 };
566
+ import {
567
+ logger as logger2,
568
+ setTrajectoryPurpose,
569
+ withStandaloneTrajectory
570
+ } from "@elizaos/core";
571
+ function cursorCacheKey(agentId, accountId) {
572
+ return `bluesky:cursor:${agentId}:${accountId}`;
354
573
  }
355
574
 
356
- // managers/agent.ts
357
575
  class BlueSkyAgentManager {
358
576
  runtime;
359
577
  config;
@@ -363,24 +581,33 @@ class BlueSkyAgentManager {
363
581
  postTimer = null;
364
582
  running = false;
365
583
  lastSeenAt = null;
584
+ accountId;
366
585
  constructor(runtime, config, client) {
367
586
  this.runtime = runtime;
368
587
  this.config = config;
369
588
  this.client = client;
589
+ this.accountId = normalizeBlueSkyAccountId(config.accountId ?? DEFAULT_BLUESKY_ACCOUNT_ID);
590
+ }
591
+ getAccountId() {
592
+ return this.accountId;
370
593
  }
371
594
  async start() {
372
595
  if (this.running)
373
596
  return;
374
597
  await this.client.authenticate();
375
598
  this.running = true;
599
+ const cached = await this.runtime.getCache(cursorCacheKey(this.runtime.agentId, this.accountId));
600
+ if (typeof cached === "string" && cached) {
601
+ this.lastSeenAt = cached;
602
+ }
376
603
  this.startNotificationPolling();
377
604
  if (this.config.enableActionProcessing) {
378
605
  this.startActionProcessing();
379
606
  }
380
- if (isPostingEnabled(this.runtime)) {
607
+ if (isPostingEnabled(this.runtime, this.accountId)) {
381
608
  this.startAutomatedPosting();
382
609
  }
383
- logger2.success({ agentId: this.runtime.agentId }, "BlueSky agent manager started");
610
+ logger2.success({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager started");
384
611
  }
385
612
  async stop() {
386
613
  this.running = false;
@@ -394,10 +621,10 @@ class BlueSkyAgentManager {
394
621
  this.actionTimer = null;
395
622
  this.postTimer = null;
396
623
  await this.client.cleanup();
397
- logger2.info({ agentId: this.runtime.agentId }, "BlueSky agent manager stopped");
624
+ logger2.info({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager stopped");
398
625
  }
399
626
  startNotificationPolling() {
400
- const interval = getPollInterval(this.runtime);
627
+ const interval = getPollInterval(this.runtime, this.accountId);
401
628
  this.pollNotifications();
402
629
  this.pollTimer = setInterval(() => this.pollNotifications(), interval);
403
630
  }
@@ -413,6 +640,7 @@ class BlueSkyAgentManager {
413
640
  }) : notifications;
414
641
  if (newNotifications.length > 0) {
415
642
  this.lastSeenAt = notifications[0].indexedAt;
643
+ await this.runtime.setCache(cursorCacheKey(this.runtime.agentId, this.accountId), this.lastSeenAt);
416
644
  for (const notification of newNotifications) {
417
645
  this.emitNotificationEvent(notification);
418
646
  }
@@ -433,26 +661,28 @@ class BlueSkyAgentManager {
433
661
  const payload = {
434
662
  runtime: this.runtime,
435
663
  source: "bluesky",
664
+ accountId: this.accountId,
436
665
  notification
437
666
  };
438
667
  this.runtime.emitEvent(event, payload);
439
668
  }
440
669
  }
441
670
  startActionProcessing() {
442
- const interval = getActionInterval(this.runtime);
443
- this.processActions();
444
- this.actionTimer = setInterval(() => this.processActions(), interval);
671
+ const interval = getActionInterval(this.runtime, this.accountId);
672
+ this.processQueuedActions();
673
+ this.actionTimer = setInterval(() => this.processQueuedActions(), interval);
445
674
  }
446
- async processActions() {
675
+ async processQueuedActions() {
447
676
  if (!this.running)
448
677
  return;
449
- const max = getMaxActionsProcessing(this.runtime);
678
+ const max = getMaxActionsProcessing(this.runtime, this.accountId);
450
679
  const { notifications } = await this.client.getNotifications(max);
451
680
  for (const notification of notifications) {
452
681
  if (notification.reason === "mention" || notification.reason === "reply") {
453
682
  const payload = {
454
683
  runtime: this.runtime,
455
684
  source: "bluesky",
685
+ accountId: this.accountId,
456
686
  notification
457
687
  };
458
688
  this.runtime.emitEvent("bluesky.should_respond", payload);
@@ -460,47 +690,93 @@ class BlueSkyAgentManager {
460
690
  }
461
691
  }
462
692
  startAutomatedPosting() {
463
- if (shouldPostImmediately(this.runtime)) {
693
+ if (shouldPostImmediately(this.runtime, this.accountId)) {
464
694
  this.createAutomatedPost();
465
695
  }
466
696
  this.scheduleNextPost();
467
697
  }
468
698
  scheduleNextPost() {
469
- const { min, max } = getPostIntervalRange(this.runtime);
699
+ const { min, max } = getPostIntervalRange(this.runtime, this.accountId);
470
700
  const interval = Math.random() * (max - min) + min;
471
701
  this.postTimer = setTimeout(() => {
472
702
  if (this.running) {
473
- this.createAutomatedPost();
474
- this.scheduleNextPost();
703
+ this.createAutomatedPost().finally(() => this.scheduleNextPost());
475
704
  }
476
705
  }, interval);
477
706
  }
478
- createAutomatedPost() {
479
- const payload = {
480
- runtime: this.runtime,
481
- source: "bluesky",
482
- automated: true
483
- };
484
- this.runtime.emitEvent("bluesky.create_post", payload);
707
+ async createAutomatedPost() {
708
+ await withStandaloneTrajectory(this.runtime, {
709
+ source: "plugin-bluesky:auto-post",
710
+ metadata: {
711
+ platform: "bluesky",
712
+ kind: "public_post_generation",
713
+ automated: true,
714
+ accountId: this.accountId
715
+ }
716
+ }, async () => {
717
+ setTrajectoryPurpose("background");
718
+ const payload = {
719
+ runtime: this.runtime,
720
+ source: "bluesky",
721
+ automated: true,
722
+ accountId: this.accountId
723
+ };
724
+ await this.runtime.emitEvent("bluesky.create_post", payload);
725
+ });
485
726
  }
486
727
  }
487
728
 
488
729
  // services/message.ts
489
- import { composePrompt, ModelType } from "@elizaos/core";
730
+ import {
731
+ ChannelType,
732
+ composePrompt,
733
+ createUniqueUuid,
734
+ ModelType
735
+ } from "@elizaos/core";
490
736
 
491
- // generated/prompts/typescript/prompts.ts
737
+ // prompts.ts
492
738
  var generateDmTemplate = `Generate a friendly direct message response under 200 characters.`;
493
739
  var generatePostTemplate = `Generate an engaging BlueSky post under {{maxLength}} characters.`;
494
740
  var truncatePostTemplate = `Shorten to under {{maxLength}} characters: "{{text}}"`;
495
741
 
496
742
  // services/message.ts
743
+ var BLUESKY_CONNECTOR_CONTEXTS = ["social", "connectors"];
744
+ function normalizeBlueSkyQuery(value) {
745
+ return value.trim().replace(/^@/, "").toLowerCase();
746
+ }
747
+ function scoreBlueSkyMatch(query, id, labels) {
748
+ if (!query)
749
+ return 0.45;
750
+ if (id.toLowerCase() === query)
751
+ return 1;
752
+ let bestScore = 0;
753
+ for (const label of labels) {
754
+ const normalized = label?.trim().replace(/^@/, "").toLowerCase();
755
+ if (!normalized)
756
+ continue;
757
+ if (normalized === query) {
758
+ bestScore = Math.max(bestScore, 0.95);
759
+ } else if (normalized.startsWith(query)) {
760
+ bestScore = Math.max(bestScore, 0.85);
761
+ } else if (normalized.includes(query)) {
762
+ bestScore = Math.max(bestScore, 0.7);
763
+ }
764
+ }
765
+ return bestScore;
766
+ }
767
+
497
768
  class BlueSkyMessageService {
498
769
  client;
499
770
  runtime;
771
+ accountId;
500
772
  static serviceType = "IMessageService";
501
- constructor(client, runtime) {
773
+ constructor(client, runtime, accountId = "default") {
502
774
  this.client = client;
503
775
  this.runtime = runtime;
776
+ this.accountId = accountId;
777
+ }
778
+ getAccountId() {
779
+ return normalizeBlueSkyAccountId(this.accountId);
504
780
  }
505
781
  async getMessages(convoId, limit = 50) {
506
782
  const response = await this.client.getMessages(convoId, limit);
@@ -514,6 +790,215 @@ class BlueSkyMessageService {
514
790
  const response = await this.client.getConversations(limit);
515
791
  return response.conversations;
516
792
  }
793
+ async handleSendMessage(runtime, target, content) {
794
+ const requestedAccountId = normalizeBlueSkyAccountId(target.accountId ?? readBlueSkyAccountId(content, target) ?? this.getAccountId());
795
+ if (requestedAccountId !== this.getAccountId()) {
796
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
797
+ }
798
+ const text = typeof content.text === "string" ? content.text.trim() : "";
799
+ if (!text) {
800
+ throw new Error("BlueSky DM connector requires non-empty text content.");
801
+ }
802
+ let convoId = target.channelId ?? target.threadId;
803
+ if (!convoId && target.roomId) {
804
+ const room = await runtime.getRoom(target.roomId);
805
+ convoId = room?.channelId;
806
+ }
807
+ if (!convoId) {
808
+ throw new Error("BlueSky DM connector requires a conversation target.");
809
+ }
810
+ await this.sendMessage(convoId, text);
811
+ }
812
+ async fetchConnectorMessages(context, params = {}) {
813
+ const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
814
+ if (requestedAccountId !== this.getAccountId()) {
815
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
816
+ }
817
+ const target = params.target ?? context.target;
818
+ let convoId = target?.channelId ?? target?.threadId;
819
+ if (!convoId && target?.roomId) {
820
+ const room = await context.runtime.getRoom(target.roomId);
821
+ convoId = room?.channelId;
822
+ }
823
+ if (convoId) {
824
+ const messages = await this.getMessages(convoId, clampLimit(params.limit, 25, 100));
825
+ return messages.map((message) => this.messageToMemory(context.runtime, message, convoId));
826
+ }
827
+ const conversations = await this.getConversations(clampLimit(params.limit, 25, 50));
828
+ const memories = [];
829
+ for (const conversation of conversations) {
830
+ const messages = await this.getMessages(conversation.id, 1);
831
+ memories.push(...messages.map((message) => this.messageToMemory(context.runtime, message, conversation.id)));
832
+ }
833
+ return memories.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
834
+ }
835
+ async resolveConnectorTargets(query, _context) {
836
+ const normalizedQuery = normalizeBlueSkyQuery(query);
837
+ const conversations = await this.getConversations(50);
838
+ return conversations.map((conversation) => {
839
+ const score = scoreBlueSkyMatch(normalizedQuery, conversation.id, [
840
+ ...conversation.members.flatMap((member) => [
841
+ member.handle,
842
+ member.displayName,
843
+ member.did
844
+ ])
845
+ ]);
846
+ return score > 0 ? this.buildConversationTarget(conversation, score) : null;
847
+ }).filter((target) => Boolean(target)).slice(0, 25);
848
+ }
849
+ async listConnectorRooms(_context) {
850
+ const conversations = await this.getConversations(50);
851
+ return conversations.map((conversation) => this.buildConversationTarget(conversation, 0.5));
852
+ }
853
+ async listRecentConnectorTargets(context) {
854
+ const targets = [];
855
+ const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
856
+ const convoId = context.target?.channelId ?? room?.channelId;
857
+ if (convoId) {
858
+ targets.push({
859
+ target: {
860
+ source: "bluesky",
861
+ accountId: this.getAccountId(),
862
+ channelId: convoId
863
+ },
864
+ label: `BlueSky conversation ${convoId}`,
865
+ kind: "thread",
866
+ score: 0.95,
867
+ contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
868
+ metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
869
+ });
870
+ }
871
+ targets.push(...await this.listConnectorRooms(context));
872
+ const seen = new Set;
873
+ return targets.filter((target) => {
874
+ const channelId = target.target.channelId;
875
+ if (!channelId || seen.has(channelId))
876
+ return false;
877
+ seen.add(channelId);
878
+ return true;
879
+ }).slice(0, 25);
880
+ }
881
+ async getConnectorChatContext(target, context) {
882
+ let convoId = target.channelId ?? target.threadId;
883
+ if (!convoId && target.roomId) {
884
+ const room = await context.runtime.getRoom(target.roomId);
885
+ convoId = room?.channelId;
886
+ }
887
+ if (!convoId)
888
+ return null;
889
+ const messages = await this.getMessages(convoId, 25);
890
+ return {
891
+ target: {
892
+ source: "bluesky",
893
+ accountId: this.getAccountId(),
894
+ channelId: convoId
895
+ },
896
+ label: `BlueSky conversation ${convoId}`,
897
+ recentMessages: messages.map((message) => ({
898
+ name: message.sender.did,
899
+ text: message.text ?? "",
900
+ timestamp: Date.parse(message.sentAt),
901
+ metadata: {
902
+ accountId: this.getAccountId(),
903
+ blueskyMessageId: message.id,
904
+ blueskySenderDid: message.sender.did
905
+ }
906
+ })),
907
+ metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
908
+ };
909
+ }
910
+ async getConnectorUserContext(entityId, _context) {
911
+ const normalizedEntity = entityId.trim().replace(/^@/, "");
912
+ if (!normalizedEntity)
913
+ return null;
914
+ const conversations = await this.getConversations(50);
915
+ for (const conversation of conversations) {
916
+ const member = conversation.members.find((candidate) => candidate.did === entityId || candidate.handle === normalizedEntity || candidate.displayName === entityId);
917
+ if (!member)
918
+ continue;
919
+ return {
920
+ entityId,
921
+ label: member.displayName || member.handle || member.did,
922
+ aliases: [member.handle, member.displayName, member.did].filter((value) => Boolean(value)),
923
+ handles: {
924
+ bluesky: member.handle ?? member.did
925
+ },
926
+ metadata: {
927
+ accountId: this.getAccountId(),
928
+ blueskyDid: member.did,
929
+ blueskyHandle: member.handle,
930
+ avatar: member.avatar
931
+ }
932
+ };
933
+ }
934
+ return null;
935
+ }
936
+ buildConversationTarget(conversation, score) {
937
+ const sessionDid = this.client.getSession()?.did;
938
+ const otherMembers = conversation.members.filter((member) => member.did !== sessionDid);
939
+ const label = otherMembers.map((member) => member.displayName || member.handle || member.did).filter(Boolean).join(", ") || `BlueSky conversation ${conversation.id}`;
940
+ return {
941
+ target: {
942
+ source: "bluesky",
943
+ accountId: this.getAccountId(),
944
+ channelId: conversation.id
945
+ },
946
+ label,
947
+ kind: "thread",
948
+ description: "BlueSky direct message conversation",
949
+ score,
950
+ contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
951
+ metadata: {
952
+ accountId: this.getAccountId(),
953
+ blueskyConvoId: conversation.id,
954
+ unreadCount: conversation.unreadCount,
955
+ muted: conversation.muted,
956
+ members: conversation.members.map((member) => ({
957
+ did: member.did,
958
+ handle: member.handle,
959
+ displayName: member.displayName
960
+ }))
961
+ }
962
+ };
963
+ }
964
+ messageToMemory(runtime, message, convoId) {
965
+ const senderDid = message.sender.did || "unknown";
966
+ const createdAt = Date.parse(message.sentAt) || Date.now();
967
+ const entityId = senderDid === runtime.agentId ? runtime.agentId : createUniqueUuid(runtime, `bluesky:user:${senderDid}`);
968
+ const roomId = createUniqueUuid(runtime, `bluesky:dm:${convoId}`);
969
+ return {
970
+ id: createUniqueUuid(runtime, `bluesky:dm:${message.id}`),
971
+ agentId: runtime.agentId,
972
+ entityId,
973
+ roomId,
974
+ createdAt,
975
+ content: {
976
+ text: message.text ?? "",
977
+ source: "bluesky",
978
+ channelType: ChannelType.DM
979
+ },
980
+ metadata: {
981
+ type: "message",
982
+ source: "bluesky",
983
+ accountId: this.getAccountId(),
984
+ provider: "bluesky",
985
+ timestamp: createdAt,
986
+ fromBot: entityId === runtime.agentId,
987
+ messageIdFull: message.id,
988
+ chatType: ChannelType.DM,
989
+ sender: {
990
+ id: senderDid
991
+ },
992
+ bluesky: {
993
+ accountId: this.getAccountId(),
994
+ messageId: message.id,
995
+ convoId,
996
+ rev: message.rev,
997
+ senderDid
998
+ }
999
+ }
1000
+ };
1001
+ }
517
1002
  async generateReply() {
518
1003
  const prompt = composePrompt({
519
1004
  state: {},
@@ -526,16 +1011,46 @@ class BlueSkyMessageService {
526
1011
  return response;
527
1012
  }
528
1013
  }
1014
+ function clampLimit(value, fallback, max) {
1015
+ if (!Number.isFinite(value))
1016
+ return fallback;
1017
+ return Math.min(Math.max(1, Math.floor(value)), max);
1018
+ }
529
1019
 
530
1020
  // services/post.ts
531
- import { composePrompt as composePrompt2, ModelType as ModelType2 } from "@elizaos/core";
1021
+ import {
1022
+ ChannelType as ChannelType2,
1023
+ composePrompt as composePrompt2,
1024
+ createUniqueUuid as createUniqueUuid2,
1025
+ ModelType as ModelType2
1026
+ } from "@elizaos/core";
1027
+ function clampLimit2(value, fallback, max) {
1028
+ if (!Number.isFinite(value))
1029
+ return fallback;
1030
+ return Math.min(Math.max(1, Math.floor(value)), max);
1031
+ }
1032
+ function readContentString(content, keys) {
1033
+ const record = content;
1034
+ for (const key of keys) {
1035
+ const value = record[key];
1036
+ if (typeof value === "string" && value.trim())
1037
+ return value.trim();
1038
+ }
1039
+ return;
1040
+ }
1041
+
532
1042
  class BlueSkyPostService {
533
1043
  client;
534
1044
  runtime;
1045
+ accountId;
535
1046
  static serviceType = "IPostService";
536
- constructor(client, runtime) {
1047
+ constructor(client, runtime, accountId = "default") {
537
1048
  this.client = client;
538
1049
  this.runtime = runtime;
1050
+ this.accountId = accountId;
1051
+ }
1052
+ getAccountId() {
1053
+ return normalizeBlueSkyAccountId(this.accountId);
539
1054
  }
540
1055
  async getPosts(limit = 50, cursor) {
541
1056
  const response = await this.client.getTimeline({ limit, cursor });
@@ -552,9 +1067,91 @@ class BlueSkyPostService {
552
1067
  };
553
1068
  return this.client.sendPost(request);
554
1069
  }
1070
+ async handleSendPost(runtime, content) {
1071
+ const requestedAccountId = normalizeBlueSkyAccountId(readBlueSkyAccountId(content) ?? this.getAccountId());
1072
+ if (requestedAccountId !== this.getAccountId()) {
1073
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
1074
+ }
1075
+ const replyUri = readContentString(content, ["replyToUri", "replyTo"]);
1076
+ const replyCid = readContentString(content, ["replyToCid"]);
1077
+ const post = await this.createPost(content.text ?? "", replyUri && replyCid ? { uri: replyUri, cid: replyCid } : undefined);
1078
+ return this.postToMemory(runtime, post);
1079
+ }
1080
+ async fetchFeed(context, params = {}) {
1081
+ const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
1082
+ if (requestedAccountId !== this.getAccountId()) {
1083
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
1084
+ }
1085
+ const response = await this.client.getTimeline({
1086
+ limit: clampLimit2(params.limit, 25, 100),
1087
+ cursor: params.cursor
1088
+ });
1089
+ return response.feed.map((item) => this.postToMemory(context.runtime, item.post));
1090
+ }
1091
+ async searchPosts(context, params) {
1092
+ const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
1093
+ if (requestedAccountId !== this.getAccountId()) {
1094
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
1095
+ }
1096
+ const query = params.query?.trim();
1097
+ if (!query) {
1098
+ throw new Error("BlueSky searchPosts connector requires a query.");
1099
+ }
1100
+ const response = await this.client.searchPosts({
1101
+ query,
1102
+ limit: clampLimit2(params.limit, 25, 100),
1103
+ cursor: params.cursor
1104
+ });
1105
+ return response.posts.map((post) => this.postToMemory(context.runtime, post));
1106
+ }
555
1107
  async deletePost(uri) {
556
1108
  await this.client.deletePost(uri);
557
1109
  }
1110
+ postToMemory(runtime, post) {
1111
+ const createdAt = Date.parse(post.indexedAt || post.record.createdAt) || Date.now();
1112
+ const authorId = post.author.did || post.author.handle || "unknown";
1113
+ const entityId = authorId === runtime.agentId ? runtime.agentId : createUniqueUuid2(runtime, `bluesky:user:${authorId}`);
1114
+ const roomId = createUniqueUuid2(runtime, `bluesky:feed:${authorId}`);
1115
+ return {
1116
+ id: createUniqueUuid2(runtime, `bluesky:post:${post.uri}`),
1117
+ agentId: runtime.agentId,
1118
+ entityId,
1119
+ roomId,
1120
+ createdAt,
1121
+ content: {
1122
+ text: post.record.text,
1123
+ source: "bluesky",
1124
+ url: `https://bsky.app/profile/${post.author.handle}/post/${post.uri.split("/").pop()}`,
1125
+ channelType: ChannelType2.FEED
1126
+ },
1127
+ metadata: {
1128
+ type: "message",
1129
+ source: "bluesky",
1130
+ accountId: this.getAccountId(),
1131
+ provider: "bluesky",
1132
+ timestamp: createdAt,
1133
+ fromBot: entityId === runtime.agentId,
1134
+ messageIdFull: post.uri,
1135
+ chatType: ChannelType2.FEED,
1136
+ sender: {
1137
+ id: authorId,
1138
+ name: post.author.displayName,
1139
+ username: post.author.handle
1140
+ },
1141
+ bluesky: {
1142
+ accountId: this.getAccountId(),
1143
+ uri: post.uri,
1144
+ cid: post.cid,
1145
+ authorDid: post.author.did,
1146
+ authorHandle: post.author.handle,
1147
+ replyCount: post.replyCount,
1148
+ repostCount: post.repostCount,
1149
+ likeCount: post.likeCount,
1150
+ quoteCount: post.quoteCount
1151
+ }
1152
+ }
1153
+ };
1154
+ }
558
1155
  async generateContent() {
559
1156
  const prompt = composePrompt2({
560
1157
  state: {
@@ -588,9 +1185,7 @@ class BlueSkyPostService {
588
1185
  // services/bluesky.ts
589
1186
  class BlueSkyService extends Service {
590
1187
  static instance;
591
- managers = new Map;
592
- messageServices = new Map;
593
- postServices = new Map;
1188
+ agents = new Map;
594
1189
  static serviceType = BLUESKY_SERVICE_NAME;
595
1190
  capabilityDescription = "Send and receive messages on BlueSky";
596
1191
  static getInstance() {
@@ -599,48 +1194,230 @@ class BlueSkyService extends Service {
599
1194
  }
600
1195
  static async start(runtime) {
601
1196
  const service = BlueSkyService.getInstance();
602
- if (service.managers.has(runtime.agentId)) {
1197
+ if (service.agents.has(runtime.agentId)) {
603
1198
  return service;
604
1199
  }
605
1200
  if (!hasBlueSkyEnabled(runtime)) {
606
1201
  return service;
607
1202
  }
608
- const config = validateBlueSkyConfig(runtime);
609
- const client = new BlueSkyClient({
610
- service: config.service,
611
- handle: config.handle,
612
- password: config.password,
613
- dryRun: config.dryRun
614
- });
615
- const manager = new BlueSkyAgentManager(runtime, config, client);
616
- service.managers.set(runtime.agentId, manager);
617
- service.messageServices.set(runtime.agentId, new BlueSkyMessageService(client, runtime));
618
- service.postServices.set(runtime.agentId, new BlueSkyPostService(client, runtime));
619
- await manager.start();
620
- logger3.success({ agentId: runtime.agentId }, "BlueSky client started");
1203
+ const accountIds = listBlueSkyAccountIds(runtime);
1204
+ const defaultAccountId = normalizeBlueSkyAccountId(resolveDefaultBlueSkyAccountId(runtime));
1205
+ const accounts = {
1206
+ defaultAccountId,
1207
+ managers: new Map,
1208
+ messageServices: new Map,
1209
+ postServices: new Map
1210
+ };
1211
+ service.agents.set(runtime.agentId, accounts);
1212
+ for (const accountId of accountIds) {
1213
+ if (!hasBlueSkyEnabled(runtime, accountId)) {
1214
+ continue;
1215
+ }
1216
+ const config = validateBlueSkyConfig(runtime, accountId);
1217
+ if (!config.handle || !config.password) {
1218
+ logger3.warn({ agentId: runtime.agentId, accountId }, "Skipping BlueSky account without handle/password");
1219
+ continue;
1220
+ }
1221
+ const client = new BlueSkyClient({
1222
+ service: config.service,
1223
+ handle: config.handle,
1224
+ password: config.password,
1225
+ dryRun: config.dryRun
1226
+ });
1227
+ const manager = new BlueSkyAgentManager(runtime, config, client);
1228
+ accounts.managers.set(accountId, manager);
1229
+ accounts.messageServices.set(accountId, new BlueSkyMessageService(client, runtime, accountId));
1230
+ accounts.postServices.set(accountId, new BlueSkyPostService(client, runtime, accountId));
1231
+ await manager.start();
1232
+ logger3.success({ agentId: runtime.agentId, accountId }, "BlueSky client started");
1233
+ }
621
1234
  return service;
622
1235
  }
623
1236
  static async stop(runtime) {
624
1237
  const service = BlueSkyService.getInstance();
625
- const manager = service.managers.get(runtime.agentId);
626
- if (!manager)
1238
+ const accounts = service.agents.get(runtime.agentId);
1239
+ if (!accounts)
627
1240
  return;
628
- await manager.stop();
629
- service.managers.delete(runtime.agentId);
630
- service.messageServices.delete(runtime.agentId);
631
- service.postServices.delete(runtime.agentId);
1241
+ for (const manager of accounts.managers.values()) {
1242
+ await manager.stop();
1243
+ }
1244
+ service.agents.delete(runtime.agentId);
632
1245
  logger3.info({ agentId: runtime.agentId }, "BlueSky client stopped");
633
1246
  }
1247
+ static registerSendHandlers(runtime, serviceInstance) {
1248
+ const accounts = serviceInstance?.agents.get(runtime.agentId);
1249
+ if (!accounts) {
1250
+ runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register BlueSky connectors; service is not initialized");
1251
+ return;
1252
+ }
1253
+ for (const postService of accounts.postServices.values()) {
1254
+ BlueSkyService.registerPostConnector(runtime, postService);
1255
+ }
1256
+ if (typeof runtime.registerMessageConnector === "function" && accounts.messageServices.size > 0) {
1257
+ for (const messageService2 of accounts.messageServices.values()) {
1258
+ BlueSkyService.registerMessageConnector(runtime, messageService2);
1259
+ }
1260
+ return;
1261
+ }
1262
+ const defaultAccountId = normalizeBlueSkyAccountId(accounts.defaultAccountId);
1263
+ const messageService = accounts.messageServices.get(defaultAccountId);
1264
+ if (!messageService) {
1265
+ runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register legacy BlueSky DM send handler; default message service is not initialized");
1266
+ return;
1267
+ }
1268
+ runtime.registerSendHandler("bluesky", messageService.handleSendMessage.bind(messageService));
1269
+ }
1270
+ static registerMessageConnector(runtime, messageService) {
1271
+ const accountId = messageService.getAccountId();
1272
+ const registration = {
1273
+ source: "bluesky",
1274
+ accountId,
1275
+ label: "BlueSky",
1276
+ description: "BlueSky DM connector for sending private messages to conversations.",
1277
+ capabilities: [
1278
+ "send_message",
1279
+ "fetch_messages",
1280
+ "resolve_targets",
1281
+ "list_rooms",
1282
+ "chat_context",
1283
+ "user_context"
1284
+ ],
1285
+ supportedTargetKinds: ["thread", "user"],
1286
+ contexts: ["social", "connectors"],
1287
+ metadata: {
1288
+ accountId,
1289
+ service: BLUESKY_SERVICE_NAME
1290
+ },
1291
+ resolveTargets: messageService.resolveConnectorTargets.bind(messageService),
1292
+ listRecentTargets: messageService.listRecentConnectorTargets.bind(messageService),
1293
+ listRooms: messageService.listConnectorRooms.bind(messageService),
1294
+ getChatContext: messageService.getConnectorChatContext.bind(messageService),
1295
+ getUserContext: messageService.getConnectorUserContext.bind(messageService),
1296
+ fetchMessages: messageService.fetchConnectorMessages.bind(messageService),
1297
+ contentShaping: {
1298
+ systemPromptFragment: "For BlueSky DMs, keep messages direct and conversational. Avoid public-feed conventions like hashtags unless the user asked.",
1299
+ constraints: {
1300
+ supportsMarkdown: false,
1301
+ channelType: ChannelType3.DM
1302
+ }
1303
+ },
1304
+ sendHandler: messageService.handleSendMessage.bind(messageService)
1305
+ };
1306
+ runtime.registerMessageConnector(registration);
1307
+ runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky DM connector");
1308
+ }
1309
+ static registerPostConnector(runtime, postService) {
1310
+ const withPostConnector = runtime;
1311
+ if (typeof withPostConnector.registerPostConnector !== "function") {
1312
+ return;
1313
+ }
1314
+ const accountId = postService.getAccountId?.() ?? DEFAULT_BLUESKY_ACCOUNT_ID;
1315
+ withPostConnector.registerPostConnector({
1316
+ source: "bluesky",
1317
+ accountId,
1318
+ label: "BlueSky",
1319
+ description: "BlueSky public feed connector for publishing posts, reading the timeline, and searching posts.",
1320
+ capabilities: ["post", "fetch_feed", "search_posts"],
1321
+ contexts: ["social", "social_posting", "connectors"],
1322
+ metadata: {
1323
+ accountId,
1324
+ service: BLUESKY_SERVICE_NAME
1325
+ },
1326
+ postHandler: postService.handleSendPost.bind(postService),
1327
+ fetchFeed: postService.fetchFeed.bind(postService),
1328
+ searchPosts: postService.searchPosts.bind(postService),
1329
+ contentShaping: {
1330
+ systemPromptFragment: "For BlueSky posts, write a public post under 300 characters. Handles, links, and facets are supported by the connector; do not exceed the platform limit.",
1331
+ constraints: {
1332
+ maxLength: 300,
1333
+ supportsMarkdown: false,
1334
+ channelType: ChannelType3.FEED
1335
+ }
1336
+ }
1337
+ });
1338
+ runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky post connector");
1339
+ }
634
1340
  async stop() {
635
- for (const manager of this.managers.values()) {
636
- await BlueSkyService.stop(manager.runtime);
1341
+ for (const [agentId, accounts] of this.agents) {
1342
+ for (const manager of accounts.managers.values()) {
1343
+ await manager.stop();
1344
+ }
1345
+ this.agents.delete(agentId);
637
1346
  }
638
1347
  }
639
- getMessageService(agentId) {
640
- return this.messageServices.get(agentId);
1348
+ getMessageServiceForAccount(accountId, agentId) {
1349
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1350
+ if (!resolvedAgentId)
1351
+ return;
1352
+ const accounts = this.agents.get(resolvedAgentId);
1353
+ if (!accounts)
1354
+ return;
1355
+ const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
1356
+ return accounts.messageServices.get(id);
1357
+ }
1358
+ getPostServiceForAccount(accountId, agentId) {
1359
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1360
+ if (!resolvedAgentId)
1361
+ return;
1362
+ const accounts = this.agents.get(resolvedAgentId);
1363
+ if (!accounts)
1364
+ return;
1365
+ const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
1366
+ return accounts.postServices.get(id);
1367
+ }
1368
+ getMessageService(agentId, accountId) {
1369
+ return this.getMessageServiceForAccount(accountId, agentId);
1370
+ }
1371
+ getPostService(agentId, accountId) {
1372
+ return this.getPostServiceForAccount(accountId, agentId);
1373
+ }
1374
+ getDefaultAccountId(agentId) {
1375
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1376
+ return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
641
1377
  }
642
- getPostService(agentId) {
643
- return this.postServices.get(agentId);
1378
+ listAccountIds(agentId) {
1379
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1380
+ const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
1381
+ return accounts ? Array.from(accounts.managers.keys()) : [];
1382
+ }
1383
+ firstAgentId() {
1384
+ return this.agents.keys().next().value;
1385
+ }
1386
+ }
1387
+
1388
+ // workflow-credential-provider.ts
1389
+ import { Service as Service2 } from "@elizaos/core";
1390
+ var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
1391
+ var SUPPORTED = ["httpHeaderAuth"];
1392
+
1393
+ class BlueskyWorkflowCredentialProvider extends Service2 {
1394
+ static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
1395
+ capabilityDescription = "Supplies Bluesky credentials to the workflow plugin.";
1396
+ static async start(runtime) {
1397
+ return new BlueskyWorkflowCredentialProvider(runtime);
1398
+ }
1399
+ async stop() {}
1400
+ async resolve(_userId, credType) {
1401
+ if (credType !== "httpHeaderAuth")
1402
+ return null;
1403
+ const handle = this.runtime.getSetting("BLUESKY_HANDLE");
1404
+ const password = this.runtime.getSetting("BLUESKY_PASSWORD");
1405
+ if (!handle?.trim() || !password?.trim())
1406
+ return null;
1407
+ return {
1408
+ status: "credential_data",
1409
+ data: {
1410
+ name: "X-Bluesky-Handle",
1411
+ value: handle.trim(),
1412
+ appPassword: password.trim()
1413
+ }
1414
+ };
1415
+ }
1416
+ checkCredentialTypes(credTypes) {
1417
+ return {
1418
+ supported: credTypes.filter((t) => SUPPORTED.includes(t)),
1419
+ unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
1420
+ };
644
1421
  }
645
1422
  }
646
1423
 
@@ -696,20 +1473,38 @@ var blueSkyPlugin = {
696
1473
  BLUESKY_ACTION_INTERVAL: process.env.BLUESKY_ACTION_INTERVAL ?? null,
697
1474
  BLUESKY_POST_IMMEDIATELY: process.env.BLUESKY_POST_IMMEDIATELY ?? null,
698
1475
  BLUESKY_MAX_ACTIONS_PROCESSING: process.env.BLUESKY_MAX_ACTIONS_PROCESSING ?? null,
699
- BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null
1476
+ BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null,
1477
+ BLUESKY_ACCOUNTS: process.env.BLUESKY_ACCOUNTS ?? null,
1478
+ BLUESKY_DEFAULT_ACCOUNT_ID: process.env.BLUESKY_DEFAULT_ACCOUNT_ID ?? null
700
1479
  },
701
- async init(_config, _runtime) {
1480
+ async init(_config, runtime) {
1481
+ try {
1482
+ const manager = getConnectorAccountManager(runtime);
1483
+ manager.registerProvider(createBlueSkyConnectorAccountProvider(runtime));
1484
+ } catch (err) {
1485
+ logger4.warn({
1486
+ src: "plugin:bluesky",
1487
+ err: err instanceof Error ? err.message : String(err)
1488
+ }, "Failed to register BlueSky provider with ConnectorAccountManager");
1489
+ }
702
1490
  logger4.log("BlueSky plugin initialized");
703
1491
  },
704
- services: [BlueSkyService],
1492
+ actions: [],
1493
+ services: [BlueSkyService, BlueskyWorkflowCredentialProvider],
705
1494
  tests: pluginTests
706
1495
  };
707
- var typescript_default = blueSkyPlugin;
708
1496
  export {
709
- typescript_default as default,
1497
+ resolveDefaultBlueSkyAccountId,
1498
+ readBlueSkyAccountId,
1499
+ normalizeBlueSkyAccountId,
1500
+ listBlueSkyAccountIds,
1501
+ default2 as default,
1502
+ createBlueSkyConnectorAccountProvider,
710
1503
  blueSkyPlugin,
1504
+ DEFAULT_BLUESKY_ACCOUNT_ID,
711
1505
  BlueSkyService,
712
- BlueSkyClient
1506
+ BlueSkyClient,
1507
+ BLUESKY_PROVIDER_ID
713
1508
  };
714
1509
 
715
- //# debugId=8E1C17CA465481F364756E2164756E21
1510
+ //# debugId=ABC5C845CC8C11C664756E2164756E21