mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-23 02:31:13 +00:00
* refactor(diffs): move ephemeral artifacts to SQLite * fix(plugin-state): satisfy SDK validation gates * test(diffs): satisfy lint contracts * fix(plugin-state): count expired blobs toward quotas * fix(plugin-state): harden blob storage boundaries * fix(plugins): replace stale install provenance
124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
// Diffs plugin module implements plugin behavior.
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
import {
|
|
resolvePreferredOpenClawTmpDir,
|
|
type OpenClawConfig,
|
|
type OpenClawPluginApi,
|
|
} from "../api.js";
|
|
import {
|
|
resolveDiffsPluginDefaults,
|
|
resolveDiffsPluginSecurity,
|
|
resolveDiffsPluginViewerBaseUrl,
|
|
} from "./config.js";
|
|
import { createDiffsHttpHandler } from "./http.js";
|
|
import { DIFFS_AGENT_GUIDANCE } from "./prompt-guidance.js";
|
|
import { DiffArtifactStore } from "./store.js";
|
|
import { createDiffsTool } from "./tool.js";
|
|
import type { DiffArtifactBlobMetadata } from "./types.js";
|
|
|
|
const DIFFS_LANGUAGE_PACK_PLUGIN_ID = "diffs-language-pack";
|
|
const DIFF_ARTIFACT_NAMESPACE = "diff-artifacts";
|
|
const DIFF_ARTIFACT_MAX_ENTRIES = 2_048;
|
|
const DIFF_ARTIFACT_MAX_BYTES_PER_ENTRY = 32 * 1024 * 1024;
|
|
const DIFF_ARTIFACT_MAX_BYTES_PER_NAMESPACE = 256 * 1024 * 1024;
|
|
|
|
export function registerDiffsPlugin(api: OpenClawPluginApi): void {
|
|
const store = new DiffArtifactStore({
|
|
rootDir: path.join(resolvePreferredOpenClawTmpDir(), "openclaw-diffs"),
|
|
blobStore: api.runtime.state.openBlobStore<DiffArtifactBlobMetadata>({
|
|
namespace: DIFF_ARTIFACT_NAMESPACE,
|
|
maxEntries: DIFF_ARTIFACT_MAX_ENTRIES,
|
|
maxBytesPerEntry: DIFF_ARTIFACT_MAX_BYTES_PER_ENTRY,
|
|
maxBytesPerNamespace: DIFF_ARTIFACT_MAX_BYTES_PER_NAMESPACE,
|
|
overflowPolicy: "reject-new",
|
|
}),
|
|
logger: api.logger,
|
|
});
|
|
const resolveCurrentPluginConfig = () =>
|
|
resolveLivePluginConfigObject(
|
|
api.runtime.config?.current
|
|
? () => api.runtime.config.current() as OpenClawConfig
|
|
: undefined,
|
|
"diffs",
|
|
api.pluginConfig as Record<string, unknown>,
|
|
) ?? {};
|
|
const resolveCurrentAccessConfig = () => {
|
|
const currentConfig = (api.runtime.config?.current?.() ?? api.config) as OpenClawConfig;
|
|
const pluginConfig = resolveCurrentPluginConfig();
|
|
return {
|
|
allowRemoteViewer: resolveDiffsPluginSecurity(pluginConfig).allowRemoteViewer,
|
|
trustedProxies: currentConfig.gateway?.trustedProxies,
|
|
allowRealIpFallback: currentConfig.gateway?.allowRealIpFallback === true,
|
|
};
|
|
};
|
|
const initialAccessConfig = resolveCurrentAccessConfig();
|
|
|
|
api.registerTool(
|
|
(ctx) => {
|
|
const pluginConfig = resolveCurrentPluginConfig();
|
|
return createDiffsTool({
|
|
api,
|
|
store,
|
|
defaults: resolveDiffsPluginDefaults(pluginConfig),
|
|
viewerBaseUrl: resolveDiffsPluginViewerBaseUrl(pluginConfig),
|
|
languagePackAvailable: resolveDiffsLanguagePackAvailability(api),
|
|
context: ctx,
|
|
});
|
|
},
|
|
{
|
|
name: "diffs",
|
|
},
|
|
);
|
|
api.registerHttpRoute({
|
|
path: "/plugins/diffs",
|
|
auth: "plugin",
|
|
match: "prefix",
|
|
handler: createDiffsHttpHandler({
|
|
store,
|
|
logger: api.logger,
|
|
allowRemoteViewer: initialAccessConfig.allowRemoteViewer,
|
|
trustedProxies: initialAccessConfig.trustedProxies,
|
|
allowRealIpFallback: initialAccessConfig.allowRealIpFallback,
|
|
resolveAccessConfig: resolveCurrentAccessConfig,
|
|
}),
|
|
});
|
|
api.on("before_prompt_build", async () => ({
|
|
prependSystemContext: DIFFS_AGENT_GUIDANCE,
|
|
}));
|
|
}
|
|
|
|
function resolveDiffsLanguagePackAvailability(api: OpenClawPluginApi): boolean {
|
|
const currentConfig = (api.runtime.config?.current?.() ?? api.config) as OpenClawConfig;
|
|
const plugins = currentConfig.plugins;
|
|
if (plugins?.enabled === false) {
|
|
return false;
|
|
}
|
|
if (plugins?.deny?.includes(DIFFS_LANGUAGE_PACK_PLUGIN_ID)) {
|
|
return false;
|
|
}
|
|
if (plugins?.allow && !plugins.allow.includes(DIFFS_LANGUAGE_PACK_PLUGIN_ID)) {
|
|
return false;
|
|
}
|
|
if (plugins?.entries?.[DIFFS_LANGUAGE_PACK_PLUGIN_ID]?.enabled === false) {
|
|
return false;
|
|
}
|
|
return hasSiblingLanguagePackRuntime(api.rootDir);
|
|
}
|
|
|
|
function hasSiblingLanguagePackRuntime(rootDir: string | undefined): boolean {
|
|
if (!rootDir) {
|
|
return false;
|
|
}
|
|
const languagePackRoot = path.join(path.dirname(rootDir), DIFFS_LANGUAGE_PACK_PLUGIN_ID);
|
|
const runtimePaths = [
|
|
path.join(languagePackRoot, "assets", "viewer-runtime.js"),
|
|
path.join(languagePackRoot, "dist", "assets", "viewer-runtime.js"),
|
|
];
|
|
return (
|
|
fs.existsSync(path.join(languagePackRoot, "openclaw.plugin.json")) &&
|
|
runtimePaths.some((runtimePath) => fs.existsSync(runtimePath))
|
|
);
|
|
}
|