Files
united-workforce/packages/workflow/src/logger.ts
T
xiaoju 01e930df8f docs(rfc-001): add execution model — Role, Moderator, Agent types
Ported from nerve's workflow types. Covers ThreadContext, StartStep,
RoleStep, Moderator (pure router), Role (async actor), AgentFn (LLM adapter),
WorkflowDefinition, and execution flow.

小橘 <xiaoju@shazhou.work>
2026-05-06 04:41:52 +00:00

58 lines
1.5 KiB
TypeScript

import { appendFileSync } from "node:fs";
const TAG_LENGTH = 8;
function assertValidLogTag(tag: string): void {
if (tag.length !== TAG_LENGTH) {
throw new Error(`log tag must be exactly ${TAG_LENGTH} characters`);
}
for (let i = 0; i < tag.length; i++) {
const ch = tag[i];
if (ch === undefined) {
throw new Error("log tag validation failed");
}
const upper = ch.toUpperCase();
if (!/[0-9A-HJKMNP-TV-Z]/.test(upper)) {
throw new Error(`invalid Crockford Base32 character in log tag: ${ch}`);
}
}
}
export type LoggerSink =
| { kind: "stderr" }
| { kind: "file"; path: string };
export type CreateLoggerOptions = {
sink: LoggerSink;
};
export type LogFn = (tag: string, content: string) => void;
/** Append one JSONL log record: `{ tag, content, timestamp }` per RFC-001. */
export function createLogger(options: CreateLoggerOptions): LogFn {
if (options.sink.kind === "stderr") {
return (tag: string, content: string) => {
assertValidLogTag(tag);
const line =
`${JSON.stringify({
tag: tag.toUpperCase(),
content,
timestamp: Date.now(),
})}\n`;
process.stderr.write(line);
};
}
const filePath = options.sink.path;
return (tag: string, content: string) => {
assertValidLogTag(tag);
const line =
`${JSON.stringify({
tag: tag.toUpperCase(),
content,
timestamp: Date.now(),
})}\n`;
appendFileSync(filePath, line, "utf8");
};
}