wave-agent-sdk 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/plugin.d.ts +2 -2
- package/dist/core/plugin.d.ts.map +1 -1
- package/dist/core/plugin.js +7 -7
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +0 -12
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +1 -1
- package/dist/services/MarketplaceService.d.ts +53 -12
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +311 -123
- package/dist/services/configurationService.d.ts +17 -1
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +104 -0
- package/dist/services/pluginLoader.d.ts +6 -0
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +52 -7
- package/dist/types/configuration.d.ts +7 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/marketplace.d.ts +28 -1
- package/dist/types/marketplace.d.ts.map +1 -1
- package/dist/types/plugins.d.ts +13 -1
- package/dist/types/plugins.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/plugin.ts +13 -7
- package/src/managers/backgroundTaskManager.ts +1 -20
- package/src/managers/pluginManager.ts +4 -1
- package/src/services/MarketplaceService.ts +425 -134
- package/src/services/configurationService.ts +131 -0
- package/src/services/pluginLoader.ts +66 -7
- package/src/types/configuration.ts +8 -0
- package/src/types/marketplace.ts +26 -1
- package/src/types/plugins.ts +13 -1
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
ConfigurationPaths,
|
|
16
16
|
WaveConfiguration,
|
|
17
17
|
Scope,
|
|
18
|
+
MarketplaceConfig,
|
|
18
19
|
} from "../types/configuration.js";
|
|
19
20
|
import {
|
|
20
21
|
getAllConfigPaths,
|
|
@@ -796,6 +797,124 @@ export class ConfigurationService {
|
|
|
796
797
|
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
797
798
|
}
|
|
798
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Get merged marketplaces from all scopes
|
|
802
|
+
*/
|
|
803
|
+
getMergedMarketplaces(workdir: string): Record<string, MarketplaceConfig> {
|
|
804
|
+
const mergedConfig = loadMergedWaveConfig(workdir);
|
|
805
|
+
return mergedConfig?.marketplaces || {};
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Get marketplaces at a specific scope
|
|
810
|
+
*/
|
|
811
|
+
getScopedMarketplaces(
|
|
812
|
+
workdir: string,
|
|
813
|
+
scope: Scope,
|
|
814
|
+
): Record<string, MarketplaceConfig> {
|
|
815
|
+
let configPath: string;
|
|
816
|
+
if (scope === "user") {
|
|
817
|
+
configPath = getUserConfigPaths()[0];
|
|
818
|
+
} else if (scope === "project") {
|
|
819
|
+
configPath = getProjectConfigPaths(workdir)[1];
|
|
820
|
+
} else {
|
|
821
|
+
configPath = getProjectConfigPaths(workdir)[0];
|
|
822
|
+
}
|
|
823
|
+
const config = loadWaveConfigFromFile(configPath);
|
|
824
|
+
return config?.marketplaces || {};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Add a marketplace to the specified scope
|
|
829
|
+
*/
|
|
830
|
+
async addMarketplaceToScope(
|
|
831
|
+
workdir: string,
|
|
832
|
+
scope: Scope,
|
|
833
|
+
name: string,
|
|
834
|
+
config: MarketplaceConfig,
|
|
835
|
+
): Promise<void> {
|
|
836
|
+
if (scope !== "user" && !existsSync(workdir)) {
|
|
837
|
+
throw new Error(`Working directory does not exist: ${workdir}`);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
let configPath: string;
|
|
841
|
+
if (scope === "user") {
|
|
842
|
+
configPath = getUserConfigPaths()[0];
|
|
843
|
+
} else if (scope === "project") {
|
|
844
|
+
configPath = getProjectConfigPaths(workdir)[1];
|
|
845
|
+
} else {
|
|
846
|
+
configPath = getProjectConfigPaths(workdir)[0];
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const configDir = path.dirname(configPath);
|
|
850
|
+
if (!existsSync(configDir)) {
|
|
851
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
let fileConfig: WaveConfiguration = {};
|
|
855
|
+
if (existsSync(configPath)) {
|
|
856
|
+
try {
|
|
857
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
858
|
+
fileConfig = JSON.parse(content);
|
|
859
|
+
} catch {
|
|
860
|
+
// Start with empty config if file is corrupted
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (!fileConfig.marketplaces) {
|
|
865
|
+
fileConfig.marketplaces = {};
|
|
866
|
+
}
|
|
867
|
+
fileConfig.marketplaces[name] = config;
|
|
868
|
+
|
|
869
|
+
await fs.writeFile(
|
|
870
|
+
configPath,
|
|
871
|
+
JSON.stringify(fileConfig, null, 2),
|
|
872
|
+
"utf-8",
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Remove a marketplace from the specified scope
|
|
878
|
+
*/
|
|
879
|
+
async removeMarketplaceFromScope(
|
|
880
|
+
workdir: string,
|
|
881
|
+
scope: Scope,
|
|
882
|
+
name: string,
|
|
883
|
+
): Promise<void> {
|
|
884
|
+
if (scope !== "user" && !existsSync(workdir)) {
|
|
885
|
+
throw new Error(`Working directory does not exist: ${workdir}`);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
let configPath: string;
|
|
889
|
+
if (scope === "user") {
|
|
890
|
+
configPath = getUserConfigPaths()[0];
|
|
891
|
+
} else if (scope === "project") {
|
|
892
|
+
configPath = getProjectConfigPaths(workdir)[1];
|
|
893
|
+
} else {
|
|
894
|
+
configPath = getProjectConfigPaths(workdir)[0];
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (!existsSync(configPath)) {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
try {
|
|
902
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
903
|
+
const fileConfig: WaveConfiguration = JSON.parse(content);
|
|
904
|
+
|
|
905
|
+
if (fileConfig.marketplaces && name in fileConfig.marketplaces) {
|
|
906
|
+
delete fileConfig.marketplaces[name];
|
|
907
|
+
await fs.writeFile(
|
|
908
|
+
configPath,
|
|
909
|
+
JSON.stringify(fileConfig, null, 2),
|
|
910
|
+
"utf-8",
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
} catch {
|
|
914
|
+
// Ignore errors for corrupted or non-existent files
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
799
918
|
/**
|
|
800
919
|
* Remove a plugin from the enabled plugins in the specified scope
|
|
801
920
|
*/
|
|
@@ -991,6 +1110,7 @@ export function loadWaveConfigFromFile(
|
|
|
991
1110
|
? config.autoMemoryEnabled
|
|
992
1111
|
: undefined,
|
|
993
1112
|
models: config.models || undefined,
|
|
1113
|
+
marketplaces: config.marketplaces || undefined,
|
|
994
1114
|
};
|
|
995
1115
|
} catch (error) {
|
|
996
1116
|
if (error instanceof SyntaxError) {
|
|
@@ -1124,6 +1244,12 @@ export function loadMergedWaveConfig(
|
|
|
1124
1244
|
mergedConfig.autoMemoryFrequency = config.autoMemoryFrequency;
|
|
1125
1245
|
}
|
|
1126
1246
|
|
|
1247
|
+
// Merge marketplaces (last one wins for same key)
|
|
1248
|
+
if (config.marketplaces) {
|
|
1249
|
+
if (!mergedConfig.marketplaces) mergedConfig.marketplaces = {};
|
|
1250
|
+
Object.assign(mergedConfig.marketplaces, config.marketplaces);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1127
1253
|
// Merge models
|
|
1128
1254
|
if (config.models) {
|
|
1129
1255
|
if (!mergedConfig.models) mergedConfig.models = {};
|
|
@@ -1157,6 +1283,11 @@ export function loadMergedWaveConfig(
|
|
|
1157
1283
|
: undefined,
|
|
1158
1284
|
language: mergedConfig.language,
|
|
1159
1285
|
autoMemoryEnabled: mergedConfig.autoMemoryEnabled,
|
|
1286
|
+
marketplaces:
|
|
1287
|
+
mergedConfig.marketplaces &&
|
|
1288
|
+
Object.keys(mergedConfig.marketplaces).length > 0
|
|
1289
|
+
? mergedConfig.marketplaces
|
|
1290
|
+
: undefined,
|
|
1160
1291
|
models:
|
|
1161
1292
|
mergedConfig.models && Object.keys(mergedConfig.models).length > 0
|
|
1162
1293
|
? mergedConfig.models
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
|
+
import * as fsSync from "fs";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import {
|
|
4
5
|
PluginManifest,
|
|
@@ -13,21 +14,79 @@ import { parseSkillFile } from "../utils/skillParser.js";
|
|
|
13
14
|
import { resolveMcpConfig } from "../managers/mcpManager.js";
|
|
14
15
|
|
|
15
16
|
export class PluginLoader {
|
|
17
|
+
/**
|
|
18
|
+
* Finds the first existing plugin manifest path.
|
|
19
|
+
* Prefers .wave-plugin/ for backward compatibility, falls back to .claude-plugin/.
|
|
20
|
+
* Returns null if neither exists.
|
|
21
|
+
*/
|
|
22
|
+
private static findPluginManifestPath(pluginPath: string): string | null {
|
|
23
|
+
const waveManifestPath = path.join(
|
|
24
|
+
pluginPath,
|
|
25
|
+
".wave-plugin",
|
|
26
|
+
"plugin.json",
|
|
27
|
+
);
|
|
28
|
+
const claudeManifestPath = path.join(
|
|
29
|
+
pluginPath,
|
|
30
|
+
".claude-plugin",
|
|
31
|
+
"plugin.json",
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Check .wave-plugin first for backward compatibility
|
|
35
|
+
try {
|
|
36
|
+
const waveStat = fsSync.statSync(waveManifestPath);
|
|
37
|
+
if (waveStat.isFile()) {
|
|
38
|
+
return waveManifestPath;
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
// .wave-plugin/plugin.json doesn't exist
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const claudeStat = fsSync.statSync(claudeManifestPath);
|
|
46
|
+
if (claudeStat.isFile()) {
|
|
47
|
+
return claudeManifestPath;
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// .claude-plugin/plugin.json doesn't exist
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
16
56
|
/**
|
|
17
57
|
* Load and validate a plugin manifest from a directory
|
|
18
58
|
* @param pluginPath Absolute path to the plugin directory
|
|
19
59
|
*/
|
|
20
60
|
static async loadManifest(pluginPath: string): Promise<PluginManifest> {
|
|
21
|
-
const
|
|
22
|
-
|
|
61
|
+
const manifestPath = this.findPluginManifestPath(pluginPath);
|
|
62
|
+
if (!manifestPath) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Plugin manifest not found at ${pluginPath}. Neither .wave-plugin/plugin.json nor .claude-plugin/plugin.json exists.`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
23
67
|
|
|
24
|
-
//
|
|
68
|
+
// Determine which directory is being used for validation
|
|
69
|
+
const pluginDirName = manifestPath.includes(".claude-plugin")
|
|
70
|
+
? ".claude-plugin"
|
|
71
|
+
: ".wave-plugin";
|
|
72
|
+
const pluginDirPath = path.join(pluginPath, pluginDirName);
|
|
73
|
+
|
|
74
|
+
// T018: Ensure plugin.json is the only file in the manifest directory
|
|
75
|
+
// For .claude-plugin/, marketplace.json is also allowed (Claude Code convention)
|
|
25
76
|
try {
|
|
26
|
-
const entries = await fs.readdir(
|
|
27
|
-
const
|
|
77
|
+
const entries = await fs.readdir(pluginDirPath);
|
|
78
|
+
const allowedFiles = ["plugin.json"];
|
|
79
|
+
if (pluginDirName === ".claude-plugin") {
|
|
80
|
+
allowedFiles.push("marketplace.json");
|
|
81
|
+
}
|
|
82
|
+
const misplaced = entries.filter((e) => !allowedFiles.includes(e));
|
|
28
83
|
if (misplaced.length > 0) {
|
|
84
|
+
const allowedMsg =
|
|
85
|
+
pluginDirName === ".claude-plugin"
|
|
86
|
+
? "Only plugin.json and marketplace.json should be in this directory."
|
|
87
|
+
: "Only plugin.json should be in this directory.";
|
|
29
88
|
throw new Error(
|
|
30
|
-
`Misplaced files/directories in
|
|
89
|
+
`Misplaced files/directories in ${pluginDirName}/: ${misplaced.join(", ")}. ${allowedMsg}`,
|
|
31
90
|
);
|
|
32
91
|
}
|
|
33
92
|
} catch (error) {
|
|
@@ -36,7 +95,7 @@ export class PluginLoader {
|
|
|
36
95
|
(error as { code?: string }).code === "ENOENT"
|
|
37
96
|
) {
|
|
38
97
|
throw new Error(
|
|
39
|
-
`Plugin manifest directory not found at ${
|
|
98
|
+
`Plugin manifest directory not found at ${pluginDirPath}`,
|
|
40
99
|
);
|
|
41
100
|
}
|
|
42
101
|
throw error;
|
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
import type { HookEvent, HookEventConfig } from "./hooks.js";
|
|
10
10
|
import type { PermissionMode } from "./permissions.js";
|
|
11
11
|
import type { ModelConfig } from "./config.js";
|
|
12
|
+
import type { MarketplaceSource } from "./marketplace.js";
|
|
12
13
|
|
|
13
14
|
export type Scope = "user" | "project" | "local";
|
|
14
15
|
|
|
16
|
+
export interface MarketplaceConfig {
|
|
17
|
+
source: MarketplaceSource;
|
|
18
|
+
autoUpdate?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Root configuration structure for all Wave Agent settings including hooks and environment variables
|
|
17
23
|
*/
|
|
@@ -39,6 +45,8 @@ export interface WaveConfiguration {
|
|
|
39
45
|
autoMemoryFrequency?: number;
|
|
40
46
|
/** Model-specific configuration overrides */
|
|
41
47
|
models?: Record<string, Partial<ModelConfig>>;
|
|
48
|
+
/** Scoped marketplace declarations */
|
|
49
|
+
marketplaces?: Record<string, MarketplaceConfig>;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
/**
|
package/src/types/marketplace.ts
CHANGED
|
@@ -7,8 +7,24 @@ export interface MarketplaceOwner {
|
|
|
7
7
|
|
|
8
8
|
export interface MarketplacePluginEntry {
|
|
9
9
|
name: string;
|
|
10
|
-
source: string;
|
|
10
|
+
source: string | MarketplaceSource;
|
|
11
11
|
description: string;
|
|
12
|
+
/** Claude Code compatibility: plugin category */
|
|
13
|
+
category?: string;
|
|
14
|
+
/** Claude Code compatibility: plugin tags */
|
|
15
|
+
tags?: string[];
|
|
16
|
+
/** Claude Code compatibility: when false, plugin.json is optional */
|
|
17
|
+
strict?: boolean;
|
|
18
|
+
/** Claude Code compatibility: inline manifest fields */
|
|
19
|
+
version?: string;
|
|
20
|
+
author?: { name: string; url?: string };
|
|
21
|
+
homepage?: string;
|
|
22
|
+
repository?: string;
|
|
23
|
+
license?: string;
|
|
24
|
+
keywords?: string[];
|
|
25
|
+
commands?: string;
|
|
26
|
+
skills?: string;
|
|
27
|
+
agents?: string;
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
export interface MarketplacePluginStatus extends MarketplacePluginEntry {
|
|
@@ -24,6 +40,8 @@ export interface MarketplaceManifest {
|
|
|
24
40
|
name: string;
|
|
25
41
|
owner: MarketplaceOwner;
|
|
26
42
|
plugins: MarketplacePluginEntry[];
|
|
43
|
+
/** Claude Code compatibility: additional metadata */
|
|
44
|
+
metadata?: Record<string, unknown>;
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
export type MarketplaceSource =
|
|
@@ -40,6 +58,11 @@ export type MarketplaceSource =
|
|
|
40
58
|
source: "git";
|
|
41
59
|
url: string;
|
|
42
60
|
ref?: string;
|
|
61
|
+
}
|
|
62
|
+
| {
|
|
63
|
+
source: "url";
|
|
64
|
+
url: string;
|
|
65
|
+
ref?: string;
|
|
43
66
|
};
|
|
44
67
|
|
|
45
68
|
export interface KnownMarketplace {
|
|
@@ -48,6 +71,8 @@ export interface KnownMarketplace {
|
|
|
48
71
|
isBuiltin?: boolean;
|
|
49
72
|
autoUpdate?: boolean;
|
|
50
73
|
lastUpdated?: string;
|
|
74
|
+
/** The scope where this marketplace was declared (user, project, local, or builtin) */
|
|
75
|
+
declaredScope?: "user" | "project" | "local" | "builtin";
|
|
51
76
|
}
|
|
52
77
|
|
|
53
78
|
export interface KnownMarketplacesRegistry {
|
package/src/types/plugins.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { McpConfig } from "./mcp.js";
|
|
|
5
5
|
import { PartialHookConfiguration } from "./configuration.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Plugin manifest structure (.wave-plugin/plugin.json)
|
|
8
|
+
* Plugin manifest structure (.wave-plugin/plugin.json or .claude-plugin/plugin.json)
|
|
9
9
|
*/
|
|
10
10
|
export interface PluginManifest {
|
|
11
11
|
name: string;
|
|
@@ -14,6 +14,18 @@ export interface PluginManifest {
|
|
|
14
14
|
author?: {
|
|
15
15
|
name: string;
|
|
16
16
|
};
|
|
17
|
+
/** Claude Code compatibility: plugin keywords */
|
|
18
|
+
keywords?: string[];
|
|
19
|
+
/** Claude Code compatibility: plugin homepage URL */
|
|
20
|
+
homepage?: string;
|
|
21
|
+
/** Claude Code compatibility: repository info */
|
|
22
|
+
repository?: string;
|
|
23
|
+
/** Claude Code compatibility: license info */
|
|
24
|
+
license?: string;
|
|
25
|
+
/** Claude Code compatibility: plugin dependencies */
|
|
26
|
+
dependencies?: Record<string, string>;
|
|
27
|
+
/** Claude Code compatibility: user configuration schema */
|
|
28
|
+
userConfig?: Record<string, unknown>;
|
|
17
29
|
}
|
|
18
30
|
|
|
19
31
|
/**
|