mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
* refactor: remove TypeScript 6 from plugin runtime and tooling Keep source plugin loading on Jiti and native module transforms, move development analysis and declaration builds to TypeScript 7, and preserve captured loader and input ownership contracts. Scaffolds compile to the same entry path without shipping a compiler in production. * refactor: use native checker for restart preflight detection * fix: keep test-directory docs out of native helper scans * fix: preserve native compiler tooling across CI runtimes Keep compiler-backed tests on the supported Node transport, preserve declared Windows aliases and partial-source analysis, and bind relocated SDK reports to their installed compiler. Retain strict package input admission and the existing fixture assertions. * test: compare messaging guard diagnostics without sorting * fix: bound native tooling memory and preserve bootstrap loading * fix: bound native declaration builds with tsdown scheduling * fix: retire compiler-only helpers from installed packages * test: migrate routing import scan to native parser * test(ui): wait for recovered model catalog receipt
289 lines
9.9 KiB
TypeScript
289 lines
9.9 KiB
TypeScript
#!/usr/bin/env node
|
|
|
|
import path from "node:path";
|
|
import * as ts from "typescript/unstable/ast";
|
|
import { resolveRepoRoot } from "./lib/repo-root.mjs";
|
|
import {
|
|
collectFileViolations,
|
|
resolveSourceRoots,
|
|
runAsScript,
|
|
toLine,
|
|
unwrapExpression,
|
|
} from "./lib/ts-guard-utils.mts";
|
|
|
|
const legacyTranscriptReaderModules = new Set([
|
|
"../gateway/session-utils.js",
|
|
"../gateway/session-utils.fs.js",
|
|
"../../gateway/session-utils.js",
|
|
"../../gateway/session-utils.fs.js",
|
|
"./session-utils.js",
|
|
"./session-utils.fs.js",
|
|
"../session-utils.js",
|
|
"../session-utils.fs.js",
|
|
]);
|
|
|
|
const transcriptReaderNames = new Set([
|
|
"attachOpenClawTranscriptMeta",
|
|
"capArrayByJsonBytes",
|
|
"readFirstUserMessageFromTranscript",
|
|
"readLatestRecentSessionUsageFromTranscriptAsync",
|
|
"readLatestSessionUsageFromTranscript",
|
|
"readLatestSessionUsageFromTranscriptAsync",
|
|
"readRecentSessionMessages",
|
|
"readRecentSessionMessagesAsync",
|
|
"readRecentSessionMessagesWithStats",
|
|
"readRecentSessionMessagesWithStatsAsync",
|
|
"readRecentSessionTranscriptLines",
|
|
"readRecentSessionUsageFromTranscript",
|
|
"readRecentSessionUsageFromTranscriptAsync",
|
|
"readSessionMessageByIdAsync",
|
|
"readSessionMessageCount",
|
|
"readSessionMessageCountAsync",
|
|
"readSessionMessages",
|
|
"readSessionMessagesAsync",
|
|
"readSessionMessagesWithSourceAsync",
|
|
"readSessionPreviewItemsFromTranscript",
|
|
"readSessionTitleFieldsFromTranscript",
|
|
"readSessionTitleFieldsFromTranscriptAsync",
|
|
"visitSessionMessages",
|
|
"visitSessionMessagesAsync",
|
|
]);
|
|
|
|
const storageSpecificTranscriptReaderAliasNames = new Set(["readSessionMessagesFromFileAsync"]);
|
|
|
|
const gatewaySessionServerMethodFiles = [
|
|
"src/gateway/server-methods/sessions-abort.ts",
|
|
"src/gateway/server-methods/sessions-compact.ts",
|
|
"src/gateway/server-methods/sessions-compaction-runner.ts",
|
|
"src/gateway/server-methods/sessions-create.ts",
|
|
"src/gateway/server-methods/sessions-delete.ts",
|
|
"src/gateway/server-methods/sessions-dispatch.ts",
|
|
"src/gateway/server-methods/sessions-groups.ts",
|
|
"src/gateway/server-methods/sessions-messaging.ts",
|
|
"src/gateway/server-methods/sessions-mutations.ts",
|
|
"src/gateway/server-methods/sessions-read.ts",
|
|
"src/gateway/server-methods/sessions-shared.ts",
|
|
"src/gateway/server-methods/sessions-subscriptions.ts",
|
|
];
|
|
|
|
export const migratedSessionTranscriptReaderFiles = new Set([
|
|
"src/agents/main-session-recovery/main-session-restart-recovery-store.ts",
|
|
"src/agents/subagents/announce/subagent-announce-output.test.ts",
|
|
"src/agents/subagents/announce/subagent-announce-output.ts",
|
|
"src/agents/subagents/announce/subagent-announce.runtime.ts",
|
|
"src/agents/subagents/registry/subagent-registry-restart-recovery.test.ts",
|
|
"src/agents/subagents/registry/subagent-registry-restart-recovery.ts",
|
|
"src/agents/tools/embedded-gateway-stub.runtime.ts",
|
|
"src/agents/tools/embedded-gateway-stub.test.ts",
|
|
"src/agents/tools/embedded-gateway-stub.ts",
|
|
"src/agents/tools/sessions-history-tool.ts",
|
|
"src/agents/tools/sessions-list-tool.ts",
|
|
"src/gateway/cli-session-history.claude.ts",
|
|
"src/gateway/gateway-models.profiles.live.test.ts",
|
|
"src/gateway/managed-image-attachments.test.ts",
|
|
"src/gateway/managed-image-attachments.ts",
|
|
"src/gateway/mcp-app-reconstruction.ts",
|
|
"src/gateway/server-methods/artifacts.test.ts",
|
|
"src/gateway/server-methods/artifacts.ts",
|
|
"src/gateway/server-methods/chat.ts",
|
|
"src/gateway/server-methods/sessions-files.test.ts",
|
|
"src/gateway/server-methods/sessions-files.ts",
|
|
...gatewaySessionServerMethodFiles,
|
|
"src/gateway/server-session-events.ts",
|
|
"src/gateway/session-history-state.test.ts",
|
|
"src/gateway/session-history-state.ts",
|
|
"src/gateway/session-reset-service.ts",
|
|
"src/gateway/session-utils.ts",
|
|
"src/gateway/sessions-history-http.revocation.test.ts",
|
|
"src/gateway/sessions-history-http.ts",
|
|
"src/status/status-message.ts",
|
|
"src/tui/embedded-backend.test.ts",
|
|
"src/tui/embedded-backend.ts",
|
|
]);
|
|
|
|
function normalizeRelativePath(filePath: string) {
|
|
return filePath.replaceAll(path.sep, "/");
|
|
}
|
|
|
|
function importedModuleName(node: ts.ImportDeclaration | ts.ExportDeclaration) {
|
|
return node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)
|
|
? node.moduleSpecifier.text
|
|
: null;
|
|
}
|
|
|
|
function bindingName(node: ts.BindingElement) {
|
|
if (node.propertyName && ts.isIdentifier(node.propertyName)) {
|
|
return node.propertyName.text;
|
|
}
|
|
if (node.name && ts.isIdentifier(node.name)) {
|
|
return node.name.text;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function destructuresLegacyNamespace(node: ts.BindingElement, legacyNamespaces: Set<string>) {
|
|
const pattern = node.parent;
|
|
const declaration = pattern?.parent;
|
|
if (
|
|
!pattern ||
|
|
!ts.isObjectBindingPattern(pattern) ||
|
|
!declaration ||
|
|
!ts.isVariableDeclaration(declaration) ||
|
|
!declaration.initializer
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
const initializer = unwrapExpression(declaration.initializer);
|
|
return ts.isIdentifier(initializer) && legacyNamespaces.has(initializer.text);
|
|
}
|
|
|
|
export function findSessionTranscriptReaderBoundaryViolations(
|
|
_content: string,
|
|
_file: string,
|
|
sourceFile: ts.SourceFile,
|
|
) {
|
|
const violations: Array<{ line: number; reason: string }> = [];
|
|
const legacyNamespaces = new Set<string>();
|
|
|
|
const visit = (node: ts.Node) => {
|
|
if (ts.isIdentifier(node) && storageSpecificTranscriptReaderAliasNames.has(node.text)) {
|
|
violations.push({
|
|
line: toLine(sourceFile, node),
|
|
reason: `uses storage-specific transcript reader alias "${node.text}"`,
|
|
});
|
|
}
|
|
|
|
if (ts.isImportDeclaration(node)) {
|
|
const moduleName = importedModuleName(node);
|
|
const namedBindings = node.importClause?.namedBindings;
|
|
if (moduleName && legacyTranscriptReaderModules.has(moduleName) && namedBindings) {
|
|
if (ts.isNamedImports(namedBindings)) {
|
|
for (const specifier of namedBindings.elements) {
|
|
const importedName = specifier.propertyName?.text ?? specifier.name.text;
|
|
if (transcriptReaderNames.has(importedName)) {
|
|
violations.push({
|
|
line: toLine(sourceFile, specifier),
|
|
reason: `imports transcript reader "${importedName}" from legacy module "${moduleName}"`,
|
|
});
|
|
}
|
|
}
|
|
} else if (ts.isNamespaceImport(namedBindings)) {
|
|
legacyNamespaces.add(namedBindings.name.text);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ts.isExportDeclaration(node)) {
|
|
const moduleName = importedModuleName(node);
|
|
if (moduleName && legacyTranscriptReaderModules.has(moduleName)) {
|
|
const exportClause = node.exportClause;
|
|
if (!exportClause) {
|
|
violations.push({
|
|
line: toLine(sourceFile, node),
|
|
reason: `re-exports transcript readers from legacy module "${moduleName}"`,
|
|
});
|
|
} else if (ts.isNamedExports(exportClause)) {
|
|
for (const specifier of exportClause.elements) {
|
|
const exportedName = specifier.propertyName?.text ?? specifier.name.text;
|
|
if (transcriptReaderNames.has(exportedName)) {
|
|
violations.push({
|
|
line: toLine(sourceFile, specifier),
|
|
reason: `re-exports transcript reader "${exportedName}" from legacy module "${moduleName}"`,
|
|
});
|
|
}
|
|
}
|
|
} else if (ts.isNamespaceExport(exportClause)) {
|
|
violations.push({
|
|
line: toLine(sourceFile, exportClause),
|
|
reason: `re-exports transcript reader namespace from legacy module "${moduleName}"`,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ts.isBindingElement(node)) {
|
|
const name = bindingName(node);
|
|
if (
|
|
name &&
|
|
transcriptReaderNames.has(name) &&
|
|
destructuresLegacyNamespace(node, legacyNamespaces)
|
|
) {
|
|
violations.push({
|
|
line: toLine(sourceFile, node),
|
|
reason: `aliases legacy transcript reader "${name}"`,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (ts.isPropertyAccessExpression(node)) {
|
|
const receiver = unwrapExpression(node.expression);
|
|
if (
|
|
ts.isIdentifier(receiver) &&
|
|
legacyNamespaces.has(receiver.text) &&
|
|
transcriptReaderNames.has(node.name.text)
|
|
) {
|
|
violations.push({
|
|
line: toLine(sourceFile, node.name),
|
|
reason: `references legacy transcript reader "${node.name.text}"`,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (ts.isElementAccessExpression(node)) {
|
|
const receiver = unwrapExpression(node.expression);
|
|
if (
|
|
ts.isIdentifier(receiver) &&
|
|
legacyNamespaces.has(receiver.text) &&
|
|
ts.isStringLiteral(node.argumentExpression) &&
|
|
transcriptReaderNames.has(node.argumentExpression.text)
|
|
) {
|
|
violations.push({
|
|
line: toLine(sourceFile, node.argumentExpression),
|
|
reason: `references legacy transcript reader "${node.argumentExpression.text}"`,
|
|
});
|
|
}
|
|
}
|
|
|
|
node.forEachChild(visit);
|
|
};
|
|
|
|
visit(sourceFile);
|
|
return violations;
|
|
}
|
|
|
|
export async function main() {
|
|
const repoRoot = resolveRepoRoot(import.meta.url);
|
|
const sourceRoots = resolveSourceRoots(repoRoot, [
|
|
"src/agents",
|
|
"src/gateway",
|
|
"src/status",
|
|
"src/tui",
|
|
]);
|
|
const violations = await collectFileViolations({
|
|
repoRoot,
|
|
sourceRoots,
|
|
includeTests: true,
|
|
skipFile: (filePath) =>
|
|
!migratedSessionTranscriptReaderFiles.has(
|
|
normalizeRelativePath(path.relative(repoRoot, filePath)),
|
|
),
|
|
findViolations: findSessionTranscriptReaderBoundaryViolations,
|
|
});
|
|
|
|
if (violations.length === 0) {
|
|
console.log("session transcript reader boundary guard passed.");
|
|
return;
|
|
}
|
|
|
|
console.error("Found legacy transcript reader usage in migrated files:");
|
|
for (const violation of violations) {
|
|
console.error(`- ${violation.path}:${violation.line}: ${violation.reason}`);
|
|
}
|
|
console.error(
|
|
"Use src/gateway/session-transcript-readers.ts for migrated transcript reader paths. Expand this ratchet only after a slice migrates more files.",
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
runAsScript(import.meta.url, main);
|