@claw-manager/openclaw-api-channel 2026.6.28 → 2026.6.30

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
@@ -2,4 +2,5 @@
2
2
 
3
3
  OpenClaw channel plugin used by Claw Manager to deliver external API messages into the normal OpenClaw agent runtime.
4
4
 
5
- The plugin expects Claw Manager to call the Gateway method `claw-manager-api.sendMessage`. It receives pre-derived sender identity and OpenViking user identity, then records an inbound session message and dispatches the reply through OpenClaw.
5
+ Claw Manager writes request files into the instance queue under `.openclaw/claw-manager-api/requests`.
6
+ When the Gateway starts the `claw-manager-api` channel, this plugin runs a queue monitor, converts each request into an inbound direct-message context, writes an OpenViking sender handoff for the current API user, dispatches the turn through OpenClaw, and writes the response under `.openclaw/claw-manager-api/responses`.
package/dist/index.js CHANGED
@@ -1,33 +1,12 @@
1
- import { apiChannelPlugin, dispatchApiMessage } from "./src/channel.js";
2
- export const API_GATEWAY_METHOD = "claw-manager-api.sendMessage";
1
+ import { apiChannelPlugin } from "./src/channel.js";
3
2
  export default {
4
3
  id: "claw-manager-api",
5
4
  name: "Claw Manager API",
6
5
  description: "External API channel for Claw Manager",
7
6
  register(api) {
7
+ api.logger?.info?.(`claw-manager-api: register channel mode=${String(api.registrationMode ?? "unknown")} ` +
8
+ `hasGatewayMethod=${String(typeof api.registerGatewayMethod === "function")}`);
8
9
  api.registerChannel({ plugin: apiChannelPlugin });
9
- api.registerGatewayMethod(API_GATEWAY_METHOD, async ({ params, context, respond }) => {
10
- try {
11
- const result = await dispatchApiMessage({
12
- ...(params ?? {}),
13
- cfg: context.getRuntimeConfig(),
14
- channelRuntime: api.runtime.channel,
15
- });
16
- respond(true, result);
17
- }
18
- catch (error) {
19
- respond(false, undefined, {
20
- code: "INVALID_REQUEST",
21
- message: errorMessage(error),
22
- });
23
- }
24
- }, { scope: "operator.admin" });
25
10
  },
26
11
  };
27
- function errorMessage(error) {
28
- if (error instanceof Error && error.message.trim()) {
29
- return error.message;
30
- }
31
- return String(error);
32
- }
33
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAExE,MAAM,CAAC,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AAEjE,eAAe;IACb,EAAE,EAAE,kBAAkB;IACtB,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,uCAAuC;IACpD,QAAQ,CAAC,GAAsB;QAC7B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAClD,GAAG,CAAC,qBAAqB,CACvB,kBAAkB,EAClB,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;YACrC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;oBACtC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;oBACjB,GAAG,EAAE,OAAO,CAAC,gBAAgB,EAAE;oBAC/B,cAAc,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO;iBACpC,CAAC,CAAC;gBACH,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE;oBACxB,IAAI,EAAE,iBAAiB;oBACvB,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;iBAC7B,CAAC,CAAC;YACL,CAAC;QACH,CAAC,EACD,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAC5B,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,eAAe;IACb,EAAE,EAAE,kBAAkB;IACtB,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,uCAAuC;IACpD,QAAQ,CAAC,GAAsB;QAC7B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CAChB,2CAA2C,MAAM,CAAC,GAAG,CAAC,gBAAgB,IAAI,SAAS,CAAC,GAAG;YACrF,oBAAoB,MAAM,CAAC,OAAO,GAAG,CAAC,qBAAqB,KAAK,UAAU,CAAC,EAAE,CAChF,CAAC;QACF,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAC"}
@@ -1,5 +1,32 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
2
4
  import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
