@grackle-ai/plugin-sdk 0.95.0
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/README.md +92 -0
- package/dist/context.d.ts +71 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +11 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +35 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +190 -0
- package/dist/loader.js.map +1 -0
- package/dist/plugin.d.ts +81 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +11 -0
- package/dist/plugin.js.map +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @grackle-ai/plugin-sdk
|
|
2
|
+
|
|
3
|
+
Plugin contract and loader for Grackle. Defines the `GracklePlugin` interface that all plugins implement, the `PluginContext` they receive, and the `loadPlugins()` function that topologically sorts, initializes, and collects contributions from plugins.
|
|
4
|
+
|
|
5
|
+
## GracklePlugin
|
|
6
|
+
|
|
7
|
+
A plugin contributes server capabilities through five extension points:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import type { GracklePlugin } from "@grackle-ai/plugin-sdk";
|
|
11
|
+
|
|
12
|
+
const myPlugin: GracklePlugin = {
|
|
13
|
+
name: "my-plugin",
|
|
14
|
+
dependencies: ["core"], // loaded after "core"
|
|
15
|
+
|
|
16
|
+
grpcHandlers: (ctx) => [
|
|
17
|
+
{ service: MyProtoService, handlers: { listItems, createItem } },
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
reconciliationPhases: (ctx) => [
|
|
21
|
+
{ name: "my-phase", execute: async () => { /* runs every tick */ } },
|
|
22
|
+
],
|
|
23
|
+
|
|
24
|
+
mcpTools: (ctx) => [
|
|
25
|
+
{ name: "my_tool", group: "my", description: "...", /* ... */ },
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
eventSubscribers: (ctx) => {
|
|
29
|
+
const unsub = ctx.subscribe((event) => {
|
|
30
|
+
if (event.type === "task.created") { /* react */ }
|
|
31
|
+
});
|
|
32
|
+
return [{ dispose: unsub }];
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
initialize: async (ctx) => {
|
|
36
|
+
ctx.logger.info("my-plugin initialized");
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
shutdown: async () => {
|
|
40
|
+
// clean up external connections
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## PluginContext
|
|
46
|
+
|
|
47
|
+
Plugins receive a thin runtime context. Database stores are accessed via direct package imports (e.g., `import { taskStore } from "@grackle-ai/database"`), not through the context.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
interface PluginContext {
|
|
51
|
+
subscribe: (cb: (event: GrackleEvent) => void) => () => void;
|
|
52
|
+
emit: (type: GrackleEventType, payload: Record<string, unknown>) => GrackleEvent;
|
|
53
|
+
logger: Logger; // pino structured logger
|
|
54
|
+
config: ServerConfig; // ports, host, grackleHome, apiKey, etc.
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## loadPlugins()
|
|
59
|
+
|
|
60
|
+
Loads an array of plugins in dependency order:
|
|
61
|
+
|
|
62
|
+
1. Validates no duplicate names or missing dependencies
|
|
63
|
+
2. Topological sort (Kahn's algorithm) on declared `dependencies`
|
|
64
|
+
3. Calls `initialize()` in dependency-first order
|
|
65
|
+
4. Collects all contributions (gRPC handlers, phases, tools, subscribers)
|
|
66
|
+
5. Returns a `LoadedPlugins` object with aggregated contributions and a `shutdown()` function
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { loadPlugins } from "@grackle-ai/plugin-sdk";
|
|
70
|
+
|
|
71
|
+
const result = await loadPlugins([corePlugin, orchestrationPlugin], ctx);
|
|
72
|
+
|
|
73
|
+
// Use contributions
|
|
74
|
+
for (const reg of result.serviceRegistrations) {
|
|
75
|
+
collector.addHandlers(reg.service, reg.handlers);
|
|
76
|
+
}
|
|
77
|
+
const manager = new ReconciliationManager(result.reconciliationPhases);
|
|
78
|
+
|
|
79
|
+
// On server shutdown
|
|
80
|
+
await result.shutdown(); // disposes subscribers, then calls plugin.shutdown() in reverse order
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Extension Points
|
|
84
|
+
|
|
85
|
+
| Method | What it contributes | Consumed by |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `grpcHandlers` | `ServiceRegistration[]` — proto service + handler pairs | `ServiceCollector` |
|
|
88
|
+
| `reconciliationPhases` | `ReconciliationPhase[]` — named async phases | `ReconciliationManager` |
|
|
89
|
+
| `mcpTools` | `PluginToolDefinition[]` — MCP tool definitions | `ToolRegistry` |
|
|
90
|
+
| `eventSubscribers` | `Disposable[]` — event bus subscriptions | Server shutdown |
|
|
91
|
+
| `initialize` | Async startup hook (e.g., connect to Neo4j) | Plugin loader |
|
|
92
|
+
| `shutdown` | Async teardown hook | Plugin loader (reverse order) |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin context types — the runtime environment provided to plugins.
|
|
3
|
+
*
|
|
4
|
+
* Stores (taskStore, sessionStore, etc.) are accessed via direct package
|
|
5
|
+
* imports from `@grackle-ai/database`, not through the context. The context
|
|
6
|
+
* provides only runtime-dynamic infrastructure: event bus, logger, and config.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { Logger } from "pino";
|
|
11
|
+
/** A resource that can be disposed to clean up subscriptions and state. */
|
|
12
|
+
export interface Disposable {
|
|
13
|
+
/** Release all resources (unsubscribe callbacks, clear dedup maps, etc.). */
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
16
|
+
/** Resolved server configuration available to plugins. */
|
|
17
|
+
export interface ServerConfig {
|
|
18
|
+
/** gRPC server port. */
|
|
19
|
+
grpcPort: number;
|
|
20
|
+
/** Web UI + WebSocket port. */
|
|
21
|
+
webPort: number;
|
|
22
|
+
/** MCP server port. */
|
|
23
|
+
mcpPort: number;
|
|
24
|
+
/** PowerLine server port. */
|
|
25
|
+
powerlinePort: number;
|
|
26
|
+
/** Bind address for all servers. */
|
|
27
|
+
host: string;
|
|
28
|
+
/** Grackle home directory (databases, API key, logs). */
|
|
29
|
+
grackleHome: string;
|
|
30
|
+
/** Loaded API key for authenticated requests. */
|
|
31
|
+
apiKey: string;
|
|
32
|
+
/** Override agent working directory (GRACKLE_WORKING_DIRECTORY). */
|
|
33
|
+
workingDirectory?: string;
|
|
34
|
+
/** Worktree base path (GRACKLE_WORKTREE_BASE). */
|
|
35
|
+
worktreeBase?: string;
|
|
36
|
+
/** Docker host for host mapping (GRACKLE_DOCKER_HOST). */
|
|
37
|
+
dockerHost?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Event types emitted by the domain event bus. */
|
|
40
|
+
export type GrackleEventType = "task.created" | "task.updated" | "task.started" | "task.completed" | "task.deleted" | "task.reparented" | "workspace.created" | "workspace.archived" | "workspace.updated" | "persona.created" | "persona.updated" | "persona.deleted" | "finding.posted" | "environment.added" | "environment.removed" | "environment.changed" | "environment.provision_progress" | "token.changed" | "credential.providers_changed" | "setting.changed" | "schedule.created" | "schedule.updated" | "schedule.deleted" | "schedule.fired" | "notification.escalated";
|
|
41
|
+
/** A domain event from the event bus. */
|
|
42
|
+
export interface GrackleEvent {
|
|
43
|
+
/** ULID — chronologically sortable unique identifier. */
|
|
44
|
+
id: string;
|
|
45
|
+
/** Dot-notation event type (e.g. "task.created"). */
|
|
46
|
+
type: GrackleEventType;
|
|
47
|
+
/** ISO 8601 timestamp. */
|
|
48
|
+
timestamp: string;
|
|
49
|
+
/** Domain-specific payload. */
|
|
50
|
+
payload: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Runtime context provided to plugins.
|
|
54
|
+
*
|
|
55
|
+
* Stores (taskStore, sessionStore, etc.) are accessed via direct package
|
|
56
|
+
* imports — not injected through the context. This keeps the contract surface
|
|
57
|
+
* minimal and avoids coupling plugins to a fat DI interface.
|
|
58
|
+
*/
|
|
59
|
+
export interface PluginContext {
|
|
60
|
+
/** Subscribe to all domain events. Returns an unsubscribe function. */
|
|
61
|
+
subscribe: (cb: (event: GrackleEvent) => void) => () => void;
|
|
62
|
+
/** Emit a domain event. */
|
|
63
|
+
emit: (type: GrackleEventType, payload: Record<string, unknown>) => GrackleEvent;
|
|
64
|
+
/** Structured logger (pino). */
|
|
65
|
+
logger: Logger;
|
|
66
|
+
/** Resolved server configuration. */
|
|
67
|
+
config: ServerConfig;
|
|
68
|
+
}
|
|
69
|
+
/** Factory function that creates a subscriber and returns a Disposable for cleanup. */
|
|
70
|
+
export type SubscriberFactory = (ctx: PluginContext) => Disposable;
|
|
71
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,oBAAoB,GACpB,mBAAmB,GACnB,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB,GAChB,mBAAmB,GACnB,qBAAqB,GACrB,qBAAqB,GACrB,gCAAgC,GAChC,eAAe,GACf,8BAA8B,GAC9B,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,wBAAwB,CAAC;AAE7B,yCAAyC;AACzC,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,IAAI,EAAE,gBAAgB,CAAC;IACvB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC7D,2BAA2B;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,YAAY,CAAC;IACjF,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,uFAAuF;AACvF,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,aAAa,KAAK,UAAU,CAAC"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin context types — the runtime environment provided to plugins.
|
|
3
|
+
*
|
|
4
|
+
* Stores (taskStore, sessionStore, etc.) are accessed via direct package
|
|
5
|
+
* imports from `@grackle-ai/database`, not through the context. The context
|
|
6
|
+
* provides only runtime-dynamic infrastructure: event bus, logger, and config.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { Disposable, ServerConfig, GrackleEventType, GrackleEvent, PluginContext, SubscriberFactory, } from "./context.js";
|
|
2
|
+
export type { GracklePlugin, ServiceRegistration, ReconciliationPhase, PluginToolDefinition, } from "./plugin.js";
|
|
3
|
+
export type { LoadedPlugins } from "./loader.js";
|
|
4
|
+
export { loadPlugins } from "./loader.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin loader — topological sort, initialization, and contribution collection.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { GracklePlugin, ReconciliationPhase, ServiceRegistration, PluginToolDefinition } from "./plugin.js";
|
|
7
|
+
import type { PluginContext, Disposable } from "./context.js";
|
|
8
|
+
/** Aggregated contributions from all loaded plugins. */
|
|
9
|
+
export interface LoadedPlugins {
|
|
10
|
+
/** All gRPC service registrations, in plugin load order. */
|
|
11
|
+
serviceRegistrations: ServiceRegistration[];
|
|
12
|
+
/** All reconciliation phases, in plugin load order. */
|
|
13
|
+
reconciliationPhases: ReconciliationPhase[];
|
|
14
|
+
/** All MCP tool definitions, in plugin load order. */
|
|
15
|
+
mcpTools: PluginToolDefinition[];
|
|
16
|
+
/** All subscriber disposables (for shutdown). */
|
|
17
|
+
subscriberDisposables: Disposable[];
|
|
18
|
+
/** Dispose all subscribers, then shutdown plugins in reverse initialization order. */
|
|
19
|
+
shutdown: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Load, sort, initialize, and collect contributions from plugins.
|
|
23
|
+
*
|
|
24
|
+
* 1. Validate: no duplicate names, no missing dependencies
|
|
25
|
+
* 2. Topological sort on declared dependencies (error on cycles)
|
|
26
|
+
* 3. Call `initialize()` in dependency order
|
|
27
|
+
* 4. Collect grpcHandlers, reconciliationPhases, mcpTools, eventSubscribers
|
|
28
|
+
* 5. Return aggregated contributions + a shutdown function
|
|
29
|
+
*
|
|
30
|
+
* @param plugins - Unordered array of plugins to load.
|
|
31
|
+
* @param ctx - Runtime context provided to each plugin.
|
|
32
|
+
* @returns Aggregated contributions and a shutdown function.
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadPlugins(plugins: GracklePlugin[], ctx: PluginContext): Promise<LoadedPlugins>;
|
|
35
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE9D,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;IAC5C,uDAAuD;IACvD,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;IAC5C,sDAAsD;IACtD,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,iDAAiD;IACjD,qBAAqB,EAAE,UAAU,EAAE,CAAC;IACpC,sFAAsF;IACtF,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EAAE,EACxB,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,aAAa,CAAC,CA6HxB"}
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin loader — topological sort, initialization, and contribution collection.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Load, sort, initialize, and collect contributions from plugins.
|
|
8
|
+
*
|
|
9
|
+
* 1. Validate: no duplicate names, no missing dependencies
|
|
10
|
+
* 2. Topological sort on declared dependencies (error on cycles)
|
|
11
|
+
* 3. Call `initialize()` in dependency order
|
|
12
|
+
* 4. Collect grpcHandlers, reconciliationPhases, mcpTools, eventSubscribers
|
|
13
|
+
* 5. Return aggregated contributions + a shutdown function
|
|
14
|
+
*
|
|
15
|
+
* @param plugins - Unordered array of plugins to load.
|
|
16
|
+
* @param ctx - Runtime context provided to each plugin.
|
|
17
|
+
* @returns Aggregated contributions and a shutdown function.
|
|
18
|
+
*/
|
|
19
|
+
export async function loadPlugins(plugins, ctx) {
|
|
20
|
+
// 1. Validate
|
|
21
|
+
const byName = new Map();
|
|
22
|
+
for (const plugin of plugins) {
|
|
23
|
+
if (byName.has(plugin.name)) {
|
|
24
|
+
throw new Error(`Duplicate plugin name: "${plugin.name}"`);
|
|
25
|
+
}
|
|
26
|
+
byName.set(plugin.name, plugin);
|
|
27
|
+
}
|
|
28
|
+
for (const plugin of plugins) {
|
|
29
|
+
for (const dep of plugin.dependencies ?? []) {
|
|
30
|
+
if (!byName.has(dep)) {
|
|
31
|
+
throw new Error(`Plugin "${plugin.name}" depends on "${dep}" which was not provided`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// 2. Topological sort (Kahn's algorithm)
|
|
36
|
+
const sorted = topologicalSort(plugins);
|
|
37
|
+
// 3. Initialize in dependency order (clean up on failure)
|
|
38
|
+
const initialized = [];
|
|
39
|
+
try {
|
|
40
|
+
for (const plugin of sorted) {
|
|
41
|
+
if (plugin.initialize) {
|
|
42
|
+
await plugin.initialize(ctx);
|
|
43
|
+
}
|
|
44
|
+
initialized.push(plugin);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// Shutdown already-initialized plugins in reverse order before re-throwing
|
|
49
|
+
for (let i = initialized.length - 1; i >= 0; i--) {
|
|
50
|
+
const plugin = initialized[i];
|
|
51
|
+
if (plugin.shutdown) {
|
|
52
|
+
try {
|
|
53
|
+
await plugin.shutdown();
|
|
54
|
+
}
|
|
55
|
+
catch (shutdownErr) {
|
|
56
|
+
ctx.logger.error({ err: shutdownErr, plugin: plugin.name }, "Plugin '%s' shutdown failed during initialization rollback", plugin.name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
// 4. Collect contributions (roll back on failure)
|
|
63
|
+
const serviceRegistrations = [];
|
|
64
|
+
const reconciliationPhases = [];
|
|
65
|
+
const mcpTools = [];
|
|
66
|
+
const subscriberDisposables = [];
|
|
67
|
+
try {
|
|
68
|
+
for (const plugin of sorted) {
|
|
69
|
+
if (plugin.grpcHandlers) {
|
|
70
|
+
serviceRegistrations.push(...plugin.grpcHandlers(ctx));
|
|
71
|
+
}
|
|
72
|
+
if (plugin.reconciliationPhases) {
|
|
73
|
+
reconciliationPhases.push(...plugin.reconciliationPhases(ctx));
|
|
74
|
+
}
|
|
75
|
+
if (plugin.mcpTools) {
|
|
76
|
+
mcpTools.push(...plugin.mcpTools(ctx));
|
|
77
|
+
}
|
|
78
|
+
if (plugin.eventSubscribers) {
|
|
79
|
+
subscriberDisposables.push(...plugin.eventSubscribers(ctx));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
// Dispose any subscribers already collected, then shutdown initialized plugins
|
|
85
|
+
for (const disposable of subscriberDisposables) {
|
|
86
|
+
try {
|
|
87
|
+
disposable.dispose();
|
|
88
|
+
}
|
|
89
|
+
catch { /* best-effort */ }
|
|
90
|
+
}
|
|
91
|
+
for (let i = initialized.length - 1; i >= 0; i--) {
|
|
92
|
+
const plugin = initialized[i];
|
|
93
|
+
if (plugin.shutdown) {
|
|
94
|
+
try {
|
|
95
|
+
await plugin.shutdown();
|
|
96
|
+
}
|
|
97
|
+
catch (shutdownErr) {
|
|
98
|
+
ctx.logger.error({ err: shutdownErr, plugin: plugin.name }, "Plugin '%s' shutdown failed during contribution rollback", plugin.name);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
// 5. Build shutdown function (reverse order, catch errors)
|
|
105
|
+
const shutdown = async () => {
|
|
106
|
+
// Dispose subscribers first
|
|
107
|
+
for (const disposable of subscriberDisposables) {
|
|
108
|
+
try {
|
|
109
|
+
disposable.dispose();
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
ctx.logger.warn({ err }, "Subscriber dispose failed during shutdown");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Shutdown plugins in reverse initialization order
|
|
116
|
+
for (let i = initialized.length - 1; i >= 0; i--) {
|
|
117
|
+
const plugin = initialized[i];
|
|
118
|
+
if (plugin.shutdown) {
|
|
119
|
+
try {
|
|
120
|
+
await plugin.shutdown();
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
ctx.logger.error({ err, plugin: plugin.name }, "Plugin '%s' shutdown failed", plugin.name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
serviceRegistrations,
|
|
130
|
+
reconciliationPhases,
|
|
131
|
+
mcpTools,
|
|
132
|
+
subscriberDisposables,
|
|
133
|
+
shutdown,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Topological sort using Kahn's algorithm.
|
|
138
|
+
*
|
|
139
|
+
* @param plugins - Plugins with optional `dependencies` arrays.
|
|
140
|
+
* @returns Plugins in dependency-first order.
|
|
141
|
+
* @throws If a cycle is detected.
|
|
142
|
+
*/
|
|
143
|
+
function topologicalSort(plugins) {
|
|
144
|
+
const byName = new Map();
|
|
145
|
+
const inDegree = new Map();
|
|
146
|
+
const dependents = new Map();
|
|
147
|
+
// Initialize
|
|
148
|
+
for (const plugin of plugins) {
|
|
149
|
+
byName.set(plugin.name, plugin);
|
|
150
|
+
inDegree.set(plugin.name, 0);
|
|
151
|
+
dependents.set(plugin.name, []);
|
|
152
|
+
}
|
|
153
|
+
// Build edges: dependency → dependent (deduplicate to avoid inflated inDegree)
|
|
154
|
+
for (const plugin of plugins) {
|
|
155
|
+
const uniqueDeps = [...new Set(plugin.dependencies ?? [])];
|
|
156
|
+
for (const dep of uniqueDeps) {
|
|
157
|
+
dependents.get(dep).push(plugin.name);
|
|
158
|
+
inDegree.set(plugin.name, inDegree.get(plugin.name) + 1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Seed queue with zero in-degree nodes
|
|
162
|
+
const queue = [];
|
|
163
|
+
for (const [name, degree] of inDegree) {
|
|
164
|
+
if (degree === 0) {
|
|
165
|
+
queue.push(name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Process
|
|
169
|
+
const sorted = [];
|
|
170
|
+
while (queue.length > 0) {
|
|
171
|
+
const name = queue.shift();
|
|
172
|
+
sorted.push(byName.get(name));
|
|
173
|
+
for (const dependent of dependents.get(name)) {
|
|
174
|
+
const newDegree = inDegree.get(dependent) - 1;
|
|
175
|
+
inDegree.set(dependent, newDegree);
|
|
176
|
+
if (newDegree === 0) {
|
|
177
|
+
queue.push(dependent);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (sorted.length !== plugins.length) {
|
|
182
|
+
// Find the cycle for a descriptive error message
|
|
183
|
+
const remaining = plugins
|
|
184
|
+
.filter((p) => !sorted.some((s) => s.name === p.name))
|
|
185
|
+
.map((p) => p.name);
|
|
186
|
+
throw new Error(`Dependency cycle detected among plugins: ${remaining.join(", ")}`);
|
|
187
|
+
}
|
|
188
|
+
return sorted;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GracklePlugin interface — the universal plugin contract.
|
|
3
|
+
*
|
|
4
|
+
* A plugin contributes server capabilities through five extension points:
|
|
5
|
+
* gRPC handlers, reconciliation phases, MCP tools, event subscribers,
|
|
6
|
+
* and lifecycle hooks.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { DescService } from "@bufbuild/protobuf";
|
|
11
|
+
import type { PluginContext, Disposable } from "./context.js";
|
|
12
|
+
/** A set of gRPC handler methods contributed to a ConnectRPC service. */
|
|
13
|
+
export interface ServiceRegistration {
|
|
14
|
+
/** The proto service definition (e.g., grackle.Grackle). */
|
|
15
|
+
service: DescService;
|
|
16
|
+
/**
|
|
17
|
+
* Handler method implementations.
|
|
18
|
+
*
|
|
19
|
+
* Uses `any` because handler functions have concrete parameter types that
|
|
20
|
+
* are not assignable to `(...args: unknown[]) => unknown` due to contravariance.
|
|
21
|
+
*/
|
|
22
|
+
handlers: Record<string, (...args: any[]) => any>;
|
|
23
|
+
}
|
|
24
|
+
/** A named async phase that runs during each reconciliation tick. */
|
|
25
|
+
export interface ReconciliationPhase {
|
|
26
|
+
/** Short name for logging (e.g. "cron", "dispatch"). */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Execute the phase. Errors are caught by the manager. */
|
|
29
|
+
execute: () => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Declarative MCP tool definition contributed by a plugin.
|
|
33
|
+
*
|
|
34
|
+
* Intentionally uses `unknown` for schema/handler types to avoid depending
|
|
35
|
+
* on `@grackle-ai/mcp`. The server maps these to concrete ToolDefinition
|
|
36
|
+
* objects when registering with the MCP tool registry.
|
|
37
|
+
*/
|
|
38
|
+
export interface PluginToolDefinition {
|
|
39
|
+
/** Unique tool name (snake_case by convention). */
|
|
40
|
+
name: string;
|
|
41
|
+
/** Logical group for filtering (e.g. "task", "session"). */
|
|
42
|
+
group: string;
|
|
43
|
+
/** Human-readable description. */
|
|
44
|
+
description: string;
|
|
45
|
+
/** Zod schema for input validation. */
|
|
46
|
+
inputSchema: unknown;
|
|
47
|
+
/** The gRPC method this tool calls. */
|
|
48
|
+
rpcMethod: string;
|
|
49
|
+
/** Whether this tool mutates state. */
|
|
50
|
+
mutating: boolean;
|
|
51
|
+
/** Optional MCP tool annotations. */
|
|
52
|
+
annotations?: Record<string, unknown>;
|
|
53
|
+
/** Handler function invoked when the tool is called. */
|
|
54
|
+
handler: (args: unknown, client: unknown, authContext?: unknown) => Promise<unknown>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A Grackle plugin that contributes server capabilities.
|
|
58
|
+
*
|
|
59
|
+
* Plugins are loaded by {@link loadPlugins} in topological order based on
|
|
60
|
+
* declared dependencies. Each plugin can contribute gRPC handlers,
|
|
61
|
+
* reconciliation phases, MCP tools, and event subscribers.
|
|
62
|
+
*/
|
|
63
|
+
export interface GracklePlugin {
|
|
64
|
+
/** Unique identifier (e.g., "core", "orchestration", "my-linear-sync"). */
|
|
65
|
+
name: string;
|
|
66
|
+
/** Plugins this one requires. The loader topologically sorts on this. */
|
|
67
|
+
dependencies?: string[];
|
|
68
|
+
/** gRPC handler groups to register on ConnectRPC services. */
|
|
69
|
+
grpcHandlers?: (ctx: PluginContext) => ServiceRegistration[];
|
|
70
|
+
/** Reconciliation phases to run on each tick. */
|
|
71
|
+
reconciliationPhases?: (ctx: PluginContext) => ReconciliationPhase[];
|
|
72
|
+
/** MCP tool definitions to register. */
|
|
73
|
+
mcpTools?: (ctx: PluginContext) => PluginToolDefinition[];
|
|
74
|
+
/** Event subscribers to wire up. Returns disposables for shutdown. */
|
|
75
|
+
eventSubscribers?: (ctx: PluginContext) => Disposable[];
|
|
76
|
+
/** Called after dependency plugins are initialized, in dependency order. */
|
|
77
|
+
initialize?: (ctx: PluginContext) => Promise<void>;
|
|
78
|
+
/** Called on graceful shutdown, in reverse load order. */
|
|
79
|
+
shutdown?: () => Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE9D,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,OAAO,EAAE,WAAW,CAAC;IACrB;;;;;OAKG;IAEH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;CACnD;AAED,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,wDAAwD;IACxD,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACtF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,mBAAmB,EAAE,CAAC;IAC7D,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,mBAAmB,EAAE,CAAC;IACrE,wCAAwC;IACxC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,oBAAoB,EAAE,CAAC;IAC1D,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,UAAU,EAAE,CAAC;IAExD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GracklePlugin interface — the universal plugin contract.
|
|
3
|
+
*
|
|
4
|
+
* A plugin contributes server capabilities through five extension points:
|
|
5
|
+
* gRPC handlers, reconciliation phases, MCP tools, event subscribers,
|
|
6
|
+
* and lifecycle hooks.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.57.7"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grackle-ai/plugin-sdk",
|
|
3
|
+
"version": "0.95.0",
|
|
4
|
+
"description": "Plugin contract and loader for Grackle — defines GracklePlugin, PluginContext, and loadPlugins()",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/nick-pape/grackle.git",
|
|
9
|
+
"directory": "packages/plugin-sdk"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"grackle",
|
|
13
|
+
"plugin",
|
|
14
|
+
"sdk"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/nick-pape/grackle#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/nick-pape/grackle/issues"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=22.0.0 <24.0.0"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@bufbuild/protobuf": "^2.11.0",
|
|
31
|
+
"pino": "~9.6.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@rushstack/heft": "1.2.7",
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"vitest": "^3.2.1",
|
|
37
|
+
"@grackle-ai/heft-rig": "0.0.1"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "heft build --clean",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"clean": "heft clean",
|
|
43
|
+
"_phase:build": "heft run --only build -- --clean",
|
|
44
|
+
"_phase:test": "vitest run"
|
|
45
|
+
}
|
|
46
|
+
}
|