@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,54 +1,88 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
1
3
  var __defProp = Object.defineProperty;
2
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
6
32
  var __toCommonJS = (from) => {
7
- var entry = __moduleCache.get(from), desc;
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
8
34
  if (entry)
9
35
  return entry;
10
36
  entry = __defProp({}, "__esModule", { value: true });
11
- if (from && typeof from === "object" || typeof from === "function")
12
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
- get: () => from[key],
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- }));
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
16
45
  __moduleCache.set(from, entry);
17
46
  return entry;
18
47
  };
48
+ var __moduleCache;
49
+ var __returnValue = (v) => v;
50
+ function __exportSetter(name, newValue) {
51
+ this[name] = __returnValue.bind(null, newValue);
52
+ }
19
53
  var __export = (target, all) => {
20
54
  for (var name in all)
21
55
  __defProp(target, name, {
22
56
  get: all[name],
23
57
  enumerable: true,
24
58
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
59
+ set: __exportSetter.bind(all, name)
26
60
  });
27
61
  };
28
62
 
29
63
  // index.node.ts
30
64
  var exports_index_node = {};
31
65
  __export(exports_index_node, {
32
- default: () => typescript_default,
66
+ resolveDefaultBlueSkyAccountId: () => resolveDefaultBlueSkyAccountId,
67
+ readBlueSkyAccountId: () => readBlueSkyAccountId,
68
+ normalizeBlueSkyAccountId: () => normalizeBlueSkyAccountId,
69
+ listBlueSkyAccountIds: () => listBlueSkyAccountIds,
70
+ default: () => import__.default,
71
+ createBlueSkyConnectorAccountProvider: () => createBlueSkyConnectorAccountProvider,
33
72
  blueSkyPlugin: () => blueSkyPlugin,
73
+ DEFAULT_BLUESKY_ACCOUNT_ID: () => DEFAULT_BLUESKY_ACCOUNT_ID,
34
74
  BlueSkyService: () => BlueSkyService,
35
- BlueSkyClient: () => BlueSkyClient
75
+ BlueSkyClient: () => BlueSkyClient,
76
+ BLUESKY_PROVIDER_ID: () => BLUESKY_PROVIDER_ID
36
77
  });
37
78
  module.exports = __toCommonJS(exports_index_node);
38
79
 
39
80
  // index.ts
40
- var import_core6 = require("@elizaos/core");
41
-
42
- // services/bluesky.ts
43
- var import_core5 = require("@elizaos/core");
44
-
45
- // client.ts
46
- var import_api = require("@atproto/api");
47
- var import_core = require("@elizaos/core");
48
- var import_lru_cache = require("lru-cache");
81
+ var import_core7 = require("@elizaos/core");
49
82
 
50
83
  // types/index.ts
51
- var import_zod = require("zod");
84
+ var zod = __toESM(require("zod"));
85
+ var z2 = zod.z ?? zod;
52
86
  var BLUESKY_SERVICE_URL = "https://bsky.social";
53
87
  var BLUESKY_MAX_POST_LENGTH = 300;
54
88
  var BLUESKY_POLL_INTERVAL = 60;
@@ -73,20 +107,20 @@ var CACHE_SIZE = {
73
107
  NOTIFICATIONS: 1000,
74
108
  CONVERSATIONS: 100
75
109
  };