5
+ import { writeApiOpenVikingHandoff } from "./openviking-handoff.js";
6
+ const API_CHANNEL_ID = "claw-manager-api";
7
+ const API_ACCOUNT_ID = "global";
8
+ const API_QUEUE_POLL_MS = 200;
9
+ async function writeOpenVikingHandoffForTurn(params) {
10
+ const secret = trim(process.env.OPENVIKING_IDENTITY_HASH_SECRET);
11
+ if (!secret) {
12
+ params.log?.debug?.(`[${API_ACCOUNT_ID}] openviking handoff skipped: identity secret missing`);
13
+ return;
14
+ }
15
+ try {
16
+ const wrote = await writeApiOpenVikingHandoff({
17
+ sessionKey: params.sessionKey,
18
+ openVikingUserId: params.openVikingUserId,
19
+ senderHash: params.senderHash,
20
+ secret,
21
+ });
22
+ if (wrote) {
23
+ params.log?.info?.(`[${API_ACCOUNT_ID}] openviking handoff written user=${params.openVikingUserId}`);
24
+ }
25
+ }
26
+ catch (error) {
27
+ params.log?.warn?.(`[${API_ACCOUNT_ID}] openviking handoff write failed: ${errorMessage(error)}`);
28
+ }
29
+ }
3
30
  export function buildApiInboundContext(params) {
4
31
  const message = trim(params.message);
5
32
  const openVikingUserId = trim(params.openVikingUserId) || trim(params.openvikingUserId);
@@ -52,8 +79,8 @@ export async function dispatchApiMessage(ctx) {
52
79
  const inbound = buildApiInboundContext(ctx);
53
80
  const route = runtime.routing.resolveAgentRoute({
54
81
  cfg: ctx.cfg,
55
- channel: "claw-manager-api",
56
- accountId: "global",
82
+ channel: API_CHANNEL_ID,
83
+ accountId: API_ACCOUNT_ID,
57
84
  peer: { kind: "direct", id: inbound.To },
58
85
  });
59
86
  const sessionKey = route.sessionKey ?? inbound.SessionKey;
@@ -62,15 +89,21 @@ export async function dispatchApiMessage(ctx) {
62
89
  agentId: route.agentId,
63
90
  });
64
91
  const finalized = runtime.reply.finalizeInboundContext(inbound);
92
+ await writeOpenVikingHandoffForTurn({
93
+ sessionKey,
94
+ openVikingUserId: inbound.openVikingUserId,
95
+ senderHash: trim(ctx.senderHash),
96
+ log: ctx.log,
97
+ });
65
98
  await runtime.session.recordInboundSession({
66
99
  storePath,
67
100
  sessionKey,
68
101
  ctx: finalized,
69
102
  updateLastRoute: {
70
103
  sessionKey: route.mainSessionKey ?? sessionKey,
71
- channel: "claw-manager-api",
104
+ channel: API_CHANNEL_ID,
72
105
  to: inbound.To,
73
- accountId: "global",
106
+ accountId: API_ACCOUNT_ID,
74
107
  },
75
108
  onRecordError: (error) => ctx.log?.error?.(`recordInboundSession: ${String(error)}`),
76
109
  });
@@ -108,19 +141,158 @@ export async function dispatchApiMessage(ctx) {
108
141
  markDispatchIdle();
109
142
  }
110
143
  return {
111
- channel: "claw-manager-api",
144
+ channel: API_CHANNEL_ID,
112
145
  messageId: trim(ctx.requestId) || randomUUID(),
113
146
  text: replyText,
114
147
  };
115
148
  }
