aws-runtime-bridge 1.9.169 → 1.9.170
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/CHANGELOG.md +33 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts +46 -0
- package/dist/adapter/AcodeSdkAdapter.js +4 -4
- package/dist/adapter/ClaudeSdkAdapter.js +2 -2
- package/dist/adapter/types.d.ts +7 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +2 -2
- package/dist/index.js +1 -1
- package/dist/routes/agent-state.d.ts +2 -0
- package/dist/routes/agent-state.js +2 -0
- package/dist/routes/capabilities.js +1 -1
- package/dist/routes/debug-mount.d.ts +2 -0
- package/dist/routes/debug-mount.js +2 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/plugins.js +1 -1
- package/dist/routes/skills.js +1 -1
- package/dist/routes/terminal.js +28 -32
- package/dist/services/acode-bg-manager.d.ts +2 -0
- package/dist/services/agent-capability-sync.js +1 -1
- package/dist/services/debug-mount.d.ts +122 -0
- package/dist/services/debug-mount.js +2 -0
- package/dist/services/desktop-mcp-manager.js +1 -1
- package/dist/services/instance-ai-config-service.js +1 -1
- package/dist/services/instance-features.d.ts +4 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/instance-init-service.js +1 -1
- package/dist/services/mcp-deploy.d.ts +31 -0
- package/dist/services/mcp-deploy.js +4 -3
- package/dist/services/mcp-env-injector.d.ts +24 -0
- package/dist/services/mcp-env-injector.js +1 -1
- package/dist/services/mcp-package.d.ts +35 -0
- package/dist/services/mcp-package.js +2 -0
- package/dist/services/mcp-probe-client.d.ts +25 -0
- package/dist/services/mcp-probe-client.js +2 -0
- package/dist/services/panel-env.d.ts +5 -0
- package/dist/services/panel-env.js +1 -1
- package/dist/services/plugin-deployment.d.ts +36 -0
- package/dist/services/plugin-deployment.js +3 -0
- package/dist/services/plugin-event-buffer.js +2 -1
- package/dist/services/plugin-fs.d.ts +5 -0
- package/dist/services/plugin-fs.js +2 -0
- package/dist/services/plugin-handler-adapter.d.ts +7 -0
- package/dist/services/plugin-handler-adapter.js +3 -0
- package/dist/services/plugin-listener-runner.d.ts +18 -0
- package/dist/services/plugin-listener-runner.js +8 -0
- package/dist/services/plugin-loader.d.ts +32 -0
- package/dist/services/plugin-loader.js +2 -0
- package/dist/services/plugin-manager.d.ts +17 -136
- package/dist/services/plugin-manager.js +4 -15
- package/dist/services/plugin-manifest.d.ts +83 -0
- package/dist/services/plugin-manifest.js +2 -0
- package/dist/services/plugin-runtime.d.ts +67 -0
- package/dist/services/plugin-runtime.js +1 -0
- package/dist/services/skill-manager.d.ts +15 -0
- package/dist/services/skill-manager.js +3 -2
- package/dist/types.d.ts +6 -0
- package/dist/utils/mcp-utils.js +1 -1
- package/package/acode/dist/compaction.d.ts +35 -1
- package/package/acode/dist/compaction.js +6 -6
- package/package/acode/dist/plugin-types.d.ts +6 -27
- package/package/acode/dist/runtime.d.ts +37 -0
- package/package/acode/dist/runtime.js +31 -30
- package/package/acode/dist/tool-group-manager.d.ts +10 -0
- package/package/acode/dist/tool-group-manager.js +5 -10
- package/package/acode/dist/types.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/ability-market-tools.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/ability-market-tools.js +2 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +13 -4
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -7
- package/package/aws-client-agent-mcp/dist/mcp-server.js +12 -12
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +20 -20
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js +2 -2
- package/package/aws-client-agent-mcp/dist/memory-tools.js +7 -7
- package/package/aws-client-agent-mcp/dist/tool-groups.js +2 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts +0 -21
- package/package.json +3 -2
- package/dist/services/builtin-commands/create-plugin.md +0 -44
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{existsSync as A,readFileSync as S}from"node:fs";import{join as p}from"node:path";import{isRecord as b,validateListenerDef as j,validateManifest as J,validateToolDef as R}from"./plugin-manifest.js";class O{constructor(){this.importNonce=0}readJsonFile(t){if(!A(t))return{ok:!0,data:{}};try{const n=JSON.parse(S(t,"utf8"));return b(n)?{ok:!0,data:n}:{ok:!1,error:`${t}: \u6839\u8282\u70B9\u987B\u4E3A\u5BF9\u8C61`}}catch(n){const i=n instanceof Error?n.message:String(n);return{ok:!1,error:`${t}: JSON \u89E3\u6790\u5931\u8D25: ${i}`}}}loadManifestOnly(t){const n=this.readJsonFile(p(t,"plugin.json"));if(!n.ok)return n;const i=n.data,a=J(i);return a?{ok:!1,error:a}:{ok:!0,manifest:i}}validateUserConfigValues(t,n){const i=Array.isArray(t.userConfig)?t.userConfig:[],a={...n};for(const e of i){if(!e||typeof e.key!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.key))return"plugin.json: userConfig.key \u975E\u6CD5\uFF08\u987B\u4E3A\u73AF\u5883\u53D8\u91CF\u540D\uFF09";if(!(typeof a[e.key]=="string"&&a[e.key].length>0)&&e.required){const f=e.default!==void 0&&typeof e.default=="string"?e.default:"";if(!f)return`\u7F3A\u5C11\u5FC5\u9700\u914D\u7F6E ${e.key}${e.description?`\uFF08${e.description}\uFF09`:""}\uFF0C\u5B89\u88C5\u65F6\u8BF7\u5728 config \u4E2D\u63D0\u4F9B`;a[e.key]=f}}return null}loadPluginFromDir(t,n,i,a){const e=this.loadManifestOnly(t);if(!e.ok)return e;const s=e.manifest,f=p(t,s.tools??"tools/tools.json"),u=this.readJsonFile(f);if(!u.ok)return u;const g=[],C=Array.isArray(u.data.tools)?u.data.tools:[];for(const r of C){const l=R(r);if(l)return{ok:!1,error:`${s.tools??"tools/tools.json"}: ${l}`};g.push(r)}const $=p(t,s.listeners??"listeners/listeners.json"),c=this.readJsonFile($);if(!c.ok)return c;const m=[],F=Array.isArray(c.data.listeners)?c.data.listeners:[];for(const r of F){const l=j(r);if(l)return{ok:!1,error:`${s.listeners??"listeners/listeners.json"}: ${l}`};m.push(r)}const k=Array.isArray(s.userConfig)?s.userConfig:[],h=this.validateUserConfigValues(s,a);if(h)return{ok:!1,error:h};const d={...a};for(const r of k){if(!r||typeof r.key!="string")continue;!(typeof d[r.key]=="string"&&d[r.key].length>0)&&r.required&&r.default!==void 0&&typeof r.default=="string"&&(d[r.key]=r.default)}const y=s.commands;let v;if(typeof y=="string"&&y.trim()){const r=p(t,y.trim());A(r)&&(v=r)}return{ok:!0,plugin:{name:s.name,version:typeof s.version=="string"?s.version:void 0,description:typeof s.description=="string"?s.description:void 0,source:n,installedAt:i,dir:t,enabled:!0,tools:g,listeners:m,commandDir:v,userConfig:k,configValues:d,status:"enabled",runtimes:m.map(r=>({def:r,stopping:!1,restarts:0,logs:[]})),moduleCache:new Map,importNonce:++this.importNonce}}}}function w(o){const t=o.inputSchema;if(!(!t||!Array.isArray(t.required)))return t.required.filter(n=>typeof n=="string")}function x(o){const t=e=>!!(e.child||e.timer)||e.def.transport?.type==="http-callback",n=e=>o.enabled?t(e)?"running":e.stopping?"stopped":"pending":"stopped",i=o.runtimes.map(e=>{const s=n(e);return{event:e.def.event,delivery:e.def.delivery??"poll",running:s==="running",state:s,error:e.lastError}}),a=i.some(e=>e.state==="running")?"running":i.some(e=>e.state==="pending")?"pending":"stopped";return{name:o.name,version:o.version,description:o.description,status:o.status,enabled:o.enabled,source:o.source,installedAt:o.installedAt,toolCount:o.tools.length,tools:o.tools.map(e=>({name:e.name,title:e.title,description:e.description})),listeners:i,running:a==="running",listenerState:a,error:o.error}}export{O as PluginLoader,w as requiredParamsOf,x as toSummary};
|
|
2
|
+
|
|
@@ -1,80 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
import { type ACodeCommandSpec, type ACodePluginBridge, type
|
|
3
|
-
import { type PluginEventMessage
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
export declare const BUILTIN_PLUGIN_MANAGE_DESC = "\u300C\u63D2\u4EF6\u7BA1\u7406\u63D2\u4EF6\u300D\u7684\u751F\u547D\u5468\u671F\u5DE5\u5177\uFF1A\u7BA1\u7406\u63D2\u4EF6\uFF08\u5B89\u88C5/\u5378\u8F7D/\u91CD\u8F7D/\u542F\u505C/\u5217\u8868\uFF09\u3002\u4EC5\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u5B89\u88C5\u3001\u79FB\u9664\u6216\u4FEE\u590D\u63D2\u4EF6\u65F6\u4F7F\u7528\u3002\n- action=list\uFF1A\u5217\u51FA\u5DF2\u5B89\u88C5\u63D2\u4EF6\uFF08\u72B6\u6001\u3001\u76D1\u542C\u5668\u5065\u5EB7\u3001\u5DE5\u5177\u6570\uFF09\u3002\n- action=install\uFF1Asource \u4E3A\u63D2\u4EF6\u6765\u6E90\uFF0C\u5F53\u524D\u652F\u6301 \"local:<\u4EFB\u610F\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>\"\uFF08\u4E0D\u505A\u76EE\u5F55\u4F4D\u7F6E\u9650\u5236\uFF09\uFF1Bconfig \u4F20\u63D2\u4EF6 manifest \u91CC userConfig \u58F0\u660E\u7684\u914D\u7F6E\u503C\u3002\u5B89\u88C5\u65F6\u4F1A\u5148\u628A\u8BE5\u76EE\u5F55\u79FB\u52A8\u5230\u672C agent \u52A0\u8F7D\u63D2\u4EF6\u7684\u4F4D\u7F6E\uFF0C\u518D\u52A0\u8F7D\u79FB\u52A8\u540E\u7684\u526F\u672C\uFF08\u79FB\u52A8\u540E\u6E90\u76EE\u5F55\u4E0D\u518D\u4FDD\u7559\uFF09\uFF0C\u5B89\u88C5\u540E\u7ACB\u5373\u751F\u6548\uFF1A\u4E0B\u4E00\u6B21 poll_message \u7684 pluginCatalog \u5373\u53EF\u89C1\u3002\n- action=reload\uFF1A\u91CD\u8F7D\u6307\u5B9A\u63D2\u4EF6\uFF08\u6539\u8FC7\u63D2\u4EF6\u6587\u4EF6\u540E\u4F7F\u7528\uFF09\uFF0C\u539F\u5B50\u6362\u88C5\uFF0C\u76D1\u542C\u5668\u91CD\u542F\u3002\n- action=enable / disable\uFF1A\u505C\u7528/\u542F\u7528\uFF08disable \u4FDD\u7559\u5B89\u88C5\u4E0E\u914D\u7F6E\uFF0C\u4EC5\u505C\u6B62\u76D1\u542C\u4E0E\u5DE5\u5177\u8C03\u7528\uFF09\u3002\n- action=uninstall\uFF1A\u5378\u8F7D\u5E76\u5220\u9664\u63D2\u4EF6\u76EE\u5F55\uFF08\u4E0D\u53EF\u9006\uFF0C\u9700\u7528\u6237\u660E\u786E\u8981\u6C42\uFF09\u3002";
|
|
9
|
-
|
|
10
|
-
export declare function getBuiltinCommandDir(): string;
|
|
11
|
-
export interface PluginUserConfigField {
|
|
12
|
-
key: string;
|
|
13
|
-
type?: "string" | "number" | "boolean";
|
|
14
|
-
required?: boolean;
|
|
15
|
-
sensitive?: boolean;
|
|
16
|
-
description?: string;
|
|
17
|
-
default?: string;
|
|
18
|
-
}
|
|
19
|
-
export interface PluginHandlerDef {
|
|
20
|
-
type: "process" | "module" | "mcp";
|
|
21
|
-
|
|
22
|
-
command?: string;
|
|
23
|
-
|
|
24
|
-
shell?: boolean;
|
|
25
|
-
|
|
26
|
-
stdin?: "none" | "params";
|
|
27
|
-
|
|
28
|
-
timeoutMs?: number;
|
|
29
|
-
|
|
30
|
-
module?: string;
|
|
31
|
-
|
|
32
|
-
export?: string;
|
|
33
|
-
}
|
|
34
|
-
export interface PluginToolDef {
|
|
35
|
-
name: string;
|
|
36
|
-
title?: string;
|
|
37
|
-
description: string;
|
|
38
|
-
inputSchema?: Record<string, unknown>;
|
|
39
|
-
handler: PluginHandlerDef;
|
|
40
|
-
}
|
|
41
|
-
export interface PluginTransportDef {
|
|
42
|
-
type: "command-stream" | "poll-command" | "http-callback";
|
|
43
|
-
|
|
44
|
-
command?: string;
|
|
45
|
-
|
|
46
|
-
intervalMs?: number;
|
|
47
|
-
|
|
48
|
-
restartBackoffMs?: number;
|
|
49
|
-
}
|
|
50
|
-
export interface PluginListenerDef {
|
|
51
|
-
event: string;
|
|
52
|
-
description?: string;
|
|
53
|
-
transport: PluginTransportDef;
|
|
54
|
-
|
|
55
|
-
delivery?: "poll" | "force" | "wake";
|
|
56
|
-
priority?: PluginEventPriority;
|
|
57
|
-
|
|
58
|
-
matcher?: Record<string, string | number | boolean | Array<string | number | boolean>>;
|
|
59
|
-
|
|
60
|
-
dedupKey?: string;
|
|
61
|
-
|
|
62
|
-
toolHints?: string[];
|
|
63
|
-
|
|
64
|
-
promptTemplate?: string;
|
|
65
|
-
}
|
|
66
|
-
export interface PluginManifest {
|
|
67
|
-
name: string;
|
|
68
|
-
version?: string;
|
|
69
|
-
description?: string;
|
|
70
|
-
author?: Record<string, unknown>;
|
|
71
|
-
tools?: string;
|
|
72
|
-
listeners?: string;
|
|
73
|
-
commands?: string;
|
|
74
|
-
userConfig?: PluginUserConfigField[];
|
|
75
|
-
[key: string]: unknown;
|
|
76
|
-
}
|
|
77
|
-
export declare function validateAgainstSchema(schema: unknown, value: unknown, path?: string): string[];
|
|
2
|
+
import { type ACodeCommandSpec, type ACodePluginBridge, type PluginInvokeContext, type PluginManageRequest, type PluginManageResult, type PluginSummary, type PluginToolInvocationResult, type PluginToolSchema, type JsonObject } from "@agentswork/acode";
|
|
3
|
+
import { type PluginEventMessage } from "./plugin-event-buffer.js";
|
|
4
|
+
export { isValidPluginName, validateManifest, validateAgainstSchema, renderCommandArgv, } from "./plugin-manifest.js";
|
|
5
|
+
export type { PluginUserConfigField, PluginHandlerDef, PluginToolDef, PluginTransportDef, PluginListenerDef, PluginManifest, } from "./plugin-manifest.js";
|
|
6
|
+
export type { ListenerRuntime, LoadedPlugin, InstalledRecord, AgentState, SharedPluginEntry, } from "./plugin-runtime.js";
|
|
78
7
|
export interface PluginManagerOptions {
|
|
79
8
|
|
|
80
9
|
rootDir?: string;
|
|
@@ -86,7 +15,11 @@ export declare class PluginManager {
|
|
|
86
15
|
private readonly rootDir;
|
|
87
16
|
private readonly runtimeRootDir?;
|
|
88
17
|
|
|
89
|
-
private
|
|
18
|
+
private readonly listeners;
|
|
19
|
+
|
|
20
|
+
private readonly loader;
|
|
21
|
+
|
|
22
|
+
private readonly deploy;
|
|
90
23
|
constructor(options?: PluginManagerOptions);
|
|
91
24
|
forAgent(agentId: string): ACodePluginBridge;
|
|
92
25
|
|
|
@@ -102,16 +35,15 @@ export declare class PluginManager {
|
|
|
102
35
|
list(agentId: string): Promise<PluginSummary[]>;
|
|
103
36
|
|
|
104
37
|
private deployedPluginVersion;
|
|
105
|
-
getCatalog(agentId: string): Promise<PluginCatalogSnapshot>;
|
|
106
|
-
getToolSchema(agentId: string, qualifiedName: string): Promise<PluginToolSchema | null>;
|
|
107
|
-
invokeTool(agentId: string, pluginName: string, toolName: string, params: JsonObject, ctx?: PluginInvokeContext): Promise<PluginToolInvocationResult>;
|
|
108
38
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
39
|
+
getPluginTools(agentId: string, pluginName: string): Promise<PluginToolSchema[]>;
|
|
40
|
+
|
|
41
|
+
debugMountPlugin(agentId: string, dir: string, config?: Record<string, string>): Promise<PluginManageResult>;
|
|
42
|
+
|
|
43
|
+
listDebugPlugins(agentId: string): Promise<PluginSummary[]>;
|
|
113
44
|
|
|
114
|
-
|
|
45
|
+
debugPluginNames(agentId: string): Promise<string[]>;
|
|
46
|
+
invokeTool(agentId: string, pluginName: string, toolName: string, params: JsonObject, ctx?: PluginInvokeContext): Promise<PluginToolInvocationResult>;
|
|
115
47
|
manage(agentId: string, request: PluginManageRequest): Promise<PluginManageResult>;
|
|
116
48
|
private doManage;
|
|
117
49
|
private installPlugin;
|
|
@@ -122,25 +54,6 @@ export declare class PluginManager {
|
|
|
122
54
|
private uninstallPlugin;
|
|
123
55
|
private reloadPlugin;
|
|
124
56
|
private setEnabled;
|
|
125
|
-
private deployedDir;
|
|
126
|
-
private deployedManifestFile;
|
|
127
|
-
|
|
128
|
-
private deployedPluginDir;
|
|
129
|
-
|
|
130
|
-
private legacySharedDir;
|
|
131
|
-
private readSharedManifest;
|
|
132
|
-
private writeSharedManifest;
|
|
133
|
-
|
|
134
|
-
private migrateLegacySharedIfNeeded;
|
|
135
|
-
|
|
136
|
-
private deployedPluginHealthy;
|
|
137
|
-
|
|
138
|
-
private runtimeDirFor;
|
|
139
|
-
private runtimeLinkPath;
|
|
140
|
-
|
|
141
|
-
private ensureRuntimeLink;
|
|
142
|
-
|
|
143
|
-
private removeRuntimeLink;
|
|
144
57
|
|
|
145
58
|
saveSharedPlugin(zipBuffer: Buffer, originalName: string, defaultEnabled?: boolean, sha256?: string): Promise<{
|
|
146
59
|
ok: boolean;
|
|
@@ -148,8 +61,6 @@ export declare class PluginManager {
|
|
|
148
61
|
pluginName?: string;
|
|
149
62
|
}>;
|
|
150
63
|
|
|
151
|
-
private deployPluginZipToPersistentDir;
|
|
152
|
-
|
|
153
64
|
private suspendDeployedPluginAcrossAgents;
|
|
154
65
|
|
|
155
66
|
applySharedToKnownAgents(): Promise<Array<{
|
|
@@ -174,31 +85,19 @@ export declare class PluginManager {
|
|
|
174
85
|
private cleanupRuntimeDir;
|
|
175
86
|
|
|
176
87
|
applyCapabilities(agentId: string): Promise<void>;
|
|
177
|
-
|
|
178
|
-
exposePlugin(agentId: string, plugin: string): void;
|
|
179
88
|
drainEvents(agentId: string, opts?: {
|
|
180
89
|
maxPerKey?: number;
|
|
181
90
|
}): Promise<{
|
|
182
91
|
revision: number;
|
|
183
92
|
events: PluginEventMessage[];
|
|
184
|
-
|
|
185
|
-
catalog: PluginCatalogSnapshot | null;
|
|
186
|
-
|
|
187
|
-
compactionSeq: number;
|
|
188
93
|
}>;
|
|
189
94
|
|
|
190
|
-
private consumeExposedCatalog;
|
|
191
|
-
|
|
192
95
|
private pushDeprecatedNotice;
|
|
193
96
|
|
|
194
|
-
markAgentCompacted(agentId: string): void;
|
|
195
|
-
|
|
196
97
|
enqueueExternalEvent(agentId: string, pluginName: string, payload: Record<string, unknown>): Promise<{
|
|
197
98
|
ok: boolean;
|
|
198
99
|
error?: string;
|
|
199
100
|
}>;
|
|
200
|
-
|
|
201
|
-
private bumpCatalogRevision;
|
|
202
101
|
private agentState;
|
|
203
102
|
|
|
204
103
|
private hydrate;
|
|
@@ -214,25 +113,12 @@ export declare class PluginManager {
|
|
|
214
113
|
private readSecrets;
|
|
215
114
|
|
|
216
115
|
private persistSecrets;
|
|
217
|
-
private readJsonFile;
|
|
218
|
-
private loadManifestOnly;
|
|
219
|
-
|
|
220
|
-
private validateUserConfigValues;
|
|
221
|
-
private loadPluginFromDir;
|
|
222
116
|
|
|
223
117
|
private ensureListenersRunning;
|
|
224
118
|
private startAllListeners;
|
|
225
119
|
private stopAllListeners;
|
|
226
|
-
private startListener;
|
|
227
|
-
private spawnListenerChild;
|
|
228
|
-
private scheduleListenerRestart;
|
|
229
120
|
|
|
230
|
-
private pollListenerOnce;
|
|
231
|
-
private stopListener;
|
|
232
|
-
private handleListenerLine;
|
|
233
121
|
private enqueueEvent;
|
|
234
|
-
private requiredParamsOf;
|
|
235
|
-
private toSummary;
|
|
236
122
|
getLogs(agentId: string, pluginName: string): Promise<{
|
|
237
123
|
ok: boolean;
|
|
238
124
|
error?: string;
|
|
@@ -260,10 +146,5 @@ export declare class PluginManager {
|
|
|
260
146
|
}>;
|
|
261
147
|
}
|
|
262
148
|
|
|
263
|
-
export declare function isValidPluginName(name: unknown): name is string;
|
|
264
|
-
export declare function validateManifest(manifest: Record<string, unknown>): string | null;
|
|
265
|
-
|
|
266
|
-
export declare function renderCommandArgv(command: string, lookup: Record<string, unknown>): string[];
|
|
267
|
-
|
|
268
149
|
export declare const pluginManager: PluginManager;
|
|
269
150
|
export declare const reconcilePluginsDefaultsFromDeployed: () => number;
|
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
import{spawn as _}from"node:child_process";import{cpSync as F,existsSync as w,lstatSync as ie,mkdirSync as N,readFileSync as b,readdirSync as x,renameSync as j,rmSync as P,symlinkSync as se,writeFileSync as q}from"node:fs";import{dirname as J,join as m,resolve as Y}from"node:path";import{fileURLToPath as oe,pathToFileURL as ae}from"node:url";import{parseAcodeCommandMarkdown as le,splitQualifiedToolName as ue}from"@agentswork/acode";import{getBridgeInstancePluginsDir as ce,getBridgeTemplatePluginsDir as de,getBridgeVersion as fe}from"../config.js";import{sanitizeAgentId as I,withFileLock as W,downloadPluginZip as ge,sanitizeArchiveFileName as pe}from"../utils/file-utils.js";import{BLOCKED_ENV_KEYS as he}from"../utils/env-safety.js";import{createLogger as me}from"../utils/logger.js";import{setDeployedDefaultsProvider as ye,reconcileDefaultsFromDeployed as Se}from"./capability-config.js";import{isCapabilityEnabled as we,getAgentPluginSources as ve}from"./instance-features.js";import{PluginEventBuffer as $e}from"./plugin-event-buffer.js";import{extractPluginZip as U}from"./plugin-package.js";const f=me("plugin-manager"),k="plugin-manager",X="\u7CFB\u7EDF\u5185\u7F6E\uFF1A\u7BA1\u7406\u63D2\u4EF6\u7684\u751F\u547D\u5468\u671F\uFF08\u5B89\u88C5/\u5378\u8F7D/\u542F\u505C/\u91CD\u8F7D\uFF09\uFF1B\u5F00\u53D1\u63D2\u4EF6\u7528\u5185\u7F6E /create-plugin \u547D\u4EE4",Q=`\u300C\u63D2\u4EF6\u7BA1\u7406\u63D2\u4EF6\u300D\u7684\u751F\u547D\u5468\u671F\u5DE5\u5177\uFF1A\u7BA1\u7406\u63D2\u4EF6\uFF08\u5B89\u88C5/\u5378\u8F7D/\u91CD\u8F7D/\u542F\u505C/\u5217\u8868\uFF09\u3002\u4EC5\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u5B89\u88C5\u3001\u79FB\u9664\u6216\u4FEE\u590D\u63D2\u4EF6\u65F6\u4F7F\u7528\u3002
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- action=enable / disable\uFF1A\u505C\u7528/\u542F\u7528\uFF08disable \u4FDD\u7559\u5B89\u88C5\u4E0E\u914D\u7F6E\uFF0C\u4EC5\u505C\u6B62\u76D1\u542C\u4E0E\u5DE5\u5177\u8C03\u7528\uFF09\u3002
|
|
6
|
-
- action=uninstall\uFF1A\u5378\u8F7D\u5E76\u5220\u9664\u63D2\u4EF6\u76EE\u5F55\uFF08\u4E0D\u53EF\u9006\uFF0C\u9700\u7528\u6237\u660E\u786E\u8981\u6C42\uFF09\u3002`,ke={type:"object",properties:{action:{type:"string",enum:["install","uninstall","reload","enable","disable","list"],description:"\u751F\u547D\u5468\u671F\u52A8\u4F5C"},name:{type:"string",description:"\u63D2\u4EF6\u540D\uFF08uninstall/reload/enable/disable \u5FC5\u586B\uFF09"},source:{type:"string",description:'install \u6765\u6E90\uFF0C\u5F53\u524D\u652F\u6301 "local:<\u4EFB\u610F\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>"\uFF08\u5B89\u88C5\u65F6\u81EA\u52A8\u79FB\u52A8\u5230\u672C agent \u63D2\u4EF6\u4F4D\u7F6E\u540E\u52A0\u8F7D\uFF09'},config:{type:"object",description:"install \u65F6\u63D2\u4EF6 userConfig \u58F0\u660E\u7684\u914D\u7F6E\u503C\uFF08\u952E\u503C\u5BF9\uFF09",additionalProperties:{type:"string"}}},required:["action"],additionalProperties:!1},Pe=new Set(["install","uninstall","reload","enable","disable","list"]);function ee(){const l=fe();return l&&l!=="unknown"?l:void 0}function be(){return oe(new URL("./builtin-commands/",import.meta.url))}const Ee=200,M=256*1024,De=4*1024,te=6e4,z=6e4,Ae=/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/,H=/^[a-z0-9][a-z0-9-]*$/,Ne=["system.","aws-","acode-"],Ce=/^[A-Za-z_][A-Za-z0-9_]*$/,Le=process.platform==="win32"?"junction":"dir",Ie=/[^\x20-\x7E]/,Oe=6e4,V=20,ne=80;function v(l){return typeof l=="object"&&l!==null&&!Array.isArray(l)}function O(l,e){let t=l;for(const n of e.split(".")){if(!v(t))return;t=t[n]}return t}function D(l,e){return l.length<=e?l:`${l.slice(0,e)}\u2026(\u622A\u65AD,\u5171 ${l.length} \u5B57\u7B26)`}async function T(l,e=8,t=200){for(let n=0;;n++)try{P(l,{recursive:!0,force:!0});return}catch(r){if(n>=e-1)throw r;await new Promise(i=>setTimeout(i,t))}}async function re(l,e){N(J(e),{recursive:!0});try{j(l,e);return}catch(t){const n=t?.code;if(!n||!["EXDEV","EPERM","EACCES","EBUSY","EINVAL"].includes(n))throw t}F(l,e,{recursive:!0,filter:t=>!/[\\/]\.git([\\/]|$)/.test(t)&&!/[\\/]node_modules([\\/]|$)/.test(t)}),await T(l)}function A(l,e){l.logs.push(`${new Date().toISOString()} ${e}`),l.logs.length>Ee&&l.logs.shift()}function G(l,e,t="params"){if(!v(l))return[];const n=[],r=l.type;if(typeof r=="string"&&!Te(r,e))return[`${t}: \u671F\u671B ${r}\uFF0C\u5B9E\u9645 ${Me(e)}`];if(Array.isArray(l.enum)&&l.enum.length>0&&(l.enum.some(s=>JSON.stringify(s)===JSON.stringify(e))||n.push(`${t}: \u503C ${JSON.stringify(e)} \u4E0D\u5728\u679A\u4E3E ${JSON.stringify(l.enum)} \u5185`)),r==="object"&&v(e)){const i=v(l.properties)?l.properties:{},s=Array.isArray(l.required)?l.required:[];for(const o of s)(!(o in e)||e[o]===void 0)&&n.push(`${t}.${o}: \u5FC5\u586B\u53C2\u6570\u7F3A\u5931`);for(const[o,a]of Object.entries(i))o in e&&e[o]!==void 0&&n.push(...G(a,e[o],`${t}.${o}`));if(l.additionalProperties===!1)for(const o of Object.keys(e))o in i||n.push(`${t}.${o}: \u4E0D\u5141\u8BB8\u7684\u672A\u77E5\u53C2\u6570\uFF08schema additionalProperties=false\uFF09`)}return r==="array"&&Array.isArray(e)&&v(l.items)&&e.forEach((i,s)=>{n.push(...G(l.items,i,`${t}[${s}]`))}),n}function Te(l,e){switch(l){case"string":return typeof e=="string";case"number":return typeof e=="number"&&Number.isFinite(e);case"boolean":return typeof e=="boolean";case"object":return v(e);case"array":return Array.isArray(e);case"null":return e===null;default:return!0}}function Me(l){return l===null?"null":Array.isArray(l)?"array":typeof l}class Re{constructor(e={}){this.agents=new Map,this.moduleNonce=0,this.templatePullInFlight=new Set,this.backgroundTemplatePulls=new Set,this.rootDir=e.rootDir??de(),this.runtimeRootDir=e.runtimeRootDir}forAgent(e){return{listPlugins:()=>this.list(e),getCatalog:()=>this.getCatalog(e),getToolSchema:t=>this.getToolSchema(e,t),invokeTool:(t,n,r,i)=>this.invokeTool(e,t,n,r,i),manage:t=>this.manage(e,t),getCommandDirs:()=>this.getCommandDirs(e),exposePlugin:t=>this.exposePlugin(e,t)}}setOnPluginsChanged(e){this.onPluginsChanged=e}notifyPluginsChanged(e){try{this.onPluginsChanged?.(e)}catch(t){f.warn("[agent:%s] onPluginsChanged \u56DE\u8C03\u5931\u8D25: %s",e,t instanceof Error?t.message:String(t))}}async getCommandDirs(e){return[...(await this.hydrate(e)).plugins.values()].filter(n=>n.enabled&&n.commandDir).map(n=>n.commandDir)}async getPluginCommands(e){const t=await this.hydrate(e),n=[],r=be(),i=this.parseCommandDir(k,r);i.length===0&&f.warn("[plugin-manager] \u5185\u7F6E\u547D\u4EE4\u76EE\u5F55\u4E0D\u53EF\u8BFB\u6216\u4E3A\u7A7A: %s\uFF08/create-plugin \u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u5B89\u88C5\u5305\u662F\u5426\u5B8C\u6574\uFF09",r),n.push(...i);for(const s of t.plugins.values())!s.enabled||!s.commandDir||n.push(...this.parsePluginCommandDir(s));return n}parsePluginCommandDir(e){return this.parseCommandDir(e.name,e.commandDir)}parseCommandDir(e,t){const n=[];if(!t)return n;try{for(const r of x(t)){if(!r.endsWith(".md"))continue;const i=m(t,r);try{const s=le(i,b(i,"utf8"));s.plugin=e,n.push(s)}catch(s){f.warn("[plugin:%s] \u547D\u4EE4\u89E3\u6790\u5931\u8D25 %s: %s",e,r,s instanceof Error?s.message:String(s))}}}catch{}return n}async list(e){const t=await this.hydrate(e),n=new Map;for(const r of t.plugins.values()){const i=this.toSummary(r);n.set(i.name,i)}n.has(k)||n.set(k,{name:k,version:ee(),description:X,status:"enabled",enabled:!0,source:"builtin",installedAt:new Date().toISOString(),toolCount:1,tools:[{name:"plugin-manager.manage",description:"\u63D2\u4EF6\u751F\u547D\u5468\u671F\u7BA1\u7406\uFF08install/uninstall/reload/disable/enable/list\uFF09"}],listeners:[],builtin:!0});for(const r of this.readSharedManifest()){const i=r.pluginName;if(!this.deployedPluginHealthy(i))continue;const o=n.get(i);if(o){String(o.source||"").startsWith("shared:")&&(o.enabled=this.effectiveSharedEnabled(e,r));continue}n.set(i,{name:i,version:this.deployedPluginVersion(i),status:"enabled",enabled:this.effectiveSharedEnabled(e,r),source:`shared:${r.fileName??`${i}.zip`}`,installedAt:new Date().toISOString(),toolCount:0,listeners:[]})}return[...n.values()].sort((r,i)=>r.name.localeCompare(i.name))}deployedPluginVersion(e){try{const t=JSON.parse(b(m(this.deployedPluginDir(e),"plugin.json"),"utf8"));return typeof t.version=="string"?t.version:void 0}catch{return}}async getCatalog(e){const t=await this.hydrate(e),n=[];for(const r of t.plugins.values()){if(!r.enabled||r.status==="error")continue;const i=r.tools.slice(0,V).map(s=>({name:s.name,description:D(s.title||s.description,ne),requiredParams:this.requiredParamsOf(s)}));r.tools.length>V&&i.push({name:`(\u53E6\u6709 ${r.tools.length-V} \u4E2A\u5DE5\u5177\u672A\u5217\u51FA)`,description:'\u7528 function_call {action:"catalog"} \u67E5\u770B\u5168\u91CF',requiredParams:void 0}),n.push({plugin:r.name,version:r.version,description:r.description,status:r.status,tools:i})}return n.push({plugin:k,version:ee(),description:X,status:"enabled",tools:[{name:"manage",description:D(Q,ne),requiredParams:["action"]}]}),{revision:t.catalogRevision,plugins:n}}async getToolSchema(e,t){const n=ue(t);if(!n)return null;if(n.plugin===k&&n.tool==="manage")return{plugin:k,name:"manage",description:Q,inputSchema:ke};const i=(await this.hydrate(e)).plugins.get(n.plugin);if(!i||!i.enabled)return null;const s=i.tools.find(o=>o.name===n.tool);return s?{plugin:i.name,name:s.name,title:s.title,description:s.description,inputSchema:s.inputSchema??{type:"object",properties:{}}}:null}async invokeTool(e,t,n,r,i){if(t===k&&n==="manage"){const u=this.buildManageRequest(r);if(!u)return{ok:!1,error:"plugin-manager.manage \u7F3A\u5C11 action"};const c=await this.manage(e,u);if(!c.ok)return{ok:!1,error:c.error??"\u63D2\u4EF6\u7BA1\u7406\u64CD\u4F5C\u5931\u8D25"};const d={ok:!0};return c.plugins&&(d.plugins=c.plugins),c.summary&&(d.summary=c.summary),{ok:!0,result:d}}const s=await this.hydrate(e),o=s.plugins.get(t);if(!o)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\uFF08\u53EF\u7528\u63D2\u4EF6: ${[...s.plugins.keys()].join(", ")||"\u65E0"}\uFF09`};if(!o.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u5DF2\u505C\u7528\uFF0C\u53EF\u8BA9\u7528\u6237\u5728\u9762\u677F\u6216\u7528 plugin-manager.manage enable \u542F\u7528`};const a=o.tools.find(u=>u.name===n);if(!a)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5DE5\u5177 ${n}\uFF08\u53EF\u7528: ${o.tools.map(u=>u.name).join(", ")||"\u65E0"}\uFF09`};if(a.inputSchema){const u=G(a.inputSchema,r);if(u.length>0)return{ok:!1,error:`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${u.join("; ")}`,inputSchema:a.inputSchema}}return this.executeHandler(e,o,a,r,i)}buildManageRequest(e){const t=e.action;if(typeof t!="string"||!Pe.has(t))return null;const n={action:t};if(typeof e.name=="string"&&e.name.trim()&&(n.name=e.name.trim()),typeof e.source=="string"&&e.source.trim()&&(n.source=e.source.trim()),e.config&&typeof e.config=="object"&&!Array.isArray(e.config)){const r={};for(const[i,s]of Object.entries(e.config))typeof s=="string"&&(r[i]=s);n.config=r}return n}async executeHandler(e,t,n,r,i){const s=Date.now(),o=n.handler??{type:"process"};try{return o.type==="mcp"?{ok:!1,error:"handler.type=mcp \u5C06\u5728\u540E\u7EED\u7248\u672C\u652F\u6301\uFF0C\u8BF7\u6539\u7528 process/module handler"}:o.type==="module"?{ok:!0,result:await this.invokeModuleHandler(t,o,r,e,i),durationMs:Date.now()-s}:!o.command||!o.command.trim()?{ok:!1,error:`\u5DE5\u5177 ${n.name} \u7684 process handler \u7F3A\u5C11 command`}:{ok:!0,result:await this.invokeProcessHandler(t,o,r,e,i),durationMs:Date.now()-s}}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a),durationMs:Date.now()-s}}}async invokeProcessHandler(e,t,n,r,i){const s=K(t.command??"",{params:n});if(s.length===0)throw new Error("process handler command \u4E3A\u7A7A");const o=typeof t.timeoutMs=="number"&&t.timeoutMs>0?t.timeoutMs:te,a=new AbortController,u=()=>a.abort();i?.signal?.addEventListener("abort",u,{once:!0});const c=setTimeout(()=>a.abort(),o);try{return await new Promise((d,y)=>{f.info("[plugin:%s] spawn: %s",e.name,s.join(" "));const p=_(s[0],s.slice(1),{cwd:e.dir,env:this.buildPluginEnv(e,r),shell:t.shell===!0,windowsHide:!0,signal:a.signal});let g="",h="";p.stdin?.on("error",()=>{}),p.stdout?.on("data",$=>{g.length<M&&(g+=$.toString("utf8"))}),p.stderr?.on("data",$=>{h.length<M&&(h+=$.toString("utf8"))}),p.on("error",$=>{y(new Error(`\u63D2\u4EF6\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${$.message}`))}),p.on("close",$=>{const E=g.trim();if($!==0){const C=D(h.trim()||E||"(\u65E0\u8F93\u51FA)",600);y(new Error(a.signal.aborted?`\u63D2\u4EF6\u8FDB\u7A0B\u8D85\u65F6(${o}ms)\u6216\u88AB\u4E2D\u6B62`:`\u63D2\u4EF6\u8FDB\u7A0B\u9000\u51FA\u7801 ${$}: ${C}`));return}if(!E){d({ok:!0});return}try{d(JSON.parse(E))}catch{d(E)}});try{t.stdin==="params"?p.stdin?.end(`${JSON.stringify(n)}
|
|
7
|
-
`):p.stdin?.end()}catch{}})}finally{clearTimeout(c),i?.signal?.removeEventListener("abort",u)}}async invokeModuleHandler(e,t,n,r,i){if(!t.module)throw new Error("module handler \u7F3A\u5C11 module \u8DEF\u5F84");const s=m(e.dir,t.module);let o=e.moduleCache.get(s);o||(o=await import(`${ae(s).href}?acode_nonce=${e.importNonce}`),e.moduleCache.set(s,o));const a=t.export||"default",c=o[a];if(typeof c!="function")throw new Error(`\u6A21\u5757 ${t.module} \u672A\u5BFC\u51FA\u51FD\u6570 ${a}`);const d=typeof t.timeoutMs=="number"&&t.timeoutMs>0?t.timeoutMs:te;let y;const p=Promise.resolve().then(()=>c(n,{agentId:r,signal:i?.signal,pluginRoot:e.dir}));try{return await Promise.race([p,new Promise((g,h)=>{y=setTimeout(()=>h(new Error(`\u63D2\u4EF6\u6A21\u5757\u6267\u884C\u8D85\u65F6(${d}ms)`)),d)})])}finally{clearTimeout(y)}}buildPluginEnv(e,t){const n={};for(const i of["PATH","HOME","USERPROFILE","SystemRoot","COMSPEC","TEMP","TMP","LANG","TZ"]){const s=process.env[i];s!==void 0&&(n[i]=s)}n.ACODE_PLUGIN_ROOT=e.dir,n.ACODE_AGENT_ID=t,n.ACODE_PLUGIN_NAME=e.name;const r=new Set((e.userConfig??[]).map(i=>i.key));for(const[i,s]of Object.entries(e.configValues))r.has(i)&&(!Ce.test(i)||he.has(i.toUpperCase())||(n[i]=s));return n}async manage(e,t){const n=this.agentState(e),r=n.queue.then(async()=>{const i=await this.hydrate(e);return this.doManage(i,t)});return n.queue=r.catch(()=>{}),r}async doManage(e,t){if(t.name===k&&(t.action==="uninstall"||t.action==="disable"||t.action==="enable"||t.action==="reload"))return{ok:!1,error:`\u5185\u7F6E\u7CFB\u7EDF\u63D2\u4EF6 ${k} \u4E0D\u53EF${t.action==="uninstall"?"\u5378\u8F7D":t.action==="reload"?"\u91CD\u8F7D":"\u505C\u7528/\u542F\u7528"}`};switch(t.action){case"list":return{ok:!0,plugins:await this.list(e.agentId)};case"install":return this.installPlugin(e,t);case"uninstall":return this.uninstallPlugin(e,t);case"reload":return this.reloadPlugin(e,t);case"enable":case"disable":return this.setEnabled(e,t,t.action==="enable");default:return{ok:!1,error:`\u672A\u77E5 action: ${String(t.action)}`}}}async installPlugin(e,t){const n=(t.source??"").trim();if(!n)return{ok:!1,error:"install \u9700\u8981 source\uFF08\u5F53\u524D\u652F\u6301 local:<\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>\uFF09"};const r=n.split(":",1)[0];if(r!=="local")return{ok:!1,error:`\u6682\u4E0D\u652F\u6301\u63D2\u4EF6\u6765\u6E90 "${r}:"\uFF08npm:/git: \u5728\u8DEF\u7EBF\u56FE\u4E2D\uFF1Bzip \u5305\u8BF7\u7528\u9762\u677F\u5BFC\u5165\u6216 POST /runtime/plugins/:agentId/upload\uFF09`};const i=n.slice(6).trim();return!i||!w(m(i,"plugin.json"))?{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11 plugin.json: ${i}`}:this.installFromDirectory(e,i,n,t.config)}async installFromZip(e,t,n,r){if(!t||t.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};const i=this.agentState(e),s=i.queue.then(async()=>{await this.hydrate(e);const o=m(this.rootDir,"tmp",`upload-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const a=await U(t,o);return w(m(a.pluginDir,"plugin.json"))?(f.info("[agent:%s] zip \u5BFC\u5165: %s\uFF08%d \u4E2A\u6587\u4EF6\uFF09",e,n,a.fileCount),await this.installFromDirectory(i,a.pluginDir,`upload:${n}`,r)):{ok:!1,error:"zip \u5185\u672A\u627E\u5230 plugin.json\uFF08\u8BF7\u6253\u5305\u63D2\u4EF6\u76EE\u5F55\uFF0C\u4E14 zip \u5185\u8DEF\u5F84\u4F7F\u7528\u82F1\u6587\uFF09"}}catch(a){const u=a instanceof Error?a.message:String(a);return f.warn("[agent:%s] zip \u5BFC\u5165\u5931\u8D25: %s",e,u),{ok:!1,error:`zip \u5BFC\u5165\u5931\u8D25: ${u}`}}finally{await T(o,3,100).catch(()=>{})}});return i.queue=s.catch(()=>{}),s}async installFromDirectory(e,t,n,r){const i=this.loadManifestOnly(t);if(!i.ok)return i;const s=i.manifest,o=s.name;if(o===k)return{ok:!1,error:`\u63D2\u4EF6\u540D ${o} \u4E3A\u7CFB\u7EDF\u4FDD\u7559\u540D\u79F0\uFF0C\u4E0D\u5141\u8BB8\u5B89\u88C5\u540C\u540D\u63D2\u4EF6`};const a=this.validateUserConfigValues(s,r??{});if(a)return{ok:!1,error:a};const u=this.loadPluginFromDir(e,t,n,new Date().toISOString(),r??{});if(!u.ok)return f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u6821\u9A8C\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,u.error),u;const c=m(this.rootDir,"cache",I(e.agentId),o),d=t===c,y=e.plugins.get(o);if(y&&this.stopAllListeners(y),!d)try{await T(c),await re(t,c)}catch(h){const $=h instanceof Error?h.message:String(h);return f.warn("[agent:%s] \u63D2\u4EF6\u79FB\u52A8\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,$),y&&this.startAllListeners(e,y),{ok:!1,error:`\u63D2\u4EF6\u79FB\u52A8\u5931\u8D25: ${$}`}}const p=this.loadPluginFromDir(e,c,n,new Date().toISOString(),r??{});if(!p.ok){if(f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,p.error),!d)try{await re(c,t)}catch(h){f.warn("[agent:%s] \u88C5\u8F7D\u5931\u8D25\u540E\u65E0\u6CD5\u79FB\u56DE\u6E90\u76EE\u5F55\uFF08%s\uFF09\uFF0C\u5DF2\u4FDD\u7559\u7F13\u5B58\u526F\u672C",e.agentId,h instanceof Error?h.message:String(h))}if(y)try{this.startAllListeners(e,y)}catch(h){f.warn("[agent:%s] \u6062\u590D\u65E7\u5B9E\u4F8B\u76D1\u542C\u5931\u8D25: %s",e.agentId,h instanceof Error?h.message:String(h))}return p}const g=p.plugin;return e.plugins.set(g.name,g),this.bumpCatalogRevision(e),this.persistInstalled(e),this.startAllListeners(e,g),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5B89\u88C5: %s@%s",e.agentId,g.name,g.version??"?"),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(g)}}async uninstallPlugin(e,t){const n=(t.name??"").trim(),r=e.plugins.get(n);return r?(this.stopAllListeners(r),e.plugins.delete(n),e.pendingExposed.delete(n),e.exposedThisSession.has(n)&&this.pushDeprecatedNotice(e,n,"\u5DF2\u88AB\u5378\u8F7D"),this.bumpCatalogRevision(e),this.readSharedManifest().some(s=>s.pluginName===n)&&this.removeRuntimeLink(e.agentId,n),this.persistInstalled(e),await T(r.dir),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5378\u8F7D: %s",e.agentId,n),this.notifyPluginsChanged(e.agentId),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`}}async reloadPlugin(e,t){const n=(t.name??"").trim(),r=e.plugins.get(n);if(!r)return{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`};const i=this.loadPluginFromDir(e,r.dir,r.source,r.installedAt,r.configValues);if(!i.ok)return{ok:!1,error:`\u91CD\u8F7D\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u7248\u672C\u8FD0\u884C\uFF09: ${i.error}`};const s=i.plugin;return this.stopAllListeners(r),e.plugins.set(n,s),this.bumpCatalogRevision(e),this.persistInstalled(e),this.startAllListeners(e,s),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u91CD\u8F7D: %s",e.agentId,n),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(s)}}async setEnabled(e,t,n){const r=(t.name??"").trim(),i=e.plugins.get(r);return i?(i.enabled=n,this.bumpCatalogRevision(e),n?this.startAllListeners(e,i):(e.pendingExposed.delete(r),e.exposedThisSession.has(r)&&this.pushDeprecatedNotice(e,r,"\u5DF2\u505C\u7528"),this.stopAllListeners(i)),this.persistInstalled(e),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(i)}):{ok:!1,error:`\u63D2\u4EF6 ${r} \u672A\u5B89\u88C5`}}deployedDir(){return m(this.rootDir,"deployed")}deployedManifestFile(){return m(this.deployedDir(),"manifest.json")}deployedPluginDir(e){if(!B(e))throw new Error(`\u975E\u6CD5\u63D2\u4EF6\u540D\uFF08\u4EC5\u5141\u8BB8 ${H}\uFF09: ${JSON.stringify(e)}`);const t=m(this.deployedDir(),e),n=Y(this.deployedDir());if(Y(t)!==m(n,e))throw new Error(`\u63D2\u4EF6\u76EE\u5F55\u8D8A\u51FA\u90E8\u7F72\u533A: ${e}`);return t}legacySharedDir(){return m(this.rootDir,"shared")}readSharedManifest(){try{const e=JSON.parse(b(this.deployedManifestFile(),"utf8"));return Array.isArray(e.plugins)?e.plugins.filter(t=>t&&B(t.pluginName)):[]}catch{return[]}}writeSharedManifest(e){N(this.deployedDir(),{recursive:!0}),q(this.deployedManifestFile(),`${JSON.stringify({version:2,plugins:e},null,2)}
|
|
8
|
-
`,"utf8")}async migrateLegacySharedIfNeeded(){try{if(w(this.deployedManifestFile()))return;const e=m(this.legacySharedDir(),"manifest.json");if(!w(e))return;const t=JSON.parse(b(e,"utf8")),n=Array.isArray(t.plugins)?t.plugins:[];if(n.length===0)return;const r=[];for(const i of n){const s=m(this.legacySharedDir(),i.zipFile);if(!(!i.pluginName||!w(s)))try{const o=await this.deployPluginZipToPersistentDir(b(s),i.pluginName);if(!o)continue;r.push(o),f.info("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u5230\u90E8\u7F72\u533A: %s",i.pluginName)}catch(o){f.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6 %s \u8FC1\u79FB\u5931\u8D25: %s",i.pluginName,o instanceof Error?o.message:String(o))}}r.length>0&&this.writeSharedManifest(r),P(this.legacySharedDir(),{recursive:!0,force:!0})}catch(e){f.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u672A\u6267\u884C\uFF08\u4E0B\u6B21\u91CD\u8BD5\uFF09: %s",e instanceof Error?e.message:String(e))}}deployedPluginHealthy(e){return w(m(this.deployedPluginDir(e),"plugin.json"))}runtimeDirFor(e){return this.runtimeRootDir?m(this.runtimeRootDir,I(e)):ce(e)}runtimeLinkPath(e,t){return m(this.runtimeDirFor(e),t)}ensureRuntimeLink(e,t){const n=this.runtimeLinkPath(e,t),r=this.deployedPluginDir(t);if(!w(m(r,"plugin.json")))return!1;N(this.runtimeDirFor(e),{recursive:!0});try{if(w(n)){let i=!1;try{i=ie(n).isSymbolicLink()}catch{i=!1}if(i)try{return x(n),!0}catch{}P(n,{recursive:!0,force:!0})}return se(r,n,Le),f.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u5EFA\u7ACB: %s \u2192 %s",e,n,r),!0}catch(i){return f.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5931\u8D25\uFF08%s \u2192 %s\uFF09: %s",e,t,r,i instanceof Error?i.message:String(i)),!1}}removeRuntimeLink(e,t){const n=this.runtimeLinkPath(e,t);try{w(n)&&(P(n,{recursive:!0,force:!0}),f.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u79FB\u9664: %s",e,t))}catch(r){f.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u79FB\u9664\u5931\u8D25\uFF08%s\uFF09: %s",e,t,r instanceof Error?r.message:String(r))}}async saveSharedPlugin(e,t,n=!0,r){if(!e||e.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};await this.migrateLegacySharedIfNeeded();const i=m(this.rootDir,"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const s=await U(e,i),o=this.loadManifestOnly(s.pluginDir);if(!o.ok)return o;const a=o.manifest.name,u=this.readSharedManifest().find(g=>g.pluginName===a);if(u&&u.sha256===r&&this.deployedPluginHealthy(a))return{ok:!0,pluginName:a};await this.suspendDeployedPluginAcrossAgents(a),N(this.deployedDir(),{recursive:!0});const c=this.deployedPluginDir(a),d=m(this.deployedDir(),`.old-${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);if(w(c))try{j(c,d)}catch{P(c,{recursive:!0,force:!0})}try{F(s.pluginDir,c,{recursive:!0,filter:g=>!/[\\/]\.git([\\/]|$)/.test(g)&&!/[\\/]node_modules([\\/]|$)/.test(g)})}catch(g){throw w(d)&&(P(c,{recursive:!0,force:!0}),j(d,c)),g}w(d)&&P(d,{recursive:!0,force:!0}),await W(this.deployedManifestFile(),async()=>{const g=this.readSharedManifest().filter(h=>h.pluginName!==a);g.push({pluginName:a,zipFile:u?.zipFile??`${a}.zip`,fileName:t,defaultEnabled:n,sha256:r}),this.writeSharedManifest(g)}),f.info("[shared] \u63D2\u4EF6\u5DF2\u90E8\u7F72\u5230 bridge: %s\uFF08\u6765\u6E90 %s%s\uFF09",a,t,r?`, sha256=${r.slice(0,12)}\u2026`:"");const p=(await this.applySharedToKnownAgents()).find(g=>g.installed.includes(a));return p&&f.info("[shared] \u5DF2\u540C\u6B65\u5230\u8FD0\u884C\u4E2D agent: %s",p.agentId),{ok:!0,pluginName:a}}catch(s){const o=s instanceof Error?s.message:String(s);return f.warn("[shared] \u63D2\u4EF6\u90E8\u7F72\u5931\u8D25\uFF08%s\uFF09: %s",t,o),{ok:!1,error:`\u63D2\u4EF6\u90E8\u7F72\u5931\u8D25: ${o}`}}finally{await T(i,3,100).catch(()=>{})}}async deployPluginZipToPersistentDir(e,t){if(!e||e.length===0)return null;const n=m(this.rootDir,"tmp",`migrate-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const r=await U(e,n),i=this.loadManifestOnly(r.pluginDir);if(!i.ok)return null;const s=i.manifest.name;if(t&&s!==t)return null;N(this.deployedDir(),{recursive:!0});const o=this.deployedPluginDir(s);return w(o)&&P(o,{recursive:!0,force:!0}),F(r.pluginDir,o,{recursive:!0,filter:a=>!/[\\/]\.git([\\/]|$)/.test(a)&&!/[\\/]node_modules([\\/]|$)/.test(a)}),{pluginName:s,zipFile:`${s}.zip`,fileName:`${s}.zip`,defaultEnabled:!0}}finally{P(n,{recursive:!0,force:!0})}}async suspendDeployedPluginAcrossAgents(e){for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const n=t.queue.then(async()=>{const r=t.plugins.get(e);r&&(this.stopAllListeners(r),t.plugins.delete(e),this.bumpCatalogRevision(t),f.info("[agent:%s] \u5DF2\u6458\u9664\u63D2\u4EF6\u7B49\u5F85\u90E8\u7F72\u533A\u66F4\u65B0: %s",t.agentId,e))});t.queue=n.catch(()=>{}),await n}}async applySharedToKnownAgents(){const e=[];for(const t of this.agents.values()){if(!t.hydrated)continue;const n=await this.syncSharedIntoStateQueued(t);n.length>0&&e.push({agentId:t.agentId,installed:n})}return e}async syncSharedIntoStateQueued(e){const t=e.queue.then(()=>this.syncSharedIntoState(e));return e.queue=t.catch(()=>[]),t}effectiveSharedEnabled(e,t){return we(e,"plugins",t.pluginName)&&(t.defaultEnabled??!0)}async pullTemplatePluginsFromCapabilities(e){const t=ve(e.agentId);if(!t)return;const n=this.readSharedManifest();for(const[r,i]of Object.entries(t)){if(!i.url)continue;const s=n.find(a=>a.pluginName===r);if(!s||!s.sha256||s.sha256===i.sha256&&this.deployedPluginHealthy(r)||this.templatePullInFlight.has(r))continue;this.templatePullInFlight.add(r);const o=m(this.rootDir,"tmp",`pull-${I(e.agentId)}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{N(o,{recursive:!0});let a=`${r}.zip`;try{const y=decodeURIComponent(i.url.split("/").pop()||"");y.toLowerCase().endsWith(".zip")&&(a=pe(y,`${r}.zip`))}catch{}const u=await ge({downloadUrl:i.url,fileName:a,sha256:i.sha256},o);if(!u)continue;const c=b(u.outputPath),d=await this.saveSharedPlugin(c,u.fileName,s.defaultEnabled??!0,i.sha256);d.ok?f.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u81EA\u52A8\u5BF9\u8D26\u66F4\u65B0\u6210\u529F: %s",e.agentId,d.pluginName??r):f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: %s: %s",e.agentId,r,d.error)}catch(a){f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6 %s \u66F4\u65B0\u5931\u8D25: %s",e.agentId,r,a instanceof Error?a.message:String(a))}finally{this.templatePullInFlight.delete(r),await T(o,3,100).catch(()=>{})}}}queueTemplatePull(e){this.backgroundTemplatePulls.has(e.agentId)||(this.backgroundTemplatePulls.add(e.agentId),this.pullTemplatePluginsFromCapabilities(e).catch(t=>{f.warn("[agent:%s] \u540E\u53F0\u63D2\u4EF6\u5BF9\u8D26\u66F4\u65B0\u5931\u8D25: %s",e.agentId,t instanceof Error?t.message:String(t))}).finally(()=>{this.backgroundTemplatePulls.delete(e.agentId)}))}async syncSharedIntoState(e){this.queueTemplatePull(e);const t=this.readSharedManifest(),n=new Set,r=[];let i=0;for(const s of t)if(n.add(s.pluginName),this.effectiveSharedEnabled(e.agentId,s)){const a=e.plugins.get(s.pluginName);if(!this.ensureRuntimeLink(e.agentId,s.pluginName))continue;const u=this.runtimeLinkPath(e.agentId,s.pluginName);if(!a){const c=this.loadPluginFromDir(e,u,`shared:${s.fileName}`,new Date().toISOString(),{});c.ok?(e.plugins.set(c.plugin.name,c.plugin),r.push(c.plugin.name),f.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u5DF2\u6309\u5F00\u542F\u6001\u52A0\u8F7D: %s",e.agentId,s.pluginName)):f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: %s: %s",e.agentId,s.pluginName,c.error)}}else{const a=e.plugins.get(s.pluginName);a&&(this.stopAllListeners(a),e.plugins.delete(s.pluginName),i++),this.removeRuntimeLink(e.agentId,s.pluginName)}return this.cleanupRuntimeDir(e,n),(r.length>0||i>0)&&this.bumpCatalogRevision(e),this.persistInstalled(e),r}cleanupRuntimeDir(e,t){const n=this.runtimeDirFor(e.agentId);try{if(!w(n))return;const r=new Set(t);for(const i of x(n))!r.has(i)&&!e.plugins.has(i)&&this.removeRuntimeLink(e.agentId,i)}catch{}}async applyCapabilities(e){const t=await this.hydrate(e);await this.pullTemplatePluginsFromCapabilities(t),await this.syncSharedIntoState(t)}exposePlugin(e,t){if(!t)return;const n=this.agentState(e);n.pendingExposed.add(t),n.exposedThisSession.add(t),f.debug("[agent:%s] \u63D2\u4EF6\u5DF2\u89E6\u53D1\u5F85\u66B4\u9732: %s",e,t)}async drainEvents(e,t={}){const n=await this.hydrate(e);this.ensureListenersRunning(n);const r=n.buffer.drain(t),i=await this.consumeExposedCatalog(n);return{revision:r.revision,events:r.events,catalog:i,compactionSeq:n.compactionSeq}}async consumeExposedCatalog(e){if(e.pendingExposed.size===0)return null;const t=await this.getCatalog(e.agentId),n=[...e.pendingExposed];e.pendingExposed.clear();const r=t.plugins.filter(i=>n.includes(i.plugin));return r.length===0?null:{revision:t.revision,plugins:r}}pushDeprecatedNotice(e,t,n){e.buffer.push({plugin:t,event:"plugin-deprecated",priority:"urgent",receivedAt:Date.now(),message:{msgId:`plugin-deprecated-${t}-${Date.now()}`,channel:"plugin-event",senderId:`plugin:${t}`,senderName:`\u63D2\u4EF6[${t}]`,receiverId:e.agentId||void 0,content:`\u3010\u7CFB\u7EDF\u63D0\u793A\u3011\u63D2\u4EF6 ${t} ${n}\uFF0C\u5176\u63D0\u4F9B\u7684\u5DE5\u5177\u4E0D\u518D\u53EF\u8C03\u7528\u3002\u5982\u4ECD\u9700\u4F7F\u7528\u8BF7\u91CD\u65B0\u5B89\u88C5/\u542F\u7528\u8BE5\u63D2\u4EF6\u3002`,requireReply:!1,role:"system",timestamp:new Date().toISOString()}})}markAgentCompacted(e){const t=this.agentState(e);t.compactionSeq++,f.debug("[agent:%s] \u4E0A\u4E0B\u6587\u538B\u7F29\u6807\u8BB0: compactionSeq=%d",e,t.compactionSeq)}async enqueueExternalEvent(e,t,n){const r=await this.hydrate(e),i=r.plugins.get(t);if(!i||!i.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\u6216\u5DF2\u505C\u7528`};const s=typeof n.event=="string"?n.event:"",o=i.listeners.find(a=>a.transport.type==="http-callback"&&(!s||a.event===s));return o?(this.enqueueEvent(r,i,o,n),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5339\u914D\u7684 http-callback \u76D1\u542C\u5668\uFF08event=${s||"(\u7A7A)"}\uFF09`}}bumpCatalogRevision(e){e.catalogRevision++}agentState(e){let t=this.agents.get(e);return t||(t={agentId:e,plugins:new Map,buffer:new $e(e),hydrated:!1,queue:Promise.resolve(),catalogRevision:0,eventSeq:0,compactionSeq:0,pendingExposed:new Set,exposedThisSession:new Set},this.agents.set(e,t)),t}async hydrate(e){const t=this.agentState(e);return t.hydratePromise||(t.hydratePromise=this.doHydrate(t).catch(n=>{throw t.hydratePromise=void 0,n})),await t.hydratePromise,t}async doHydrate(e){const t=e.agentId;await this.migrateLegacySharedIfNeeded();const n=this.installedFile(t);if(!w(n)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const r=JSON.parse(b(n,"utf8")),i=this.readSecrets(t);for(const s of r.plugins??[]){if(String(s.source||"").startsWith("shared:"))continue;const a=m(this.rootDir,"cache",I(t),s.name);if(!w(m(a,"plugin.json"))){f.warn("[agent:%s] \u5DF2\u5B89\u88C5\u63D2\u4EF6\u76EE\u5F55\u7F3A\u5931\uFF0C\u8DF3\u8FC7: %s",t,s.name);continue}const u={...s.config,...i[s.name]??{}},c=this.loadPluginFromDir(e,a,s.source,s.installedAt,u);if(c.ok){const d=c.plugin;d.enabled=s.enabled,e.plugins.set(d.name,d)}else f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25: %s: %s",t,s.name,c.error)}}catch(r){const i=r instanceof Error?r.message:String(r);f.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,i)}await this.syncSharedIntoState(e).catch(r=>{f.warn("[agent:%s] \u5171\u4EAB\u63D2\u4EF6\u540C\u6B65\u5931\u8D25: %s",t,r instanceof Error?r.message:String(r))}),e.hydrated=!0}installedFile(e){return m(this.rootDir,"agents",I(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);N(J(t),{recursive:!0});const n=[...e.plugins.values()].map(r=>({name:r.name,source:r.source,installedAt:r.installedAt,enabled:r.enabled,config:this.stripSensitiveValues(r)}));q(t,`${JSON.stringify({version:1,plugins:n},null,2)}
|
|
9
|
-
`,"utf8")}sensitiveKeysOf(e){return(e.userConfig??[]).filter(t=>t?.sensitive===!0&&typeof t.key=="string").map(t=>t.key)}stripSensitiveValues(e){const t=new Set(this.sensitiveKeysOf(e)),n={};for(const[r,i]of Object.entries(e.configValues))t.has(r)||(n[r]=i);return n}secretsFile(e){return m(this.rootDir,"agents",I(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(b(this.secretsFile(e),"utf8"));if(!v(t)||!v(t.secrets))return{};const n={};for(const[r,i]of Object.entries(t.secrets)){if(!v(i))continue;const s={};for(const[o,a]of Object.entries(i))typeof a=="string"&&(s[o]=a);n[r]=s}return n}catch{return{}}}persistSecrets(e){const t={};for(const r of e.plugins.values()){const i={};for(const s of this.sensitiveKeysOf(r)){const o=r.configValues[s];typeof o=="string"&&o.length>0&&(i[s]=o)}Object.keys(i).length>0&&(t[r.name]=i)}const n=this.secretsFile(e.agentId);if(Object.keys(t).length===0){try{P(n,{force:!0})}catch{}return}N(J(n),{recursive:!0}),q(n,`${JSON.stringify({version:1,secrets:t},null,2)}
|
|
10
|
-
`,{encoding:"utf8",mode:384})}readJsonFile(e){if(!w(e))return{ok:!0,data:{}};try{const t=JSON.parse(b(e,"utf8"));return v(t)?{ok:!0,data:t}:{ok:!1,error:`${e}: \u6839\u8282\u70B9\u987B\u4E3A\u5BF9\u8C61`}}catch(t){const n=t instanceof Error?t.message:String(t);return{ok:!1,error:`${e}: JSON \u89E3\u6790\u5931\u8D25: ${n}`}}}loadManifestOnly(e){const t=this.readJsonFile(m(e,"plugin.json"));if(!t.ok)return t;const n=t.data,r=_e(n);return r?{ok:!1,error:r}:{ok:!0,manifest:n}}validateUserConfigValues(e,t){const n=Array.isArray(e.userConfig)?e.userConfig:[],r={...t};for(const i of n){if(!i||typeof i.key!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i.key))return"plugin.json: userConfig.key \u975E\u6CD5\uFF08\u987B\u4E3A\u73AF\u5883\u53D8\u91CF\u540D\uFF09";if(!(typeof r[i.key]=="string"&&r[i.key].length>0)&&i.required){const o=i.default!==void 0&&typeof i.default=="string"?i.default:"";if(!o)return`\u7F3A\u5C11\u5FC5\u9700\u914D\u7F6E ${i.key}${i.description?`\uFF08${i.description}\uFF09`:""}\uFF0C\u5B89\u88C5\u65F6\u8BF7\u5728 config \u4E2D\u63D0\u4F9B`;r[i.key]=o}}return null}loadPluginFromDir(e,t,n,r,i){const s=this.loadManifestOnly(t);if(!s.ok)return s;const o=s.manifest,a=m(t,o.tools??"tools/tools.json"),u=this.readJsonFile(a);if(!u.ok)return u;const c=[],d=Array.isArray(u.data.tools)?u.data.tools:[];for(const S of d){const L=Fe(S);if(L)return{ok:!1,error:`${o.tools??"tools/tools.json"}: ${L}`};c.push(S)}const y=m(t,o.listeners??"listeners/listeners.json"),p=this.readJsonFile(y);if(!p.ok)return p;const g=[],h=Array.isArray(p.data.listeners)?p.data.listeners:[];for(const S of h){const L=xe(S);if(L)return{ok:!1,error:`${o.listeners??"listeners/listeners.json"}: ${L}`};g.push(S)}const $=Array.isArray(o.userConfig)?o.userConfig:[],E=this.validateUserConfigValues(o,i);if(E)return{ok:!1,error:E};const C={...i};for(const S of $){if(!S||typeof S.key!="string")continue;!(typeof C[S.key]=="string"&&C[S.key].length>0)&&S.required&&S.default!==void 0&&typeof S.default=="string"&&(C[S.key]=S.default)}const R=o.commands;let Z;if(typeof R=="string"&&R.trim()){const S=m(t,R.trim());w(S)&&(Z=S)}return{ok:!0,plugin:{name:o.name,version:typeof o.version=="string"?o.version:void 0,description:typeof o.description=="string"?o.description:void 0,source:n,installedAt:r,dir:t,enabled:!0,tools:c,listeners:g,commandDir:Z,userConfig:$,configValues:C,status:"enabled",runtimes:g.map(S=>({def:S,stopping:!1,restarts:0,logs:[]})),moduleCache:new Map,importNonce:++this.moduleNonce}}}ensureListenersRunning(e){for(const t of e.plugins.values())if(t.enabled)for(const n of t.runtimes)!n.child&&!n.timer&&!n.backoffTimer&&!n.stopping&&this.startListener(e,t,n)}startAllListeners(e,t){for(const n of t.runtimes)this.stopListener(n),t.enabled&&this.startListener(e,t,n)}stopAllListeners(e){for(const t of e.runtimes)this.stopListener(t)}startListener(e,t,n){const r=n.def.transport??{type:"command-stream"};if(r.type==="http-callback"){n.stopping=!1;return}if(r.type==="poll-command"){const i=Math.max(5e3,r.intervalMs??3e4);n.stopping=!1,n.timer=setInterval(()=>{this.pollListenerOnce(e,t,n)},i),this.pollListenerOnce(e,t,n);return}if(!r.command){n.lastError="command-stream \u7F3A\u5C11 command",A(n,`ERROR ${n.lastError}`);return}n.stopping=!1,this.spawnListenerChild(e,t,n)}spawnListenerChild(e,t,n){const i=n.def.transport?.command??"",s=K(i,{});try{A(n,`spawn: ${s.join(" ")}`);const o=_(s[0],s.slice(1),{cwd:t.dir,env:this.buildPluginEnv(t,e.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});n.child=o,n.lastError=void 0;let a="";o.stdout?.on("data",u=>{a+=u.toString("utf8");let c=a.indexOf(`
|
|
11
|
-
`);for(;c>=0;){const d=a.slice(0,c).trim();a=a.slice(c+1),d&&this.handleListenerLine(e,t,n,d),c=a.indexOf(`
|
|
12
|
-
`)}a.length>1024*1024&&(a=a.slice(-4096))}),o.stderr?.on("data",u=>{A(n,`stderr: ${D(u.toString("utf8").trimEnd(),400)}`)}),o.on("error",u=>{n.lastError=`\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${u.message}`,A(n,`ERROR ${n.lastError}`)}),o.on("close",u=>{n.child=void 0,!n.stopping&&(n.lastError=`\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\uFF08code=${u??"signal"}\uFF09`,A(n,`exit code=${u??"signal"}\uFF0C\u51C6\u5907\u91CD\u542F`),this.scheduleListenerRestart(e,t,n))})}catch(o){const a=o instanceof Error?o.message:String(o);n.lastError=a,A(n,`ERROR spawn: ${a}`),this.scheduleListenerRestart(e,t,n)}}scheduleListenerRestart(e,t,n){if(n.stopping)return;const r=Math.max(500,n.def.transport?.restartBackoffMs??2e3),i=Math.min(Oe,r*2**Math.min(n.restarts,6));n.restarts++,A(n,`restart #${n.restarts} in ${i}ms`),n.backoffTimer=setTimeout(()=>{n.backoffTimer=void 0,n.stopping||this.spawnListenerChild(e,t,n)},i)}async pollListenerOnce(e,t,n){if(n.polling)return;const r=n.def.transport?.command??"",i=K(r,{});if(i.length===0)return;n.polling=!0;const s=new AbortController,o=setTimeout(()=>s.abort(),z);try{const a=await new Promise((u,c)=>{const d=_(i[0],i.slice(1),{cwd:t.dir,env:this.buildPluginEnv(t,e.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"],signal:s.signal});let y="",p="";d.stdout?.on("data",h=>{y.length<M&&(y+=h.toString("utf8"))}),d.stderr?.on("data",h=>{p.length<M&&(p+=h.toString("utf8"))});const g=()=>s.signal.aborted;d.on("error",h=>{c(g()?new Error(`poll \u8D85\u65F6(${z}ms)`):h)}),d.on("close",h=>{g()?c(new Error(`poll \u8D85\u65F6(${z}ms)`)):h===0?u(y):c(new Error(`exit ${h}: ${D(p||y,300)}`))})});n.lastError=void 0;for(const u of a.split(`
|
|
13
|
-
`)){const c=u.trim();c&&this.handleListenerLine(e,t,n,c)}}catch(a){const u=a instanceof Error?a.message:String(a);n.lastError=u,A(n,`poll \u5931\u8D25: ${u}`)}finally{clearTimeout(o),n.polling=!1}}stopListener(e){if(e.stopping=!0,e.restarts=0,e.child){try{e.child.kill()}catch{}e.child=void 0}e.timer&&(clearInterval(e.timer),e.timer=void 0),e.backoffTimer&&(clearTimeout(e.backoffTimer),e.backoffTimer=void 0)}handleListenerLine(e,t,n,r){A(n,`event: ${D(r,300)}`);let i;try{i=JSON.parse(r)}catch{n.lastError="\u4E8B\u4EF6\u884C\u4E0D\u662F\u5408\u6CD5 JSON\uFF08\u8981\u6C42 JSONL\uFF09";return}if(!v(i))return;const s=typeof i.event=="string"?i.event:n.def.event,o=s===n.def.event?n.def:t.listeners.find(a=>a.event===s);o&&(o.delivery&&o.delivery!=="poll"&&f.debug("[plugin:%s] delivery=%s \u6682\u6309 poll \u4EA4\u4ED8",t.name,o.delivery),this.enqueueEvent(e,t,o,i))}enqueueEvent(e,t,n,r){for(const[p,g]of Object.entries(n.matcher??{})){const h=O(r,p);if(!(Array.isArray(g)?g:[g]).some(E=>JSON.stringify(E)===JSON.stringify(h)))return}let i;if(n.dedupKey){const p=O(r,n.dedupKey);p!=null&&(i=D(String(p),256))}e.eventSeq++;const s=D(qe(r.data??r),De),o=n.promptTemplate?je(n.promptTemplate,r):`\u3010\u63D2\u4EF6\u4E8B\u4EF6\u3011${t.name} \xB7 ${n.event}${n.description?`
|
|
14
|
-
${n.description}`:""}`,a=(n.toolHints??[]).join(","),u=`${o}
|
|
15
|
-
[plugin-event] plugin=${t.name} event=${n.event}`+(a?` toolHints=${a}`:"")+` payload=${s}`,c={msgId:D(i?`plugin-event-${t.name}-${i}`:`plugin-event-${t.name}-${n.event}-${e.eventSeq}`,512),channel:"plugin-event",senderId:`plugin:${t.name}`,senderName:`\u63D2\u4EF6[${t.name}]`,receiverId:e.agentId||void 0,content:u,requireReply:!1,role:"system",timestamp:new Date().toISOString()},d=n.priority??"normal";e.buffer.push({plugin:t.name,event:n.event,priority:d,dedupKey:i,receivedAt:Date.now(),message:c})&&this.exposePlugin(e.agentId,t.name)}requiredParamsOf(e){const t=e.inputSchema;if(!(!t||!Array.isArray(t.required)))return t.required.filter(n=>typeof n=="string")}toSummary(e){return{name:e.name,version:e.version,description:e.description,status:e.status,enabled:e.enabled,source:e.source,installedAt:e.installedAt,toolCount:e.tools.length,tools:e.tools.map(t=>({name:t.name,title:t.title,description:t.description})),listeners:e.runtimes.map(t=>({event:t.def.event,delivery:t.def.delivery??"poll",running:!!(t.child||t.timer)||t.def.transport?.type==="http-callback",error:t.lastError})),running:e.runtimes.some(t=>!!(t.child||t.timer)||t.def.transport?.type==="http-callback"),error:e.error}}async getLogs(e,t){const n=await this.hydrate(e);if(t===k)return{ok:!0,logs:[`\u2500\u2500 ${k} \u4E3A\u7CFB\u7EDF\u5185\u7F6E\u63D2\u4EF6\uFF08\u65E0\u9700\u5B89\u88C5\uFF09\uFF0C\u65E0\u72EC\u7ACB\u76D1\u542C\u5668\u8FD0\u884C\u65E5\u5FD7\u3002`,"\u5176\u751F\u547D\u5468\u671F\u64CD\u4F5C\uFF08install/reload/disable/enable/list\uFF09\u7531 bridge plugin-manager \u5904\u7406\uFF0C\u53EF\u901A\u8FC7\u300C\u91CD\u8F7D/\u542F\u7528/\u505C\u7528\u300D\u6309\u94AE\u89C2\u5BDF\u72B6\u6001\u53D8\u5316\u3002"]};const r=n.plugins.get(t);if(!r)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5`,logs:[]};const i=[];for(const s of r.runtimes)i.push(`\u2500\u2500 listener ${s.def.event} (${s.def.transport?.type}) ${s.lastError?`[error: ${s.lastError}]`:""}`),i.push(...s.logs);return{ok:!0,logs:i}}async stopAgent(e){const t=this.agents.get(e);if(t){for(const n of t.plugins.values())this.stopAllListeners(n);this.agents.delete(e)}}async stopAll(){for(const e of[...this.agents.keys()])await this.stopAgent(e)}async listDeployed(){await this.migrateLegacySharedIfNeeded();const e=this.readSharedManifest(),t=[];for(const n of e){const r=this.deployedPluginDir(n.pluginName),i=this.deployedPluginHealthy(n.pluginName);let s;if(i)try{const o=JSON.parse(b(m(r,"plugin.json"),"utf8"));typeof o.version=="string"&&(s=o.version)}catch{}t.push({pluginName:n.pluginName,version:s,fileName:n.fileName??`${n.pluginName}.zip`,defaultEnabled:n.defaultEnabled!==!1,sha256:n.sha256,deployed:i})}return t}deployedDefaultEnabled(){const e=this.readSharedManifest(),t={};for(const n of e)t[n.pluginName]=n.defaultEnabled!==!1;return t}async removeDeployedPlugin(e){if(!B(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};await this.migrateLegacySharedIfNeeded();for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const n=t.queue.then(async()=>{const r=t.plugins.get(e);r&&(this.stopAllListeners(r),t.plugins.delete(e)),this.removeRuntimeLink(t.agentId,e),this.persistInstalled(t)});t.queue=n.catch(()=>{}),await n}await W(this.deployedManifestFile(),async()=>{const t=this.readSharedManifest().filter(n=>n.pluginName!==e);this.writeSharedManifest(t)});try{P(this.deployedPluginDir(e),{recursive:!0,force:!0})}catch(t){return{ok:!1,error:`\u90E8\u7F72\u76EE\u5F55\u6E05\u7406\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`}}return f.info("[shared] \u63D2\u4EF6\u5DF2\u4ECE bridge \u79FB\u9664\u90E8\u7F72: %s",e),{ok:!0}}}function B(l){return typeof l=="string"&&l.length>0&&l.length<=64&&H.test(l)}function _e(l){const e=l.name;if(typeof e!="string"||!H.test(e))return`plugin.json: name \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF09: ${String(e)}`;if(e.length>64)return"plugin.json: name \u8FC7\u957F\uFF08\u226464\uFF09";for(const t of Ne)if(e===t.replace(/\.$/,"")||e.startsWith(t))return`plugin.json: name \u4F7F\u7528\u4E86\u4FDD\u7559\u524D\u7F00 ${t}`;return Ie.test(e)?"plugin.json: name \u542B\u975E ASCII \u5B57\u7B26\uFF08\u540C\u5F62\u5B57\u9632\u62A4\uFF09":l.version!==void 0&&typeof l.version!="string"?"plugin.json: version \u987B\u4E3A\u5B57\u7B26\u4E32":l.userConfig!==void 0&&!Array.isArray(l.userConfig)?"plugin.json: userConfig \u987B\u4E3A\u6570\u7EC4":null}function Fe(l){if(!v(l))return"\u5DE5\u5177\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";const e=l.name;if(typeof e!="string"||!Ae.test(e))return`\u5DE5\u5177 name \u975E\u6CD5: ${String(e)}`;if(typeof l.description!="string"||!l.description.trim())return`\u5DE5\u5177 ${e} \u7F3A\u5C11 description`;const t=l.handler;if(!v(t))return`\u5DE5\u5177 ${e} \u7F3A\u5C11 handler`;const n=t.type;return n!=="process"&&n!=="module"&&n!=="mcp"?`\u5DE5\u5177 ${e} handler.type \u975E\u6CD5: ${String(n)}`:n==="process"&&typeof t.command!="string"?`\u5DE5\u5177 ${e} \u7684 process handler \u7F3A\u5C11 command`:n==="module"&&typeof t.module!="string"?`\u5DE5\u5177 ${e} \u7684 module handler \u7F3A\u5C11 module`:l.inputSchema!==void 0&&!v(l.inputSchema)?`\u5DE5\u5177 ${e} inputSchema \u987B\u4E3A\u5BF9\u8C61`:null}function xe(l){if(!v(l))return"\u76D1\u542C\u5668\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";if(typeof l.event!="string"||!l.event.trim())return"\u76D1\u542C\u5668\u7F3A\u5C11 event";const e=l.transport;if(!v(e))return`\u76D1\u542C\u5668 ${String(l.event)} \u7F3A\u5C11 transport`;const t=e.type;if(t!=="command-stream"&&t!=="poll-command"&&t!=="http-callback")return`\u76D1\u542C\u5668 ${String(l.event)} transport.type \u975E\u6CD5: ${String(t)}`;if((t==="command-stream"||t==="poll-command")&&typeof e.command!="string")return`\u76D1\u542C\u5668 ${String(l.event)} \u7684 ${t} transport \u7F3A\u5C11 command`;const n=l.delivery;return n!==void 0&&n!=="poll"&&n!=="force"&&n!=="wake"?`\u76D1\u542C\u5668 ${String(l.event)} delivery \u975E\u6CD5: ${String(n)}`:null}function K(l,e){return l.trim().split(/\s+/).map(t=>{const n=/^\{\{(.+)\}\}$/.exec(t);if(n){const r=O(e,n[1].trim());return r==null?"":typeof r=="string"?r:JSON.stringify(r)}return t.replace(/\{\{(.+?)\}\}/g,(r,i)=>{const s=O(e,i.trim());return s==null?"":typeof s=="string"?s:JSON.stringify(s)})}).filter(t=>t!=="")}function je(l,e){return l.replace(/\{\{(.+?)\}\}/g,(t,n)=>{let r=O(e,n.trim());return r===void 0&&!n.trim().startsWith("params.")&&(r=O(e,`data.${n.trim()}`)),r==null?"":typeof r=="string"?r:JSON.stringify(r)})}function qe(l){try{return JSON.stringify(l)??"null"}catch{return"(\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u4E8B\u4EF6\u8D1F\u8F7D)"}}const Je=new Re;ye("plugins",()=>Je.deployedDefaultEnabled());const nt=()=>Se("plugins");export{X as BUILTIN_PLUGIN_DESCRIPTION,Q as BUILTIN_PLUGIN_MANAGE_DESC,k as BUILTIN_PLUGIN_NAME,Re as PluginManager,be as getBuiltinCommandDir,B as isValidPluginName,Je as pluginManager,nt as reconcilePluginsDefaultsFromDeployed,K as renderCommandArgv,G as validateAgainstSchema,_e as validateManifest};
|
|
1
|
+
import{existsSync as y,mkdirSync as D,readFileSync as S,readdirSync as I,rmSync as C,writeFileSync as $}from"node:fs";import{dirname as A,join as h}from"node:path";import{parseAcodeCommandMarkdown as F}from"@agentswork/acode";import{getBridgeTemplatePluginsDir as L}from"../config.js";import{sanitizeAgentId as v,downloadPluginZip as O,sanitizeArchiveFileName as R}from"../utils/file-utils.js";import{createLogger as M}from"../utils/logger.js";import{setDeployedDefaultsProvider as j,reconcileDefaultsFromDeployed as T}from"./capability-config.js";import{executeHandler as x}from"./plugin-handler-adapter.js";import{isCapabilityEnabled as z,getAgentPluginSources as q}from"./instance-features.js";import{PluginEventBuffer as V}from"./plugin-event-buffer.js";import{ListenerRunner as H}from"./plugin-listener-runner.js";import{PluginLoader as J,toSummary as w}from"./plugin-loader.js";import{extractPluginZip as E}from"./plugin-package.js";import{isRecord as k,isValidPluginName as K,validateAgainstSchema as W}from"./plugin-manifest.js";import{movePluginDir as N,rmWithRetry as P}from"./plugin-fs.js";import{PluginDeploymentStore as U}from"./plugin-deployment.js";import{isValidPluginName as pe,validateManifest as me,validateAgainstSchema as ye,renderCommandArgv as Se}from"./plugin-manifest.js";const u=M("plugin-manager");class Z{constructor(e={}){this.agents=new Map,this.listeners=new H,this.loader=new J,this.templatePullInFlight=new Set,this.backgroundTemplatePulls=new Set,this.rootDir=e.rootDir??L(),this.runtimeRootDir=e.runtimeRootDir,this.deploy=new U(this.rootDir,this.runtimeRootDir,this.loader)}forAgent(e){return{listPlugins:()=>this.list(e),getPluginTools:t=>this.getPluginTools(e,t),invokeTool:(t,s,n,i)=>this.invokeTool(e,t,s,n,i),manage:t=>this.manage(e,t),getCommandDirs:()=>this.getCommandDirs(e)}}setOnPluginsChanged(e){this.onPluginsChanged=e}notifyPluginsChanged(e){try{this.onPluginsChanged?.(e)}catch(t){u.warn("[agent:%s] onPluginsChanged \u56DE\u8C03\u5931\u8D25: %s",e,t instanceof Error?t.message:String(t))}}async getCommandDirs(e){return[...(await this.hydrate(e)).plugins.values()].filter(s=>s.enabled&&s.commandDir).map(s=>s.commandDir)}async getPluginCommands(e){const t=await this.hydrate(e),s=[];for(const n of t.plugins.values())!n.enabled||!n.commandDir||s.push(...this.parsePluginCommandDir(n));return s}parsePluginCommandDir(e){return this.parseCommandDir(e.name,e.commandDir)}parseCommandDir(e,t){const s=[];if(!t)return s;try{for(const n of I(t)){if(!n.endsWith(".md"))continue;const i=h(t,n);try{const r=F(i,S(i,"utf8"));r.plugin=e,s.push(r)}catch(r){u.warn("[plugin:%s] \u547D\u4EE4\u89E3\u6790\u5931\u8D25 %s: %s",e,n,r instanceof Error?r.message:String(r))}}}catch{}return s}async list(e){const t=await this.hydrate(e),s=new Map;for(const n of t.plugins.values()){const i=w(n);s.set(i.name,i)}for(const n of this.deploy.readSharedManifest()){const i=n.pluginName;if(!this.deploy.deployedPluginHealthy(i))continue;const o=s.get(i);if(o){String(o.source||"").startsWith("shared:")&&(o.enabled=this.effectiveSharedEnabled(e,n));continue}s.set(i,{name:i,version:this.deployedPluginVersion(i),status:"enabled",enabled:this.effectiveSharedEnabled(e,n),source:`shared:${n.fileName??`${i}.zip`}`,installedAt:new Date().toISOString(),toolCount:0,listeners:[]})}return[...s.values()].sort((n,i)=>n.name.localeCompare(i.name))}deployedPluginVersion(e){try{const t=JSON.parse(S(h(this.deploy.deployedPluginDir(e),"plugin.json"),"utf8"));return typeof t.version=="string"?t.version:void 0}catch{return}}async getPluginTools(e,t){const n=(await this.hydrate(e)).plugins.get(t);return!n||!n.enabled?[]:n.tools.map(i=>({plugin:n.name,name:i.name,title:i.title,description:i.description,inputSchema:i.inputSchema??{type:"object",properties:{}}}))}async debugMountPlugin(e,t,s={}){const n=String(t??"").trim();if(!n)return{ok:!1,error:"\u63D2\u4EF6\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A"};if(!y(n))return{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728: ${n}`};const i=this.loader.loadManifestOnly(n);if(!i.ok)return i;const r=i.manifest.name,o=this.agentState(e),a=o.queue.then(async()=>{const l=await this.hydrate(e),c=l.plugins.get(r);c&&this.stopAllListeners(c);const g=this.loader.loadPluginFromDir(n,`debug:${n}`,new Date().toISOString(),s);if(!g.ok)return c&&this.startAllListeners(l,c),{ok:!1,error:`\u8C03\u8BD5\u6302\u8F7D\u5931\u8D25: ${g.error}`};const d=g.plugin;return d.debug=!0,l.plugins.set(d.name,d),this.startAllListeners(l,d),u.info("[agent:%s] \u63D2\u4EF6\u5DF2\u8C03\u8BD5\u6302\u8F7D: %s@%s\uFF08dir=%s\uFF09",l.agentId,d.name,d.version??"?",n),this.notifyPluginsChanged(l.agentId),{ok:!0,summary:w(d)}});return o.queue=a.catch(()=>{}),a}async listDebugPlugins(e){return[...(await this.hydrate(e)).plugins.values()].filter(s=>s.debug===!0).map(s=>w(s))}async debugPluginNames(e){return[...(await this.hydrate(e)).plugins.values()].filter(s=>s.debug===!0).map(s=>s.name)}async invokeTool(e,t,s,n,i){const r=await this.hydrate(e),o=r.plugins.get(t);if(!o)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\uFF08\u53EF\u7528\u63D2\u4EF6: ${[...r.plugins.keys()].join(", ")||"\u65E0"}\uFF09`};if(!o.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u5DF2\u505C\u7528\uFF0C\u8BF7\u8BA9\u7528\u6237\u5728\u9762\u677F\u542F\u7528`};const a=o.tools.find(l=>l.name===s);if(!a)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5DE5\u5177 ${s}\uFF08\u53EF\u7528: ${o.tools.map(l=>l.name).join(", ")||"\u65E0"}\uFF09`};if(a.inputSchema){const l=W(a.inputSchema,n);if(l.length>0)return{ok:!1,error:`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${l.join("; ")}`,inputSchema:a.inputSchema}}return x(e,o,a,n,i)}async manage(e,t){const s=this.agentState(e),n=s.queue.then(async()=>{const i=await this.hydrate(e);return this.doManage(i,t)});return s.queue=n.catch(()=>{}),n}async doManage(e,t){switch(t.action){case"list":return{ok:!0,plugins:await this.list(e.agentId)};case"install":return this.installPlugin(e,t);case"uninstall":return this.uninstallPlugin(e,t);case"reload":return this.reloadPlugin(e,t);case"enable":case"disable":return this.setEnabled(e,t,t.action==="enable");default:return{ok:!1,error:`\u672A\u77E5 action: ${String(t.action)}`}}}async installPlugin(e,t){const s=(t.source??"").trim();if(!s)return{ok:!1,error:"install \u9700\u8981 source\uFF08\u5F53\u524D\u652F\u6301 local:<\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>\uFF09"};const n=s.split(":",1)[0];if(n!=="local")return{ok:!1,error:`\u6682\u4E0D\u652F\u6301\u63D2\u4EF6\u6765\u6E90 "${n}:"\uFF08npm:/git: \u5728\u8DEF\u7EBF\u56FE\u4E2D\uFF1Bzip \u5305\u8BF7\u7528\u9762\u677F\u5BFC\u5165\u6216 POST /runtime/plugins/:agentId/upload\uFF09`};const i=s.slice(6).trim();return!i||!y(h(i,"plugin.json"))?{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11 plugin.json: ${i}`}:this.installFromDirectory(e,i,s,t.config)}async installFromZip(e,t,s,n){if(!t||t.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};const i=this.agentState(e),r=i.queue.then(async()=>{await this.hydrate(e);const o=h(this.rootDir,"tmp",`upload-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const a=await E(t,o);return y(h(a.pluginDir,"plugin.json"))?(u.info("[agent:%s] zip \u5BFC\u5165: %s\uFF08%d \u4E2A\u6587\u4EF6\uFF09",e,s,a.fileCount),await this.installFromDirectory(i,a.pluginDir,`upload:${s}`,n)):{ok:!1,error:"zip \u5185\u672A\u627E\u5230 plugin.json\uFF08\u8BF7\u6253\u5305\u63D2\u4EF6\u76EE\u5F55\uFF0C\u4E14 zip \u5185\u8DEF\u5F84\u4F7F\u7528\u82F1\u6587\uFF09"}}catch(a){const l=a instanceof Error?a.message:String(a);return u.warn("[agent:%s] zip \u5BFC\u5165\u5931\u8D25: %s",e,l),{ok:!1,error:`zip \u5BFC\u5165\u5931\u8D25: ${l}`}}finally{await P(o,3,100).catch(()=>{})}});return i.queue=r.catch(()=>{}),r}async installFromDirectory(e,t,s,n){const i=this.loader.loadManifestOnly(t);if(!i.ok)return i;const r=i.manifest,o=r.name,a=this.loader.validateUserConfigValues(r,n??{});if(a)return{ok:!1,error:a};const l=this.loader.loadPluginFromDir(t,s,new Date().toISOString(),n??{});if(!l.ok)return u.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u6821\u9A8C\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,l.error),l;const c=h(this.rootDir,"cache",v(e.agentId),o),g=t===c,d=e.plugins.get(o);if(d&&this.stopAllListeners(d),!g)try{await P(c),await N(t,c)}catch(f){const b=f instanceof Error?f.message:String(f);return u.warn("[agent:%s] \u63D2\u4EF6\u79FB\u52A8\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,b),d&&this.startAllListeners(e,d),{ok:!1,error:`\u63D2\u4EF6\u79FB\u52A8\u5931\u8D25: ${b}`}}const m=this.loader.loadPluginFromDir(c,s,new Date().toISOString(),n??{});if(!m.ok){if(u.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,m.error),!g)try{await N(c,t)}catch(f){u.warn("[agent:%s] \u88C5\u8F7D\u5931\u8D25\u540E\u65E0\u6CD5\u79FB\u56DE\u6E90\u76EE\u5F55\uFF08%s\uFF09\uFF0C\u5DF2\u4FDD\u7559\u7F13\u5B58\u526F\u672C",e.agentId,f instanceof Error?f.message:String(f))}if(d)try{this.startAllListeners(e,d)}catch(f){u.warn("[agent:%s] \u6062\u590D\u65E7\u5B9E\u4F8B\u76D1\u542C\u5931\u8D25: %s",e.agentId,f instanceof Error?f.message:String(f))}return m}const p=m.plugin;return e.plugins.set(p.name,p),this.persistInstalled(e),this.startAllListeners(e,p),u.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5B89\u88C5: %s@%s",e.agentId,p.name,p.version??"?"),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:w(p)}}async uninstallPlugin(e,t){const s=(t.name??"").trim(),n=e.plugins.get(s);return n?(this.stopAllListeners(n),e.plugins.delete(s),n.tools.length>0&&this.pushDeprecatedNotice(e,s,"\u5DF2\u88AB\u5378\u8F7D"),this.deploy.readSharedManifest().some(r=>r.pluginName===s)&&this.deploy.removeRuntimeLink(e.agentId,s),this.persistInstalled(e),n.debug!==!0&&await P(n.dir),u.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5378\u8F7D: %s",e.agentId,s),this.notifyPluginsChanged(e.agentId),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${s} \u672A\u5B89\u88C5`}}async reloadPlugin(e,t){const s=(t.name??"").trim(),n=e.plugins.get(s);if(!n)return{ok:!1,error:`\u63D2\u4EF6 ${s} \u672A\u5B89\u88C5`};const i=this.loader.loadPluginFromDir(n.dir,n.source,n.installedAt,n.configValues);if(!i.ok)return{ok:!1,error:`\u91CD\u8F7D\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u7248\u672C\u8FD0\u884C\uFF09: ${i.error}`};const r=i.plugin;return r.debug=n.debug,this.stopAllListeners(n),e.plugins.set(s,r),this.persistInstalled(e),this.startAllListeners(e,r),u.info("[agent:%s] \u63D2\u4EF6\u5DF2\u91CD\u8F7D: %s",e.agentId,s),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:w(r)}}async setEnabled(e,t,s){const n=(t.name??"").trim(),i=e.plugins.get(n);return i?(i.enabled=s,s?this.startAllListeners(e,i):(i.tools.length>0&&this.pushDeprecatedNotice(e,n,"\u5DF2\u505C\u7528"),this.stopAllListeners(i)),this.persistInstalled(e),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:w(i)}):{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`}}async saveSharedPlugin(e,t,s=!0,n){if(!e||e.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};await this.deploy.migrateLegacySharedIfNeeded();const i=h(this.rootDir,"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const r=await E(e,i),o=this.loader.loadManifestOnly(r.pluginDir);if(!o.ok)return o;const a=o.manifest.name,l=this.deploy.readSharedManifest().find(d=>d.pluginName===a);if(l&&l.sha256===n&&this.deploy.deployedPluginHealthy(a))return{ok:!0,pluginName:a};await this.suspendDeployedPluginAcrossAgents(a),this.deploy.replaceDeployedDir(a,r.pluginDir),await this.deploy.updateSharedManifest(d=>{const m=d.filter(p=>p.pluginName!==a);return m.push({pluginName:a,zipFile:l?.zipFile??`${a}.zip`,fileName:t,defaultEnabled:s,sha256:n}),m}),u.info("[shared] \u63D2\u4EF6\u5DF2\u90E8\u7F72\u5230 bridge: %s\uFF08\u6765\u6E90 %s%s\uFF09",a,t,n?`, sha256=${n.slice(0,12)}\u2026`:"");const g=(await this.applySharedToKnownAgents()).find(d=>d.installed.includes(a));return g&&u.info("[shared] \u5DF2\u540C\u6B65\u5230\u8FD0\u884C\u4E2D agent: %s",g.agentId),{ok:!0,pluginName:a}}catch(r){const o=r instanceof Error?r.message:String(r);return u.warn("[shared] \u63D2\u4EF6\u90E8\u7F72\u5931\u8D25\uFF08%s\uFF09: %s",t,o),{ok:!1,error:`\u63D2\u4EF6\u90E8\u7F72\u5931\u8D25: ${o}`}}finally{await P(i,3,100).catch(()=>{})}}async suspendDeployedPluginAcrossAgents(e){for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const s=t.queue.then(async()=>{const n=t.plugins.get(e);n&&(this.stopAllListeners(n),t.plugins.delete(e),u.info("[agent:%s] \u5DF2\u6458\u9664\u63D2\u4EF6\u7B49\u5F85\u90E8\u7F72\u533A\u66F4\u65B0: %s",t.agentId,e))});t.queue=s.catch(()=>{}),await s}}async applySharedToKnownAgents(){const e=[];for(const t of this.agents.values()){if(!t.hydrated)continue;const s=await this.syncSharedIntoStateQueued(t);s.length>0&&e.push({agentId:t.agentId,installed:s})}return e}async syncSharedIntoStateQueued(e){const t=e.queue.then(()=>this.syncSharedIntoState(e));return e.queue=t.catch(()=>[]),t}effectiveSharedEnabled(e,t){return z(e,"plugins",t.pluginName)&&(t.defaultEnabled??!0)}async pullTemplatePluginsFromCapabilities(e){const t=q(e.agentId);if(!t)return;const s=this.deploy.readSharedManifest();for(const[n,i]of Object.entries(t)){if(!i.url)continue;const r=s.find(a=>a.pluginName===n);if(!r||!r.sha256||r.sha256===i.sha256&&this.deploy.deployedPluginHealthy(n)||this.templatePullInFlight.has(n))continue;this.templatePullInFlight.add(n);const o=h(this.rootDir,"tmp",`pull-${v(e.agentId)}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{D(o,{recursive:!0});let a=`${n}.zip`;try{const d=decodeURIComponent(i.url.split("/").pop()||"");d.toLowerCase().endsWith(".zip")&&(a=R(d,`${n}.zip`))}catch{}const l=await O({downloadUrl:i.url,fileName:a,sha256:i.sha256},o);if(!l)continue;const c=S(l.outputPath),g=await this.saveSharedPlugin(c,l.fileName,r.defaultEnabled??!0,i.sha256);g.ok?u.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u81EA\u52A8\u5BF9\u8D26\u66F4\u65B0\u6210\u529F: %s",e.agentId,g.pluginName??n):u.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: %s: %s",e.agentId,n,g.error)}catch(a){u.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6 %s \u66F4\u65B0\u5931\u8D25: %s",e.agentId,n,a instanceof Error?a.message:String(a))}finally{this.templatePullInFlight.delete(n),await P(o,3,100).catch(()=>{})}}}queueTemplatePull(e){this.backgroundTemplatePulls.has(e.agentId)||(this.backgroundTemplatePulls.add(e.agentId),this.pullTemplatePluginsFromCapabilities(e).catch(t=>{u.warn("[agent:%s] \u540E\u53F0\u63D2\u4EF6\u5BF9\u8D26\u66F4\u65B0\u5931\u8D25: %s",e.agentId,t instanceof Error?t.message:String(t))}).finally(()=>{this.backgroundTemplatePulls.delete(e.agentId)}))}async syncSharedIntoState(e){this.queueTemplatePull(e);const t=this.deploy.readSharedManifest(),s=new Set,n=[];let i=0;for(const r of t)if(s.add(r.pluginName),this.effectiveSharedEnabled(e.agentId,r)){const a=e.plugins.get(r.pluginName);if(!this.deploy.ensureRuntimeLink(e.agentId,r.pluginName))continue;const l=this.deploy.runtimeLinkPath(e.agentId,r.pluginName);if(!a){const c=this.loader.loadPluginFromDir(l,`shared:${r.fileName}`,new Date().toISOString(),{});c.ok?(e.plugins.set(c.plugin.name,c.plugin),n.push(c.plugin.name),u.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u5DF2\u6309\u5F00\u542F\u6001\u52A0\u8F7D: %s",e.agentId,r.pluginName)):u.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: %s: %s",e.agentId,r.pluginName,c.error)}}else{const a=e.plugins.get(r.pluginName);a&&(this.stopAllListeners(a),e.plugins.delete(r.pluginName),i++),this.deploy.removeRuntimeLink(e.agentId,r.pluginName)}return this.cleanupRuntimeDir(e,s),this.persistInstalled(e),n}cleanupRuntimeDir(e,t){const s=this.deploy.runtimeDirFor(e.agentId);try{if(!y(s))return;const n=new Set(t);for(const i of I(s))!n.has(i)&&!e.plugins.has(i)&&this.deploy.removeRuntimeLink(e.agentId,i)}catch{}}async applyCapabilities(e){const t=await this.hydrate(e);await this.pullTemplatePluginsFromCapabilities(t),await this.syncSharedIntoState(t)}async drainEvents(e,t={}){const s=await this.hydrate(e);this.ensureListenersRunning(s);const n=s.buffer.drain(t);return{revision:n.revision,events:n.events}}pushDeprecatedNotice(e,t,s){e.buffer.push({plugin:t,event:"plugin-deprecated",priority:"urgent",receivedAt:Date.now(),message:{msgId:`plugin-deprecated-${t}-${Date.now()}`,channel:"plugin-event",senderId:`plugin:${t}`,senderName:`\u63D2\u4EF6[${t}]`,receiverId:e.agentId||void 0,content:`[plugin:${t}] plugin-deprecated
|
|
2
|
+
\u3010\u7CFB\u7EDF\u63D0\u793A\u3011\u63D2\u4EF6 ${t} ${s}\uFF0C\u5176\u63D0\u4F9B\u7684\u5DE5\u5177\u4E0D\u518D\u53EF\u8C03\u7528\uFF08\u5176 plugin:${t} \u6536\u7EB3\u7EC4\u5DF2\u5931\u6548\uFF09\u3002\u5982\u4ECD\u9700\u4F7F\u7528\u8BF7\u91CD\u65B0\u5B89\u88C5/\u542F\u7528\u8BE5\u63D2\u4EF6\u3002`,requireReply:!1,role:"system",timestamp:new Date().toISOString()}})}async enqueueExternalEvent(e,t,s){const n=await this.hydrate(e),i=n.plugins.get(t);if(!i||!i.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\u6216\u5DF2\u505C\u7528`};const r=typeof s.event=="string"?s.event:"",o=i.listeners.find(a=>a.transport.type==="http-callback"&&(!r||a.event===r));return o?(this.enqueueEvent(n,i,o,s),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5339\u914D\u7684 http-callback \u76D1\u542C\u5668\uFF08event=${r||"(\u7A7A)"}\uFF09`}}agentState(e){let t=this.agents.get(e);return t||(t={agentId:e,plugins:new Map,buffer:new V(e),hydrated:!1,queue:Promise.resolve(),eventSeq:0},this.agents.set(e,t)),t}async hydrate(e){const t=this.agentState(e);return t.hydratePromise||(t.hydratePromise=this.doHydrate(t).catch(s=>{throw t.hydratePromise=void 0,s})),await t.hydratePromise,t}async doHydrate(e){const t=e.agentId;await this.deploy.migrateLegacySharedIfNeeded();const s=this.installedFile(t);if(!y(s)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const n=JSON.parse(S(s,"utf8")),i=this.readSecrets(t);for(const r of n.plugins??[]){if(String(r.source||"").startsWith("shared:"))continue;const a=h(this.rootDir,"cache",v(t),r.name);if(!y(h(a,"plugin.json"))){u.warn("[agent:%s] \u5DF2\u5B89\u88C5\u63D2\u4EF6\u76EE\u5F55\u7F3A\u5931\uFF0C\u8DF3\u8FC7: %s",t,r.name);continue}const l={...r.config,...i[r.name]??{}},c=this.loader.loadPluginFromDir(a,r.source,r.installedAt,l);if(c.ok){const g=c.plugin;g.enabled=r.enabled,e.plugins.set(g.name,g)}else u.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25: %s: %s",t,r.name,c.error)}}catch(n){const i=n instanceof Error?n.message:String(n);u.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,i)}await this.syncSharedIntoState(e).catch(n=>{u.warn("[agent:%s] \u5171\u4EAB\u63D2\u4EF6\u540C\u6B65\u5931\u8D25: %s",t,n instanceof Error?n.message:String(n))}),e.hydrated=!0}installedFile(e){return h(this.rootDir,"agents",v(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);D(A(t),{recursive:!0});const s=[...e.plugins.values()].filter(n=>n.debug!==!0).map(n=>({name:n.name,source:n.source,installedAt:n.installedAt,enabled:n.enabled,config:this.stripSensitiveValues(n)}));$(t,`${JSON.stringify({version:1,plugins:s},null,2)}
|
|
3
|
+
`,"utf8")}sensitiveKeysOf(e){return(e.userConfig??[]).filter(t=>t?.sensitive===!0&&typeof t.key=="string").map(t=>t.key)}stripSensitiveValues(e){const t=new Set(this.sensitiveKeysOf(e)),s={};for(const[n,i]of Object.entries(e.configValues))t.has(n)||(s[n]=i);return s}secretsFile(e){return h(this.rootDir,"agents",v(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(S(this.secretsFile(e),"utf8"));if(!k(t)||!k(t.secrets))return{};const s={};for(const[n,i]of Object.entries(t.secrets)){if(!k(i))continue;const r={};for(const[o,a]of Object.entries(i))typeof a=="string"&&(r[o]=a);s[n]=r}return s}catch{return{}}}persistSecrets(e){const t={};for(const n of e.plugins.values()){if(n.debug===!0)continue;const i={};for(const r of this.sensitiveKeysOf(n)){const o=n.configValues[r];typeof o=="string"&&o.length>0&&(i[r]=o)}Object.keys(i).length>0&&(t[n.name]=i)}const s=this.secretsFile(e.agentId);if(Object.keys(t).length===0){try{C(s,{force:!0})}catch{}return}D(A(s),{recursive:!0}),$(s,`${JSON.stringify({version:1,secrets:t},null,2)}
|
|
4
|
+
`,{encoding:"utf8",mode:384})}ensureListenersRunning(e){this.listeners.ensureRunning(e)}startAllListeners(e,t){this.listeners.startAll(e,t)}stopAllListeners(e){this.listeners.stopAll(e)}enqueueEvent(e,t,s,n){this.listeners.enqueue(e,t,s,n)}async getLogs(e,t){const n=(await this.hydrate(e)).plugins.get(t);if(!n)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5`,logs:[]};const i=[];for(const r of n.runtimes)i.push(`\u2500\u2500 listener ${r.def.event} (${r.def.transport?.type}) ${r.lastError?`[error: ${r.lastError}]`:""}`),i.push(...r.logs);return{ok:!0,logs:i}}async stopAgent(e){const t=this.agents.get(e);if(t){for(const s of t.plugins.values())this.stopAllListeners(s);this.agents.delete(e)}}async stopAll(){for(const e of[...this.agents.keys()])await this.stopAgent(e)}async listDeployed(){await this.deploy.migrateLegacySharedIfNeeded();const e=this.deploy.readSharedManifest(),t=[];for(const s of e){const n=this.deploy.deployedPluginDir(s.pluginName),i=this.deploy.deployedPluginHealthy(s.pluginName);let r;if(i)try{const o=JSON.parse(S(h(n,"plugin.json"),"utf8"));typeof o.version=="string"&&(r=o.version)}catch{}t.push({pluginName:s.pluginName,version:r,fileName:s.fileName??`${s.pluginName}.zip`,defaultEnabled:s.defaultEnabled!==!1,sha256:s.sha256,deployed:i})}return t}deployedDefaultEnabled(){const e=this.deploy.readSharedManifest(),t={};for(const s of e)t[s.pluginName]=s.defaultEnabled!==!1;return t}async removeDeployedPlugin(e){if(!K(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};await this.deploy.migrateLegacySharedIfNeeded();for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const s=t.queue.then(async()=>{const n=t.plugins.get(e);n&&(this.stopAllListeners(n),t.plugins.delete(e)),this.deploy.removeRuntimeLink(t.agentId,e),this.persistInstalled(t)});t.queue=s.catch(()=>{}),await s}await this.deploy.updateSharedManifest(t=>t.filter(s=>s.pluginName!==e));try{this.deploy.removeDeployedDir(e)}catch(t){return{ok:!1,error:`\u90E8\u7F72\u76EE\u5F55\u6E05\u7406\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`}}return u.info("[shared] \u63D2\u4EF6\u5DF2\u4ECE bridge \u79FB\u9664\u90E8\u7F72: %s",e),{ok:!0}}}const Q=new Z;j("plugins",()=>Q.deployedDefaultEnabled());const ge=()=>T("plugins");export{Z as PluginManager,pe as isValidPluginName,Q as pluginManager,ge as reconcilePluginsDefaultsFromDeployed,Se as renderCommandArgv,ye as validateAgainstSchema,me as validateManifest};
|
|
16
5
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
import type { PluginEventPriority } from "./plugin-event-buffer.js";
|
|
3
|
+
export interface PluginUserConfigField {
|
|
4
|
+
key: string;
|
|
5
|
+
type?: "string" | "number" | "boolean";
|
|
6
|
+
required?: boolean;
|
|
7
|
+
sensitive?: boolean;
|
|
8
|
+
description?: string;
|
|
9
|
+
default?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PluginHandlerDef {
|
|
12
|
+
type: "process" | "module" | "mcp";
|
|
13
|
+
|
|
14
|
+
command?: string;
|
|
15
|
+
|
|
16
|
+
shell?: boolean;
|
|
17
|
+
|
|
18
|
+
stdin?: "none" | "params";
|
|
19
|
+
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
|
|
22
|
+
module?: string;
|
|
23
|
+
|
|
24
|
+
export?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PluginToolDef {
|
|
27
|
+
name: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
description: string;
|
|
30
|
+
inputSchema?: Record<string, unknown>;
|
|
31
|
+
handler: PluginHandlerDef;
|
|
32
|
+
}
|
|
33
|
+
export interface PluginTransportDef {
|
|
34
|
+
type: "command-stream" | "poll-command" | "http-callback";
|
|
35
|
+
|
|
36
|
+
command?: string;
|
|
37
|
+
|
|
38
|
+
intervalMs?: number;
|
|
39
|
+
|
|
40
|
+
restartBackoffMs?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface PluginListenerDef {
|
|
43
|
+
event: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
transport: PluginTransportDef;
|
|
46
|
+
|
|
47
|
+
delivery?: "poll" | "force" | "wake";
|
|
48
|
+
priority?: PluginEventPriority;
|
|
49
|
+
|
|
50
|
+
matcher?: Record<string, string | number | boolean | Array<string | number | boolean>>;
|
|
51
|
+
|
|
52
|
+
dedupKey?: string;
|
|
53
|
+
|
|
54
|
+
toolHints?: string[];
|
|
55
|
+
|
|
56
|
+
summary?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface PluginManifest {
|
|
59
|
+
name: string;
|
|
60
|
+
version?: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
author?: Record<string, unknown>;
|
|
63
|
+
tools?: string;
|
|
64
|
+
listeners?: string;
|
|
65
|
+
commands?: string;
|
|
66
|
+
userConfig?: PluginUserConfigField[];
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}
|
|
69
|
+
export declare const PLUGIN_NAME_PATTERN: RegExp;
|
|
70
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
71
|
+
|
|
72
|
+
export declare function getPath(obj: unknown, path: string): unknown;
|
|
73
|
+
export declare function truncate(text: string, max: number): string;
|
|
74
|
+
export declare function validateAgainstSchema(schema: unknown, value: unknown, path?: string): string[];
|
|
75
|
+
|
|
76
|
+
export declare function isValidPluginName(name: unknown): name is string;
|
|
77
|
+
export declare function validateManifest(manifest: Record<string, unknown>): string | null;
|
|
78
|
+
export declare function validateToolDef(item: unknown): string | null;
|
|
79
|
+
export declare function validateListenerDef(item: unknown): string | null;
|
|
80
|
+
|
|
81
|
+
export declare function renderCommandArgv(command: string, lookup: Record<string, unknown>): string[];
|
|
82
|
+
export declare function renderTemplate(template: string, eventObj: Record<string, unknown>): string;
|
|
83
|
+
export declare function safeStableStringify(value: unknown): string;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const l=/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/,c=/^[a-z0-9][a-z0-9-]*$/,y=["system.","aws-","acode-"],g=/[^\x20-\x7E]/;function f(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function p(r,n){let t=r;for(const e of n.split(".")){if(!f(t))return;t=t[e]}return t}function S(r,n){return r.length<=n?r:`${r.slice(0,n)}\u2026(\u622A\u65AD,\u5171 ${r.length} \u5B57\u7B26)`}function a(r,n,t="params"){if(!f(r))return[];const e=[],i=r.type;if(typeof i=="string"&&!$(i,n))return[`${t}: \u671F\u671B ${i}\uFF0C\u5B9E\u9645 ${m(n)}`];if(Array.isArray(r.enum)&&r.enum.length>0&&(r.enum.some(o=>JSON.stringify(o)===JSON.stringify(n))||e.push(`${t}: \u503C ${JSON.stringify(n)} \u4E0D\u5728\u679A\u4E3E ${JSON.stringify(r.enum)} \u5185`)),i==="object"&&f(n)){const u=f(r.properties)?r.properties:{},o=Array.isArray(r.required)?r.required:[];for(const s of o)(!(s in n)||n[s]===void 0)&&e.push(`${t}.${s}: \u5FC5\u586B\u53C2\u6570\u7F3A\u5931`);for(const[s,d]of Object.entries(u))s in n&&n[s]!==void 0&&e.push(...a(d,n[s],`${t}.${s}`));if(r.additionalProperties===!1)for(const s of Object.keys(n))s in u||e.push(`${t}.${s}: \u4E0D\u5141\u8BB8\u7684\u672A\u77E5\u53C2\u6570\uFF08schema additionalProperties=false\uFF09`)}return i==="array"&&Array.isArray(n)&&f(r.items)&&n.forEach((u,o)=>{e.push(...a(r.items,u,`${t}[${o}]`))}),e}function $(r,n){switch(r){case"string":return typeof n=="string";case"number":return typeof n=="number"&&Number.isFinite(n);case"boolean":return typeof n=="boolean";case"object":return f(n);case"array":return Array.isArray(n);case"null":return n===null;default:return!0}}function m(r){return r===null?"null":Array.isArray(r)?"array":typeof r}function A(r){return typeof r=="string"&&r.length>0&&r.length<=64&&c.test(r)}function N(r){const n=r.name;if(typeof n!="string"||!c.test(n))return`plugin.json: name \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF09: ${String(n)}`;if(n.length>64)return"plugin.json: name \u8FC7\u957F\uFF08\u226464\uFF09";for(const t of y)if(n===t.replace(/\.$/,"")||n.startsWith(t))return`plugin.json: name \u4F7F\u7528\u4E86\u4FDD\u7559\u524D\u7F00 ${t}`;return g.test(n)?"plugin.json: name \u542B\u975E ASCII \u5B57\u7B26\uFF08\u540C\u5F62\u5B57\u9632\u62A4\uFF09":r.version!==void 0&&typeof r.version!="string"?"plugin.json: version \u987B\u4E3A\u5B57\u7B26\u4E32":r.userConfig!==void 0&&!Array.isArray(r.userConfig)?"plugin.json: userConfig \u987B\u4E3A\u6570\u7EC4":null}function h(r){if(!f(r))return"\u5DE5\u5177\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";const n=r.name;if(typeof n!="string"||!l.test(n))return`\u5DE5\u5177 name \u975E\u6CD5: ${String(n)}`;if(typeof r.description!="string"||!r.description.trim())return`\u5DE5\u5177 ${n} \u7F3A\u5C11 description`;const t=r.handler;if(!f(t))return`\u5DE5\u5177 ${n} \u7F3A\u5C11 handler`;const e=t.type;return e!=="process"&&e!=="module"&&e!=="mcp"?`\u5DE5\u5177 ${n} handler.type \u975E\u6CD5: ${String(e)}`:e==="process"&&typeof t.command!="string"?`\u5DE5\u5177 ${n} \u7684 process handler \u7F3A\u5C11 command`:e==="module"&&typeof t.module!="string"?`\u5DE5\u5177 ${n} \u7684 module handler \u7F3A\u5C11 module`:r.inputSchema!==void 0&&!f(r.inputSchema)?`\u5DE5\u5177 ${n} inputSchema \u987B\u4E3A\u5BF9\u8C61`:null}function b(r){if(!f(r))return"\u76D1\u542C\u5668\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";if(typeof r.event!="string"||!r.event.trim())return"\u76D1\u542C\u5668\u7F3A\u5C11 event";const n=r.transport;if(!f(n))return`\u76D1\u542C\u5668 ${String(r.event)} \u7F3A\u5C11 transport`;const t=n.type;if(t!=="command-stream"&&t!=="poll-command"&&t!=="http-callback")return`\u76D1\u542C\u5668 ${String(r.event)} transport.type \u975E\u6CD5: ${String(t)}`;if((t==="command-stream"||t==="poll-command")&&typeof n.command!="string")return`\u76D1\u542C\u5668 ${String(r.event)} \u7684 ${t} transport \u7F3A\u5C11 command`;const e=r.delivery;return e!==void 0&&e!=="poll"&&e!=="force"&&e!=="wake"?`\u76D1\u542C\u5668 ${String(r.event)} delivery \u975E\u6CD5: ${String(e)}`:null}function O(r,n){return r.trim().split(/\s+/).map(t=>{const e=/^\{\{(.+)\}\}$/.exec(t);if(e){const i=p(n,e[1].trim());return i==null?"":typeof i=="string"?i:JSON.stringify(i)}return t.replace(/\{\{(.+?)\}\}/g,(i,u)=>{const o=p(n,u.trim());return o==null?"":typeof o=="string"?o:JSON.stringify(o)})}).filter(t=>t!=="")}function E(r,n){return r.replace(/\{\{(.+?)\}\}/g,(t,e)=>{let i=p(n,e.trim());return i===void 0&&!e.trim().startsWith("params.")&&(i=p(n,`data.${e.trim()}`)),i==null?"":typeof i=="string"?i:JSON.stringify(i)})}function P(r){try{return JSON.stringify(r)??"null"}catch{return"(\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u4E8B\u4EF6\u8D1F\u8F7D)"}}export{c as PLUGIN_NAME_PATTERN,p as getPath,f as isRecord,A as isValidPluginName,O as renderCommandArgv,E as renderTemplate,P as safeStableStringify,S as truncate,a as validateAgainstSchema,b as validateListenerDef,N as validateManifest,h as validateToolDef};
|
|
2
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
import type { ChildProcess } from "node:child_process";
|
|
3
|
+
import type { PluginStatus } from "@agentswork/acode";
|
|
4
|
+
import type { PluginEventBuffer } from "./plugin-event-buffer.js";
|
|
5
|
+
import type { PluginListenerDef, PluginToolDef, PluginUserConfigField } from "./plugin-manifest.js";
|
|
6
|
+
export interface ListenerRuntime {
|
|
7
|
+
def: PluginListenerDef;
|
|
8
|
+
child?: ChildProcess;
|
|
9
|
+
timer?: ReturnType<typeof setInterval>;
|
|
10
|
+
backoffTimer?: ReturnType<typeof setTimeout>;
|
|
11
|
+
stopping: boolean;
|
|
12
|
+
restarts: number;
|
|
13
|
+
lastError?: string;
|
|
14
|
+
logs: string[];
|
|
15
|
+
|
|
16
|
+
polling?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface LoadedPlugin {
|
|
19
|
+
name: string;
|
|
20
|
+
version?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
source: string;
|
|
23
|
+
installedAt: string;
|
|
24
|
+
dir: string;
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
tools: PluginToolDef[];
|
|
27
|
+
listeners: PluginListenerDef[];
|
|
28
|
+
commandDir?: string;
|
|
29
|
+
userConfig: PluginUserConfigField[];
|
|
30
|
+
configValues: Record<string, string>;
|
|
31
|
+
status: PluginStatus;
|
|
32
|
+
error?: string;
|
|
33
|
+
runtimes: ListenerRuntime[];
|
|
34
|
+
moduleCache: Map<string, unknown>;
|
|
35
|
+
|
|
36
|
+
debug?: boolean;
|
|
37
|
+
|
|
38
|
+
importNonce: number;
|
|
39
|
+
}
|
|
40
|
+
export interface InstalledRecord {
|
|
41
|
+
name: string;
|
|
42
|
+
source: string;
|
|
43
|
+
installedAt: string;
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
config: Record<string, string>;
|
|
46
|
+
}
|
|
47
|
+
export interface AgentState {
|
|
48
|
+
agentId: string;
|
|
49
|
+
plugins: Map<string, LoadedPlugin>;
|
|
50
|
+
buffer: PluginEventBuffer;
|
|
51
|
+
hydrated: boolean;
|
|
52
|
+
|
|
53
|
+
queue: Promise<unknown>;
|
|
54
|
+
|
|
55
|
+
hydratePromise?: Promise<void>;
|
|
56
|
+
eventSeq: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface SharedPluginEntry {
|
|
60
|
+
pluginName: string;
|
|
61
|
+
zipFile: string;
|
|
62
|
+
fileName: string;
|
|
63
|
+
|
|
64
|
+
defaultEnabled?: boolean;
|
|
65
|
+
|
|
66
|
+
sha256?: string;
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -5,6 +5,8 @@ export interface DeployedSkillEntry {
|
|
|
5
5
|
fileName: string;
|
|
6
6
|
defaultEnabled: boolean;
|
|
7
7
|
sha256?: string;
|
|
8
|
+
|
|
9
|
+
deployed?: boolean;
|
|
8
10
|
}
|
|
9
11
|
export interface DeployedSkillResult {
|
|
10
12
|
ok: boolean;
|
|
@@ -32,6 +34,19 @@ export declare class SkillManager {
|
|
|
32
34
|
}>;
|
|
33
35
|
|
|
34
36
|
listDeployed(): DeployedSkillEntry[];
|
|
37
|
+
private privateManifestFile;
|
|
38
|
+
private privateSkillDir;
|
|
39
|
+
private readPrivateManifest;
|
|
40
|
+
private writePrivateManifest;
|
|
41
|
+
|
|
42
|
+
savePrivateSkill(agentId: string, zipBuffer: Buffer, originalName: string, sha256?: string, requestedSkillName?: string): Promise<DeployedSkillResult>;
|
|
43
|
+
|
|
44
|
+
removePrivateSkill(agentId: string, skillName: string): Promise<{
|
|
45
|
+
ok: boolean;
|
|
46
|
+
error?: string;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
listPrivateSkills(agentId: string): DeployedSkillEntry[];
|
|
35
50
|
|
|
36
51
|
deployedDefaultEnabled(): Record<string, boolean>;
|
|
37
52
|
|