@24klynx/plugins 0.1.0 → 0.1.2
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/index.d.mts +1 -1
- package/dist/index.mjs +6 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -65,7 +65,7 @@ interface PluginHook {
|
|
|
65
65
|
* Runtime context passed to every plugin during initialisation.
|
|
66
66
|
*
|
|
67
67
|
* This is the INTERNAL contract between the plugin loader and plugin modules.
|
|
68
|
-
* Plugin authors use the SDK's `PluginContext` (from `@
|
|
68
|
+
* Plugin authors use the SDK's `PluginContext` (from `@24klynx/plugin-sdk`),
|
|
69
69
|
* which wraps this with additional registration helpers.
|
|
70
70
|
*/
|
|
71
71
|
interface PluginRuntimeContext {
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { PluginError, PluginLoadError } from "@
|
|
3
|
+
import { PluginError, PluginLoadError } from "@24klynx/core";
|
|
4
4
|
//#region src/discovery.ts
|
|
5
5
|
/**
|
|
6
6
|
* Plugin discovery — scan directories for manifest.json files.
|
|
@@ -245,8 +245,8 @@ var PredictiveLoader = class {
|
|
|
245
245
|
* "@" → model/provider plugins
|
|
246
246
|
*/
|
|
247
247
|
onInputPrefix(prefix) {
|
|
248
|
-
if (prefix === "/") this.enqueue("@
|
|
249
|
-
else if (prefix === "@") this.enqueue("@
|
|
248
|
+
if (prefix === "/") this.enqueue("@24klynx/skills", 100);
|
|
249
|
+
else if (prefix === "@") this.enqueue("@24klynx/model-picker", 90);
|
|
250
250
|
}
|
|
251
251
|
/**
|
|
252
252
|
* Predict which plugins to warm based on a pending action.
|
|
@@ -254,9 +254,9 @@ var PredictiveLoader = class {
|
|
|
254
254
|
* "code_edit" → editor tool plugin
|
|
255
255
|
*/
|
|
256
256
|
onActionPending(action) {
|
|
257
|
-
if (action.startsWith("file_")) this.enqueue("@
|
|
258
|
-
else if (action === "code_edit") this.enqueue("@
|
|
259
|
-
else if (action === "web_search") this.enqueue("@
|
|
257
|
+
if (action.startsWith("file_")) this.enqueue("@24klynx/tool-file-ops", 80);
|
|
258
|
+
else if (action === "code_edit") this.enqueue("@24klynx/tool-edit", 80);
|
|
259
|
+
else if (action === "web_search") this.enqueue("@24klynx/tool-web", 70);
|
|
260
260
|
}
|
|
261
261
|
};
|
|
262
262
|
//#endregion
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["PluginLoadError"],"sources":["../src/discovery.ts","../src/loader.ts","../src/hooks.ts","../src/predictive-loader.ts","../src/sdk/define-plugin.ts","../src/sdk/define-tool.ts"],"sourcesContent":["/**\n * Plugin discovery — scan directories for manifest.json files.\n *\n * The discovery layer only reads manifest metadata; it never imports\n * plugin code. This keeps the registry fast and allows filtering\n * before loading.\n */\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { PluginManifest } from \"./types.js\";\nimport { PluginError } from \"@lynx/core\";\n\n// ── Types ──────────────────────────────────────────\n\nexport interface ManifestEntry {\n /** Absolute path to the plugin directory. */\n dir: string;\n /** Parsed manifest content. */\n manifest: PluginManifest;\n}\n\nexport interface ManifestRegistry {\n /** Scan one or more directories for plugin manifests. */\n scan(directories: string[]): ManifestEntry[];\n /** Get a plugin by id (fast lookup). */\n getById(id: string): ManifestEntry | undefined;\n /** List all discovered plugins. */\n listAll(): ManifestEntry[];\n /** How many plugins are registered. */\n count(): number;\n}\n\n// ── Constants ──────────────────────────────────────\n\nconst MANIFEST_FILE = \"manifest.json\";\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Create a manifest registry.\n *\n * Call `scan()` during Phase 2 startup to populate it,\n * then use `getById()` / `listAll()` for fast lookups.\n */\nexport function createManifestRegistry(): ManifestRegistry {\n const entries = new Map<string, ManifestEntry>();\n\n const registry: ManifestRegistry = {\n scan(directories: string[]): ManifestEntry[] {\n const discovered: ManifestEntry[] = [];\n\n for (const dir of directories) {\n const manifestPath = join(dir, MANIFEST_FILE);\n\n if (!existsSync(manifestPath)) continue;\n\n let raw: string;\n try {\n raw = readFileSync(manifestPath, \"utf-8\");\n } catch (err) {\n throw new PluginLoadError(`Cannot read manifest at ${manifestPath}: ${String(err)}`);\n }\n\n let manifest: PluginManifest;\n try {\n manifest = JSON.parse(raw) as PluginManifest;\n } catch {\n throw new PluginError(`Invalid JSON in manifest: ${manifestPath}`, {\n category: \"plugin\",\n userVisible: true,\n });\n }\n\n // Validate required fields\n if (!manifest.name || !manifest.version || !manifest.entry) {\n throw new PluginError(\n `manifest.json at ${dir} is missing required fields (name, version, entry)`,\n { category: \"plugin\", userVisible: true },\n );\n }\n\n const entry: ManifestEntry = { dir, manifest };\n entries.set(manifest.name, entry);\n discovered.push(entry);\n }\n\n return discovered;\n },\n\n getById(id: string): ManifestEntry | undefined {\n return entries.get(id);\n },\n\n listAll(): ManifestEntry[] {\n return [...entries.values()];\n },\n\n count(): number {\n return entries.size;\n },\n };\n\n return registry;\n}\n\n// ── Error helper ───────────────────────────────────\n\nclass PluginLoadError extends PluginError {\n constructor(message: string) {\n super(message, { userVisible: true });\n }\n}\n","/**\n * Plugin loader — lazy import() with caching.\n *\n * Plugins are loaded once and cached. If a plugin fails to load,\n * the error is isolated — other plugins continue working.\n *\n * Fail‑open: a plugin that throws during init is unloaded but\n * the app continues without it.\n * Fail‑closed: for security‑critical hooks, a failing plugin\n * blocks the operation (see hooks.ts).\n */\n\nimport type { PluginContract, PluginRuntimeContext, PluginEntryFunction } from \"./types.js\";\nimport type { ManifestEntry } from \"./discovery.js\";\nimport { PluginError, PluginLoadError } from \"@lynx/core\";\n\n// ── Types ──────────────────────────────────────────\n\nexport interface PluginLoader {\n /** Load a plugin from its manifest entry. */\n load(entry: ManifestEntry, ctx: PluginRuntimeContext): Promise<PluginContract>;\n /** Unload a previously loaded plugin. */\n unload(pluginName: string): void;\n /** Get a loaded plugin. */\n getLoaded(pluginName: string): PluginContract | undefined;\n /** List all loaded plugin names. */\n listLoaded(): string[];\n /** Destroy all loaded plugins. */\n destroy(): Promise<void>;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Create a plugin loader with lazy import and caching.\n */\nexport function createPluginLoader(): PluginLoader {\n const loaded = new Map<string, PluginContract>();\n\n const loader: PluginLoader = {\n async load(entry: ManifestEntry, ctx: PluginRuntimeContext): Promise<PluginContract> {\n // Return cached if already loaded\n const existing = loaded.get(entry.manifest.name);\n if (existing) return existing;\n\n let mod: { default?: PluginEntryFunction };\n try {\n // Dynamic import — the entry path is relative to the plugin directory\n const entryPath = `${entry.dir}/${entry.manifest.entry}`;\n mod = await import(entryPath);\n } catch (err) {\n throw new PluginLoadError(\n `Failed to import plugin \"${entry.manifest.name}\" from ${entry.manifest.entry}: ${String(err)}`,\n );\n }\n\n if (!mod.default || typeof mod.default !== \"function\") {\n throw new PluginError(\n `Plugin \"${entry.manifest.name}\" does not have a default export function`,\n { category: \"plugin\", userVisible: true },\n );\n }\n\n let contract: PluginContract;\n try {\n contract = await mod.default(ctx);\n } catch (err) {\n throw new PluginError(\n `Plugin \"${entry.manifest.name}\" initialisation threw: ${String(err)}`,\n { category: \"plugin\", userVisible: true },\n );\n }\n\n loaded.set(entry.manifest.name, contract);\n return contract;\n },\n\n unload(pluginName: string): void {\n const contract = loaded.get(pluginName);\n if (contract?.onUnload) {\n void contract.onUnload();\n }\n loaded.delete(pluginName);\n },\n\n getLoaded(pluginName: string): PluginContract | undefined {\n return loaded.get(pluginName);\n },\n\n listLoaded(): string[] {\n return [...loaded.keys()];\n },\n\n async destroy(): Promise<void> {\n for (const [, contract] of loaded) {\n if (contract.onUnload) {\n try {\n await contract.onUnload();\n } catch {\n // Best effort cleanup\n }\n }\n }\n loaded.clear();\n },\n };\n\n return loader;\n}\n","/**\n * Hook engine — dispatch events to registered plugin hooks.\n *\n * Supports two failure strategies:\n * fail‑open — hook failure does not block the operation (default)\n * fail‑closed — hook failure blocks the operation (security‑critical)\n */\n\nimport type { HookType, HookSink } from \"./types.js\";\n\n// ── Types ──────────────────────────────────────────\n\nexport type FailureStrategy = \"fail-open\" | \"fail-closed\";\n\nexport interface HookRegistry {\n /** Register a sink. Lower priority runs first. */\n register(sink: HookSink): void;\n /** Remove all sinks for a plugin. */\n unregisterPlugin(pluginId: string): void;\n /** Dispatch an event to all registered hooks of this type. */\n dispatch(type: HookType, event: unknown, strategy?: FailureStrategy): Promise<void>;\n /** How many sinks are registered. */\n count(): number;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Create a hook registry.\n *\n * Hooks are stored in priority‑ordered arrays per hook type.\n * Dispatch iterates them in order and short‑circuits on fail‑closed.\n */\nexport function createHookRegistry(): HookRegistry {\n const sinks = new Map<HookType, HookSink[]>();\n\n function ensureList(type: HookType): HookSink[] {\n let list = sinks.get(type);\n if (!list) {\n list = [];\n sinks.set(type, list);\n }\n return list;\n }\n\n const registry: HookRegistry = {\n register(sink: HookSink): void {\n const list = ensureList(sink.type);\n list.push(sink);\n // Keep sorted by priority (ascending)\n list.sort((a, b) => a.priority - b.priority);\n },\n\n unregisterPlugin(pluginId: string): void {\n for (const [type, list] of sinks) {\n const filtered = list.filter((s) => s.pluginId !== pluginId);\n if (filtered.length === 0) {\n sinks.delete(type);\n } else {\n sinks.set(type, filtered);\n }\n }\n },\n\n async dispatch(\n type: HookType,\n event: unknown,\n strategy: FailureStrategy = \"fail-open\",\n ): Promise<void> {\n const list = sinks.get(type);\n if (!list || list.length === 0) return;\n\n for (const sink of list) {\n try {\n await sink.handler(event);\n } catch (err) {\n if (strategy === \"fail-closed\") {\n throw err;\n }\n // fail‑open: continue to next hook\n }\n }\n },\n\n count(): number {\n let total = 0;\n for (const list of sinks.values()) {\n total += list.length;\n }\n return total;\n },\n };\n\n return registry;\n}\n","/**\n * Predictive loader — pre‑warms plugins before they are needed.\n *\n * Priority‑based warmup queue that loads plugins during idle time\n * (Phase 2 startup background tasks) and reactively based on input\n * prefix heuristics.\n *\n * Reference: §17 PredictiveLoader design\n */\n\n// ── Types ────────────────────────────────────────────\n\n/** A plugin module after lazy import(). */\ntype PluginModule = unknown;\n\ninterface QueueEntry {\n pluginId: string;\n priority: number;\n}\n\n// ── PredictiveLoader ─────────────────────────────────\n\nexport class PredictiveLoader {\n /** Map of already loaded plugins. */\n private _loaded = new Map<string, PluginModule>();\n\n /** Set of plugin ids currently being imported. */\n private _warming = new Set<string>();\n\n /** Priority queue (higher number = more important). */\n private _queue: QueueEntry[] = [];\n\n /** Abort controller for cancelling in‑flight warmups. */\n private _abort = new AbortController();\n\n /** Dynamic import function (injectable for testing). */\n private _importFn: (id: string) => Promise<PluginModule>;\n\n constructor(importFn: (id: string) => Promise<PluginModule>) {\n this._importFn = importFn;\n }\n\n // ── Public API ─────────────────────────────────────\n\n /** Whether the plugin has already been loaded. */\n isLoaded(pluginId: string): boolean {\n return this._loaded.has(pluginId);\n }\n\n /** Whether the plugin is currently being warmed. */\n isWarming(pluginId: string): boolean {\n return this._warming.has(pluginId);\n }\n\n /**\n * Enqueue a plugin for warming at the given priority.\n * Higher priority plugins are processed first.\n */\n enqueue(pluginId: string, priority: number): void {\n if (this.isLoaded(pluginId) || this.isWarming(pluginId)) {\n return; // Already done or in progress — idempotent\n }\n\n // Insert sorted by priority (descending)\n const idx = this._queue.findIndex((e) => e.priority < priority);\n if (idx < 0) {\n this._queue.push({ pluginId, priority });\n } else {\n this._queue.splice(idx, 0, { pluginId, priority });\n }\n }\n\n /** Process the entire warmup queue (non‑blocking — yields between imports). */\n async processQueue(): Promise<void> {\n while (this._queue.length > 0) {\n if (this._abort.signal.aborted) return;\n\n const entry = this._queue.shift()!;\n if (this.isLoaded(entry.pluginId)) continue;\n\n this._warming.add(entry.pluginId);\n\n try {\n const mod = await this._importFn(entry.pluginId);\n this._loaded.set(entry.pluginId, mod);\n } catch {\n // Silently skip plugins that fail to warm — they'll be\n // retried (with an error report) when actually requested.\n } finally {\n this._warming.delete(entry.pluginId);\n }\n\n // Yield to the event loop so the main thread stays responsive.\n await new Promise((resolve) => setImmediate(resolve));\n }\n }\n\n /** Stop all in‑flight warmups and clear the queue. */\n abort(): void {\n this._abort.abort();\n this._queue.length = 0;\n this._warming.clear();\n }\n\n /** Number of plugins currently queued for warming. */\n get pendingCount(): number {\n return this._queue.length;\n }\n\n /** Number of plugins that have been successfully loaded. */\n get loadedCount(): number {\n return this._loaded.size;\n }\n\n // ── Input prediction ───────────────────────────────\n\n /**\n * Predict which plugins to warm based on input prefix.\n *\n * Heuristics (200 ms window after prefix character):\n * \"/\" → skill plugins\n * \"@\" → model/provider plugins\n */\n onInputPrefix(prefix: string): void {\n if (prefix === \"/\") {\n this.enqueue(\"@lynx/skills\", 100);\n } else if (prefix === \"@\") {\n this.enqueue(\"@lynx/model-picker\", 90);\n }\n }\n\n /**\n * Predict which plugins to warm based on a pending action.\n * \"file_read\" → file operations plugin\n * \"code_edit\" → editor tool plugin\n */\n onActionPending(action: string): void {\n if (action.startsWith(\"file_\")) {\n this.enqueue(\"@lynx/tool-file-ops\", 80);\n } else if (action === \"code_edit\") {\n this.enqueue(\"@lynx/tool-edit\", 80);\n } else if (action === \"web_search\") {\n this.enqueue(\"@lynx/tool-web\", 70);\n }\n }\n}\n","/**\n * `definePlugin` — the entry point for every Lynx plugin.\n *\n * Pattern (inspired by FengMing's `definePluginEntry`):\n * export default definePlugin((ctx) => {\n * ctx.logger.info(\"loaded\");\n * return { name: \"my-plugin\", tools: [...] };\n * });\n */\n\nimport type { PluginContext } from \"./types.js\";\nimport type { PluginManifest } from \"../types.js\";\n\n// ── Types ────────────────────────────────────────────\n\n/** What a plugin's register function returns. */\nexport interface PluginDefinition {\n /** Display name. */\n name: string;\n /** Semver version string. */\n version?: string;\n /** Human‑readable description. */\n description?: string;\n /** Hook declarations (executed by the hook engine). */\n hooks?: HookDeclaration[];\n}\n\n/** A hook declaration — static metadata, handler runs at runtime. */\nexport interface HookDeclaration {\n /** Hook event name (e.g. \"before_agent_reply\"). */\n type: string;\n /** \"fail-open\": hook failure doesn't block; \"fail-closed\": hook failure rejects. */\n policy: \"fail-open\" | \"fail-closed\";\n /** The handler function. */\n handler: (event: unknown) => Promise<void>;\n}\n\n/** The register callback signature. */\nexport type PluginRegisterFn = (ctx: PluginContext) => PluginDefinition | Promise<PluginDefinition>;\n\n/** The shape returned by `definePlugin`. */\nexport interface DefinedPlugin {\n /** Unique plugin id (from manifest.json). */\n id: string;\n manifest: PluginManifest;\n register: PluginRegisterFn;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Declare a Lynx plugin.\n *\n * `register` is called once at startup with a {@link PluginContext}\n * that provides logging, storage, and event emission.\n */\nexport function definePlugin(manifest: PluginManifest, register: PluginRegisterFn): DefinedPlugin {\n return {\n id: manifest.name,\n manifest,\n register,\n };\n}\n","/**\n * `defineTool` — declare a tool that a plugin contributes to the agent.\n *\n * Pattern (inspired by FengMing's `api.registerTool`):\n * const myTool = defineTool({\n * name: \"my_tool\",\n * description: \"Does something useful\",\n * inputSchema: { type: \"object\", properties: {...} },\n * handler: async (input) => ({ success: true, content: \"done\" }),\n * });\n */\n\n// ── Types ────────────────────────────────────────────\n\n/**\n * Lightweight tool descriptor — the subset of internal `ToolDescriptor`\n * that plugin authors need to provide. Extra fields (kind, safety, executor,\n * owner) are filled in by the registration layer.\n */\nexport interface SdkToolDescriptor {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\n/** The shape a plugin author provides to define a tool. */\nexport interface ToolBlueprint {\n /** Unique tool name (e.g. \"web_search\"). */\n name: string;\n /** Human‑readable description for the LLM. */\n description: string;\n /** JSON Schema for the input parameters. */\n inputSchema: Record<string, unknown>;\n /** The tool implementation. */\n handler: (input: Record<string, unknown>) => Promise<{ success: boolean; content: string }>;\n}\n\n/** A defined tool — ready for registration. */\nexport interface DefinedTool {\n /** Unique tool name. */\n name: string;\n /** The tool descriptor that the registration layer consumes. */\n descriptor: SdkToolDescriptor;\n /** The tool implementation. */\n handler: (input: Record<string, unknown>) => Promise<{ success: boolean; content: string }>;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Declare a tool that can be registered by a plugin.\n *\n * The returned `DefinedTool` is passed to the plugin's register\n * function or returned from `definePlugin`.\n */\nexport function defineTool(blueprint: ToolBlueprint): DefinedTool {\n return {\n name: blueprint.name,\n descriptor: {\n name: blueprint.name,\n description: blueprint.description,\n inputSchema: blueprint.inputSchema,\n },\n handler: blueprint.handler,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["PluginLoadError"],"sources":["../src/discovery.ts","../src/loader.ts","../src/hooks.ts","../src/predictive-loader.ts","../src/sdk/define-plugin.ts","../src/sdk/define-tool.ts"],"sourcesContent":["/**\n * Plugin discovery — scan directories for manifest.json files.\n *\n * The discovery layer only reads manifest metadata; it never imports\n * plugin code. This keeps the registry fast and allows filtering\n * before loading.\n */\n\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { PluginManifest } from \"./types.js\";\nimport { PluginError } from \"@24klynx/core\";\n\n// ── Types ──────────────────────────────────────────\n\nexport interface ManifestEntry {\n /** Absolute path to the plugin directory. */\n dir: string;\n /** Parsed manifest content. */\n manifest: PluginManifest;\n}\n\nexport interface ManifestRegistry {\n /** Scan one or more directories for plugin manifests. */\n scan(directories: string[]): ManifestEntry[];\n /** Get a plugin by id (fast lookup). */\n getById(id: string): ManifestEntry | undefined;\n /** List all discovered plugins. */\n listAll(): ManifestEntry[];\n /** How many plugins are registered. */\n count(): number;\n}\n\n// ── Constants ──────────────────────────────────────\n\nconst MANIFEST_FILE = \"manifest.json\";\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Create a manifest registry.\n *\n * Call `scan()` during Phase 2 startup to populate it,\n * then use `getById()` / `listAll()` for fast lookups.\n */\nexport function createManifestRegistry(): ManifestRegistry {\n const entries = new Map<string, ManifestEntry>();\n\n const registry: ManifestRegistry = {\n scan(directories: string[]): ManifestEntry[] {\n const discovered: ManifestEntry[] = [];\n\n for (const dir of directories) {\n const manifestPath = join(dir, MANIFEST_FILE);\n\n if (!existsSync(manifestPath)) continue;\n\n let raw: string;\n try {\n raw = readFileSync(manifestPath, \"utf-8\");\n } catch (err) {\n throw new PluginLoadError(`Cannot read manifest at ${manifestPath}: ${String(err)}`);\n }\n\n let manifest: PluginManifest;\n try {\n manifest = JSON.parse(raw) as PluginManifest;\n } catch {\n throw new PluginError(`Invalid JSON in manifest: ${manifestPath}`, {\n category: \"plugin\",\n userVisible: true,\n });\n }\n\n // Validate required fields\n if (!manifest.name || !manifest.version || !manifest.entry) {\n throw new PluginError(\n `manifest.json at ${dir} is missing required fields (name, version, entry)`,\n { category: \"plugin\", userVisible: true },\n );\n }\n\n const entry: ManifestEntry = { dir, manifest };\n entries.set(manifest.name, entry);\n discovered.push(entry);\n }\n\n return discovered;\n },\n\n getById(id: string): ManifestEntry | undefined {\n return entries.get(id);\n },\n\n listAll(): ManifestEntry[] {\n return [...entries.values()];\n },\n\n count(): number {\n return entries.size;\n },\n };\n\n return registry;\n}\n\n// ── Error helper ───────────────────────────────────\n\nclass PluginLoadError extends PluginError {\n constructor(message: string) {\n super(message, { userVisible: true });\n }\n}\n","/**\n * Plugin loader — lazy import() with caching.\n *\n * Plugins are loaded once and cached. If a plugin fails to load,\n * the error is isolated — other plugins continue working.\n *\n * Fail‑open: a plugin that throws during init is unloaded but\n * the app continues without it.\n * Fail‑closed: for security‑critical hooks, a failing plugin\n * blocks the operation (see hooks.ts).\n */\n\nimport type { PluginContract, PluginRuntimeContext, PluginEntryFunction } from \"./types.js\";\nimport type { ManifestEntry } from \"./discovery.js\";\nimport { PluginError, PluginLoadError } from \"@24klynx/core\";\n\n// ── Types ──────────────────────────────────────────\n\nexport interface PluginLoader {\n /** Load a plugin from its manifest entry. */\n load(entry: ManifestEntry, ctx: PluginRuntimeContext): Promise<PluginContract>;\n /** Unload a previously loaded plugin. */\n unload(pluginName: string): void;\n /** Get a loaded plugin. */\n getLoaded(pluginName: string): PluginContract | undefined;\n /** List all loaded plugin names. */\n listLoaded(): string[];\n /** Destroy all loaded plugins. */\n destroy(): Promise<void>;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Create a plugin loader with lazy import and caching.\n */\nexport function createPluginLoader(): PluginLoader {\n const loaded = new Map<string, PluginContract>();\n\n const loader: PluginLoader = {\n async load(entry: ManifestEntry, ctx: PluginRuntimeContext): Promise<PluginContract> {\n // Return cached if already loaded\n const existing = loaded.get(entry.manifest.name);\n if (existing) return existing;\n\n let mod: { default?: PluginEntryFunction };\n try {\n // Dynamic import — the entry path is relative to the plugin directory\n const entryPath = `${entry.dir}/${entry.manifest.entry}`;\n mod = await import(entryPath);\n } catch (err) {\n throw new PluginLoadError(\n `Failed to import plugin \"${entry.manifest.name}\" from ${entry.manifest.entry}: ${String(err)}`,\n );\n }\n\n if (!mod.default || typeof mod.default !== \"function\") {\n throw new PluginError(\n `Plugin \"${entry.manifest.name}\" does not have a default export function`,\n { category: \"plugin\", userVisible: true },\n );\n }\n\n let contract: PluginContract;\n try {\n contract = await mod.default(ctx);\n } catch (err) {\n throw new PluginError(\n `Plugin \"${entry.manifest.name}\" initialisation threw: ${String(err)}`,\n { category: \"plugin\", userVisible: true },\n );\n }\n\n loaded.set(entry.manifest.name, contract);\n return contract;\n },\n\n unload(pluginName: string): void {\n const contract = loaded.get(pluginName);\n if (contract?.onUnload) {\n void contract.onUnload();\n }\n loaded.delete(pluginName);\n },\n\n getLoaded(pluginName: string): PluginContract | undefined {\n return loaded.get(pluginName);\n },\n\n listLoaded(): string[] {\n return [...loaded.keys()];\n },\n\n async destroy(): Promise<void> {\n for (const [, contract] of loaded) {\n if (contract.onUnload) {\n try {\n await contract.onUnload();\n } catch {\n // Best effort cleanup\n }\n }\n }\n loaded.clear();\n },\n };\n\n return loader;\n}\n","/**\n * Hook engine — dispatch events to registered plugin hooks.\n *\n * Supports two failure strategies:\n * fail‑open — hook failure does not block the operation (default)\n * fail‑closed — hook failure blocks the operation (security‑critical)\n */\n\nimport type { HookType, HookSink } from \"./types.js\";\n\n// ── Types ──────────────────────────────────────────\n\nexport type FailureStrategy = \"fail-open\" | \"fail-closed\";\n\nexport interface HookRegistry {\n /** Register a sink. Lower priority runs first. */\n register(sink: HookSink): void;\n /** Remove all sinks for a plugin. */\n unregisterPlugin(pluginId: string): void;\n /** Dispatch an event to all registered hooks of this type. */\n dispatch(type: HookType, event: unknown, strategy?: FailureStrategy): Promise<void>;\n /** How many sinks are registered. */\n count(): number;\n}\n\n// ── Public API ─────────────────────────────────────\n\n/**\n * Create a hook registry.\n *\n * Hooks are stored in priority‑ordered arrays per hook type.\n * Dispatch iterates them in order and short‑circuits on fail‑closed.\n */\nexport function createHookRegistry(): HookRegistry {\n const sinks = new Map<HookType, HookSink[]>();\n\n function ensureList(type: HookType): HookSink[] {\n let list = sinks.get(type);\n if (!list) {\n list = [];\n sinks.set(type, list);\n }\n return list;\n }\n\n const registry: HookRegistry = {\n register(sink: HookSink): void {\n const list = ensureList(sink.type);\n list.push(sink);\n // Keep sorted by priority (ascending)\n list.sort((a, b) => a.priority - b.priority);\n },\n\n unregisterPlugin(pluginId: string): void {\n for (const [type, list] of sinks) {\n const filtered = list.filter((s) => s.pluginId !== pluginId);\n if (filtered.length === 0) {\n sinks.delete(type);\n } else {\n sinks.set(type, filtered);\n }\n }\n },\n\n async dispatch(\n type: HookType,\n event: unknown,\n strategy: FailureStrategy = \"fail-open\",\n ): Promise<void> {\n const list = sinks.get(type);\n if (!list || list.length === 0) return;\n\n for (const sink of list) {\n try {\n await sink.handler(event);\n } catch (err) {\n if (strategy === \"fail-closed\") {\n throw err;\n }\n // fail‑open: continue to next hook\n }\n }\n },\n\n count(): number {\n let total = 0;\n for (const list of sinks.values()) {\n total += list.length;\n }\n return total;\n },\n };\n\n return registry;\n}\n","/**\n * Predictive loader — pre‑warms plugins before they are needed.\n *\n * Priority‑based warmup queue that loads plugins during idle time\n * (Phase 2 startup background tasks) and reactively based on input\n * prefix heuristics.\n *\n * Reference: §17 PredictiveLoader design\n */\n\n// ── Types ────────────────────────────────────────────\n\n/** A plugin module after lazy import(). */\ntype PluginModule = unknown;\n\ninterface QueueEntry {\n pluginId: string;\n priority: number;\n}\n\n// ── PredictiveLoader ─────────────────────────────────\n\nexport class PredictiveLoader {\n /** Map of already loaded plugins. */\n private _loaded = new Map<string, PluginModule>();\n\n /** Set of plugin ids currently being imported. */\n private _warming = new Set<string>();\n\n /** Priority queue (higher number = more important). */\n private _queue: QueueEntry[] = [];\n\n /** Abort controller for cancelling in‑flight warmups. */\n private _abort = new AbortController();\n\n /** Dynamic import function (injectable for testing). */\n private _importFn: (id: string) => Promise<PluginModule>;\n\n constructor(importFn: (id: string) => Promise<PluginModule>) {\n this._importFn = importFn;\n }\n\n // ── Public API ─────────────────────────────────────\n\n /** Whether the plugin has already been loaded. */\n isLoaded(pluginId: string): boolean {\n return this._loaded.has(pluginId);\n }\n\n /** Whether the plugin is currently being warmed. */\n isWarming(pluginId: string): boolean {\n return this._warming.has(pluginId);\n }\n\n /**\n * Enqueue a plugin for warming at the given priority.\n * Higher priority plugins are processed first.\n */\n enqueue(pluginId: string, priority: number): void {\n if (this.isLoaded(pluginId) || this.isWarming(pluginId)) {\n return; // Already done or in progress — idempotent\n }\n\n // Insert sorted by priority (descending)\n const idx = this._queue.findIndex((e) => e.priority < priority);\n if (idx < 0) {\n this._queue.push({ pluginId, priority });\n } else {\n this._queue.splice(idx, 0, { pluginId, priority });\n }\n }\n\n /** Process the entire warmup queue (non‑blocking — yields between imports). */\n async processQueue(): Promise<void> {\n while (this._queue.length > 0) {\n if (this._abort.signal.aborted) return;\n\n const entry = this._queue.shift()!;\n if (this.isLoaded(entry.pluginId)) continue;\n\n this._warming.add(entry.pluginId);\n\n try {\n const mod = await this._importFn(entry.pluginId);\n this._loaded.set(entry.pluginId, mod);\n } catch {\n // Silently skip plugins that fail to warm — they'll be\n // retried (with an error report) when actually requested.\n } finally {\n this._warming.delete(entry.pluginId);\n }\n\n // Yield to the event loop so the main thread stays responsive.\n await new Promise((resolve) => setImmediate(resolve));\n }\n }\n\n /** Stop all in‑flight warmups and clear the queue. */\n abort(): void {\n this._abort.abort();\n this._queue.length = 0;\n this._warming.clear();\n }\n\n /** Number of plugins currently queued for warming. */\n get pendingCount(): number {\n return this._queue.length;\n }\n\n /** Number of plugins that have been successfully loaded. */\n get loadedCount(): number {\n return this._loaded.size;\n }\n\n // ── Input prediction ───────────────────────────────\n\n /**\n * Predict which plugins to warm based on input prefix.\n *\n * Heuristics (200 ms window after prefix character):\n * \"/\" → skill plugins\n * \"@\" → model/provider plugins\n */\n onInputPrefix(prefix: string): void {\n if (prefix === \"/\") {\n this.enqueue(\"@24klynx/skills\", 100);\n } else if (prefix === \"@\") {\n this.enqueue(\"@24klynx/model-picker\", 90);\n }\n }\n\n /**\n * Predict which plugins to warm based on a pending action.\n * \"file_read\" → file operations plugin\n * \"code_edit\" → editor tool plugin\n */\n onActionPending(action: string): void {\n if (action.startsWith(\"file_\")) {\n this.enqueue(\"@24klynx/tool-file-ops\", 80);\n } else if (action === \"code_edit\") {\n this.enqueue(\"@24klynx/tool-edit\", 80);\n } else if (action === \"web_search\") {\n this.enqueue(\"@24klynx/tool-web\", 70);\n }\n }\n}\n","/**\n * `definePlugin` — the entry point for every Lynx plugin.\n *\n * Pattern (inspired by FengMing's `definePluginEntry`):\n * export default definePlugin((ctx) => {\n * ctx.logger.info(\"loaded\");\n * return { name: \"my-plugin\", tools: [...] };\n * });\n */\n\nimport type { PluginContext } from \"./types.js\";\nimport type { PluginManifest } from \"../types.js\";\n\n// ── Types ────────────────────────────────────────────\n\n/** What a plugin's register function returns. */\nexport interface PluginDefinition {\n /** Display name. */\n name: string;\n /** Semver version string. */\n version?: string;\n /** Human‑readable description. */\n description?: string;\n /** Hook declarations (executed by the hook engine). */\n hooks?: HookDeclaration[];\n}\n\n/** A hook declaration — static metadata, handler runs at runtime. */\nexport interface HookDeclaration {\n /** Hook event name (e.g. \"before_agent_reply\"). */\n type: string;\n /** \"fail-open\": hook failure doesn't block; \"fail-closed\": hook failure rejects. */\n policy: \"fail-open\" | \"fail-closed\";\n /** The handler function. */\n handler: (event: unknown) => Promise<void>;\n}\n\n/** The register callback signature. */\nexport type PluginRegisterFn = (ctx: PluginContext) => PluginDefinition | Promise<PluginDefinition>;\n\n/** The shape returned by `definePlugin`. */\nexport interface DefinedPlugin {\n /** Unique plugin id (from manifest.json). */\n id: string;\n manifest: PluginManifest;\n register: PluginRegisterFn;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Declare a Lynx plugin.\n *\n * `register` is called once at startup with a {@link PluginContext}\n * that provides logging, storage, and event emission.\n */\nexport function definePlugin(manifest: PluginManifest, register: PluginRegisterFn): DefinedPlugin {\n return {\n id: manifest.name,\n manifest,\n register,\n };\n}\n","/**\n * `defineTool` — declare a tool that a plugin contributes to the agent.\n *\n * Pattern (inspired by FengMing's `api.registerTool`):\n * const myTool = defineTool({\n * name: \"my_tool\",\n * description: \"Does something useful\",\n * inputSchema: { type: \"object\", properties: {...} },\n * handler: async (input) => ({ success: true, content: \"done\" }),\n * });\n */\n\n// ── Types ────────────────────────────────────────────\n\n/**\n * Lightweight tool descriptor — the subset of internal `ToolDescriptor`\n * that plugin authors need to provide. Extra fields (kind, safety, executor,\n * owner) are filled in by the registration layer.\n */\nexport interface SdkToolDescriptor {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n}\n\n/** The shape a plugin author provides to define a tool. */\nexport interface ToolBlueprint {\n /** Unique tool name (e.g. \"web_search\"). */\n name: string;\n /** Human‑readable description for the LLM. */\n description: string;\n /** JSON Schema for the input parameters. */\n inputSchema: Record<string, unknown>;\n /** The tool implementation. */\n handler: (input: Record<string, unknown>) => Promise<{ success: boolean; content: string }>;\n}\n\n/** A defined tool — ready for registration. */\nexport interface DefinedTool {\n /** Unique tool name. */\n name: string;\n /** The tool descriptor that the registration layer consumes. */\n descriptor: SdkToolDescriptor;\n /** The tool implementation. */\n handler: (input: Record<string, unknown>) => Promise<{ success: boolean; content: string }>;\n}\n\n// ── Public API ───────────────────────────────────────\n\n/**\n * Declare a tool that can be registered by a plugin.\n *\n * The returned `DefinedTool` is passed to the plugin's register\n * function or returned from `definePlugin`.\n */\nexport function defineTool(blueprint: ToolBlueprint): DefinedTool {\n return {\n name: blueprint.name,\n descriptor: {\n name: blueprint.name,\n description: blueprint.description,\n inputSchema: blueprint.inputSchema,\n },\n handler: blueprint.handler,\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@24klynx/plugins",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Plugin system — discovery, loading, hook engine, SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@24klynx/core": "0.1.
|
|
15
|
+
"@24klynx/core": "0.1.2"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist"
|