mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
feat(plugins): expose the selected runtime entry (#144818)
This commit is contained in:
parent
fe1c715de7
commit
f0e841aed3
7 changed files with 95 additions and 0 deletions
|
|
@ -85,6 +85,7 @@ and external URLs. Registering another provider replaces the current provider.
|
|||
| `api.version` | `string?` | Plugin version (optional) |
|
||||
| `api.description` | `string?` | Plugin description (optional) |
|
||||
| `api.source` | `string` | Plugin source path |
|
||||
| `api.runtimeSource` | `string?` | Selected runtime entrypoint path, when the loader has selected a runtime artifact |
|
||||
| `api.rootDir` | `string?` | Plugin root directory (optional) |
|
||||
| `api.config` | `OpenClawConfig` | Current config snapshot (active in-memory runtime snapshot when available) |
|
||||
| `api.pluginConfig` | `Record<string, unknown>` | Plugin-specific config from `plugins.entries.<id>.config` |
|
||||
|
|
@ -93,6 +94,14 @@ and external URLs. Registering another provider replaces the current provider.
|
|||
| `api.registrationMode` | `PluginRegistrationMode` | Current load mode; `"setup-runtime"` is the lightweight setup flow with runtime available |
|
||||
| `api.resolvePath(input)` | `(string) => string` | Resolve path relative to plugin root |
|
||||
|
||||
Use `api.runtimeSource` to locate private modules beside the selected runtime
|
||||
entrypoint. It records the loader's source, standalone package, or bundled
|
||||
artifact choice and always identifies the main runtime entry, even during
|
||||
setup registration. `api.source` and `api.rootDir` retain discovery identity;
|
||||
they can differ from the selected artifact. `runtimeSource` is absent when no
|
||||
runtime artifact has been selected, including metadata-only APIs. This path is
|
||||
a location fact, not authorization to invoke a retired plugin.
|
||||
|
||||
## Where each section moved
|
||||
|
||||
Every section of the single-page version now lives on this page or on one of
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ type BuildPluginApiParams = {
|
|||
version?: string;
|
||||
description?: string;
|
||||
source: string;
|
||||
runtimeSource?: string;
|
||||
rootDir?: string;
|
||||
registrationMode: OpenClawPluginApi["registrationMode"];
|
||||
config: OpenClawConfig;
|
||||
|
|
@ -131,6 +132,7 @@ export function buildPluginApi(params: BuildPluginApiParams): OpenClawPluginApi
|
|||
version: params.version,
|
||||
description: params.description,
|
||||
source: params.source,
|
||||
runtimeSource: params.runtimeSource,
|
||||
rootDir: params.rootDir,
|
||||
registrationMode: params.registrationMode,
|
||||
config: params.config,
|
||||
|
|
|
|||
|
|
@ -183,6 +183,8 @@ export type OpenClawPluginApi = {
|
|||
version?: string;
|
||||
description?: string;
|
||||
source: string;
|
||||
/** Selected runtime entrypoint, independent of setup; absent without runtime artifact selection. */
|
||||
readonly runtimeSource?: string;
|
||||
rootDir?: string;
|
||||
registrationMode: PluginRegistrationMode;
|
||||
config: OpenClawConfig;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ export function hasCompletedPluginRuntimeRegistration(record: ArtifactBoundRecor
|
|||
);
|
||||
}
|
||||
|
||||
export function getPluginRuntimeEntrySource(record: ArtifactBoundRecord): string | undefined {
|
||||
return record[RUNTIME_ARTIFACT_SELECTION]?.runtimeEntry.source;
|
||||
}
|
||||
|
||||
export function matchesPluginRuntimeArtifactSelection(
|
||||
record: ArtifactBoundRecord,
|
||||
params: RuntimeArtifactSelectionInput & { rootDir: string; source: string },
|
||||
|
|
|
|||
|
|
@ -79,6 +79,45 @@ afterEach(() => {
|
|||
});
|
||||
|
||||
describe("resolvePluginRuntimeArtifact", () => {
|
||||
it.each(["source", "package-local", "root-bundled"])(
|
||||
"exposes the selected %s runtime entry to registration",
|
||||
(layout) => {
|
||||
const fixture = createBundledPluginFixture();
|
||||
const entry =
|
||||
layout === "source"
|
||||
? fixture.source
|
||||
: layout === "package-local"
|
||||
? path.join(fixture.rootDir, "dist", "index.js")
|
||||
: fixture.builtSource;
|
||||
fs.mkdirSync(path.dirname(entry), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
entry,
|
||||
`export default {
|
||||
id: "fixture",
|
||||
register(api) {
|
||||
api.registerService({ id: api.runtimeSource ?? "missing runtime source", start() {} });
|
||||
}
|
||||
};\n`,
|
||||
);
|
||||
const registry = withEnv(
|
||||
{
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: path.dirname(fixture.rootDir),
|
||||
OPENCLAW_TEST_TRUST_BUNDLED_PLUGINS_DIR: "1",
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS: undefined,
|
||||
},
|
||||
() =>
|
||||
loadOpenClawPlugins({
|
||||
cache: false,
|
||||
config: { plugins: { allow: ["fixture"], entries: { fixture: { enabled: true } } } },
|
||||
onlyPluginIds: ["fixture"],
|
||||
preferBuiltPluginArtifacts: layout !== "source",
|
||||
}),
|
||||
);
|
||||
expect(registry.services.map(({ service }) => service.id)).toEqual([entry]);
|
||||
expect(registry.plugins[0]?.source).toBe(fixture.source);
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["missing", "present", "staging-symlink", "canonical-directory-symlink"])(
|
||||
"keeps the execution entry and boundary together for a %s canonical entry",
|
||||
(layout) => {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
schedulePluginSessionTurn,
|
||||
unschedulePluginSessionTurnsByTag,
|
||||
} from "./host-hook-scheduled-turns.js";
|
||||
import { getPluginRuntimeEntrySource } from "./plugin-runtime-artifact-binding.js";
|
||||
import { isPluginRegistryActivated, isPluginRegistryRetired } from "./registry-lifecycle.js";
|
||||
import type { PluginRegistrars } from "./registry-registrars.js";
|
||||
import type { PluginRuntimeResolver } from "./registry-runtime.js";
|
||||
|
|
@ -130,6 +131,7 @@ export function createPluginApiFactory(
|
|||
version: record.version,
|
||||
description: record.description,
|
||||
source: record.source,
|
||||
runtimeSource: getPluginRuntimeEntrySource(record),
|
||||
rootDir: record.rootDir,
|
||||
registrationMode,
|
||||
config: params.config,
|
||||
|
|
|
|||
37
src/plugins/registry.runtime-artifact.test.ts
Normal file
37
src/plugins/registry.runtime-artifact.test.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createPluginRecord } from "./loader-records.js";
|
||||
import { bindPluginRuntimeArtifactSelection } from "./plugin-runtime-artifact-binding.js";
|
||||
import { createPluginRegistry } from "./registry.js";
|
||||
import { createPluginRuntime } from "./runtime/index.js";
|
||||
|
||||
describe("plugin API runtime entrypoint", () => {
|
||||
it("preserves the selected main entry during setup registration without guessing an unselected entry", () => {
|
||||
const builder = createPluginRegistry({
|
||||
logger: console,
|
||||
runtime: createPluginRuntime(),
|
||||
activateGlobalSideEffects: false,
|
||||
});
|
||||
const rootDir = path.resolve("plugins/fixture");
|
||||
const source = path.join(rootDir, "index.ts");
|
||||
const record = createPluginRecord({
|
||||
id: "fixture",
|
||||
source,
|
||||
rootDir,
|
||||
origin: "global",
|
||||
enabled: true,
|
||||
configSchema: false,
|
||||
});
|
||||
expect(builder.createApi(record, { config: {} }).runtimeSource).toBeUndefined();
|
||||
|
||||
const runtimeSource = path.join(rootDir, "dist/index.js");
|
||||
bindPluginRuntimeArtifactSelection(record, {
|
||||
runtimeEntry: { source: runtimeSource, rootDir },
|
||||
setupEntry: { source: path.join(rootDir, "dist/setup.js"), rootDir },
|
||||
});
|
||||
const api = builder.createApi(record, { config: {}, registrationMode: "setup-runtime" });
|
||||
expect(api.runtimeSource).toBe(runtimeSource);
|
||||
expect(api.source).toBe(source);
|
||||
expect(api.rootDir).toBe(rootDir);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue