aws-runtime-bridge 1.9.186 → 1.9.192
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 +1878 -1673
- package/README.md +256 -256
- package/dist/adapter/AcodeSdkAdapter.d.ts +13 -0
- package/dist/adapter/AcodeSdkAdapter.js +3 -3
- package/dist/browser/browser-pool.d.ts +2 -0
- package/dist/browser/browser-pool.js +1 -1
- package/dist/config.d.ts +12 -31
- package/dist/config.js +2 -2
- package/dist/index.js +1 -1
- package/dist/remote-desktop/win32-api.js +1 -1
- package/dist/routes/background-tasks.js +4 -4
- package/dist/routes/dashboard.js +17 -17
- package/dist/routes/media-files.d.ts +19 -0
- package/dist/routes/media-files.js +2 -0
- package/dist/routes/plugin-ui.d.ts +17 -0
- package/dist/routes/plugin-ui.js +2 -0
- package/dist/routes/plugins.d.ts +3 -0
- package/dist/routes/plugins.js +1 -1
- package/dist/routes/terminal.d.ts +83 -0
- package/dist/routes/terminal.js +23 -23
- package/dist/scripts/migrate-user-scopes.js +1 -1
- package/dist/services/acode-bg-manager.js +1 -1
- package/dist/services/agent-capability-sync.js +1 -1
- package/dist/services/auto-register.js +1 -1
- package/dist/services/capability-config.d.ts +0 -1
- package/dist/services/capability-config.js +2 -2
- package/dist/services/instance-ai-config-service.js +1 -1
- package/dist/services/instance-features.d.ts +9 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/panel-auth.d.ts +4 -0
- package/dist/services/panel-auth.js +1 -1
- package/dist/services/plugin-deployment.d.ts +2 -30
- package/dist/services/plugin-deployment.js +2 -2
- package/dist/services/plugin-handler-adapter.d.ts +10 -1
- package/dist/services/plugin-handler-adapter.js +2 -2
- package/dist/services/plugin-listener-runner.js +4 -4
- package/dist/services/plugin-loader.d.ts +7 -1
- package/dist/services/plugin-loader.js +1 -1
- package/dist/services/plugin-manager.d.ts +32 -12
- package/dist/services/plugin-manager.js +4 -4
- package/dist/services/plugin-manifest.d.ts +53 -3
- package/dist/services/plugin-manifest.js +1 -1
- package/dist/services/plugin-runtime.d.ts +8 -9
- package/dist/services/plugin-ui-status-runtime.d.ts +8 -0
- package/dist/services/plugin-ui-status-runtime.js +2 -0
- package/dist/services/plugin-ui-status.d.ts +96 -0
- package/dist/services/plugin-ui-status.js +2 -0
- package/dist/services/plugin-wecom-callback.d.ts +61 -0
- package/dist/services/plugin-wecom-callback.js +3 -0
- package/dist/services/user-scope.d.ts +1 -9
- package/dist/services/user-scope.js +1 -1
- package/dist/services/workspace-files.js +1 -1
- package/dist/utils/dynamic-import.d.ts +31 -0
- package/dist/utils/dynamic-import.js +2 -0
- package/dist/utils/sdk-package-loader.js +1 -1
- package/node_modules/playwright/cli.js +0 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +0 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +0 -0
- package/node_modules/playwright-core/cli.js +0 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +0 -0
- package/package/acode/dist/await-complete-tool.js +3 -3
- package/package/acode/dist/plugin-types.d.ts +41 -1
- package/package/acode/dist/runtime.d.ts +26 -0
- package/package/acode/dist/runtime.js +21 -22
- package/package/acode/dist/sub-agent-manager.d.ts +33 -0
- package/package/acode/dist/sub-agent-manager.js +9 -1
- package/package/acode/dist/sub-agent-tools.d.ts +4 -0
- package/package/acode/dist/sub-agent-tools.js +27 -12
- package/package/acode/dist/subagent-handback.d.ts +16 -0
- package/package/acode/dist/subagent-handback.js +7 -0
- package/package/acode/dist/tool-allocation.js +1 -1
- package/package/acode/package.json +38 -38
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +10 -5
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +27 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +16 -16
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +34 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +65 -19
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +10 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.js +1 -1
- package/package/aws-client-agent-mcp/dist/tool-groups.js +2 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts +81 -3
- package/package/aws-client-agent-mcp/package.json +53 -53
- package/package.json +134 -132
- /package/package/acode/dist/grammars/{grammars/tree-sitter-bash.wasm → tree-sitter-bash.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-c-sharp.wasm → tree-sitter-c-sharp.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-c.wasm → tree-sitter-c.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-cpp.wasm → tree-sitter-cpp.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-go.wasm → tree-sitter-go.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-java.wasm → tree-sitter-java.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-javascript.wasm → tree-sitter-javascript.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-jsx.wasm → tree-sitter-jsx.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-lua.wasm → tree-sitter-lua.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-php.wasm → tree-sitter-php.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-python.wasm → tree-sitter-python.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-ruby.wasm → tree-sitter-ruby.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-rust.wasm → tree-sitter-rust.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-tsx.wasm → tree-sitter-tsx.wasm} +0 -0
- /package/package/acode/dist/grammars/{grammars/tree-sitter-typescript.wasm → tree-sitter-typescript.wasm} +0 -0
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
|
|
21
22
|
import { type ACodeCommandSpec, type ACodePluginBridge, type PluginInvokeContext, type PluginManageRequest, type PluginManageResult, type PluginSummary, type PluginToolInvocationResult, type PluginToolSchema, type JsonObject } from "@agentswork/acode";
|
|
22
23
|
import { type PluginEventMessage } from "./plugin-event-buffer.js";
|
|
23
|
-
|
|
24
|
-
export type { PluginUserConfigField, PluginHandlerDef, PluginToolDef, PluginTransportDef, PluginListenerDef, PluginManifest, } from "./plugin-manifest.js";
|
|
24
|
+
import type { AgentState } from "./plugin-runtime.js";
|
|
25
25
|
import { type UserScope } from "./user-scope.js";
|
|
26
26
|
export type { ListenerRuntime, LoadedPlugin, InstalledRecord, AgentState, SharedPluginEntry, } from "./plugin-runtime.js";
|
|
27
27
|
export interface PluginManagerOptions {
|
|
@@ -29,14 +29,11 @@ export interface PluginManagerOptions {
|
|
|
29
29
|
rootDir?: string;
|
|
30
30
|
|
|
31
31
|
runtimeRootDir?: string;
|
|
32
|
-
|
|
33
|
-
legacyRootDir?: string;
|
|
34
32
|
}
|
|
35
33
|
export declare class PluginManager {
|
|
36
34
|
private agents;
|
|
37
35
|
private readonly rootDir?;
|
|
38
36
|
private readonly runtimeRootDir?;
|
|
39
|
-
private readonly legacyRootDir?;
|
|
40
37
|
|
|
41
38
|
private readonly deployStores;
|
|
42
39
|
|
|
@@ -45,18 +42,17 @@ export declare class PluginManager {
|
|
|
45
42
|
private readonly loader;
|
|
46
43
|
constructor(options?: PluginManagerOptions);
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
45
|
private deployForScope;
|
|
52
46
|
|
|
47
|
+
private resolveDeployStore;
|
|
48
|
+
|
|
49
|
+
private deployStagingRoot;
|
|
50
|
+
|
|
53
51
|
|
|
54
52
|
|
|
55
53
|
|
|
56
54
|
private deployForAgent;
|
|
57
55
|
|
|
58
|
-
private get deploy();
|
|
59
|
-
|
|
60
56
|
|
|
61
57
|
|
|
62
58
|
|
|
@@ -65,6 +61,17 @@ export declare class PluginManager {
|
|
|
65
61
|
forAgent(agentId: string): ACodePluginBridge;
|
|
66
62
|
|
|
67
63
|
setOnPluginsChanged(cb: (agentId: string) => void): void;
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
getEnabledPluginDir(agentId: string, pluginName: string): Promise<string | null>;
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
hydrateForAdmin(agentId: string): Promise<AgentState>;
|
|
68
75
|
private onPluginsChanged?;
|
|
69
76
|
private notifyPluginsChanged;
|
|
70
77
|
|
|
@@ -203,6 +210,18 @@ export declare class PluginManager {
|
|
|
203
210
|
private stopAllListeners;
|
|
204
211
|
|
|
205
212
|
private enqueueEvent;
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
updatePluginConfig(agentId: string, pluginName: string, configPatch: Record<string, unknown>): Promise<PluginManageResult>;
|
|
206
225
|
getLogs(agentId: string, pluginName: string): Promise<{
|
|
207
226
|
ok: boolean;
|
|
208
227
|
error?: string;
|
|
@@ -215,7 +234,7 @@ export declare class PluginManager {
|
|
|
215
234
|
|
|
216
235
|
|
|
217
236
|
|
|
218
|
-
listDeployed(scope
|
|
237
|
+
listDeployed(scope: UserScope): Promise<Array<{
|
|
219
238
|
pluginName: string;
|
|
220
239
|
version?: string;
|
|
221
240
|
fileName: string;
|
|
@@ -229,7 +248,8 @@ export declare class PluginManager {
|
|
|
229
248
|
|
|
230
249
|
|
|
231
250
|
|
|
232
|
-
|
|
251
|
+
|
|
252
|
+
removeDeployedPlugin(pluginName: string, scope: UserScope): Promise<{
|
|
233
253
|
ok: boolean;
|
|
234
254
|
error?: string;
|
|
235
255
|
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{existsSync as y,mkdirSync as $,readFileSync as S,readdirSync as E,rmSync as O,writeFileSync as N}from"node:fs";import{dirname as L,join as f}from"node:path";import{parseAcodeCommandMarkdown as j}from"@agentswork/acode";import{getBridgeTemplatePluginsDir as k}from"../config.js";import{sanitizeAgentId as P,downloadPluginZip as T,sanitizeArchiveFileName as x}from"../utils/file-utils.js";import{createLogger as z}from"../utils/logger.js";import{getAgentOwner as w}from"./agent-ownership.js";import{setDeployedDefaultsProvider as q,reconcileDefaultsFromDeployed as _}from"./capability-config.js";import{isCapabilityEnabled as V,getAgentPluginSources as H}from"./instance-features.js";import{PluginDeploymentStore as A}from"./plugin-deployment.js";import{PluginEventBuffer as J}from"./plugin-event-buffer.js";import{movePluginDir as C,rmWithRetry as I}from"./plugin-fs.js";import{executeHandler as K}from"./plugin-handler-adapter.js";import{ListenerRunner as W}from"./plugin-listener-runner.js";import{PluginLoader as U,toSummary as D}from"./plugin-loader.js";import{isRecord as F,isValidPluginName as Z,validateAgainstSchema as Q}from"./plugin-manifest.js";import{extractPluginZip as M}from"./plugin-package.js";const d=z("plugin-manager");import{isValidPluginName as De,validateManifest as ve,validateAgainstSchema as ke,renderCommandArgv as Pe}from"./plugin-manifest.js";import{getUserPluginsDir as R,isLegacyFallbackDisabled as B}from"./user-scope.js";class G{constructor(e={}){this.agents=new Map,this.deployStores=new Map,this.listeners=new W,this.loader=new U,this.templatePullInFlight=new Set,this.backgroundTemplatePulls=new Set,this.rootDir=e.rootDir,this.runtimeRootDir=e.runtimeRootDir,this.legacyRootDir=e.legacyRootDir}deployForScope(e){const t=this.rootDir??R(e),s=`scope:${t}`;let n=this.deployStores.get(s);return n||(n=new A(t,this.runtimeRootDir,this.loader,this.legacyRootDir??(B()?void 0:k())),this.deployStores.set(s,n)),n}deployForAgent(e){if(this.rootDir){const r="__injected__";let i=this.deployStores.get(r);return i||(i=new A(this.rootDir,this.runtimeRootDir,this.loader,this.legacyRootDir),this.deployStores.set(r,i)),i}const t=w(e);if(t)return this.deployForScope(t);const s="__legacy__";let n=this.deployStores.get(s);return n||(n=new A(k(),this.runtimeRootDir,this.loader),this.deployStores.set(s,n)),n}get deploy(){const e="__legacy__";let t=this.deployStores.get(e);return t||(t=new A(this.rootDir??k(),this.runtimeRootDir,this.loader),this.deployStores.set(e,t)),t}dataRootForAgent(e){if(this.rootDir)return this.rootDir;const t=w(e);return t?R(t):k()}forAgent(e){return{listPlugins:()=>this.list(e),getPluginTools:t=>this.getPluginTools(e,t),invokeTool:(t,s,n,r)=>this.invokeTool(e,t,s,n,r),manage:t=>this.manage(e,t),getCommandDirs:()=>this.getCommandDirs(e)}}setOnPluginsChanged(e){this.onPluginsChanged=e}notifyPluginsChanged(e){try{this.onPluginsChanged?.(e)}catch(t){d.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 E(t)){if(!n.endsWith(".md"))continue;const r=f(t,n);try{const i=j(r,S(r,"utf8"));i.plugin=e,s.push(i)}catch(i){d.warn("[plugin:%s] \u547D\u4EE4\u89E3\u6790\u5931\u8D25 %s: %s",e,n,i instanceof Error?i.message:String(i))}}}catch{}return s}async list(e){const t=await this.hydrate(e),s=new Map;for(const r of t.plugins.values()){const i=D(r);s.set(i.name,i)}const n=this.deployForAgent(e);for(const r of n.readSharedManifest()){const i=r.pluginName;if(!n.deployedPluginHealthy(i))continue;const o=s.get(i);if(o){String(o.source||"").startsWith("shared:")&&(o.enabled=this.effectiveSharedEnabled(e,r));continue}s.set(i,{name:i,version:this.deployedPluginVersion(e,i),status:"enabled",enabled:this.effectiveSharedEnabled(e,r),source:`shared:${r.fileName??`${i}.zip`}`,installedAt:new Date().toISOString(),toolCount:0,listeners:[]})}return[...s.values()].sort((r,i)=>r.name.localeCompare(i.name))}deployedPluginVersion(e,t){try{const s=this.deployForAgent(e).resolveDeployedSourceDir(t);if(!s)return;const n=JSON.parse(S(f(s,"plugin.json"),"utf8"));return typeof n.version=="string"?n.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(r=>({plugin:n.name,name:r.name,title:r.title,description:r.description,inputSchema:r.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 r=this.loader.loadManifestOnly(n);if(!r.ok)return r;const i=r.manifest.name,l=this.agentState(e),o=l.queue.then(async()=>{const a=await this.hydrate(e),u=a.plugins.get(i);u&&this.stopAllListeners(u);const g=this.loader.loadPluginFromDir(n,`debug:${n}`,new Date().toISOString(),s);if(!g.ok)return u&&this.startAllListeners(a,u),{ok:!1,error:`\u8C03\u8BD5\u6302\u8F7D\u5931\u8D25: ${g.error}`};const c=g.plugin;return c.debug=!0,a.plugins.set(c.name,c),this.startAllListeners(a,c),d.info("[agent:%s] \u63D2\u4EF6\u5DF2\u8C03\u8BD5\u6302\u8F7D: %s@%s\uFF08dir=%s\uFF09",a.agentId,c.name,c.version??"?",n),this.notifyPluginsChanged(a.agentId),{ok:!0,summary:D(c)}});return l.queue=o.catch(()=>{}),o}async listDebugPlugins(e){return[...(await this.hydrate(e)).plugins.values()].filter(s=>s.debug===!0).map(s=>D(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,r){const i=await this.hydrate(e),l=i.plugins.get(t);if(!l)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\uFF08\u53EF\u7528\u63D2\u4EF6: ${[...i.plugins.keys()].join(", ")||"\u65E0"}\uFF09`};if(!l.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u5DF2\u505C\u7528\uFF0C\u8BF7\u8BA9\u7528\u6237\u5728\u9762\u677F\u542F\u7528`};const o=l.tools.find(a=>a.name===s);if(!o)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5DE5\u5177 ${s}\uFF08\u53EF\u7528: ${l.tools.map(a=>a.name).join(", ")||"\u65E0"}\uFF09`};if(o.inputSchema){const a=Q(o.inputSchema,n);if(a.length>0)return{ok:!1,error:`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${a.join("; ")}`,inputSchema:o.inputSchema}}return K(e,l,o,n,r)}async manage(e,t){const s=this.agentState(e),n=s.queue.then(async()=>{const r=await this.hydrate(e);return this.doManage(r,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 r=s.slice(6).trim();return!r||!y(f(r,"plugin.json"))?{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11 plugin.json: ${r}`}:this.installFromDirectory(e,r,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 r=this.agentState(e),i=r.queue.then(async()=>{await this.hydrate(e);const l=f(this.dataRootForAgent(e),"tmp",`upload-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const o=await M(t,l);return y(f(o.pluginDir,"plugin.json"))?(d.info("[agent:%s] zip \u5BFC\u5165: %s\uFF08%d \u4E2A\u6587\u4EF6\uFF09",e,s,o.fileCount),await this.installFromDirectory(r,o.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(o){const a=o instanceof Error?o.message:String(o);return d.warn("[agent:%s] zip \u5BFC\u5165\u5931\u8D25: %s",e,a),{ok:!1,error:`zip \u5BFC\u5165\u5931\u8D25: ${a}`}}finally{await I(l,3,100).catch(()=>{})}});return r.queue=i.catch(()=>{}),i}async installFromDirectory(e,t,s,n){const r=this.loader.loadManifestOnly(t);if(!r.ok)return r;const i=r.manifest,l=i.name,o=this.loader.validateUserConfigValues(i,n??{});if(o)return{ok:!1,error:o};const a=this.loader.loadPluginFromDir(t,s,new Date().toISOString(),n??{});if(!a.ok)return d.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u6821\u9A8C\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,a.error),a;const u=f(this.dataRootForAgent(e.agentId),"cache",P(e.agentId),l),g=t===u,c=e.plugins.get(l);if(c&&this.stopAllListeners(c),!g)try{await I(u),await C(t,u)}catch(h){const b=h instanceof Error?h.message:String(h);return d.warn("[agent:%s] \u63D2\u4EF6\u79FB\u52A8\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,b),c&&this.startAllListeners(e,c),{ok:!1,error:`\u63D2\u4EF6\u79FB\u52A8\u5931\u8D25: ${b}`}}const m=this.loader.loadPluginFromDir(u,s,new Date().toISOString(),n??{});if(!m.ok){if(d.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,m.error),!g)try{await C(u,t)}catch(h){d.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(c)try{this.startAllListeners(e,c)}catch(h){d.warn("[agent:%s] \u6062\u590D\u65E7\u5B9E\u4F8B\u76D1\u542C\u5931\u8D25: %s",e.agentId,h instanceof Error?h.message:String(h))}return m}const p=m.plugin;return e.plugins.set(p.name,p),this.persistInstalled(e),this.startAllListeners(e,p),d.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5B89\u88C5: %s@%s",e.agentId,p.name,p.version??"?"),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:D(p)}}async uninstallPlugin(e,t){const s=(t.name??"").trim(),n=e.plugins.get(s);if(!n)return{ok:!1,error:`\u63D2\u4EF6 ${s} \u672A\u5B89\u88C5`};this.stopAllListeners(n),e.plugins.delete(s),n.tools.length>0&&this.pushDeprecatedNotice(e,s,"\u5DF2\u88AB\u5378\u8F7D");const r=this.deployForAgent(e.agentId);return r.readSharedManifest().some(l=>l.pluginName===s)&&r.removeRuntimeLink(e.agentId,s),this.persistInstalled(e),n.debug!==!0&&await I(n.dir),d.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5378\u8F7D: %s",e.agentId,s),this.notifyPluginsChanged(e.agentId),{ok:!0}}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 r=this.loader.loadPluginFromDir(n.dir,n.source,n.installedAt,n.configValues);if(!r.ok)return{ok:!1,error:`\u91CD\u8F7D\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u7248\u672C\u8FD0\u884C\uFF09: ${r.error}`};const i=r.plugin;return i.debug=n.debug,this.stopAllListeners(n),e.plugins.set(s,i),this.persistInstalled(e),this.startAllListeners(e,i),d.info("[agent:%s] \u63D2\u4EF6\u5DF2\u91CD\u8F7D: %s",e.agentId,s),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:D(i)}}async setEnabled(e,t,s){const n=(t.name??"").trim(),r=e.plugins.get(n);return r?(r.enabled=s,s?this.startAllListeners(e,r):(r.tools.length>0&&this.pushDeprecatedNotice(e,n,"\u5DF2\u505C\u7528"),this.stopAllListeners(r)),this.persistInstalled(e),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:D(r)}):{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`}}async saveSharedPlugin(e,t,s=!0,n,r){if(!e||e.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};const i=r?this.deployForScope(r):this.deploy;await i.migrateLegacySharedIfNeeded();const l=f(r?R(r):this.rootDir??k(),"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const o=await M(e,l),a=this.loader.loadManifestOnly(o.pluginDir);if(!a.ok)return a;const u=a.manifest.name,g=i.readSharedManifest().find(p=>p.pluginName===u);if(g&&g.sha256===n&&i.deployedPluginHealthy(u))return{ok:!0,pluginName:u};await this.suspendDeployedPluginAcrossAgents(u,r),i.replaceDeployedDir(u,o.pluginDir),await i.updateSharedManifest(p=>{const h=p.filter(b=>b.pluginName!==u);return h.push({pluginName:u,zipFile:g?.zipFile??`${u}.zip`,fileName:t,defaultEnabled:s,sha256:n}),h}),d.info("[shared] \u63D2\u4EF6\u5DF2\u90E8\u7F72\u5230 bridge: %s\uFF08\u6765\u6E90 %s%s\uFF09",u,t,n?`, sha256=${n.slice(0,12)}\u2026`:"");const m=(await this.applySharedToKnownAgents(r)).find(p=>p.installed.includes(u));return m&&d.info("[shared] \u5DF2\u540C\u6B65\u5230\u8FD0\u884C\u4E2D agent: %s",m.agentId),{ok:!0,pluginName:u}}catch(o){const a=o instanceof Error?o.message:String(o);return d.warn("[shared] \u63D2\u4EF6\u90E8\u7F72\u5931\u8D25\uFF08%s\uFF09: %s",t,a),{ok:!1,error:`\u63D2\u4EF6\u90E8\u7F72\u5931\u8D25: ${a}`}}finally{await I(l,3,100).catch(()=>{})}}async suspendDeployedPluginAcrossAgents(e,t){for(const s of[...this.agents.values()]){if(!s.hydrated)continue;if(t){const r=w(s.agentId);if(r&&(r.tenantId!==t.tenantId||r.userId!==t.userId))continue}const n=s.queue.then(async()=>{const r=s.plugins.get(e);r&&(this.stopAllListeners(r),s.plugins.delete(e),d.info("[agent:%s] \u5DF2\u6458\u9664\u63D2\u4EF6\u7B49\u5F85\u90E8\u7F72\u533A\u66F4\u65B0: %s",s.agentId,e))});s.queue=n.catch(()=>{}),await n}}async applySharedToKnownAgents(e){const t=[];for(const s of this.agents.values()){if(!s.hydrated)continue;if(e){const r=w(s.agentId);if(r&&(r.tenantId!==e.tenantId||r.userId!==e.userId))continue}const n=await this.syncSharedIntoStateQueued(s);n.length>0&&t.push({agentId:s.agentId,installed:n})}return t}async syncSharedIntoStateQueued(e){const t=e.queue.then(()=>this.syncSharedIntoState(e));return e.queue=t.catch(()=>[]),t}effectiveSharedEnabled(e,t){return V(e,"plugins",t.pluginName)&&(t.defaultEnabled??!0)}async pullTemplatePluginsFromCapabilities(e){const t=H(e.agentId);if(!t)return;const s=this.deployForAgent(e.agentId),n=s.readSharedManifest();for(const[r,i]of Object.entries(t)){if(!i.url)continue;const l=n.find(a=>a.pluginName===r);if(!l||!l.sha256||l.sha256===i.sha256&&s.deployedPluginHealthy(r)||this.templatePullInFlight.has(r))continue;this.templatePullInFlight.add(r);const o=f(this.dataRootForAgent(e.agentId),"tmp",`pull-${P(e.agentId)}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{$(o,{recursive:!0});let a=`${r}.zip`;try{const m=decodeURIComponent(i.url.split("/").pop()||"");m.toLowerCase().endsWith(".zip")&&(a=x(m,`${r}.zip`))}catch{}const u=await T({downloadUrl:i.url,fileName:a,sha256:i.sha256},o);if(!u)continue;const g=S(u.outputPath),c=await this.saveSharedPlugin(g,u.fileName,l.defaultEnabled??!0,i.sha256,w(e.agentId)??void 0);c.ok?d.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u81EA\u52A8\u5BF9\u8D26\u66F4\u65B0\u6210\u529F: %s",e.agentId,c.pluginName??r):d.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: %s: %s",e.agentId,r,c.error)}catch(a){d.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 I(o,3,100).catch(()=>{})}}}queueTemplatePull(e){this.backgroundTemplatePulls.has(e.agentId)||(this.backgroundTemplatePulls.add(e.agentId),this.pullTemplatePluginsFromCapabilities(e).catch(t=>{d.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.deployForAgent(e.agentId),s=t.readSharedManifest(),n=new Set,r=[];for(const i of s)if(n.add(i.pluginName),this.effectiveSharedEnabled(e.agentId,i)){const o=e.plugins.get(i.pluginName);if(!t.ensureRuntimeLink(e.agentId,i.pluginName))continue;const a=t.runtimeLinkPath(e.agentId,i.pluginName);if(!o){const u=this.loader.loadPluginFromDir(a,`shared:${i.fileName??`${i.pluginName}.zip`}`,new Date().toISOString(),{});u.ok?(e.plugins.set(u.plugin.name,u.plugin),r.push(u.plugin.name),d.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u5DF2\u6309\u5F00\u542F\u6001\u52A0\u8F7D: %s",e.agentId,i.pluginName)):d.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: %s: %s",e.agentId,i.pluginName,u.error)}}else{const o=e.plugins.get(i.pluginName);o&&(this.stopAllListeners(o),e.plugins.delete(i.pluginName)),t.removeRuntimeLink(e.agentId,i.pluginName)}return this.cleanupRuntimeDir(e,n),this.persistInstalled(e),r}cleanupRuntimeDir(e,t){const s=this.deployForAgent(e.agentId).runtimeDirFor(e.agentId);try{if(!y(s))return;const n=new Set(t),r=this.deployForAgent(e.agentId);for(const i of E(s))!n.has(i)&&!e.plugins.has(i)&&r.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),r=n.plugins.get(t);if(!r||!r.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\u6216\u5DF2\u505C\u7528`};const i=typeof s.event=="string"?s.event:"",l=r.listeners.find(o=>o.transport.type==="http-callback"&&(!i||o.event===i));return l?(this.enqueueEvent(n,r,l,s),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5339\u914D\u7684 http-callback \u76D1\u542C\u5668\uFF08event=${i||"(\u7A7A)"}\uFF09`}}agentState(e){let t=this.agents.get(e);return t||(t={agentId:e,plugins:new Map,buffer:new
|
|
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,r]of Object.entries(e.configValues))t.has(n)||(s[n]=r);return s}secretsFile(e){return
|
|
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 r=[];for(const i of n.runtimes)r.push(`\u2500\u2500 listener ${i.def.event} (${i.def.transport?.type}) ${i.lastError?`[error: ${i.lastError}]`:""}`),r.push(...i.logs);return{ok:!0,logs:r}}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(e){const t=
|
|
1
|
+
import{existsSync as S,mkdirSync as A,readFileSync as w,readdirSync as F,rmSync as O,writeFileSync as E}from"node:fs";import{dirname as R,join as m}from"node:path";import{parseAcodeCommandMarkdown as j}from"@agentswork/acode";import{sanitizeAgentId as D,downloadPluginZip as M,sanitizeArchiveFileName as T}from"../utils/file-utils.js";import{createLogger as q}from"../utils/logger.js";import{getAgentOwner as k}from"./agent-ownership.js";import{setDeployedDefaultsProvider as x,reconcileDefaultsFromDeployed as z}from"./capability-config.js";import{isCapabilityEnabled as V,getAgentPluginSources as _}from"./instance-features.js";import{PluginDeploymentStore as C}from"./plugin-deployment.js";import{PluginEventBuffer as H}from"./plugin-event-buffer.js";import{movePluginDir as N,rmWithRetry as P}from"./plugin-fs.js";import{executeHandler as J}from"./plugin-handler-adapter.js";import{ListenerRunner as U}from"./plugin-listener-runner.js";import{PluginLoader as K,toSummary as y}from"./plugin-loader.js";import{isRecord as b,isValidPluginName as W,validateAgainstSchema as Z}from"./plugin-manifest.js";import{extractPluginZip as L}from"./plugin-package.js";const c=q("plugin-manager");import{getUserPluginsDir as $}from"./user-scope.js";class Q{constructor(e={}){this.agents=new Map,this.deployStores=new Map,this.listeners=new U,this.loader=new K,this.templatePullInFlight=new Set,this.backgroundTemplatePulls=new Set,this.rootDir=e.rootDir,this.runtimeRootDir=e.runtimeRootDir}deployForScope(e){const t=this.rootDir??$(e),s=`scope:${t}`;let n=this.deployStores.get(s);return n||(n=new C(t,this.runtimeRootDir),this.deployStores.set(s,n)),n}resolveDeployStore(e){if(e)return this.deployForScope(e);if(this.rootDir)return this.deployForAgent("__injected__");throw new Error("saveSharedPlugin \u9700\u8981\u663E\u5F0F UserScope\uFF08\u751F\u4EA7\u94FE\u8DEF\u65E0\u9057\u7559\u56DE\u9000\uFF09")}deployStagingRoot(e){return e?$(e):this.rootDir??(()=>{throw new Error("saveSharedPlugin \u9700\u8981\u663E\u5F0F UserScope\uFF08\u751F\u4EA7\u94FE\u8DEF\u65E0\u9057\u7559\u56DE\u9000\uFF09")})()}deployForAgent(e){if(this.rootDir){const s="__injected__";let n=this.deployStores.get(s);return n||(n=new C(this.rootDir,this.runtimeRootDir),this.deployStores.set(s,n)),n}const t=k(e);if(!t)throw new Error(`agent ${e} \u5F52\u5C5E\u672A\u767B\u8BB0\uFF08owner.json \u7F3A\u5931\uFF09\uFF0C\u65E0\u6CD5\u89E3\u6790\u63D2\u4EF6\u6570\u636E\u6839`);return this.deployForScope(t)}dataRootForAgent(e){if(this.rootDir)return this.rootDir;const t=k(e);if(!t)throw new Error(`agent ${e} \u5F52\u5C5E\u672A\u767B\u8BB0\uFF08owner.json \u7F3A\u5931\uFF09\uFF0C\u65E0\u6CD5\u89E3\u6790\u63D2\u4EF6\u6570\u636E\u6839`);return $(t)}forAgent(e){return{listPlugins:()=>this.list(e),getPluginTools:t=>this.getPluginTools(e,t),invokeTool:(t,s,n,r)=>this.invokeTool(e,t,s,n,r),manage:t=>this.manage(e,t),getCommandDirs:()=>this.getCommandDirs(e)}}setOnPluginsChanged(e){this.onPluginsChanged=e}async getEnabledPluginDir(e,t){const n=(await this.hydrate(e)).plugins.get(t);return!n||!n.enabled?null:n.dir}async hydrateForAdmin(e){return this.hydrate(e)}notifyPluginsChanged(e){try{this.onPluginsChanged?.(e)}catch(t){c.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 F(t)){if(!n.endsWith(".md"))continue;const r=m(t,n);try{const i=j(r,w(r,"utf8"));i.plugin=e,s.push(i)}catch(i){c.warn("[plugin:%s] \u547D\u4EE4\u89E3\u6790\u5931\u8D25 %s: %s",e,n,i instanceof Error?i.message:String(i))}}}catch{}return s}async list(e){const t=await this.hydrate(e),s=new Map;for(const r of t.plugins.values()){const i=y(r,e);s.set(i.name,i)}const n=this.deployForAgent(e);for(const r of n.readSharedManifest()){const i=r.pluginName;if(!n.deployedPluginHealthy(i))continue;const o=s.get(i);if(o){String(o.source||"").startsWith("shared:")&&(o.enabled=this.effectiveSharedEnabled(e,r));continue}s.set(i,{name:i,version:this.deployedPluginVersion(e,i),status:"enabled",enabled:this.effectiveSharedEnabled(e,r),source:`shared:${r.fileName??`${i}.zip`}`,installedAt:new Date().toISOString(),toolCount:0,listeners:[]})}return[...s.values()].sort((r,i)=>r.name.localeCompare(i.name))}deployedPluginVersion(e,t){try{const s=this.deployForAgent(e).resolveDeployedSourceDir(t);if(!s)return;const n=JSON.parse(w(m(s,"plugin.json"),"utf8"));return typeof n.version=="string"?n.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(r=>({plugin:n.name,name:r.name,title:r.title,description:r.description,inputSchema:r.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(!S(n))return{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728: ${n}`};const r=this.loader.loadManifestOnly(n);if(!r.ok)return r;const i=r.manifest.name,l=this.agentState(e),o=l.queue.then(async()=>{const a=await this.hydrate(e),u=a.plugins.get(i);u&&this.stopAllListeners(u);const g=this.loader.loadPluginFromDir(n,`debug:${n}`,new Date().toISOString(),s);if(!g.ok)return u&&this.startAllListeners(a,u),{ok:!1,error:`\u8C03\u8BD5\u6302\u8F7D\u5931\u8D25: ${g.error}`};const d=g.plugin;return d.debug=!0,a.plugins.set(d.name,d),this.startAllListeners(a,d),c.info("[agent:%s] \u63D2\u4EF6\u5DF2\u8C03\u8BD5\u6302\u8F7D: %s@%s\uFF08dir=%s\uFF09",a.agentId,d.name,d.version??"?",n),this.notifyPluginsChanged(a.agentId),{ok:!0,summary:y(d)}});return l.queue=o.catch(()=>{}),o}async listDebugPlugins(e){return[...(await this.hydrate(e)).plugins.values()].filter(s=>s.debug===!0).map(s=>y(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,r){const i=await this.hydrate(e),l=i.plugins.get(t);if(!l)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\uFF08\u53EF\u7528\u63D2\u4EF6: ${[...i.plugins.keys()].join(", ")||"\u65E0"}\uFF09`};if(!l.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u5DF2\u505C\u7528\uFF0C\u8BF7\u8BA9\u7528\u6237\u5728\u9762\u677F\u542F\u7528`};const o=l.tools.find(a=>a.name===s);if(!o)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5DE5\u5177 ${s}\uFF08\u53EF\u7528: ${l.tools.map(a=>a.name).join(", ")||"\u65E0"}\uFF09`};if(o.inputSchema){const a=Z(o.inputSchema,n);if(a.length>0)return{ok:!1,error:`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${a.join("; ")}`,inputSchema:o.inputSchema}}return J(e,l,o,n,r)}async manage(e,t){const s=this.agentState(e),n=s.queue.then(async()=>{const r=await this.hydrate(e);return this.doManage(r,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 r=s.slice(6).trim();return!r||!S(m(r,"plugin.json"))?{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11 plugin.json: ${r}`}:this.installFromDirectory(e,r,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 r=this.agentState(e),i=r.queue.then(async()=>{await this.hydrate(e);const l=m(this.dataRootForAgent(e),"tmp",`upload-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const o=await L(t,l);return S(m(o.pluginDir,"plugin.json"))?(c.info("[agent:%s] zip \u5BFC\u5165: %s\uFF08%d \u4E2A\u6587\u4EF6\uFF09",e,s,o.fileCount),await this.installFromDirectory(r,o.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(o){const a=o instanceof Error?o.message:String(o);return c.warn("[agent:%s] zip \u5BFC\u5165\u5931\u8D25: %s",e,a),{ok:!1,error:`zip \u5BFC\u5165\u5931\u8D25: ${a}`}}finally{await P(l,3,100).catch(()=>{})}});return r.queue=i.catch(()=>{}),i}async installFromDirectory(e,t,s,n){const r=this.loader.loadManifestOnly(t);if(!r.ok)return r;const i=r.manifest,l=i.name,o=this.loader.validateUserConfigValues(i,n??{});if(o)return{ok:!1,error:o};const a=this.loader.loadPluginFromDir(t,s,new Date().toISOString(),n??{});if(!a.ok)return c.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u6821\u9A8C\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,a.error),a;const u=m(this.dataRootForAgent(e.agentId),"cache",D(e.agentId),l),g=t===u,d=e.plugins.get(l);if(d&&this.stopAllListeners(d),!g)try{await P(u),await N(t,u)}catch(f){const I=f instanceof Error?f.message:String(f);return c.warn("[agent:%s] \u63D2\u4EF6\u79FB\u52A8\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,I),d&&this.startAllListeners(e,d),{ok:!1,error:`\u63D2\u4EF6\u79FB\u52A8\u5931\u8D25: ${I}`}}const p=this.loader.loadPluginFromDir(u,s,new Date().toISOString(),n??{});if(!p.ok){if(c.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,s,p.error),!g)try{await N(u,t)}catch(f){c.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){c.warn("[agent:%s] \u6062\u590D\u65E7\u5B9E\u4F8B\u76D1\u542C\u5931\u8D25: %s",e.agentId,f instanceof Error?f.message:String(f))}return p}const h=p.plugin;return e.plugins.set(h.name,h),this.persistInstalled(e),this.startAllListeners(e,h),c.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5B89\u88C5: %s@%s",e.agentId,h.name,h.version??"?"),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:y(h)}}async uninstallPlugin(e,t){const s=(t.name??"").trim(),n=e.plugins.get(s);if(!n)return{ok:!1,error:`\u63D2\u4EF6 ${s} \u672A\u5B89\u88C5`};this.stopAllListeners(n),e.plugins.delete(s),n.tools.length>0&&this.pushDeprecatedNotice(e,s,"\u5DF2\u88AB\u5378\u8F7D");const r=this.deployForAgent(e.agentId);return r.readSharedManifest().some(l=>l.pluginName===s)&&r.removeRuntimeLink(e.agentId,s),this.persistInstalled(e),n.debug!==!0&&await P(n.dir),c.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5378\u8F7D: %s",e.agentId,s),this.notifyPluginsChanged(e.agentId),{ok:!0}}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 r=this.loader.loadPluginFromDir(n.dir,n.source,n.installedAt,n.configValues);if(!r.ok)return{ok:!1,error:`\u91CD\u8F7D\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u7248\u672C\u8FD0\u884C\uFF09: ${r.error}`};const i=r.plugin;return i.debug=n.debug,this.stopAllListeners(n),e.plugins.set(s,i),this.persistInstalled(e),this.startAllListeners(e,i),c.info("[agent:%s] \u63D2\u4EF6\u5DF2\u91CD\u8F7D: %s",e.agentId,s),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:y(i)}}async setEnabled(e,t,s){const n=(t.name??"").trim(),r=e.plugins.get(n);return r?(r.enabled=s,s?this.startAllListeners(e,r):(r.tools.length>0&&this.pushDeprecatedNotice(e,n,"\u5DF2\u505C\u7528"),this.stopAllListeners(r)),this.persistInstalled(e),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:y(r)}):{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`}}async saveSharedPlugin(e,t,s=!0,n,r){if(!e||e.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};const i=this.resolveDeployStore(r),l=m(this.deployStagingRoot(r),"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const o=await L(e,l),a=this.loader.loadManifestOnly(o.pluginDir);if(!a.ok)return a;const u=a.manifest.name,g=i.readSharedManifest().find(h=>h.pluginName===u);if(g&&g.sha256===n&&i.deployedPluginHealthy(u))return{ok:!0,pluginName:u};await this.suspendDeployedPluginAcrossAgents(u,r),i.replaceDeployedDir(u,o.pluginDir),await i.updateSharedManifest(h=>{const f=h.filter(I=>I.pluginName!==u);return f.push({pluginName:u,fileName:t,defaultEnabled:s,sha256:n}),f}),c.info("[shared] \u63D2\u4EF6\u5DF2\u90E8\u7F72\u5230 bridge: %s\uFF08\u6765\u6E90 %s%s\uFF09",u,t,n?`, sha256=${n.slice(0,12)}\u2026`:"");const p=(await this.applySharedToKnownAgents(r)).find(h=>h.installed.includes(u));return p&&c.info("[shared] \u5DF2\u540C\u6B65\u5230\u8FD0\u884C\u4E2D agent: %s",p.agentId),{ok:!0,pluginName:u}}catch(o){const a=o instanceof Error?o.message:String(o);return c.warn("[shared] \u63D2\u4EF6\u90E8\u7F72\u5931\u8D25\uFF08%s\uFF09: %s",t,a),{ok:!1,error:`\u63D2\u4EF6\u90E8\u7F72\u5931\u8D25: ${a}`}}finally{await P(l,3,100).catch(()=>{})}}async suspendDeployedPluginAcrossAgents(e,t){for(const s of[...this.agents.values()]){if(!s.hydrated)continue;if(t){const r=k(s.agentId);if(r&&(r.tenantId!==t.tenantId||r.userId!==t.userId))continue}const n=s.queue.then(async()=>{const r=s.plugins.get(e);r&&(this.stopAllListeners(r),s.plugins.delete(e),c.info("[agent:%s] \u5DF2\u6458\u9664\u63D2\u4EF6\u7B49\u5F85\u90E8\u7F72\u533A\u66F4\u65B0: %s",s.agentId,e))});s.queue=n.catch(()=>{}),await n}}async applySharedToKnownAgents(e){const t=[];for(const s of this.agents.values()){if(!s.hydrated)continue;if(e){const r=k(s.agentId);if(r&&(r.tenantId!==e.tenantId||r.userId!==e.userId))continue}const n=await this.syncSharedIntoStateQueued(s);n.length>0&&t.push({agentId:s.agentId,installed:n})}return t}async syncSharedIntoStateQueued(e){const t=e.queue.then(()=>this.syncSharedIntoState(e));return e.queue=t.catch(()=>[]),t}effectiveSharedEnabled(e,t){return V(e,"plugins",t.pluginName)&&(t.defaultEnabled??!0)}async pullTemplatePluginsFromCapabilities(e){const t=_(e.agentId);if(!t)return;const s=this.deployForAgent(e.agentId),n=s.readSharedManifest();for(const[r,i]of Object.entries(t)){if(!i.url)continue;const l=n.find(a=>a.pluginName===r);if(!l||!l.sha256||l.sha256===i.sha256&&s.deployedPluginHealthy(r)||this.templatePullInFlight.has(r))continue;this.templatePullInFlight.add(r);const o=m(this.dataRootForAgent(e.agentId),"tmp",`pull-${D(e.agentId)}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{A(o,{recursive:!0});let a=`${r}.zip`;try{const p=decodeURIComponent(i.url.split("/").pop()||"");p.toLowerCase().endsWith(".zip")&&(a=T(p,`${r}.zip`))}catch{}const u=await M({downloadUrl:i.url,fileName:a,sha256:i.sha256},o);if(!u)continue;const g=w(u.outputPath),d=await this.saveSharedPlugin(g,u.fileName,l.defaultEnabled??!0,i.sha256,k(e.agentId)??void 0);d.ok?c.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u81EA\u52A8\u5BF9\u8D26\u66F4\u65B0\u6210\u529F: %s",e.agentId,d.pluginName??r):c.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: %s: %s",e.agentId,r,d.error)}catch(a){c.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 P(o,3,100).catch(()=>{})}}}queueTemplatePull(e){this.backgroundTemplatePulls.has(e.agentId)||(this.backgroundTemplatePulls.add(e.agentId),this.pullTemplatePluginsFromCapabilities(e).catch(t=>{c.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.deployForAgent(e.agentId),s=t.readSharedManifest(),n=new Set,r=[];for(const i of s)if(n.add(i.pluginName),this.effectiveSharedEnabled(e.agentId,i)){const o=e.plugins.get(i.pluginName);if(!t.ensureRuntimeLink(e.agentId,i.pluginName))continue;const a=t.runtimeLinkPath(e.agentId,i.pluginName);if(!o){const u=this.loader.loadPluginFromDir(a,`shared:${i.fileName??`${i.pluginName}.zip`}`,new Date().toISOString(),{});u.ok?(e.plugins.set(u.plugin.name,u.plugin),r.push(u.plugin.name),c.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u5DF2\u6309\u5F00\u542F\u6001\u52A0\u8F7D: %s",e.agentId,i.pluginName)):c.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: %s: %s",e.agentId,i.pluginName,u.error)}}else{const o=e.plugins.get(i.pluginName);o&&(this.stopAllListeners(o),e.plugins.delete(i.pluginName)),t.removeRuntimeLink(e.agentId,i.pluginName)}return this.cleanupRuntimeDir(e,n),this.persistInstalled(e),r}cleanupRuntimeDir(e,t){const s=this.deployForAgent(e.agentId).runtimeDirFor(e.agentId);try{if(!S(s))return;const n=new Set(t),r=this.deployForAgent(e.agentId);for(const i of F(s))!n.has(i)&&!e.plugins.has(i)&&r.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),r=n.plugins.get(t);if(!r||!r.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\u6216\u5DF2\u505C\u7528`};const i=typeof s.event=="string"?s.event:"",l=r.listeners.find(o=>o.transport.type==="http-callback"&&(!i||o.event===i));return l?(this.enqueueEvent(n,r,l,s),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5339\u914D\u7684 http-callback \u76D1\u542C\u5668\uFF08event=${i||"(\u7A7A)"}\uFF09`}}agentState(e){let t=this.agents.get(e);return t||(t={agentId:e,plugins:new Map,buffer:new H(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,s=this.installedFile(t);if(!S(s)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const n=JSON.parse(w(s,"utf8")),r=this.readSecrets(t);for(const i of n.plugins??[]){if(String(i.source||"").startsWith("shared:"))continue;const o=m(this.dataRootForAgent(t),"cache",D(t),i.name);if(!S(m(o,"plugin.json"))){c.warn("[agent:%s] \u5DF2\u5B89\u88C5\u63D2\u4EF6\u76EE\u5F55\u7F3A\u5931\uFF0C\u8DF3\u8FC7: %s",t,i.name);continue}const a={...i.config,...r[i.name]??{}},u=this.loader.loadPluginFromDir(o,i.source,i.installedAt,a);if(u.ok){const g=u.plugin;g.enabled=i.enabled,e.plugins.set(g.name,g)}else c.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25: %s: %s",t,i.name,u.error)}}catch(n){const r=n instanceof Error?n.message:String(n);c.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,r)}await this.syncSharedIntoState(e).catch(n=>{c.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 m(this.dataRootForAgent(e),"agents",D(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);A(R(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)}));E(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,r]of Object.entries(e.configValues))t.has(n)||(s[n]=r);return s}secretsFile(e){return m(this.dataRootForAgent(e),"agents",D(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(w(this.secretsFile(e),"utf8"));if(!b(t)||!b(t.secrets))return{};const s={};for(const[n,r]of Object.entries(t.secrets)){if(!b(r))continue;const i={};for(const[l,o]of Object.entries(r))typeof o=="string"&&(i[l]=o);s[n]=i}return s}catch{return{}}}persistSecrets(e){const t={};for(const n of e.plugins.values()){if(n.debug===!0)continue;const r={};for(const i of this.sensitiveKeysOf(n)){const l=n.configValues[i];typeof l=="string"&&l.length>0&&(r[i]=l)}Object.keys(r).length>0&&(t[n.name]=r)}const s=this.secretsFile(e.agentId);if(Object.keys(t).length===0){try{O(s,{force:!0})}catch{}return}A(R(s),{recursive:!0}),E(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 updatePluginConfig(e,t,s){const n=await this.hydrate(e),r=n.queue.then(async()=>{const i=n.plugins.get(t);if(!i)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5`};if(!b(s))return{ok:!1,error:"config \u987B\u4E3A JSON \u5BF9\u8C61"};const l="********",o={...i.configValues};for(const[p,h]of Object.entries(s)){if(h===null){delete o[p];continue}const f=String(h);f!==l&&(o[p]=f)}const a={userConfig:i.userConfig},u=this.loader.validateUserConfigValues(a,o);if(u)return{ok:!1,error:u};const g=this.loader.loadPluginFromDir(i.dir,i.source,i.installedAt,o);if(!g.ok)return{ok:!1,error:`\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF08\u4FDD\u7559\u539F\u914D\u7F6E\uFF09: ${g.error}`};const d=g.plugin;return d.debug=i.debug,this.stopAllListeners(i),n.plugins.set(t,d),this.persistInstalled(n),this.startAllListeners(n,d),c.info("[agent:%s] \u63D2\u4EF6\u914D\u7F6E\u5DF2\u66F4\u65B0\u5E76\u91CD\u8F7D: %s\uFF08\u952E: %s\uFF09",e,t,Object.keys(s).join(", ")||"(\u65E0\u53D8\u66F4)"),this.notifyPluginsChanged(e),{ok:!0,summary:y(d)}});return n.queue=r.catch(()=>{}),r}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 r=[];for(const i of n.runtimes)r.push(`\u2500\u2500 listener ${i.def.event} (${i.def.transport?.type}) ${i.lastError?`[error: ${i.lastError}]`:""}`),r.push(...i.logs);return{ok:!0,logs:r}}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(e){const t=this.deployForScope(e),s=t.readSharedManifest(),n=[];for(const r of s){const i=t.deployedPluginDir(r.pluginName),l=t.deployedPluginHealthy(r.pluginName);let o;if(l)try{const a=JSON.parse(w(m(i,"plugin.json"),"utf8"));typeof a.version=="string"&&(o=a.version)}catch{}n.push({pluginName:r.pluginName,version:o,fileName:r.fileName??`${r.pluginName}.zip`,defaultEnabled:r.defaultEnabled!==!1,sha256:r.sha256,deployed:l})}return n}deployedDefaultEnabled(e){if(!e)return{};const s=this.deployForScope(e).readSharedManifest(),n={};for(const r of s)n[r.pluginName]=r.defaultEnabled!==!1;return n}async removeDeployedPlugin(e,t){if(!W(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};const s=this.deployForScope(t);for(const n of[...this.agents.values()]){if(!n.hydrated)continue;if(t){const i=k(n.agentId);if(i&&(i.tenantId!==t.tenantId||i.userId!==t.userId))continue}const r=n.queue.then(async()=>{const i=n.plugins.get(e);i&&(this.stopAllListeners(i),n.plugins.delete(e)),s.removeRuntimeLink(n.agentId,e),this.persistInstalled(n)});n.queue=r.catch(()=>{}),await r}await s.updateSharedManifest(n=>n.filter(r=>r.pluginName!==e));try{s.removeDeployedDir(e)}catch(n){return{ok:!1,error:`\u90E8\u7F72\u76EE\u5F55\u6E05\u7406\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`}}return c.info("[shared] \u63D2\u4EF6\u5DF2\u4ECE bridge \u79FB\u9664\u90E8\u7F72: %s",e),{ok:!0}}}const B=new Q;x("plugins",v=>v?B.deployedDefaultEnabled(v):{});const he=v=>z(v,"plugins");export{Q as PluginManager,B as pluginManager,he as reconcilePluginsDefaultsFromDeployed};
|
|
5
5
|
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
import type { PluginEventPriority } from "./plugin-event-buffer.js";
|
|
13
|
+
|
|
14
|
+
export declare const PLUGIN_MANIFEST_SCHEMA_VERSION = 2;
|
|
13
15
|
export interface PluginUserConfigField {
|
|
14
16
|
key: string;
|
|
15
17
|
type?: "string" | "number" | "boolean";
|
|
@@ -19,7 +21,7 @@ export interface PluginUserConfigField {
|
|
|
19
21
|
default?: string;
|
|
20
22
|
}
|
|
21
23
|
export interface PluginHandlerDef {
|
|
22
|
-
type: "process" | "module"
|
|
24
|
+
type: "process" | "module";
|
|
23
25
|
|
|
24
26
|
command?: string;
|
|
25
27
|
|
|
@@ -54,7 +56,7 @@ export interface PluginListenerDef {
|
|
|
54
56
|
description?: string;
|
|
55
57
|
transport: PluginTransportDef;
|
|
56
58
|
|
|
57
|
-
delivery?: "poll"
|
|
59
|
+
delivery?: "poll";
|
|
58
60
|
priority?: PluginEventPriority;
|
|
59
61
|
|
|
60
62
|
matcher?: Record<string, string | number | boolean | Array<string | number | boolean>>;
|
|
@@ -65,16 +67,64 @@ export interface PluginListenerDef {
|
|
|
65
67
|
|
|
66
68
|
summary?: string;
|
|
67
69
|
}
|
|
70
|
+
|
|
71
|
+
export declare const PLUGIN_UI_BUILTIN_ICONS: readonly ["puzzle", "github"];
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
export interface PluginUiManifest {
|
|
79
|
+
|
|
80
|
+
icon?: string;
|
|
81
|
+
|
|
82
|
+
status?: {
|
|
83
|
+
|
|
84
|
+
tool: string;
|
|
85
|
+
|
|
86
|
+
persistent?: boolean;
|
|
87
|
+
|
|
88
|
+
hideWhenZero?: boolean;
|
|
89
|
+
|
|
90
|
+
pollIntervalMs?: number;
|
|
91
|
+
|
|
92
|
+
title?: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
view?: {
|
|
96
|
+
|
|
97
|
+
entry: string;
|
|
98
|
+
|
|
99
|
+
title: string;
|
|
100
|
+
|
|
101
|
+
assetsRoot?: string;
|
|
102
|
+
|
|
103
|
+
invoke: string[];
|
|
104
|
+
|
|
105
|
+
defaultRoute?: string;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export declare function isValidUiIcon(value: string): boolean;
|
|
110
|
+
|
|
111
|
+
export declare function isValidUiPath(value: string): boolean;
|
|
112
|
+
|
|
113
|
+
export declare function validateUiManifest(ui: unknown): string | null;
|
|
68
114
|
export interface PluginManifest {
|
|
115
|
+
|
|
116
|
+
schemaVersion: number;
|
|
69
117
|
name: string;
|
|
70
118
|
version?: string;
|
|
71
119
|
description?: string;
|
|
120
|
+
|
|
72
121
|
author?: Record<string, unknown>;
|
|
73
122
|
tools?: string;
|
|
74
123
|
listeners?: string;
|
|
75
124
|
commands?: string;
|
|
76
125
|
userConfig?: PluginUserConfigField[];
|
|
77
|
-
|
|
126
|
+
|
|
127
|
+
ui?: PluginUiManifest;
|
|
78
128
|
}
|
|
79
129
|
export declare const PLUGIN_NAME_PATTERN: RegExp;
|
|
80
130
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const l=/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/,
|
|
1
|
+
const R=2,S=["puzzle","github"];function $(n){return S.includes(n)?!0:!n||n.includes("..")||n.includes("\\")||/^\//.test(n)?!1:n.startsWith("ui/")&&/\.(svg|png)$/i.test(n)}function c(n){return!n||n.includes("..")||n.includes("\\")||/^\//.test(n)?!1:n.startsWith("ui/")}const m=new Set(["icon","status","view"]),w=new Set(["tool","persistent","hideWhenZero","pollIntervalMs","title"]),E=new Set(["entry","title","assetsRoot","invoke","defaultRoute"]);function _(n){if(n===void 0)return null;if(!o(n))return"plugin.json: ui \u987B\u4E3A\u5BF9\u8C61";const t=l("plugin.json: ui",n,m);if(t)return t;if(n.icon!==void 0&&(typeof n.icon!="string"||!$(n.icon)))return"plugin.json: ui.icon \u975E\u6CD5\uFF08\u5185\u7F6E\u540D puzzle/github \u6216 ui/ \u4E0B\u7684 svg/png \u76F8\u5BF9\u8DEF\u5F84\uFF09";if(n.status!==void 0){if(!o(n.status))return"plugin.json: ui.status \u987B\u4E3A\u5BF9\u8C61";const r=l("plugin.json: ui.status",n.status,w);if(r)return r;if(typeof n.status.tool!="string"||!a.test(n.status.tool))return"plugin.json: ui.status.tool \u975E\u6CD5\uFF08\u987B\u4E3A\u672C\u63D2\u4EF6 tools \u4E2D\u7684\u5DE5\u5177\u540D\uFF09";if(n.status.pollIntervalMs!==void 0&&(typeof n.status.pollIntervalMs!="number"||n.status.pollIntervalMs<5e3||n.status.pollIntervalMs>3e5))return"plugin.json: ui.status.pollIntervalMs \u987B\u4E3A 5000~300000 \u6BEB\u79D2";if(n.status.persistent!==void 0&&typeof n.status.persistent!="boolean")return"plugin.json: ui.status.persistent \u987B\u4E3A\u5E03\u5C14";if(n.status.hideWhenZero!==void 0&&typeof n.status.hideWhenZero!="boolean")return"plugin.json: ui.status.hideWhenZero \u987B\u4E3A\u5E03\u5C14";if(n.status.title!==void 0&&typeof n.status.title!="string")return"plugin.json: ui.status.title \u987B\u4E3A\u5B57\u7B26\u4E32"}if(n.view!==void 0){if(!o(n.view))return"plugin.json: ui.view \u987B\u4E3A\u5BF9\u8C61";const r=l("plugin.json: ui.view",n.view,E);if(r)return r;if(typeof n.view.entry!="string"||!c(n.view.entry))return"plugin.json: ui.view.entry \u975E\u6CD5\uFF08\u987B\u4E3A ui/ \u4E0B\u7684\u76F8\u5BF9\u8DEF\u5F84\uFF09";if(!/\.(html)$/i.test(n.view.entry))return"plugin.json: ui.view.entry \u987B\u4E3A .html \u6587\u4EF6";if(typeof n.view.title!="string"||!n.view.title.trim())return"plugin.json: ui.view.title \u5FC5\u586B\uFF08\u529F\u80FD\u683C\u6309\u94AE/\u5F39\u7A97\u6807\u9898\uFF09";if(n.view.title.length>64)return"plugin.json: ui.view.title \u8FC7\u957F\uFF08\u226464\uFF09";if(n.view.assetsRoot!==void 0&&(typeof n.view.assetsRoot!="string"||!c(n.view.assetsRoot)))return"plugin.json: ui.view.assetsRoot \u975E\u6CD5\uFF08\u987B\u4E3A ui/ \u4E0B\u7684\u76F8\u5BF9\u8DEF\u5F84\uFF09";if(!Array.isArray(n.view.invoke))return"plugin.json: ui.view.invoke \u987B\u4E3A\u5DE5\u5177\u540D\u6570\u7EC4";for(const i of n.view.invoke)if(typeof i!="string"||!a.test(i))return`plugin.json: ui.view.invoke \u542B\u975E\u6CD5\u5DE5\u5177\u540D: ${String(i)}`;if(n.view.defaultRoute!==void 0&&(typeof n.view.defaultRoute!="string"||n.view.defaultRoute.length>256))return"plugin.json: ui.view.defaultRoute \u987B\u4E3A \u2264256 \u5B57\u7B26\u7684\u5B57\u7B26\u4E32"}return null}const a=/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/,d=/^[a-z0-9][a-z0-9-]*$/,A=["system.","aws-","acode-"],h=/[^\x20-\x7E]/,j=new Set(["schemaVersion","name","version","description","author","tools","listeners","commands","userConfig","ui"]),v=new Set(["key","type","required","sensitive","description","default"]),I=new Set(["type","command","shell","stdin","timeoutMs","module","export"]),N=new Set(["name","title","description","inputSchema","handler"]),L=new Set(["event","description","transport","delivery","priority","matcher","dedupKey","toolHints","summary"]),O=new Set(["type","command","intervalMs","restartBackoffMs"]);function l(n,t,r){if(!o(t))return null;const i=Object.keys(t).filter(e=>!r.has(e));return i.length===0?null:`${n}: \u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${i.map(e=>`"${e}"`).join("\u3001")}\uFF08\u5141\u8BB8: ${[...r].join("\u3001")}\uFF09`}function o(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function p(n,t){let r=n;for(const i of t.split(".")){if(!o(r))return;r=r[i]}return r}function M(n,t){return n.length<=t?n:`${n.slice(0,t)}\u2026(\u622A\u65AD,\u5171 ${n.length} \u5B57\u7B26)`}function g(n,t,r="params"){if(!o(n))return[];const i=[],e=n.type;if(typeof e=="string"&&!k(e,t))return[`${r}: \u671F\u671B ${e}\uFF0C\u5B9E\u9645 ${U(t)}`];if(Array.isArray(n.enum)&&n.enum.length>0&&(n.enum.some(u=>JSON.stringify(u)===JSON.stringify(t))||i.push(`${r}: \u503C ${JSON.stringify(t)} \u4E0D\u5728\u679A\u4E3E ${JSON.stringify(n.enum)} \u5185`)),e==="object"&&o(t)){const s=o(n.properties)?n.properties:{},u=Array.isArray(n.required)?n.required:[];for(const f of u)(!(f in t)||t[f]===void 0)&&i.push(`${r}.${f}: \u5FC5\u586B\u53C2\u6570\u7F3A\u5931`);for(const[f,y]of Object.entries(s))f in t&&t[f]!==void 0&&i.push(...g(y,t[f],`${r}.${f}`));if(n.additionalProperties===!1)for(const f of Object.keys(t))f in s||i.push(`${r}.${f}: \u4E0D\u5141\u8BB8\u7684\u672A\u77E5\u53C2\u6570\uFF08schema additionalProperties=false\uFF09`)}return e==="array"&&Array.isArray(t)&&o(n.items)&&t.forEach((s,u)=>{i.push(...g(n.items,s,`${r}[${u}]`))}),i}function k(n,t){switch(n){case"string":return typeof t=="string";case"number":return typeof t=="number"&&Number.isFinite(t);case"boolean":return typeof t=="boolean";case"object":return o(t);case"array":return Array.isArray(t);case"null":return t===null;default:return!0}}function U(n){return n===null?"null":Array.isArray(n)?"array":typeof n}function T(n){return typeof n=="string"&&n.length>0&&n.length<=64&&d.test(n)}function b(n){if(n.schemaVersion!==2)return'plugin.json: \u7F3A\u5C11\u6216\u4E0D\u652F\u6301\u7684 schemaVersion\uFF08\u987B\u4E3A 2\uFF09\uFF1B\u8BF7\u5728 plugin.json \u9876\u5C42\u58F0\u660E "schemaVersion": 2';const t=l("plugin.json",n,j);if(t)return t;const r=n.name;if(typeof r!="string"||!d.test(r))return`plugin.json: name \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF09: ${String(r)}`;if(r.length>64)return"plugin.json: name \u8FC7\u957F\uFF08\u226464\uFF09";for(const e of A)if(r===e.replace(/\.$/,"")||r.startsWith(e))return`plugin.json: name \u4F7F\u7528\u4E86\u4FDD\u7559\u524D\u7F00 ${e}`;if(h.test(r))return"plugin.json: name \u542B\u975E ASCII \u5B57\u7B26\uFF08\u540C\u5F62\u5B57\u9632\u62A4\uFF09";if(n.version!==void 0&&typeof n.version!="string")return"plugin.json: version \u987B\u4E3A\u5B57\u7B26\u4E32";if(n.userConfig!==void 0){if(!Array.isArray(n.userConfig))return"plugin.json: userConfig \u987B\u4E3A\u6570\u7EC4";for(const e of n.userConfig){if(!o(e))return"plugin.json: userConfig \u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";const s=l(`plugin.json: userConfig["${String(e.key)}"]`,e,v);if(s)return s;if(typeof e.key!="string"||!e.key.trim())return"plugin.json: userConfig \u6761\u76EE\u7F3A\u5C11 key"}}const i=_(n.ui);return i||null}function P(n){if(!o(n))return"\u5DE5\u5177\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";const t=n.name;if(typeof t!="string"||!a.test(t))return`\u5DE5\u5177 name \u975E\u6CD5: ${String(t)}`;const r=l(`\u5DE5\u5177 ${t}`,n,N);if(r)return r;if(typeof n.description!="string"||!n.description.trim())return`\u5DE5\u5177 ${t} \u7F3A\u5C11 description`;const i=n.handler;if(!o(i))return`\u5DE5\u5177 ${t} \u7F3A\u5C11 handler`;const e=l(`\u5DE5\u5177 ${t} handler`,i,I);if(e)return e;const s=i.type;return s!=="process"&&s!=="module"?`\u5DE5\u5177 ${t} handler.type \u975E\u6CD5: ${String(s)}\uFF08\u5F53\u524D\u652F\u6301 process / module\uFF09`:s==="process"&&typeof i.command!="string"?`\u5DE5\u5177 ${t} \u7684 process handler \u7F3A\u5C11 command`:s==="module"&&typeof i.module!="string"?`\u5DE5\u5177 ${t} \u7684 module handler \u7F3A\u5C11 module`:n.inputSchema!==void 0&&!o(n.inputSchema)?`\u5DE5\u5177 ${t} inputSchema \u987B\u4E3A\u5BF9\u8C61`:null}function W(n){if(!o(n))return"\u76D1\u542C\u5668\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";if(typeof n.event!="string"||!n.event.trim())return"\u76D1\u542C\u5668\u7F3A\u5C11 event";const t=l(`\u76D1\u542C\u5668 ${String(n.event)}`,n,L);if(t)return t;const r=n.transport;if(!o(r))return`\u76D1\u542C\u5668 ${String(n.event)} \u7F3A\u5C11 transport`;const i=l(`\u76D1\u542C\u5668 ${String(n.event)} transport`,r,O);if(i)return i;const e=r.type;if(e!=="command-stream"&&e!=="poll-command"&&e!=="http-callback")return`\u76D1\u542C\u5668 ${String(n.event)} transport.type \u975E\u6CD5: ${String(e)}`;if((e==="command-stream"||e==="poll-command")&&typeof r.command!="string")return`\u76D1\u542C\u5668 ${String(n.event)} \u7684 ${e} transport \u7F3A\u5C11 command`;const s=n.delivery;return s!==void 0&&s!=="poll"?`\u76D1\u542C\u5668 ${String(n.event)} delivery \u975E\u6CD5: ${String(s)}\uFF08\u5F53\u524D\u4EC5\u652F\u6301 "poll"\uFF09`:null}function x(n,t){return n.trim().split(/\s+/).map(r=>{const i=/^\{\{(.+)\}\}$/.exec(r);if(i){const e=p(t,i[1].trim());return e==null?"":typeof e=="string"?e:JSON.stringify(e)}return r.replace(/\{\{(.+?)\}\}/g,(e,s)=>{const u=p(t,s.trim());return u==null?"":typeof u=="string"?u:JSON.stringify(u)})}).filter(r=>r!=="")}function C(n,t){return n.replace(/\{\{(.+?)\}\}/g,(r,i)=>{let e=p(t,i.trim());return e===void 0&&!i.trim().startsWith("params.")&&(e=p(t,`data.${i.trim()}`)),e==null?"":typeof e=="string"?e:JSON.stringify(e)})}function D(n){try{return JSON.stringify(n)??"null"}catch{return"(\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u4E8B\u4EF6\u8D1F\u8F7D)"}}export{R as PLUGIN_MANIFEST_SCHEMA_VERSION,d as PLUGIN_NAME_PATTERN,S as PLUGIN_UI_BUILTIN_ICONS,p as getPath,o as isRecord,T as isValidPluginName,$ as isValidUiIcon,c as isValidUiPath,x as renderCommandArgv,C as renderTemplate,D as safeStableStringify,M as truncate,g as validateAgainstSchema,W as validateListenerDef,b as validateManifest,P as validateToolDef,_ as validateUiManifest};
|
|
2
2
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import type { ChildProcess } from "node:child_process";
|
|
12
12
|
import type { PluginStatus } from "@agentswork/acode";
|
|
13
13
|
import type { PluginEventBuffer } from "./plugin-event-buffer.js";
|
|
14
|
-
import type { PluginListenerDef, PluginToolDef, PluginUserConfigField } from "./plugin-manifest.js";
|
|
14
|
+
import type { PluginListenerDef, PluginToolDef, PluginUiManifest, PluginUserConfigField } from "./plugin-manifest.js";
|
|
15
15
|
export interface ListenerRuntime {
|
|
16
16
|
def: PluginListenerDef;
|
|
17
17
|
child?: ChildProcess;
|
|
@@ -35,8 +35,15 @@ export interface LoadedPlugin {
|
|
|
35
35
|
tools: PluginToolDef[];
|
|
36
36
|
listeners: PluginListenerDef[];
|
|
37
37
|
commandDir?: string;
|
|
38
|
+
|
|
39
|
+
ui?: PluginUiManifest;
|
|
38
40
|
userConfig: PluginUserConfigField[];
|
|
39
41
|
configValues: Record<string, string>;
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
configuredKeys?: ReadonlySet<string>;
|
|
40
47
|
status: PluginStatus;
|
|
41
48
|
error?: string;
|
|
42
49
|
runtimes: ListenerRuntime[];
|
|
@@ -74,17 +81,9 @@ export interface AgentState {
|
|
|
74
81
|
export interface SharedPluginEntry {
|
|
75
82
|
pluginName: string;
|
|
76
83
|
|
|
77
|
-
zipFile?: string;
|
|
78
|
-
|
|
79
84
|
fileName?: string;
|
|
80
85
|
|
|
81
86
|
defaultEnabled?: boolean;
|
|
82
87
|
|
|
83
88
|
sha256?: string;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
removed?: boolean;
|
|
90
89
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{pluginManager as t}from"./plugin-manager.js";import{createServerNotifier as n,PluginUiStatusWatcher as l}from"./plugin-ui-status.js";const u=new l({invokeTool:(i,e,o,r)=>t.invokeTool(i,e,o,r),listPlugins:i=>t.list(i),notifyServer:n()});export{u as pluginUiStatusWatcher};
|
|
2
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export interface PluginUiStatusEntry {
|
|
18
|
+
pluginName: string;
|
|
19
|
+
|
|
20
|
+
status: Record<string, unknown> | null;
|
|
21
|
+
|
|
22
|
+
updatedAt: number;
|
|
23
|
+
|
|
24
|
+
error?: string;
|
|
25
|
+
|
|
26
|
+
revision: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type AgentUiStatusSnapshot = Record<string, PluginUiStatusEntry>;
|
|
30
|
+
|
|
31
|
+
export interface WatcherDeps {
|
|
32
|
+
|
|
33
|
+
invokeTool: (agentId: string, plugin: string, tool: string, params: Record<string, unknown>) => Promise<{
|
|
34
|
+
ok: boolean;
|
|
35
|
+
result?: unknown;
|
|
36
|
+
error?: string;
|
|
37
|
+
}>;
|
|
38
|
+
|
|
39
|
+
listPlugins: (agentId: string) => Promise<Array<{
|
|
40
|
+
name: string;
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
frontend?: {
|
|
43
|
+
status?: {
|
|
44
|
+
tool: string;
|
|
45
|
+
pollIntervalMs?: number;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}>>;
|
|
49
|
+
|
|
50
|
+
notifyServer: (agentId: string, states: AgentUiStatusSnapshot) => Promise<void>;
|
|
51
|
+
|
|
52
|
+
setInterval?: (fn: () => void, ms: number) => ReturnType<typeof setInterval>;
|
|
53
|
+
clearInterval?: (handle: unknown) => void;
|
|
54
|
+
now?: () => number;
|
|
55
|
+
}
|
|
56
|
+
export declare class PluginUiStatusWatcher {
|
|
57
|
+
private readonly deps;
|
|
58
|
+
|
|
59
|
+
private readonly cache;
|
|
60
|
+
|
|
61
|
+
private readonly nextRunAt;
|
|
62
|
+
|
|
63
|
+
private readonly collecting;
|
|
64
|
+
private timer;
|
|
65
|
+
private started;
|
|
66
|
+
constructor(deps: WatcherDeps);
|
|
67
|
+
start(): void;
|
|
68
|
+
stop(): void;
|
|
69
|
+
|
|
70
|
+
snapshot(agentId: string): AgentUiStatusSnapshot;
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
onPluginChanged(agentId: string, pluginName: string, removed: boolean): Promise<void>;
|
|
77
|
+
|
|
78
|
+
clearAgent(agentId: string): void;
|
|
79
|
+
|
|
80
|
+
scan(): Promise<void>;
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
refreshAgent(agentId: string): Promise<void>;
|
|
86
|
+
stopAgent(agentId: string): Promise<void>;
|
|
87
|
+
private collectDue;
|
|
88
|
+
private collectOne;
|
|
89
|
+
private safeNotify;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
export declare function createServerNotifier(): (agentId: string, states: AgentUiStatusSnapshot) => Promise<void>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createLogger as R}from"../utils/logger.js";const w=R("plugin-ui-status"),M=3e4,k=5e3,T=5e3;class g{constructor(t){this.cache=new Map,this.nextRunAt=new Map,this.collecting=new Set,this.timer=null,this.started=!1,this.deps=t}start(){if(this.started)return;this.started=!0;const t=this.deps.setInterval??((e,s)=>{const r=setInterval(e,s);return r.unref?.(),r});this.timer=t(()=>void this.scan(),T)}stop(){this.started=!1,this.timer&&((this.deps.clearInterval??clearInterval)(this.timer),this.timer=null),this.cache.clear(),this.nextRunAt.clear(),this.collecting.clear()}snapshot(t){const e=this.cache.get(t);return e?structuredClone(e):{}}async onPluginChanged(t,e,s){const r=this.cache.get(t);if(s){if(r&&r[e]){const i={...r};delete i[e],this.cache.set(t,i),await this.safeNotify(t,i)}this.nextRunAt.get(t)?.delete(e);return}if(r?.[e]){const i={...r};delete i[e],this.cache.set(t,i)}const o=this.nextRunAt.get(t);o&&o.delete(e),await this.refreshAgent(t)}clearAgent(t){this.cache.delete(t),this.nextRunAt.delete(t)}async scan(){const t=(this.deps.now??Date.now)(),e=Array.from(this.nextRunAt.keys());for(const s of e)await this.collectDue(s,t)}async refreshAgent(t){let e;try{e=await this.deps.listPlugins(t)}catch(n){w.warn("[plugin-ui-status] \u5217\u51FA\u63D2\u4EF6\u5931\u8D25 agent=%s: %s",t,x(n));return}const s=(this.deps.now??Date.now)(),r=this.nextRunAt.get(t)??new Map,o=new Set;for(const n of e){const h=n.frontend?.status;!n.enabled||!h?.tool||(o.add(n.name),r.has(n.name)||r.set(n.name,0))}this.nextRunAt.set(t,r);const i=this.cache.get(t);if(i){let n=!1;const h={...i};for(const a of Object.keys(h))o.has(a)||(delete h[a],r.delete(a),n=!0);n&&(this.cache.set(t,h),await this.safeNotify(t,h))}await this.collectDue(t,s)}async stopAgent(t){this.clearAgent(t)}async collectDue(t,e){const s=this.nextRunAt.get(t);if(!(!s||s.size===0)&&!this.collecting.has(t)){this.collecting.add(t);try{for(const[r,o]of s)o>e||await this.collectOne(t,r)}finally{this.collecting.delete(t)}}}async collectOne(t,e){const s=this.nextRunAt.get(t),r=s?.get(e);let o;try{const A=(await this.deps.listPlugins(t)).find(y=>y.name===e),u=A?.frontend?.status;if(!A||!A.enabled||!u?.tool){const y=this.cache.get(t);if(y?.[e]){const v={...y};delete v[e],this.cache.set(t,v),s?.delete(e),await this.safeNotify(t,v)}return}o={agentId:t,pluginName:e,tool:u.tool,intervalMs:typeof u.pollIntervalMs=="number"&&u.pollIntervalMs>=k?u.pollIntervalMs:M}}catch(f){w.warn("[plugin-ui-status] \u8BFB\u53D6\u63D2\u4EF6\u58F0\u660E\u5931\u8D25 %s/%s: %s",t,e,x(f));return}const i=await this.deps.invokeTool(t,e,o.tool,{}),n=(this.deps.now??Date.now)(),h=i.ok?o.intervalMs:Math.min(o.intervalMs,3e4);s?.set(e,n+h);const a=this.cache.get(t)??{},l=a[e];if(!i.ok){const f={...a,[e]:{pluginName:e,status:l?.status??null,updatedAt:n,error:i.error??"\u72B6\u6001\u5DE5\u5177\u6267\u884C\u5931\u8D25",revision:l?.revision??0}};this.cache.set(t,f);return}const d=_(i.result),p=JSON.stringify(d),S=l&&l.status?JSON.stringify(l.status):null;if(p===S){l&&(a[e]={...l,updatedAt:n,error:void 0});return}const m={...a,[e]:{pluginName:e,status:d,updatedAt:n,revision:(l?.revision??0)+1}};this.cache.set(t,m),await this.safeNotify(t,m)}async safeNotify(t,e){try{await this.deps.notifyServer(t,structuredClone(e))}catch(s){w.warn("[plugin-ui-status] \u72B6\u6001\u56DE\u8C03\u5931\u8D25\uFF08\u4E0B\u8F6E\u53D8\u5316\u91CD\u8BD5\uFF09agent=%s: %s",t,x(s))}}}function _(c){return!c||typeof c!="object"||Array.isArray(c)?null:c}function x(c){return c instanceof Error?c.message:String(c)}function O(){return async(c,t)=>{const{resolveSchedulerBaseUrlFrom:e}=await import("../config.js"),{getRuntimeAccessToken:s}=await import("./runtime-binding.js"),{requestRuntimeAccessTokenRefreshForServer:r,resolveTargetIdentityFromToken:o}=await import("./auto-register.js"),i=e(void 0).url;let n=s(i)??s()??void 0;if(!n){w.warn("[plugin-ui-status] \u65E0 runtime token\uFF0C\u8DF3\u8FC7\u72B6\u6001\u56DE\u8C03");return}const h=JSON.stringify({agentId:c,states:t}),a=async()=>{await fetch(`${i}/api/runtime/callback/plugin-ui-status`,{method:"POST",headers:{"Content-Type":"application/json","X-Runtime-Token":n},body:h,signal:AbortSignal.timeout(1e4)})};try{await a()}catch(l){if(l?.response?.status===401){const p=o(n)?.identityKey;if((await r(i,p)).success){n=s(i)??s()??n,await a();return}}throw l}}}export{g as PluginUiStatusWatcher,O as createServerNotifier};
|
|
2
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export interface WecomVerifyParams {
|
|
13
|
+
msg_signature: string;
|
|
14
|
+
timestamp: string;
|
|
15
|
+
nonce: string;
|
|
16
|
+
echostr: string;
|
|
17
|
+
}
|
|
18
|
+
export interface WecomMessageParams extends WecomVerifyParams {
|
|
19
|
+
|
|
20
|
+
xml?: string;
|
|
21
|
+
}
|
|
22
|
+
interface WecomCrypto {
|
|
23
|
+
token: string;
|
|
24
|
+
aesKey: Buffer;
|
|
25
|
+
receiveId: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function verifySignature(crypto: WecomCrypto, timestamp: string, nonce: string, encrypt: string): string;
|
|
28
|
+
|
|
29
|
+
export declare function decryptMessage(crypto: WecomCrypto, ciphertextB64: string): {
|
|
30
|
+
message: Buffer;
|
|
31
|
+
receiveId: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export declare function extractEncrypt(xml: string): string | null;
|
|
35
|
+
|
|
36
|
+
export declare function parseWecomXml(xml: string): Record<string, string>;
|
|
37
|
+
export interface WecomCallbackResult {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
status: number;
|
|
40
|
+
|
|
41
|
+
body: string;
|
|
42
|
+
}
|
|
43
|
+
export declare class PluginWecomCallbackService {
|
|
44
|
+
private readonly pluginManager;
|
|
45
|
+
constructor(pluginManager: {
|
|
46
|
+
hydrateForAdmin: (agentId: string) => Promise<{
|
|
47
|
+
plugins: Map<string, {
|
|
48
|
+
dir: string;
|
|
49
|
+
enabled: boolean;
|
|
50
|
+
configValues: Record<string, string>;
|
|
51
|
+
}>;
|
|
52
|
+
}>;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
private resolveCrypto;
|
|
56
|
+
|
|
57
|
+
verify(agentId: string, params: WecomVerifyParams, pluginName?: string): Promise<WecomCallbackResult>;
|
|
58
|
+
|
|
59
|
+
receive(agentId: string, params: WecomMessageParams, xmlBody: string, pluginName?: string): Promise<WecomCallbackResult>;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{createDecipheriv as w,createHash as b}from"node:crypto";import{appendFileSync as E,existsSync as v,mkdirSync as k}from"node:fs";import{join as m}from"node:path";import{createLogger as C}from"../utils/logger.js";const i=C("plugin-wecom-callback"),I="im-bot";function f(t,e,r,o){const s=[t.token,e,r,o].sort();return b("sha1").update(s.join("")).digest("hex")}function d(t,e){const r=Buffer.from(e,"base64"),o=w("aes-256-cbc",t.aesKey,t.aesKey.subarray(0,16));o.setAutoPadding(!1);const s=Buffer.concat([o.update(r),o.final()]),n=s[s.length-1];if(n<1||n>32)throw new Error(`\u975E\u6CD5 PKCS#7 \u586B\u5145: ${n}`);const c=s.subarray(0,s.length-n);if(c.length<20)throw new Error("\u89E3\u5BC6\u5185\u5BB9\u8FC7\u77ED");const a=c.readUInt32BE(16);if(a+20>c.length)throw new Error(`\u6D88\u606F\u957F\u5EA6\u8D8A\u754C: ${a} > ${c.length-20}`);return{message:c.subarray(20,20+a),receiveId:c.subarray(20+a).toString("utf8")}}function S(t){const e=Buffer.from(`${t}=`,"base64");if(e.length!==32)throw new Error(`EncodingAESKey \u975E\u6CD5\uFF08Base64 \u89E3\u7801\u540E\u987B\u4E3A 32 \u5B57\u8282\uFF0C\u5B9E\u9645 ${e.length}\uFF09`);return e}function p(t){const e=/<Encrypt><!\[CDATA\[([\s\S]*?)\]\]><\/Encrypt>|<Encrypt>([\s\S]*?)<\/Encrypt>/.exec(t);return e?.[1]??e?.[2]??null}function x(t){const e={},r=/<(\w+)><!\[CDATA\[([\s\S]*?)\]\]><\/\1>|<(\w+)>([^<]*)<\/\3>/g;let o;for(;(o=r.exec(t))!==null;){const s=o[1]??o[3],n=o[2]??o[4]??"";s&&(e[s]=n)}return e}function _(t){const e=String(t.MsgType??"");if(e!=="text"&&e!=="image")return null;const r=t.ChatType==="group"||t.ChatId?t.ChatType??"single":"single",o=e==="text"?String(t.Content??""):"";return e==="text"&&!o?null:{event:"wecom.message",data:{msgId:t.MsgId??`${t.FromUserName??"unknown"}-${t.CreateTime??Date.now()}`,msgType:e,content:o,fromUser:t.FromUserName??"",chatId:t.ChatId??t.FromUserName??"",chatType:r,groupName:t.ChatName??void 0,createTime:Number(t.CreateTime)||Math.floor(Date.now()/1e3)}}}class O{constructor(e){this.pluginManager=e}async resolveCrypto(e,r=I){const s=(await this.pluginManager.hydrateForAdmin(e)).plugins.get(r);if(!s||!s.enabled)return{error:`\u63D2\u4EF6 ${r} \u672A\u5B89\u88C5\u6216\u672A\u542F\u7528`};const n=s.configValues??{},c=n.WECOM_TOKEN,a=n.WECOM_ENCODING_AES_KEY;if(!c||!a)return{error:"\u63D2\u4EF6\u7F3A\u5C11 WECOM_TOKEN / WECOM_ENCODING_AES_KEY \u914D\u7F6E"};let u;try{u=S(a)}catch(g){return{error:g instanceof Error?g.message:String(g)}}return{crypto:{token:c,aesKey:u,receiveId:n.WECOM_CORP_ID??""},pluginDir:s.dir}}async verify(e,r,o){const s=await this.resolveCrypto(e,o);if(!s.crypto)return{ok:!1,status:400,body:s.error??"unconfigured"};const n=s.crypto;if(!r.msg_signature||!r.echostr)return{ok:!1,status:400,body:"msg_signature/echostr is required"};try{if(f(n,r.timestamp??"",r.nonce??"",r.echostr)!==r.msg_signature)return i.warn("[wecom-callback] URL \u9A8C\u8BC1\u7B7E\u540D\u4E0D\u7B26 agentId=%s",e),{ok:!1,status:403,body:"signature mismatch"};const a=d(n,r.echostr);return n.receiveId&&a.receiveId!==n.receiveId?(i.warn("[wecom-callback] URL \u9A8C\u8BC1 receiveId \u4E0D\u7B26: %s",a.receiveId),{ok:!1,status:403,body:"receiveId mismatch"}):{ok:!0,status:200,body:a.message.toString("utf8")}}catch(c){return i.warn("[wecom-callback] URL \u9A8C\u8BC1\u5931\u8D25 agentId=%s: %s",e,c instanceof Error?c.message:c),{ok:!1,status:400,body:"verify failed"}}}async receive(e,r,o,s){const n=await this.resolveCrypto(e,s);if(!n.crypto||!n.pluginDir)return i.warn("[wecom-callback] \u62D2\u7EDD\u6D88\u606F\u56DE\u8C03 agentId=%s: %s",e,n.error),{ok:!1,status:400,body:"success"};const c=n.crypto,a=p(o)??p(r.xml??"");if(!a)return{ok:!1,status:400,body:"success"};try{if(f(c,r.timestamp??"",r.nonce??"",a)!==r.msg_signature)return i.warn("[wecom-callback] \u6D88\u606F\u7B7E\u540D\u4E0D\u7B26 agentId=%s",e),{ok:!1,status:403,body:"success"};const g=d(c,a);if(c.receiveId&&g.receiveId!==c.receiveId)return i.warn("[wecom-callback] \u6D88\u606F receiveId \u4E0D\u7B26: %s",g.receiveId),{ok:!0,status:200,body:"success"};const h=x(g.message.toString("utf8")),l=_(h);if(l){const y=m(n.pluginDir,"data",e);v(y)||k(y,{recursive:!0}),E(m(y,"inbox-wecom.jsonl"),`${JSON.stringify(l)}
|
|
2
|
+
`,"utf8"),i.info("[wecom-callback] \u5165\u7AD9\u6D88\u606F\u5DF2\u5165\u6536\u4EF6\u7BB1 agentId=%s msgId=%s",e,l.data.msgId)}return{ok:!0,status:200,body:"success"}}catch(u){return i.warn("[wecom-callback] \u6D88\u606F\u5904\u7406\u5931\u8D25 agentId=%s: %s",e,u instanceof Error?u.message:u),{ok:!0,status:200,body:"success"}}}}export{O as PluginWecomCallbackService,d as decryptMessage,p as extractEncrypt,x as parseWecomXml,f as verifySignature};
|
|
3
|
+
|