mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-20 21:51:28 +00:00
refactor: share boundary open and gateway test helpers
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { DEFAULT_PLUGIN_ENTRY_CANDIDATES, PLUGIN_MANIFEST_FILENAME } from "./manifest.js";
|
||||
import type { PluginBundleFormat } from "./types.js";
|
||||
@@ -102,17 +102,19 @@ function loadBundleManifestFile(params: {
|
||||
rejectHardlinks: params.rejectHardlinks,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
if (opened.reason === "path") {
|
||||
if (params.allowMissing) {
|
||||
return { ok: true, raw: {}, manifestPath };
|
||||
}
|
||||
return { ok: false, error: `plugin manifest not found: ${manifestPath}`, manifestPath };
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
error: `unsafe plugin manifest path: ${manifestPath} (${opened.reason})`,
|
||||
manifestPath,
|
||||
};
|
||||
return matchBoundaryFileOpenFailure(opened, {
|
||||
path: () => {
|
||||
if (params.allowMissing) {
|
||||
return { ok: true, raw: {}, manifestPath };
|
||||
}
|
||||
return { ok: false, error: `plugin manifest not found: ${manifestPath}`, manifestPath };
|
||||
},
|
||||
fallback: (failure) => ({
|
||||
ok: false,
|
||||
error: `unsafe plugin manifest path: ${manifestPath} (${failure.reason})`,
|
||||
manifestPath,
|
||||
}),
|
||||
});
|
||||
}
|
||||
try {
|
||||
const raw = JSON.parse(fs.readFileSync(opened.fd, "utf-8")) as unknown;
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { applyMergePatch } from "../config/merge-patch.js";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import {
|
||||
CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH,
|
||||
@@ -57,10 +57,18 @@ function readPluginJsonObject(params: {
|
||||
rejectHardlinks: true,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
if (opened.reason === "path" && params.allowMissing) {
|
||||
return { ok: true, raw: {} };
|
||||
}
|
||||
return { ok: false, error: `unable to read ${params.relativePath}: ${opened.reason}` };
|
||||
return matchBoundaryFileOpenFailure(opened, {
|
||||
path: () => {
|
||||
if (params.allowMissing) {
|
||||
return { ok: true, raw: {} };
|
||||
}
|
||||
return { ok: false, error: `unable to read ${params.relativePath}: path` };
|
||||
},
|
||||
fallback: (failure) => ({
|
||||
ok: false,
|
||||
error: `unable to read ${params.relativePath}: ${failure.reason}`,
|
||||
}),
|
||||
});
|
||||
}
|
||||
try {
|
||||
const raw = JSON.parse(fs.readFileSync(opened.fd, "utf-8")) as unknown;
|
||||
|
||||
@@ -7,34 +7,12 @@ import type {
|
||||
SessionBindingAdapter,
|
||||
SessionBindingRecord,
|
||||
} from "../infra/outbound/session-binding-service.js";
|
||||
import { createEmptyPluginRegistry } from "./registry-empty.js";
|
||||
import type { PluginRegistry } from "./registry.js";
|
||||
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-binding-"));
|
||||
const approvalsPath = path.join(tempRoot, "plugin-binding-approvals.json");
|
||||
|
||||
function createEmptyPluginRegistry(): PluginRegistry {
|
||||
return {
|
||||
plugins: [],
|
||||
tools: [],
|
||||
hooks: [],
|
||||
typedHooks: [],
|
||||
channels: [],
|
||||
channelSetups: [],
|
||||
providers: [],
|
||||
speechProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
webSearchProviders: [],
|
||||
gatewayHandlers: {},
|
||||
httpRoutes: [],
|
||||
cliRegistrars: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
conversationBindingResolvedHandlers: [],
|
||||
diagnostics: [],
|
||||
};
|
||||
}
|
||||
|
||||
const sessionBindingState = vi.hoisted(() => {
|
||||
const records = new Map<string, SessionBindingRecord>();
|
||||
let nextId = 1;
|
||||
@@ -105,9 +83,13 @@ const sessionBindingState = vi.hoisted(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const pluginRuntimeState = vi.hoisted(() => ({
|
||||
registry: createEmptyPluginRegistry(),
|
||||
}));
|
||||
const pluginRuntimeState = vi.hoisted(
|
||||
() =>
|
||||
({
|
||||
// The runtime mock is initialized before imports; beforeEach installs the real shared stub.
|
||||
registry: null as unknown as PluginRegistry,
|
||||
}) satisfies { registry: PluginRegistry },
|
||||
);
|
||||
|
||||
vi.mock("../infra/home-dir.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../infra/home-dir.js")>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { detectBundleManifestFormat, loadBundleManifest } from "./bundle-manifest.js";
|
||||
import {
|
||||
@@ -476,25 +476,25 @@ function resolvePackageEntrySource(params: {
|
||||
rejectHardlinks: params.rejectHardlinks ?? true,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
if (opened.reason === "path") {
|
||||
// File missing (ENOENT) — skip, not a security violation.
|
||||
return null;
|
||||
}
|
||||
if (opened.reason === "io") {
|
||||
// Filesystem error (EACCES, EMFILE, etc.) — warn but don't abort.
|
||||
params.diagnostics.push({
|
||||
level: "warn",
|
||||
message: `extension entry unreadable (I/O error): ${params.entryPath}`,
|
||||
source: params.sourceLabel,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
params.diagnostics.push({
|
||||
level: "error",
|
||||
message: `extension entry escapes package directory: ${params.entryPath}`,
|
||||
source: params.sourceLabel,
|
||||
return matchBoundaryFileOpenFailure(opened, {
|
||||
path: () => null,
|
||||
io: () => {
|
||||
params.diagnostics.push({
|
||||
level: "warn",
|
||||
message: `extension entry unreadable (I/O error): ${params.entryPath}`,
|
||||
source: params.sourceLabel,
|
||||
});
|
||||
return null;
|
||||
},
|
||||
fallback: () => {
|
||||
params.diagnostics.push({
|
||||
level: "error",
|
||||
message: `extension entry escapes package directory: ${params.entryPath}`,
|
||||
source: params.sourceLabel,
|
||||
});
|
||||
return null;
|
||||
},
|
||||
});
|
||||
return null;
|
||||
}
|
||||
const safeSource = opened.path;
|
||||
fs.closeSync(opened.fd);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { MANIFEST_KEY } from "../compat/legacy-names.js";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { matchBoundaryFileOpenFailure, openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import type { PluginConfigUiHint, PluginKind } from "./types.js";
|
||||
|
||||
@@ -159,14 +159,18 @@ export function loadPluginManifest(
|
||||
rejectHardlinks,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
if (opened.reason === "path") {
|
||||
return { ok: false, error: `plugin manifest not found: ${manifestPath}`, manifestPath };
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
error: `unsafe plugin manifest path: ${manifestPath} (${opened.reason})`,
|
||||
manifestPath,
|
||||
};
|
||||
return matchBoundaryFileOpenFailure(opened, {
|
||||
path: () => ({
|
||||
ok: false,
|
||||
error: `plugin manifest not found: ${manifestPath}`,
|
||||
manifestPath,
|
||||
}),
|
||||
fallback: (failure) => ({
|
||||
ok: false,
|
||||
error: `unsafe plugin manifest path: ${manifestPath} (${failure.reason})`,
|
||||
manifestPath,
|
||||
}),
|
||||
});
|
||||
}
|
||||
let raw: unknown;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user