mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-24 10:11:14 +00:00
* feat(channels): bundle Reef guarded claw-to-claw channel Moves the Reef channel extension from openclaw/reef into the bundled extensions tree with the wire protocol vendored under extensions/reef/protocol. Includes channelConfigs manifest metadata, runtime status reporting via setStatus/buildAccountSnapshot, abort-aware inbox shutdown, monotonic device-auth timestamps, and immutable-model guard admission (dated snapshots plus documented gpt-5.6 ids). * fix(reef): pin zod exactly per dependency pin guard * style(reef): satisfy extension lint gates Curly braces in vendored protocol, explicit type re-exports, typed catch callbacks, Object.assign over map-spread, abort-aware loop restructure. * fix(reef): CI gates — knip workspace, boundary tsconfig, facade imports, cycle break, contract baselines - knip: reef workspace project includes vendored protocol/ (owns noble deps) - tsconfig: conform to canonical extension package-boundary include/exclude; add @openclaw/plugin-sdk devDependency - imports: channel-inbound/channel-outbound facades instead of deprecated subpaths - protocol: home ReplayStore contract in envelope.ts to break the envelope<->replay type cycle - baselines: reef in unguarded runtime-api list; regenerate bundled channel config metadata * feat(reef): plugin catalog cover art, channel label, changelog revert * fix(reef): drop unused error-class exports, register lint suppression * fix(reef): knip entry surface for vendored protocol, explicit WebSocketLike export
94 lines
3.0 KiB
TypeScript
94 lines
3.0 KiB
TypeScript
import type { CompletedReplay, MessageBody, ReplayClaim, ReplayStore } from "./envelope.js";
|
|
import type { SignedReceipt } from "./receipts.js";
|
|
|
|
export type { CompletedReplay, ReplayClaim, ReplayStore } from "./envelope.js";
|
|
|
|
interface ReplayRecord {
|
|
envelopeHash: string;
|
|
state: "available" | "in_flight" | "completed" | "consumed";
|
|
receipt?: SignedReceipt;
|
|
body?: MessageBody;
|
|
}
|
|
|
|
export class MemoryReplayStore implements ReplayStore {
|
|
readonly #bindings = new Map<string, ReplayRecord>();
|
|
|
|
async claim(peer: string, id: string, envelopeHash: string): Promise<ReplayClaim> {
|
|
const key = replayKey(peer, id);
|
|
const existing = this.#bindings.get(key);
|
|
if (existing === undefined) {
|
|
this.#bindings.set(key, { envelopeHash, state: "in_flight" });
|
|
return "new";
|
|
}
|
|
if (existing.envelopeHash !== envelopeHash) {
|
|
return "mismatch";
|
|
}
|
|
if (existing.state === "completed" || existing.state === "consumed") {
|
|
return "duplicate";
|
|
}
|
|
if (existing.state === "in_flight") {
|
|
return "in_flight";
|
|
}
|
|
existing.state = "in_flight";
|
|
return "new";
|
|
}
|
|
|
|
async complete(
|
|
peer: string,
|
|
id: string,
|
|
receipt: SignedReceipt,
|
|
body?: MessageBody,
|
|
): Promise<void> {
|
|
const existing = this.#bindings.get(replayKey(peer, id));
|
|
if (existing?.state !== "in_flight") {
|
|
throw new Error("replay claim is not in flight");
|
|
}
|
|
if (receipt.id !== id) {
|
|
throw new Error("receipt id does not match replay claim");
|
|
}
|
|
validateCompletion(receipt, body);
|
|
existing.state = "completed";
|
|
existing.receipt = structuredClone(receipt);
|
|
if (body !== undefined) {
|
|
existing.body = structuredClone(body);
|
|
}
|
|
}
|
|
|
|
async consume(peer: string, id: string): Promise<void> {
|
|
const existing = this.#bindings.get(replayKey(peer, id));
|
|
if (existing?.state !== "in_flight") {
|
|
throw new Error("replay claim is not in flight");
|
|
}
|
|
existing.state = "consumed";
|
|
delete existing.receipt;
|
|
delete existing.body;
|
|
}
|
|
|
|
async release(peer: string, id: string): Promise<void> {
|
|
const existing = this.#bindings.get(replayKey(peer, id));
|
|
if (existing?.state === "in_flight") {
|
|
existing.state = "available";
|
|
}
|
|
}
|
|
|
|
async completed(peer: string, id: string): Promise<CompletedReplay | undefined> {
|
|
const existing = this.#bindings.get(replayKey(peer, id));
|
|
if (existing?.state !== "completed" || existing.receipt === undefined) {
|
|
return undefined;
|
|
}
|
|
return existing.body === undefined
|
|
? { receipt: structuredClone(existing.receipt) }
|
|
: { receipt: structuredClone(existing.receipt), body: structuredClone(existing.body) };
|
|
}
|
|
}
|
|
|
|
function replayKey(peer: string, id: string): string {
|
|
return `${peer}\n${id}`;
|
|
}
|
|
|
|
function validateCompletion(receipt: SignedReceipt, body: MessageBody | undefined): void {
|
|
if ((receipt.status === "accepted") !== (body !== undefined)) {
|
|
throw new Error("accepted replay completion requires body; rejected completion forbids body");
|
|
}
|
|
}
|