149
+ export function resolveApiQueueRoot() {
150
+ const home = trim(process.env.OPENCLAW_HOME) || trim(process.env.HOME) || process.cwd();
151
+ return path.join(home, ".openclaw", API_CHANNEL_ID);
152
+ }
153
+ export async function monitorApiQueue(ctx) {
154
+ if (!ctx.channelRuntime) {
155
+ throw new Error("ctx.channelRuntime missing");
156
+ }
157
+ if (!ctx.cfg) {
158
+ throw new Error("ctx.cfg missing");
159
+ }
160
+ const root = resolveApiQueueRoot();
161
+ const dirs = {
162
+ root,
163
+ requests: path.join(root, "requests"),
164
+ processing: path.join(root, "processing"),
165
+ responses: path.join(root, "responses"),
166
+ failed: path.join(root, "failed"),
167
+ };
168
+ await Promise.all(Object.values(dirs).map((dir) => fs.mkdir(dir, { recursive: true })));
169
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] claw-manager-api queue monitor started (${root})`);
170
+ ctx.setStatus?.({
171
+ accountId: API_ACCOUNT_ID,
172
+ running: true,
173
+ configured: true,
174
+ lastStartAt: Date.now(),
175
+ lastError: null,
176
+ });
177
+ while (!ctx.abortSignal?.aborted) {
178
+ await processPendingApiRequests(ctx, dirs);
179
+ await sleep(API_QUEUE_POLL_MS, ctx.abortSignal);
180
+ }
181
+ }
182
+ async function processPendingApiRequests(ctx, dirs) {
183
+ let files = [];
184
+ try {
185
+ files = (await fs.readdir(dirs.requests))
186
+ .filter((file) => file.endsWith(".json"))
187
+ .sort();
188
+ }
189
+ catch (error) {
190
+ ctx.log?.error?.(`[${API_ACCOUNT_ID}] read queue failed: ${errorMessage(error)}`);
191
+ return;
192
+ }
193
+ for (const file of files) {
194
+ if (ctx.abortSignal?.aborted) {
195
+ return;
196
+ }
197
+ const requestPath = path.join(dirs.requests, file);
198
+ const processingPath = path.join(dirs.processing, file);
199
+ try {
200
+ await fs.rename(requestPath, processingPath);
201
+ }
202
+ catch {
203
+ continue;
204
+ }
205
+ await processApiRequestFile(ctx, processingPath, path.join(dirs.responses, file), path.join(dirs.failed, file));
206
+ }
207
+ }
208
+ async function processApiRequestFile(ctx, processingPath, responsePath, failedPath) {
209
+ let request = {};
210
+ let requestId = path.basename(processingPath, ".json");
211
+ try {
212
+ request = JSON.parse(await fs.readFile(processingPath, "utf8"));
213
+ requestId = trim(request.requestId) || requestId;
214
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] api request received requestId=${requestId} user=${trim(request.openVikingUserId) || trim(request.openvikingUserId) || "missing"}`);
215
+ ctx.setStatus?.({
216
+ accountId: API_ACCOUNT_ID,
217
+ running: true,
218
+ lastInboundAt: Date.now(),
219
+ lastEventAt: Date.now(),
220
+ });
221
+ const result = await dispatchApiMessage({
222
+ ...request,
223
+ requestId,
224
+ cfg: ctx.cfg,
225
+ channelRuntime: ctx.channelRuntime,
226
+ log: ctx.log,
227
+ });
228
+ await writeQueueResponse(responsePath, {
229
+ ok: true,
230
+ requestId,
231
+ messageId: result.messageId,
232
+ text: result.text,
233
+ finishedAt: new Date().toISOString(),
234
+ });
235
+ ctx.setStatus?.({
236
+ accountId: API_ACCOUNT_ID,
237
+ running: true,
238
+ lastOutboundAt: Date.now(),
239
+ lastEventAt: Date.now(),
240
+ lastError: null,
241
+ });
242
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] api request completed requestId=${requestId} textLen=${result.text.length}`);
243
+ await safeUnlink(processingPath);
244
+ }
245
+ catch (error) {
246
+ const message = errorMessage(error);
247
+ ctx.log?.error?.(`[${API_ACCOUNT_ID}] api request failed requestId=${requestId}: ${message}`);
248
+ await writeQueueResponse(responsePath, {
249
+ ok: false,
250
+ requestId,
251
+ error: message,
252
+ finishedAt: new Date().toISOString(),
253
+ });
254
+ await fs.mkdir(path.dirname(failedPath), { recursive: true });
255
+ await fs.rename(processingPath, failedPath).catch(() => safeUnlink(processingPath));
256
+ ctx.setStatus?.({
257
+ accountId: API_ACCOUNT_ID,
258
+ running: true,
259
+ lastError: message,
260
+ lastEventAt: Date.now(),
261
+ });
262
+ }
263
+ }
264
+ async function writeQueueResponse(responsePath, response) {
265
+ await fs.mkdir(path.dirname(responsePath), { recursive: true });
266
+ const tmpPath = `${responsePath}.tmp-${process.pid}-${Date.now()}`;
267
+ await fs.writeFile(tmpPath, JSON.stringify(response), "utf8");
268
+ await fs.rename(tmpPath, responsePath);
269
+ }
270
+ async function safeUnlink(file) {
271
+ try {
272
+ await fs.unlink(file);
273
+ }
274
+ catch { }
275
+ }
276
+ function sleep(ms, signal) {
277
+ if (signal?.aborted) {
278
+ return Promise.resolve();
279
+ }
280
+ return new Promise((resolve) => {
281
+ const timer = setTimeout(resolve, ms);
282
+ signal?.addEventListener("abort", () => {
283
+ clearTimeout(timer);
284
+ resolve();
285
+ }, { once: true });
286
+ });
287
+ }
116
288
  export const apiChannelPlugin = {
117
- id: "claw-manager-api",
289
+ id: API_CHANNEL_ID,
118
290
  meta: {
119
- id: "claw-manager-api",
120
- label: "claw-manager-api",
291
+ id: API_CHANNEL_ID,
292
+ label: API_CHANNEL_ID,
121
293
  selectionLabel: "Claw Manager API",
122
- docsPath: "/channels/claw-manager-api",
123
- docsLabel: "claw-manager-api",
294
+ docsPath: `/channels/${API_CHANNEL_ID}`,
295
+ docsLabel: API_CHANNEL_ID,
124
296
  blurb: "External API channel.",
125
297
  order: 80,
126
298
  },
@@ -137,11 +309,11 @@ export const apiChannelPlugin = {
137
309
  blockStreaming: true,
138
310
  },
139
311
  config: {
140
- listAccountIds: () => ["global"],
312
+ listAccountIds: () => [API_ACCOUNT_ID],
141
313
  resolveAccount: () => ({}),
142
314
  isConfigured: () => true,
143
315
  describeAccount: () => ({
144
- accountId: "global",
316
+ accountId: API_ACCOUNT_ID,
145
317
  name: "Claw Manager API",
146
318
  enabled: true,
147
319
  configured: true,
@@ -151,14 +323,14 @@ export const apiChannelPlugin = {
151
323
  deliveryMode: "direct",
152
324
  textChunkLimit: 8000,
153
325
  sendText: async (ctx) => ({
154
- channel: "claw-manager-api",
326
+ channel: API_CHANNEL_ID,
155
327
  messageId: randomUUID(),
156
328
  text: ctx.text ?? "",
157
329
  }),
158
330
  },
159
331
  status: {
160
332
  defaultRuntime: {
161
- accountId: "global",
333
+ accountId: API_ACCOUNT_ID,
162
334
  lastError: null,
163
335
  lastInboundAt: null,
164
336
  lastOutboundAt: null,
@@ -172,17 +344,26 @@ export const apiChannelPlugin = {
172
344
  }),
173
345
  buildAccountSnapshot: ({ runtime }) => ({
174
346
  ...runtime,
175
- accountId: "global",
347
+ accountId: API_ACCOUNT_ID,
176
348
  name: "Claw Manager API",
177
349
  enabled: true,
178
350
  configured: true,
179
351
  }),
180
352
  },
181
353
  gateway: {
182
- sendMessage: async (ctx) => dispatchApiMessage(ctx),
354
+ startAccount: async (ctx) => monitorApiQueue(ctx),
355
+ stopAccount: async (ctx) => {
356
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] claw-manager-api queue monitor stopping`);
357
+ },
183
358
  },
