@coclaw/openclaw-coclaw 0.11.5 → 0.12.0

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/README.md CHANGED
@@ -98,7 +98,7 @@ openclaw coclaw enroll [--server <url>]
98
98
  {
99
99
  "default": {
100
100
  "serverUrl": "https://coclaw.net",
101
- "botId": "bot-xxx",
101
+ "clawId": "claw-xxx",
102
102
  "token": "token-xxx",
103
103
  "boundAt": "2026-03-05T..."
104
104
  }
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { bindBot, unbindBot, enrollBot, waitForClaimAndSave } from './src/common/bot-binding.js';
1
+ import { bindClaw, unbindClaw, enrollClaw, waitForClaimAndSave } from './src/common/claw-binding.js';
2
2
  import { registerCoclawCli } from './src/cli-registrar.js';
3
3
  import { resolveErrorMessage } from './src/common/errors.js';
4
4
  import { notBound, bindOk, unbindOk, claimCodeCreated } from './src/common/messages.js';
@@ -116,7 +116,7 @@ const plugin = {
116
116
  await stopRealtimeBridge();
117
117
  let result;
118
118
  try {
119
- result = await bindBot({
119
+ result = await bindClaw({
120
120
  code,
121
121
  serverUrl: serverUrl ?? api.pluginConfig?.serverUrl,
122
122
  });
@@ -133,7 +133,7 @@ const plugin = {
133
133
  }
134
134
 
135
135
  async function doUnbind({ serverUrl }) {
136
- const result = await unbindBot({
136
+ const result = await unbindClaw({
137
137
  serverUrl: serverUrl ?? api.pluginConfig?.serverUrl,
138
138
  });
139
139
  await stopRealtimeBridge();
@@ -153,9 +153,9 @@ const plugin = {
153
153
  });
154
154
  respond(true, {
155
155
  status: {
156
- botId: result.botId,
156
+ clawId: result.clawId,
157
157
  rebound: result.rebound,
158
- previousBotId: result.previousBotId,
158
+ previousClawId: result.previousClawId,
159
159
  },
160
160
  });
161
161
  }
@@ -167,7 +167,7 @@ const plugin = {
167
167
  api.registerGatewayMethod('coclaw.unbind', async ({ params, respond }) => {
168
168
  try {
169
169
  const result = await doUnbind({ serverUrl: params?.serverUrl });
170
- respond(true, { status: { botId: result.botId } });
170
+ respond(true, { status: { clawId: result.clawId } });
171
171
  }
172
172
  catch (err) {
173
173
  respondError(respond, err);
@@ -187,7 +187,7 @@ const plugin = {
187
187
  activeEnrollAbort = abortController;
188
188
 
189
189
  const serverUrl = params?.serverUrl ?? api.pluginConfig?.serverUrl;
190
- const result = await enrollBot({ serverUrl });
190
+ const result = await enrollClaw({ serverUrl });
191
191
 
192
192
  const rawMinutes = Math.round(
193
193
  (new Date(result.expiresAt).getTime() - Date.now()) / 60_000,
@@ -552,7 +552,7 @@ const plugin = {
552
552
  activeEnrollAbort = abortController;
553
553
 
554
554
  const serverUrl = options.server ?? api.pluginConfig?.serverUrl;
555
- const result = await enrollBot({ serverUrl });
555
+ const result = await enrollClaw({ serverUrl });
556
556
  const rawMinutes = Math.round(
557
557
  (new Date(result.expiresAt).getTime() - Date.now()) / 60_000,
558
558
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coclaw/openclaw-coclaw",
3
- "version": "0.11.5",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "OpenClaw CoClaw channel plugin for remote chat",
package/src/api.js CHANGED
@@ -31,7 +31,7 @@ const CLAIM_CODE_TIMEOUT = 15_000;
31
31
  const CLAIM_WAIT_TIMEOUT = 30_000;
32
32
 
33
33
  export async function bindWithServer({ baseUrl, code, name }) {
34
- return requestJson(baseUrl, '/api/v1/bots/bind', {
34
+ return requestJson(baseUrl, '/api/v1/claws/bind', {
35
35
  method: 'POST',
36
36
  headers: { 'content-type': 'application/json' },
37
37
  body: { code, name },
@@ -40,7 +40,7 @@ export async function bindWithServer({ baseUrl, code, name }) {
40
40
  }
41
41
 
42
42
  export async function unbindWithServer({ baseUrl, token, timeout = UNBIND_TIMEOUT }) {
43
- return requestJson(baseUrl, '/api/v1/bots/unbind', {
43
+ return requestJson(baseUrl, '/api/v1/claws/unbind', {
44
44
  method: 'POST',
45
45
  headers: {
46
46
  Authorization: `Bearer ${token}`,
@@ -8,14 +8,14 @@ function resolveServerUrl(serverUrl) {
8
8
  return serverUrl ?? process.env.COCLAW_SERVER_URL ?? DEFAULT_SERVER_URL;
9
9
  }
10
10
 
11
- // 这些 HTTP 状态码表示 bot 在 server 端已不存在,视为解绑成功
11
+ // 这些 HTTP 状态码表示 claw 在 server 端已不存在,视为解绑成功
12
12
  const ALREADY_UNBOUND_STATUSES = new Set([401, 404, 410]);
13
13
 
14
14
  function isAlreadyUnbound(err) {
15
15
  return ALREADY_UNBOUND_STATUSES.has(err?.response?.status);
16
16
  }
17
17
 
18
- export async function bindBot({ code, serverUrl }, deps = {}) {
18
+ export async function bindClaw({ code, serverUrl }, deps = {}) {
19
19
  const {
20
20
  readCfg = readConfig,
21
21
  clearCfg = clearConfig,
@@ -30,10 +30,10 @@ export async function bindBot({ code, serverUrl }, deps = {}) {
30
30
 
31
31
  const config = await readCfg();
32
32
 
33
- // 已绑定时必须先解绑旧 bot,避免产生孤儿记录
34
- let previousBotId;
33
+ // 已绑定时必须先解绑旧 claw,避免产生孤儿记录
34
+ let previousClawId;
35
35
  if (config?.token) {
36
- previousBotId = config.botId || 'unknown';
36
+ previousClawId = config.clawId || 'unknown';
37
37
  const oldBaseUrl = config.serverUrl;
38
38
  if (oldBaseUrl) {
39
39
  try {
@@ -41,7 +41,7 @@ export async function bindBot({ code, serverUrl }, deps = {}) {
41
41
  } catch (err) {
42
42
  if (!isAlreadyUnbound(err)) {
43
43
  const rebindErr = new Error(
44
- `Failed to unbind previous bot (${previousBotId}): ${err.message}. ` +
44
+ `Failed to unbind previous claw (${previousClawId}): ${err.message}. ` +
45
45
  'Unbind manually first, then retry.',
46
46
  );
47
47
  rebindErr.code = 'UNBIND_FAILED';
@@ -59,25 +59,25 @@ export async function bindBot({ code, serverUrl }, deps = {}) {
59
59
  code,
60
60
  });
61
61
 
62
- if (!data?.botId || !data?.token) {
62
+ if (!data?.clawId || !data?.token) {
63
63
  throw new Error('invalid bind response');
64
64
  }
65
65
 
66
66
  await writeCfg({
67
67
  serverUrl: baseUrl,
68
- botId: data.botId,
68
+ clawId: data.clawId,
69
69
  token: data.token,
70
70
  boundAt: new Date().toISOString(),
71
71
  });
72
72
 
73
73
  return {
74
- botId: data.botId,
74
+ clawId: data.clawId,
75
75
  rebound: Boolean(data.rebound),
76
- previousBotId,
76
+ previousClawId,
77
77
  };
78
78
  }
79
79
 
80
- export async function enrollBot({ serverUrl }, deps = {}) {
80
+ export async function enrollClaw({ serverUrl }, deps = {}) {
81
81
  const { createClaimCode = createClaimCodeOnServer, readCfg = readConfig } = deps;
82
82
 
83
83
  const config = await readCfg();
@@ -129,14 +129,14 @@ export async function waitForClaimAndSave({ serverUrl, code, waitToken, signal }
129
129
  }
130
130
 
131
131
  // 已认领
132
- if (data?.botId && data?.token) {
132
+ if (data?.clawId && data?.token) {
133
133
  await writeCfg({
134
134
  serverUrl: baseUrl,
135
- botId: data.botId,
135
+ clawId: data.clawId,
136
136
  token: data.token,
137
137
  boundAt: new Date().toISOString(),
138
138
  });
139
- return { botId: data.botId };
139
+ return { clawId: data.clawId };
140
140
  }
141
141
 
142
142
  // PENDING — 延迟后继续轮询
@@ -150,7 +150,7 @@ export async function waitForClaimAndSave({ serverUrl, code, waitToken, signal }
150
150
  }
151
151
  }
152
152
 
153
- export async function unbindBot({ serverUrl }, deps = {}) {
153
+ export async function unbindClaw({ serverUrl }, deps = {}) {
154
154
  const {
155
155
  readCfg = readConfig,
156
156
  clearCfg = clearConfig,
@@ -170,7 +170,7 @@ export async function unbindBot({ serverUrl }, deps = {}) {
170
170
  try {
171
171
  await unbindServer({ baseUrl, token: config.token });
172
172
  } catch (err) {
173
- // bot 在 server 已不存在 — 视为解绑成功,继续清理本地
173
+ // claw 在 server 已不存在 — 视为解绑成功,继续清理本地
174
174
  if (!isAlreadyUnbound(err)) {
175
175
  throw err;
176
176
  }
@@ -179,5 +179,5 @@ export async function unbindBot({ serverUrl }, deps = {}) {
179
179
 
180
180
  await clearCfg();
181
181
 
182
- return { botId: config.botId };
182
+ return { clawId: config.clawId };
183
183
  }
@@ -3,7 +3,7 @@ const ERROR_TEXT_MAP = {
3
3
  BINDING_CODE_INVALID: 'Binding code is invalid',
4
4
  BINDING_CODE_EXPIRED: 'Binding code has expired, please get a new one',
5
5
  BINDING_CODE_EXHAUSTED: 'Server cannot generate binding code right now, please try again later',
6
- BOT_BLOCKED: 'Claw is blocked, please contact the admin',
6
+ CLAW_BLOCKED: 'Claw is blocked, please contact the admin',
7
7
  UNAUTHORIZED: 'Auth failed, please check token or re-bind',
8
8
  INTERNAL_SERVER_ERROR: 'Server error, please try again later',
9
9
  };
@@ -1,15 +1,15 @@
1
1
  // bind/unbind CLI 及 command 的用户提示文案(统一出口)
2
2
 
3
- export function bindOk({ botId, rebound, previousBotId }) {
3
+ export function bindOk({ clawId, rebound, previousClawId }) {
4
4
  const action = rebound ? 're-bound' : 'bound';
5
- const prev = previousBotId
6
- ? ` (previous Claw ${previousBotId} was auto-unbound)`
5
+ const prev = previousClawId
6
+ ? ` (previous Claw ${previousClawId} was auto-unbound)`
7
7
  : '';
8
- return `OK. Claw (${botId}) ${action} to CoClaw.${prev}`;
8
+ return `OK. Claw (${clawId}) ${action} to CoClaw.${prev}`;
9
9
  }
10
10
 
11
- export function unbindOk({ botId }) {
12
- const id = botId ?? 'unknown';
11
+ export function unbindOk({ clawId }) {
12
+ const id = clawId ?? 'unknown';
13
13
  return `OK. Claw (${id}) unbound from CoClaw.`;
14
14
  }
15
15
 
package/src/config.js CHANGED
@@ -58,7 +58,12 @@ const bindingsMutex = createMutex();
58
58
  export async function readConfig(accountId = DEFAULT_ACCOUNT_ID) {
59
59
  const bindingsPath = getBindingsPath();
60
60
  const bindings = await readJson(bindingsPath);
61
- return toRecord(bindings[accountId]);
61
+ const record = toRecord(bindings[accountId]);
62
+ // 向后兼容:旧 bindings.json 使用 botId,映射到 clawId
63
+ if (record.botId && !record.clawId) {
64
+ record.clawId = record.botId;
65
+ }
66
+ return record;
62
67
  }
63
68
 
64
69
  export async function writeConfig(nextConfig, accountId = DEFAULT_ACCOUNT_ID) {
@@ -69,7 +74,7 @@ export async function writeConfig(nextConfig, accountId = DEFAULT_ACCOUNT_ID) {
69
74
 
70
75
  const next = { ...current };
71
76
  if (nextConfig.serverUrl !== undefined) next.serverUrl = nextConfig.serverUrl;
72
- if (nextConfig.botId !== undefined) next.botId = nextConfig.botId;
77
+ if (nextConfig.clawId !== undefined) next.clawId = nextConfig.clawId;
73
78
  if (nextConfig.token !== undefined) next.token = nextConfig.token;
74
79
  if (nextConfig.boundAt !== undefined) next.boundAt = nextConfig.boundAt;
75
80
 
@@ -24,7 +24,7 @@ const SERVER_HB_MAX_MISS = 4; // 连续 4 次无响应才断连(~3 分钟)
24
24
  function toServerWsUrl(baseUrl, token) {
25
25
  const url = new URL(baseUrl);
26
26
  url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
27
- url.pathname = '/api/v1/bots/stream';
27
+ url.pathname = '/api/v1/claws/stream';
28
28
  url.searchParams.set('token', token);
29
29
  return url.toString();
30
30
  }
@@ -176,13 +176,13 @@ export class RealtimeBridge {
176
176
  ?? DEFAULT_GATEWAY_WS_URL;
177
177
  }
178
178
 
179
- async __clearTokenLocal(unboundBotId) {
179
+ async __clearTokenLocal(unboundClawId) {
180
180
  const cfg = await this.__readConfig();
181
181
  if (!cfg?.token) {
182
182
  return;
183
183
  }
184
- // 只清除匹配的 bot,避免新绑定被误清
185
- if (unboundBotId && cfg.botId && cfg.botId !== unboundBotId) {
184
+ // 只清除匹配的 claw,避免新绑定被误清
185
+ if (unboundClawId && cfg.clawId && cfg.clawId !== unboundClawId) {
186
186
  return;
187
187
  }
188
188
  await this.__clearConfig();
@@ -824,10 +824,10 @@ export class RealtimeBridge {
824
824
  this.__resetServerHbTimeout(sock);
825
825
  try {
826
826
  const payload = JSON.parse(String(event.data ?? '{}'));
827
- if (payload?.type === 'bot.unbound') {
828
- remoteLog('ws.bot-unbound');
829
- await this.__clearTokenLocal(payload.botId);
830
- try { sock.close(4001, 'bot_unbound'); }
827
+ if (payload?.type === 'claw.unbound') {
828
+ remoteLog('ws.claw-unbound');
829
+ await this.__clearTokenLocal(payload.clawId);
830
+ try { sock.close(4001, 'claw_unbound'); }
831
831
  /* c8 ignore next */
832
832
  catch {}
833
833
  return;
@@ -994,7 +994,7 @@ export class RealtimeBridge {
994
994
  if (sock) {
995
995
  this.intentionallyClosed = true;
996
996
  this.serverWs = null;
997
- // 等待 WebSocket 真正关闭,避免残留连接收到 bot.unbound 等消息
997
+ // 等待 WebSocket 真正关闭,避免残留连接收到 claw.unbound 等消息
998
998
  /* c8 ignore next -- readyState === 3 时跳过 */
999
999
  if (sock.readyState === 3) return;
1000
1000
  await new Promise((resolve) => {
@@ -110,7 +110,7 @@ export async function preloadNdc(deps = {}) {
110
110
  const log = deps.remoteLog ?? defaultRemoteLog;
111
111
  const platform = deps.platform ?? process.platform;
112
112
  const arch = deps.arch ?? process.arch;
113
- const pluginRoot = deps.pluginRoot ?? nodePath.resolve(import.meta.dirname, '..');
113
+ const pluginRoot = deps.pluginRoot ?? nodePath.resolve(import.meta.dirname, '../..');
114
114
  const resolvePaths = deps.resolvePaths ?? defaultResolvePaths;
115
115
  const importTimeout = deps.importTimeout ?? DEFAULT_IMPORT_TIMEOUT_MS;
116
116