mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-14 10:46:05 +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
117 lines
3.6 KiB
TypeScript
117 lines
3.6 KiB
TypeScript
const encoder = new TextEncoder();
|
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
|
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
const base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
export function utf8(value: string): Uint8Array {
|
|
return encoder.encode(value);
|
|
}
|
|
|
|
export function decodeUtf8(value: Uint8Array): string {
|
|
return decoder.decode(value);
|
|
}
|
|
|
|
export function base64url(value: Uint8Array): string {
|
|
let output = "";
|
|
for (let index = 0; index < value.length; index += 3) {
|
|
const a = value[index] ?? 0;
|
|
const b = value[index + 1] ?? 0;
|
|
const c = value[index + 2] ?? 0;
|
|
const bits = (a << 16) | (b << 8) | c;
|
|
output += alphabet[(bits >>> 18) & 63];
|
|
output += alphabet[(bits >>> 12) & 63];
|
|
if (index + 1 < value.length) {
|
|
output += alphabet[(bits >>> 6) & 63];
|
|
}
|
|
if (index + 2 < value.length) {
|
|
output += alphabet[bits & 63];
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
export function fromBase64url(value: string): Uint8Array {
|
|
if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) {
|
|
throw new Error("invalid base64url");
|
|
}
|
|
const output = new Uint8Array(Math.floor((value.length * 6) / 8));
|
|
let bits = 0;
|
|
let count = 0;
|
|
let offset = 0;
|
|
for (const character of value) {
|
|
const digit = alphabet.indexOf(character);
|
|
bits = (bits << 6) | digit;
|
|
count += 6;
|
|
if (count >= 8) {
|
|
count -= 8;
|
|
output[offset++] = (bits >>> count) & 0xff;
|
|
}
|
|
}
|
|
if (count > 0 && (bits & ((1 << count) - 1)) !== 0) {
|
|
throw new Error("invalid base64url padding");
|
|
}
|
|
return output;
|
|
}
|
|
|
|
export function base64(value: Uint8Array): string {
|
|
let output = "";
|
|
for (let index = 0; index < value.length; index += 3) {
|
|
const a = value[index] ?? 0;
|
|
const b = value[index + 1] ?? 0;
|
|
const c = value[index + 2] ?? 0;
|
|
const bits = (a << 16) | (b << 8) | c;
|
|
output += base64Alphabet[(bits >>> 18) & 63];
|
|
output += base64Alphabet[(bits >>> 12) & 63];
|
|
output += index + 1 < value.length ? base64Alphabet[(bits >>> 6) & 63] : "=";
|
|
output += index + 2 < value.length ? base64Alphabet[bits & 63] : "=";
|
|
}
|
|
return output;
|
|
}
|
|
|
|
export function fromBase64(value: string): Uint8Array {
|
|
if (
|
|
value.length % 4 !== 0 ||
|
|
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)
|
|
) {
|
|
throw new Error("invalid base64");
|
|
}
|
|
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
const output = new Uint8Array((value.length / 4) * 3 - padding);
|
|
let offset = 0;
|
|
for (let index = 0; index < value.length; index += 4) {
|
|
const digits = [value[index]!, value[index + 1]!, value[index + 2]!, value[index + 3]!].map(
|
|
(character) => (character === "=" ? 0 : base64Alphabet.indexOf(character)),
|
|
);
|
|
const bits = (digits[0]! << 18) | (digits[1]! << 12) | (digits[2]! << 6) | digits[3]!;
|
|
if (offset < output.length) {
|
|
output[offset++] = (bits >>> 16) & 0xff;
|
|
}
|
|
if (offset < output.length) {
|
|
output[offset++] = (bits >>> 8) & 0xff;
|
|
}
|
|
if (offset < output.length) {
|
|
output[offset++] = bits & 0xff;
|
|
}
|
|
}
|
|
if (base64(output) !== value) {
|
|
throw new Error("non-canonical base64");
|
|
}
|
|
return output;
|
|
}
|
|
|
|
export function hex(value: Uint8Array): string {
|
|
return Array.from(value, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
}
|
|
|
|
export function equalBytes(a: Uint8Array, b: Uint8Array): boolean {
|
|
if (a.length !== b.length) {
|
|
return false;
|
|
}
|
|
let difference = 0;
|
|
for (let index = 0; index < a.length; index++) {
|
|
difference |= a[index]! ^ b[index]!;
|
|
}
|
|
return difference === 0;
|
|
}
|