@openclaw/raft 2026.7.1 → 2026.7.2-beta.2

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.
@@ -1,29 +1,22 @@
1
1
  import { a as resolveRaftAccount, i as resolveDefaultRaftAccountId, n as RAFT_CHANNEL_ID, r as listRaftAccountIds, t as raftSetupPlugin } from "./setup-BTG_4kEW.js";
2
2
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
3
3
  import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
4
- import { buildBaseChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
5
4
  import { detectBinary } from "openclaw/plugin-sdk/setup-tools";
6
- import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
5
+ import { buildBaseChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
6
+ import { buildChannelConfigSchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
7
7
  import { z } from "zod";
8
8
  import { spawn } from "node:child_process";
9
- import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
9
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
10
10
  import { createServer } from "node:http";
11
11
  import { keepHttpServerTaskAlive, waitUntilAbort } from "openclaw/plugin-sdk/channel-outbound";
12
12
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
13
- import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
14
- //#region extensions/raft/src/config-schema.ts
15
- const RaftAccountSchema = z.object({
13
+ import { createChannelReplayGuard } from "openclaw/plugin-sdk/persistent-dedupe";
14
+ import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
15
+ const raftChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(z.object({
16
16
  name: z.string().optional(),
17
17
  enabled: z.boolean().optional(),
18
18
  profile: z.string().min(1).optional()
19
- }).strict();
20
- const raftChannelConfigSchema = buildChannelConfigSchema(z.object({
21
- name: z.string().optional(),
22
- enabled: z.boolean().optional(),
23
- profile: z.string().min(1).optional(),
24
- defaultAccount: z.string().optional(),
25
- accounts: z.record(z.string(), RaftAccountSchema).optional()
26
- }).strict());
19
+ }).strict()));
27
20
  //#endregion
28
21
  //#region extensions/raft/src/inbound.ts
29
22
  const WAKE_TEXT = "Raft wake hint received. Check Raft for pending messages, then reply through the Raft CLI.";
@@ -137,6 +130,21 @@ const WAKE_EVENT_ID_FIELDS = [
137
130
  "wake_id",
138
131
  "id"
139
132
  ];
133
+ function createRaftWakeReplayGuard(params) {
134
+ return createChannelReplayGuard({
135
+ dedupe: {
136
+ ttlMs: WAKE_DEDUPE_TTL_MS,
137
+ memoryMaxSize: WAKE_DEDUPE_MEMORY_MAX_SIZE,
138
+ pluginId: RAFT_CHANNEL_ID,
139
+ namespacePrefix: "raft-wake-dedupe",
140
+ stateMaxEntries: WAKE_DEDUPE_STATE_MAX_ENTRIES,
141
+ ...params?.env ? { env: params.env } : {},
142
+ ...params?.onDiskError ? { onDiskError: params.onDiskError } : {}
143
+ },
144
+ buildReplayKey: (event) => event.key,
145
+ namespace: (event) => event.accountId
146
+ });
147
+ }
140
148
  var WakeRequestError = class extends Error {
141
149
  constructor(statusCode, message) {
142
150
  super(message);
@@ -168,9 +176,7 @@ function spawnRaftBridge(params) {
168
176
  function hasMatchingToken(request, expected) {
169
177
  const value = request.headers[WAKE_TOKEN_HEADER];
170
178
  if (typeof value !== "string") return false;
171
- const received = Buffer.from(value);
172
- const required = Buffer.from(expected);
173
- return received.length === required.length && timingSafeEqual(received, required);
179
+ return safeEqualSecret(value, expected);
174
180
  }
175
181
  async function readWakePayload(request) {
176
182
  const chunks = [];
@@ -254,16 +260,9 @@ async function startRaftGatewayAccount(ctx, deps = {}) {
254
260
  if (!profile) throw new Error(`Raft account "${ctx.accountId}" is missing a CLI profile.`);
255
261
  if (!ctx.channelRuntime) throw new Error("Raft requires OpenClaw channel runtime support. Update OpenClaw and retry.");
256
262
  const wakeQueue = new KeyedAsyncQueue();
257
- const wakeDedupe = deps.wakeDedupe ?? createClaimableDedupe({
258
- ttlMs: WAKE_DEDUPE_TTL_MS,
259
- memoryMaxSize: WAKE_DEDUPE_MEMORY_MAX_SIZE,
260
- pluginId: "raft",
261
- namespacePrefix: "raft-wake-dedupe",
262
- stateMaxEntries: WAKE_DEDUPE_STATE_MAX_ENTRIES,
263
- onDiskError: (error) => {
264
- ctx.log?.warn?.(`Raft wake dedupe storage failed: ${String(error)}`);
265
- }
266
- });
263
+ const wakeDedupe = deps.wakeDedupe ?? createRaftWakeReplayGuard({ onDiskError: (error) => {
264
+ ctx.log?.warn?.(`Raft wake dedupe storage failed: ${String(error)}`);
265
+ } });
267
266
  const token = (deps.createToken ?? createToken)();
268
267
  const runtimeSession = randomUUID();
269
268
  const sockets = /* @__PURE__ */ new Set();
@@ -306,22 +305,17 @@ async function startRaftGatewayAccount(ctx, deps = {}) {
306
305
  if (!dedupeKey) throw new WakeRequestError(400, "Wake payload must include a stable event identity.");
307
306
  const dispatched = await wakeQueue.enqueue(ctx.accountId, async () => {
308
307
  if (ctx.abortSignal?.aborted) throw new WakeRequestError(503, "Raft Gateway is stopping.");
309
- const claim = await wakeDedupe.claim(dedupeKey, { namespace: ctx.accountId });
310
- if (claim.kind === "duplicate") return false;
311
- if (claim.kind === "inflight") {
312
- if (await claim.pending) return false;
313
- throw new WakeRequestError(503, "Raft wake delivery is retrying.");
314
- }
315
- try {
308
+ const result = await wakeDedupe.processGuarded({
309
+ accountId: ctx.accountId,
310
+ key: dedupeKey
311
+ }, async () => {
316
312
  await dispatchRaftWake({ ctx });
317
- } catch (error) {
318
- wakeDedupe.release(dedupeKey, {
319
- namespace: ctx.accountId,
320
- error
321
- });
322
- throw error;
313
+ });
314
+ if (result.kind === "duplicate") return false;
315
+ if (result.kind === "inflight") {
316
+ if (await result.pending) return false;
317
+ throw new WakeRequestError(503, "Raft wake delivery is retrying.");
323
318
  }
324
- await wakeDedupe.commit(dedupeKey, { namespace: ctx.accountId });
325
319
  return true;
326
320
  });
327
321
  sendJson(response, 202, {
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@openclaw/raft",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/raft",
9
- "version": "2026.7.1",
9
+ "version": "2026.7.2-beta.2",
10
10
  "dependencies": {
11
11
  "zod": "4.4.3"
12
12
  },
13
13
  "peerDependencies": {
14
- "openclaw": ">=2026.7.1"
14
+ "openclaw": ">=2026.7.2-beta.2"
15
15
  },
16
16
  "peerDependenciesMeta": {
17
17
  "openclaw": {
@@ -24,9 +24,6 @@
24
24
  "type": "string",
25
25
  "minLength": 1
26
26
  },
27
- "defaultAccount": {
28
- "type": "string"
29
- },
30
27
  "accounts": {
31
28
  "type": "object",
32
29
  "propertyNames": {
@@ -48,6 +45,9 @@
48
45
  },
49
46
  "additionalProperties": false
50
47
  }
48
+ },
49
+ "defaultAccount": {
50
+ "type": "string"
51
51
  }
52
52
  },
53
53
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/raft",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "description": "OpenClaw Raft channel plugin for Raft CLI wake bridges.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,7 +11,7 @@
11
11
  "zod": "4.4.3"
12
12
  },
13
13
  "peerDependencies": {
14
- "openclaw": ">=2026.7.1"
14
+ "openclaw": ">=2026.7.2-beta.2"
15
15
  },
16
16
  "peerDependenciesMeta": {
17
17
  "openclaw": {
@@ -29,10 +29,10 @@
29
29
  "minHostVersion": ">=2026.6.8"
30
30
  },
31
31
  "compat": {
32
- "pluginApi": ">=2026.7.1"
32
+ "pluginApi": ">=2026.7.2-beta.2"
33
33
  },
34
34
  "build": {
35
- "openclawVersion": "2026.7.1"
35
+ "openclawVersion": "2026.7.2-beta.2"
36
36
  },
37
37
  "channel": {
38
38
  "id": "raft",