@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.
- package/INSTALL.md +64 -0
- package/README.md +121 -19
- package/dist/index.js +10 -19
- package/dist/setup-entry.js +3 -0
- package/dist/src/api-client.js +78 -10
- package/dist/src/api-types.test-d.js +10 -0
- package/dist/src/channel.js +25 -156
- package/dist/src/channel.setup.js +120 -0
- package/dist/src/client.js +37 -41
- package/dist/src/config.js +75 -17
- package/dist/src/inbound.js +79 -61
- package/dist/src/login.runtime.js +84 -19
- package/dist/src/media-runtime.js +8 -8
- package/dist/src/message-mapper.js +1 -1
- package/dist/src/mock-transport.js +31 -0
- package/dist/src/outbound.js +410 -26
- package/dist/src/protocol-types.js +63 -0
- package/dist/src/protocol-types.typecheck.js +1 -0
- package/dist/src/protocol.js +2 -7
- package/dist/src/reply-dispatcher.js +157 -54
- package/dist/src/runtime.js +795 -119
- package/dist/src/storage.js +689 -0
- package/dist/src/tools-schema.js +98 -16
- package/dist/src/tools.js +422 -135
- package/dist/src/ws-alignment.js +178 -0
- package/dist/src/ws-client.js +588 -0
- package/dist/src/ws-log.js +19 -0
- package/index.ts +10 -22
- package/openclaw.plugin.json +37 -2
- package/package.json +17 -4
- package/setup-entry.ts +4 -0
- package/skills/clawchat/SKILL.md +88 -0
- package/src/api-client.test.ts +274 -14
- package/src/api-client.ts +138 -23
- package/src/api-types.test-d.ts +12 -0
- package/src/api-types.ts +90 -4
- package/src/buffered-stream.test.ts +14 -12
- package/src/buffered-stream.ts +1 -1
- package/src/channel.outbound.test.ts +269 -60
- package/src/channel.setup.ts +146 -0
- package/src/channel.test.ts +130 -24
- package/src/channel.ts +30 -186
- package/src/client.test.ts +197 -11
- package/src/client.ts +50 -57
- package/src/config.test.ts +108 -6
- package/src/config.ts +95 -24
- package/src/inbound.test.ts +288 -37
- package/src/inbound.ts +96 -84
- package/src/login.runtime.test.ts +347 -13
- package/src/login.runtime.ts +105 -23
- package/src/manifest.test.ts +146 -74
- package/src/media-runtime.test.ts +57 -2
- package/src/media-runtime.ts +26 -17
- package/src/message-mapper.test.ts +2 -2
- package/src/message-mapper.ts +2 -2
- package/src/mock-transport.test.ts +35 -0
- package/src/mock-transport.ts +38 -0
- package/src/outbound.test.ts +694 -73
- package/src/outbound.ts +484 -31
- package/src/plugin-entry.test.ts +1 -0
- package/src/protocol-types.test.ts +69 -0
- package/src/protocol-types.ts +296 -0
- package/src/protocol-types.typecheck.ts +89 -0
- package/src/protocol.test.ts +1 -6
- package/src/protocol.ts +2 -7
- package/src/reply-dispatcher.test.ts +819 -119
- package/src/reply-dispatcher.ts +202 -60
- package/src/runtime.test.ts +2120 -41
- package/src/runtime.ts +935 -142
- package/src/scripts.test.ts +85 -0
- package/src/storage.test.ts +793 -0
- package/src/storage.ts +1095 -0
- package/src/streaming.test.ts +9 -8
- package/src/streaming.ts +1 -1
- package/src/tools-schema.ts +148 -20
- package/src/tools.test.ts +377 -50
- package/src/tools.ts +574 -154
- package/src/ws-alignment.test.ts +103 -0
- package/src/ws-alignment.ts +275 -0
- package/src/ws-client.test.ts +1218 -0
- package/src/ws-client.ts +662 -0
- package/src/ws-log.test.ts +32 -0
- package/src/ws-log.ts +31 -0
- package/skills/clawchat-account-tools/SKILL.md +0 -26
- package/skills/clawchat-activate/SKILL.md +0 -47
package/src/media-runtime.ts
CHANGED
|
@@ -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
|
|
10
|
+
* Local structural superset of the protocol media fragment narrow types.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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:
|
|
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 "
|
|
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:
|
|
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[] = [
|
package/src/message-mapper.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Fragment } from "
|
|
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
|
-
*
|
|
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
|
+
}
|