@newbase-clawchat/openclaw-clawchat 2026.5.4 → 2026.5.12-13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/INSTALL.md +64 -0
  2. package/README.md +121 -19
  3. package/dist/index.js +10 -19
  4. package/dist/setup-entry.js +3 -0
  5. package/dist/src/api-client.js +78 -10
  6. package/dist/src/api-types.test-d.js +10 -0
  7. package/dist/src/channel.js +25 -156
  8. package/dist/src/channel.setup.js +120 -0
  9. package/dist/src/client.js +37 -41
  10. package/dist/src/config.js +75 -17
  11. package/dist/src/inbound.js +79 -61
  12. package/dist/src/login.runtime.js +84 -19
  13. package/dist/src/media-runtime.js +8 -8
  14. package/dist/src/message-mapper.js +1 -1
  15. package/dist/src/mock-transport.js +31 -0
  16. package/dist/src/outbound.js +410 -26
  17. package/dist/src/protocol-types.js +63 -0
  18. package/dist/src/protocol-types.typecheck.js +1 -0
  19. package/dist/src/protocol.js +2 -7
  20. package/dist/src/reply-dispatcher.js +157 -54
  21. package/dist/src/runtime.js +795 -119
  22. package/dist/src/storage.js +689 -0
  23. package/dist/src/tools-schema.js +98 -16
  24. package/dist/src/tools.js +422 -135
  25. package/dist/src/ws-alignment.js +178 -0
  26. package/dist/src/ws-client.js +588 -0
  27. package/dist/src/ws-log.js +19 -0
  28. package/index.ts +10 -22
  29. package/openclaw.plugin.json +37 -2
  30. package/package.json +17 -4
  31. package/setup-entry.ts +4 -0
  32. package/skills/clawchat/SKILL.md +88 -0
  33. package/src/api-client.test.ts +274 -14
  34. package/src/api-client.ts +138 -23
  35. package/src/api-types.test-d.ts +12 -0
  36. package/src/api-types.ts +90 -4
  37. package/src/buffered-stream.test.ts +14 -12
  38. package/src/buffered-stream.ts +1 -1
  39. package/src/channel.outbound.test.ts +269 -60
  40. package/src/channel.setup.ts +146 -0
  41. package/src/channel.test.ts +130 -24
  42. package/src/channel.ts +30 -186
  43. package/src/client.test.ts +197 -11
  44. package/src/client.ts +50 -57
  45. package/src/config.test.ts +108 -6
  46. package/src/config.ts +95 -24
  47. package/src/inbound.test.ts +288 -37
  48. package/src/inbound.ts +96 -84
  49. package/src/login.runtime.test.ts +347 -13
  50. package/src/login.runtime.ts +105 -23
  51. package/src/manifest.test.ts +146 -74
  52. package/src/media-runtime.test.ts +57 -2
  53. package/src/media-runtime.ts +26 -17
  54. package/src/message-mapper.test.ts +2 -2
  55. package/src/message-mapper.ts +2 -2
  56. package/src/mock-transport.test.ts +35 -0
  57. package/src/mock-transport.ts +38 -0
  58. package/src/outbound.test.ts +694 -73
  59. package/src/outbound.ts +484 -31
  60. package/src/plugin-entry.test.ts +1 -0
  61. package/src/protocol-types.test.ts +69 -0
  62. package/src/protocol-types.ts +296 -0
  63. package/src/protocol-types.typecheck.ts +89 -0
  64. package/src/protocol.test.ts +1 -6
  65. package/src/protocol.ts +2 -7
  66. package/src/reply-dispatcher.test.ts +819 -119
  67. package/src/reply-dispatcher.ts +202 -60
  68. package/src/runtime.test.ts +2120 -41
  69. package/src/runtime.ts +935 -142
  70. package/src/scripts.test.ts +85 -0
  71. package/src/storage.test.ts +793 -0
  72. package/src/storage.ts +1095 -0
  73. package/src/streaming.test.ts +9 -8
  74. package/src/streaming.ts +1 -1
  75. package/src/tools-schema.ts +148 -20
  76. package/src/tools.test.ts +377 -50
  77. package/src/tools.ts +574 -154
  78. package/src/ws-alignment.test.ts +103 -0
  79. package/src/ws-alignment.ts +275 -0
  80. package/src/ws-client.test.ts +1218 -0
  81. package/src/ws-client.ts +662 -0
  82. package/src/ws-log.test.ts +32 -0
  83. package/src/ws-log.ts +31 -0
  84. package/skills/clawchat-account-tools/SKILL.md +0 -26
  85. package/skills/clawchat-activate/SKILL.md +0 -47
