@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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { createOpenclawClawlingApiClient } from "./api-client.js";
|
|
3
3
|
import { ClawlingApiError } from "./api-types.js";
|
|
4
|
-
import { CHANNEL_ID, mergeOpenclawClawchatToolAllow, resolveOpenclawClawlingAccount, } from "./config.js";
|
|
4
|
+
import { CHANNEL_ID, mergeOpenclawClawchatRuntimePluginActivation, mergeOpenclawClawchatToolAllow, resolveOpenclawClawlingAccount, } from "./config.js";
|
|
5
|
+
import { getClawChatStore } from "./storage.js";
|
|
5
6
|
/**
|
|
6
7
|
* Platform tag sent to `/v1/agents/connect`. Identifies the host of this
|
|
7
8
|
* agent runtime — openclaw's bundled clawchat channel.
|
|
@@ -38,38 +39,69 @@ async function promptInviteCodeFromStdin(runtime) {
|
|
|
38
39
|
function buildLoginConfig(cfg, result) {
|
|
39
40
|
const channels = (cfg.channels ?? {});
|
|
40
41
|
const existing = (channels[CHANNEL_ID] ?? {});
|
|
42
|
+
const groupMode = existing.groupMode === "mention" || existing.groupMode === "all"
|
|
43
|
+
? existing.groupMode
|
|
44
|
+
: "all";
|
|
41
45
|
const nextSection = {
|
|
42
46
|
...existing,
|
|
43
47
|
enabled: true,
|
|
48
|
+
groupMode,
|
|
44
49
|
token: result.access_token,
|
|
45
50
|
userId: result.agent.user_id,
|
|
51
|
+
ownerUserId: result.agent.owner_id,
|
|
46
52
|
};
|
|
47
53
|
if (result.refresh_token) {
|
|
48
54
|
nextSection.refreshToken = result.refresh_token;
|
|
49
55
|
}
|
|
50
|
-
|
|
56
|
+
else {
|
|
57
|
+
delete nextSection.refreshToken;
|
|
58
|
+
}
|
|
59
|
+
return mergeOpenclawClawchatRuntimePluginActivation(mergeOpenclawClawchatToolAllow({
|
|
51
60
|
...cfg,
|
|
52
61
|
channels: { ...channels, [CHANNEL_ID]: nextSection },
|
|
53
|
-
});
|
|
62
|
+
}));
|
|
54
63
|
}
|
|
55
64
|
async function persistLoginConfig(params, result) {
|
|
56
65
|
if (params.mutateConfigFile) {
|
|
66
|
+
params.runtime.log(`Persisting ClawChat credentials and plugin activation for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id} with Gateway restart intent.`);
|
|
57
67
|
await params.mutateConfigFile({
|
|
58
|
-
afterWrite: {
|
|
68
|
+
afterWrite: {
|
|
69
|
+
mode: "restart",
|
|
70
|
+
reason: "openclaw-clawchat credentials changed",
|
|
71
|
+
},
|
|
59
72
|
mutate(draft) {
|
|
60
73
|
Object.assign(draft, buildLoginConfig(draft, result));
|
|
61
74
|
},
|
|
62
75
|
});
|
|
76
|
+
params.runtime.log(`ClawChat credentials and plugin activation persisted for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id}.`);
|
|
63
77
|
return;
|
|
64
78
|
}
|
|
65
79
|
if (params.persistConfig) {
|
|
80
|
+
params.runtime.log(`Persisting ClawChat credentials and plugin activation for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id}.`);
|
|
66
81
|
await params.persistConfig(buildLoginConfig(params.cfg, result));
|
|
82
|
+
params.runtime.log(`ClawChat credentials and plugin activation persisted for userId=${result.agent.user_id} ownerUserId=${result.agent.owner_id}.`);
|
|
67
83
|
return;
|
|
68
84
|
}
|
|
69
85
|
throw new Error("openclaw-clawchat: mutateConfigFile is required to persist login credentials");
|
|
70
86
|
}
|
|
87
|
+
function requireConnectString(value, fieldName) {
|
|
88
|
+
if (typeof value !== "string") {
|
|
89
|
+
throw new Error(`agents/connect response missing required fields (${fieldName})`);
|
|
90
|
+
}
|
|
91
|
+
const trimmed = value.trim();
|
|
92
|
+
if (!trimmed) {
|
|
93
|
+
throw new Error(`agents/connect response missing required fields (${fieldName})`);
|
|
94
|
+
}
|
|
95
|
+
return trimmed;
|
|
96
|
+
}
|
|
97
|
+
function readOptionalConnectString(value, fieldName) {
|
|
98
|
+
if (value == null) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return requireConnectString(value, fieldName);
|
|
102
|
+
}
|
|
71
103
|
/**
|
|
72
|
-
* Run the invite-code credential exchange used by
|
|
104
|
+
* Run the invite-code credential exchange used by `/clawchat-login`,
|
|
73
105
|
* `openclaw channels add --channel openclaw-clawchat --token <invite-code>`,
|
|
74
106
|
* and `openclaw channels login --channel openclaw-clawchat`:
|
|
75
107
|
* 1. Read the existing channel section; require `baseUrl` to be set so we
|
|
@@ -113,20 +145,53 @@ export async function runOpenclawClawlingLogin(params) {
|
|
|
113
145
|
}
|
|
114
146
|
throw err;
|
|
115
147
|
}
|
|
116
|
-
|
|
117
|
-
|
|
148
|
+
const accessToken = requireConnectString(result?.access_token, "access_token");
|
|
149
|
+
const agentUserId = requireConnectString(result?.agent?.user_id, "agent.user_id");
|
|
150
|
+
const ownerUserId = requireConnectString(result?.agent?.owner_id, "agent.owner_id");
|
|
151
|
+
const agentId = readOptionalConnectString(result?.agent?.id, "agent.id");
|
|
152
|
+
let conversationId = null;
|
|
153
|
+
if (result?.conversation != null) {
|
|
154
|
+
conversationId = requireConnectString(result.conversation.id, "conversation.id");
|
|
155
|
+
}
|
|
156
|
+
const normalizedResult = {
|
|
157
|
+
...result,
|
|
158
|
+
access_token: accessToken,
|
|
159
|
+
refresh_token: typeof result?.refresh_token === "string" ? result.refresh_token.trim() : "",
|
|
160
|
+
agent: {
|
|
161
|
+
...result.agent,
|
|
162
|
+
...(agentId ? { id: agentId } : {}),
|
|
163
|
+
owner_id: ownerUserId,
|
|
164
|
+
user_id: agentUserId,
|
|
165
|
+
},
|
|
166
|
+
...(conversationId
|
|
167
|
+
? {
|
|
168
|
+
conversation: {
|
|
169
|
+
...result.conversation,
|
|
170
|
+
id: conversationId,
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
: {}),
|
|
174
|
+
};
|
|
175
|
+
runtime.log(`Updating config: channels.${CHANNEL_ID}.token=[REDACTED] userId=${normalizedResult.agent.user_id} ownerUserId=${normalizedResult.agent.owner_id}${normalizedResult.refresh_token ? " refreshToken=[REDACTED]" : ""} plugins.entries.${CHANNEL_ID}.enabled=true plugins.allow+=${CHANNEL_ID} …`);
|
|
176
|
+
await persistLoginConfig(params, normalizedResult);
|
|
177
|
+
try {
|
|
178
|
+
const store = params.store ??
|
|
179
|
+
getClawChatStore({
|
|
180
|
+
...(params.dbPath ? { dbPath: params.dbPath } : {}),
|
|
181
|
+
log: { error: runtime.log },
|
|
182
|
+
});
|
|
183
|
+
store.upsertActivation({
|
|
184
|
+
platform: "openclaw",
|
|
185
|
+
accountId: account.accountId,
|
|
186
|
+
userId: normalizedResult.agent.user_id,
|
|
187
|
+
ownerUserId: normalizedResult.agent.owner_id,
|
|
188
|
+
conversationId: normalizedResult.conversation?.id ?? null,
|
|
189
|
+
loginMethod: "login",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
runtime.log("openclaw-clawchat sqlite activation persistence failed; login continues.");
|
|
118
194
|
}
|
|
119
|
-
const tokenPreview = redactToken(result.access_token);
|
|
120
|
-
runtime.log(`Updating config: channels.${CHANNEL_ID}.token=${tokenPreview} userId=${result.agent.user_id}${result.refresh_token ? " refreshToken=***" : ""} …`);
|
|
121
|
-
await persistLoginConfig(params, result);
|
|
122
195
|
runtime.log(`Config file updated.`);
|
|
123
|
-
runtime.log(`openclaw-clawchat login succeeded (user_id=${
|
|
124
|
-
}
|
|
125
|
-
/** Shortens a token for display logs without revealing the full secret. */
|
|
126
|
-
function redactToken(token) {
|
|
127
|
-
if (!token)
|
|
128
|
-
return "(empty)";
|
|
129
|
-
if (token.length <= 8)
|
|
130
|
-
return "***";
|
|
131
|
-
return `${token.slice(0, 4)}…${token.slice(-4)}`;
|
|
196
|
+
runtime.log(`openclaw-clawchat login succeeded (user_id=${normalizedResult.agent.user_id}, owner_user_id=${normalizedResult.agent.owner_id}, nickname=${normalizedResult.agent.nickname || "-"}).`);
|
|
132
197
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildOutboundMediaLoadOptions, } from "openclaw/plugin-sdk/media-runtime";
|
|
1
2
|
export function inferMediaKindFromMime(mime) {
|
|
2
3
|
if (!mime)
|
|
3
4
|
return "file";
|
|
@@ -56,25 +57,24 @@ export async function uploadOutboundMedia(urls, ctx) {
|
|
|
56
57
|
const out = [];
|
|
57
58
|
for (const url of urls) {
|
|
58
59
|
try {
|
|
59
|
-
const loaded = await ctx.runtime.media.loadWebMedia(url, {
|
|
60
|
+
const loaded = await ctx.runtime.media.loadWebMedia(url, buildOutboundMediaLoadOptions({
|
|
60
61
|
maxBytes,
|
|
61
|
-
...(ctx.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
62
|
+
...(ctx.mediaAccess ? { mediaAccess: ctx.mediaAccess } : {}),
|
|
63
|
+
...(ctx.mediaLocalRoots ? { mediaLocalRoots: ctx.mediaLocalRoots } : {}),
|
|
64
|
+
...(ctx.mediaReadFile ? { mediaReadFile: ctx.mediaReadFile } : {}),
|
|
65
|
+
}));
|
|
65
66
|
const uploaded = await ctx.apiClient.uploadMedia({
|
|
66
67
|
buffer: loaded.buffer,
|
|
67
68
|
filename: loaded.fileName ?? "upload.bin",
|
|
68
69
|
mime: loaded.contentType,
|
|
69
70
|
});
|
|
70
71
|
const fragment = {
|
|
71
|
-
kind:
|
|
72
|
+
kind: uploaded.kind,
|
|
72
73
|
url: uploaded.url,
|
|
74
|
+
name: uploaded.name,
|
|
73
75
|
mime: uploaded.mime,
|
|
74
76
|
size: uploaded.size,
|
|
75
77
|
};
|
|
76
|
-
if (loaded.fileName)
|
|
77
|
-
fragment.name = loaded.fileName;
|
|
78
78
|
out.push(fragment);
|
|
79
79
|
}
|
|
80
80
|
catch (err) {
|
|
@@ -53,7 +53,7 @@ export function textToFragments(text) {
|
|
|
53
53
|
/**
|
|
54
54
|
* Extract media fragments from a body (image/file/audio/video). Skips
|
|
55
55
|
* entries missing `url`. Preserves all optional metadata fields the
|
|
56
|
-
*
|
|
56
|
+
* protocol carries through (mime/size/width/height/duration/name).
|
|
57
57
|
*/
|
|
58
58
|
export function extractMediaFragments(fragments) {
|
|
59
59
|
const out = [];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class MockTransport {
|
|
2
|
+
handlers;
|
|
3
|
+
currentState = "closed";
|
|
4
|
+
sent = [];
|
|
5
|
+
get state() {
|
|
6
|
+
return this.currentState;
|
|
7
|
+
}
|
|
8
|
+
async connect(_url, handlers) {
|
|
9
|
+
this.handlers = handlers;
|
|
10
|
+
this.currentState = "open";
|
|
11
|
+
handlers.onOpen();
|
|
12
|
+
}
|
|
13
|
+
send(data) {
|
|
14
|
+
if (this.currentState !== "open") {
|
|
15
|
+
throw new Error("transport is not open");
|
|
16
|
+
}
|
|
17
|
+
this.sent.push(data);
|
|
18
|
+
}
|
|
19
|
+
close(code = 1000, reason = "client close") {
|
|
20
|
+
if (this.currentState === "closed")
|
|
21
|
+
return;
|
|
22
|
+
this.currentState = "closed";
|
|
23
|
+
this.handlers?.onClose(code, reason);
|
|
24
|
+
}
|
|
25
|
+
emitInbound(data) {
|
|
26
|
+
this.handlers?.onMessage(data);
|
|
27
|
+
}
|
|
28
|
+
emitError(err) {
|
|
29
|
+
this.handlers?.onError(err);
|
|
30
|
+
}
|
|
31
|
+
}
|