


















@@ -408,8 +408,9 @@ describe("plugins cli install", () => {
408408expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
409409demo: expect.objectContaining({
410410source: "clawhub",
411-spec: "clawhub:demo@1.2.3",
411+spec: "clawhub:demo",
412412installPath: cliInstallPath("demo"),
413+version: "1.2.3",
413414clawhubPackage: "demo",
414415clawhubFamily: "code-plugin",
415416clawhubChannel: "official",
@@ -491,6 +492,48 @@ describe("plugins cli install", () => {
491492);
492493});
493494495+it("keeps explicit ClawHub versions pinned in install records", async () => {
496+const cfg = {
497+plugins: {
498+entries: {},
499+},
500+} as OpenClawConfig;
501+const enabledCfg = createEnabledPluginConfig("demo");
502+503+loadConfig.mockReturnValue(cfg);
504+parseClawHubPluginSpec.mockReturnValue({ name: "demo", version: "1.2.3" });
505+installPluginFromClawHub.mockResolvedValue(
506+createClawHubInstallResult({
507+pluginId: "demo",
508+packageName: "demo",
509+version: "1.2.3",
510+channel: "official",
511+}),
512+);
513+enablePluginInConfig.mockReturnValue({ config: enabledCfg });
514+applyExclusiveSlotSelection.mockReturnValue({
515+config: enabledCfg,
516+warnings: [],
517+});
518+519+await runPluginsCommand(["plugins", "install", "clawhub:demo@1.2.3"]);
520+521+expect(installPluginFromClawHub).toHaveBeenCalledWith(
522+expect.objectContaining({
523+spec: "clawhub:demo@1.2.3",
524+}),
525+);
526+expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
527+demo: expect.objectContaining({
528+source: "clawhub",
529+spec: "clawhub:demo@1.2.3",
530+installPath: cliInstallPath("demo"),
531+version: "1.2.3",
532+clawhubPackage: "demo",
533+}),
534+});
535+});
536+494537it("prefers ClawHub before npm for bare plugin specs", async () => {
495538const cfg = {
496539plugins: {
@@ -524,14 +567,54 @@ describe("plugins cli install", () => {
524567expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
525568demo: expect.objectContaining({
526569source: "clawhub",
527-spec: "clawhub:demo@1.2.3",
570+spec: "clawhub:demo",
528571installPath: cliInstallPath("demo"),
572+version: "1.2.3",
529573clawhubPackage: "demo",
530574}),
531575});
532576expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
533577});
534578579+it("keeps explicit bare ClawHub selectors in install records", async () => {
580+const cfg = {
581+plugins: {
582+entries: {},
583+},
584+} as OpenClawConfig;
585+const enabledCfg = createEnabledPluginConfig("demo");
586+loadConfig.mockReturnValue(cfg);
587+installPluginFromClawHub.mockResolvedValue(
588+createClawHubInstallResult({
589+pluginId: "demo",
590+packageName: "demo",
591+version: "1.2.3-beta.1",
592+channel: "community",
593+}),
594+);
595+enablePluginInConfig.mockReturnValue({ config: enabledCfg });
596+applyExclusiveSlotSelection.mockReturnValue({
597+config: enabledCfg,
598+warnings: [],
599+});
600+601+await runPluginsCommand(["plugins", "install", "demo@beta"]);
602+603+expect(installPluginFromClawHub).toHaveBeenCalledWith(
604+expect.objectContaining({
605+spec: "clawhub:demo@beta",
606+}),
607+);
608+expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
609+demo: expect.objectContaining({
610+source: "clawhub",
611+spec: "clawhub:demo@beta",
612+version: "1.2.3-beta.1",
613+clawhubPackage: "demo",
614+}),
615+});
616+});
617+535618it("falls back to npm when ClawHub does not have the package", async () => {
536619primeNpmPluginFallback();
537620此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。