76
- var BlueSkyConfigSchema = import_zod.z.object({
77
- handle: import_zod.z.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
78
- password: import_zod.z.string().min(1),
79
- service: import_zod.z.string().url().default(BLUESKY_SERVICE_URL),
80
- dryRun: import_zod.z.boolean().default(false),
81
- pollInterval: import_zod.z.number().positive().default(BLUESKY_POLL_INTERVAL),
82
- enablePost: import_zod.z.boolean().default(true),
83
- postIntervalMin: import_zod.z.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
84
- postIntervalMax: import_zod.z.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
85
- enableActionProcessing: import_zod.z.boolean().default(true),
86
- actionInterval: import_zod.z.number().positive().default(BLUESKY_ACTION_INTERVAL),
87
- postImmediately: import_zod.z.boolean().default(false),
88
- maxActionsProcessing: import_zod.z.number().positive().default(BLUESKY_MAX_ACTIONS),
89
- enableDMs: import_zod.z.boolean().default(true)
110
+ var BlueSkyConfigSchema = z2.object({
111
+ handle: z2.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
112
+ password: z2.string().min(1),
113
+ service: z2.string().url().default(BLUESKY_SERVICE_URL),
114
+ dryRun: z2.boolean().default(false),
115
+ pollInterval: z2.number().positive().default(BLUESKY_POLL_INTERVAL),
116
+ enablePost: z2.boolean().default(true),
117
+ postIntervalMin: z2.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
118
+ postIntervalMax: z2.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
119
+ enableActionProcessing: z2.boolean().default(true),
120
+ actionInterval: z2.number().positive().default(BLUESKY_ACTION_INTERVAL),
121
+ postImmediately: z2.boolean().default(false),
122
+ maxActionsProcessing: z2.number().positive().default(BLUESKY_MAX_ACTIONS),
123
+ enableDMs: z2.boolean().default(true)
90
124
  });
91
125
 
92
126
  class BlueSkyError extends Error {
@@ -100,7 +134,250 @@ class BlueSkyError extends Error {
100
134
  }
101
135
  }
102
136
 
137
+ // utils/config.ts
138
+ var DEFAULT_BLUESKY_ACCOUNT_ID = "default";
139
+ function getApiKeyOptional(runtime, key) {
140
+ const value = runtime.getSetting(key);
141
+ return typeof value === "string" ? value : undefined;
142
+ }
143
+ function stringSetting(runtime, key) {
144
+ const value = runtime.getSetting(key);
145
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
146
+ }
147
+ function characterConfig(runtime) {
148
+ const settings = runtime.character?.settings;
149
+ const raw = settings?.bluesky;
150
+ return raw && typeof raw === "object" ? raw : {};
151
+ }
152
+ function parseAccountsJson(runtime) {
153
+ const raw = stringSetting(runtime, "BLUESKY_ACCOUNTS");
154
+ if (!raw)
155
+ return {};
156
+ try {
157
+ const parsed = JSON.parse(raw);
158
+ if (Array.isArray(parsed)) {
159
+ return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [
160
+ normalizeBlueSkyAccountId(item.accountId ?? item.id),
161
+ item
162
+ ]));
163
+ }
164
+ return parsed && typeof parsed === "object" ? parsed : {};
165
+ } catch {
166
+ return {};
167
+ }
168
+ }
169
+ function allAccountConfigs(runtime) {
170
+ return {
171
+ ...characterConfig(runtime).accounts ?? {},
172
+ ...parseAccountsJson(runtime)
173
+ };
174
+ }
175
+ function accountConfig(runtime, accountId) {
176
+ const accounts = allAccountConfigs(runtime);
177
+ return accounts[accountId] ?? accounts[normalizeBlueSkyAccountId(accountId)] ?? {};
178
+ }
179
+ function readRawField(record, keys) {
180
+ if (!record)
181
+ return;
182
+ for (const key of keys) {
183
+ const value = record[key];
184
+ if (typeof value === "string" && value.trim())
185
+ return value.trim();
186
+ if (typeof value === "number" || typeof value === "boolean")
187
+ return String(value);
188
+ }
189
+ return;
190
+ }
191
+ function boolValue(value, fallback = false) {
192
+ if (typeof value === "boolean")
193
+ return value;
194
+ if (typeof value === "string")
195
+ return value.trim().toLowerCase() === "true";
196
+ return fallback;
197
+ }
198
+ function intValue(value, fallback) {
199
+ const parsed = Number.parseInt(String(value ?? ""), 10);
200
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
201
+ }
202
+ function normalizeBlueSkyAccountId(accountId) {
203
+ if (typeof accountId !== "string")
204
+ return DEFAULT_BLUESKY_ACCOUNT_ID;
205
+ const trimmed = accountId.trim();
206
+ return trimmed || DEFAULT_BLUESKY_ACCOUNT_ID;
207
+ }
208
+ function listBlueSkyAccountIds(runtime) {
209
+ const ids = new Set;
210
+ const config = characterConfig(runtime);
211
+ if (stringSetting(runtime, "BLUESKY_HANDLE") || config.handle && config.password) {
212
+ ids.add(DEFAULT_BLUESKY_ACCOUNT_ID);
213
+ }
214
+ for (const id of Object.keys(allAccountConfigs(runtime))) {
215
+ ids.add(normalizeBlueSkyAccountId(id));
216
+ }
217
+ return Array.from(ids.size ? ids : new Set([DEFAULT_BLUESKY_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
218
+ }
219
+ function resolveDefaultBlueSkyAccountId(runtime) {
220
+ const requested = stringSetting(runtime, "BLUESKY_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "BLUESKY_ACCOUNT_ID");
221
+ if (requested)
222
+ return normalizeBlueSkyAccountId(requested);
223
+ const ids = listBlueSkyAccountIds(runtime);
224
+ return ids.includes(DEFAULT_BLUESKY_ACCOUNT_ID) ? DEFAULT_BLUESKY_ACCOUNT_ID : ids[0] ?? DEFAULT_BLUESKY_ACCOUNT_ID;
225
+ }
226
+ function readBlueSkyAccountId(...sources) {
227
+ for (const source of sources) {
228
+ if (!source || typeof source !== "object")
229
+ continue;
230
+ const record = source;
231
+ const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
232
+ const data = record.data && typeof record.data === "object" ? record.data : {};
233
+ const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
234
+ const bluesky = data.bluesky && typeof data.bluesky === "object" ? data.bluesky : {};
235
+ const value = record.accountId ?? parameters.accountId ?? data.accountId ?? bluesky.accountId ?? metadata.accountId;
236
+ if (typeof value === "string" && value.trim())
237
+ return normalizeBlueSkyAccountId(value);
238
+ }
239
+ return;
240
+ }
241
+ function hasBlueSkyEnabled(runtime, accountId) {
242
+ const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
243
+ const base = characterConfig(runtime);
244
+ const account = accountConfig(runtime, normalizedAccountId);
245
+ const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
246
+ const enabled = readRawField(account, ["enabled", "BLUESKY_ENABLED"]) ?? readRawField(base, ["enabled", "BLUESKY_ENABLED"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLED") : undefined);
247
+ if (enabled)
248
+ return String(enabled).toLowerCase() === "true";
249
+ 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)));
250
+ }
251
+ function validateBlueSkyConfig(runtime, accountId) {
252
+ const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
253
+ const base = characterConfig(runtime);
254
+ const account = accountConfig(runtime, normalizedAccountId);
255
+ const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
256
+ const result = BlueSkyConfigSchema.safeParse({
257
+ handle: readRawField(account, ["handle", "BLUESKY_HANDLE"]) ?? readRawField(base, ["handle", "BLUESKY_HANDLE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_HANDLE") : undefined) ?? "",
258
+ password: readRawField(account, ["password", "BLUESKY_PASSWORD"]) ?? readRawField(base, ["password", "BLUESKY_PASSWORD"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_PASSWORD") : undefined) ?? "",
259
+ service: readRawField(account, ["service", "BLUESKY_SERVICE"]) ?? readRawField(base, ["service", "BLUESKY_SERVICE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_SERVICE") : undefined) ?? BLUESKY_SERVICE_URL,
260
+ dryRun: boolValue(readRawField(account, ["dryRun", "BLUESKY_DRY_RUN"]) ?? readRawField(base, ["dryRun", "BLUESKY_DRY_RUN"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_DRY_RUN") : undefined)),
261
+ pollInterval: intValue(readRawField(account, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? readRawField(base, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POLL_INTERVAL") : undefined), BLUESKY_POLL_INTERVAL),
262
+ enablePost: String(readRawField(account, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? readRawField(base, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_POSTING") : undefined) ?? "true").toLowerCase() !== "false",
263
+ 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),
264
+ 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),
265
+ enableActionProcessing: String(readRawField(account, [
266
+ "enableActionProcessing",
267
+ "BLUESKY_ENABLE_ACTION_PROCESSING"
268
+ ]) ?? readRawField(base, [
269
+ "enableActionProcessing",
270
+ "BLUESKY_ENABLE_ACTION_PROCESSING"
271
+ ]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") : undefined) ?? "true").toLowerCase() !== "false",
272
+ actionInterval: intValue(readRawField(account, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? readRawField(base, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ACTION_INTERVAL") : undefined), BLUESKY_ACTION_INTERVAL),
273
+ postImmediately: boolValue(readRawField(account, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? readRawField(base, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_IMMEDIATELY") : undefined)),
274
+ maxActionsProcessing: intValue(readRawField(account, [
275
+ "maxActionsProcessing",
276
+ "BLUESKY_MAX_ACTIONS_PROCESSING"
277
+ ]) ?? readRawField(base, [
278
+ "maxActionsProcessing",
279
+ "BLUESKY_MAX_ACTIONS_PROCESSING"
280
+ ]) ?? (allowEnv ? runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") : undefined), BLUESKY_MAX_ACTIONS),
281
+ enableDMs: String(readRawField(account, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? readRawField(base, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_DMS") : undefined) ?? "true").toLowerCase() !== "false"
282
+ });
283
+ if (!result.success) {
284
+ const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
285
+ throw new Error(`Invalid BlueSky configuration: ${errors}`);
286
+ }
287
+ return { ...result.data, accountId: normalizedAccountId };
288
+ }
289
+ function getPollInterval(runtime, accountId) {
290
+ const config = validateBlueSkyConfig(runtime, accountId);
291
+ return config.pollInterval * 1000;
292
+ }
293
+ function getActionInterval(runtime, accountId) {
294
+ const config = validateBlueSkyConfig(runtime, accountId);
295
+ return config.actionInterval * 1000;
296
+ }
297
+ function getMaxActionsProcessing(runtime, accountId) {
298
+ const config = validateBlueSkyConfig(runtime, accountId);
299
+ return config.maxActionsProcessing;
300
+ }
301
+ function isPostingEnabled(runtime, accountId) {
302
+ const config = validateBlueSkyConfig(runtime, accountId);
303
+ return config.enablePost;
304
+ }
305
+ function shouldPostImmediately(runtime, accountId) {
306
+ const config = validateBlueSkyConfig(runtime, accountId);
307
+ return config.postImmediately;
308
+ }
309
+ function getPostIntervalRange(runtime, accountId) {
310
+ const config = validateBlueSkyConfig(runtime, accountId);
311
+ return {
312
+ min: config.postIntervalMin * 1000,
313
+ max: config.postIntervalMax * 1000
314
+ };
315
+ }
316
+
317
+ // connector-account-provider.ts
318
+ var BLUESKY_PROVIDER_ID = "bluesky";
319
+ function toConnectorAccount(runtime, accountId) {
320
+ let connected = false;
321
+ let handle = "";
322
+ let service = "";
323
+ try {
324
+ const config = validateBlueSkyConfig(runtime, accountId);
325
+ connected = Boolean(config.handle && config.password);
326
+ handle = config.handle;
327
+ service = config.service;
328
+ } catch {
329
+ connected = false;
330
+ }
331
+ const now = Date.now();
332
+ return {
333
+ id: accountId,
334
+ provider: BLUESKY_PROVIDER_ID,
335
+ label: handle || accountId,
336
+ role: "OWNER",
337
+ purpose: ["posting", "reading"],
338
+ accessGate: "open",
339
+ status: connected ? "connected" : "disabled",
340
+ externalId: handle || undefined,
341
+ displayHandle: handle || undefined,
342
+ createdAt: now,
343
+ updatedAt: now,
344
+ metadata: {
345
+ service
346
+ }
347
+ };
348
+ }
349
+ function createBlueSkyConnectorAccountProvider(runtime) {
350
+ return {
351
+ provider: BLUESKY_PROVIDER_ID,
352
+ label: "BlueSky",
353
+ listAccounts: async (_manager) => {
354
+ const ids = listBlueSkyAccountIds(runtime);
355
+ return ids.map((id) => toConnectorAccount(runtime, id));
356
+ },
357
+ createAccount: async (input, _manager) => {
358
+ return {
359
+ ...input,
360
+ provider: BLUESKY_PROVIDER_ID,
361
+ role: input.role ?? "OWNER",
362
+ purpose: input.purpose ?? ["posting", "reading"],
363
+ accessGate: input.accessGate ?? "open",
364
+ status: input.status ?? "pending"
365
+ };
366
+ },
367
+ patchAccount: async (_accountId, patch, _manager) => {
368
+ return { ...patch, provider: BLUESKY_PROVIDER_ID };
369
+ },
370
+ deleteAccount: async (_accountId, _manager) => {}
371
+ };
372
+ }
373
+
374
+ // services/bluesky.ts
375
+ var import_core5 = require("@elizaos/core");
376
+
103
377
  // client.ts
378
+ var import_api = require("@atproto/api");
379
+ var import_core = require("@elizaos/core");
380
+ var import_lru_cache = require("lru-cache");
104
381
  function isPostView(item) {
105
382
  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";
106
383
  }
@@ -218,6 +495,18 @@ class BlueSkyClient {
218
495
  })
219
496
  };
220
497
  }
498
+ async searchPosts(params) {
499
+ const api = this.agent;
500
+ const response = await api.app.bsky.feed.searchPosts({
501
+ q: params.query,
502
+ limit: params.limit ?? 25,
503
+ cursor: params.cursor
504
+ });
505
+ return {
506
+ posts: response.data.posts.map(adaptPostView),
507
+ cursor: response.data.cursor
508
+ };
509
+ }
221
510
  async sendPost(request) {
222
511
  if (this.config.dryRun) {
223
512
  import_core.logger.info(`Dry run: would create post with text: ${request.content.text}`);
@@ -237,7 +526,20 @@ class BlueSkyClient {
237
526
  if (request.content.embed) {
238
527
  record.embed = request.content.embed;
239
528
  }
529
+ import_core.logger.info({
530
+ src: "plugin:bluesky",
531
+ op: "atproto:post",
532
+ textLength: rt.text.length,
533
+ hasReply: Boolean(request.replyTo),
534
+ hasEmbed: Boolean(request.content.embed)
535
+ }, "Publishing Bluesky post via atproto");
240
536
  const response = await this.agent.post(record);
537
+ import_core.logger.info({
538
+ src: "plugin:bluesky",
539
+ op: "atproto:post",
540
+ uri: response.uri,
541
+ cid: response.cid
542
+ }, "Bluesky post published");
241
543
  const thread = await this.agent.getPostThread({
242
544
  uri: response.uri,
243
545
  depth: 0
@@ -334,64 +636,10 @@ class BlueSkyClient {
334
636
 
335
637
  // managers/agent.ts
336
638
  var import_core2 = require("@elizaos/core");
337
-
338
- // utils/config.ts
339
- function getApiKeyOptional(runtime, key) {
340
- const value = runtime.getSetting(key);
341
- return typeof value === "string" ? value : undefined;
342
- }
343
- function hasBlueSkyEnabled(runtime) {
344
- const enabled = runtime.getSetting("BLUESKY_ENABLED");
345
- if (enabled)
346
- return String(enabled).toLowerCase() === "true";
347
- return Boolean(runtime.getSetting("BLUESKY_HANDLE") && runtime.getSetting("BLUESKY_PASSWORD"));
348
- }
349
- function validateBlueSkyConfig(runtime) {
350
- const result = BlueSkyConfigSchema.safeParse({
351
- handle: String(runtime.getSetting("BLUESKY_HANDLE") ?? ""),
352
- password: String(runtime.getSetting("BLUESKY_PASSWORD") ?? ""),
353
- service: String(runtime.getSetting("BLUESKY_SERVICE") ?? BLUESKY_SERVICE_URL),
354
- dryRun: runtime.getSetting("BLUESKY_DRY_RUN") === "true",
355
- pollInterval: parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL,
356
- enablePost: runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false",
357
- postIntervalMin: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN,
358
- postIntervalMax: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX,
359
- enableActionProcessing: runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") !== "false",
360
- actionInterval: parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL,
361
- postImmediately: runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true",
362
- maxActionsProcessing: parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS,
363
- enableDMs: runtime.getSetting("BLUESKY_ENABLE_DMS") !== "false"
364
- });
365
- if (!result.success) {
366
- const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
367
- throw new Error(`Invalid BlueSky configuration: ${errors}`);
368
- }
369
- return result.data;
370
- }
371
- function getPollInterval(runtime) {
372
- const seconds = parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL;
373
- return seconds * 1000;
374
- }
375
- function getActionInterval(runtime) {
376
- const seconds = parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL;
377
- return seconds * 1000;
378
- }
379
- function getMaxActionsProcessing(runtime) {
380
- return parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS;
381
- }
382
- function isPostingEnabled(runtime) {
383
- return runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false";
384
- }
385
- function shouldPostImmediately(runtime) {
386
- return runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true";
387
- }
388
- function getPostIntervalRange(runtime) {
389
- const min = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN;
390
- const max = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX;
391
- return { min: min * 1000, max: max * 1000 };
639
+ function cursorCacheKey(agentId, accountId) {
640
+ return `bluesky:cursor:${agentId}:${accountId}`;
392
641
  }
393
642
 
394
- // managers/agent.ts
395
643
  class BlueSkyAgentManager {
396
644
  runtime;
397
645
  config;
@@ -401,24 +649,33 @@ class BlueSkyAgentManager {
401
649
  postTimer = null;
402
650
  running = false;
403
651
  lastSeenAt = null;
652
+ accountId;
404
653
  constructor(runtime, config, client) {
405
654
  this.runtime = runtime;
406
655
  this.config = config;
407
656
  this.client = client;
657
+ this.accountId = normalizeBlueSkyAccountId(config.accountId ?? DEFAULT_BLUESKY_ACCOUNT_ID);
658
+ }
659
+ getAccountId() {
660
+ return this.accountId;
408
661
  }
409
662
  async start() {
410
663
  if (this.running)
411
664
  return;
412
665
  await this.client.authenticate();
413
666
  this.running = true;
667
+ const cached = await this.runtime.getCache(cursorCacheKey(this.runtime.agentId, this.accountId));
668
+ if (typeof cached === "string" && cached) {
669
+ this.lastSeenAt = cached;
670
+ }
414
671
  this.startNotificationPolling();
415
672
  if (this.config.enableActionProcessing) {
416
673
  this.startActionProcessing();
417
674
  }
418
- if (isPostingEnabled(this.runtime)) {
675
+ if (isPostingEnabled(this.runtime, this.accountId)) {
419
676
  this.startAutomatedPosting();
420
677
  }
421
- import_core2.logger.success({ agentId: this.runtime.agentId }, "BlueSky agent manager started");
678
+ import_core2.logger.success({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager started");
422
679
  }
423
680
  async stop() {
424
681
  this.running = false;
@@ -432,10 +689,10 @@ class BlueSkyAgentManager {
432
689
  this.actionTimer = null;
433
690
  this.postTimer = null;
434
691
  await this.client.cleanup();
435
- import_core2.logger.info({ agentId: this.runtime.agentId }, "BlueSky agent manager stopped");
692
+ import_core2.logger.info({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager stopped");
436
693
  }
437
694
  startNotificationPolling() {
438
- const interval = getPollInterval(this.runtime);
695
+ const interval = getPollInterval(this.runtime, this.accountId);
439
696
  this.pollNotifications();
440
697
  this.pollTimer = setInterval(() => this.pollNotifications(), interval);
441
698
  }
@@ -451,6 +708,7 @@ class BlueSkyAgentManager {
451
708
  }) : notifications;
452
709
  if (newNotifications.length > 0) {
453
710
  this.lastSeenAt = notifications[0].indexedAt;
711
+ await this.runtime.setCache(cursorCacheKey(this.runtime.agentId, this.accountId), this.lastSeenAt);
454
712
  for (const notification of newNotifications) {
455
713
  this.emitNotificationEvent(notification);
456
714
  }
@@ -471,26 +729,28 @@ class BlueSkyAgentManager {
471
729
  const payload = {
472
730
  runtime: this.runtime,
473
731
  source: "bluesky",
732
+ accountId: this.accountId,
474
733
  notification
475
734
  };
476
735
  this.runtime.emitEvent(event, payload);
477
736
  }
478
737
  }
479
738
  startActionProcessing() {
480
- const interval = getActionInterval(this.runtime);
481
- this.processActions();
482
- this.actionTimer = setInterval(() => this.processActions(), interval);
739
+ const interval = getActionInterval(this.runtime, this.accountId);
740
+ this.processQueuedActions();
741
+ this.actionTimer = setInterval(() => this.processQueuedActions(), interval);
483
742
  }
484
- async processActions() {
743
+ async processQueuedActions() {
485
744
  if (!this.running)
486
745
  return;
487
- const max = getMaxActionsProcessing(this.runtime);
746
+ const max = getMaxActionsProcessing(this.runtime, this.accountId);
488
747
  const { notifications } = await this.client.getNotifications(max);
489
748
  for (const notification of notifications) {
490
749
  if (notification.reason === "mention" || notification.reason === "reply") {
491
750
  const payload = {
492
751
  runtime: this.runtime,
493
752
  source: "bluesky",
753
+ accountId: this.accountId,
494
754
  notification
495
755
  };
496
756
  this.runtime.emitEvent("bluesky.should_respond", payload);
@@ -498,47 +758,88 @@ class BlueSkyAgentManager {
498
758
  }
499
759
  }
500
760
  startAutomatedPosting() {
501
- if (shouldPostImmediately(this.runtime)) {
761
+ if (shouldPostImmediately(this.runtime, this.accountId)) {
502
762
  this.createAutomatedPost();
503
763
  }
504
764
  this.scheduleNextPost();
505
765
  }
506
766
  scheduleNextPost() {
507
- const { min, max } = getPostIntervalRange(this.runtime);
767
+ const { min, max } = getPostIntervalRange(this.runtime, this.accountId);
508
768
  const interval = Math.random() * (max - min) + min;
509
769
  this.postTimer = setTimeout(() => {
510
770
  if (this.running) {
511
- this.createAutomatedPost();
512
- this.scheduleNextPost();
771
+ this.createAutomatedPost().finally(() => this.scheduleNextPost());
513
772
  }
514
773
  }, interval);
515
774
  }
516
- createAutomatedPost() {
517
- const payload = {
518
- runtime: this.runtime,
519
- source: "bluesky",
520
- automated: true
521
- };
522
- this.runtime.emitEvent("bluesky.create_post", payload);
775
+ async createAutomatedPost() {
776
+ await import_core2.withStandaloneTrajectory(this.runtime, {
777
+ source: "plugin-bluesky:auto-post",
778
+ metadata: {
779
+ platform: "bluesky",
780
+ kind: "public_post_generation",
781
+ automated: true,
782
+ accountId: this.accountId
783
+ }
784
+ }, async () => {
785
+ import_core2.setTrajectoryPurpose("background");
786
+ const payload = {
787
+ runtime: this.runtime,
788
+ source: "bluesky",
789
+ automated: true,
790
+ accountId: this.accountId
791
+ };
792
+ await this.runtime.emitEvent("bluesky.create_post", payload);
793
+ });
523
794
  }
524
795
  }
525
796
 
526
797
  // services/message.ts
527
798
  var import_core3 = require("@elizaos/core");
528
799
 
529
- // generated/prompts/typescript/prompts.ts
800
+ // prompts.ts
530
801
  var generateDmTemplate = `Generate a friendly direct message response under 200 characters.`;
531
802
  var generatePostTemplate = `Generate an engaging BlueSky post under {{maxLength}} characters.`;
532
803
  var truncatePostTemplate = `Shorten to under {{maxLength}} characters: "{{text}}"`;
533
804
 
534
805
  // services/message.ts
806
+ var BLUESKY_CONNECTOR_CONTEXTS = ["social", "connectors"];
807
+ function normalizeBlueSkyQuery(value) {
808
+ return value.trim().replace(/^@/, "").toLowerCase();
809
+ }
810
+ function scoreBlueSkyMatch(query, id, labels) {
811
+ if (!query)
812
+ return 0.45;
813
+ if (id.toLowerCase() === query)
814
+ return 1;
815
+ let bestScore = 0;
816
+ for (const label of labels) {
817
+ const normalized = label?.trim().replace(/^@/, "").toLowerCase();
818
+ if (!normalized)
819
+ continue;
820
+ if (normalized === query) {
821
+ bestScore = Math.max(bestScore, 0.95);
822
+ } else if (normalized.startsWith(query)) {
823
+ bestScore = Math.max(bestScore, 0.85);
824
+ } else if (normalized.includes(query)) {
825
+ bestScore = Math.max(bestScore, 0.7);
826
+ }
827
+ }
828
+ return bestScore;
829
+ }
830
+
535
831
  class BlueSkyMessageService {
536
832
  client;
537
833
  runtime;
834
+ accountId;
538
835
  static serviceType = "IMessageService";
539
- constructor(client, runtime) {
836
+ constructor(client, runtime, accountId = "default") {
540
837
  this.client = client;
541
838
  this.runtime = runtime;
839
+ this.accountId = accountId;
840
+ }
841
+ getAccountId() {
842
+ return normalizeBlueSkyAccountId(this.accountId);
542
843
  }
543
844
  async getMessages(convoId, limit = 50) {
544
845
  const response = await this.client.getMessages(convoId, limit);
@@ -552,6 +853,215 @@ class BlueSkyMessageService {
552
853
  const response = await this.client.getConversations(limit);
553
854
  return response.conversations;
554
855
  }
856
+ async handleSendMessage(runtime, target, content) {
857
+ const requestedAccountId = normalizeBlueSkyAccountId(target.accountId ?? readBlueSkyAccountId(content, target) ?? this.getAccountId());
858
+ if (requestedAccountId !== this.getAccountId()) {
859
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
860
+ }
861
+ const text = typeof content.text === "string" ? content.text.trim() : "";
862
+ if (!text) {
863
+ throw new Error("BlueSky DM connector requires non-empty text content.");
864
+ }
865
+ let convoId = target.channelId ?? target.threadId;
866
+ if (!convoId && target.roomId) {
867
+ const room = await runtime.getRoom(target.roomId);
868
+ convoId = room?.channelId;
869
+ }
870
+ if (!convoId) {
871
+ throw new Error("BlueSky DM connector requires a conversation target.");
872
+ }
873
+ await this.sendMessage(convoId, text);
874
+ }
875
+ async fetchConnectorMessages(context, params = {}) {
876
+ const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
877
+ if (requestedAccountId !== this.getAccountId()) {
878
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
879
+ }
880
+ const target = params.target ?? context.target;
881
+ let convoId = target?.channelId ?? target?.threadId;
882
+ if (!convoId && target?.roomId) {
883
+ const room = await context.runtime.getRoom(target.roomId);
884
+ convoId = room?.channelId;
885
+ }
886
+ if (convoId) {
887
+ const messages = await this.getMessages(convoId, clampLimit(params.limit, 25, 100));
888
+ return messages.map((message) => this.messageToMemory(context.runtime, message, convoId));
889
+ }
890
+ const conversations = await this.getConversations(clampLimit(params.limit, 25, 50));
891
+ const memories = [];
892
+ for (const conversation of conversations) {
893
+ const messages = await this.getMessages(conversation.id, 1);
894
+ memories.push(...messages.map((message) => this.messageToMemory(context.runtime, message, conversation.id)));
895
+ }
896
+ return memories.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
897
+ }
898
+ async resolveConnectorTargets(query, _context) {
899
+ const normalizedQuery = normalizeBlueSkyQuery(query);
900
+ const conversations = await this.getConversations(50);
901
+ return conversations.map((conversation) => {
902
+ const score = scoreBlueSkyMatch(normalizedQuery, conversation.id, [
903
+ ...conversation.members.flatMap((member) => [
904
+ member.handle,
905
+ member.displayName,
906
+ member.did
907
+ ])
908
+ ]);
909
+ return score > 0 ? this.buildConversationTarget(conversation, score) : null;
910
+ }).filter((target) => Boolean(target)).slice(0, 25);
911
+ }
912
+ async listConnectorRooms(_context) {
913
+ const conversations = await this.getConversations(50);
914
+ return conversations.map((conversation) => this.buildConversationTarget(conversation, 0.5));
915
+ }
916
+ async listRecentConnectorTargets(context) {
917
+ const targets = [];
918
+ const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
919
+ const convoId = context.target?.channelId ?? room?.channelId;
920
+ if (convoId) {
921
+ targets.push({
922
+ target: {
923
+ source: "bluesky",
924
+ accountId: this.getAccountId(),
925
+ channelId: convoId
926
+ },
927
+ label: `BlueSky conversation ${convoId}`,
928
+ kind: "thread",
929
+ score: 0.95,
930
+ contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
931
+ metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
932
+ });
933
+ }
934
+ targets.push(...await this.listConnectorRooms(context));
935
+ const seen = new Set;
936
+ return targets.filter((target) => {
937
+ const channelId = target.target.channelId;
938
+ if (!channelId || seen.has(channelId))
939
+ return false;
940
+ seen.add(channelId);
941
+ return true;
942
+ }).slice(0, 25);
943
+ }
944
+ async getConnectorChatContext(target, context) {
945
+ let convoId = target.channelId ?? target.threadId;
946
+ if (!convoId && target.roomId) {
947
+ const room = await context.runtime.getRoom(target.roomId);
948
+ convoId = room?.channelId;
949
+ }
950
+ if (!convoId)
951
+ return null;
952
+ const messages = await this.getMessages(convoId, 25);
953
+ return {
954
+ target: {
955
+ source: "bluesky",
956
+ accountId: this.getAccountId(),
957
+ channelId: convoId
958
+ },
959
+ label: `BlueSky conversation ${convoId}`,
960
+ recentMessages: messages.map((message) => ({
961
+ name: message.sender.did,
962
+ text: message.text ?? "",
963
+ timestamp: Date.parse(message.sentAt),
964
+ metadata: {
965
+ accountId: this.getAccountId(),
966
+ blueskyMessageId: message.id,
967
+ blueskySenderDid: message.sender.did
968
+ }
969
+ })),
970
+ metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
971
+ };
972
+ }
973
+ async getConnectorUserContext(entityId, _context) {
974
+ const normalizedEntity = entityId.trim().replace(/^@/, "");
975
+ if (!normalizedEntity)
976
+ return null;
977
+ const conversations = await this.getConversations(50);
978
+ for (const conversation of conversations) {
979
+ const member = conversation.members.find((candidate) => candidate.did === entityId || candidate.handle === normalizedEntity || candidate.displayName === entityId);
980
+ if (!member)
981
+ continue;
982
+ return {
983
+ entityId,
984
+ label: member.displayName || member.handle || member.did,
985
+ aliases: [member.handle, member.displayName, member.did].filter((value) => Boolean(value)),
986
+ handles: {
987
+ bluesky: member.handle ?? member.did
988
+ },
989
+ metadata: {
990
+ accountId: this.getAccountId(),
991
+ blueskyDid: member.did,
992
+ blueskyHandle: member.handle,
993
+ avatar: member.avatar
994
+ }
995
+ };
996
+ }
997
+ return null;
998
+ }
999
+ buildConversationTarget(conversation, score) {
1000
+ const sessionDid = this.client.getSession()?.did;
1001
+ const otherMembers = conversation.members.filter((member) => member.did !== sessionDid);
1002
+ const label = otherMembers.map((member) => member.displayName || member.handle || member.did).filter(Boolean).join(", ") || `BlueSky conversation ${conversation.id}`;
1003
+ return {
1004
+ target: {
1005
+ source: "bluesky",
1006
+ accountId: this.getAccountId(),
1007
+ channelId: conversation.id
1008
+ },
1009
+ label,
1010
+ kind: "thread",
1011
+ description: "BlueSky direct message conversation",
1012
+ score,
1013
+ contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
1014
+ metadata: {
1015
+ accountId: this.getAccountId(),
1016
+ blueskyConvoId: conversation.id,
1017
+ unreadCount: conversation.unreadCount,
1018
+ muted: conversation.muted,
1019
+ members: conversation.members.map((member) => ({
1020
+ did: member.did,
1021
+ handle: member.handle,
1022
+ displayName: member.displayName
1023
+ }))
1024
+ }
1025
+ };
1026
+ }
1027
+ messageToMemory(runtime, message, convoId) {
1028
+ const senderDid = message.sender.did || "unknown";
1029
+ const createdAt = Date.parse(message.sentAt) || Date.now();
1030
+ const entityId = senderDid === runtime.agentId ? runtime.agentId : import_core3.createUniqueUuid(runtime, `bluesky:user:${senderDid}`);
1031
+ const roomId = import_core3.createUniqueUuid(runtime, `bluesky:dm:${convoId}`);
1032
+ return {
1033
+ id: import_core3.createUniqueUuid(runtime, `bluesky:dm:${message.id}`),
1034
+ agentId: runtime.agentId,
1035
+ entityId,
1036
+ roomId,
1037
+ createdAt,
1038
+ content: {
1039
+ text: message.text ?? "",
1040
+ source: "bluesky",
1041
+ channelType: import_core3.ChannelType.DM
1042
+ },
1043
+ metadata: {
1044
+ type: "message",
1045
+ source: "bluesky",
1046
+ accountId: this.getAccountId(),
1047
+ provider: "bluesky",
1048
+ timestamp: createdAt,
1049
+ fromBot: entityId === runtime.agentId,
1050
+ messageIdFull: message.id,
1051
+ chatType: import_core3.ChannelType.DM,
1052
+ sender: {
1053
+ id: senderDid
1054
+ },
1055
+ bluesky: {
1056
+ accountId: this.getAccountId(),
1057
+ messageId: message.id,
1058
+ convoId,
1059
+ rev: message.rev,
1060
+ senderDid
1061
+ }
1062
+ }
1063
+ };
1064
+ }
555
1065
  async generateReply() {
556
1066
  const prompt = import_core3.composePrompt({
557
1067
  state: {},
@@ -564,16 +1074,41 @@ class BlueSkyMessageService {
564
1074
  return response;
565
1075
  }
566
1076
  }
1077
+ function clampLimit(value, fallback, max) {
1078
+ if (!Number.isFinite(value))
1079
+ return fallback;
1080
+ return Math.min(Math.max(1, Math.floor(value)), max);
1081
+ }
567
1082
 
568
1083
  // services/post.ts
569
1084
  var import_core4 = require("@elizaos/core");
1085
+ function clampLimit2(value, fallback, max) {
1086
+ if (!Number.isFinite(value))
1087
+ return fallback;
1088
+ return Math.min(Math.max(1, Math.floor(value)), max);
1089
+ }
1090
+ function readContentString(content, keys) {
1091
+ const record = content;
1092
+ for (const key of keys) {
1093
+ const value = record[key];
1094
+ if (typeof value === "string" && value.trim())
1095
+ return value.trim();
1096
+ }
1097
+ return;
1098
+ }
1099
+
570
1100
  class BlueSkyPostService {
571
1101
  client;
572
1102
  runtime;
1103
+ accountId;
573
1104
  static serviceType = "IPostService";
574
- constructor(client, runtime) {
1105
+ constructor(client, runtime, accountId = "default") {
575
1106
  this.client = client;
576
1107
  this.runtime = runtime;
1108
+ this.accountId = accountId;
1109
+ }
1110
+ getAccountId() {
1111
+ return normalizeBlueSkyAccountId(this.accountId);
577
1112
  }
578
1113
  async getPosts(limit = 50, cursor) {
579
1114
  const response = await this.client.getTimeline({ limit, cursor });
@@ -590,9 +1125,91 @@ class BlueSkyPostService {
590
1125
  };
591
1126
  return this.client.sendPost(request);
592
1127
  }
1128
+ async handleSendPost(runtime, content) {
1129
+ const requestedAccountId = normalizeBlueSkyAccountId(readBlueSkyAccountId(content) ?? this.getAccountId());
1130
+ if (requestedAccountId !== this.getAccountId()) {
1131
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
1132
+ }
1133
+ const replyUri = readContentString(content, ["replyToUri", "replyTo"]);
1134
+ const replyCid = readContentString(content, ["replyToCid"]);
1135
+ const post = await this.createPost(content.text ?? "", replyUri && replyCid ? { uri: replyUri, cid: replyCid } : undefined);
1136
+ return this.postToMemory(runtime, post);
1137
+ }
1138
+ async fetchFeed(context, params = {}) {
1139
+ const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
1140
+ if (requestedAccountId !== this.getAccountId()) {
1141
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
1142
+ }
1143
+ const response = await this.client.getTimeline({
1144
+ limit: clampLimit2(params.limit, 25, 100),
1145
+ cursor: params.cursor
1146
+ });
1147
+ return response.feed.map((item) => this.postToMemory(context.runtime, item.post));
1148
+ }
1149
+ async searchPosts(context, params) {
1150
+ const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
1151
+ if (requestedAccountId !== this.getAccountId()) {
1152
+ throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
1153
+ }
1154
+ const query = params.query?.trim();
1155
+ if (!query) {
1156
+ throw new Error("BlueSky searchPosts connector requires a query.");
1157
+ }
1158
+ const response = await this.client.searchPosts({
1159
+ query,
1160
+ limit: clampLimit2(params.limit, 25, 100),
1161
+ cursor: params.cursor
1162
+ });
1163
+ return response.posts.map((post) => this.postToMemory(context.runtime, post));
1164
+ }
593
1165
  async deletePost(uri) {
594
1166
  await this.client.deletePost(uri);
595
1167
  }
1168
+ postToMemory(runtime, post) {
1169
+ const createdAt = Date.parse(post.indexedAt || post.record.createdAt) || Date.now();
1170
+ const authorId = post.author.did || post.author.handle || "unknown";
1171
+ const entityId = authorId === runtime.agentId ? runtime.agentId : import_core4.createUniqueUuid(runtime, `bluesky:user:${authorId}`);
1172
+ const roomId = import_core4.createUniqueUuid(runtime, `bluesky:feed:${authorId}`);
1173
+ return {
1174
+ id: import_core4.createUniqueUuid(runtime, `bluesky:post:${post.uri}`),
1175
+ agentId: runtime.agentId,
1176
+ entityId,
1177
+ roomId,
1178
+ createdAt,
1179
+ content: {
1180
+ text: post.record.text,
1181
+ source: "bluesky",
1182
+ url: `https://bsky.app/profile/${post.author.handle}/post/${post.uri.split("/").pop()}`,
1183
+ channelType: import_core4.ChannelType.FEED
1184
+ },
1185
+ metadata: {
1186
+ type: "message",
1187
+ source: "bluesky",
1188
+ accountId: this.getAccountId(),
1189
+ provider: "bluesky",
1190
+ timestamp: createdAt,
1191
+ fromBot: entityId === runtime.agentId,
1192
+ messageIdFull: post.uri,
1193
+ chatType: import_core4.ChannelType.FEED,
1194
+ sender: {
1195
+ id: authorId,
1196
+ name: post.author.displayName,
1197
+ username: post.author.handle
1198
+ },
1199
+ bluesky: {
1200
+ accountId: this.getAccountId(),
1201
+ uri: post.uri,
1202
+ cid: post.cid,
1203
+ authorDid: post.author.did,
1204
+ authorHandle: post.author.handle,
1205
+ replyCount: post.replyCount,
1206
+ repostCount: post.repostCount,
1207
+ likeCount: post.likeCount,
1208
+ quoteCount: post.quoteCount
1209
+ }
1210
+ }
1211
+ };
1212
+ }
596
1213
  async generateContent() {
597
1214
  const prompt = import_core4.composePrompt({
598
1215
  state: {
@@ -626,9 +1243,7 @@ class BlueSkyPostService {
626
1243
  // services/bluesky.ts
627
1244
  class BlueSkyService extends import_core5.Service {
628
1245
  static instance;
629
- managers = new Map;
630
- messageServices = new Map;
631
- postServices = new Map;
1246
+ agents = new Map;
632
1247
  static serviceType = BLUESKY_SERVICE_NAME;
633
1248
  capabilityDescription = "Send and receive messages on BlueSky";
634
1249
  static getInstance() {
@@ -637,48 +1252,230 @@ class BlueSkyService extends import_core5.Service {
637
1252
  }
638
1253
  static async start(runtime) {
639
1254
  const service = BlueSkyService.getInstance();
640
- if (service.managers.has(runtime.agentId)) {
1255
+ if (service.agents.has(runtime.agentId)) {
641
1256
  return service;
642
1257
  }
643
1258
  if (!hasBlueSkyEnabled(runtime)) {
644
1259
  return service;
645
1260
  }
646
- const config = validateBlueSkyConfig(runtime);
647
- const client = new BlueSkyClient({
648
- service: config.service,
649
- handle: config.handle,
650
- password: config.password,
651
- dryRun: config.dryRun
652
- });
653
- const manager = new BlueSkyAgentManager(runtime, config, client);
654
- service.managers.set(runtime.agentId, manager);
655
- service.messageServices.set(runtime.agentId, new BlueSkyMessageService(client, runtime));
656
- service.postServices.set(runtime.agentId, new BlueSkyPostService(client, runtime));
657
- await manager.start();
658
- import_core5.logger.success({ agentId: runtime.agentId }, "BlueSky client started");
1261
+ const accountIds = listBlueSkyAccountIds(runtime);
1262
+ const defaultAccountId = normalizeBlueSkyAccountId(resolveDefaultBlueSkyAccountId(runtime));
1263
+ const accounts = {
1264
+ defaultAccountId,
1265
+ managers: new Map,
1266
+ messageServices: new Map,
1267
+ postServices: new Map
1268
+ };
1269
+ service.agents.set(runtime.agentId, accounts);
1270
+ for (const accountId of accountIds) {
1271
+ if (!hasBlueSkyEnabled(runtime, accountId)) {
1272
+ continue;
1273
+ }
1274
+ const config = validateBlueSkyConfig(runtime, accountId);
1275
+ if (!config.handle || !config.password) {
1276
+ import_core5.logger.warn({ agentId: runtime.agentId, accountId }, "Skipping BlueSky account without handle/password");
1277
+ continue;
1278
+ }
1279
+ const client = new BlueSkyClient({
1280
+ service: config.service,
1281
+ handle: config.handle,
1282
+ password: config.password,
1283
+ dryRun: config.dryRun
1284
+ });
1285
+ const manager = new BlueSkyAgentManager(runtime, config, client);
1286
+ accounts.managers.set(accountId, manager);
1287
+ accounts.messageServices.set(accountId, new BlueSkyMessageService(client, runtime, accountId));
1288
+ accounts.postServices.set(accountId, new BlueSkyPostService(client, runtime, accountId));
1289
+ await manager.start();
1290
+ import_core5.logger.success({ agentId: runtime.agentId, accountId }, "BlueSky client started");
1291
+ }
659
1292
  return service;
660
1293
  }
661
1294
  static async stop(runtime) {
662
1295
  const service = BlueSkyService.getInstance();
663
- const manager = service.managers.get(runtime.agentId);
664
- if (!manager)
1296
+ const accounts = service.agents.get(runtime.agentId);
1297
+ if (!accounts)
665
1298
  return;
666
- await manager.stop();
667
- service.managers.delete(runtime.agentId);
668
- service.messageServices.delete(runtime.agentId);
669
- service.postServices.delete(runtime.agentId);
1299
+ for (const manager of accounts.managers.values()) {
1300
+ await manager.stop();
1301
+ }
1302
+ service.agents.delete(runtime.agentId);
670
1303
  import_core5.logger.info({ agentId: runtime.agentId }, "BlueSky client stopped");
671
1304
  }
1305
+ static registerSendHandlers(runtime, serviceInstance) {
1306
+ const accounts = serviceInstance?.agents.get(runtime.agentId);
1307
+ if (!accounts) {
1308
+ runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register BlueSky connectors; service is not initialized");
1309
+ return;
1310
+ }
1311
+ for (const postService of accounts.postServices.values()) {
1312
+ BlueSkyService.registerPostConnector(runtime, postService);
1313
+ }
1314
+ if (typeof runtime.registerMessageConnector === "function" && accounts.messageServices.size > 0) {
1315
+ for (const messageService2 of accounts.messageServices.values()) {
1316
+ BlueSkyService.registerMessageConnector(runtime, messageService2);
1317
+ }
1318
+ return;
1319
+ }
1320
+ const defaultAccountId = normalizeBlueSkyAccountId(accounts.defaultAccountId);
1321
+ const messageService = accounts.messageServices.get(defaultAccountId);
1322
+ if (!messageService) {
1323
+ runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register legacy BlueSky DM send handler; default message service is not initialized");
1324
+ return;
1325
+ }
1326
+ runtime.registerSendHandler("bluesky", messageService.handleSendMessage.bind(messageService));
1327
+ }
1328
+ static registerMessageConnector(runtime, messageService) {
1329
+ const accountId = messageService.getAccountId();
1330
+ const registration = {
1331
+ source: "bluesky",
1332
+ accountId,
1333
+ label: "BlueSky",
1334
+ description: "BlueSky DM connector for sending private messages to conversations.",
1335
+ capabilities: [
1336
+ "send_message",
1337
+ "fetch_messages",
1338
+ "resolve_targets",
1339
+ "list_rooms",
1340
+ "chat_context",
1341
+ "user_context"
1342
+ ],
1343
+ supportedTargetKinds: ["thread", "user"],
1344
+ contexts: ["social", "connectors"],
1345
+ metadata: {
1346
+ accountId,
1347
+ service: BLUESKY_SERVICE_NAME
1348
+ },
1349
+ resolveTargets: messageService.resolveConnectorTargets.bind(messageService),
1350
+ listRecentTargets: messageService.listRecentConnectorTargets.bind(messageService),
1351
+ listRooms: messageService.listConnectorRooms.bind(messageService),
1352
+ getChatContext: messageService.getConnectorChatContext.bind(messageService),
1353
+ getUserContext: messageService.getConnectorUserContext.bind(messageService),
1354
+ fetchMessages: messageService.fetchConnectorMessages.bind(messageService),
1355
+ contentShaping: {
1356
+ systemPromptFragment: "For BlueSky DMs, keep messages direct and conversational. Avoid public-feed conventions like hashtags unless the user asked.",
1357
+ constraints: {
1358
+ supportsMarkdown: false,
1359
+ channelType: import_core5.ChannelType.DM
1360
+ }
1361
+ },
1362
+ sendHandler: messageService.handleSendMessage.bind(messageService)
1363
+ };
1364
+ runtime.registerMessageConnector(registration);
1365
+ runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky DM connector");
1366
+ }
1367
+ static registerPostConnector(runtime, postService) {
1368
+ const withPostConnector = runtime;
1369
+ if (typeof withPostConnector.registerPostConnector !== "function") {
1370
+ return;
1371
+ }
1372
+ const accountId = postService.getAccountId?.() ?? DEFAULT_BLUESKY_ACCOUNT_ID;
1373
+ withPostConnector.registerPostConnector({
1374
+ source: "bluesky",
1375
+ accountId,
1376
+ label: "BlueSky",
1377
+ description: "BlueSky public feed connector for publishing posts, reading the timeline, and searching posts.",
1378
+ capabilities: ["post", "fetch_feed", "search_posts"],
1379
+ contexts: ["social", "social_posting", "connectors"],
1380
+ metadata: {
1381
+ accountId,
1382
+ service: BLUESKY_SERVICE_NAME
1383
+ },
1384
+ postHandler: postService.handleSendPost.bind(postService),
1385
+ fetchFeed: postService.fetchFeed.bind(postService),
1386
+ searchPosts: postService.searchPosts.bind(postService),
1387
+ contentShaping: {
1388
+ 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.",
1389
+ constraints: {
1390
+ maxLength: 300,
1391
+ supportsMarkdown: false,
1392
+ channelType: import_core5.ChannelType.FEED
1393
+ }
1394
+ }
1395
+ });
1396
+ runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky post connector");
1397
+ }
672
1398
  async stop() {
673
- for (const manager of this.managers.values()) {
674
- await BlueSkyService.stop(manager.runtime);
1399
+ for (const [agentId, accounts] of this.agents) {
1400
+ for (const manager of accounts.managers.values()) {
1401
+ await manager.stop();
1402
+ }
1403
+ this.agents.delete(agentId);
675
1404
  }
676
1405
  }
677
- getMessageService(agentId) {
678
- return this.messageServices.get(agentId);
1406
+ getMessageServiceForAccount(accountId, agentId) {
1407
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1408
+ if (!resolvedAgentId)
1409
+ return;
1410
+ const accounts = this.agents.get(resolvedAgentId);
1411
+ if (!accounts)
1412
+ return;
1413
+ const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
1414
+ return accounts.messageServices.get(id);
1415
+ }
1416
+ getPostServiceForAccount(accountId, agentId) {
1417
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1418
+ if (!resolvedAgentId)
1419
+ return;
1420
+ const accounts = this.agents.get(resolvedAgentId);
1421
+ if (!accounts)
1422
+ return;
1423
+ const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
1424
+ return accounts.postServices.get(id);
1425
+ }
1426
+ getMessageService(agentId, accountId) {
1427
+ return this.getMessageServiceForAccount(accountId, agentId);
1428
+ }
1429
+ getPostService(agentId, accountId) {
1430
+ return this.getPostServiceForAccount(accountId, agentId);
1431
+ }
1432
+ getDefaultAccountId(agentId) {
1433
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1434
+ return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
679
1435
  }
680
- getPostService(agentId) {
681
- return this.postServices.get(agentId);
1436
+ listAccountIds(agentId) {
1437
+ const resolvedAgentId = agentId ?? this.firstAgentId();
1438
+ const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
1439
+ return accounts ? Array.from(accounts.managers.keys()) : [];
1440
+ }
1441
+ firstAgentId() {
1442
+ return this.agents.keys().next().value;
1443
+ }
1444
+ }
1445
+
1446
+ // workflow-credential-provider.ts
1447
+ var import_core6 = require("@elizaos/core");
1448
+ var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
1449
+ var SUPPORTED = ["httpHeaderAuth"];
1450
+
1451
+ class BlueskyWorkflowCredentialProvider extends import_core6.Service {
1452
+ static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
1453
+ capabilityDescription = "Supplies Bluesky credentials to the workflow plugin.";
1454
+ static async start(runtime) {
1455
+ return new BlueskyWorkflowCredentialProvider(runtime);
1456
+ }
1457
+ async stop() {}
1458
+ async resolve(_userId, credType) {
1459
+ if (credType !== "httpHeaderAuth")
1460
+ return null;
1461
+ const handle = this.runtime.getSetting("BLUESKY_HANDLE");
1462
+ const password = this.runtime.getSetting("BLUESKY_PASSWORD");
1463
+ if (!handle?.trim() || !password?.trim())
1464
+ return null;
1465
+ return {
1466
+ status: "credential_data",
1467
+ data: {
1468
+ name: "X-Bluesky-Handle",
1469
+ value: handle.trim(),
1470
+ appPassword: password.trim()
1471
+ }
1472
+ };
1473
+ }
1474
+ checkCredentialTypes(credTypes) {
1475
+ return {
1476
+ supported: credTypes.filter((t) => SUPPORTED.includes(t)),
1477
+ unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
1478
+ };
682
1479
  }
683
1480
  }
684
1481
 
@@ -695,7 +1492,7 @@ var pluginTests = [
695
1492
  if (!handle || !password) {
696
1493
  throw new Error("BLUESKY_HANDLE and BLUESKY_PASSWORD are not configured");
697
1494
  }
698
- import_core6.logger.log("BlueSky credentials are configured");
1495
+ import_core7.logger.log("BlueSky credentials are configured");
699
1496
  }
700
1497
  },
701
1498
  {
@@ -704,14 +1501,14 @@ var pluginTests = [
704
1501
  const handle = getApiKeyOptional(runtime, "BLUESKY_HANDLE");
705
1502
  const password = getApiKeyOptional(runtime, "BLUESKY_PASSWORD");
706
1503
  if (!handle || !password) {
707
- import_core6.logger.log("Skipping service initialization test - credentials not configured");
1504
+ import_core7.logger.log("Skipping service initialization test - credentials not configured");
708
1505
  return;
709
1506
  }
710
1507
  const service = await BlueSkyService.start(runtime);
711
1508
  if (!service) {
712
1509
  throw new Error("Failed to initialize BlueSky service");
713
1510
  }
714
- import_core6.logger.log("BlueSky service initialized successfully");
1511
+ import_core7.logger.log("BlueSky service initialized successfully");
715
1512
  }
716
1513
  }
717
1514
  ]
@@ -734,14 +1531,25 @@ var blueSkyPlugin = {
734
1531
  BLUESKY_ACTION_INTERVAL: process.env.BLUESKY_ACTION_INTERVAL ?? null,
735
1532
  BLUESKY_POST_IMMEDIATELY: process.env.BLUESKY_POST_IMMEDIATELY ?? null,
736
1533
  BLUESKY_MAX_ACTIONS_PROCESSING: process.env.BLUESKY_MAX_ACTIONS_PROCESSING ?? null,
737
- BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null
1534
+ BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null,
1535
+ BLUESKY_ACCOUNTS: process.env.BLUESKY_ACCOUNTS ?? null,
1536
+ BLUESKY_DEFAULT_ACCOUNT_ID: process.env.BLUESKY_DEFAULT_ACCOUNT_ID ?? null
738
1537
  },
739
- async init(_config, _runtime) {
740
- import_core6.logger.log("BlueSky plugin initialized");
1538
+ async init(_config, runtime) {
1539
+ try {
1540
+ const manager = import_core7.getConnectorAccountManager(runtime);
1541
+ manager.registerProvider(createBlueSkyConnectorAccountProvider(runtime));
1542
+ } catch (err) {
1543
+ import_core7.logger.warn({
1544
+ src: "plugin:bluesky",
1545
+ err: err instanceof Error ? err.message : String(err)
1546
+ }, "Failed to register BlueSky provider with ConnectorAccountManager");
1547
+ }
1548
+ import_core7.logger.log("BlueSky plugin initialized");
741
1549
  },
742
- services: [BlueSkyService],
1550
+ actions: [],
1551
+ services: [BlueSkyService, BlueskyWorkflowCredentialProvider],
743
1552
  tests: pluginTests
744
1553
  };
745
- var typescript_default = blueSkyPlugin;
746
1554
 
747
- //# debugId=68580B9EB5B89C7664756E2164756E21
1555
+ //# debugId=3E81B0DAACB595C464756E2164756E21