kist 0.1.44 → 0.1.57
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/js/actions/DirectoryCleanAction/DirectoryCleanAction.js +2 -2
- package/js/actions/SvgPackagerAction/SvgPackagerAction.js +2 -2
- package/js/actions/TemplateRenderAction/TemplateRenderAction.js +1 -1
- package/js/actions/VersionWriteAction/VersionWriteAction.js +2 -2
- package/js/config/actions.config.d.ts +35 -0
- package/js/config/actions.config.js +119 -0
- package/js/core/config/ConfigLoader.js +1 -30
- package/js/core/pipeline/Action.d.ts +1 -1
- package/js/core/pipeline/Action.js +1 -1
- package/js/core/pipeline/ActionRegistry.d.ts +5 -1
- package/js/core/pipeline/ActionRegistry.js +18 -27
- package/js/core/pipeline/Step.js +1 -1
- package/js/core/plugin/PluginManager.d.ts +70 -0
- package/js/core/plugin/PluginManager.js +288 -0
- package/js/index.d.ts +7 -0
- package/js/index.js +14 -1
- package/js/interface/ActionPlugin.d.ts +43 -0
- package/js/interface/PluginMetadata.d.ts +33 -0
- package/js/interface/PluginMetadata.js +5 -0
- package/js/kist.js +25 -4
- package/package.json +17 -9
- package/ts/actions/DirectoryCleanAction/DirectoryCleanAction.ts +2 -2
- package/ts/actions/StyleProcessingAction/postcss.config.expanded.ts +0 -1
- package/ts/actions/SvgPackagerAction/SvgPackagerAction.ts +2 -2
- package/ts/actions/TemplateRenderAction/TemplateRenderAction.ts +1 -1
- package/ts/actions/VersionWriteAction/VersionWriteAction.ts +6 -2
- package/ts/config/actions.config.ts +137 -0
- package/ts/core/config/ConfigLoader.ts +1 -35
- package/ts/core/config/ConfigStore copy.ts +27 -1
- package/ts/core/pipeline/Action.ts +1 -1
- package/ts/core/pipeline/ActionRegistry.ts +22 -36
- package/ts/core/pipeline/Step.ts +1 -1
- package/ts/core/plugin/PluginManager.ts +310 -0
- package/ts/index.ts +25 -0
- package/ts/interface/ActionPlugin.ts +48 -1
- package/ts/interface/PluginMetadata.ts +43 -0
- package/ts/interface/SVG.ts +2 -0
- package/ts/kist.ts +25 -2
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Core Actions Configuration
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Defines which actions are considered "core" and will remain in the main
|
|
7
|
+
* kist package. All other actions should be moved to separate plugin packages.
|
|
8
|
+
*
|
|
9
|
+
* Core Actions (Lightweight essentials):
|
|
10
|
+
* - Directory operations (create, copy, clean)
|
|
11
|
+
* - File operations (copy, rename)
|
|
12
|
+
* - Version management
|
|
13
|
+
* - Template rendering (lightweight)
|
|
14
|
+
*
|
|
15
|
+
* Plugin Actions (Move to separate repos):
|
|
16
|
+
* - StyleProcessingAction -> @kist/action-sass
|
|
17
|
+
* - TypeScriptCompilerAction -> @kist/action-typescript
|
|
18
|
+
* - JavaScriptMinifyAction -> @kist/action-terser
|
|
19
|
+
* - SvgPackagerAction -> @kist/action-svg
|
|
20
|
+
* - SvgSpriteAction -> @kist/action-svg
|
|
21
|
+
* - SvgToPngAction -> @kist/action-svg
|
|
22
|
+
* - SvgReaderAction -> @kist/action-svg
|
|
23
|
+
* - LintAction -> @kist/action-lint
|
|
24
|
+
* - DocumentationAction -> @kist/action-docs
|
|
25
|
+
* - PackageManagerAction -> @kist/action-package-manager
|
|
26
|
+
* - RunScriptAction -> @kist/action-scripts
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export const CORE_ACTIONS = [
|
|
30
|
+
"DirectoryCleanAction",
|
|
31
|
+
"DirectoryCopyAction",
|
|
32
|
+
"DirectoryCreateAction",
|
|
33
|
+
"FileCopyAction",
|
|
34
|
+
"FileRenameAction",
|
|
35
|
+
"TemplateRenderAction",
|
|
36
|
+
"VersionWriteAction",
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
export const PLUGIN_ACTIONS = [
|
|
40
|
+
// @kist/action-sass
|
|
41
|
+
"StyleProcessingAction",
|
|
42
|
+
|
|
43
|
+
// @kist/action-typescript
|
|
44
|
+
"TypeScriptCompilerAction",
|
|
45
|
+
|
|
46
|
+
// @kist/action-terser
|
|
47
|
+
"JavaScriptMinifyAction",
|
|
48
|
+
|
|
49
|
+
// @kist/action-svg
|
|
50
|
+
"SvgPackagerAction",
|
|
51
|
+
"SvgReaderAction",
|
|
52
|
+
"SvgSpriteAction",
|
|
53
|
+
"SvgToPngAction",
|
|
54
|
+
|
|
55
|
+
// @kist/action-lint
|
|
56
|
+
"LintAction",
|
|
57
|
+
|
|
58
|
+
// @kist/action-docs
|
|
59
|
+
"DocumentationAction",
|
|
60
|
+
|
|
61
|
+
// @kist/action-package-manager
|
|
62
|
+
"PackageManagerAction",
|
|
63
|
+
|
|
64
|
+
// @kist/action-scripts
|
|
65
|
+
"RunScriptAction",
|
|
66
|
+
] as const;
|
|
67
|
+
|
|
68
|
+
export type CoreActionName = (typeof CORE_ACTIONS)[number];
|
|
69
|
+
export type PluginActionName = (typeof PLUGIN_ACTIONS)[number];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Plugin package mappings for migration guide
|
|
73
|
+
*/
|
|
74
|
+
export const PLUGIN_PACKAGES: Record<
|
|
75
|
+
PluginActionName,
|
|
76
|
+
{
|
|
77
|
+
package: string;
|
|
78
|
+
npm: string;
|
|
79
|
+
github: string;
|
|
80
|
+
}
|
|
81
|
+
> = {
|
|
82
|
+
StyleProcessingAction: {
|
|
83
|
+
package: "@kist/action-sass",
|
|
84
|
+
npm: "npm install --save-dev @kist/action-sass",
|
|
85
|
+
github: "https://github.com/getkist/action-sass",
|
|
86
|
+
},
|
|
87
|
+
TypeScriptCompilerAction: {
|
|
88
|
+
package: "@kist/action-typescript",
|
|
89
|
+
npm: "npm install --save-dev @kist/action-typescript",
|
|
90
|
+
github: "https://github.com/getkist/action-typescript",
|
|
91
|
+
},
|
|
92
|
+
JavaScriptMinifyAction: {
|
|
93
|
+
package: "@kist/action-terser",
|
|
94
|
+
npm: "npm install --save-dev @kist/action-terser",
|
|
95
|
+
github: "https://github.com/getkist/action-terser",
|
|
96
|
+
},
|
|
97
|
+
SvgPackagerAction: {
|
|
98
|
+
package: "@kist/action-svg",
|
|
99
|
+
npm: "npm install --save-dev @kist/action-svg",
|
|
100
|
+
github: "https://github.com/getkist/action-svg",
|
|
101
|
+
},
|
|
102
|
+
SvgReaderAction: {
|
|
103
|
+
package: "@kist/action-svg",
|
|
104
|
+
npm: "npm install --save-dev @kist/action-svg",
|
|
105
|
+
github: "https://github.com/getkist/action-svg",
|
|
106
|
+
},
|
|
107
|
+
SvgSpriteAction: {
|
|
108
|
+
package: "@kist/action-svg",
|
|
109
|
+
npm: "npm install --save-dev @kist/action-svg",
|
|
110
|
+
github: "https://github.com/getkist/action-svg",
|
|
111
|
+
},
|
|
112
|
+
SvgToPngAction: {
|
|
113
|
+
package: "@kist/action-svg",
|
|
114
|
+
npm: "npm install --save-dev @kist/action-svg",
|
|
115
|
+
github: "https://github.com/getkist/action-svg",
|
|
116
|
+
},
|
|
117
|
+
LintAction: {
|
|
118
|
+
package: "@kist/action-lint",
|
|
119
|
+
npm: "npm install --save-dev @kist/action-lint",
|
|
120
|
+
github: "https://github.com/getkist/action-lint",
|
|
121
|
+
},
|
|
122
|
+
DocumentationAction: {
|
|
123
|
+
package: "@kist/action-docs",
|
|
124
|
+
npm: "npm install --save-dev @kist/action-docs",
|
|
125
|
+
github: "https://github.com/getkist/action-docs",
|
|
126
|
+
},
|
|
127
|
+
PackageManagerAction: {
|
|
128
|
+
package: "@kist/action-package-manager",
|
|
129
|
+
npm: "npm install --save-dev @kist/action-package-manager",
|
|
130
|
+
github: "https://github.com/getkist/action-package-manager",
|
|
131
|
+
},
|
|
132
|
+
RunScriptAction: {
|
|
133
|
+
package: "@kist/action-scripts",
|
|
134
|
+
npm: "npm install --save-dev @kist/action-scripts",
|
|
135
|
+
github: "https://github.com/getkist/action-scripts",
|
|
136
|
+
},
|
|
137
|
+
};
|
|
@@ -71,7 +71,7 @@ export class ConfigLoader extends AbstractProcess {
|
|
|
71
71
|
this.configPath = resolvedPath;
|
|
72
72
|
this.logDebug(`Configuration file found: ${resolvedPath}`);
|
|
73
73
|
return;
|
|
74
|
-
} catch (
|
|
74
|
+
} catch (_error) {
|
|
75
75
|
this.logDebug(`File not accessible: ${resolvedPath}`);
|
|
76
76
|
|
|
77
77
|
// ❗ If user explicitly provided --config and it fails, stop immediately
|
|
@@ -87,40 +87,6 @@ export class ConfigLoader extends AbstractProcess {
|
|
|
87
87
|
"No configuration file found. Proceeding with default settings.",
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
|
-
// public async initialize(): Promise<void> {
|
|
91
|
-
// const parser = new ArgumentParser();
|
|
92
|
-
// const cliFlags = parser.getAllFlags();
|
|
93
|
-
// const cliPath =
|
|
94
|
-
// typeof cliFlags.config === "string" ? cliFlags.config : undefined;
|
|
95
|
-
|
|
96
|
-
// const searchPaths = cliPath ? [cliPath] : this.defaultFilenames;
|
|
97
|
-
|
|
98
|
-
// this.logDebug(`Current working directory: ${process.cwd()}`);
|
|
99
|
-
// this.logDebug(
|
|
100
|
-
// `Searching for config file${cliPath ? ` from --config=${cliPath}` : ""}...`,
|
|
101
|
-
// );
|
|
102
|
-
|
|
103
|
-
// for (const fileName of searchPaths) {
|
|
104
|
-
// const resolvedPath = path.resolve(process.cwd(), fileName);
|
|
105
|
-
// this.logDebug(`Checking: ${resolvedPath}`);
|
|
106
|
-
|
|
107
|
-
// try {
|
|
108
|
-
// await fs.promises.access(
|
|
109
|
-
// resolvedPath,
|
|
110
|
-
// fs.constants.F_OK | fs.constants.R_OK,
|
|
111
|
-
// );
|
|
112
|
-
// this.configPath = resolvedPath;
|
|
113
|
-
// this.logDebug(`Configuration file found: ${resolvedPath}`);
|
|
114
|
-
// return;
|
|
115
|
-
// } catch (error) {
|
|
116
|
-
// this.logDebug(`File not accessible: ${resolvedPath}`);
|
|
117
|
-
// }
|
|
118
|
-
// }
|
|
119
|
-
|
|
120
|
-
// this.logWarn(
|
|
121
|
-
// "No configuration file found. Proceeding with default settings.",
|
|
122
|
-
// );
|
|
123
|
-
// }
|
|
124
90
|
|
|
125
91
|
/**
|
|
126
92
|
* Loads and validates the configuration file.
|
|
@@ -97,13 +97,39 @@ export class ConfigStore extends AbstractProcess {
|
|
|
97
97
|
|
|
98
98
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
99
99
|
const k = keys[i];
|
|
100
|
+
|
|
101
|
+
// Guard against prototype pollution
|
|
102
|
+
if (
|
|
103
|
+
k === "__proto__" ||
|
|
104
|
+
k === "constructor" ||
|
|
105
|
+
k === "prototype"
|
|
106
|
+
) {
|
|
107
|
+
this.logWarn(
|
|
108
|
+
`Attempted to set protected key "${k}". This operation is blocked for security reasons.`,
|
|
109
|
+
);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
100
113
|
if (!current[k] || typeof current[k] !== "object") {
|
|
101
114
|
current[k] = {};
|
|
102
115
|
}
|
|
103
116
|
current = current[k];
|
|
104
117
|
}
|
|
105
118
|
|
|
106
|
-
|
|
119
|
+
// Guard against prototype pollution for the final key
|
|
120
|
+
const finalKey = keys[keys.length - 1];
|
|
121
|
+
if (
|
|
122
|
+
finalKey === "__proto__" ||
|
|
123
|
+
finalKey === "constructor" ||
|
|
124
|
+
finalKey === "prototype"
|
|
125
|
+
) {
|
|
126
|
+
this.logWarn(
|
|
127
|
+
`Attempted to set protected key "${finalKey}". This operation is blocked for security reasons.`,
|
|
128
|
+
);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
current[finalKey] = value;
|
|
107
133
|
this.logDebug(
|
|
108
134
|
`Set configuration key "${key}" to: ${JSON.stringify(value)}`,
|
|
109
135
|
);
|
|
@@ -55,7 +55,7 @@ export abstract class Action
|
|
|
55
55
|
* @returns A boolean indicating whether the options are valid. Default
|
|
56
56
|
* implementation always returns true.
|
|
57
57
|
*/
|
|
58
|
-
validateOptions(
|
|
58
|
+
validateOptions(_options: ActionOptionsType): boolean {
|
|
59
59
|
// Default validation: always returns true, can be overridden in
|
|
60
60
|
// derived classes
|
|
61
61
|
return true;
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
// Import
|
|
3
3
|
// ============================================================================
|
|
4
4
|
|
|
5
|
-
import { readdirSync } from "fs";
|
|
6
|
-
import { join } from "path";
|
|
7
5
|
import { coreActions } from "../../actions/CoreActions";
|
|
8
6
|
import { ActionInterface } from "../../interface/ActionInterface";
|
|
9
|
-
import { ActionPlugin } from "../../interface/ActionPlugin";
|
|
10
7
|
import { AbstractProcess } from "../abstract/AbstractProcess";
|
|
8
|
+
import { PluginManager } from "../plugin/PluginManager";
|
|
11
9
|
|
|
12
10
|
// ============================================================================
|
|
13
11
|
// Class
|
|
@@ -46,7 +44,8 @@ export class ActionRegistry extends AbstractProcess {
|
|
|
46
44
|
this.registry = new Map();
|
|
47
45
|
// Automatically register core actions
|
|
48
46
|
this.registerCoreActions();
|
|
49
|
-
|
|
47
|
+
// Register plugin actions via PluginManager
|
|
48
|
+
this.registerPluginActions();
|
|
50
49
|
this.logInfo("ActionRegistry initialized.");
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -169,40 +168,27 @@ export class ActionRegistry extends AbstractProcess {
|
|
|
169
168
|
this.logInfo("Core actions registered successfully.");
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
plugin &&
|
|
190
|
-
typeof plugin.registerActions === "function"
|
|
191
|
-
) {
|
|
192
|
-
const actions = plugin.registerActions();
|
|
193
|
-
for (const [name, actionClass] of Object.entries(
|
|
194
|
-
actions,
|
|
195
|
-
)) {
|
|
196
|
-
this.registerAction(actionClass);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
171
|
+
/**
|
|
172
|
+
* Registers actions from loaded plugins via PluginManager.
|
|
173
|
+
* This method integrates the plugin system with the action registry.
|
|
174
|
+
*/
|
|
175
|
+
private registerPluginActions(): void {
|
|
176
|
+
const pluginManager = PluginManager.getInstance();
|
|
177
|
+
const pluginActions = pluginManager.getPluginActions();
|
|
178
|
+
|
|
179
|
+
for (const [actionName, actionClass] of pluginActions.entries()) {
|
|
180
|
+
try {
|
|
181
|
+
this.registerAction(actionClass);
|
|
182
|
+
this.logDebug(`Registered plugin action: ${actionName}`);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.logError(
|
|
185
|
+
`Failed to register plugin action ${actionName}:`,
|
|
186
|
+
error,
|
|
187
|
+
);
|
|
200
188
|
}
|
|
201
|
-
|
|
202
|
-
this.logInfo("Plugins loaded successfully.");
|
|
203
|
-
} catch (error) {
|
|
204
|
-
this.logError("Failed to discover plugins.", error);
|
|
205
189
|
}
|
|
190
|
+
|
|
191
|
+
this.logInfo(`Registered ${pluginActions.size} actions from plugins.`);
|
|
206
192
|
}
|
|
207
193
|
|
|
208
194
|
/**
|
package/ts/core/pipeline/Step.ts
CHANGED
|
@@ -46,7 +46,7 @@ export class Step extends AbstractProcess {
|
|
|
46
46
|
// const ActionClass = actionRegistry.getAction(step.action.name);
|
|
47
47
|
const ActionClass = actionRegistry.getAction(String(step.action));
|
|
48
48
|
if (!ActionClass) {
|
|
49
|
-
|
|
49
|
+
const msg = `
|
|
50
50
|
Unknown action "${step.action}" for step "${this.name}".
|
|
51
51
|
Ensure the action is registered in the registry.
|
|
52
52
|
`;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Import
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
import { readdirSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { ActionInterface } from "../../interface/ActionInterface";
|
|
8
|
+
import { ActionPlugin } from "../../interface/ActionPlugin";
|
|
9
|
+
import { PluginMetadata } from "../../interface/PluginMetadata";
|
|
10
|
+
import { AbstractProcess } from "../abstract/AbstractProcess";
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Class
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* PluginManager handles discovery, loading, and lifecycle of kist plugins.
|
|
18
|
+
* Supports both npm-installed plugins (@kist/plugin-*) and local plugins.
|
|
19
|
+
*/
|
|
20
|
+
export class PluginManager extends AbstractProcess {
|
|
21
|
+
// Parameters
|
|
22
|
+
// ========================================================================
|
|
23
|
+
|
|
24
|
+
private static instance: PluginManager | null = null;
|
|
25
|
+
private loadedPlugins: Map<string, PluginMetadata> = new Map();
|
|
26
|
+
private pluginActions: Map<string, new () => ActionInterface> = new Map();
|
|
27
|
+
|
|
28
|
+
// Constructor
|
|
29
|
+
// ========================================================================
|
|
30
|
+
|
|
31
|
+
private constructor() {
|
|
32
|
+
super();
|
|
33
|
+
this.logInfo("PluginManager initialized.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Singleton Methods
|
|
37
|
+
// ========================================================================
|
|
38
|
+
|
|
39
|
+
public static getInstance(): PluginManager {
|
|
40
|
+
if (!PluginManager.instance) {
|
|
41
|
+
PluginManager.instance = new PluginManager();
|
|
42
|
+
}
|
|
43
|
+
return PluginManager.instance;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public static resetInstance(): void {
|
|
47
|
+
PluginManager.instance = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Plugin Discovery
|
|
51
|
+
// ========================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Discovers and loads all available plugins from:
|
|
55
|
+
* - node_modules/@kist/plugin-*
|
|
56
|
+
* - node_modules/kist-plugin-*
|
|
57
|
+
* - Local plugins directory (if configured)
|
|
58
|
+
*/
|
|
59
|
+
public async discoverPlugins(options?: {
|
|
60
|
+
localPluginsPath?: string;
|
|
61
|
+
pluginPrefixes?: string[];
|
|
62
|
+
}): Promise<void> {
|
|
63
|
+
this.logInfo("Starting plugin discovery...");
|
|
64
|
+
|
|
65
|
+
const prefixes = options?.pluginPrefixes || [
|
|
66
|
+
"@kist/action-",
|
|
67
|
+
"kist-plugin-",
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// Discover npm plugins
|
|
71
|
+
await this.discoverNpmPlugins(prefixes);
|
|
72
|
+
|
|
73
|
+
// Discover local plugins if path provided
|
|
74
|
+
if (options?.localPluginsPath) {
|
|
75
|
+
await this.discoverLocalPlugins(options.localPluginsPath);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.logInfo(
|
|
79
|
+
`Plugin discovery complete. Loaded ${this.loadedPlugins.size} plugins.`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Discovers plugins installed via npm with specified prefixes
|
|
85
|
+
*/
|
|
86
|
+
private async discoverNpmPlugins(prefixes: string[]): Promise<void> {
|
|
87
|
+
const nodeModulesPath = join(process.cwd(), "node_modules");
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const directories = readdirSync(nodeModulesPath, {
|
|
91
|
+
withFileTypes: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
for (const dir of directories) {
|
|
95
|
+
// Check for scoped packages (@kist/plugin-*)
|
|
96
|
+
if (dir.isDirectory() && dir.name.startsWith("@")) {
|
|
97
|
+
await this.discoverScopedPlugins(
|
|
98
|
+
join(nodeModulesPath, dir.name),
|
|
99
|
+
prefixes,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check for non-scoped packages (kist-plugin-*)
|
|
104
|
+
for (const prefix of prefixes) {
|
|
105
|
+
if (
|
|
106
|
+
dir.isDirectory() &&
|
|
107
|
+
!prefix.startsWith("@") &&
|
|
108
|
+
dir.name.startsWith(prefix)
|
|
109
|
+
) {
|
|
110
|
+
await this.loadPlugin(
|
|
111
|
+
join(nodeModulesPath, dir.name),
|
|
112
|
+
dir.name,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
this.logError("Failed to discover npm plugins.", error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Discovers plugins in scoped packages (@kist/*)
|
|
124
|
+
*/
|
|
125
|
+
private async discoverScopedPlugins(
|
|
126
|
+
scopePath: string,
|
|
127
|
+
prefixes: string[],
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
try {
|
|
130
|
+
const packages = readdirSync(scopePath, { withFileTypes: true });
|
|
131
|
+
|
|
132
|
+
for (const pkg of packages) {
|
|
133
|
+
for (const prefix of prefixes) {
|
|
134
|
+
const scopePrefix = prefix.split("/")[1]; // Extract "plugin-" from "@kist/plugin-"
|
|
135
|
+
if (
|
|
136
|
+
pkg.isDirectory() &&
|
|
137
|
+
scopePrefix &&
|
|
138
|
+
pkg.name.startsWith(scopePrefix)
|
|
139
|
+
) {
|
|
140
|
+
const fullName = `${scopePath.split("/").pop()}/${pkg.name}`;
|
|
141
|
+
await this.loadPlugin(
|
|
142
|
+
join(scopePath, pkg.name),
|
|
143
|
+
fullName,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (_error) {
|
|
149
|
+
this.logDebug(`No scoped plugins found in ${scopePath}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Discovers plugins from a local directory
|
|
155
|
+
*/
|
|
156
|
+
private async discoverLocalPlugins(localPath: string): Promise<void> {
|
|
157
|
+
try {
|
|
158
|
+
const pluginPath = join(process.cwd(), localPath);
|
|
159
|
+
const directories = readdirSync(pluginPath, {
|
|
160
|
+
withFileTypes: true,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
for (const dir of directories) {
|
|
164
|
+
if (dir.isDirectory()) {
|
|
165
|
+
await this.loadPlugin(
|
|
166
|
+
join(pluginPath, dir.name),
|
|
167
|
+
`local:${dir.name}`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch (_error) {
|
|
172
|
+
this.logDebug(`No local plugins found at ${localPath}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Plugin Loading
|
|
177
|
+
// ========================================================================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Loads a single plugin from the specified path
|
|
181
|
+
*/
|
|
182
|
+
private async loadPlugin(
|
|
183
|
+
pluginPath: string,
|
|
184
|
+
pluginName: string,
|
|
185
|
+
): Promise<void> {
|
|
186
|
+
try {
|
|
187
|
+
this.logDebug(`Loading plugin: ${pluginName}`);
|
|
188
|
+
|
|
189
|
+
const pluginModule = await import(pluginPath);
|
|
190
|
+
const plugin: ActionPlugin = pluginModule.default || pluginModule;
|
|
191
|
+
|
|
192
|
+
if (!plugin || typeof plugin.registerActions !== "function") {
|
|
193
|
+
this.logWarn(
|
|
194
|
+
`Plugin ${pluginName} does not implement ActionPlugin interface.`,
|
|
195
|
+
);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Get plugin metadata if available
|
|
200
|
+
const metadata: PluginMetadata = {
|
|
201
|
+
name: pluginName,
|
|
202
|
+
version:
|
|
203
|
+
typeof plugin.version === "string"
|
|
204
|
+
? plugin.version
|
|
205
|
+
: "unknown",
|
|
206
|
+
description:
|
|
207
|
+
typeof plugin.description === "string"
|
|
208
|
+
? plugin.description
|
|
209
|
+
: undefined,
|
|
210
|
+
actions: [],
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Register actions from the plugin
|
|
214
|
+
const actions = plugin.registerActions();
|
|
215
|
+
for (const [actionName, actionClass] of Object.entries(actions)) {
|
|
216
|
+
this.pluginActions.set(actionName, actionClass);
|
|
217
|
+
metadata.actions.push(actionName);
|
|
218
|
+
this.logDebug(` - Registered action: ${actionName}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.loadedPlugins.set(pluginName, metadata);
|
|
222
|
+
this.logInfo(
|
|
223
|
+
`Plugin "${pluginName}" loaded successfully with ${metadata.actions.length} actions.`,
|
|
224
|
+
);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
this.logError(`Failed to load plugin ${pluginName}:`, error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Manually register a plugin programmatically
|
|
232
|
+
*/
|
|
233
|
+
public registerPlugin(plugin: ActionPlugin, name: string): void {
|
|
234
|
+
this.logInfo(`Manually registering plugin: ${name}`);
|
|
235
|
+
|
|
236
|
+
const metadata: PluginMetadata = {
|
|
237
|
+
name,
|
|
238
|
+
version:
|
|
239
|
+
typeof plugin.version === "string"
|
|
240
|
+
? plugin.version
|
|
241
|
+
: "unknown",
|
|
242
|
+
description:
|
|
243
|
+
typeof plugin.description === "string"
|
|
244
|
+
? plugin.description
|
|
245
|
+
: undefined,
|
|
246
|
+
actions: [],
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const actions = plugin.registerActions();
|
|
250
|
+
for (const [actionName, actionClass] of Object.entries(actions)) {
|
|
251
|
+
this.pluginActions.set(actionName, actionClass);
|
|
252
|
+
metadata.actions.push(actionName);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
this.loadedPlugins.set(name, metadata);
|
|
256
|
+
this.logInfo(
|
|
257
|
+
`Plugin "${name}" registered with ${metadata.actions.length} actions.`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Plugin Queries
|
|
262
|
+
// ========================================================================
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Gets all actions from loaded plugins
|
|
266
|
+
*/
|
|
267
|
+
public getPluginActions(): Map<string, new () => ActionInterface> {
|
|
268
|
+
return new Map(this.pluginActions);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Gets metadata for all loaded plugins
|
|
273
|
+
*/
|
|
274
|
+
public getLoadedPlugins(): PluginMetadata[] {
|
|
275
|
+
return Array.from(this.loadedPlugins.values());
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Gets metadata for a specific plugin
|
|
280
|
+
*/
|
|
281
|
+
public getPluginMetadata(name: string): PluginMetadata | undefined {
|
|
282
|
+
return this.loadedPlugins.get(name);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Checks if a specific plugin is loaded
|
|
287
|
+
*/
|
|
288
|
+
public isPluginLoaded(name: string): boolean {
|
|
289
|
+
return this.loadedPlugins.has(name);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Lists all action names from plugins
|
|
294
|
+
*/
|
|
295
|
+
public listPluginActions(): string[] {
|
|
296
|
+
return Array.from(this.pluginActions.keys());
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Cleanup
|
|
300
|
+
// ========================================================================
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Clears all loaded plugins and their actions
|
|
304
|
+
*/
|
|
305
|
+
public clearPlugins(): void {
|
|
306
|
+
this.loadedPlugins.clear();
|
|
307
|
+
this.pluginActions.clear();
|
|
308
|
+
this.logInfo("All plugins cleared.");
|
|
309
|
+
}
|
|
310
|
+
}
|
package/ts/index.ts
CHANGED
|
@@ -22,3 +22,28 @@ export * from "./types";
|
|
|
22
22
|
|
|
23
23
|
// CLI Functions (if required programmatically)
|
|
24
24
|
export * from "./cli.js";
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Plugin System Exports
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
// Core plugin interfaces and types
|
|
31
|
+
export { ActionInterface } from "./interface/ActionInterface";
|
|
32
|
+
export { ActionPlugin } from "./interface/ActionPlugin";
|
|
33
|
+
export { PluginMetadata } from "./interface/PluginMetadata";
|
|
34
|
+
|
|
35
|
+
// Plugin management
|
|
36
|
+
export { PluginManager } from "./core/plugin/PluginManager";
|
|
37
|
+
|
|
38
|
+
// Action system
|
|
39
|
+
export { Action } from "./core/pipeline/Action";
|
|
40
|
+
export { ActionRegistry } from "./core/pipeline/ActionRegistry";
|
|
41
|
+
|
|
42
|
+
// Configuration for plugin developers
|
|
43
|
+
export {
|
|
44
|
+
CORE_ACTIONS,
|
|
45
|
+
PLUGIN_ACTIONS,
|
|
46
|
+
PLUGIN_PACKAGES,
|
|
47
|
+
type CoreActionName,
|
|
48
|
+
type PluginActionName,
|
|
49
|
+
} from "./config/actions.config";
|