@shadowob/openclaw-shadowob 1.1.0 → 1.1.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,4 +1,8 @@
1
1
  // src/monitor.ts
2
+ import nodeCrypto from "crypto";
3
+ import fsPromises from "fs/promises";
4
+ import nodeOs from "os";
5
+ import nodePath from "path";
2
6
  import { ShadowClient, ShadowSocket } from "@shadowob/sdk";
3
7
 
4
8
  // src/runtime.ts
@@ -12,11 +16,18 @@ var tryGetShadowRuntime = store.tryGetRuntime;
12
16
 
13
17
  // src/monitor.ts
14
18
  async function getDataDir() {
15
- const nodeOs = await import("os");
16
- const nodePath = await import("path");
17
19
  const dataDir = process.env.OPENCLAW_DATA_DIR;
18
20
  return dataDir || nodePath.join(nodeOs.homedir(), ".openclaw");
19
21
  }
22
+ function resolveSessionStore(cfg) {
23
+ const raw = cfg.session?.store;
24
+ if (typeof raw === "string") return raw;
25
+ if (raw && typeof raw === "object") {
26
+ const pathValue = raw.path;
27
+ if (typeof pathValue === "string") return pathValue;
28
+ }
29
+ return void 0;
30
+ }
20
31
  function createTypingCallbacks(params) {
21
32
  const {
22
33
  start,
@@ -263,9 +274,6 @@ async function processShadowMessage(params) {
263
274
  return map[ext] ?? headerType ?? "application/octet-stream";
264
275
  };
265
276
  if (allRawUrls.length > 0) {
266
- const fsPromises = await import("fs/promises");
267
- const nodePath = await import("path");
268
- const nodeCrypto = await import("crypto");
269
277
  const dataDir = await getDataDir();
270
278
  const mediaDir = nodePath.join(dataDir, "media", "inbound");
271
279
  await fsPromises.mkdir(mediaDir, { recursive: true });
@@ -308,6 +316,7 @@ async function processShadowMessage(params) {
308
316
  const escapedBotUsername = botUsername.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
309
317
  const mentionRegex = new RegExp(`@${escapedBotUsername}(?:\\s|$)`, "i");
310
318
  const wasMentioned = mentionRegex.test(message.content);
319
+ const triggerChain = message.metadata?.agentChain;
311
320
  const ctxPayload = core.channel.reply.finalizeInboundContext({
312
321
  Body: body,
313
322
  BodyForAgent: cleanBody,
@@ -342,10 +351,9 @@ async function processShadowMessage(params) {
342
351
  ...message.replyToId ? { ReplyToId: message.replyToId } : {},
343
352
  ...mediaCtx
344
353
  });
345
- const storePath = core.channel.session.resolveStorePath(
346
- cfg.session,
347
- { agentId: route.agentId }
348
- );
354
+ const storePath = core.channel.session.resolveStorePath(resolveSessionStore(cfg), {
355
+ agentId: route.agentId
356
+ });
349
357
  await core.channel.session.recordInboundSession({
350
358
  storePath,
351
359
  sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
@@ -360,7 +368,6 @@ async function processShadowMessage(params) {
360
368
  }
361
369
  runtime.log?.(`[msg] Dispatching to AI pipeline for message ${message.id}`);
362
370
  const client = new ShadowClient(account.serverUrl, account.token);
363
- const triggerChain = message.metadata?.agentChain;
364
371
  const typingCbs = createTypingCallbacks({
365
372
  start: async () => {
366
373
  socket.sendTyping(channelId);
@@ -554,7 +561,7 @@ ${attachmentLines.join("\n")}` : attachmentLines.join("\n");
554
561
  cfg,
555
562
  channel: "shadowob",
556
563
  accountId,
557
- peer: { kind: "private", id: peerId }
564
+ peer: { kind: "direct", id: peerId }
558
565
  });
559
566
  runtime.log?.(`[routing] DM resolved agent: ${route.agentId} (account ${accountId})`);
560
567
  const body = core.channel.reply.formatAgentEnvelope({
@@ -589,10 +596,9 @@ ${attachmentLines.join("\n")}` : attachmentLines.join("\n");
589
596
  AgentId: route.agentId,
590
597
  ChannelId: dmChannelId
591
598
  });
592
- const storePath = core.channel.session.resolveStorePath(
593
- cfg.session,
594
- { agentId: route.agentId }
595
- );
599
+ const storePath = core.channel.session.resolveStorePath(resolveSessionStore(cfg), {
600
+ agentId: route.agentId
601
+ });
596
602
  await core.channel.session.recordInboundSession({
597
603
  storePath,
598
604
  sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
@@ -689,14 +695,11 @@ async function deliverShadowDmReply(params) {
689
695
  }
690
696
  }
691
697
  async function getSessionCachePath(accountId) {
692
- const nodePath = await import("path");
693
698
  const dataDir = await getDataDir();
694
699
  return nodePath.join(dataDir, "shadow", `session-cache-${accountId}.json`);
695
700
  }
696
701
  async function saveSessionCache(accountId, data) {
697
702
  try {
698
- const fsPromises = await import("fs/promises");
699
- const nodePath = await import("path");
700
703
  const cachePath = await getSessionCachePath(accountId);
701
704
  await fsPromises.mkdir(nodePath.dirname(cachePath), { recursive: true });
702
705
  await fsPromises.writeFile(cachePath, JSON.stringify(data), "utf-8");
@@ -705,7 +708,6 @@ async function saveSessionCache(accountId, data) {
705
708
  }
706
709
  async function loadSessionCache(accountId) {
707
710
  try {
708
- const fsPromises = await import("fs/promises");
709
711
  const cachePath = await getSessionCachePath(accountId);
710
712
  const raw = await fsPromises.readFile(cachePath, "utf-8");
711
713
  return JSON.parse(raw);
@@ -1,9 +1,6 @@
1
1
  // src/channel.ts
2
2
  import { ShadowClient as ShadowClient2 } from "@shadowob/sdk";
3
- import {
4
- createChannelPluginBase,
5
- createChatChannelPlugin
6
- } from "openclaw/plugin-sdk/core";
3
+ import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
7
4
 
8
5
  // src/config.ts
9
6
  var DEFAULT_ACCOUNT_ID = "default";
@@ -148,10 +145,28 @@ function inspectAccount(cfg, accountId) {
148
145
  };
149
146
  }
150
147
  var shadowPlugin = createChatChannelPlugin({
151
- base: createChannelPluginBase({
148
+ base: {
152
149
  id: "shadowob",
150
+ meta: {
151
+ id: "shadowob",
152
+ label: "ShadowOwnBuddy",
153
+ selectionLabel: "ShadowOwnBuddy (Server)",
154
+ docsPath: "/channels/shadowob",
155
+ blurb: "Shadow server channel integration \u2014 chat with AI agents in Shadow channels",
156
+ aliases: ["shadow-server", "openclaw-shadowob"]
157
+ },
158
+ capabilities: {
159
+ chatTypes: ["channel", "thread"],
160
+ reactions: true,
161
+ threads: true,
162
+ media: true,
163
+ reply: true,
164
+ edit: true,
165
+ unsend: true
166
+ },
153
167
  config: {
154
168
  listAccountIds: (cfg) => listAccountIds(cfg),
169
+ inspectAccount,
155
170
  resolveAccount: (cfg, accountId) => {
156
171
  return resolveAccount(cfg, accountId);
157
172
  },
@@ -169,10 +184,10 @@ var shadowPlugin = createChatChannelPlugin({
169
184
  })
170
185
  },
171
186
  setup: {
172
- resolveAccount,
173
- inspectAccount
187
+ resolveAccountId: ({ accountId }) => accountId ?? DEFAULT_ACCOUNT_ID,
188
+ applyAccountConfig: ({ cfg }) => cfg
174
189
  }
175
- }),
190
+ },
176
191
  // DM security: define allowlist-based DM policy
177
192
  security: {
178
193
  dm: {
@@ -190,7 +205,7 @@ var shadowPlugin = createChatChannelPlugin({
190
205
  resolveReplyToMode: ({ cfg }) => {
191
206
  const shadow = cfg.channels?.shadowob ?? cfg.channels?.["openclaw-shadowob"];
192
207
  const mode = shadow?.replyToMode;
193
- if (mode === "first" || mode === "all" || mode === "off" || mode === "reply") return mode;
208
+ if (mode === "first" || mode === "all" || mode === "off") return mode;
194
209
  return "first";
195
210
  }
196
211
  },
@@ -347,7 +362,7 @@ shadowPlugin.gateway = {
347
362
  lastError: null
348
363
  });
349
364
  ctx.log?.info(`Starting Shadow connection for account ${accountId}`);
350
- const { monitorShadowProvider } = await import("./monitor-G2J667KY.js");
365
+ const { monitorShadowProvider } = await import("./monitor-L5CUPMSN.js");
351
366
  await monitorShadowProvider({
352
367
  account,
353
368
  accountId,
@@ -384,21 +399,24 @@ var SHADOW_ACTIONS = [
384
399
  "get-connection-status"
385
400
  ];
386
401
  shadowPlugin.actions = {
387
- listActions: () => [...SHADOW_ACTIONS],
402
+ describeMessageTool: () => null,
388
403
  supportsAction: ({ action }) => SHADOW_ACTIONS.includes(action),
389
404
  handleAction: async (ctx) => {
405
+ const textResult = (value) => ({
406
+ content: [
407
+ {
408
+ type: "text",
409
+ text: JSON.stringify(value)
410
+ }
411
+ ],
412
+ details: value
413
+ });
390
414
  const account = getAccountConfig(ctx.cfg, ctx.accountId ?? DEFAULT_ACCOUNT_ID);
391
415
  if (!account) {
392
- return {
393
- content: [
394
- {
395
- type: "text",
396
- text: JSON.stringify({ ok: false, error: "Shadow account not configured" })
397
- }
398
- ]
399
- };
416
+ return textResult({ ok: false, error: "Shadow account not configured" });
400
417
  }
401
- const { action, params } = ctx;
418
+ const action = String(ctx.action);
419
+ const { params } = ctx;
402
420
  if (action === "sendAttachment") {
403
421
  try {
404
422
  const client = new ShadowClient2(account.serverUrl, account.token);
@@ -419,17 +437,10 @@ shadowPlugin.actions = {
419
437
  replyToId: params.replyTo
420
438
  });
421
439
  } else {
422
- return {
423
- content: [
424
- {
425
- type: "text",
426
- text: JSON.stringify({
427
- ok: false,
428
- error: "Could not resolve target channel or thread"
429
- })
430
- }
431
- ]
432
- };
440
+ return textResult({
441
+ ok: false,
442
+ error: "Could not resolve target channel or thread"
443
+ });
433
444
  }
434
445
  if (base64Buffer) {
435
446
  const raw = base64Buffer.includes(",") ? base64Buffer.split(",")[1] ?? "" : base64Buffer;
@@ -440,43 +451,19 @@ shadowPlugin.actions = {
440
451
  } else if (mediaUrl) {
441
452
  await client.uploadMediaFromUrl(mediaUrl, message.id);
442
453
  } else {
443
- return {
444
- content: [
445
- {
446
- type: "text",
447
- text: JSON.stringify({
448
- ok: false,
449
- error: "No buffer or media URL provided for attachment"
450
- })
451
- }
452
- ]
453
- };
454
+ return textResult({
455
+ ok: false,
456
+ error: "No buffer or media URL provided for attachment"
457
+ });
454
458
  }
455
- return {
456
- content: [
457
- {
458
- type: "text",
459
- text: JSON.stringify({
460
- ok: true,
461
- action: "sendAttachment",
462
- messageId: message.id,
463
- filename
464
- })
465
- }
466
- ]
467
- };
459
+ return textResult({
460
+ ok: true,
461
+ action: "sendAttachment",
462
+ messageId: message.id,
463
+ filename
464
+ });
468
465
  } catch (err) {
469
- return {
470
- content: [
471
- {
472
- type: "text",
473
- text: JSON.stringify({
474
- ok: false,
475
- error: err instanceof Error ? err.message : String(err)
476
- })
477
- }
478
- ]
479
- };
466
+ return textResult({ ok: false, error: err instanceof Error ? err.message : String(err) });
480
467
  }
481
468
  }
482
469
  if (action === "react") {
@@ -484,31 +471,13 @@ shadowPlugin.actions = {
484
471
  const messageId = params.messageId ?? params.message_id ?? "";
485
472
  const emoji = params.emoji ?? params.reaction ?? "";
486
473
  if (!messageId || !emoji) {
487
- return {
488
- content: [
489
- {
490
- type: "text",
491
- text: JSON.stringify({ ok: false, error: "messageId and emoji are required" })
492
- }
493
- ]
494
- };
474
+ return textResult({ ok: false, error: "messageId and emoji are required" });
495
475
  }
496
476
  try {
497
477
  await client.addReaction(messageId, emoji);
498
- return {
499
- content: [
500
- {
501
- type: "text",
502
- text: JSON.stringify({ ok: true, action: "react", messageId, emoji })
503
- }
504
- ]
505
- };
478
+ return textResult({ ok: true, action: "react", messageId, emoji });
506
479
  } catch (err) {
507
- return {
508
- content: [
509
- { type: "text", text: JSON.stringify({ ok: false, error: String(err) }) }
510
- ]
511
- };
480
+ return textResult({ ok: false, error: String(err) });
512
481
  }
513
482
  }
514
483
  if (action === "edit") {
@@ -516,144 +485,62 @@ shadowPlugin.actions = {
516
485
  const messageId = params.messageId ?? params.message_id ?? "";
517
486
  const content = params.message ?? params.content ?? "";
518
487
  if (!messageId || !content) {
519
- return {
520
- content: [
521
- {
522
- type: "text",
523
- text: JSON.stringify({ ok: false, error: "messageId and content are required" })
524
- }
525
- ]
526
- };
488
+ return textResult({ ok: false, error: "messageId and content are required" });
527
489
  }
528
490
  try {
529
491
  await client.editMessage(messageId, content);
530
- return {
531
- content: [
532
- {
533
- type: "text",
534
- text: JSON.stringify({ ok: true, action: "edit", messageId })
535
- }
536
- ]
537
- };
492
+ return textResult({ ok: true, action: "edit", messageId });
538
493
  } catch (err) {
539
- return {
540
- content: [
541
- { type: "text", text: JSON.stringify({ ok: false, error: String(err) }) }
542
- ]
543
- };
494
+ return textResult({ ok: false, error: String(err) });
544
495
  }
545
496
  }
546
497
  if (action === "delete") {
547
498
  const client = new ShadowClient2(account.serverUrl, account.token);
548
499
  const messageId = params.messageId ?? params.message_id ?? "";
549
500
  if (!messageId) {
550
- return {
551
- content: [
552
- {
553
- type: "text",
554
- text: JSON.stringify({ ok: false, error: "messageId is required" })
555
- }
556
- ]
557
- };
501
+ return textResult({ ok: false, error: "messageId is required" });
558
502
  }
559
503
  try {
560
504
  await client.deleteMessage(messageId);
561
- return {
562
- content: [
563
- {
564
- type: "text",
565
- text: JSON.stringify({ ok: true, action: "delete", messageId })
566
- }
567
- ]
568
- };
505
+ return textResult({ ok: true, action: "delete", messageId });
569
506
  } catch (err) {
570
- return {
571
- content: [
572
- { type: "text", text: JSON.stringify({ ok: false, error: String(err) }) }
573
- ]
574
- };
507
+ return textResult({ ok: false, error: String(err) });
575
508
  }
576
509
  }
577
510
  if (action === "pin" || action === "unpin") {
578
- return {
579
- content: [
580
- {
581
- type: "text",
582
- text: JSON.stringify({
583
- ok: false,
584
- error: `${action} is not yet supported for Shadow channels`
585
- })
586
- }
587
- ]
588
- };
511
+ return textResult({ ok: false, error: `${action} is not yet supported for Shadow channels` });
589
512
  }
590
513
  if (action === "get-server") {
591
514
  const serverId = params.serverId ?? params.server_id ?? params.server ?? "";
592
515
  if (!serverId) {
593
- return {
594
- content: [
595
- {
596
- type: "text",
597
- text: JSON.stringify({ ok: false, error: "serverId is required" })
598
- }
599
- ]
600
- };
516
+ return textResult({ ok: false, error: "serverId is required" });
601
517
  }
602
518
  try {
603
519
  const client = new ShadowClient2(account.serverUrl, account.token);
604
520
  const server = await client.getServer(serverId);
605
- return {
606
- content: [
607
- {
608
- type: "text",
609
- text: JSON.stringify({ ok: true, action: "get-server", server })
610
- }
611
- ]
612
- };
521
+ return textResult({ ok: true, action: "get-server", server });
613
522
  } catch (err) {
614
- return {
615
- content: [
616
- { type: "text", text: JSON.stringify({ ok: false, error: String(err) }) }
617
- ]
618
- };
523
+ return textResult({ ok: false, error: String(err) });
619
524
  }
620
525
  }
621
526
  if (action === "update-homepage") {
622
527
  const serverId = params.serverId ?? params.server_id ?? params.server ?? "";
623
528
  const html = params.html ?? params.homepageHtml ?? params.homepage_html ?? null;
624
529
  if (!serverId) {
625
- return {
626
- content: [
627
- {
628
- type: "text",
629
- text: JSON.stringify({ ok: false, error: "serverId is required" })
630
- }
631
- ]
632
- };
530
+ return textResult({ ok: false, error: "serverId is required" });
633
531
  }
634
532
  try {
635
533
  const client = new ShadowClient2(account.serverUrl, account.token);
636
534
  const result = await client.updateServerHomepage(serverId, html);
637
- return {
638
- content: [
639
- {
640
- type: "text",
641
- text: JSON.stringify({
642
- ok: true,
643
- action: "update-homepage",
644
- serverId: result.id,
645
- slug: result.slug,
646
- homepageHtml: result.homepageHtml ? `(${result.homepageHtml.length} chars)` : null
647
- })
648
- }
649
- ]
650
- };
535
+ return textResult({
536
+ ok: true,
537
+ action: "update-homepage",
538
+ serverId: result.id,
539
+ slug: result.slug,
540
+ homepageHtml: result.homepageHtml ? `(${result.homepageHtml.length} chars)` : null
541
+ });
651
542
  } catch (err) {
652
- return {
653
- content: [
654
- { type: "text", text: JSON.stringify({ ok: false, error: String(err) }) }
655
- ]
656
- };
543
+ return textResult({ ok: false, error: String(err) });
657
544
  }
658
545
  }
659
546
  if (action === "get-connection-status") {
@@ -687,23 +574,9 @@ shadowPlugin.actions = {
687
574
  }
688
575
  })
689
576
  );
690
- return {
691
- content: [
692
- {
693
- type: "text",
694
- text: JSON.stringify({ ok: true, action: "get-connection-status", accounts: results })
695
- }
696
- ]
697
- };
577
+ return textResult({ ok: true, action: "get-connection-status", accounts: results });
698
578
  }
699
- return {
700
- content: [
701
- {
702
- type: "text",
703
- text: JSON.stringify({ ok: false, error: `Action ${action} not yet implemented` })
704
- }
705
- ]
706
- };
579
+ return textResult({ ok: false, error: `Action ${action} not yet implemented` });
707
580
  }
708
581
  };
709
582
 
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  shadowPlugin
3
- } from "./chunk-XPNVTXKL.js";
3
+ } from "./chunk-LUNQKMJD.js";
4
4
  import {
5
5
  getShadowRuntime,
6
6
  monitorShadowProvider,
7
7
  setShadowRuntime,
8
8
  tryGetShadowRuntime
9
- } from "./chunk-QFUUQPJZ.js";
9
+ } from "./chunk-JGLINAN5.js";
10
10
 
11
11
  // index.ts
12
12
  import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  monitorShadowProvider
3
- } from "./chunk-QFUUQPJZ.js";
3
+ } from "./chunk-JGLINAN5.js";
4
4
  export {
5
5
  monitorShadowProvider
6
6
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  shadowPlugin
3
- } from "./chunk-XPNVTXKL.js";
3
+ } from "./chunk-LUNQKMJD.js";
4
4
 
5
5
  // setup-entry.ts
6
6
  import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shadowob/openclaw-shadowob",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "OpenClaw Shadow channel plugin — enables AI agents to interact in Shadow server channels",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -57,15 +57,8 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "zod": "^3.25.67",
60
- "@shadowob/sdk": "1.1.0"
61
- },
62
- "peerDependencies": {
63
- "openclaw": ">=2026.3.0"
64
- },
65
- "peerDependenciesMeta": {
66
- "openclaw": {
67
- "optional": true
68
- }
60
+ "openclaw": "^2026.3.23-2",
61
+ "@shadowob/sdk": "1.1.1"
69
62
  },
70
63
  "devDependencies": {
71
64
  "@types/node": "^22.15.21",