mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:30:57 +00:00
feat(plugins): prefer clawhub for bundled cutovers
This commit is contained in:
@@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/source checkout: load bundled plugins from the `extensions/*` pnpm workspace tree in source checkouts, so plugin-local dependencies and edits are used directly while packaged installs keep using the built runtime tree. Thanks @vincentkoc.
|
||||
- Plugins/ClawHub: prefer versioned ClawPack artifacts when ClawHub publishes digest metadata, verifying the ClawPack response header and downloaded bytes before installing. Thanks @vincentkoc.
|
||||
- Plugins/ClawHub: persist ClawPack digest metadata on ClawHub plugin install and update records so registry refreshes and download verification can reuse stored artifact facts. Thanks @vincentkoc.
|
||||
- Plugins/ClawHub: allow official bundled-plugin cutovers to prefer ClawHub installs with npm fallback only when the ClawHub package or version is absent. Thanks @vincentkoc.
|
||||
- Providers/OpenAI: add `extraBody`/`extra_body` passthrough for OpenAI-compatible TTS endpoints, so custom speech servers can receive fields such as `lang` in `/audio/speech` requests. Fixes #39900. Thanks @R3NK0R.
|
||||
- Dependencies: refresh workspace dependency pins, including TypeBox 1.1.37, AWS SDK 3.1041.0, Microsoft Teams 2.0.9, and Marked 18.0.3. Thanks @mariozechner, @aws, and @microsoft.
|
||||
- Discord/channels: add reusable message-channel access groups plus Discord channel-audience DM authorization, so allowlists can reference `accessGroup:<name>` across channel auth paths. (#75813)
|
||||
|
||||
@@ -21,6 +21,7 @@ function buildBridgeFromPersistedBundledRecord(
|
||||
return {
|
||||
bundledPluginId: record.pluginId,
|
||||
pluginId: record.pluginId,
|
||||
preferredSource: "npm",
|
||||
npmSpec,
|
||||
...(record.enabledByDefault ? { enabledByDefault: true } : {}),
|
||||
...(manifest?.channels.length ? { channelIds: manifest.channels } : {}),
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
export type ExternalizedBundledPluginPreferredSource = "npm" | "clawhub";
|
||||
|
||||
export type ExternalizedBundledPluginBridge = {
|
||||
/** Plugin id used while the plugin was bundled in core. */
|
||||
bundledPluginId: string;
|
||||
/** Plugin id declared by the external package. Defaults to bundledPluginId. */
|
||||
pluginId?: string;
|
||||
/** npm spec OpenClaw should install when migrating the bundled plugin out. */
|
||||
npmSpec: string;
|
||||
/** Preferred external source when migrating the bundled plugin out. Defaults to npm. */
|
||||
preferredSource?: ExternalizedBundledPluginPreferredSource;
|
||||
/** npm spec OpenClaw can install when migrating the bundled plugin out. */
|
||||
npmSpec?: string;
|
||||
/** ClawHub spec OpenClaw can install when migrating the bundled plugin out. */
|
||||
clawhubSpec?: string;
|
||||
/** Optional ClawHub base URL for non-default registries. */
|
||||
clawhubUrl?: string;
|
||||
/** Bundled directory name, when it differs from bundledPluginId. */
|
||||
bundledDirName?: string;
|
||||
/** Previous bundled manifest default enablement from the persisted registry. */
|
||||
@@ -21,6 +29,36 @@ function normalizePluginId(value: string | undefined): string {
|
||||
return value?.trim() ?? "";
|
||||
}
|
||||
|
||||
function normalizeOptionalSpec(value: string | undefined): string {
|
||||
return value?.trim() ?? "";
|
||||
}
|
||||
|
||||
export function getExternalizedBundledPluginPreferredSource(
|
||||
bridge: ExternalizedBundledPluginBridge,
|
||||
): ExternalizedBundledPluginPreferredSource {
|
||||
if (bridge.preferredSource === "clawhub") {
|
||||
return "clawhub";
|
||||
}
|
||||
if (bridge.preferredSource === "npm") {
|
||||
return "npm";
|
||||
}
|
||||
return normalizeOptionalSpec(bridge.clawhubSpec) && !normalizeOptionalSpec(bridge.npmSpec)
|
||||
? "clawhub"
|
||||
: "npm";
|
||||
}
|
||||
|
||||
export function getExternalizedBundledPluginNpmSpec(
|
||||
bridge: ExternalizedBundledPluginBridge,
|
||||
): string {
|
||||
return normalizeOptionalSpec(bridge.npmSpec);
|
||||
}
|
||||
|
||||
export function getExternalizedBundledPluginClawHubSpec(
|
||||
bridge: ExternalizedBundledPluginBridge,
|
||||
): string {
|
||||
return normalizeOptionalSpec(bridge.clawhubSpec);
|
||||
}
|
||||
|
||||
export function getExternalizedBundledPluginTargetId(
|
||||
bridge: ExternalizedBundledPluginBridge,
|
||||
): string {
|
||||
|
||||
@@ -38,6 +38,11 @@ vi.mock("./marketplace.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./clawhub.js", () => ({
|
||||
CLAWHUB_INSTALL_ERROR_CODE: {
|
||||
PACKAGE_NOT_FOUND: "package_not_found",
|
||||
VERSION_NOT_FOUND: "version_not_found",
|
||||
ARCHIVE_INTEGRITY_MISMATCH: "archive_integrity_mismatch",
|
||||
},
|
||||
installPluginFromClawHub: (...args: unknown[]) => installPluginFromClawHubMock(...args),
|
||||
}));
|
||||
|
||||
@@ -73,6 +78,36 @@ function createSuccessfulNpmUpdateResult(params?: {
|
||||
};
|
||||
}
|
||||
|
||||
function createSuccessfulClawHubUpdateResult(params?: {
|
||||
pluginId?: string;
|
||||
targetDir?: string;
|
||||
version?: string;
|
||||
clawhubPackage?: string;
|
||||
}) {
|
||||
return {
|
||||
ok: true,
|
||||
pluginId: params?.pluginId ?? "legacy-chat",
|
||||
targetDir: params?.targetDir ?? "/tmp/openclaw-plugins/legacy-chat",
|
||||
version: params?.version ?? "2026.5.1-beta.2",
|
||||
extensions: ["index.ts"],
|
||||
packageName: params?.clawhubPackage ?? "legacy-chat",
|
||||
clawhub: {
|
||||
source: "clawhub" as const,
|
||||
clawhubUrl: "https://clawhub.ai",
|
||||
clawhubPackage: params?.clawhubPackage ?? "legacy-chat",
|
||||
clawhubFamily: "code-plugin" as const,
|
||||
clawhubChannel: "official" as const,
|
||||
version: params?.version ?? "2026.5.1-beta.2",
|
||||
integrity: "sha256-clawpack",
|
||||
resolvedAt: "2026-05-01T00:00:00.000Z",
|
||||
clawpackSha256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
clawpackSpecVersion: 1,
|
||||
clawpackManifestSha256: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
clawpackSize: 4096,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createNpmInstallConfig(params: {
|
||||
pluginId: string;
|
||||
spec: string;
|
||||
@@ -1481,6 +1516,7 @@ describe("updateNpmInstalledPlugins", () => {
|
||||
describe("syncPluginsForUpdateChannel", () => {
|
||||
beforeEach(() => {
|
||||
installPluginFromNpmSpecMock.mockReset();
|
||||
installPluginFromClawHubMock.mockReset();
|
||||
installPluginFromGitSpecMock.mockReset();
|
||||
resolveBundledPluginSourcesMock.mockReset();
|
||||
});
|
||||
@@ -1662,6 +1698,193 @@ describe("syncPluginsForUpdateChannel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("installs a ClawHub-preferred externalized bundled plugin", async () => {
|
||||
resolveBundledPluginSourcesMock.mockReturnValue(new Map());
|
||||
installPluginFromClawHubMock.mockResolvedValue(
|
||||
createSuccessfulClawHubUpdateResult({
|
||||
pluginId: "legacy-chat",
|
||||
targetDir: "/tmp/openclaw-plugins/legacy-chat",
|
||||
version: "2026.5.1-beta.2",
|
||||
clawhubPackage: "legacy-chat",
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await syncPluginsForUpdateChannel({
|
||||
channel: "stable",
|
||||
externalizedBundledPluginBridges: [
|
||||
{
|
||||
bundledPluginId: "legacy-chat",
|
||||
preferredSource: "clawhub",
|
||||
clawhubSpec: "clawhub:legacy-chat@2026.5.1-beta.2",
|
||||
clawhubUrl: "https://clawhub.ai",
|
||||
npmSpec: "@openclaw/legacy-chat",
|
||||
channelIds: ["legacy-chat"],
|
||||
},
|
||||
],
|
||||
config: {
|
||||
channels: {
|
||||
"legacy-chat": {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
load: { paths: [appBundledPluginRoot("legacy-chat")] },
|
||||
installs: {
|
||||
"legacy-chat": {
|
||||
source: "path",
|
||||
sourcePath: appBundledPluginRoot("legacy-chat"),
|
||||
installPath: appBundledPluginRoot("legacy-chat"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(installPluginFromClawHubMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "clawhub:legacy-chat@2026.5.1-beta.2",
|
||||
baseUrl: "https://clawhub.ai",
|
||||
mode: "update",
|
||||
expectedPluginId: "legacy-chat",
|
||||
}),
|
||||
);
|
||||
expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled();
|
||||
expect(result.changed).toBe(true);
|
||||
expect(result.summary.switchedToClawHub).toEqual(["legacy-chat"]);
|
||||
expect(result.summary.switchedToNpm).toEqual([]);
|
||||
expect(result.summary.errors).toEqual([]);
|
||||
expect(result.config.plugins?.load?.paths).toEqual([]);
|
||||
expect(result.config.plugins?.installs?.["legacy-chat"]).toMatchObject({
|
||||
source: "clawhub",
|
||||
spec: "clawhub:legacy-chat@2026.5.1-beta.2",
|
||||
installPath: "/tmp/openclaw-plugins/legacy-chat",
|
||||
version: "2026.5.1-beta.2",
|
||||
integrity: "sha256-clawpack",
|
||||
clawhubUrl: "https://clawhub.ai",
|
||||
clawhubPackage: "legacy-chat",
|
||||
clawhubFamily: "code-plugin",
|
||||
clawhubChannel: "official",
|
||||
clawpackSha256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
clawpackSpecVersion: 1,
|
||||
clawpackManifestSha256: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
clawpackSize: 4096,
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back from ClawHub to npm only when the ClawHub package is absent", async () => {
|
||||
resolveBundledPluginSourcesMock.mockReturnValue(new Map());
|
||||
installPluginFromClawHubMock.mockResolvedValue({
|
||||
ok: false,
|
||||
code: "package_not_found",
|
||||
error: "Package not found on ClawHub.",
|
||||
});
|
||||
installPluginFromNpmSpecMock.mockResolvedValue(
|
||||
createSuccessfulNpmUpdateResult({
|
||||
pluginId: "legacy-chat",
|
||||
targetDir: "/tmp/openclaw-plugins/legacy-chat",
|
||||
version: "2.0.0",
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await syncPluginsForUpdateChannel({
|
||||
channel: "stable",
|
||||
externalizedBundledPluginBridges: [
|
||||
{
|
||||
bundledPluginId: "legacy-chat",
|
||||
preferredSource: "clawhub",
|
||||
clawhubSpec: "clawhub:legacy-chat@2026.5.1-beta.2",
|
||||
npmSpec: "@openclaw/legacy-chat",
|
||||
channelIds: ["legacy-chat"],
|
||||
},
|
||||
],
|
||||
config: {
|
||||
channels: {
|
||||
"legacy-chat": {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
load: { paths: [appBundledPluginRoot("legacy-chat")] },
|
||||
installs: {
|
||||
"legacy-chat": {
|
||||
source: "path",
|
||||
sourcePath: appBundledPluginRoot("legacy-chat"),
|
||||
installPath: appBundledPluginRoot("legacy-chat"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(installPluginFromNpmSpecMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "@openclaw/legacy-chat",
|
||||
mode: "update",
|
||||
expectedPluginId: "legacy-chat",
|
||||
}),
|
||||
);
|
||||
expect(result.changed).toBe(true);
|
||||
expect(result.summary.switchedToClawHub).toEqual([]);
|
||||
expect(result.summary.switchedToNpm).toEqual(["legacy-chat"]);
|
||||
expect(result.summary.warnings).toEqual([
|
||||
"ClawHub clawhub:legacy-chat@2026.5.1-beta.2 unavailable for legacy-chat; falling back to npm @openclaw/legacy-chat.",
|
||||
]);
|
||||
expect(result.summary.errors).toEqual([]);
|
||||
expect(result.config.plugins?.installs?.["legacy-chat"]).toMatchObject({
|
||||
source: "npm",
|
||||
spec: "@openclaw/legacy-chat",
|
||||
installPath: "/tmp/openclaw-plugins/legacy-chat",
|
||||
version: "2.0.0",
|
||||
});
|
||||
});
|
||||
|
||||
it("fails closed without npm fallback when ClawHub returns integrity drift", async () => {
|
||||
resolveBundledPluginSourcesMock.mockReturnValue(new Map());
|
||||
installPluginFromClawHubMock.mockResolvedValue({
|
||||
ok: false,
|
||||
code: "archive_integrity_mismatch",
|
||||
error: "ClawHub ClawPack integrity mismatch.",
|
||||
});
|
||||
const config: OpenClawConfig = {
|
||||
channels: {
|
||||
"legacy-chat": {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
load: { paths: [appBundledPluginRoot("legacy-chat")] },
|
||||
installs: {
|
||||
"legacy-chat": {
|
||||
source: "path",
|
||||
sourcePath: appBundledPluginRoot("legacy-chat"),
|
||||
installPath: appBundledPluginRoot("legacy-chat"),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await syncPluginsForUpdateChannel({
|
||||
channel: "stable",
|
||||
externalizedBundledPluginBridges: [
|
||||
{
|
||||
bundledPluginId: "legacy-chat",
|
||||
preferredSource: "clawhub",
|
||||
clawhubSpec: "clawhub:legacy-chat@2026.5.1-beta.2",
|
||||
npmSpec: "@openclaw/legacy-chat",
|
||||
channelIds: ["legacy-chat"],
|
||||
},
|
||||
],
|
||||
config,
|
||||
});
|
||||
|
||||
expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled();
|
||||
expect(result.changed).toBe(false);
|
||||
expect(result.config).toBe(config);
|
||||
expect(result.summary.errors).toEqual([
|
||||
"Failed to update legacy-chat: ClawHub ClawPack integrity mismatch. (ClawHub clawhub:legacy-chat@2026.5.1-beta.2).",
|
||||
]);
|
||||
});
|
||||
|
||||
it("externalizes bundled plugins that were enabled by default", async () => {
|
||||
resolveBundledPluginSourcesMock.mockReturnValue(new Map());
|
||||
installPluginFromNpmSpecMock.mockResolvedValue(
|
||||
|
||||
@@ -11,11 +11,14 @@ import { compareComparableSemver, parseComparableSemver } from "../infra/semver-
|
||||
import type { UpdateChannel } from "../infra/update-channels.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { resolveBundledPluginSources } from "./bundled-sources.js";
|
||||
import { installPluginFromClawHub } from "./clawhub.js";
|
||||
import { CLAWHUB_INSTALL_ERROR_CODE, installPluginFromClawHub } from "./clawhub.js";
|
||||
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
|
||||
import {
|
||||
getExternalizedBundledPluginLegacyPathSuffix,
|
||||
getExternalizedBundledPluginClawHubSpec,
|
||||
getExternalizedBundledPluginLookupIds,
|
||||
getExternalizedBundledPluginNpmSpec,
|
||||
getExternalizedBundledPluginPreferredSource,
|
||||
getExternalizedBundledPluginTargetId,
|
||||
type ExternalizedBundledPluginBridge,
|
||||
} from "./externalized-bundled-plugins.js";
|
||||
@@ -62,6 +65,7 @@ export type PluginUpdateIntegrityDriftParams = {
|
||||
|
||||
export type PluginChannelSyncSummary = {
|
||||
switchedToBundled: string[];
|
||||
switchedToClawHub: string[];
|
||||
switchedToNpm: string[];
|
||||
warnings: string[];
|
||||
errors: string[];
|
||||
@@ -383,6 +387,27 @@ function isExternalizedBundledPluginEnabled(params: {
|
||||
return false;
|
||||
}
|
||||
|
||||
function shouldFallbackClawHubBridgeToNpm(result: { ok: false; code?: string }): boolean {
|
||||
return (
|
||||
result.code === CLAWHUB_INSTALL_ERROR_CODE.PACKAGE_NOT_FOUND ||
|
||||
result.code === CLAWHUB_INSTALL_ERROR_CODE.VERSION_NOT_FOUND
|
||||
);
|
||||
}
|
||||
|
||||
function isBridgeAlreadyInstalledFromPreferredSource(params: {
|
||||
bridge: ExternalizedBundledPluginBridge;
|
||||
record: PluginInstallRecord;
|
||||
}): boolean {
|
||||
const npmSpec = getExternalizedBundledPluginNpmSpec(params.bridge);
|
||||
if (npmSpec && params.record.source === "npm" && params.record.spec === npmSpec) {
|
||||
return true;
|
||||
}
|
||||
const clawhubSpec = getExternalizedBundledPluginClawHubSpec(params.bridge);
|
||||
return Boolean(
|
||||
clawhubSpec && params.record.source === "clawhub" && params.record.spec === clawhubSpec,
|
||||
);
|
||||
}
|
||||
|
||||
function replacePluginIdInList(
|
||||
entries: string[] | undefined,
|
||||
fromId: string,
|
||||
@@ -1012,6 +1037,7 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
const logger = params.logger ?? {};
|
||||
const summary: PluginChannelSyncSummary = {
|
||||
switchedToBundled: [],
|
||||
switchedToClawHub: [],
|
||||
switchedToNpm: [],
|
||||
warnings: [],
|
||||
errors: [],
|
||||
@@ -1080,7 +1106,13 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existing?.record.source === "npm" && existing.record.spec === bridge.npmSpec) {
|
||||
if (
|
||||
existing &&
|
||||
isBridgeAlreadyInstalledFromPreferredSource({
|
||||
bridge,
|
||||
record: existing.record,
|
||||
})
|
||||
) {
|
||||
if (existing.pluginId !== targetPluginId) {
|
||||
next = migratePluginConfigId(next, existing.pluginId, targetPluginId);
|
||||
installs = next.plugins?.installs ?? {};
|
||||
@@ -1101,19 +1133,67 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = await installPluginFromNpmSpec({
|
||||
spec: bridge.npmSpec,
|
||||
mode: "update",
|
||||
expectedPluginId: targetPluginId,
|
||||
logger,
|
||||
});
|
||||
if (!result.ok) {
|
||||
const message = formatNpmInstallFailure({
|
||||
pluginId: targetPluginId,
|
||||
spec: bridge.npmSpec,
|
||||
phase: "update",
|
||||
result,
|
||||
const preferredSource = getExternalizedBundledPluginPreferredSource(bridge);
|
||||
const npmSpec = getExternalizedBundledPluginNpmSpec(bridge);
|
||||
const clawhubSpec = getExternalizedBundledPluginClawHubSpec(bridge);
|
||||
let installSource = preferredSource;
|
||||
let installSpec = preferredSource === "clawhub" ? clawhubSpec : npmSpec;
|
||||
let result:
|
||||
| Awaited<ReturnType<typeof installPluginFromNpmSpec>>
|
||||
| Awaited<ReturnType<typeof installPluginFromClawHub>>;
|
||||
|
||||
if (!installSpec) {
|
||||
const message = `Failed to update ${targetPluginId}: missing ${preferredSource} install spec for externalized bundled plugin.`;
|
||||
summary.errors.push(message);
|
||||
logger.error?.(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preferredSource === "clawhub") {
|
||||
result = await installPluginFromClawHub({
|
||||
spec: clawhubSpec,
|
||||
...(bridge.clawhubUrl ? { baseUrl: bridge.clawhubUrl } : {}),
|
||||
mode: "update",
|
||||
expectedPluginId: targetPluginId,
|
||||
logger,
|
||||
});
|
||||
if (!result.ok && npmSpec && shouldFallbackClawHubBridgeToNpm(result)) {
|
||||
const warning = `ClawHub ${clawhubSpec} unavailable for ${targetPluginId}; falling back to npm ${npmSpec}.`;
|
||||
summary.warnings.push(warning);
|
||||
logger.warn?.(warning);
|
||||
installSource = "npm";
|
||||
installSpec = npmSpec;
|
||||
result = await installPluginFromNpmSpec({
|
||||
spec: npmSpec,
|
||||
mode: "update",
|
||||
expectedPluginId: targetPluginId,
|
||||
logger,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
result = await installPluginFromNpmSpec({
|
||||
spec: npmSpec,
|
||||
mode: "update",
|
||||
expectedPluginId: targetPluginId,
|
||||
logger,
|
||||
});
|
||||
}
|
||||
|
||||
if (!result.ok) {
|
||||
const message =
|
||||
installSource === "clawhub"
|
||||
? formatClawHubInstallFailure({
|
||||
pluginId: targetPluginId,
|
||||
spec: installSpec,
|
||||
phase: "update",
|
||||
error: result.error,
|
||||
})
|
||||
: formatNpmInstallFailure({
|
||||
pluginId: targetPluginId,
|
||||
spec: installSpec,
|
||||
phase: "update",
|
||||
result,
|
||||
});
|
||||
summary.errors.push(message);
|
||||
logger.error?.(message);
|
||||
continue;
|
||||
@@ -1124,14 +1204,42 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
next = migratePluginConfigId(next, existing.pluginId, resolvedPluginId);
|
||||
}
|
||||
const nextVersion = result.version ?? (await readInstalledPackageVersion(result.targetDir));
|
||||
next = recordPluginInstall(next, {
|
||||
pluginId: resolvedPluginId,
|
||||
source: "npm",
|
||||
spec: bridge.npmSpec,
|
||||
installPath: result.targetDir,
|
||||
version: nextVersion,
|
||||
...buildNpmResolutionInstallFields(result.npmResolution),
|
||||
});
|
||||
if (installSource === "clawhub") {
|
||||
const clawhubResult = result as Extract<
|
||||
Awaited<ReturnType<typeof installPluginFromClawHub>>,
|
||||
{ ok: true }
|
||||
>;
|
||||
next = recordPluginInstall(next, {
|
||||
pluginId: resolvedPluginId,
|
||||
source: "clawhub",
|
||||
spec: installSpec,
|
||||
installPath: result.targetDir,
|
||||
version: nextVersion,
|
||||
integrity: clawhubResult.clawhub.integrity,
|
||||
resolvedAt: clawhubResult.clawhub.resolvedAt,
|
||||
clawhubUrl: clawhubResult.clawhub.clawhubUrl,
|
||||
clawhubPackage: clawhubResult.clawhub.clawhubPackage,
|
||||
clawhubFamily: clawhubResult.clawhub.clawhubFamily,
|
||||
clawhubChannel: clawhubResult.clawhub.clawhubChannel,
|
||||
clawpackSha256: clawhubResult.clawhub.clawpackSha256,
|
||||
clawpackSpecVersion: clawhubResult.clawhub.clawpackSpecVersion,
|
||||
clawpackManifestSha256: clawhubResult.clawhub.clawpackManifestSha256,
|
||||
clawpackSize: clawhubResult.clawhub.clawpackSize,
|
||||
});
|
||||
} else {
|
||||
const npmResult = result as Extract<
|
||||
Awaited<ReturnType<typeof installPluginFromNpmSpec>>,
|
||||
{ ok: true }
|
||||
>;
|
||||
next = recordPluginInstall(next, {
|
||||
pluginId: resolvedPluginId,
|
||||
source: "npm",
|
||||
spec: installSpec,
|
||||
installPath: result.targetDir,
|
||||
version: nextVersion,
|
||||
...buildNpmResolutionInstallFields(npmResult.npmResolution),
|
||||
});
|
||||
}
|
||||
installs = next.plugins?.installs ?? {};
|
||||
if (existing?.record.sourcePath) {
|
||||
loadHelpers.removePath(existing.record.sourcePath);
|
||||
@@ -1140,7 +1248,11 @@ export async function syncPluginsForUpdateChannel(params: {
|
||||
loadHelpers.removePath(existing.record.installPath);
|
||||
}
|
||||
removeBridgeBundledLoadPaths({ bridge, loadPaths: loadHelpers, env });
|
||||
summary.switchedToNpm.push(resolvedPluginId);
|
||||
if (installSource === "clawhub") {
|
||||
summary.switchedToClawHub.push(resolvedPluginId);
|
||||
} else {
|
||||
summary.switchedToNpm.push(resolvedPluginId);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user