aws-runtime-bridge 1.9.189 → 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 +111 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts +13 -0
- package/dist/adapter/AcodeSdkAdapter.js +3 -3
- package/dist/config.d.ts +0 -31
- package/dist/config.js +2 -2
- package/dist/index.js +1 -1
- 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 +25 -25
- package/dist/scripts/migrate-user-scopes.js +1 -1
- package/dist/services/agent-capability-sync.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/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/package/acode/dist/plugin-types.d.ts +41 -1
- package/package/acode/dist/runtime.d.ts +17 -0
- package/package/acode/dist/runtime.js +20 -20
- package/package/aws-client-agent-mcp/dist/mcp-server.js +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +15 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +35 -5
- package/package/aws-client-agent-mcp/dist/tool-groups.js +2 -2
- package/package.json +1 -1
|
@@ -8,36 +8,22 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
import type { PluginLoader } from "./plugin-loader.js";
|
|
12
11
|
import type { SharedPluginEntry } from "./plugin-runtime.js";
|
|
13
12
|
export declare class PluginDeploymentStore {
|
|
14
13
|
private readonly rootDir;
|
|
15
14
|
private readonly runtimeRootDir;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
private readonly legacyRootDir?;
|
|
19
|
-
constructor(rootDir: string, runtimeRootDir: string | undefined, loader: PluginLoader,
|
|
20
|
-
|
|
21
|
-
legacyRootDir?: string | undefined);
|
|
15
|
+
constructor(rootDir: string, runtimeRootDir: string | undefined);
|
|
22
16
|
deployedDir(): string;
|
|
23
17
|
deployedManifestFile(): string;
|
|
24
18
|
|
|
25
|
-
private legacyDeployedDir;
|
|
26
|
-
private legacyDeployedManifestFile;
|
|
27
|
-
|
|
28
19
|
|
|
29
20
|
|
|
30
21
|
|
|
31
22
|
|
|
32
23
|
deployedPluginDir(name: string): string;
|
|
33
24
|
|
|
34
|
-
private legacySharedDir;
|
|
35
|
-
|
|
36
25
|
deployedPluginHealthy(name: string): boolean;
|
|
37
26
|
|
|
38
|
-
private legacyPluginHealthy;
|
|
39
|
-
|
|
40
|
-
|
|
41
27
|
|
|
42
28
|
|
|
43
29
|
resolveDeployedSourceDir(name: string): string | null;
|
|
@@ -45,22 +31,10 @@ export declare class PluginDeploymentStore {
|
|
|
45
31
|
|
|
46
32
|
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
private readMergedManifest;
|
|
50
|
-
|
|
51
|
-
hasLegacyManifestEntry(pluginName: string): boolean;
|
|
52
|
-
|
|
53
|
-
activeSharedManifest(): SharedPluginEntry[];
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
34
|
readSharedManifest(): SharedPluginEntry[];
|
|
59
35
|
writeSharedManifest(entries: SharedPluginEntry[]): void;
|
|
60
36
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
deployPluginZipToPersistentDir(zipBuffer: Buffer, expectedName?: string): Promise<SharedPluginEntry | null>;
|
|
37
|
+
updateSharedManifest(mutator: (entries: SharedPluginEntry[]) => SharedPluginEntry[]): Promise<void>;
|
|
64
38
|
|
|
65
39
|
|
|
66
40
|
|
|
@@ -69,8 +43,6 @@ export declare class PluginDeploymentStore {
|
|
|
69
43
|
|
|
70
44
|
removeDeployedDir(pluginName: string): void;
|
|
71
45
|
|
|
72
|
-
updateSharedManifest(mutator: (entries: SharedPluginEntry[]) => SharedPluginEntry[]): Promise<void>;
|
|
73
|
-
|
|
74
46
|
|
|
75
47
|
|
|
76
48
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{cpSync as
|
|
2
|
-
`,"utf8")}async
|
|
1
|
+
import{cpSync as y,existsSync as o,lstatSync as p,mkdirSync as c,readFileSync as D,readdirSync as g,renameSync as d,rmSync as u,symlinkSync as m,writeFileSync as S}from"node:fs";import{join as n,resolve as f}from"node:path";import{getBridgeInstancePluginsDir as P}from"../config.js";import{sanitizeAgentId as v,withFileLock as w}from"../utils/file-utils.js";import{createLogger as M}from"../utils/logger.js";import{isValidPluginName as h,PLUGIN_NAME_PATTERN as k}from"./plugin-manifest.js";const l=M("plugin-manager"),F=process.platform==="win32"?"junction":"dir",a=3;class O{constructor(r,i){this.rootDir=r,this.runtimeRootDir=i}deployedDir(){return n(this.rootDir,"deployed")}deployedManifestFile(){return n(this.deployedDir(),"manifest.json")}deployedPluginDir(r){if(!h(r))throw new Error(`\u975E\u6CD5\u63D2\u4EF6\u540D\uFF08\u4EC5\u5141\u8BB8 ${k}\uFF09: ${JSON.stringify(r)}`);const i=n(this.deployedDir(),r),e=f(this.deployedDir());if(f(i)!==n(e,r))throw new Error(`\u63D2\u4EF6\u76EE\u5F55\u8D8A\u51FA\u90E8\u7F72\u533A: ${r}`);return i}deployedPluginHealthy(r){return o(n(this.deployedPluginDir(r),"plugin.json"))}resolveDeployedSourceDir(r){return this.deployedPluginHealthy(r)?this.deployedPluginDir(r):null}readSharedManifest(){try{if(!o(this.deployedManifestFile()))return[];const r=JSON.parse(D(this.deployedManifestFile(),"utf8"));if(r.version!==a)return l.warn("[shared] \u90E8\u7F72\u6E05\u5355\u7248\u672C %s \u975E %d\uFF08\u65E7\u683C\u5F0F\u4E0D\u518D\u517C\u5BB9\uFF09\uFF0C\u6309\u7A7A\u90E8\u7F72\u96C6\u5904\u7406\u2014\u2014\u8BF7\u91CD\u65B0\u90E8\u7F72\u63D2\u4EF6",String(r.version??"(\u7F3A\u5931)"),a),[];const i=[];for(const e of Array.isArray(r.plugins)?r.plugins:[])e&&h(e.pluginName)&&i.push(e);return i}catch{return[]}}writeSharedManifest(r){c(this.deployedDir(),{recursive:!0}),S(this.deployedManifestFile(),`${JSON.stringify({version:a,plugins:r},null,2)}
|
|
2
|
+
`,"utf8")}async updateSharedManifest(r){await w(this.deployedManifestFile(),async()=>{this.writeSharedManifest(r(this.readSharedManifest()))})}replaceDeployedDir(r,i){c(this.deployedDir(),{recursive:!0});const e=this.deployedPluginDir(r),t=n(this.deployedDir(),`.old-${r}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);o(e)&&d(e,t);try{y(i,e,{recursive:!0,filter:s=>!/[\\/]\.git([\\/]|$)/.test(s)&&!/[\\/]node_modules([\\/]|$)/.test(s)})}catch(s){throw o(t)&&(u(e,{recursive:!0,force:!0}),d(t,e)),s}o(t)&&u(t,{recursive:!0,force:!0})}removeDeployedDir(r){u(this.deployedPluginDir(r),{recursive:!0,force:!0})}runtimeDirFor(r){return this.runtimeRootDir?n(this.runtimeRootDir,v(r)):P(r)}runtimeLinkPath(r,i){return n(this.runtimeDirFor(r),i)}ensureRuntimeLink(r,i){const e=this.runtimeLinkPath(r,i),t=this.resolveDeployedSourceDir(i);if(!t)return!1;c(this.runtimeDirFor(r),{recursive:!0});try{if(o(e)){let s=!1;try{s=p(e).isSymbolicLink()}catch{s=!1}if(s)try{return g(e),!0}catch{}u(e,{recursive:!0,force:!0})}return m(t,e,F),l.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u5EFA\u7ACB: %s \u2192 %s",r,e,t),!0}catch(s){return l.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5931\u8D25\uFF08%s \u2192 %s\uFF09: %s",r,i,t,s instanceof Error?s.message:String(s)),!1}}removeRuntimeLink(r,i){const e=this.runtimeLinkPath(r,i);try{o(e)&&(u(e,{recursive:!0,force:!0}),l.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u79FB\u9664: %s",r,i))}catch(t){l.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u79FB\u9664\u5931\u8D25\uFF08%s\uFF09: %s",r,i,t instanceof Error?t.message:String(t))}}}export{O as PluginDeploymentStore};
|
|
3
3
|
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
import type { PluginInvokeContext, PluginToolInvocationResult, JsonObject } from "@agentswork/acode";
|
|
13
12
|
import type { PluginToolDef } from "./plugin-manifest.js";
|
|
14
13
|
import type { LoadedPlugin } from "./plugin-runtime.js";
|
|
@@ -17,4 +16,14 @@ export declare function executeHandler(agentId: string, plugin: LoadedPlugin, to
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export declare function resolveEffectiveConfigValues(plugin: LoadedPlugin, agentId?: string): Record<string, string>;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
20
29
|
export declare function buildPluginEnv(plugin: LoadedPlugin, agentId: string): NodeJS.ProcessEnv;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawn as T}from"node:child_process";import{join as _}from"node:path";import{pathToFileURL as O}from"node:url";import{loadDynamicModule as P}from"../utils/dynamic-import.js";import{BLOCKED_ENV_KEYS as
|
|
2
|
-
`):c.stdin?.end()}catch{}})}finally{clearTimeout(E),
|
|
1
|
+
import{spawn as T}from"node:child_process";import{join as _}from"node:path";import{pathToFileURL as O}from"node:url";import{loadDynamicModule as P}from"../utils/dynamic-import.js";import{BLOCKED_ENV_KEYS as k}from"../utils/env-safety.js";import{getPanelEnvVars as A}from"./panel-env.js";import{createLogger as C}from"../utils/logger.js";import{renderCommandArgv as b,truncate as N}from"./plugin-manifest.js";const S=C("plugin-manager"),g=256*1024,h=6e4,D=/^[A-Za-z_][A-Za-z0-9_]*$/;async function Y(o,e,n,s,i){const t=Date.now(),r=n.handler??{type:"process"};try{return r.type==="module"?{ok:!0,result:await $(e,r,s,o,i),durationMs:Date.now()-t}:!r.command||!r.command.trim()?{ok:!1,error:`\u5DE5\u5177 ${n.name} \u7684 process handler \u7F3A\u5C11 command`}:{ok:!0,result:await L(e,r,s,o,i),durationMs:Date.now()-t}}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a),durationMs:Date.now()-t}}}async function L(o,e,n,s,i){const t=e.command??"";if(e.shell===!0&&/\{\{.+?\}\}/.test(t))throw new Error('shell:true \u7684 handler \u4E0D\u652F\u6301 {{params.*}} \u63D2\u503C\uFF08\u547D\u4EE4\u6CE8\u5165\u98CE\u9669\uFF09\uFF1B\u8BF7\u6539\u7528 shell:false \u7684 argv \u6A21\u5F0F\u6216 stdin:"params"');const r=b(t,{params:n});if(r.length===0)throw new Error("process handler command \u4E3A\u7A7A");const a=typeof e.timeoutMs=="number"&&e.timeoutMs>0?e.timeoutMs:h,f=new AbortController,l=()=>f.abort();i?.signal?.addEventListener("abort",l,{once:!0});const E=setTimeout(()=>f.abort(),a);try{return await new Promise((u,y)=>{S.info("[plugin:%s] spawn: %s",o.name,r.join(" "));const c=T(r[0],r.slice(1),{cwd:o.dir,env:U(o,s),shell:e.shell===!0,windowsHide:!0,signal:f.signal});let d="",p="";c.stdin?.on("error",()=>{}),c.stdout?.on("data",m=>{d.length<g&&(d+=m.toString("utf8"))}),c.stderr?.on("data",m=>{p.length<g&&(p+=m.toString("utf8"))}),c.on("error",m=>{y(new Error(`\u63D2\u4EF6\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${m.message}`))}),c.on("close",m=>{const w=d.trim();if(m!==0){const M=N(p.trim()||w||"(\u65E0\u8F93\u51FA)",600);y(new Error(f.signal.aborted?`\u63D2\u4EF6\u8FDB\u7A0B\u8D85\u65F6(${a}ms)\u6216\u88AB\u4E2D\u6B62`:`\u63D2\u4EF6\u8FDB\u7A0B\u9000\u51FA\u7801 ${m}: ${M}`));return}if(!w){u({ok:!0});return}try{u(JSON.parse(w))}catch{u(w)}});try{e.stdin==="params"?c.stdin?.end(`${JSON.stringify(n)}
|
|
2
|
+
`):c.stdin?.end()}catch{}})}finally{clearTimeout(E),i?.signal?.removeEventListener("abort",l)}}async function $(o,e,n,s,i){if(!e.module)throw new Error("module handler \u7F3A\u5C11 module \u8DEF\u5F84");const t=_(o.dir,e.module);let r=o.moduleCache.get(t);r||(r=await P(O(t).href,{freshInstanceQuery:`acode_nonce=${o.importNonce}`}),o.moduleCache.set(t,r));const a=e.export||"default",l=r[a];if(typeof l!="function")throw new Error(`\u6A21\u5757 ${e.module} \u672A\u5BFC\u51FA\u51FD\u6570 ${a}`);const E=typeof e.timeoutMs=="number"&&e.timeoutMs>0?e.timeoutMs:h;let u;const y=Promise.resolve().then(()=>l(n,{agentId:s,signal:i?.signal,pluginRoot:o.dir,config:v(o,s)}));try{return await Promise.race([y,new Promise((c,d)=>{u=setTimeout(()=>d(new Error(`\u63D2\u4EF6\u6A21\u5757\u6267\u884C\u8D85\u65F6(${E}ms)`)),E)})])}finally{clearTimeout(u)}}function v(o,e){const n={};for(const s of o.userConfig??[]){if(!s||typeof s.key!="string")continue;const i=e?A(e)[s.key]:void 0,t=i!==void 0&&i.length>0?i:process.env[s.key];typeof t=="string"&&t.length>0&&(n[s.key]=t)}for(const[s,i]of Object.entries(o.configValues))typeof i=="string"&&i.length>0&&(n[s]=i);return n}function U(o,e){const n={};for(const t of["PATH","HOME","USERPROFILE","SystemRoot","COMSPEC","TEMP","TMP","LANG","TZ"]){const r=process.env[t];r!==void 0&&(n[t]=r)}n.ACODE_PLUGIN_ROOT=o.dir,n.ACODE_AGENT_ID=e,n.ACODE_PLUGIN_NAME=o.name;const s=new Set((o.userConfig??[]).map(t=>t.key)),i=v(o,e);for(const[t,r]of Object.entries(i))s.has(t)&&(!D.test(t)||k.has(t.toUpperCase())||(n[t]=r));return n}export{U as buildPluginEnv,Y as executeHandler,v as resolveEffectiveConfigValues};
|
|
3
3
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{spawn as
|
|
1
|
+
import{spawn as y}from"node:child_process";import{createLogger as R}from"../utils/logger.js";import{buildPluginEnv as S}from"./plugin-handler-adapter.js";import{getPath as E,isRecord as _,renderCommandArgv as w,renderTemplate as A,safeStableStringify as b,truncate as h}from"./plugin-manifest.js";const P=R("plugin-manager"),I=200,T=256*1024,L=4*1024,v=6e4,M=6e4;function p(g,n){g.logs.push(`${new Date().toISOString()} ${n}`),g.logs.length>I&&g.logs.shift()}class J{ensureRunning(n){for(const t of n.plugins.values())if(t.enabled)for(const e of t.runtimes)!e.child&&!e.timer&&!e.backoffTimer&&!e.stopping&&this.start(n,t,e)}startAll(n,t){for(const e of t.runtimes)this.stop(e),t.enabled&&this.start(n,t,e)}stopAll(n){for(const t of n.runtimes)this.stop(t)}start(n,t,e){const i=e.def.transport??{type:"command-stream"};if(i.type==="http-callback"){e.stopping=!1;return}if(i.type==="poll-command"){const s=Math.max(5e3,i.intervalMs??3e4);e.stopping=!1,e.timer=setInterval(()=>{this.pollOnce(n,t,e)},s),this.pollOnce(n,t,e);return}if(!i.command){e.lastError="command-stream \u7F3A\u5C11 command",p(e,`ERROR ${e.lastError}`);return}e.stopping=!1,this.spawnChild(n,t,e)}spawnChild(n,t,e){const s=e.def.transport?.command??"",d=w(s,{});try{p(e,`spawn: ${d.join(" ")}`);const r=y(d[0],d.slice(1),{cwd:t.dir,env:S(t,n.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});e.child=r,e.lastError=void 0;let o="";r.stdout?.on("data",a=>{o+=a.toString("utf8");let l=o.indexOf(`
|
|
2
2
|
`);for(;l>=0;){const f=o.slice(0,l).trim();o=o.slice(l+1),f&&this.handleLine(n,t,e,f),l=o.indexOf(`
|
|
3
|
-
`)}o.length>1024*1024&&(o=o.slice(-4096))}),
|
|
4
|
-
`)){const l=a.trim();l&&this.handleLine(n,t,e,l)}}catch(o){const a=o instanceof Error?o.message:String(o);e.lastError=a,p(e,`poll \u5931\u8D25: ${a}`)}finally{clearTimeout(
|
|
5
|
-
${
|
|
3
|
+
`)}o.length>1024*1024&&(o=o.slice(-4096))}),r.stderr?.on("data",a=>{p(e,`stderr: ${h(a.toString("utf8").trimEnd(),400)}`)}),r.on("error",a=>{e.lastError=`\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${a.message}`,p(e,`ERROR ${e.lastError}`)}),r.on("close",a=>{e.child=void 0,!e.stopping&&(e.lastError=`\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\uFF08code=${a??"signal"}\uFF09`,p(e,`exit code=${a??"signal"}\uFF0C\u51C6\u5907\u91CD\u542F`),this.scheduleRestart(n,t,e))})}catch(r){const o=r instanceof Error?r.message:String(r);e.lastError=o,p(e,`ERROR spawn: ${o}`),this.scheduleRestart(n,t,e)}}scheduleRestart(n,t,e){if(e.stopping)return;const i=Math.max(500,e.def.transport?.restartBackoffMs??2e3),s=Math.min(M,i*2**Math.min(e.restarts,6));e.restarts++,p(e,`restart #${e.restarts} in ${s}ms`),e.backoffTimer=setTimeout(()=>{e.backoffTimer=void 0,e.stopping||this.spawnChild(n,t,e)},s)}async pollOnce(n,t,e){if(e.polling)return;const i=e.def.transport?.command??"",s=w(i,{});if(s.length===0)return;e.polling=!0;const d=new AbortController,r=setTimeout(()=>d.abort(),v);try{const o=await new Promise((a,l)=>{const f=y(s[0],s.slice(1),{cwd:t.dir,env:S(t,n.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"],signal:d.signal});let c="",u="";f.stdout?.on("data",m=>{c.length<T&&(c+=m.toString("utf8"))}),f.stderr?.on("data",m=>{u.length<T&&(u+=m.toString("utf8"))});const $=()=>d.signal.aborted;f.on("error",m=>{l($()?new Error(`poll \u8D85\u65F6(${v}ms)`):m)}),f.on("close",m=>{$()?l(new Error(`poll \u8D85\u65F6(${v}ms)`)):m===0?a(c):l(new Error(`exit ${m}: ${h(u||c,300)}`))})});e.lastError=void 0;for(const a of o.split(`
|
|
4
|
+
`)){const l=a.trim();l&&this.handleLine(n,t,e,l)}}catch(o){const a=o instanceof Error?o.message:String(o);e.lastError=a,p(e,`poll \u5931\u8D25: ${a}`)}finally{clearTimeout(r),e.polling=!1}}stop(n){if(n.stopping=!0,n.restarts=0,n.child){try{n.child.kill()}catch{}n.child=void 0}n.timer&&(clearInterval(n.timer),n.timer=void 0),n.backoffTimer&&(clearTimeout(n.backoffTimer),n.backoffTimer=void 0)}handleLine(n,t,e,i){p(e,`event: ${h(i,300)}`);let s;try{s=JSON.parse(i)}catch{e.lastError="\u4E8B\u4EF6\u884C\u4E0D\u662F\u5408\u6CD5 JSON\uFF08\u8981\u6C42 JSONL\uFF09";return}if(!_(s))return;const d=typeof s.event=="string"?s.event:e.def.event,r=d===e.def.event?e.def:t.listeners.find(o=>o.event===d);r&&this.enqueue(n,t,r,s)}enqueue(n,t,e,i){for(const[c,u]of Object.entries(e.matcher??{})){const $=E(i,c);if(!(Array.isArray(u)?u:[u]).some(O=>JSON.stringify(O)===JSON.stringify($)))return}let s;if(e.dedupKey){const c=E(i,e.dedupKey);c!=null&&(s=h(String(c),256))}n.eventSeq++;const d=h(b(i.data??i),L),r=e.summary?A(e.summary,i):e.description??"",o=(e.toolHints??[]).join(","),a=`[plugin:${t.name}] ${e.event}`+(r?`
|
|
5
|
+
${r}`:"")+(o?`
|
|
6
6
|
toolHints=${o}`:"")+`
|
|
7
7
|
payload=${d}`,l={msgId:h(s?`plugin-event-${t.name}-${s}`:`plugin-event-${t.name}-${e.event}-${n.eventSeq}`,512),channel:"plugin-event",senderId:`plugin:${t.name}`,senderName:`\u63D2\u4EF6[${t.name}]`,receiverId:n.agentId||void 0,content:a,requireReply:!1,role:"system",timestamp:new Date().toISOString()},f=e.priority??"normal";n.buffer.push({plugin:t.name,event:e.event,priority:f,dedupKey:s,receivedAt:Date.now(),message:l})}}export{J as ListenerRunner};
|
|
8
8
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import type { PluginSummary } from "@agentswork/acode";
|
|
15
15
|
import { type PluginManifest, type PluginToolDef } from "./plugin-manifest.js";
|
|
16
|
+
import type { PluginUiSummary } from "@agentswork/acode";
|
|
16
17
|
import type { LoadedPlugin } from "./plugin-runtime.js";
|
|
17
18
|
export type ManifestReadResult = {
|
|
18
19
|
ok: true;
|
|
@@ -44,4 +45,9 @@ export declare class PluginLoader {
|
|
|
44
45
|
|
|
45
46
|
export declare function requiredParamsOf(tool: PluginToolDef): string[] | undefined;
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export declare function toFrontendSummary(plugin: LoadedPlugin): PluginUiSummary | undefined;
|
|
52
|
+
|
|
53
|
+
export declare function toSummary(plugin: LoadedPlugin, agentId?: string): PluginSummary;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{existsSync as
|
|
1
|
+
import{existsSync as C,readFileSync as F}from"node:fs";import{join as k}from"node:path";import{isRecord as V,validateListenerDef as b,validateManifest as I,validateToolDef as M,PLUGIN_UI_BUILTIN_ICONS as N}from"./plugin-manifest.js";import{getPanelEnvVars as R}from"./panel-env.js";class L{constructor(){this.importNonce=0}readJsonFile(e){if(!C(e))return{ok:!0,data:{}};try{const r=JSON.parse(F(e,"utf8"));return V(r)?{ok:!0,data:r}:{ok:!1,error:`${e}: \u6839\u8282\u70B9\u987B\u4E3A\u5BF9\u8C61`}}catch(r){const u=r instanceof Error?r.message:String(r);return{ok:!1,error:`${e}: JSON \u89E3\u6790\u5931\u8D25: ${u}`}}}loadManifestOnly(e){const r=this.readJsonFile(k(e,"plugin.json"));if(!r.ok)return r;const u=I(r.data);return u?{ok:!1,error:u}:{ok:!0,manifest:r.data}}validateUserConfigValues(e,r){const u=Array.isArray(e.userConfig)?e.userConfig:[],a={...r};for(const o of u){if(!o||typeof o.key!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(o.key))return"plugin.json: userConfig.key \u975E\u6CD5\uFF08\u987B\u4E3A\u73AF\u5883\u53D8\u91CF\u540D\uFF09";if(!(typeof a[o.key]=="string"&&a[o.key].length>0)&&o.required){const y=o.default!==void 0&&typeof o.default=="string"?o.default:"";if(!y)return`\u7F3A\u5C11\u5FC5\u9700\u914D\u7F6E ${o.key}${o.description?`\uFF08${o.description}\uFF09`:""}\uFF0C\u5B89\u88C5\u65F6\u8BF7\u5728 config \u4E2D\u63D0\u4F9B`;a[o.key]=y}}return null}loadPluginFromDir(e,r,u,a){const o=this.loadManifestOnly(e);if(!o.ok)return o;const i=o.manifest,y=k(e,i.tools??"tools/tools.json"),c=this.readJsonFile(y);if(!c.ok)return c;const p=[],t=Array.isArray(c.data.tools)?c.data.tools:[];for(const n of t){const l=M(n);if(l)return{ok:!1,error:`${i.tools??"tools/tools.json"}: ${l}`};p.push(n)}const d=k(e,i.listeners??"listeners/listeners.json"),m=this.readJsonFile(d);if(!m.ok)return m;const g=[],S=Array.isArray(m.data.listeners)?m.data.listeners:[];for(const n of S){const l=b(n);if(l)return{ok:!1,error:`${i.listeners??"listeners/listeners.json"}: ${l}`};g.push(n)}const h=Array.isArray(i.userConfig)?i.userConfig:[],$=this.validateUserConfigValues(i,a);if($)return{ok:!1,error:$};const v={...a};for(const n of h){if(!n||typeof n.key!="string")continue;!(typeof v[n.key]=="string"&&v[n.key].length>0)&&n.required&&n.default!==void 0&&typeof n.default=="string"&&(v[n.key]=n.default)}const j=new Set(h.filter(n=>n&&typeof n.key=="string"&&typeof a[n.key]=="string"&&a[n.key].length>0).map(n=>n.key)),w=i.commands;let A;if(typeof w=="string"&&w.trim()){const n=k(e,w.trim());C(n)&&(A=n)}const f=i.ui;if(f){const n=new Set(p.map(l=>l.name));if(f.status&&!n.has(f.status.tool))return{ok:!1,error:`plugin.json: ui.status.tool "${f.status.tool}" \u4E0D\u5728\u672C\u63D2\u4EF6 tools \u5B9A\u4E49\u4E2D`};for(const l of f.view?.invoke??[])if(!n.has(l))return{ok:!1,error:`plugin.json: ui.view.invoke \u5F15\u7528\u7684\u5DE5\u5177 "${l}" \u4E0D\u5728\u672C\u63D2\u4EF6 tools \u5B9A\u4E49\u4E2D`};if(f.view&&!C(k(e,f.view.entry)))return{ok:!1,error:`plugin.json: ui.view.entry \u6587\u4EF6\u4E0D\u5B58\u5728: ${f.view.entry}`}}return{ok:!0,plugin:{name:i.name,version:typeof i.version=="string"?i.version:void 0,description:typeof i.description=="string"?i.description:void 0,source:r,installedAt:u,dir:e,enabled:!0,tools:p,listeners:g,commandDir:A,ui:f,userConfig:h,configValues:v,configuredKeys:j,status:"enabled",runtimes:g.map(n=>({def:n,stopping:!1,restarts:0,logs:[]})),moduleCache:new Map,importNonce:++this.importNonce}}}}function P(s){const e=s.inputSchema;if(!(!e||!Array.isArray(e.required)))return e.required.filter(r=>typeof r=="string")}function q(s){const e=s.ui;if(!e)return;const r=e.icon?N.includes(e.icon)?`builtin:${e.icon}`:e.icon:void 0,u=e.status?{tool:e.status.tool,persistent:e.status.persistent===!0,...e.status.hideWhenZero===!0?{hideWhenZero:!0}:{},pollIntervalMs:typeof e.status.pollIntervalMs=="number"&&e.status.pollIntervalMs>=5e3&&e.status.pollIntervalMs<=3e5?e.status.pollIntervalMs:3e4,...e.status.title?{title:e.status.title}:{}}:void 0,a=e.view?{entry:e.view.entry,title:e.view.title,invoke:[...e.view.invoke??[]],...e.view.defaultRoute?{defaultRoute:e.view.defaultRoute}:{}}:void 0;if(!(!u&&!a))return{...r?{icon:r}:{},...u?{status:u}:{},...a?{view:a}:{}}}function U(s,e){const r=t=>!!(t.child||t.timer)||t.def.transport?.type==="http-callback",u=t=>s.enabled?r(t)?"running":t.stopping?"stopped":"pending":"stopped",a=s.runtimes.map(t=>{const d=u(t);return{event:t.def.event,delivery:t.def.delivery??"poll",running:d==="running",state:d,error:t.lastError}}),o=a.some(t=>t.state==="running")?"running":a.some(t=>t.state==="pending")?"pending":"stopped",i=q(s),y=s.configuredKeys??new Set,c=e?R(e):{},p=(s.userConfig??[]).map(t=>{const d=typeof c[t.key]=="string"&&c[t.key].length>0?c[t.key]:typeof process.env[t.key]=="string"&&(process.env[t.key]??"").length>0?process.env[t.key]:void 0,m=y.has(t.key)?"json":d!==void 0?"env":"default",g=typeof s.configValues[t.key]=="string"&&s.configValues[t.key].length>0?s.configValues[t.key]:d!==void 0?d:typeof t.default=="string"?t.default:"";return{key:t.key,...t.description?{description:t.description}:{},...t.required?{required:!0}:{},...t.sensitive?{sensitive:!0}:{},source:m,...g.length>0?{value:g}:{}}});return{name:s.name,version:s.version,description:s.description,status:s.status,enabled:s.enabled,source:s.source,installedAt:s.installedAt,toolCount:s.tools.length,tools:s.tools.map(t=>({name:t.name,title:t.title,description:t.description})),listeners:a,running:o==="running",listenerState:o,error:s.error,...i?{frontend:i}:{},...p.length>0?{userConfig:p}:{}}}export{L as PluginLoader,P as requiredParamsOf,q as toFrontendSummary,U as toSummary};
|
|
2
2
|
|
|
@@ -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>;
|