184
359
  };
185
360
  function trim(value) {
186
361
  return typeof value === "string" ? value.trim() : "";
187
362
  }
363
+ function errorMessage(error) {
364
+ if (error instanceof Error && error.message.trim()) {
365
+ return error.message;
366
+ }
367
+ return String(error);
368
+ }
188
369
  //# sourceMappingURL=channel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { createHmac } from "node:crypto";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ function trimString(value) {
6
+ return typeof value === "string" ? value.trim() : "";
7
+ }
8
+ function hmacSha256Hex(secret, value) {
9
+ return createHmac("sha256", secret).update(value, "utf8").digest("hex");
10
+ }
11
+ export function resolveApiOpenVikingHandoffStateDir(stateDir) {
12
+ return (trimString(stateDir) ||
13
+ trimString(process.env.OPENCLAW_STATE_DIR) ||
14
+ trimString(process.env.CLAWDBOT_STATE_DIR) ||
15
+ path.join(os.homedir(), ".openclaw"));
16
+ }
17
+ function handoffPath(stateDir) {
18
+ return path.join(resolveApiOpenVikingHandoffStateDir(stateDir), "openviking", "sender-handoff.json");
19
+ }
20
+ function sessionKeyHash(sessionKey, secret) {
21
+ const normalizedSessionKey = trimString(sessionKey);
22
+ const normalizedSecret = trimString(secret);
23
+ if (!normalizedSessionKey || !normalizedSecret) {
24
+ return undefined;
25
+ }
26
+ return hmacSha256Hex(normalizedSecret, normalizedSessionKey).slice(0, 32);
27
+ }
28
+ async function readHandoffFile(filePath) {
29
+ try {
30
+ const raw = await readFile(filePath, "utf8");
31
+ const parsed = JSON.parse(raw);
32
+ return {
33
+ version: 1,
34
+ entries: parsed.entries && typeof parsed.entries === "object" ? parsed.entries : {},
35
+ };
36
+ }
37
+ catch (error) {
38
+ if (error.code === "ENOENT") {
39
+ return { version: 1, entries: {} };
40
+ }
41
+ throw error;
42
+ }
43
+ }
44
+ export async function writeApiOpenVikingHandoff(params) {
45
+ const key = sessionKeyHash(params.sessionKey ?? "", params.secret ?? "");
46
+ const openVikingUserId = trimString(params.openVikingUserId);
47
+ const senderHash = trimString(params.senderHash);
48
+ if (!key || !openVikingUserId || !senderHash) {
49
+ return false;
50
+ }
51
+ const filePath = handoffPath(params.stateDir);
52
+ await mkdir(path.dirname(filePath), { recursive: true });
53
+ const file = await readHandoffFile(filePath);
54
+ file.entries[key] = {
55
+ openVikingUserId,
56
+ senderHash,
57
+ updatedAt: new Date().toISOString(),
58
+ };
59
+ const tempPath = `${filePath}.${process.pid}.tmp`;
60
+ await writeFile(tempPath, `${JSON.stringify(file, null, 2)}\n`, "utf8");
61
+ await rename(tempPath, filePath);
62
+ return true;
63
+ }
64
+ export async function readApiOpenVikingHandoff(params) {
65
+ const key = sessionKeyHash(params.sessionKey ?? "", params.secret ?? "");
66
+ if (!key) {
67
+ return undefined;
68
+ }
69
+ const file = await readHandoffFile(handoffPath(params.stateDir));
70
+ return file.entries[key];
71
+ }
72
+ //# sourceMappingURL=openviking-handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openviking-handoff.js","sourceRoot":"","sources":["../../src/openviking-handoff.ts"],"names":[],"mappings":"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"}
package/index.ts CHANGED
@@ -1,40 +1,16 @@
1
1
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
2
2
 
3
- import { apiChannelPlugin, dispatchApiMessage } from "./src/channel.js";
4
-
5
- export const API_GATEWAY_METHOD = "claw-manager-api.sendMessage";
3
+ import { apiChannelPlugin } from "./src/channel.js";
6
4
 
