wraptc 1.0.3 → 1.0.4
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/bin/wraptc +4 -4
- package/package.json +2 -2
- package/src/cli/__tests__/cli.test.ts +337 -0
- package/src/cli/index.ts +149 -0
- package/src/core/__tests__/fixtures/configs/project-config.json +14 -0
- package/src/core/__tests__/fixtures/configs/system-config.json +14 -0
- package/src/core/__tests__/fixtures/configs/user-config.json +15 -0
- package/src/core/__tests__/integration/integration.test.ts +241 -0
- package/src/core/__tests__/integration/mock-coder-adapter.test.ts +243 -0
- package/src/core/__tests__/test-utils.ts +136 -0
- package/src/core/__tests__/unit/adapters/runner.test.ts +302 -0
- package/src/core/__tests__/unit/basic-test.test.ts +44 -0
- package/src/core/__tests__/unit/basic.test.ts +12 -0
- package/src/core/__tests__/unit/config.test.ts +244 -0
- package/src/core/__tests__/unit/error-patterns.test.ts +181 -0
- package/src/core/__tests__/unit/memory-monitor.test.ts +354 -0
- package/src/core/__tests__/unit/plugin/registry.test.ts +356 -0
- package/src/core/__tests__/unit/providers/codex.test.ts +173 -0
- package/src/core/__tests__/unit/providers/configurable.test.ts +429 -0
- package/src/core/__tests__/unit/providers/gemini.test.ts +251 -0
- package/src/core/__tests__/unit/providers/opencode.test.ts +258 -0
- package/src/core/__tests__/unit/providers/qwen-code.test.ts +195 -0
- package/src/core/__tests__/unit/providers/simple-codex.test.ts +18 -0
- package/src/core/__tests__/unit/router.test.ts +967 -0
- package/src/core/__tests__/unit/state.test.ts +1079 -0
- package/src/core/__tests__/unit/unified/capabilities.test.ts +186 -0
- package/src/core/__tests__/unit/wrap-terminalcoder.test.ts +32 -0
- package/src/core/adapters/builtin/codex.ts +35 -0
- package/src/core/adapters/builtin/gemini.ts +34 -0
- package/src/core/adapters/builtin/index.ts +31 -0
- package/src/core/adapters/builtin/mock-coder.ts +148 -0
- package/src/core/adapters/builtin/qwen.ts +34 -0
- package/src/core/adapters/define.ts +48 -0
- package/src/core/adapters/index.ts +43 -0
- package/src/core/adapters/loader.ts +143 -0
- package/src/core/adapters/provider-bridge.ts +190 -0
- package/src/core/adapters/runner.ts +437 -0
- package/src/core/adapters/types.ts +172 -0
- package/src/core/config.ts +290 -0
- package/src/core/define-provider.ts +212 -0
- package/src/core/error-patterns.ts +147 -0
- package/src/core/index.ts +130 -0
- package/src/core/memory-monitor.ts +171 -0
- package/src/core/plugin/builtin.ts +87 -0
- package/src/core/plugin/index.ts +34 -0
- package/src/core/plugin/registry.ts +350 -0
- package/src/core/plugin/types.ts +209 -0
- package/src/core/provider-factory.ts +397 -0
- package/src/core/provider-loader.ts +171 -0
- package/src/core/providers/codex.ts +56 -0
- package/src/core/providers/configurable.ts +637 -0
- package/src/core/providers/custom.ts +261 -0
- package/src/core/providers/gemini.ts +41 -0
- package/src/core/providers/index.ts +383 -0
- package/src/core/providers/opencode.ts +168 -0
- package/src/core/providers/qwen-code.ts +41 -0
- package/src/core/router.ts +370 -0
- package/src/core/state.ts +258 -0
- package/src/core/types.ts +206 -0
- package/src/core/unified/capabilities.ts +184 -0
- package/src/core/unified/errors.ts +141 -0
- package/src/core/unified/index.ts +29 -0
- package/src/core/unified/output.ts +189 -0
- package/src/core/wrap-terminalcoder.ts +245 -0
- package/src/mcp/__tests__/server.test.ts +295 -0
- package/src/mcp/server.ts +284 -0
- package/src/test-fixtures/mock-coder.sh +194 -0
- package/dist/cli/index.js +0 -16501
- package/dist/core/index.js +0 -7531
- package/dist/mcp/server.js +0 -14568
- package/dist/wraptc-1.0.3.tgz +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginRegistry - Central registry for terminal coder plugins
|
|
3
|
+
*
|
|
4
|
+
* Singleton pattern with lifecycle management and provider creation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { binaryExists } from "../provider-factory";
|
|
8
|
+
import type { Provider } from "../providers/index";
|
|
9
|
+
import type {
|
|
10
|
+
CreateProviderOptions,
|
|
11
|
+
IPluginRegistry,
|
|
12
|
+
ManagedProvider,
|
|
13
|
+
PluginContext,
|
|
14
|
+
PluginDefinition,
|
|
15
|
+
PluginInfo,
|
|
16
|
+
PluginRegistrationOptions,
|
|
17
|
+
PluginRegistrationResult,
|
|
18
|
+
} from "./types";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* PluginRegistry - Manages plugin registration and provider creation
|
|
22
|
+
*/
|
|
23
|
+
export class PluginRegistry implements IPluginRegistry {
|
|
24
|
+
private static instance: PluginRegistry | null = null;
|
|
25
|
+
|
|
26
|
+
/** Registered plugins by type */
|
|
27
|
+
private plugins = new Map<string, PluginDefinition>();
|
|
28
|
+
|
|
29
|
+
/** Track built-in plugins */
|
|
30
|
+
private builtInTypes = new Set<string>();
|
|
31
|
+
|
|
32
|
+
/** Track initialized providers for lifecycle management */
|
|
33
|
+
private initializedProviders = new Map<string, ManagedProvider>();
|
|
34
|
+
|
|
35
|
+
/** Default context */
|
|
36
|
+
private defaultContext: PluginContext;
|
|
37
|
+
|
|
38
|
+
private constructor() {
|
|
39
|
+
this.defaultContext = {
|
|
40
|
+
registry: this,
|
|
41
|
+
cwd: process.cwd(),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the singleton instance
|
|
47
|
+
*/
|
|
48
|
+
static getInstance(): PluginRegistry {
|
|
49
|
+
if (!PluginRegistry.instance) {
|
|
50
|
+
PluginRegistry.instance = new PluginRegistry();
|
|
51
|
+
}
|
|
52
|
+
return PluginRegistry.instance;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reset the singleton instance (for testing)
|
|
57
|
+
*/
|
|
58
|
+
static resetInstance(): void {
|
|
59
|
+
if (PluginRegistry.instance) {
|
|
60
|
+
// Clean up initialized providers
|
|
61
|
+
PluginRegistry.instance.shutdownAll().catch(console.warn);
|
|
62
|
+
}
|
|
63
|
+
PluginRegistry.instance = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register a plugin (async, with lifecycle hooks)
|
|
68
|
+
*/
|
|
69
|
+
async register(
|
|
70
|
+
plugin: PluginDefinition,
|
|
71
|
+
options: PluginRegistrationOptions = {},
|
|
72
|
+
): Promise<PluginRegistrationResult> {
|
|
73
|
+
const { overwrite = false, builtIn = false } = options;
|
|
74
|
+
|
|
75
|
+
// Check for existing registration
|
|
76
|
+
if (this.plugins.has(plugin.type)) {
|
|
77
|
+
if (!overwrite) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
type: plugin.type,
|
|
81
|
+
message: `Plugin type '${plugin.type}' is already registered.`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Call onUnregister for existing plugin
|
|
86
|
+
const existing = this.plugins.get(plugin.type);
|
|
87
|
+
if (existing?.onUnregister) {
|
|
88
|
+
try {
|
|
89
|
+
await existing.onUnregister(this.defaultContext);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.warn(`Error during onUnregister for plugin '${plugin.type}':`, error);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Register the plugin
|
|
97
|
+
this.plugins.set(plugin.type, plugin);
|
|
98
|
+
|
|
99
|
+
// Mark as built-in if specified
|
|
100
|
+
if (builtIn) {
|
|
101
|
+
this.builtInTypes.add(plugin.type);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Call onRegister hook
|
|
105
|
+
if (plugin.onRegister) {
|
|
106
|
+
try {
|
|
107
|
+
await plugin.onRegister(this.defaultContext);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Rollback on failure
|
|
110
|
+
this.plugins.delete(plugin.type);
|
|
111
|
+
this.builtInTypes.delete(plugin.type);
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
type: plugin.type,
|
|
115
|
+
message: `Plugin onRegister failed: ${
|
|
116
|
+
error instanceof Error ? error.message : String(error)
|
|
117
|
+
}`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
type: plugin.type,
|
|
125
|
+
overwritten: this.plugins.has(plugin.type) && overwrite,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Register a plugin synchronously (no lifecycle hooks called)
|
|
131
|
+
*/
|
|
132
|
+
registerSync(
|
|
133
|
+
plugin: PluginDefinition,
|
|
134
|
+
options: PluginRegistrationOptions = {},
|
|
135
|
+
): PluginRegistrationResult {
|
|
136
|
+
const { overwrite = false, builtIn = false } = options;
|
|
137
|
+
|
|
138
|
+
if (this.plugins.has(plugin.type) && !overwrite) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
type: plugin.type,
|
|
142
|
+
message: `Plugin type '${plugin.type}' is already registered.`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.plugins.set(plugin.type, plugin);
|
|
147
|
+
if (builtIn) {
|
|
148
|
+
this.builtInTypes.add(plugin.type);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { success: true, type: plugin.type };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Unregister a plugin
|
|
156
|
+
*/
|
|
157
|
+
async unregister(type: string): Promise<boolean> {
|
|
158
|
+
const plugin = this.plugins.get(type);
|
|
159
|
+
if (!plugin) return false;
|
|
160
|
+
|
|
161
|
+
// Shutdown any initialized provider
|
|
162
|
+
const provider = this.initializedProviders.get(type);
|
|
163
|
+
if (provider?.shutdown) {
|
|
164
|
+
await provider.shutdown();
|
|
165
|
+
this.initializedProviders.delete(type);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Call onUnregister hook
|
|
169
|
+
if (plugin.onUnregister) {
|
|
170
|
+
await plugin.onUnregister(this.defaultContext);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.plugins.delete(type);
|
|
174
|
+
this.builtInTypes.delete(type);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check if a plugin type is registered
|
|
180
|
+
*/
|
|
181
|
+
has(type: string): boolean {
|
|
182
|
+
return this.plugins.has(type);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get a plugin definition
|
|
187
|
+
*/
|
|
188
|
+
get(type: string): PluginDefinition | undefined {
|
|
189
|
+
return this.plugins.get(type);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get all registered plugin types
|
|
194
|
+
*/
|
|
195
|
+
getTypes(): string[] {
|
|
196
|
+
return Array.from(this.plugins.keys());
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* List all plugins with metadata
|
|
201
|
+
*/
|
|
202
|
+
listPlugins(): PluginInfo[] {
|
|
203
|
+
const infos: PluginInfo[] = [];
|
|
204
|
+
|
|
205
|
+
for (const plugin of this.plugins.values()) {
|
|
206
|
+
infos.push({
|
|
207
|
+
type: plugin.type,
|
|
208
|
+
displayName: plugin.displayName,
|
|
209
|
+
description: plugin.description,
|
|
210
|
+
docsUrl: plugin.docsUrl,
|
|
211
|
+
version: plugin.version,
|
|
212
|
+
capabilities: plugin.capabilities,
|
|
213
|
+
binary: plugin.binary,
|
|
214
|
+
hasLifecycle: plugin.hasLifecycle,
|
|
215
|
+
isBuiltIn: this.builtInTypes.has(plugin.type),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return infos;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get plugin info by type
|
|
224
|
+
*/
|
|
225
|
+
getPluginInfo(type: string): PluginInfo | undefined {
|
|
226
|
+
const plugin = this.plugins.get(type);
|
|
227
|
+
if (!plugin) return undefined;
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
type: plugin.type,
|
|
231
|
+
displayName: plugin.displayName,
|
|
232
|
+
description: plugin.description,
|
|
233
|
+
docsUrl: plugin.docsUrl,
|
|
234
|
+
version: plugin.version,
|
|
235
|
+
capabilities: plugin.capabilities,
|
|
236
|
+
binary: plugin.binary,
|
|
237
|
+
hasLifecycle: plugin.hasLifecycle,
|
|
238
|
+
isBuiltIn: this.builtInTypes.has(type),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Create a provider from a registered plugin
|
|
244
|
+
*/
|
|
245
|
+
createProvider(
|
|
246
|
+
type: string,
|
|
247
|
+
config: unknown,
|
|
248
|
+
options: CreateProviderOptions = {},
|
|
249
|
+
): ManagedProvider {
|
|
250
|
+
const plugin = this.plugins.get(type);
|
|
251
|
+
if (!plugin) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`No plugin registered for type '${type}'. ` +
|
|
254
|
+
`Available types: ${this.getTypes().join(", ") || "none"}`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Use factory function
|
|
259
|
+
const context = options.context ?? this.defaultContext;
|
|
260
|
+
return plugin.factory(config, context) as ManagedProvider;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Create and initialize a provider with lifecycle management
|
|
265
|
+
*/
|
|
266
|
+
async createAndInitProvider(
|
|
267
|
+
type: string,
|
|
268
|
+
config: unknown,
|
|
269
|
+
options: CreateProviderOptions = {},
|
|
270
|
+
): Promise<ManagedProvider> {
|
|
271
|
+
const provider = this.createProvider(type, config, options);
|
|
272
|
+
|
|
273
|
+
// Initialize if lifecycle provider
|
|
274
|
+
if (provider.init) {
|
|
275
|
+
await provider.init();
|
|
276
|
+
this.initializedProviders.set(type, provider);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return provider;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Check if a plugin's binary is available
|
|
284
|
+
*/
|
|
285
|
+
async isAvailable(type: string): Promise<boolean> {
|
|
286
|
+
const plugin = this.plugins.get(type);
|
|
287
|
+
if (!plugin) return false;
|
|
288
|
+
if (!plugin.binary) return true;
|
|
289
|
+
return binaryExists(plugin.binary);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get all available plugin types (with valid binaries)
|
|
294
|
+
*/
|
|
295
|
+
async getAvailableTypes(): Promise<string[]> {
|
|
296
|
+
const types = this.getTypes();
|
|
297
|
+
const availability = await Promise.all(types.map((type) => this.isAvailable(type)));
|
|
298
|
+
return types.filter((_, i) => availability[i]);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Shutdown all initialized providers
|
|
303
|
+
*/
|
|
304
|
+
async shutdownAll(): Promise<void> {
|
|
305
|
+
for (const provider of this.initializedProviders.values()) {
|
|
306
|
+
if (provider.shutdown) {
|
|
307
|
+
try {
|
|
308
|
+
await provider.shutdown();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.warn("Error during provider shutdown:", error);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
this.initializedProviders.clear();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Clear all plugins
|
|
319
|
+
*/
|
|
320
|
+
async clear(): Promise<void> {
|
|
321
|
+
// Shutdown initialized providers
|
|
322
|
+
await this.shutdownAll();
|
|
323
|
+
|
|
324
|
+
// Call onUnregister for all plugins
|
|
325
|
+
for (const plugin of this.plugins.values()) {
|
|
326
|
+
if (plugin.onUnregister) {
|
|
327
|
+
try {
|
|
328
|
+
await plugin.onUnregister(this.defaultContext);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.warn(`Error during onUnregister for plugin '${plugin.type}':`, error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
this.plugins.clear();
|
|
336
|
+
this.builtInTypes.clear();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get the number of registered plugins
|
|
341
|
+
*/
|
|
342
|
+
get size(): number {
|
|
343
|
+
return this.plugins.size;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get the global plugin registry instance
|
|
349
|
+
*/
|
|
350
|
+
export const getPluginRegistry = (): PluginRegistry => PluginRegistry.getInstance();
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin System Types
|
|
3
|
+
*
|
|
4
|
+
* Provides a formal registry for terminal coder plugins with lifecycle hooks
|
|
5
|
+
* and configuration validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Provider } from "../providers/index";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Plugin lifecycle interface
|
|
12
|
+
* Providers can optionally implement these for managed lifecycle
|
|
13
|
+
*/
|
|
14
|
+
export interface IPluginLifecycle {
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the plugin (async setup, warmup, etc.)
|
|
17
|
+
* Called once when the plugin is first used
|
|
18
|
+
*/
|
|
19
|
+
init?(): Promise<void>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Perform health check
|
|
23
|
+
* @returns true if healthy
|
|
24
|
+
*/
|
|
25
|
+
healthcheck?(): Promise<boolean>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Clean shutdown
|
|
29
|
+
* Called when the system is shutting down
|
|
30
|
+
*/
|
|
31
|
+
shutdown?(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Factory function type for creating provider instances
|
|
36
|
+
*/
|
|
37
|
+
export type ProviderFactory<TConfig = unknown> = (
|
|
38
|
+
config: TConfig,
|
|
39
|
+
context?: PluginContext,
|
|
40
|
+
) => Provider;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Context passed to plugin factory and lifecycle methods
|
|
44
|
+
*/
|
|
45
|
+
export interface PluginContext {
|
|
46
|
+
/** Plugin registry for accessing other plugins */
|
|
47
|
+
registry: IPluginRegistry;
|
|
48
|
+
/** Configuration path (if loaded from file) */
|
|
49
|
+
configPath?: string;
|
|
50
|
+
/** Working directory */
|
|
51
|
+
cwd?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Plugin definition interface
|
|
56
|
+
*
|
|
57
|
+
* This is the main interface for defining a terminal coder plugin.
|
|
58
|
+
* For simple tools, use the adapter system instead.
|
|
59
|
+
*/
|
|
60
|
+
export interface PluginDefinition<TConfig = unknown> {
|
|
61
|
+
/**
|
|
62
|
+
* Unique type identifier for this plugin
|
|
63
|
+
* Used as discriminator in config and registry lookup
|
|
64
|
+
*/
|
|
65
|
+
readonly type: string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Human-readable name for display
|
|
69
|
+
*/
|
|
70
|
+
readonly displayName: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Brief description of the plugin
|
|
74
|
+
*/
|
|
75
|
+
readonly description?: string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* URL to documentation
|
|
79
|
+
*/
|
|
80
|
+
readonly docsUrl?: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Version of the plugin (semver)
|
|
84
|
+
*/
|
|
85
|
+
readonly version?: string;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Factory function to create provider instances
|
|
89
|
+
*/
|
|
90
|
+
readonly factory: ProviderFactory<TConfig>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Whether this provider implements IPluginLifecycle
|
|
94
|
+
* If true, init/shutdown will be called during lifecycle management
|
|
95
|
+
*/
|
|
96
|
+
readonly hasLifecycle: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Capabilities this plugin supports
|
|
100
|
+
* e.g., ["generate", "edit", "explain", "test", "review"]
|
|
101
|
+
*/
|
|
102
|
+
readonly capabilities?: string[];
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Binary required by this plugin
|
|
106
|
+
* Used for availability checking
|
|
107
|
+
*/
|
|
108
|
+
readonly binary?: string;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Hook called when plugin is registered
|
|
112
|
+
* Use for one-time setup or validation
|
|
113
|
+
*/
|
|
114
|
+
onRegister?(context: PluginContext): void | Promise<void>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Hook called when plugin is unregistered
|
|
118
|
+
* Use for cleanup
|
|
119
|
+
*/
|
|
120
|
+
onUnregister?(context: PluginContext): void | Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Registration options
|
|
125
|
+
*/
|
|
126
|
+
export interface PluginRegistrationOptions {
|
|
127
|
+
/** Whether to overwrite if plugin type already exists */
|
|
128
|
+
overwrite?: boolean;
|
|
129
|
+
/** Mark as built-in plugin */
|
|
130
|
+
builtIn?: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Registration result
|
|
135
|
+
*/
|
|
136
|
+
export interface PluginRegistrationResult {
|
|
137
|
+
success: boolean;
|
|
138
|
+
type: string;
|
|
139
|
+
message?: string;
|
|
140
|
+
overwritten?: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Plugin metadata for queries
|
|
145
|
+
*/
|
|
146
|
+
export interface PluginInfo {
|
|
147
|
+
type: string;
|
|
148
|
+
displayName: string;
|
|
149
|
+
description?: string;
|
|
150
|
+
docsUrl?: string;
|
|
151
|
+
version?: string;
|
|
152
|
+
capabilities?: string[];
|
|
153
|
+
binary?: string;
|
|
154
|
+
hasLifecycle: boolean;
|
|
155
|
+
isBuiltIn: boolean;
|
|
156
|
+
isAvailable?: boolean;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Options for creating a provider
|
|
161
|
+
*/
|
|
162
|
+
export interface CreateProviderOptions {
|
|
163
|
+
/** Plugin context */
|
|
164
|
+
context?: PluginContext;
|
|
165
|
+
/** Skip config validation */
|
|
166
|
+
skipValidation?: boolean;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Extended provider type with optional lifecycle
|
|
171
|
+
*/
|
|
172
|
+
export type ManagedProvider = Provider & Partial<IPluginLifecycle>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Type guard for lifecycle providers
|
|
176
|
+
*/
|
|
177
|
+
export function isLifecycleProvider(provider: Provider): provider is Provider & IPluginLifecycle {
|
|
178
|
+
return (
|
|
179
|
+
provider != null &&
|
|
180
|
+
typeof provider === "object" &&
|
|
181
|
+
"init" in provider &&
|
|
182
|
+
typeof (provider as IPluginLifecycle).init === "function"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Helper type to extract config type from plugin
|
|
188
|
+
*/
|
|
189
|
+
export type PluginConfigType<T> = T extends PluginDefinition<infer C> ? C : never;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Plugin registry interface (for dependency injection)
|
|
193
|
+
*/
|
|
194
|
+
export interface IPluginRegistry {
|
|
195
|
+
register(
|
|
196
|
+
plugin: PluginDefinition,
|
|
197
|
+
options?: PluginRegistrationOptions,
|
|
198
|
+
): Promise<PluginRegistrationResult>;
|
|
199
|
+
registerSync(
|
|
200
|
+
plugin: PluginDefinition,
|
|
201
|
+
options?: PluginRegistrationOptions,
|
|
202
|
+
): PluginRegistrationResult;
|
|
203
|
+
unregister(type: string): Promise<boolean>;
|
|
204
|
+
has(type: string): boolean;
|
|
205
|
+
get(type: string): PluginDefinition | undefined;
|
|
206
|
+
getTypes(): string[];
|
|
207
|
+
listPlugins(): PluginInfo[];
|
|
208
|
+
createProvider(type: string, config: unknown, options?: CreateProviderOptions): ManagedProvider;
|
|
209
|
+
}
|