mirror of
https://github.com/hanzoai/bot.git
synced 2026-10-30 17:24:26 +00:00
The automated rebrand sed script replaced "OpenClaw" with "Hanzo Bot" (with space) instead of "HanzoBot" in 8,315 TypeScript identifier occurrences across 1,632 files. This fixes all broken class names, type names, function names, and variable names. Co-authored-by: Hanzo Dev <[email protected]>
248 lines
6.6 KiB
TypeScript
248 lines
6.6 KiB
TypeScript
import { promises as fs } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { ErrorCodes, PROTOCOL_VERSION, ProtocolSchemas } from "../src/gateway/protocol/schema.js";
|
|
|
|
type JsonSchema = {
|
|
type?: string | string[];
|
|
properties?: Record<string, JsonSchema>;
|
|
required?: string[];
|
|
items?: JsonSchema;
|
|
enum?: string[];
|
|
patternProperties?: Record<string, JsonSchema>;
|
|
};
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = path.resolve(__dirname, "..");
|
|
const outPaths = [
|
|
path.join(repoRoot, "apps", "macos", "Sources", "HanzoBotProtocol", "GatewayModels.swift"),
|
|
path.join(
|
|
repoRoot,
|
|
"apps",
|
|
"shared",
|
|
"HanzoBotKit",
|
|
"Sources",
|
|
"HanzoBotProtocol",
|
|
"GatewayModels.swift",
|
|
),
|
|
];
|
|
|
|
const header = `// Generated by scripts/protocol-gen-swift.ts — do not edit by hand\n// swiftlint:disable file_length\nimport Foundation\n\npublic let GATEWAY_PROTOCOL_VERSION = ${PROTOCOL_VERSION}\n\npublic enum ErrorCode: String, Codable, Sendable {\n${Object.values(
|
|
ErrorCodes,
|
|
)
|
|
.map((c) => ` case ${camelCase(c)} = "${c}"`)
|
|
.join("\n")}\n}\n`;
|
|
|
|
const reserved = new Set([
|
|
"associatedtype",
|
|
"class",
|
|
"deinit",
|
|
"enum",
|
|
"extension",
|
|
"fileprivate",
|
|
"func",
|
|
"import",
|
|
"init",
|
|
"inout",
|
|
"internal",
|
|
"let",
|
|
"open",
|
|
"operator",
|
|
"private",
|
|
"precedencegroup",
|
|
"protocol",
|
|
"public",
|
|
"rethrows",
|
|
"static",
|
|
"struct",
|
|
"subscript",
|
|
"typealias",
|
|
"var",
|
|
]);
|
|
|
|
function camelCase(input: string) {
|
|
return input
|
|
.replace(/[^a-zA-Z0-9]+/g, " ")
|
|
.trim()
|
|
.toLowerCase()
|
|
.split(/\s+/)
|
|
.map((p, i) => (i === 0 ? p : p[0].toUpperCase() + p.slice(1)))
|
|
.join("");
|
|
}
|
|
|
|
function safeName(name: string) {
|
|
const cc = camelCase(name.replace(/-/g, "_"));
|
|
if (reserved.has(cc)) {
|
|
return `_${cc}`;
|
|
}
|
|
return cc;
|
|
}
|
|
|
|
// filled later once schemas are loaded
|
|
const schemaNameByObject = new Map<object, string>();
|
|
|
|
function swiftType(schema: JsonSchema, required: boolean): string {
|
|
const t = schema.type;
|
|
const isOptional = !required;
|
|
let base: string;
|
|
const named = schemaNameByObject.get(schema as object);
|
|
if (named) {
|
|
base = named;
|
|
} else if (t === "string") {
|
|
base = "String";
|
|
} else if (t === "integer") {
|
|
base = "Int";
|
|
} else if (t === "number") {
|
|
base = "Double";
|
|
} else if (t === "boolean") {
|
|
base = "Bool";
|
|
} else if (t === "array") {
|
|
base = `[${swiftType(schema.items ?? { type: "Any" }, true)}]`;
|
|
} else if (schema.enum) {
|
|
base = "String";
|
|
} else if (schema.patternProperties) {
|
|
base = "[String: AnyCodable]";
|
|
} else if (t === "object") {
|
|
base = "[String: AnyCodable]";
|
|
} else {
|
|
base = "AnyCodable";
|
|
}
|
|
return isOptional ? `${base}?` : base;
|
|
}
|
|
|
|
function emitStruct(name: string, schema: JsonSchema): string {
|
|
const props = schema.properties ?? {};
|
|
const required = new Set(schema.required ?? []);
|
|
const lines: string[] = [];
|
|
if (Object.keys(props).length === 0) {
|
|
return `public struct ${name}: Codable, Sendable {}\n`;
|
|
}
|
|
lines.push(`public struct ${name}: Codable, Sendable {`);
|
|
const codingKeys: string[] = [];
|
|
for (const [key, propSchema] of Object.entries(props)) {
|
|
const propName = safeName(key);
|
|
const propType = swiftType(propSchema, required.has(key));
|
|
lines.push(` public let ${propName}: ${propType}`);
|
|
if (propName !== key) {
|
|
codingKeys.push(` case ${propName} = "${key}"`);
|
|
} else {
|
|
codingKeys.push(` case ${propName}`);
|
|
}
|
|
}
|
|
lines.push(
|
|
"\n public init(\n" +
|
|
Object.entries(props)
|
|
.map(([key, prop]) => {
|
|
const propName = safeName(key);
|
|
const req = required.has(key);
|
|
return ` ${propName}: ${swiftType(prop, true)}${req ? "" : "?"}`;
|
|
})
|
|
.join(",\n") +
|
|
")\n" +
|
|
" {\n" +
|
|
Object.entries(props)
|
|
.map(([key]) => {
|
|
const propName = safeName(key);
|
|
return ` self.${propName} = ${propName}`;
|
|
})
|
|
.join("\n") +
|
|
"\n }\n\n" +
|
|
" private enum CodingKeys: String, CodingKey {\n" +
|
|
codingKeys.join("\n") +
|
|
"\n }\n}",
|
|
);
|
|
lines.push("");
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function emitGatewayFrame(): string {
|
|
const cases = ["req", "res", "event"];
|
|
const associated: Record<string, string> = {
|
|
req: "RequestFrame",
|
|
res: "ResponseFrame",
|
|
event: "EventFrame",
|
|
};
|
|
const caseLines = cases.map((c) => ` case ${safeName(c)}(${associated[c]})`);
|
|
const initLines = `
|
|
private enum CodingKeys: String, CodingKey {
|
|
case type
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
|
let type = try typeContainer.decode(String.self, forKey: .type)
|
|
switch type {
|
|
case "req":
|
|
self = try .req(RequestFrame(from: decoder))
|
|
case "res":
|
|
self = try .res(ResponseFrame(from: decoder))
|
|
case "event":
|
|
self = try .event(EventFrame(from: decoder))
|
|
default:
|
|
let container = try decoder.singleValueContainer()
|
|
let raw = try container.decode([String: AnyCodable].self)
|
|
self = .unknown(type: type, raw: raw)
|
|
}
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
switch self {
|
|
case let .req(v):
|
|
try v.encode(to: encoder)
|
|
case let .res(v):
|
|
try v.encode(to: encoder)
|
|
case let .event(v):
|
|
try v.encode(to: encoder)
|
|
case let .unknown(_, raw):
|
|
var container = encoder.singleValueContainer()
|
|
try container.encode(raw)
|
|
}
|
|
}
|
|
`;
|
|
|
|
return [
|
|
"public enum GatewayFrame: Codable, Sendable {",
|
|
...caseLines,
|
|
" case unknown(type: String, raw: [String: AnyCodable])",
|
|
initLines.trimEnd(),
|
|
"}",
|
|
"",
|
|
].join("\n");
|
|
}
|
|
|
|
async function generate() {
|
|
const definitions = Object.entries(ProtocolSchemas) as Array<[string, JsonSchema]>;
|
|
|
|
for (const [name, schema] of definitions) {
|
|
schemaNameByObject.set(schema as object, name);
|
|
}
|
|
|
|
const parts: string[] = [];
|
|
parts.push(header);
|
|
|
|
// Value structs
|
|
for (const [name, schema] of definitions) {
|
|
if (name === "GatewayFrame") {
|
|
continue;
|
|
}
|
|
if (schema.type === "object") {
|
|
parts.push(emitStruct(name, schema));
|
|
}
|
|
}
|
|
|
|
// Frame enum must come after payload structs
|
|
parts.push(emitGatewayFrame());
|
|
|
|
const content = parts.join("\n");
|
|
for (const outPath of outPaths) {
|
|
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
await fs.writeFile(outPath, content);
|
|
console.log(`wrote ${outPath}`);
|
|
}
|
|
}
|
|
|
|
generate().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|