144 lines
5.5 KiB
TypeScript
144 lines
5.5 KiB
TypeScript
// Low-level protobuf wire-format primitives for the Cursor Agent codec, extracted
|
|
// verbatim from ../cursorAgentProtobuf.ts (god-file decomposition). Pure and
|
|
// dependency-free (Buffer only): varint/tag/length-delimited encode+decode and the
|
|
// generic field walker. Framing, the value codec, and the message encoders/decoders
|
|
// all build on this layer. Nothing here was part of the module's public API, so the
|
|
// host imports these back internally (no re-export).
|
|
|
|
// ─── Wire-type constants ───────────────────────────────────────────────────
|
|
|
|
export const WT_VARINT = 0;
|
|
export const WT_LEN = 2;
|
|
|
|
// ─── Primitive encoders ────────────────────────────────────────────────────
|
|
|
|
export function encodeVarint(value: number | bigint): Buffer {
|
|
let v = typeof value === "bigint" ? value : BigInt(value);
|
|
const bytes: number[] = [];
|
|
while (v > 0x7fn) {
|
|
bytes.push(Number(v & 0x7fn) | 0x80);
|
|
v >>= 7n;
|
|
}
|
|
bytes.push(Number(v));
|
|
return Buffer.from(bytes);
|
|
}
|
|
|
|
export function encodeTag(fieldNumber: number, wireType: number): Buffer {
|
|
return encodeVarint((fieldNumber << 3) | wireType);
|
|
}
|
|
|
|
export function encodeBytes(fieldNumber: number, value: Buffer | Uint8Array): Buffer {
|
|
const buf = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
return Buffer.concat([encodeTag(fieldNumber, WT_LEN), encodeVarint(buf.length), buf]);
|
|
}
|
|
|
|
export function encodeString(fieldNumber: number, value: string): Buffer {
|
|
return encodeBytes(fieldNumber, Buffer.from(value, "utf8"));
|
|
}
|
|
|
|
export function encodeMessage(fieldNumber: number, parts: Buffer[]): Buffer {
|
|
const inner = Buffer.concat(parts);
|
|
return Buffer.concat([encodeTag(fieldNumber, WT_LEN), encodeVarint(inner.length), inner]);
|
|
}
|
|
|
|
export function encodeUInt32Field(fieldNumber: number, value: number): Buffer {
|
|
return Buffer.concat([encodeTag(fieldNumber, WT_VARINT), encodeVarint(value)]);
|
|
}
|
|
|
|
export function encodeBoolField(fieldNumber: number, value: boolean): Buffer {
|
|
return Buffer.concat([encodeTag(fieldNumber, WT_VARINT), encodeVarint(value ? 1 : 0)]);
|
|
}
|
|
|
|
export function encodeDoubleField(fieldNumber: number, value: number): Buffer {
|
|
// wire type 1 = 64-bit fixed (double)
|
|
const buf = Buffer.alloc(8);
|
|
buf.writeDoubleLE(value, 0);
|
|
return Buffer.concat([encodeTag(fieldNumber, 1), buf]);
|
|
}
|
|
|
|
// ─── Primitive decoders ────────────────────────────────────────────────────
|
|
|
|
export function decodeVarint(buf: Buffer, offset: number): [bigint, number] {
|
|
let result = 0n;
|
|
let shift = 0n;
|
|
let pos = offset;
|
|
while (pos < buf.length) {
|
|
const byte = buf[pos++];
|
|
result |= BigInt(byte & 0x7f) << shift;
|
|
if ((byte & 0x80) === 0) return [result, pos];
|
|
shift += 7n;
|
|
}
|
|
throw new Error("varint truncated");
|
|
}
|
|
|
|
export type Field =
|
|
| { fieldNumber: number; wireType: 0; varint: bigint }
|
|
| { fieldNumber: number; wireType: 2; bytes: Buffer };
|
|
|
|
/**
|
|
* Validate a length-delimited field's declared length against the bytes that
|
|
* actually remain in the buffer. Cursor's frames are well-formed, but a
|
|
* corrupted or hostile upstream could declare a length that overruns the
|
|
* buffer; without this guard `Buffer.subarray` silently clamps to EOF and a
|
|
* truncated tool argument (or any nested message) is decoded as empty/partial
|
|
* data instead of being recognized as malformed. Throwing lets the caller —
|
|
* `processFrame`, wrapped in driveH2's per-frame try/catch — skip the bad
|
|
* frame rather than act on corrupted fields. Also rejects absurd lengths that
|
|
* would not fit a JS safe integer.
|
|
*/
|
|
export function checkedLen(len: bigint, pos: number, buf: Buffer): number {
|
|
if (len < 0n || len > BigInt(buf.length - pos)) {
|
|
throw new Error(
|
|
`length-delimited field overruns buffer (len=${len}, remaining=${buf.length - pos})`
|
|
);
|
|
}
|
|
return Number(len);
|
|
}
|
|
|
|
export function decodeFields(buf: Buffer): Field[] {
|
|
const fields: Field[] = [];
|
|
let pos = 0;
|
|
while (pos < buf.length) {
|
|
const [tag, np] = decodeVarint(buf, pos);
|
|
pos = np;
|
|
const fieldNumber = Number(tag >> 3n);
|
|
const wireType = Number(tag & 0x7n);
|
|
if (wireType === WT_VARINT) {
|
|
const [v, np2] = decodeVarint(buf, pos);
|
|
pos = np2;
|
|
fields.push({ fieldNumber, wireType: 0, varint: v });
|
|
} else if (wireType === WT_LEN) {
|
|
const [len, np2] = decodeVarint(buf, pos);
|
|
pos = np2;
|
|
const lenN = checkedLen(len, pos, buf);
|
|
fields.push({ fieldNumber, wireType: 2, bytes: buf.subarray(pos, pos + lenN) });
|
|
pos += lenN;
|
|
} else if (wireType === 5) {
|
|
pos += 4;
|
|
} else if (wireType === 1) {
|
|
pos += 8;
|
|
} else {
|
|
throw new Error(`unsupported wireType ${wireType}`);
|
|
}
|
|
}
|
|
return fields;
|
|
}
|
|
|
|
export function findField(fields: Field[], fieldNumber: number): Field | undefined {
|
|
return fields.find((f) => f.fieldNumber === fieldNumber);
|
|
}
|
|
|
|
export function decodeStringField(buf: Buffer, fieldNumber: number): string {
|
|
const fields = decodeFields(buf);
|
|
const f = findField(fields, fieldNumber);
|
|
if (f && f.wireType === 2) return f.bytes.toString("utf8");
|
|
return "";
|
|
}
|
|
|
|
export function decodeVarintField(buf: Buffer, fieldNumber: number): number {
|
|
const fields = decodeFields(buf);
|
|
const f = findField(fields, fieldNumber);
|
|
if (f && f.wireType === 0) return Number(f.varint);
|
|
return 0;
|
|
}
|