mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
fix(plugins): avoid source rebuilds for policy toggles
Reuse current installed-plugin registry records for policy-only enable and disable refreshes.\n\nThanks @vincentkoc
This commit is contained in:
@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Onboarding/configure: avoid staging every default plugin runtime dependency after config writes, so skipped setup flows only prepare config-selected plugin deps instead of pulling broad feature-plugin packages. Thanks @vincentkoc.
|
||||
- Thinking/providers: resolve bundled provider thinking profiles through lightweight provider policy artifacts when startup-lazy providers are not active, so OpenAI Codex GPT-5.x keeps xhigh available in Gateway session validation. Fixes #74796. Thanks @maxschachere.
|
||||
- Security/Windows: ignore workspace `.env` system-path variables and resolve stale-process `taskkill.exe` from the validated Windows install root, preventing repository-local env files from redirecting cleanup helpers. Thanks @pgondhi987.
|
||||
- CLI/plugins: refresh persisted plugin registry policy in place for `plugins enable` and `plugins disable`, so routine toggles no longer rebuild and hash every plugin source when the target is already indexed. Thanks @vincentkoc.
|
||||
- CLI/plugins: scope install and enable slot selection to the selected plugin manifest/runtime fallback, so plugin installs no longer load every plugin runtime or broad status snapshot just to update memory/context slots. Thanks @vincentkoc.
|
||||
- Plugins/TTS: keep bundled speech-provider discovery available on cold package Gateway paths and add bundled plugin matrix runtime probes for health, readiness, RPC, TTS discovery, and post-ready runtime-deps watchdog coverage. Refs #75283. Thanks @vincentkoc.
|
||||
- Google Meet/Twilio: show delegated voice call ID, DTMF, and intro-greeting state in `googlemeet doctor`, and avoid claiming DTMF was sent when no Meet PIN sequence was configured. Refs #72478. Thanks @DougButdorf.
|
||||
|
||||
@@ -607,9 +607,11 @@ export function resetPluginsCliTestState() {
|
||||
ok: false,
|
||||
error: "marketplace install failed",
|
||||
});
|
||||
enablePluginInConfig.mockImplementation(((cfg: OpenClawConfig) => ({ config: cfg })) as (
|
||||
...args: unknown[]
|
||||
) => unknown);
|
||||
enablePluginInConfig.mockImplementation(((cfg: OpenClawConfig, pluginId: string) => ({
|
||||
config: cfg,
|
||||
enabled: true,
|
||||
pluginId,
|
||||
})) as (...args: unknown[]) => unknown);
|
||||
recordPluginInstall.mockImplementation(
|
||||
((cfg: OpenClawConfig) => cfg) as (...args: unknown[]) => unknown,
|
||||
);
|
||||
|
||||
@@ -26,6 +26,7 @@ describe("plugins cli policy mutations", () => {
|
||||
enablePluginInConfig.mockReturnValue({
|
||||
config: enabledConfig,
|
||||
enabled: true,
|
||||
pluginId: "alpha",
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "enable", "alpha"]);
|
||||
@@ -34,6 +35,7 @@ describe("plugins cli policy mutations", () => {
|
||||
expect(refreshPluginRegistry).toHaveBeenCalledWith({
|
||||
config: enabledConfig,
|
||||
installRecords: {},
|
||||
policyPluginIds: ["alpha"],
|
||||
reason: "policy-changed",
|
||||
});
|
||||
});
|
||||
@@ -54,6 +56,7 @@ describe("plugins cli policy mutations", () => {
|
||||
expect(refreshPluginRegistry).toHaveBeenCalledWith({
|
||||
config: nextConfig,
|
||||
installRecords: {},
|
||||
policyPluginIds: ["alpha"],
|
||||
reason: "policy-changed",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,6 +132,7 @@ export function registerPluginsCli(program: Command) {
|
||||
await refreshPluginRegistryAfterConfigMutation({
|
||||
config: next,
|
||||
reason: "policy-changed",
|
||||
policyPluginIds: [enableResult.pluginId],
|
||||
logger: {
|
||||
warn: (message) => defaultRuntime.log(theme.warn(message)),
|
||||
},
|
||||
@@ -166,6 +167,7 @@ export function registerPluginsCli(program: Command) {
|
||||
await refreshPluginRegistryAfterConfigMutation({
|
||||
config: next,
|
||||
reason: "policy-changed",
|
||||
policyPluginIds: [id],
|
||||
logger: {
|
||||
warn: (message) => defaultRuntime.log(theme.warn(message)),
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ export async function refreshPluginRegistryAfterConfigMutation(params: {
|
||||
workspaceDir?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
installRecords?: Awaited<ReturnType<typeof loadInstalledPluginIndexInstallRecords>>;
|
||||
policyPluginIds?: readonly string[];
|
||||
traceCommand?: string;
|
||||
logger?: PluginRegistryRefreshLogger;
|
||||
}): Promise<void> {
|
||||
@@ -33,6 +34,7 @@ export async function refreshPluginRegistryAfterConfigMutation(params: {
|
||||
config: params.config,
|
||||
reason: params.reason,
|
||||
installRecords,
|
||||
...(params.policyPluginIds ? { policyPluginIds: params.policyPluginIds } : {}),
|
||||
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
|
||||
...(params.env ? { env: params.env } : {}),
|
||||
}),
|
||||
|
||||
@@ -32,9 +32,10 @@ vi.mock("../plugins/install.js", () => ({
|
||||
}));
|
||||
|
||||
const enablePluginInConfig = vi.hoisted(() =>
|
||||
vi.fn<(cfg: OpenClawConfig, pluginId: string) => PluginEnableResult>((cfg) => ({
|
||||
vi.fn<(cfg: OpenClawConfig, pluginId: string) => PluginEnableResult>((cfg, pluginId) => ({
|
||||
config: cfg,
|
||||
enabled: true,
|
||||
pluginId,
|
||||
})),
|
||||
);
|
||||
vi.mock("../plugins/enable.js", () => ({
|
||||
@@ -342,6 +343,7 @@ describe("ensureOnboardingPluginInstalled", () => {
|
||||
enablePluginInConfig.mockReturnValueOnce({
|
||||
config: {},
|
||||
enabled: false,
|
||||
pluginId: "demo",
|
||||
reason: "blocked by allowlist",
|
||||
});
|
||||
const note = vi.fn(async () => {});
|
||||
@@ -484,65 +486,62 @@ describe("ensureOnboardingPluginInstalled", () => {
|
||||
});
|
||||
|
||||
it("hides the npm download option for bundled plugins so the menu matches non-npm channels", async () => {
|
||||
await withTempDir(
|
||||
{ prefix: "openclaw-onboarding-install-bundled-prompt-" },
|
||||
async (temp) => {
|
||||
const bundledDir = path.join(temp, "dist", "extensions", "tlon");
|
||||
await fs.mkdir(bundledDir, { recursive: true });
|
||||
const realBundledDir = await fs.realpath(bundledDir);
|
||||
// Both code paths that surface a bundled plugin to the install
|
||||
// pipeline must agree on the local path: the catalog-driven
|
||||
// resolver (used when an npm spec is present) and the pluginId
|
||||
// fallback. We stub both so the prompt sees a stable bundled path.
|
||||
resolveBundledInstallPlanForCatalogEntry.mockReturnValue({
|
||||
bundledSource: { localPath: realBundledDir },
|
||||
});
|
||||
findBundledPluginSourceInMap.mockReturnValue({ localPath: realBundledDir });
|
||||
await withTempDir({ prefix: "openclaw-onboarding-install-bundled-prompt-" }, async (temp) => {
|
||||
const bundledDir = path.join(temp, "dist", "extensions", "tlon");
|
||||
await fs.mkdir(bundledDir, { recursive: true });
|
||||
const realBundledDir = await fs.realpath(bundledDir);
|
||||
// Both code paths that surface a bundled plugin to the install
|
||||
// pipeline must agree on the local path: the catalog-driven
|
||||
// resolver (used when an npm spec is present) and the pluginId
|
||||
// fallback. We stub both so the prompt sees a stable bundled path.
|
||||
resolveBundledInstallPlanForCatalogEntry.mockReturnValue({
|
||||
bundledSource: { localPath: realBundledDir },
|
||||
});
|
||||
findBundledPluginSourceInMap.mockReturnValue({ localPath: realBundledDir });
|
||||
|
||||
let captured:
|
||||
| {
|
||||
message: string;
|
||||
options: Array<{ value: "npm" | "local" | "skip"; label: string; hint?: string }>;
|
||||
initialValue: "npm" | "local" | "skip";
|
||||
}
|
||||
| undefined;
|
||||
let captured:
|
||||
| {
|
||||
message: string;
|
||||
options: Array<{ value: "npm" | "local" | "skip"; label: string; hint?: string }>;
|
||||
initialValue: "npm" | "local" | "skip";
|
||||
}
|
||||
| undefined;
|
||||
|
||||
await ensureOnboardingPluginInstalled({
|
||||
cfg: {},
|
||||
entry: {
|
||||
pluginId: "tlon",
|
||||
label: "Tlon",
|
||||
install: {
|
||||
npmSpec: "@openclaw/tlon",
|
||||
defaultChoice: "npm",
|
||||
},
|
||||
await ensureOnboardingPluginInstalled({
|
||||
cfg: {},
|
||||
entry: {
|
||||
pluginId: "tlon",
|
||||
label: "Tlon",
|
||||
install: {
|
||||
npmSpec: "@openclaw/tlon",
|
||||
defaultChoice: "npm",
|
||||
},
|
||||
prompter: {
|
||||
select: vi.fn(async (input) => {
|
||||
captured = input;
|
||||
return "skip";
|
||||
}),
|
||||
} as never,
|
||||
runtime: {} as never,
|
||||
});
|
||||
},
|
||||
prompter: {
|
||||
select: vi.fn(async (input) => {
|
||||
captured = input;
|
||||
return "skip";
|
||||
}),
|
||||
} as never,
|
||||
runtime: {} as never,
|
||||
});
|
||||
|
||||
expect(captured).toBeDefined();
|
||||
// "Download from npm (@openclaw/tlon)" must NOT appear: the bundled
|
||||
// copy is what gets enabled, so the npm hint would only confuse
|
||||
// users into thinking the plugin is missing.
|
||||
expect(captured?.options).toEqual([
|
||||
{
|
||||
value: "local",
|
||||
label: "Use local plugin path",
|
||||
hint: realBundledDir,
|
||||
},
|
||||
{ value: "skip", label: "Skip for now" },
|
||||
]);
|
||||
expect(captured?.initialValue).toBe("local");
|
||||
findBundledPluginSourceInMap.mockReset();
|
||||
resolveBundledInstallPlanForCatalogEntry.mockReset();
|
||||
},
|
||||
);
|
||||
expect(captured).toBeDefined();
|
||||
// "Download from npm (@openclaw/tlon)" must NOT appear: the bundled
|
||||
// copy is what gets enabled, so the npm hint would only confuse
|
||||
// users into thinking the plugin is missing.
|
||||
expect(captured?.options).toEqual([
|
||||
{
|
||||
value: "local",
|
||||
label: "Use local plugin path",
|
||||
hint: realBundledDir,
|
||||
},
|
||||
{ value: "skip", label: "Skip for now" },
|
||||
]);
|
||||
expect(captured?.initialValue).toBe("local");
|
||||
findBundledPluginSourceInMap.mockReset();
|
||||
resolveBundledInstallPlanForCatalogEntry.mockReset();
|
||||
});
|
||||
});
|
||||
|
||||
it("enables bundled plugins without adding their bundled directory as a local install", async () => {
|
||||
@@ -564,6 +563,7 @@ describe("ensureOnboardingPluginInstalled", () => {
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
pluginId: "discord",
|
||||
});
|
||||
|
||||
const result = await ensureOnboardingPluginInstalled({
|
||||
|
||||
@@ -5,6 +5,7 @@ import { setPluginEnabledInConfig } from "./toggle-config.js";
|
||||
export type PluginEnableResult = {
|
||||
config: OpenClawConfig;
|
||||
enabled: boolean;
|
||||
pluginId: string;
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
@@ -16,10 +17,10 @@ export function enablePluginInConfig(
|
||||
const builtInChannelId = normalizeChatChannelId(pluginId);
|
||||
const resolvedId = builtInChannelId ?? pluginId;
|
||||
if (cfg.plugins?.enabled === false) {
|
||||
return { config: cfg, enabled: false, reason: "plugins disabled" };
|
||||
return { config: cfg, enabled: false, pluginId: resolvedId, reason: "plugins disabled" };
|
||||
}
|
||||
if (cfg.plugins?.deny?.includes(pluginId) || cfg.plugins?.deny?.includes(resolvedId)) {
|
||||
return { config: cfg, enabled: false, reason: "blocked by denylist" };
|
||||
return { config: cfg, enabled: false, pluginId: resolvedId, reason: "blocked by denylist" };
|
||||
}
|
||||
const allow = cfg.plugins?.allow;
|
||||
if (
|
||||
@@ -28,10 +29,11 @@ export function enablePluginInConfig(
|
||||
!allow.includes(pluginId) &&
|
||||
!allow.includes(resolvedId)
|
||||
) {
|
||||
return { config: cfg, enabled: false, reason: "blocked by allowlist" };
|
||||
return { config: cfg, enabled: false, pluginId: resolvedId, reason: "blocked by allowlist" };
|
||||
}
|
||||
return {
|
||||
config: setPluginEnabledInConfig(cfg, resolvedId, true, options),
|
||||
enabled: true,
|
||||
pluginId: resolvedId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ function createIndex(overrides: Partial<InstalledPluginIndex> = {}): InstalledPl
|
||||
};
|
||||
}
|
||||
|
||||
function createCandidate(rootDir: string): PluginCandidate {
|
||||
function createCandidate(rootDir: string, options: { id?: string } = {}): PluginCandidate {
|
||||
const id = options.id ?? "demo";
|
||||
fs.writeFileSync(
|
||||
path.join(rootDir, "index.ts"),
|
||||
"throw new Error('runtime entry should not load while persisting installed plugin index');\n",
|
||||
@@ -63,15 +64,15 @@ function createCandidate(rootDir: string): PluginCandidate {
|
||||
fs.writeFileSync(
|
||||
path.join(rootDir, "openclaw.plugin.json"),
|
||||
JSON.stringify({
|
||||
id: "demo",
|
||||
name: "Demo",
|
||||
id,
|
||||
name: id === "demo" ? "Demo" : "Next Demo",
|
||||
configSchema: { type: "object" },
|
||||
providers: ["demo"],
|
||||
providers: [id],
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
return {
|
||||
idHint: "demo",
|
||||
idHint: id,
|
||||
source: path.join(rootDir, "index.ts"),
|
||||
rootDir,
|
||||
origin: "global",
|
||||
@@ -278,6 +279,99 @@ describe("installed plugin index persistence", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("refreshes policy state from the persisted registry without rebuilding source records", async () => {
|
||||
const stateDir = makeTempDir();
|
||||
const pluginDir = path.join(stateDir, "plugins", "demo");
|
||||
fs.mkdirSync(pluginDir, { recursive: true });
|
||||
const candidate = createCandidate(pluginDir);
|
||||
const env = {
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: undefined,
|
||||
OPENCLAW_VERSION: "2026.4.25",
|
||||
VITEST: "true",
|
||||
};
|
||||
const initial = await refreshPersistedInstalledPluginIndex({
|
||||
reason: "manual",
|
||||
stateDir,
|
||||
candidates: [candidate],
|
||||
env,
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "openclaw.plugin.json"),
|
||||
JSON.stringify({
|
||||
id: "demo",
|
||||
name: "Demo",
|
||||
configSchema: { type: "object" },
|
||||
providers: ["demo", "changed"],
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const refreshed = await refreshPersistedInstalledPluginIndex({
|
||||
reason: "policy-changed",
|
||||
stateDir,
|
||||
candidates: [candidate],
|
||||
env,
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
demo: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
policyPluginIds: ["demo"],
|
||||
});
|
||||
|
||||
expect(refreshed.plugins).toHaveLength(initial.plugins.length);
|
||||
expect(refreshed.plugins[0]).toMatchObject({
|
||||
pluginId: "demo",
|
||||
enabled: false,
|
||||
manifestHash: initial.plugins[0]?.manifestHash,
|
||||
});
|
||||
expect(refreshed.policyHash).not.toBe(initial.policyHash);
|
||||
});
|
||||
|
||||
it("falls back to a source rebuild when a policy refresh target is missing", async () => {
|
||||
const stateDir = makeTempDir();
|
||||
const pluginDir = path.join(stateDir, "plugins", "demo");
|
||||
const nextPluginDir = path.join(stateDir, "plugins", "next-demo");
|
||||
fs.mkdirSync(pluginDir, { recursive: true });
|
||||
fs.mkdirSync(nextPluginDir, { recursive: true });
|
||||
const candidate = createCandidate(pluginDir);
|
||||
const nextCandidate = createCandidate(nextPluginDir, { id: "next-demo" });
|
||||
const env = {
|
||||
OPENCLAW_BUNDLED_PLUGINS_DIR: undefined,
|
||||
OPENCLAW_VERSION: "2026.4.25",
|
||||
VITEST: "true",
|
||||
};
|
||||
await refreshPersistedInstalledPluginIndex({
|
||||
reason: "manual",
|
||||
stateDir,
|
||||
candidates: [candidate],
|
||||
env,
|
||||
});
|
||||
|
||||
const refreshed = await refreshPersistedInstalledPluginIndex({
|
||||
reason: "policy-changed",
|
||||
stateDir,
|
||||
candidates: [candidate, nextCandidate],
|
||||
env,
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"next-demo": {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
policyPluginIds: ["next-demo"],
|
||||
});
|
||||
|
||||
expect(refreshed.plugins.map((plugin) => plugin.pluginId)).toContain("next-demo");
|
||||
});
|
||||
|
||||
it("preserves existing install records when refreshing the manifest cache", async () => {
|
||||
const stateDir = makeTempDir();
|
||||
await writePersistedInstalledPluginIndex(
|
||||
|
||||
@@ -3,7 +3,11 @@ import { saveJsonFile } from "../infra/json-file.js";
|
||||
import { readJsonFile, readJsonFileSync, writeJsonAtomic } from "../infra/json-files.js";
|
||||
import { isBlockedObjectKey } from "../infra/prototype-keys.js";
|
||||
import { safeParseWithSchema } from "../utils/zod-parse.js";
|
||||
import { resolveCompatibilityHostVersion } from "../version.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
|
||||
import { clearCurrentPluginMetadataSnapshotState } from "./current-plugin-metadata-state.js";
|
||||
import { hashJson } from "./installed-plugin-index-hash.js";
|
||||
import { resolveCompatRegistryVersion } from "./installed-plugin-index-policy.js";
|
||||
import {
|
||||
resolveInstalledPluginIndexStorePath,
|
||||
type InstalledPluginIndexStoreOptions,
|
||||
@@ -15,6 +19,7 @@ import {
|
||||
INSTALLED_PLUGIN_INDEX_VERSION,
|
||||
INSTALLED_PLUGIN_INDEX_MIGRATION_VERSION,
|
||||
loadInstalledPluginIndex,
|
||||
resolveInstalledPluginIndexPolicyHash,
|
||||
refreshInstalledPluginIndex,
|
||||
type InstalledPluginIndex,
|
||||
type InstalledPluginInstallRecordInfo,
|
||||
@@ -185,6 +190,65 @@ export function writePersistedInstalledPluginIndexSync(
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function hasPolicyRefreshTargets(
|
||||
persisted: InstalledPluginIndex,
|
||||
policyPluginIds: readonly string[] | undefined,
|
||||
): boolean {
|
||||
if (!policyPluginIds || policyPluginIds.length === 0) {
|
||||
return true;
|
||||
}
|
||||
const pluginIds = new Set(persisted.plugins.map((plugin) => plugin.pluginId));
|
||||
return policyPluginIds.every((pluginId) => pluginIds.has(pluginId));
|
||||
}
|
||||
|
||||
function canRefreshPersistedPolicyState(
|
||||
persisted: InstalledPluginIndex | null,
|
||||
params: RefreshInstalledPluginIndexParams & InstalledPluginIndexStoreOptions,
|
||||
): persisted is InstalledPluginIndex {
|
||||
if (!persisted || params.reason !== "policy-changed") {
|
||||
return false;
|
||||
}
|
||||
const env = params.env ?? process.env;
|
||||
if (
|
||||
persisted.version !== INSTALLED_PLUGIN_INDEX_VERSION ||
|
||||
persisted.hostContractVersion !== resolveCompatibilityHostVersion(env) ||
|
||||
persisted.compatRegistryVersion !== resolveCompatRegistryVersion() ||
|
||||
persisted.migrationVersion !== INSTALLED_PLUGIN_INDEX_MIGRATION_VERSION
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
params.installRecords &&
|
||||
hashJson(params.installRecords) !== hashJson(persisted.installRecords ?? {})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return hasPolicyRefreshTargets(persisted, params.policyPluginIds);
|
||||
}
|
||||
|
||||
function refreshPersistedPolicyState(
|
||||
persisted: InstalledPluginIndex,
|
||||
params: RefreshInstalledPluginIndexParams,
|
||||
): InstalledPluginIndex {
|
||||
const normalizedConfig = normalizePluginsConfig(params.config?.plugins);
|
||||
return {
|
||||
...persisted,
|
||||
policyHash: resolveInstalledPluginIndexPolicyHash(params.config),
|
||||
generatedAtMs: (params.now?.() ?? new Date()).getTime(),
|
||||
refreshReason: params.reason,
|
||||
plugins: persisted.plugins.map((plugin) => ({
|
||||
...plugin,
|
||||
enabled: resolveEffectiveEnableState({
|
||||
id: plugin.pluginId,
|
||||
origin: plugin.origin,
|
||||
config: normalizedConfig,
|
||||
rootConfig: params.config,
|
||||
enabledByDefault: plugin.enabledByDefault,
|
||||
}).enabled,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export async function inspectPersistedInstalledPluginIndex(
|
||||
params: LoadInstalledPluginIndexParams & InstalledPluginIndexStoreOptions = {},
|
||||
): Promise<InstalledPluginIndexStoreInspection> {
|
||||
@@ -215,7 +279,15 @@ export async function inspectPersistedInstalledPluginIndex(
|
||||
export async function refreshPersistedInstalledPluginIndex(
|
||||
params: RefreshInstalledPluginIndexParams & InstalledPluginIndexStoreOptions,
|
||||
): Promise<InstalledPluginIndex> {
|
||||
const persisted = params.installRecords ? null : await readPersistedInstalledPluginIndex(params);
|
||||
const persisted =
|
||||
params.reason === "policy-changed" || !params.installRecords
|
||||
? await readPersistedInstalledPluginIndex(params)
|
||||
: null;
|
||||
if (canRefreshPersistedPolicyState(persisted, params)) {
|
||||
const index = refreshPersistedPolicyState(persisted, params);
|
||||
await writePersistedInstalledPluginIndex(index, params);
|
||||
return index;
|
||||
}
|
||||
const index = refreshInstalledPluginIndex({
|
||||
...params,
|
||||
installRecords:
|
||||
@@ -228,7 +300,15 @@ export async function refreshPersistedInstalledPluginIndex(
|
||||
export function refreshPersistedInstalledPluginIndexSync(
|
||||
params: RefreshInstalledPluginIndexParams & InstalledPluginIndexStoreOptions,
|
||||
): InstalledPluginIndex {
|
||||
const persisted = params.installRecords ? null : readPersistedInstalledPluginIndexSync(params);
|
||||
const persisted =
|
||||
params.reason === "policy-changed" || !params.installRecords
|
||||
? readPersistedInstalledPluginIndexSync(params)
|
||||
: null;
|
||||
if (canRefreshPersistedPolicyState(persisted, params)) {
|
||||
const index = refreshPersistedPolicyState(persisted, params);
|
||||
writePersistedInstalledPluginIndexSync(index, params);
|
||||
return index;
|
||||
}
|
||||
const index = refreshInstalledPluginIndex({
|
||||
...params,
|
||||
installRecords:
|
||||
|
||||
@@ -125,4 +125,5 @@ export type LoadInstalledPluginIndexParams = {
|
||||
|
||||
export type RefreshInstalledPluginIndexParams = LoadInstalledPluginIndexParams & {
|
||||
reason: InstalledPluginIndexRefreshReason;
|
||||
policyPluginIds?: readonly string[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user