@@ -1,16 +1,18 @@
1
1
  import type { PluginRuntime } from "openclaw/plugin-sdk/core";
2
+ import {
3
+ buildOutboundMediaLoadOptions,
4
+ type OutboundMediaAccess,
5
+ type OutboundMediaReadFile,
6
+ } from "openclaw/plugin-sdk/media-runtime";
2
7
  import type { OpenclawClawlingApiClient } from "./api-client.ts";
3
8
 
4
9
  /**
5
- * Local structural superset of the SDK's media fragment narrow types.
10
+ * Local structural superset of the protocol media fragment narrow types.
6
11
  *
7
- * `@clawling/chat-sdk@^0.2.0` exports each narrow type
8
- * (`ImageFragment` / `FileFragment` / `AudioFragment` / `VideoFragment`)
9
- * with a literal `kind` that distinguishes them. Building the wide
10
- * shape locally avoids per-kind switch statements when constructing
11
- * outbound fragments; structural compatibility lets a single object
12
- * satisfy whichever narrow type matches its runtime `kind`. We cast
13
- * to `Fragment[]` only at the SDK boundary (outbound.ts).
12
+ * Each media fragment has a literal `kind` that distinguishes it. Building the
13
+ * wide shape locally avoids per-kind switch statements when constructing
14
+ * outbound fragments; structural compatibility lets a single object satisfy
15
+ * whichever narrow type matches its runtime `kind`.
14
16
  */
15
17
  export interface MediaItem {
16
18
  kind: "image" | "file" | "audio" | "video";
@@ -42,8 +44,12 @@ export interface UploadOutboundCtx {
42
44
  runtime: PluginRuntime;
43
45
  log?: LogSink;
44
46
  maxBytes?: number;
47
+ /** Host-authorized outbound media access from OpenClaw message delivery. */
48
+ mediaAccess?: OutboundMediaAccess;
45
49
  /** Allowed local roots for path-based uploads. Empty/undefined = use loadWebMedia defaults. */
46
- mediaLocalRoots?: readonly string[];
50
+ mediaLocalRoots?: readonly string[] | "any";
51
+ /** Host-provided read bridge for sandbox/allowed local media paths. */
52
+ mediaReadFile?: OutboundMediaReadFile;
47
53
  }
48
54
 
49
55
  export function inferMediaKindFromMime(mime: string | undefined): MediaItem["kind"] {
@@ -114,24 +120,27 @@ export async function uploadOutboundMedia(
114
120
  const out: ClawlingMediaFragment[] = [];
115
121
  for (const url of urls) {
116
122
  try {
117
- const loaded = await ctx.runtime.media.loadWebMedia(url, {
118
- maxBytes,
119
- ...(ctx.mediaLocalRoots && ctx.mediaLocalRoots.length > 0
120
- ? { localRoots: ctx.mediaLocalRoots }
121
- : {}),
122
- });
123
+ const loaded = await ctx.runtime.media.loadWebMedia(
124
+ url,
125
+ buildOutboundMediaLoadOptions({
126
+ maxBytes,
127
+ ...(ctx.mediaAccess ? { mediaAccess: ctx.mediaAccess } : {}),
128
+ ...(ctx.mediaLocalRoots ? { mediaLocalRoots: ctx.mediaLocalRoots } : {}),
129
+ ...(ctx.mediaReadFile ? { mediaReadFile: ctx.mediaReadFile } : {}),
130
+ }),
131
+ );
123
132
  const uploaded = await ctx.apiClient.uploadMedia({
124
133
  buffer: loaded.buffer,
125
134
  filename: loaded.fileName ?? "upload.bin",
126
135
  mime: loaded.contentType,
127
136
  });
128
137
  const fragment: ClawlingMediaFragment = {
129
- kind: inferMediaKindFromMime(uploaded.mime),
138
+ kind: uploaded.kind,
130
139
  url: uploaded.url,
140
+ name: uploaded.name,
131
141
  mime: uploaded.mime,
132
142
  size: uploaded.size,
133
143
  };
134
- if (loaded.fileName) fragment.name = loaded.fileName;
135
144
  out.push(fragment);
136
145
  } catch (err) {
137
146
  ctx.log?.error?.(
@@ -1,4 +1,4 @@
1
- import type { Fragment } from "@newbase-clawchat/sdk";
1
+ import type { Fragment } from "./protocol-types.ts";
2
2
  import { describe, expect, it } from "vitest";
3
3
  import { extractMediaFragments, fragmentsToText, textToFragments } from "./message-mapper.ts";
4
4
 
@@ -119,7 +119,7 @@ describe("openclaw-clawchat message-mapper", () => {
119
119
  });
120
120
 
121
121
  it("extractMediaFragments skips media fragments without url", () => {
122
- // Note: SDK 0.2.0 requires `url` on image fragments — using a partial
122
+ // Note: the protocol type requires `url` on image fragments — using a partial
123
123
  // object here exercises the runtime guard. Casting here is intentional
124
124
  // (we want to verify defensive handling of malformed input).
125
125
  const fragments: Fragment[] = [
@@ -1,4 +1,4 @@
1
- import type { Fragment } from "@newbase-clawchat/sdk";
1
+ import type { Fragment } from "./protocol-types.ts";
2
2
  import type { MediaItem } from "./media-runtime.ts";
3
3
 
4
4
  interface FlattenOptions {
@@ -61,7 +61,7 @@ export function textToFragments(text: string): Fragment[] {
61
61
  /**
62
62
  * Extract media fragments from a body (image/file/audio/video). Skips
63
63
  * entries missing `url`. Preserves all optional metadata fields the
64
- * SDK passes through (mime/size/width/height/duration/name).
64
+ * protocol carries through (mime/size/width/height/duration/name).
65
65
  */
66
66
  export function extractMediaFragments(fragments: Fragment[]): MediaItem[] {
67
67
  const out: MediaItem[] = [];
@@ -0,0 +1,35 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { MockTransport } from "./mock-transport.ts";
3
+
4
+ describe("MockTransport", () => {
5
+ it("opens, records sent frames, emits inbound frames, and closes", async () => {
6
+ const transport = new MockTransport();
7
+ const onOpen = vi.fn();
8
+ const onMessage = vi.fn();
9
+ const onClose = vi.fn();
10
+ const onError = vi.fn();
11
+
12
+ await transport.connect("ws://test", { onOpen, onMessage, onClose, onError });
13
+ expect(transport.state).toBe("open");
14
+ expect(onOpen).toHaveBeenCalledTimes(1);
15
+
16
+ transport.send("frame-1");
17
+ expect(transport.sent).toEqual(["frame-1"]);
18
+
19
+ transport.emitInbound("frame-2");
20
+ expect(onMessage).toHaveBeenCalledWith("frame-2");
21
+
22
+ const buffer = Buffer.from("frame-3");
23
+ transport.emitInbound(buffer);
24
+ expect(onMessage).toHaveBeenCalledWith(buffer);
25
+
26
+ const err = new Error("boom");
27
+ transport.emitError(err);
28
+ expect(onError).toHaveBeenCalledWith(err);
29
+
30
+ transport.close(1000, "done");
31
+ expect(transport.state).toBe("closed");
32
+ expect(onClose).toHaveBeenCalledWith(1000, "done");
33
+ expect(onError).toHaveBeenCalledTimes(1);
34
+ });
35
+ });
@@ -0,0 +1,38 @@
1
+ import type { Transport, TransportEvents, TransportState } from "./protocol-types.ts";
2
+
3
+ export class MockTransport implements Transport {
4
+ private handlers?: TransportEvents;
5
+ private currentState: TransportState = "closed";
6
+ readonly sent: string[] = [];
7
+
8
+ get state(): TransportState {
9
+ return this.currentState;
10
+ }
11
+
12
+ async connect(_url: string, handlers: TransportEvents): Promise<void> {
13
+ this.handlers = handlers;
14
+ this.currentState = "open";
15
+ handlers.onOpen();
16
+ }
17
+
18
+ send(data: string): void {
19
+ if (this.currentState !== "open") {
20
+ throw new Error("transport is not open");
21
+ }
22
+ this.sent.push(data);
23
+ }
24
+
25
+ close(code = 1000, reason = "client close"): void {
26
+ if (this.currentState === "closed") return;
27
+ this.currentState = "closed";
28
+ this.handlers?.onClose(code, reason);
29
+ }
30
+
31
+ emitInbound(data: string | Buffer): void {
32
+ this.handlers?.onMessage(data);
33
+ }
34
+
35
+ emitError(err: Error): void {
36
+ this.handlers?.onError(err);
37
+ }
38
+ }