7
5
  export default {
8
6
  id: "claw-manager-api",
9
7
  name: "Claw Manager API",
10
8
  description: "External API channel for Claw Manager",
11
9
  register(api: OpenClawPluginApi) {
12
- api.registerChannel({ plugin: apiChannelPlugin });
13
- api.registerGatewayMethod(
14
- API_GATEWAY_METHOD,
15
- async ({ params, context, respond }) => {
16
- try {
17
- const result = await dispatchApiMessage({
18
- ...(params ?? {}),
19
- cfg: context.getRuntimeConfig(),
20
- channelRuntime: api.runtime.channel,
21
- });
22
- respond(true, result);
23
- } catch (error) {
24
- respond(false, undefined, {
25
- code: "INVALID_REQUEST",
26
- message: errorMessage(error),
27
- });
28
- }
29
- },
30
- { scope: "operator.admin" },
10
+ api.logger?.info?.(
11
+ `claw-manager-api: register channel mode=${String(api.registrationMode ?? "unknown")} ` +
12
+ `hasGatewayMethod=${String(typeof api.registerGatewayMethod === "function")}`,
31
13
  );
14
+ api.registerChannel({ plugin: apiChannelPlugin });
32
15
  },
33
16
  };
34
-
35
- function errorMessage(error: unknown): string {
36
- if (error instanceof Error && error.message.trim()) {
37
- return error.message;
38
- }
39
- return String(error);
40
- }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "claw-manager-api",
3
- "version": "2026.6.28",
3
+ "version": "2026.6.30",
4
4
  "channels": [
5
5
  "claw-manager-api"
6
6
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claw-manager/openclaw-api-channel",
3
- "version": "2026.6.28",
3
+ "version": "2026.6.30",
4
4
  "description": "OpenClaw API channel for Claw Manager",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/channel.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
2
4
 
3
5
  import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime";
4
6
  import type { ChannelPlugin, OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/core";
5
7
 
8
+ import { writeApiOpenVikingHandoff } from "./openviking-handoff.js";
9
+
6
10
  export type ApiSendMessageParams = {
7
11
  requestId?: string;
8
12
  openVikingUserId?: string;
@@ -39,9 +43,63 @@ export type ApiMsgContext = {
39
43
  type GatewaySendMessageContext = ApiSendMessageParams & {
40
44
  cfg?: OpenClawConfig;
41
45
  channelRuntime?: PluginRuntime["channel"];
42
- log?: { info?: (message: string) => void; error?: (message: string) => void };
46
+ log?: ApiLogSink;
47
+ };
48
+
49
+ type ApiLogSink = {
50
+ debug?: (message: string) => void;
51
+ info?: (message: string) => void;
52
+ warn?: (message: string) => void;
53
+ error?: (message: string) => void;
54
+ };
55
+
56
+ type ApiGatewayStartContext = {
57
+ cfg?: OpenClawConfig;
58
+ channelRuntime?: PluginRuntime["channel"];
59
+ abortSignal?: AbortSignal;
60
+ log?: ApiLogSink;
61
+ setStatus?: (next: Record<string, unknown>) => void;
62
+ };
63
+
64
+ type ApiQueueResponse = {
65
+ ok: boolean;
66
+ requestId: string;
67
+ messageId?: string;
68
+ text?: string;
69
+ error?: string;
70
+ finishedAt: string;
43
71
  };
44
72
 
73
+ const API_CHANNEL_ID = "claw-manager-api";
74
+ const API_ACCOUNT_ID = "global";
75
+ const API_QUEUE_POLL_MS = 200;
76
+
77
+ async function writeOpenVikingHandoffForTurn(params: {
78
+ sessionKey?: string;
79
+ openVikingUserId?: string;
80
+ senderHash?: string;
81
+ log?: ApiLogSink;
82
+ }): Promise<void> {
83
+ const secret = trim(process.env.OPENVIKING_IDENTITY_HASH_SECRET);
84
+ if (!secret) {
85
+ params.log?.debug?.(`[${API_ACCOUNT_ID}] openviking handoff skipped: identity secret missing`);
86
+ return;
87
+ }
88
+ try {
89
+ const wrote = await writeApiOpenVikingHandoff({
90
+ sessionKey: params.sessionKey,
91
+ openVikingUserId: params.openVikingUserId,
92
+ senderHash: params.senderHash,
93
+ secret,
94
+ });
95
+ if (wrote) {
96
+ params.log?.info?.(`[${API_ACCOUNT_ID}] openviking handoff written user=${params.openVikingUserId}`);
97
+ }
98
+ } catch (error) {
99
+ params.log?.warn?.(`[${API_ACCOUNT_ID}] openviking handoff write failed: ${errorMessage(error)}`);
100
+ }
101
+ }
102
+
45
103
  export function buildApiInboundContext(params: ApiSendMessageParams): ApiMsgContext {
46
104
  const message = trim(params.message);
47
105
  const openVikingUserId = trim(params.openVikingUserId) || trim(params.openvikingUserId);
@@ -95,8 +153,8 @@ export async function dispatchApiMessage(ctx: GatewaySendMessageContext): Promis
95
153
  const inbound = buildApiInboundContext(ctx);
96
154
  const route = runtime.routing.resolveAgentRoute({
97
155
  cfg: ctx.cfg,
98
- channel: "claw-manager-api",
99
- accountId: "global",
156
+ channel: API_CHANNEL_ID,
157
+ accountId: API_ACCOUNT_ID,
100
158
  peer: { kind: "direct", id: inbound.To },
101
159
  });
102
160
  const sessionKey = route.sessionKey ?? inbound.SessionKey;
@@ -107,15 +165,21 @@ export async function dispatchApiMessage(ctx: GatewaySendMessageContext): Promis
107
165
  const finalized = runtime.reply.finalizeInboundContext(
108
166
  inbound as Parameters<typeof runtime.reply.finalizeInboundContext>[0],
109
167
  ) as unknown as ApiMsgContext & { CommandAuthorized: boolean };
168
+ await writeOpenVikingHandoffForTurn({
169
+ sessionKey,
170
+ openVikingUserId: inbound.openVikingUserId,
171
+ senderHash: trim(ctx.senderHash),
172
+ log: ctx.log,
173
+ });
110
174
  await runtime.session.recordInboundSession({
111
175
  storePath,
112
176
  sessionKey,
113
177
  ctx: finalized as Parameters<typeof runtime.session.recordInboundSession>[0]["ctx"],
114
178
  updateLastRoute: {
115
179
  sessionKey: route.mainSessionKey ?? sessionKey,
116
- channel: "claw-manager-api",
180
+ channel: API_CHANNEL_ID,
117
181
  to: inbound.To,
118
- accountId: "global",
182
+ accountId: API_ACCOUNT_ID,
119
183
  },
120
184
  onRecordError: (error: unknown) => ctx.log?.error?.(`recordInboundSession: ${String(error)}`),
121
185
  });
@@ -155,20 +219,172 @@ export async function dispatchApiMessage(ctx: GatewaySendMessageContext): Promis
155
219
  markDispatchIdle();
156
220
  }
157
221
  return {
158
- channel: "claw-manager-api",
222
+ channel: API_CHANNEL_ID,
159
223
  messageId: trim(ctx.requestId) || randomUUID(),
160
224
  text: replyText,
161
225
  };
162
226
  }
163
227
 
228
+ export function resolveApiQueueRoot(): string {
229
+ const home = trim(process.env.OPENCLAW_HOME) || trim(process.env.HOME) || process.cwd();
230
+ return path.join(home, ".openclaw", API_CHANNEL_ID);
231
+ }
232
+
233
+ export async function monitorApiQueue(ctx: ApiGatewayStartContext): Promise<void> {
234
+ if (!ctx.channelRuntime) {
235
+ throw new Error("ctx.channelRuntime missing");
236
+ }
237
+ if (!ctx.cfg) {
238
+ throw new Error("ctx.cfg missing");
239
+ }
240
+ const root = resolveApiQueueRoot();
241
+ const dirs = {
242
+ root,
243
+ requests: path.join(root, "requests"),
244
+ processing: path.join(root, "processing"),
245
+ responses: path.join(root, "responses"),
246
+ failed: path.join(root, "failed"),
247
+ };
248
+ await Promise.all(Object.values(dirs).map((dir) => fs.mkdir(dir, { recursive: true })));
249
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] claw-manager-api queue monitor started (${root})`);
250
+ ctx.setStatus?.({
251
+ accountId: API_ACCOUNT_ID,
252
+ running: true,
253
+ configured: true,
254
+ lastStartAt: Date.now(),
255
+ lastError: null,
256
+ });
257
+
258
+ while (!ctx.abortSignal?.aborted) {
259
+ await processPendingApiRequests(ctx, dirs);
260
+ await sleep(API_QUEUE_POLL_MS, ctx.abortSignal);
261
+ }
262
+ }
263
+
264
+ async function processPendingApiRequests(
265
+ ctx: ApiGatewayStartContext,
266
+ dirs: { requests: string; processing: string; responses: string; failed: string },
267
+ ): Promise<void> {
268
+ let files: string[] = [];
269
+ try {
270
+ files = (await fs.readdir(dirs.requests))
271
+ .filter((file) => file.endsWith(".json"))
272
+ .sort();
273
+ } catch (error) {
274
+ ctx.log?.error?.(`[${API_ACCOUNT_ID}] read queue failed: ${errorMessage(error)}`);
275
+ return;
276
+ }
277
+
278
+ for (const file of files) {
279
+ if (ctx.abortSignal?.aborted) {
280
+ return;
281
+ }
282
+ const requestPath = path.join(dirs.requests, file);
283
+ const processingPath = path.join(dirs.processing, file);
284
+ try {
285
+ await fs.rename(requestPath, processingPath);
286
+ } catch {
287
+ continue;
288
+ }
289
+ await processApiRequestFile(ctx, processingPath, path.join(dirs.responses, file), path.join(dirs.failed, file));
290
+ }
291
+ }
292
+
293
+ async function processApiRequestFile(
294
+ ctx: ApiGatewayStartContext,
295
+ processingPath: string,
296
+ responsePath: string,
297
+ failedPath: string,
298
+ ): Promise<void> {
299
+ let request: ApiSendMessageParams = {};
300
+ let requestId = path.basename(processingPath, ".json");
301
+ try {
302
+ request = JSON.parse(await fs.readFile(processingPath, "utf8")) as ApiSendMessageParams;
303
+ requestId = trim(request.requestId) || requestId;
304
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] api request received requestId=${requestId} user=${trim(request.openVikingUserId) || trim(request.openvikingUserId) || "missing"}`);
305
+ ctx.setStatus?.({
306
+ accountId: API_ACCOUNT_ID,
307
+ running: true,
308
+ lastInboundAt: Date.now(),
309
+ lastEventAt: Date.now(),
310
+ });
311
+ const result = await dispatchApiMessage({
312
+ ...request,
313
+ requestId,
314
+ cfg: ctx.cfg,
315
+ channelRuntime: ctx.channelRuntime,
316
+ log: ctx.log,
317
+ });
318
+ await writeQueueResponse(responsePath, {
319
+ ok: true,
320
+ requestId,
321
+ messageId: result.messageId,
322
+ text: result.text,
323
+ finishedAt: new Date().toISOString(),
324
+ });
325
+ ctx.setStatus?.({
326
+ accountId: API_ACCOUNT_ID,
327
+ running: true,
328
+ lastOutboundAt: Date.now(),
329
+ lastEventAt: Date.now(),
330
+ lastError: null,
331
+ });
332
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] api request completed requestId=${requestId} textLen=${result.text.length}`);
333
+ await safeUnlink(processingPath);
334
+ } catch (error) {
335
+ const message = errorMessage(error);
336
+ ctx.log?.error?.(`[${API_ACCOUNT_ID}] api request failed requestId=${requestId}: ${message}`);
337
+ await writeQueueResponse(responsePath, {
338
+ ok: false,
339
+ requestId,
340
+ error: message,
341
+ finishedAt: new Date().toISOString(),
342
+ });
343
+ await fs.mkdir(path.dirname(failedPath), { recursive: true });
344
+ await fs.rename(processingPath, failedPath).catch(() => safeUnlink(processingPath));
345
+ ctx.setStatus?.({
346
+ accountId: API_ACCOUNT_ID,
347
+ running: true,
348
+ lastError: message,
349
+ lastEventAt: Date.now(),
350
+ });
351
+ }
352
+ }
353
+
354
+ async function writeQueueResponse(responsePath: string, response: ApiQueueResponse): Promise<void> {
355
+ await fs.mkdir(path.dirname(responsePath), { recursive: true });
356
+ const tmpPath = `${responsePath}.tmp-${process.pid}-${Date.now()}`;
357
+ await fs.writeFile(tmpPath, JSON.stringify(response), "utf8");
358
+ await fs.rename(tmpPath, responsePath);
359
+ }
360
+
361
+ async function safeUnlink(file: string): Promise<void> {
362
+ try {
363
+ await fs.unlink(file);
364
+ } catch {}
365
+ }
366
+
367
+ function sleep(ms: number, signal?: AbortSignal): Promise<void> {
368
+ if (signal?.aborted) {
369
+ return Promise.resolve();
370
+ }
371
+ return new Promise((resolve) => {
372
+ const timer = setTimeout(resolve, ms);
373
+ signal?.addEventListener("abort", () => {
374
+ clearTimeout(timer);
375
+ resolve();
376
+ }, { once: true });
377
+ });
378
+ }
379
+
164
380
  export const apiChannelPlugin: ChannelPlugin<Record<string, never>> = {
165
- id: "claw-manager-api",
381
+ id: API_CHANNEL_ID,
166
382
  meta: {
167
- id: "claw-manager-api",
168
- label: "claw-manager-api",
383
+ id: API_CHANNEL_ID,
384
+ label: API_CHANNEL_ID,
169
385
  selectionLabel: "Claw Manager API",
170
- docsPath: "/channels/claw-manager-api",
171
- docsLabel: "claw-manager-api",
386
+ docsPath: `/channels/${API_CHANNEL_ID}`,
387
+ docsLabel: API_CHANNEL_ID,
172
388
  blurb: "External API channel.",
173
389
  order: 80,
174
390
  },
@@ -185,11 +401,11 @@ export const apiChannelPlugin: ChannelPlugin<Record<string, never>> = {
185
401
  blockStreaming: true,
186
402
  },
187
403
  config: {
188
- listAccountIds: () => ["global"],
404
+ listAccountIds: () => [API_ACCOUNT_ID],
189
405
  resolveAccount: () => ({}),
190
406
  isConfigured: () => true,
191
407
  describeAccount: () => ({
192
- accountId: "global",
408
+ accountId: API_ACCOUNT_ID,
193
409
  name: "Claw Manager API",
194
410
  enabled: true,
195
411
  configured: true,
@@ -199,14 +415,14 @@ export const apiChannelPlugin: ChannelPlugin<Record<string, never>> = {
199
415
  deliveryMode: "direct",
200
416
  textChunkLimit: 8000,
201
417
  sendText: async (ctx: { text?: string }) => ({
202
- channel: "claw-manager-api",
418
+ channel: API_CHANNEL_ID,
203
419
  messageId: randomUUID(),
204
420
  text: ctx.text ?? "",
205
421
  }),
206
422
  },
207
423
  status: {
208
424
  defaultRuntime: {
209
- accountId: "global",
425
+ accountId: API_ACCOUNT_ID,
210
426
  lastError: null,
211
427
  lastInboundAt: null,
212
428
  lastOutboundAt: null,
@@ -220,17 +436,27 @@ export const apiChannelPlugin: ChannelPlugin<Record<string, never>> = {
220
436
  }),
221
437
  buildAccountSnapshot: ({ runtime }: { runtime: Record<string, unknown> }) => ({
222
438
  ...runtime,
223
- accountId: "global",
439
+ accountId: API_ACCOUNT_ID,
224
440
  name: "Claw Manager API",
225
441
  enabled: true,
226
442
  configured: true,
227
443
  }),
228
444
  },
229
445
  gateway: {
230
- sendMessage: async (ctx: GatewaySendMessageContext) => dispatchApiMessage(ctx),
446
+ startAccount: async (ctx: ApiGatewayStartContext) => monitorApiQueue(ctx),
447
+ stopAccount: async (ctx: { log?: ApiLogSink }) => {
448
+ ctx.log?.info?.(`[${API_ACCOUNT_ID}] claw-manager-api queue monitor stopping`);
449
+ },
231
450
  },
232
451
  } as unknown as ChannelPlugin<Record<string, never>>;
233
452
 
234
453
  function trim(value: unknown): string {
235
454
  return typeof value === "string" ? value.trim() : "";
236
455
  }
456
+
457
+ function errorMessage(error: unknown): string {
458
+ if (error instanceof Error && error.message.trim()) {
459
+ return error.message;
460
+ }
461
+ return String(error);
462
+ }
@@ -0,0 +1,102 @@
1
+ import { createHmac } from "node:crypto";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+
6
+ export type ApiOpenVikingHandoff = {
7
+ openVikingUserId: string;
8
+ senderHash: string;
9
+ updatedAt: string;
10
+ };
11
+
12
+ type HandoffFile = {
13
+ version: 1;
14
+ entries: Record<string, ApiOpenVikingHandoff>;
15
+ };
16
+
17
+ function trimString(value: unknown): string {
18
+ return typeof value === "string" ? value.trim() : "";
19
+ }
20
+
21
+ function hmacSha256Hex(secret: string, value: string): string {
22
+ return createHmac("sha256", secret).update(value, "utf8").digest("hex");
23
+ }
24
+
25
+ export function resolveApiOpenVikingHandoffStateDir(stateDir?: string): string {
26
+ return (
27
+ trimString(stateDir) ||
28
+ trimString(process.env.OPENCLAW_STATE_DIR) ||
29
+ trimString(process.env.CLAWDBOT_STATE_DIR) ||
30
+ path.join(os.homedir(), ".openclaw")
31
+ );
32
+ }
33
+
34
+ function handoffPath(stateDir?: string): string {
35
+ return path.join(resolveApiOpenVikingHandoffStateDir(stateDir), "openviking", "sender-handoff.json");
36
+ }
37
+
38
+ function sessionKeyHash(sessionKey: string, secret: string): string | undefined {
39
+ const normalizedSessionKey = trimString(sessionKey);
40
+ const normalizedSecret = trimString(secret);
41
+ if (!normalizedSessionKey || !normalizedSecret) {
42
+ return undefined;
43
+ }
44
+ return hmacSha256Hex(normalizedSecret, normalizedSessionKey).slice(0, 32);
45
+ }
46
+
47
+ async function readHandoffFile(filePath: string): Promise<HandoffFile> {
48
+ try {
49
+ const raw = await readFile(filePath, "utf8");
50
+ const parsed = JSON.parse(raw) as Partial<HandoffFile>;
51
+ return {
52
+ version: 1,
53
+ entries: parsed.entries && typeof parsed.entries === "object" ? parsed.entries : {},
54
+ };
55
+ } catch (error) {
56
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
57
+ return { version: 1, entries: {} };
58
+ }
59
+ throw error;
60
+ }
61
+ }
62
+
63
+ export async function writeApiOpenVikingHandoff(params: {
64
+ stateDir?: string;
65
+ sessionKey?: string;
66
+ openVikingUserId?: string;
67
+ senderHash?: string;
68
+ secret?: string;
69
+ }): Promise<boolean> {
70
+ const key = sessionKeyHash(params.sessionKey ?? "", params.secret ?? "");
71
+ const openVikingUserId = trimString(params.openVikingUserId);
72
+ const senderHash = trimString(params.senderHash);
73
+ if (!key || !openVikingUserId || !senderHash) {
74
+ return false;
75
+ }
76
+
77
+ const filePath = handoffPath(params.stateDir);
78
+ await mkdir(path.dirname(filePath), { recursive: true });
79
+ const file = await readHandoffFile(filePath);
80
+ file.entries[key] = {
81
+ openVikingUserId,
82
+ senderHash,
83
+ updatedAt: new Date().toISOString(),
84
+ };
85
+ const tempPath = `${filePath}.${process.pid}.tmp`;
86
+ await writeFile(tempPath, `${JSON.stringify(file, null, 2)}\n`, "utf8");
87
+ await rename(tempPath, filePath);
88
+ return true;
89
+ }
90
+
91
+ export async function readApiOpenVikingHandoff(params: {
92
+ stateDir?: string;
93
+ sessionKey?: string;
94
+ secret?: string;
95
+ }): Promise<ApiOpenVikingHandoff | undefined> {
96
+ const key = sessionKeyHash(params.sessionKey ?? "", params.secret ?? "");
97
+ if (!key) {
98
+ return undefined;
99
+ }
100
+ const file = await readHandoffFile(handoffPath(params.stateDir));
101
+ return file.entries[key];
102
+ }