Files
openclaw/extensions/reef/protocol/ulid.ts
Peter Steinberger 882b2fe900 feat(channels): bundle Reef guarded claw-to-claw channel (#106232)
* 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
2026-07-13 05:17:44 -07:00

66 lines
1.7 KiB
TypeScript

import { randomBytes } from "@noble/hashes/utils.js";
const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
export interface UlidFactoryOptions {
clock?: () => number;
rng?: (length: number) => Uint8Array;
}
export function createMonotonicUlidFactory(options: UlidFactoryOptions = {}): () => string {
const clock = options.clock ?? Date.now;
const rng = options.rng ?? randomBytes;
let lastTime = -1;
let randomness = new Uint8Array(10);
return () => {
const now = Math.floor(clock());
if (!Number.isSafeInteger(now) || now < 0 || now > 0xffffffffffff) {
throw new Error("invalid ULID clock");
}
if (now > lastTime) {
const generated = rng(10);
if (generated.length !== 10) {
throw new Error("invalid ULID rng");
}
randomness = generated.slice();
lastTime = now;
} else {
increment(randomness);
}
return encodeTime(lastTime) + encodeRandom(randomness);
};
}
function increment(value: Uint8Array): void {
for (let index = value.length - 1; index >= 0; index--) {
value[index] = (value[index]! + 1) & 0xff;
if (value[index] !== 0) {
return;
}
}
throw new Error("ULID monotonic overflow");
}
function encodeTime(time: number): string {
let value = BigInt(time);
let output = "";
for (let index = 0; index < 10; index++) {
output = CROCKFORD[Number(value & 31n)]! + output;
value >>= 5n;
}
return output;
}
function encodeRandom(bytes: Uint8Array): string {
let value = 0n;
for (const byte of bytes) {
value = (value << 8n) | BigInt(byte);
}
let output = "";
for (let index = 0; index < 16; index++) {
output = CROCKFORD[Number(value & 31n)]! + output;
value >>= 5n;
}
return output;
}