aws-runtime-bridge 1.9.183 → 1.9.185
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 +86 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts +30 -1
- package/dist/adapter/AcodeSdkAdapter.js +3 -3
- package/dist/adapter/ClaudeSdkAdapter.d.ts +3 -1
- package/dist/adapter/ClaudeSdkAdapter.js +3 -3
- package/dist/adapter/CodexSdkAdapter.d.ts +3 -1
- package/dist/adapter/CodexSdkAdapter.js +2 -2
- package/dist/adapter/OpencodeSdkAdapter.d.ts +3 -1
- package/dist/adapter/OpencodeSdkAdapter.js +3 -3
- package/dist/adapter/types.d.ts +7 -1
- package/dist/index.js +1 -1
- package/dist/routes/terminal.d.ts +13 -0
- package/dist/routes/terminal.js +28 -28
- package/dist/services/agent-ownership.d.ts +16 -0
- package/dist/services/agent-ownership.js +2 -2
- package/dist/services/auto-register.js +1 -1
- package/dist/services/bridge-pairing.js +1 -1
- package/dist/services/plugin-deployment.d.ts +14 -0
- package/dist/services/plugin-deployment.js +2 -2
- package/dist/services/plugin-manager.d.ts +3 -0
- package/dist/services/plugin-manager.js +4 -4
- package/dist/services/plugin-runtime.d.ts +10 -2
- package/dist/services/user-scope.d.ts +1 -0
- package/dist/services/user-scope.js +1 -1
- package/package/acode/dist/background-command-manager.d.ts +22 -0
- package/package/acode/dist/background-command-manager.js +5 -5
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/acode/dist/hibernation-journal.d.ts +67 -0
- package/package/acode/dist/hibernation-journal.js +2 -0
- package/package/acode/dist/index.d.ts +1 -0
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/runtime-environment.d.ts +4 -1
- package/package/acode/dist/runtime-environment.js +3 -3
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +7 -0
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +4 -4
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +8 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +20 -20
- package/package/aws-client-agent-mcp/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomBytes as ce}from"node:crypto";import p from"node:fs";import N from"node:os";import w from"axios";import{v4 as ue}from"uuid";import{getActiveBridgeHttpPort as de,getAutoRegisterConfigFilePath as S,isDesktopAppMode as le,schedulerBaseUrl as m}from"../config.js";import{runExclusiveConfigWrite as D}from"../utils/config-write-queue.js";import{logger as a}from"../utils/logger.js";import{buildTargetKey as x,fingerprintUserKey as fe,serverUrlOfTargetKey as F}from"../utils/target-identity.js";import{clearRuntimeBinding as ge,clearScopedRuntimeAccessToken as me,getRuntimeAccessToken as G,getRuntimeBindingPublicState as pe,getScopedRuntimeAccessToken as q,listScopedRuntimeAccessTokens as ye,loadRuntimeBinding as M,normalizeSchedulerBaseUrl as T,saveRuntimeBinding as Y,saveScopedRuntimeAccessToken as H}from"./runtime-binding.js";const V={enabled:!1,serverUrl:m,preferSameLanIp:!0,maxRetries:3,retryInterval:2e3},d={registered:!1,registrations:{}};let k=[];const Ie=10*60*1e3,B=new Map;function X(){const e=Date.now();for(const[r,t]of B)t.expiresAt<=e&&B.delete(r)}function Je(){X();const e=[...k];for(const r of B.values())r.key&&!e.includes(r.key)&&e.push(r.key);return e}function qe(e){const r=String(e||"").trim();if(!r)return[];const t=K(),n=t.filter(s=>String(s.connectionKey||"").trim()===r);if(n.length>0)return n;X();const o=new Set;for(const[s,i]of B)i.key===r&&o.add(s);return o.size===0?[]:t.filter(s=>o.has(C(s)))}function Ye(){return k[0]??""}const Ae=3e4;function Q(){const e=Number(process.env.AWS_REGISTER_BACKGROUND_RETRY_INTERVAL);return Number.isFinite(e)&&e>0?e:Ae}const $=new Map;function Z(e){const r=e.serverUrl||m,t=C(e)||r;if($.has(t))return;const n=setTimeout(()=>{$.delete(t),Re(e,t)},Q());n.unref?.(),$.set(t,n)}async function Re(e,r){try{const t=K().find(n=>C(n)===r)||e;await J(t,{backgroundRetry:!0})}catch(t){a.warn(`[AutoRegister] \u540E\u53F0\u91CD\u8BD5\u6CE8\u518C\u5F02\u5E38: ${t.message}`),Z(e)}}function He(){for(const e of $.values())clearTimeout(e);$.clear()}function Se(e){const r=e.trim();if(r.length<8)return r;for(let t=1;t<=r.length/2;t++){if(r.length%t!==0)continue;const n=r.slice(0,t);let o=!0;for(let s=t;s<r.length;s+=t)if(r.slice(s,s+t)!==n){o=!1;break}if(o&&n.length<r.length)return n}return r}function Te(e){const r=g(e);if(!r)return r;const t=Se(r);return t!==r&&a.warn(`[AutoRegister] userKey \u7591\u4F3C\u88AB\u590D\u5236\u4E86\u4E24\u904D\uFF08\u957F\u5EA6 ${r.length} \u2192 ${t.length}\uFF09\uFF0C\u5DF2\u81EA\u52A8\u53BB\u91CD\u3002\u8BF7\u68C0\u67E5\u914D\u7F6E\u6587\u4EF6\u662F\u5426\u91CD\u590D\u7C98\u8D34\u3002`),t}function g(e){return e==null?void 0:String(e).trim()||void 0}function _(e){return T(e)}function ee(e){return Array.isArray(e.autoRegisterTargets)?e.autoRegisterTargets.filter(r=>!!(r&&typeof r=="object"&&!Array.isArray(r))):[]}function C(e){const r=e?.serverUrl||m;return x(r,e?.credentialId||e?.userKey)}function y(e){return e?.credentialId||e?.userKey}function P(e){if(typeof e=="boolean")return e;if(typeof e=="string"){const r=e.trim().toLowerCase();if(["true","1","yes","y"].includes(r))return!0;if(["false","0","no","n"].includes(r))return!1}}function re(e){if(e==null||e==="")return;const r=Number(e);return Number.isFinite(r)?r:void 0}function O(e){const r=g(e.serverUrl)||g(e.schedulerBaseUrl),t=g(e.connectionKey)||g(e.password);return{enabled:P(e.enabled),serverUrl:r,tenantId:g(e.tenantId),userId:g(e.userId),userKey:Te(e.userKey),machineToken:g(e.machineToken),connectionKey:t,instanceName:g(e.instanceName),instanceId:g(e.instanceId),credentialId:g(e.credentialId),credentialRef:g(e.credentialRef),credentialType:g(e.credentialType),projectId:g(e.projectId),roleName:g(e.roleName),workspacePath:g(e.workspacePath),registerAddr:g(e.registerAddr),registerIp:g(e.registerIp),virtualIp:g(e.virtualIp),tunnel:P(e.tunnel),preferSameLanIp:P(e.preferSameLanIp),maxRetries:re(e.maxRetries),retryInterval:re(e.retryInterval)}}function te(){const e=S();try{if(!p.existsSync(e))return a.debug(`[AutoRegister] \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${e}`),null;const r=p.readFileSync(e,"utf-8"),t=JSON.parse(r);return a.info(`[AutoRegister] \u4ECE\u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u914D\u7F6E: ${e}`),O(t)}catch(r){const t=r;return a.warn(`[AutoRegister] \u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${e}, error: ${t.message}`),null}}function he(){const e=S();try{if(!p.existsSync(e))return[];const r=p.readFileSync(e,"utf-8"),t=JSON.parse(r),n=O(t),o=Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets:[];return o.length===0?n.userKey||n.machineToken?[n]:[]:o.filter(s=>!!(s&&typeof s=="object"&&!Array.isArray(s))).map(s=>{const i=O(s);return{...n,...i,machineToken:i.machineToken||(i.userKey?void 0:n.machineToken),connectionKey:i.connectionKey}})}catch(r){const t=r;return a.warn(`[AutoRegister] \u8BFB\u53D6\u81EA\u52A8\u6CE8\u518C\u76EE\u6807\u5931\u8D25: ${e}, error: ${t.message}`),[]}}function Ve(){const e=S();if(!p.existsSync(e))return[];let r;try{r=JSON.parse(p.readFileSync(e,"utf-8"))}catch(i){return a.warn(`[AutoRegister] \u8BFB\u53D6 config.json \u5931\u8D25\uFF08targets \u6458\u8981\uFF09: ${i.message}`),[]}const t=O(r),n=Array.isArray(r.autoRegisterTargets)?r.autoRegisterTargets:[],o=n.length>0?n.filter(i=>!!(i&&typeof i=="object"&&!Array.isArray(i))).map(i=>{const c=O(i);return{...t,...c,machineToken:c.machineToken||(c.userKey?void 0:t.machineToken),connectionKey:c.connectionKey}}):t.userKey||t.machineToken?[t]:[],s=i=>{const c=String(i||"").trim();return c?c.length<=8?"****":`${c.slice(0,8)}****${c.slice(-4)}`:""};return o.map((i,c)=>({index:c,serverUrl:i.serverUrl||"",instanceName:i.instanceName||"",registerAddr:i.registerAddr||i.registerIp||i.virtualIp||"",tunnel:typeof i.tunnel=="boolean"?i.tunnel:null,userKeyMasked:s(i.userKey),machineTokenSet:!!String(i.machineToken||"").trim(),credentialType:i.credentialType||"",credentialId:i.credentialId||"",credentialRef:i.credentialRef||"",connectionKeySet:!!String(i.connectionKey||"").trim()}))}function ne(){const e={};return process.env.AWS_AUTO_REGISTER==="true"&&(e.enabled=!0),process.env.AWS_RUNTIME_SCHEDULER_BASE_URL&&(e.serverUrl=process.env.AWS_RUNTIME_SCHEDULER_BASE_URL),process.env.AWS_TENANT_ID&&(e.tenantId=process.env.AWS_TENANT_ID),process.env.AWS_USER_KEY&&(e.userKey=process.env.AWS_USER_KEY),process.env.AWS_BRIDGE_MACHINE_TOKEN&&(e.machineToken=process.env.AWS_BRIDGE_MACHINE_TOKEN),process.env.AWS_BRIDGE_CONNECTION_KEY&&(e.connectionKey=process.env.AWS_BRIDGE_CONNECTION_KEY),process.env.AWS_INSTANCE_NAME&&(e.instanceName=process.env.AWS_INSTANCE_NAME),process.env.AWS_PROJECT_NAME&&(e.projectId=process.env.AWS_PROJECT_NAME),process.env.AWS_ROLE_NAME&&(e.roleName=process.env.AWS_ROLE_NAME),process.env.AWS_WORKSPACE_PATH&&(e.workspacePath=process.env.AWS_WORKSPACE_PATH),process.env.AWS_REGISTER_ADDR&&(e.registerAddr=process.env.AWS_REGISTER_ADDR),process.env.AWS_REGISTER_IP&&(e.registerIp=process.env.AWS_REGISTER_IP),process.env.AWS_VIRTUAL_IP&&(e.virtualIp=process.env.AWS_VIRTUAL_IP),process.env.AWS_PREFER_SAME_LAN_IP&&(e.preferSameLanIp=P(process.env.AWS_PREFER_SAME_LAN_IP)??!0),process.env.AWS_REGISTER_MAX_RETRIES&&(e.maxRetries=parseInt(process.env.AWS_REGISTER_MAX_RETRIES,10)),process.env.AWS_REGISTER_RETRY_INTERVAL&&(e.retryInterval=parseInt(process.env.AWS_REGISTER_RETRY_INTERVAL,10)),e}function W(){const e=te(),r=ne(),t={...V,...e||{},...r},n=t.instanceName||N.hostname();let o=!1;return process.env.AWS_AUTO_REGISTER!==void 0?o=process.env.AWS_AUTO_REGISTER==="true":e&&(o=!0),{enabled:o,serverUrl:t.serverUrl||m,tenantId:t.tenantId||"",userKey:t.userKey||"",machineToken:t.machineToken,connectionKey:t.connectionKey,instanceName:n,instanceId:t.instanceId,credentialId:t.credentialId,credentialRef:t.credentialRef,credentialType:t.credentialType,projectId:t.projectId,roleName:t.roleName,workspacePath:t.workspacePath,registerAddr:t.registerAddr,registerIp:t.registerIp,virtualIp:t.virtualIp,tunnel:typeof t.tunnel=="boolean"?t.tunnel:void 0,preferSameLanIp:t.preferSameLanIp!==!1,maxRetries:t.maxRetries,retryInterval:t.retryInterval}}function K(){const e=he(),r=ne(),n=(e.length>0?e:[te()||{}]).map(o=>{const s={...V,...o,...r};let i=!1;return process.env.AWS_AUTO_REGISTER!==void 0?i=process.env.AWS_AUTO_REGISTER==="true":i=e.length>0||!!(s.userKey||s.machineToken),{enabled:i,serverUrl:s.serverUrl||m,tenantId:s.tenantId||"",userId:s.userId,userKey:s.userKey||"",machineToken:s.machineToken,connectionKey:s.connectionKey,instanceName:s.instanceName||N.hostname(),instanceId:s.instanceId,credentialId:s.credentialId,credentialRef:s.credentialRef,credentialType:s.credentialType,projectId:s.projectId,roleName:s.roleName,workspacePath:s.workspacePath,registerAddr:s.registerAddr,registerIp:s.registerIp,virtualIp:s.virtualIp,tunnel:typeof s.tunnel=="boolean"?s.tunnel:void 0,preferSameLanIp:s.preferSameLanIp!==!1,maxRetries:s.maxRetries,retryInterval:s.retryInterval}});return k=n.map(o=>o.connectionKey).filter(o=>!!(o&&o.trim())),n}function ve(){const e=N.networkInterfaces(),r=[];for(const t of Object.keys(e)){const n=e[t];if(n)for(const o of n)o.family==="IPv4"&&!o.internal&&r.push({address:o.address,netmask:o.netmask,name:t})}return r}function z(e){const r=e.split(".").map(Number);return(r[0]<<24)+(r[1]<<16)+(r[2]<<8)+r[3]}function Ke(e,r,t){const n=z(e),o=z(r),s=z(t);return(n&s)===(o&s)}function Ue(e){try{const r=new URL(e);return r.hostname==="localhost"||r.hostname==="127.0.0.1"?"127.0.0.1":/^(\d{1,3}\.){3}\d{1,3}$/.test(r.hostname)?r.hostname:null}catch{return null}}function ke(e){return e==="127.0.0.1"}function _e(e){const r=ve();if(r.length===0)return a.warn("[AutoRegister] \u672A\u627E\u5230\u4EFB\u4F55\u975E\u5185\u90E8 IP \u5730\u5740\uFF0C\u4F7F\u7528 127.0.0.1"),"127.0.0.1";const t=Ue(e);if(t){if(ke(t))return"127.0.0.1";for(const n of r)if(Ke(n.address,t,n.netmask))return a.info(`[AutoRegister] \u627E\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u540C\u5B50\u7F51\u7684 IP: ${n.address} (\u7F51\u5361: ${n.name}, \u5B50\u7F51\u63A9\u7801: ${n.netmask})`),n.address;a.warn(`[AutoRegister] \u672A\u627E\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3 (${t}) \u540C\u5B50\u7F51\u7684\u672C\u673A IP\uFF0C\u4F7F\u7528 127.0.0.1`)}else a.warn("[AutoRegister] \u65E0\u6CD5\u89E3\u6790\u8C03\u5EA6\u4E2D\u5FC3 IP\uFF0C\u4F7F\u7528 127.0.0.1");return"127.0.0.1"}function Ee(e=m){return _e(e)}function se(e){const r=e||W();return!r.enabled||!r.userKey&&!r.machineToken?!1:r.tunnel===!0?!0:r.tunnel===!1?!1:!r.registerAddr&&!r.registerIp&&!r.virtualIp}function L(e){const r=de();if(se(e)){const s=e.serverUrl||m;try{const i=new URL(s);if(!e.instanceId)a.warn("[AutoRegister] \u96A7\u9053\u6A21\u5F0F\u4F46\u7F3A\u5C11 instanceId\uFF0C\u964D\u7EA7\u4E3A\u76F4\u8FDE\u6A21\u5F0F");else return`${i.protocol}//${i.host}/tunnel/${e.instanceId}`}catch{a.warn("[AutoRegister] serverUrl \u89E3\u6790\u5931\u8D25\uFF0C\u964D\u7EA7\u4E3A\u76F4\u8FDE\u6A21\u5F0F")}}const t=le()||e.preferSameLanIp===!1?"127.0.0.1":Ee(e.serverUrl||m),n=e.registerAddr||e.registerIp||e.virtualIp||t;return n?/^https?:\/\//i.test(n)?n:/:[0-9]+$/.test(n)?`http://${n}`:`http://${n}:${r}`:`http://127.0.0.1:${r}`}function b(){return K()[0]||W()}function Xe(e){return Ne(e)?.serverUrl}function Ne(e){const r=String(e||"").trim();if(!r)return;for(const n of K()){if(!n?.serverUrl)continue;const o=q(n.serverUrl,y(n));if(o&&o===r)return{serverUrl:T(n.serverUrl)||n.serverUrl,userKey:n.userKey,identityKey:y(n),instanceId:n.instanceId};const s=ye(n.serverUrl);for(const i of s){if(i.accessToken!==r)continue;const c=fe(n.userKey)===i.userKeyFingerprint?n.userKey:void 0;return{serverUrl:T(n.serverUrl)||n.serverUrl,userKey:c,identityKey:c}}}const t=M();if(t.schedulerBaseUrl){const n=q(t.schedulerBaseUrl);if(n&&n===r)return{serverUrl:T(t.schedulerBaseUrl)||t.schedulerBaseUrl}}}function we(e){if(!e)return;const r=Object.entries(d.registrations).find(([,t])=>t===e);return r?F(r[0]):void 0}function Be(e,r){const t=T(e);if(!t)return;if(String(r||"").trim()){const o=d.registrations[x(t,r)];return o||void 0}const n=Object.entries(d.registrations).filter(([o])=>F(o)===t);return n.length===1?n[0][1]:void 0}function j(e,r){const t=we(r);if(t)return t;const n=g(e.serverUrl);if(n&&(n!==m||process.env.AWS_RUNTIME_SCHEDULER_BASE_URL))return n;const o=M(),s=g(o.schedulerBaseUrl);return o.status==="paired"&&s?s:n||m}async function $e(e){const r=e.instanceName||N.hostname(),t=e.serverUrl||m,n={tenantId:e.tenantId,userKey:e.machineToken?void 0:e.userKey,machineToken:e.machineToken,instanceId:e.instanceId,instanceName:r,hostname:N.hostname(),virtualIp:e.registerAddr||e.registerIp||e.virtualIp,runtimeBridgeBaseUrl:L(e),connectionKey:e.connectionKey,workspacePath:e.workspacePath,projectId:e.projectId,roleName:e.roleName};return a.info(`[AutoRegister] \u6B63\u5728\u6CE8\u518C\u5B9E\u4F8B: ${r}`),a.info(`[AutoRegister] \u8C03\u5EA6\u4E2D\u5FC3: ${t}`),a.info(e.machineToken?"[AutoRegister] \u51ED\u636E: \u673A\u5668\u81EA\u6301\u51ED\u636E\uFF08machineToken\uFF09":`[AutoRegister] \u7528\u6237 Key: ${e.userKey?"****"+e.userKey.slice(-4):"(\u672A\u8BBE\u7F6E)"}`),a.info(`[AutoRegister] \u79DF\u6237 ID: ${e.tenantId||"(\u81EA\u52A8\u63A8\u5BFC)"}`),(await w.post(`${t}/api/instances/register`,n,{headers:{"Content-Type":"application/json"},timeout:1e4})).data}function Ce(e,r,t,n,o,s,i,c,u){const l=S();return D(l,()=>{if(!p.existsSync(l))return;const I=p.readFileSync(l,"utf-8"),h=JSON.parse(I),U=T(e);if(!U)return;let A=!1;const R=f=>T(String(f.serverUrl||f.schedulerBaseUrl||""))!==U?!1:String(t||"").trim()?String(f.userKey??"").trim()===String(t).trim():String(i||"").trim()?String(f.machineToken??"").trim()===String(i).trim():!0,v=f=>{R(f)&&(r&&f.instanceId!==r&&(f.instanceId=r,A=!0),n&&f.credentialId!==n&&(f.credentialId=n,A=!0),o&&f.credentialRef!==o&&(f.credentialRef=o,A=!0),s&&f.credentialType!==s&&(f.credentialType=s,A=!0),c&&f.tenantId!==c&&(f.tenantId=c,A=!0),u&&f.userId!==u&&(f.userId=u,A=!0))};if(Array.isArray(h.autoRegisterTargets))for(const f of h.autoRegisterTargets)f&&typeof f=="object"&&!Array.isArray(f)&&v(f);else v(h);A&&(p.writeFileSync(l,JSON.stringify(h,null,2),"utf-8"),a.info(`[AutoRegister] \u5DF2\u56DE\u5199 instanceId \u5230 config.json (serverUrl=${e}${t?`, userKey=****${String(t).slice(-4)}`:""})`))})}async function Oe(e,r){const t=String(r||"").trim();if(!t)return;const n=y(e),o=_(e.serverUrl);if(!n||!o)return;const s=S();try{await D(s,()=>{if(!p.existsSync(s))return;const i=p.readFileSync(s,"utf-8"),c=JSON.parse(i),l=ee(c).find(I=>_(I.serverUrl||I.schedulerBaseUrl)===o&&String(I.credentialId||I.userKey||"").trim()===n);if(!l){a.warn(`[AutoRegister] \u65E0\u6CD5\u5B9A\u4F4D connectionKey \u5BF9\u5E94 target\uFF0C\u8DF3\u8FC7\u5199\u76D8: server=${o}`);return}String(l.connectionKey??"").trim()!==t&&(l.connectionKey=t,p.writeFileSync(s,JSON.stringify(c,null,2),"utf-8"),a.info(`[AutoRegister] \u5DF2\u5199\u5165 target connectionKey \u5230 config.json: ${s}`))})}catch(i){a.warn(`[AutoRegister] \u5199\u5165 target connectionKey \u5230 config.json \u5931\u8D25: ${i.message}`)}}async function Qe(e,r){const t=String(e||"").trim();if(!t)throw new Error("connectionKey is required");const n=_(r?.serverUrl);if(!n)throw new Error("serverUrl is required to rotate a target connectionKey");const o=S();let s;const i=await D(o,()=>{if(!p.existsSync(o))return!1;const l=JSON.parse(p.readFileSync(o,"utf-8")),I=ee(l),h=String(r?.instanceId||"").trim(),U=String(r?.credentialId||"").trim(),A=I.filter(v=>{if(_(v.serverUrl||v.schedulerBaseUrl)!==n)return!1;if(h&&String(v.instanceId||"").trim()===h)return!0;if(U){const E=String(v.credentialId||"").trim(),ae=String(v.userKey||"").trim();if(E===U||ae===U)return!0}return!h&&!U&&I.filter(E=>_(E.serverUrl||E.schedulerBaseUrl)===n).length===1});if(A.length!==1)throw new Error(`\u65E0\u6CD5\u552F\u4E00\u5B9A\u4F4D connectionKey \u5BF9\u5E94 target\uFF08server=${n}, matches=${A.length}\uFF09`);const R=A[0];return String(R.connectionKey??"").trim()===t?!1:(s={targetKey:x(String(R.serverUrl??R.schedulerBaseUrl??""),R.credentialId||R.userKey),previousConnectionKey:String(R.connectionKey??"").trim()},R.connectionKey=t,String(l.connectionKey??"").trim()&&delete l.connectionKey,p.writeFileSync(o,JSON.stringify(l,null,2),"utf-8"),a.info(`[AutoRegister] \u5DF2\u8F6E\u6362 target connectionKey \u5E76\u5199\u56DE config.json: ${o}`),!0)}),c=!!process.env.AWS_BRIDGE_CONNECTION_KEY;c&&i&&(process.env.AWS_BRIDGE_CONNECTION_KEY=t,a.warn("[AutoRegister] \u68C0\u6D4B\u5230 AWS_BRIDGE_CONNECTION_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u5DF2\u540C\u6B65\u66F4\u65B0\u5F53\u524D\u8FDB\u7A0B\uFF1B\u91CD\u542F bridge \u540E\u4F1A\u56DE\u5230\u73AF\u5883\u53D8\u91CF\u4E2D\u7684\u65E7\u503C\uFF0C\u8BF7\u4E00\u5E76\u4FEE\u6539\u90E8\u7F72\u914D\u7F6E"));const u=K().map(l=>String(l.connectionKey||"").trim()).filter(l=>l.length>0);return k=i?[...new Set([t,...u])]:[...new Set(u)],i&&s&&s.previousConnectionKey!==t&&B.set(s.targetKey,{key:s.previousConnectionKey,expiresAt:Date.now()+Ie}),{envOverrideWarning:c,rotated:i}}async function ie(e){let r=0;for(const t of e)String(t.connectionKey||"").trim()||(t.connectionKey=`awsb_${ce(24).toString("base64url")}`,r+=1,k.includes(t.connectionKey)||(k=[...k,t.connectionKey]),await Oe(t,t.connectionKey));r!==0&&a.info(`[AutoRegister] ${r} \u4E2A\u76EE\u6807\u7F3A\u5C11 connectionKey\uFF0C\u5DF2\u6309 target \u72EC\u7ACB\u751F\u6210\u5E76\u5199\u5165 config.json`)}function oe(e){if(!e)return;const r=Object.entries(d.registrations).find(([,s])=>s===e);if(!r)return;const t=r[0],n=F(t),o=K().find(s=>C(s)===t);return{serverUrl:n,identityKey:o?y(o):void 0,targetKey:t}}async function be(e){const r=oe(e),t=r?.serverUrl||j(b(),e),n=G(t,r?.identityKey);if(!n)throw new Error("runtimeAccessToken is required to unregister bridge instance");return(await w.post(`${t}/api/instances/unregister`,{instanceId:e},{headers:{"Content-Type":"application/json","X-Runtime-Token":n},timeout:1e4})).data}async function Ze(e){const r=e?[{...W(),...e}]:K();await ie(r);const n=(r.length>1?await Promise.all(r.map(s=>J(s))):[await J(r[0]||W())]).every(Boolean),o=r.every(s=>!s.enabled||!s.userKey&&!s.machineToken);if(!e&&(n||o)){const{stopTunnelClientsExcept:s}=await import("./tunnel-client.js");s(r.filter(i=>!!i.serverUrl).map(i=>x(i.serverUrl,y(i))))}return n}async function J(e,r){if(!e.enabled)return a.info("[AutoRegister] \u81EA\u52A8\u6CE8\u518C\u672A\u542F\u7528"),a.info(`[AutoRegister] \u8981\u542F\u7528\u81EA\u52A8\u6CE8\u518C\uFF0C\u8BF7\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6 ${S()} \u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AWS_AUTO_REGISTER=true`),!1;if(!e.userKey&&!e.machineToken)return a.error("[AutoRegister] \u7F3A\u5C11\u6CE8\u518C\u51ED\u636E\uFF08\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u8BBE\u7F6E userKey \u6216 machineToken\uFF0C\u4E5F\u53EF\u7528 `awsb pair --ticket ...` \u914D\u5BF9\uFF09"),!1;String(e.connectionKey||"").trim()||await ie([e]);const t=se(e);t&&!e.instanceId&&(e.instanceId=`bridge-${ue().replace(/-/g,"").slice(0,12)}`,a.info(`[AutoRegister] \u96A7\u9053\u6A21\u5F0F\uFF1A\u5DF2\u9884\u58F0\u660E instanceId=${e.instanceId}`));const n=r?.backgroundRetry?1:e.maxRetries??3,o=e.retryInterval??2e3;for(let s=1;s<=n;s++){try{d.lastAttempt=new Date;const i=await $e(e);if(i.success){if(d.registered=!0,d.instanceId=i.instanceId,i.credentialId&&(e.credentialId=i.credentialId,e.credentialType=i.credentialType),e.credentialRef=i.credentialRef,i.tenantId&&(e.tenantId=i.tenantId),i.userId&&(e.userId=i.userId),i.instanceId&&(d.registrations[C(e)]=i.instanceId,e.instanceId=i.instanceId),d.error=void 0,a.info("[AutoRegister] \u2713 \u6CE8\u518C\u6210\u529F\uFF01"),a.info(`[AutoRegister] \u5B9E\u4F8B ID: ${i.instanceId}`),a.info(`[AutoRegister] \u6D88\u606F: ${i.message}`),a.info(`[AutoRegister] \u66F4\u65B0: ${i.isUpdate?"\u662F":"\u5426"}`),i.runtimeAccessToken)try{const c=e.serverUrl||m;Y({accessToken:i.runtimeAccessToken,instanceId:i.instanceId,userId:i.userId,schedulerBaseUrl:c,userKey:y(e)});const u=T(i.serverUrl);u&&u!==T(c)&&(H({serverBaseUrl:u,accessToken:i.runtimeAccessToken,userKey:y(e)}),a.info(`[AutoRegister] \u68C0\u6D4B\u5230 server \u4E0B\u53D1\u5730\u5740(${u})\u4E0E\u8FDE\u63A5\u5730\u5740(${c})\u4E0D\u540C\uFF0C\u5DF2\u6309\u4E24\u4E2A\u5730\u5740\u5206\u522B\u4FDD\u5B58 scoped token`)),a.info("[AutoRegister] \u5DF2\u4FDD\u5B58\u8C03\u5EA6\u4E2D\u5FC3\u4E0B\u53D1\u7684 runtime access token"),i.instanceId&&Ce(e.serverUrl||m,i.instanceId,e.userKey,i.credentialId,i.credentialRef,i.credentialType,e.machineToken,i.tenantId,i.userId).catch(l=>{a.warn(`[AutoRegister] \u56DE\u5199 instanceId \u5230 config.json \u5931\u8D25: ${l.message}`)})}catch(c){const u=c;return a.error(`[AutoRegister] \u4FDD\u5B58 runtime access token \u5931\u8D25: ${u.message}`),d.error=u.message,!1}else a.warn("[AutoRegister] \u6CE8\u518C\u54CD\u5E94\u672A\u5305\u542B runtime access token\uFF0CMCP \u5C06\u65E0\u6CD5\u8BBF\u95EE\u8C03\u5EA6\u4E2D\u5FC3\uFF0C\u8BF7\u68C0\u67E5 server \u7684 bridge \u5B9E\u4F8B token \u9881\u53D1\u903B\u8F91");if(t&&i.instanceId&&i.runtimeAccessToken){const{startTunnelClient:c}=await import("./tunnel-client.js"),u=e.serverUrl||m;c(u,i.instanceId,y(e)),a.info(`[AutoRegister] \u96A7\u9053\u5BA2\u6237\u7AEF\u5DF2\u542F\u52A8: server=${u}, instanceId=${i.instanceId}${e.userKey?`, userKey=****${e.userKey.slice(-4)}`:""}`)}return!0}else a.warn(`[AutoRegister] \u6CE8\u518C\u5931\u8D25: ${i.message}`),d.error=i.message}catch(i){const c=i,u=c.response?.data?JSON.stringify(c.response.data):c.message;a.error(`[AutoRegister] \u6CE8\u518C\u8BF7\u6C42\u5931\u8D25 (\u5C1D\u8BD5 ${s}/${n}): ${u}`),d.error=u}s<n&&(a.info(`[AutoRegister] ${o/1e3} \u79D2\u540E\u91CD\u8BD5...`),await new Promise(i=>setTimeout(i,o)))}return r?.backgroundRetry?a.warn(`[AutoRegister] \u540E\u53F0\u91CD\u8BD5\u6CE8\u518C\u4ECD\u672A\u6210\u529F (server=${e.serverUrl||m}): ${d.error||"\u672A\u77E5"}`):(a.error("[AutoRegister] \u2717 \u6CE8\u518C\u5931\u8D25\uFF0C\u5DF2\u8FBE\u6700\u5927\u91CD\u8BD5\u6B21\u6570"),a.error("[AutoRegister] ========================================"),a.error("[AutoRegister] \u81EA\u52A8\u6CE8\u518C\u5DF2\u542F\u7528\u4F46\u6CE8\u518C\u5931\u8D25\uFF0C\u5B9E\u4F8B\u6682\u65F6\u65E0\u6CD5\u6B63\u5E38\u5DE5\u4F5C"),a.error("[AutoRegister] \u8BF7\u68C0\u67E5\u4EE5\u4E0B\u914D\u7F6E\uFF1A"),a.error(`[AutoRegister] - \u7528\u6237 Key (userKey): ${e.userKey?"****"+e.userKey.slice(-4):"(\u672A\u8BBE\u7F6E)"}`),a.error(`[AutoRegister] - \u79DF\u6237 ID (tenantId): ${e.tenantId||"(\u53EF\u9009\uFF0C\u5C06\u81EA\u52A8\u63A8\u5BFC)"}`),a.error(`[AutoRegister] - \u5B9E\u4F8B\u540D (instanceName): ${e.instanceName||"(\u4F7F\u7528\u4E3B\u673A\u540D)"}`),a.error(`[AutoRegister] - \u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740: ${e.serverUrl||m}`),a.error(`[AutoRegister] - \u6700\u540E\u9519\u8BEF: ${d.error||"\u672A\u77E5"}`),a.error("[AutoRegister] ========================================"),a.error(`[AutoRegister] \u914D\u7F6E\u6765\u6E90\uFF1A${S()} \u6216\u73AF\u5883\u53D8\u91CF`),a.warn(`[AutoRegister] \u5C06\u5728\u540E\u53F0\u6301\u7EED\u91CD\u8BD5\u6CE8\u518C\uFF08\u6BCF ${Math.round(Q()/1e3)} \u79D2\uFF09\uFF0C\u76F4\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u5EFA\u7ACB\u8FDE\u63A5`)),Z(e),!1}async function er(){const e=b();if(!e.userKey&&!e.machineToken)return{success:!1,error:"userKey or machineToken is required for runtime token refresh"};const r=pe(),t=j(e),n=L(e);try{const o=await w.post(`${t}/api/instances/runtime-tokens/refresh`,{tenantId:e.tenantId,userKey:e.machineToken?void 0:e.userKey,machineToken:e.machineToken,instanceId:r.instanceId,instanceName:e.instanceName,runtimeBridgeBaseUrl:n},{headers:{"Content-Type":"application/json"},timeout:1e4});if(!o.data.success||!o.data.runtimeAccessToken)return{success:!1,error:o.data.message||"scheduler did not return runtimeAccessToken"};const i=G(t,y(e))!==o.data.runtimeAccessToken;return Y({accessToken:o.data.runtimeAccessToken,instanceId:r.instanceId,userId:o.data.userId||e.userKey||e.credentialId||"",schedulerBaseUrl:t,userKey:y(e)}),{success:!0,runtimeAccessToken:o.data.runtimeAccessToken,updated:i}}catch(o){const s=o;return{success:!1,error:s.response?.data?JSON.stringify(s.response.data):s.message}}}async function rr(e,r){const t=_(e);if(!t)return{success:!1,error:"serverUrl is required for runtime token refresh"};const n=K().filter(u=>_(u.serverUrl)===t),o=String(r||"").trim(),s=o?n.find(u=>String(u.userKey||"").trim()===o||String(u.credentialId||"").trim()===o):n.length===1?n[0]:void 0;if(!s)return{success:!1,error:o?"no auto-register target matches this identity":"multiple auto-register targets share this serverUrl; specify the target identity"};if(!s.userKey&&!s.machineToken)return{success:!1,error:"userKey or machineToken is required for runtime token refresh"};const i={instanceId:Be(t,y(s))},c=L(s);try{const u=await w.post(`${t}/api/instances/runtime-tokens/refresh`,{tenantId:s.tenantId,userKey:s.machineToken?void 0:s.userKey,machineToken:s.machineToken,instanceId:i.instanceId,instanceName:s.instanceName,runtimeBridgeBaseUrl:c},{headers:{"Content-Type":"application/json"},timeout:1e4});if(!u.data.success||!u.data.runtimeAccessToken)return{success:!1,error:u.data.message||"scheduler did not return runtimeAccessToken"};const I=G(t,y(s))!==u.data.runtimeAccessToken;return H({serverBaseUrl:t,accessToken:u.data.runtimeAccessToken,userKey:y(s)}),{success:!0,runtimeAccessToken:u.data.runtimeAccessToken,updated:I}}catch(u){const l=u;return{success:!1,error:l.response?.data?JSON.stringify(l.response.data):l.message}}}async function tr(){if(!d.registered||!d.instanceId)return a.info("[AutoRegister] \u5B9E\u4F8B\u672A\u6CE8\u518C\uFF0C\u65E0\u9700\u6CE8\u9500"),!0;try{const e=await be(d.instanceId);if(e.success){a.info(`[AutoRegister] \u2713 \u6CE8\u9500\u6210\u529F: ${d.instanceId}`);const r=oe(d.instanceId),t=r?.serverUrl||j(b(),d.instanceId);M().schedulerBaseUrl&&me(t,r?.identityKey),ge();try{const{stopTunnelClientForTarget:o}=await import("./tunnel-client.js");o(t,r?.identityKey??y(b()))}catch(o){a.warn(`[AutoRegister] \u6CE8\u9500\u540E\u505C\u6B62\u96A7\u9053\u5931\u8D25: ${o.message}`)}if(!r)d.registered=!1,d.instanceId=void 0;else{delete d.registrations[r.targetKey];const o=Object.entries(d.registrations);d.registered=o.length>0,d.instanceId=o[o.length-1]?.[1]}return!0}else return a.warn(`[AutoRegister] \u6CE8\u9500\u5931\u8D25: ${e.message}`),!1}catch(e){const r=e;return a.error(`[AutoRegister] \u6CE8\u9500\u8BF7\u6C42\u5931\u8D25: ${r.message}`),!1}}function nr(){return{...d}}function sr(){return d.registered}function ir(){return d.instanceId}async function or(){const e=b(),r=d.instanceId,t=j(e,r),n={};if(r&&(n.instanceId=r),e.enabled&&(n.runtimeBridgeBaseUrl=L(e)),!n.instanceId&&!n.runtimeBridgeBaseUrl)return a.info("[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\uFF1A\u6CA1\u6709 instanceId \u6216 runtimeBridgeBaseUrl\uFF0C\u8DF3\u8FC7\u6E05\u7406"),{success:!0,agentCount:0};try{a.info("[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\uFF1A\u6B63\u5728\u901A\u77E5\u8C03\u5EA6\u4E2D\u5FC3..."),a.info(`[AutoRegister] instanceId: ${n.instanceId||"(\u65E0)"}`),a.info(`[AutoRegister] runtimeBridgeBaseUrl: ${n.runtimeBridgeBaseUrl||"(\u65E0)"}`);const o=await w.post(`${t}/api/instances/bridge-restart-cleanup`,n,{headers:{"Content-Type":"application/json"},timeout:1e4});return o.data.success?(a.info(`[AutoRegister] \u2713 Bridge \u91CD\u542F\u6E05\u7406\u6210\u529F\uFF1A${o.data.message}`),a.info(`[AutoRegister] \u6E05\u7406\u4E86 ${o.data.agentCount||0} \u4E2A Agent`),{success:!0,agentCount:o.data.agentCount||0}):(a.warn(`[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\u5931\u8D25\uFF1A${o.data.message}`),{success:!1,error:o.data.message})}catch(o){const s=o,i=s.response?.data?JSON.stringify(s.response.data):s.message;return a.warn(`[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\u8BF7\u6C42\u5931\u8D25\uFF1A${i}`),{success:!1,error:i}}}export{Ze as autoRegister,or as bridgeRestartCleanup,He as cancelBackgroundRegisterRetries,Se as collapseDuplicatedSecret,qe as findConfigsByConnectionKey,S as getAutoRegisterConfigFilePath,Je as getConfiguredConnectionKeys,Ye as getCurrentConfiguredConnectionKey,ir as getInstanceId,nr as getRegistrationState,sr as isRegistered,se as isTunnelConfig,Ve as listConfiguredTargetSummaries,W as loadConfig,K as loadConfigs,b as loadPrimaryLifecycleConfig,Qe as replaceConfiguredConnectionKey,er as requestRuntimeAccessTokenRefresh,rr as requestRuntimeAccessTokenRefreshForServer,Be as resolveInstanceIdForTarget,L as resolveRuntimeBridgeBaseUrl,Xe as resolveSchedulerBaseUrlFromToken,Ne as resolveTargetIdentityFromToken,tr as unregister};
|
|
1
|
+
import{randomBytes as ce}from"node:crypto";import p from"node:fs";import w from"node:os";import $ from"axios";import{v4 as ue}from"uuid";import{getActiveBridgeHttpPort as de,getAutoRegisterConfigFilePath as T,isDesktopAppMode as le,schedulerBaseUrl as m}from"../config.js";import{runExclusiveConfigWrite as D}from"../utils/config-write-queue.js";import{logger as a}from"../utils/logger.js";import{buildTargetKey as x,fingerprintUserKey as fe,serverUrlOfTargetKey as F}from"../utils/target-identity.js";import{clearRuntimeBinding as ge,clearScopedRuntimeAccessToken as me,getRuntimeAccessToken as G,getRuntimeBindingPublicState as pe,getScopedRuntimeAccessToken as Y,listScopedRuntimeAccessTokens as ye,loadRuntimeBinding as M,normalizeSchedulerBaseUrl as S,saveRuntimeBinding as H,saveScopedRuntimeAccessToken as V}from"./runtime-binding.js";const X={enabled:!1,serverUrl:m,preferSameLanIp:!0,maxRetries:3,retryInterval:2e3},d={registered:!1,registrations:{}};let k=[];const Ie=10*60*1e3,B=new Map;function Q(){const e=Date.now();for(const[r,t]of B)t.expiresAt<=e&&B.delete(r)}function Je(){Q();const e=[...k];for(const r of B.values())r.key&&!e.includes(r.key)&&e.push(r.key);return e}function qe(e){const r=String(e||"").trim();if(!r)return[];const t=h(),n=t.filter(i=>String(i.connectionKey||"").trim()===r);if(n.length>0)return n;Q();const o=new Set;for(const[i,s]of B)s.key===r&&o.add(i);return o.size===0?[]:t.filter(i=>o.has(N(i)))}function Ye(){return k[0]??""}const Ae=3e4;function Z(){const e=Number(process.env.AWS_REGISTER_BACKGROUND_RETRY_INTERVAL);return Number.isFinite(e)&&e>0?e:Ae}const C=new Map;function ee(e){const r=e.serverUrl||m,t=N(e)||r;if(C.has(t))return;const n=setTimeout(()=>{C.delete(t),Re(e,t)},Z());n.unref?.(),C.set(t,n)}async function Re(e,r){try{const t=h().find(n=>N(n)===r)||e;await q(t,{backgroundRetry:!0})}catch(t){a.warn(`[AutoRegister] \u540E\u53F0\u91CD\u8BD5\u6CE8\u518C\u5F02\u5E38: ${t.message}`),ee(e)}}function He(){for(const e of C.values())clearTimeout(e);C.clear()}function Te(e){const r=e.trim();if(r.length<8)return r;for(let t=1;t<=r.length/2;t++){if(r.length%t!==0)continue;const n=r.slice(0,t);let o=!0;for(let i=t;i<r.length;i+=t)if(r.slice(i,i+t)!==n){o=!1;break}if(o&&n.length<r.length)return n}return r}function Se(e){const r=g(e);if(!r)return r;const t=Te(r);return t!==r&&a.warn(`[AutoRegister] userKey \u7591\u4F3C\u88AB\u590D\u5236\u4E86\u4E24\u904D\uFF08\u957F\u5EA6 ${r.length} \u2192 ${t.length}\uFF09\uFF0C\u5DF2\u81EA\u52A8\u53BB\u91CD\u3002\u8BF7\u68C0\u67E5\u914D\u7F6E\u6587\u4EF6\u662F\u5426\u91CD\u590D\u7C98\u8D34\u3002`),t}function g(e){return e==null?void 0:String(e).trim()||void 0}function _(e){return S(e)}function re(e){return Array.isArray(e.autoRegisterTargets)?e.autoRegisterTargets.filter(r=>!!(r&&typeof r=="object"&&!Array.isArray(r))):[]}function N(e){const r=e?.serverUrl||m;return x(r,e?.credentialId||e?.userKey)}function y(e){return e?.credentialId||e?.userKey}function P(e){if(typeof e=="boolean")return e;if(typeof e=="string"){const r=e.trim().toLowerCase();if(["true","1","yes","y"].includes(r))return!0;if(["false","0","no","n"].includes(r))return!1}}function te(e){if(e==null||e==="")return;const r=Number(e);return Number.isFinite(r)?r:void 0}function O(e){const r=g(e.serverUrl)||g(e.schedulerBaseUrl),t=g(e.connectionKey)||g(e.password);return{enabled:P(e.enabled),serverUrl:r,tenantId:g(e.tenantId),userId:g(e.userId),userKey:Se(e.userKey),machineToken:g(e.machineToken),connectionKey:t,instanceName:g(e.instanceName),instanceId:g(e.instanceId),credentialId:g(e.credentialId),credentialRef:g(e.credentialRef),credentialType:g(e.credentialType),projectId:g(e.projectId),roleName:g(e.roleName),workspacePath:g(e.workspacePath),registerAddr:g(e.registerAddr),registerIp:g(e.registerIp),virtualIp:g(e.virtualIp),tunnel:P(e.tunnel),preferSameLanIp:P(e.preferSameLanIp),maxRetries:te(e.maxRetries),retryInterval:te(e.retryInterval)}}function ne(){const e=T();try{if(!p.existsSync(e))return a.debug(`[AutoRegister] \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${e}`),null;const r=p.readFileSync(e,"utf-8"),t=JSON.parse(r);return a.info(`[AutoRegister] \u4ECE\u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u914D\u7F6E: ${e}`),O(t)}catch(r){const t=r;return a.warn(`[AutoRegister] \u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${e}, error: ${t.message}`),null}}function he(){const e=T();try{if(!p.existsSync(e))return[];const r=p.readFileSync(e,"utf-8"),t=JSON.parse(r),n=O(t),o=Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets:[];return o.length===0?n.userKey||n.machineToken?[n]:[]:o.filter(i=>!!(i&&typeof i=="object"&&!Array.isArray(i))).map(i=>{const s=O(i);return{...n,...s,machineToken:s.machineToken||(s.userKey?void 0:n.machineToken),connectionKey:s.connectionKey}})}catch(r){const t=r;return a.warn(`[AutoRegister] \u8BFB\u53D6\u81EA\u52A8\u6CE8\u518C\u76EE\u6807\u5931\u8D25: ${e}, error: ${t.message}`),[]}}function Ve(){const e=T();if(!p.existsSync(e))return[];let r;try{r=JSON.parse(p.readFileSync(e,"utf-8"))}catch(s){return a.warn(`[AutoRegister] \u8BFB\u53D6 config.json \u5931\u8D25\uFF08targets \u6458\u8981\uFF09: ${s.message}`),[]}const t=O(r),n=Array.isArray(r.autoRegisterTargets)?r.autoRegisterTargets:[],o=n.length>0?n.filter(s=>!!(s&&typeof s=="object"&&!Array.isArray(s))).map(s=>{const c=O(s);return{...t,...c,machineToken:c.machineToken||(c.userKey?void 0:t.machineToken),connectionKey:c.connectionKey}}):t.userKey||t.machineToken?[t]:[],i=s=>{const c=String(s||"").trim();return c?c.length<=8?"****":`${c.slice(0,8)}****${c.slice(-4)}`:""};return o.map((s,c)=>({index:c,serverUrl:s.serverUrl||"",instanceName:s.instanceName||"",registerAddr:s.registerAddr||s.registerIp||s.virtualIp||"",tunnel:typeof s.tunnel=="boolean"?s.tunnel:null,userKeyMasked:i(s.userKey),machineTokenSet:!!String(s.machineToken||"").trim(),credentialType:s.credentialType||"",credentialId:s.credentialId||"",credentialRef:s.credentialRef||"",connectionKeySet:!!String(s.connectionKey||"").trim()}))}function se(){const e={};return process.env.AWS_AUTO_REGISTER==="true"&&(e.enabled=!0),process.env.AWS_RUNTIME_SCHEDULER_BASE_URL&&(e.serverUrl=process.env.AWS_RUNTIME_SCHEDULER_BASE_URL),process.env.AWS_TENANT_ID&&(e.tenantId=process.env.AWS_TENANT_ID),process.env.AWS_USER_KEY&&(e.userKey=process.env.AWS_USER_KEY),process.env.AWS_BRIDGE_MACHINE_TOKEN&&(e.machineToken=process.env.AWS_BRIDGE_MACHINE_TOKEN),process.env.AWS_BRIDGE_CONNECTION_KEY&&(e.connectionKey=process.env.AWS_BRIDGE_CONNECTION_KEY),process.env.AWS_INSTANCE_NAME&&(e.instanceName=process.env.AWS_INSTANCE_NAME),process.env.AWS_PROJECT_NAME&&(e.projectId=process.env.AWS_PROJECT_NAME),process.env.AWS_ROLE_NAME&&(e.roleName=process.env.AWS_ROLE_NAME),process.env.AWS_WORKSPACE_PATH&&(e.workspacePath=process.env.AWS_WORKSPACE_PATH),process.env.AWS_REGISTER_ADDR&&(e.registerAddr=process.env.AWS_REGISTER_ADDR),process.env.AWS_REGISTER_IP&&(e.registerIp=process.env.AWS_REGISTER_IP),process.env.AWS_VIRTUAL_IP&&(e.virtualIp=process.env.AWS_VIRTUAL_IP),process.env.AWS_PREFER_SAME_LAN_IP&&(e.preferSameLanIp=P(process.env.AWS_PREFER_SAME_LAN_IP)??!0),process.env.AWS_REGISTER_MAX_RETRIES&&(e.maxRetries=parseInt(process.env.AWS_REGISTER_MAX_RETRIES,10)),process.env.AWS_REGISTER_RETRY_INTERVAL&&(e.retryInterval=parseInt(process.env.AWS_REGISTER_RETRY_INTERVAL,10)),e}function W(){const e=ne(),r=se(),t={...X,...e||{},...r},n=t.instanceName||w.hostname();let o=!1;return process.env.AWS_AUTO_REGISTER!==void 0?o=process.env.AWS_AUTO_REGISTER==="true":e&&(o=!0),{enabled:o,serverUrl:t.serverUrl||m,tenantId:t.tenantId||"",userKey:t.userKey||"",machineToken:t.machineToken,connectionKey:t.connectionKey,instanceName:n,instanceId:t.instanceId,credentialId:t.credentialId,credentialRef:t.credentialRef,credentialType:t.credentialType,projectId:t.projectId,roleName:t.roleName,workspacePath:t.workspacePath,registerAddr:t.registerAddr,registerIp:t.registerIp,virtualIp:t.virtualIp,tunnel:typeof t.tunnel=="boolean"?t.tunnel:void 0,preferSameLanIp:t.preferSameLanIp!==!1,maxRetries:t.maxRetries,retryInterval:t.retryInterval}}function h(){const e=he(),r=se(),n=(e.length>0?e:[ne()||{}]).map(o=>{const i={...X,...o,...r};let s=!1;return process.env.AWS_AUTO_REGISTER!==void 0?s=process.env.AWS_AUTO_REGISTER==="true":s=e.length>0||!!(i.userKey||i.machineToken),{enabled:s,serverUrl:i.serverUrl||m,tenantId:i.tenantId||"",userId:i.userId,userKey:i.userKey||"",machineToken:i.machineToken,connectionKey:i.connectionKey,instanceName:i.instanceName||w.hostname(),instanceId:i.instanceId,credentialId:i.credentialId,credentialRef:i.credentialRef,credentialType:i.credentialType,projectId:i.projectId,roleName:i.roleName,workspacePath:i.workspacePath,registerAddr:i.registerAddr,registerIp:i.registerIp,virtualIp:i.virtualIp,tunnel:typeof i.tunnel=="boolean"?i.tunnel:void 0,preferSameLanIp:i.preferSameLanIp!==!1,maxRetries:i.maxRetries,retryInterval:i.retryInterval}});return k=n.map(o=>o.connectionKey).filter(o=>!!(o&&o.trim())),n}function ve(){const e=w.networkInterfaces(),r=[];for(const t of Object.keys(e)){const n=e[t];if(n)for(const o of n)o.family==="IPv4"&&!o.internal&&r.push({address:o.address,netmask:o.netmask,name:t})}return r}function z(e){const r=e.split(".").map(Number);return(r[0]<<24)+(r[1]<<16)+(r[2]<<8)+r[3]}function Ke(e,r,t){const n=z(e),o=z(r),i=z(t);return(n&i)===(o&i)}function Ue(e){try{const r=new URL(e);return r.hostname==="localhost"||r.hostname==="127.0.0.1"?"127.0.0.1":/^(\d{1,3}\.){3}\d{1,3}$/.test(r.hostname)?r.hostname:null}catch{return null}}function ke(e){return e==="127.0.0.1"}function _e(e){const r=ve();if(r.length===0)return a.warn("[AutoRegister] \u672A\u627E\u5230\u4EFB\u4F55\u975E\u5185\u90E8 IP \u5730\u5740\uFF0C\u4F7F\u7528 127.0.0.1"),"127.0.0.1";const t=Ue(e);if(t){if(ke(t))return"127.0.0.1";for(const n of r)if(Ke(n.address,t,n.netmask))return a.info(`[AutoRegister] \u627E\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u540C\u5B50\u7F51\u7684 IP: ${n.address} (\u7F51\u5361: ${n.name}, \u5B50\u7F51\u63A9\u7801: ${n.netmask})`),n.address;a.warn(`[AutoRegister] \u672A\u627E\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3 (${t}) \u540C\u5B50\u7F51\u7684\u672C\u673A IP\uFF0C\u4F7F\u7528 127.0.0.1`)}else a.warn("[AutoRegister] \u65E0\u6CD5\u89E3\u6790\u8C03\u5EA6\u4E2D\u5FC3 IP\uFF0C\u4F7F\u7528 127.0.0.1");return"127.0.0.1"}function Ee(e=m){return _e(e)}function ie(e){const r=e||W();return!r.enabled||!r.userKey&&!r.machineToken?!1:r.tunnel===!0?!0:r.tunnel===!1?!1:!r.registerAddr&&!r.registerIp&&!r.virtualIp}function L(e){const r=de();if(ie(e)){const i=e.serverUrl||m;try{const s=new URL(i);if(!e.instanceId)a.warn("[AutoRegister] \u96A7\u9053\u6A21\u5F0F\u4F46\u7F3A\u5C11 instanceId\uFF0C\u964D\u7EA7\u4E3A\u76F4\u8FDE\u6A21\u5F0F");else return`${s.protocol}//${s.host}/tunnel/${e.instanceId}`}catch{a.warn("[AutoRegister] serverUrl \u89E3\u6790\u5931\u8D25\uFF0C\u964D\u7EA7\u4E3A\u76F4\u8FDE\u6A21\u5F0F")}}const t=le()||e.preferSameLanIp===!1?"127.0.0.1":Ee(e.serverUrl||m),n=e.registerAddr||e.registerIp||e.virtualIp||t;return n?/^https?:\/\//i.test(n)?n:/:[0-9]+$/.test(n)?`http://${n}`:`http://${n}:${r}`:`http://127.0.0.1:${r}`}function b(){return h()[0]||W()}function Xe(e){return Ne(e)?.serverUrl}function Ne(e){const r=String(e||"").trim();if(!r)return;for(const n of h()){if(!n?.serverUrl)continue;const o=Y(n.serverUrl,y(n));if(o&&o===r)return{serverUrl:S(n.serverUrl)||n.serverUrl,userKey:n.userKey,identityKey:y(n),instanceId:n.instanceId};const i=ye(n.serverUrl);for(const s of i){if(s.accessToken!==r)continue;const c=fe(n.userKey)===s.userKeyFingerprint?n.userKey:void 0;return{serverUrl:S(n.serverUrl)||n.serverUrl,userKey:c,identityKey:c}}}const t=M();if(t.schedulerBaseUrl){const n=Y(t.schedulerBaseUrl);if(n&&n===r)return{serverUrl:S(t.schedulerBaseUrl)||t.schedulerBaseUrl}}}function we(e){if(!e)return;const r=Object.entries(d.registrations).find(([,t])=>t===e);return r?F(r[0]):void 0}function $e(e,r){const t=S(e);if(!t)return;if(String(r||"").trim()){const o=d.registrations[x(t,r)];return o||void 0}const n=Object.entries(d.registrations).filter(([o])=>F(o)===t);return n.length===1?n[0][1]:void 0}function j(e,r){const t=we(r);if(t)return t;const n=g(e.serverUrl);if(n&&(n!==m||process.env.AWS_RUNTIME_SCHEDULER_BASE_URL))return n;const o=M(),i=g(o.schedulerBaseUrl);return o.status==="paired"&&i?i:n||m}async function Be(e){const r=e.instanceName||w.hostname(),t=e.serverUrl||m,n={tenantId:e.tenantId,userKey:e.machineToken?void 0:e.userKey,machineToken:e.machineToken,instanceId:e.instanceId,instanceName:r,hostname:w.hostname(),virtualIp:e.registerAddr||e.registerIp||e.virtualIp,runtimeBridgeBaseUrl:L(e),connectionKey:e.connectionKey,workspacePath:e.workspacePath,projectId:e.projectId,roleName:e.roleName};return a.info(`[AutoRegister] \u6B63\u5728\u6CE8\u518C\u5B9E\u4F8B: ${r}`),a.info(`[AutoRegister] \u8C03\u5EA6\u4E2D\u5FC3: ${t}`),a.info(e.machineToken?"[AutoRegister] \u51ED\u636E: \u673A\u5668\u81EA\u6301\u51ED\u636E\uFF08machineToken\uFF09":`[AutoRegister] \u7528\u6237 Key: ${e.userKey?"****"+e.userKey.slice(-4):"(\u672A\u8BBE\u7F6E)"}`),a.info(`[AutoRegister] \u79DF\u6237 ID: ${e.tenantId||"(\u81EA\u52A8\u63A8\u5BFC)"}`),(await $.post(`${t}/api/instances/register`,n,{headers:{"Content-Type":"application/json"},timeout:1e4})).data}function Ce(e,r,t,n,o,i,s,c,u){const l=T();return D(l,()=>{if(!p.existsSync(l))return;const I=p.readFileSync(l,"utf-8"),v=JSON.parse(I),U=S(e);if(!U)return;let A=!1;const R=f=>S(String(f.serverUrl||f.schedulerBaseUrl||""))!==U?!1:String(t||"").trim()?String(f.userKey??"").trim()===String(t).trim():String(s||"").trim()?String(f.machineToken??"").trim()===String(s).trim():!0,K=f=>{R(f)&&(r&&f.instanceId!==r&&(f.instanceId=r,A=!0),n&&f.credentialId!==n&&(f.credentialId=n,A=!0),o&&f.credentialRef!==o&&(f.credentialRef=o,A=!0),i&&f.credentialType!==i&&(f.credentialType=i,A=!0),c&&f.tenantId!==c&&(f.tenantId=c,A=!0),u&&f.userId!==u&&(f.userId=u,A=!0))};if(Array.isArray(v.autoRegisterTargets))for(const f of v.autoRegisterTargets)f&&typeof f=="object"&&!Array.isArray(f)&&K(f);else K(v);A&&(p.writeFileSync(l,JSON.stringify(v,null,2),"utf-8"),a.info(`[AutoRegister] \u5DF2\u56DE\u5199 instanceId \u5230 config.json (serverUrl=${e}${t?`, userKey=****${String(t).slice(-4)}`:""})`))})}async function Oe(e,r){const t=String(r||"").trim();if(!t)return;const n=y(e),o=_(e.serverUrl);if(!n||!o)return;const i=T();try{await D(i,()=>{if(!p.existsSync(i))return;const s=p.readFileSync(i,"utf-8"),c=JSON.parse(s),l=re(c).find(I=>_(I.serverUrl||I.schedulerBaseUrl)===o&&String(I.credentialId||I.userKey||"").trim()===n);if(!l){a.warn(`[AutoRegister] \u65E0\u6CD5\u5B9A\u4F4D connectionKey \u5BF9\u5E94 target\uFF0C\u8DF3\u8FC7\u5199\u76D8: server=${o}`);return}String(l.connectionKey??"").trim()!==t&&(l.connectionKey=t,p.writeFileSync(i,JSON.stringify(c,null,2),"utf-8"),a.info(`[AutoRegister] \u5DF2\u5199\u5165 target connectionKey \u5230 config.json: ${i}`))})}catch(s){a.warn(`[AutoRegister] \u5199\u5165 target connectionKey \u5230 config.json \u5931\u8D25: ${s.message}`)}}async function Qe(e,r){const t=String(e||"").trim();if(!t)throw new Error("connectionKey is required");const n=_(r?.serverUrl);if(!n)throw new Error("serverUrl is required to rotate a target connectionKey");const o=T();let i;const s=await D(o,()=>{if(!p.existsSync(o))return!1;const l=JSON.parse(p.readFileSync(o,"utf-8")),I=re(l),v=String(r?.instanceId||"").trim(),U=String(r?.credentialId||"").trim(),A=I.filter(K=>{if(_(K.serverUrl||K.schedulerBaseUrl)!==n)return!1;if(v&&String(K.instanceId||"").trim()===v)return!0;if(U){const E=String(K.credentialId||"").trim(),ae=String(K.userKey||"").trim();if(E===U||ae===U)return!0}return!v&&!U&&I.filter(E=>_(E.serverUrl||E.schedulerBaseUrl)===n).length===1});if(A.length!==1)throw new Error(`\u65E0\u6CD5\u552F\u4E00\u5B9A\u4F4D connectionKey \u5BF9\u5E94 target\uFF08server=${n}, matches=${A.length}\uFF09`);const R=A[0];return String(R.connectionKey??"").trim()===t?!1:(i={targetKey:x(String(R.serverUrl??R.schedulerBaseUrl??""),R.credentialId||R.userKey),previousConnectionKey:String(R.connectionKey??"").trim()},R.connectionKey=t,String(l.connectionKey??"").trim()&&delete l.connectionKey,p.writeFileSync(o,JSON.stringify(l,null,2),"utf-8"),a.info(`[AutoRegister] \u5DF2\u8F6E\u6362 target connectionKey \u5E76\u5199\u56DE config.json: ${o}`),!0)}),c=!!process.env.AWS_BRIDGE_CONNECTION_KEY;c&&s&&(process.env.AWS_BRIDGE_CONNECTION_KEY=t,a.warn("[AutoRegister] \u68C0\u6D4B\u5230 AWS_BRIDGE_CONNECTION_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u5DF2\u540C\u6B65\u66F4\u65B0\u5F53\u524D\u8FDB\u7A0B\uFF1B\u91CD\u542F bridge \u540E\u4F1A\u56DE\u5230\u73AF\u5883\u53D8\u91CF\u4E2D\u7684\u65E7\u503C\uFF0C\u8BF7\u4E00\u5E76\u4FEE\u6539\u90E8\u7F72\u914D\u7F6E"));const u=h().map(l=>String(l.connectionKey||"").trim()).filter(l=>l.length>0);return k=s?[...new Set([t,...u])]:[...new Set(u)],s&&i&&i.previousConnectionKey!==t&&B.set(i.targetKey,{key:i.previousConnectionKey,expiresAt:Date.now()+Ie}),{envOverrideWarning:c,rotated:s}}async function oe(e){let r=0;for(const t of e)String(t.connectionKey||"").trim()||(t.connectionKey=`awsb_${ce(24).toString("base64url")}`,r+=1,k.includes(t.connectionKey)||(k=[...k,t.connectionKey]),await Oe(t,t.connectionKey));r!==0&&a.info(`[AutoRegister] ${r} \u4E2A\u76EE\u6807\u7F3A\u5C11 connectionKey\uFF0C\u5DF2\u6309 target \u72EC\u7ACB\u751F\u6210\u5E76\u5199\u5165 config.json`)}function J(e){if(!e)return;const r=Object.entries(d.registrations).find(([,i])=>i===e);if(!r)return;const t=r[0],n=F(t),o=h().find(i=>N(i)===t);return{serverUrl:n,identityKey:o?y(o):void 0,targetKey:t}}async function be(e){const r=J(e),t=r?.serverUrl||j(b(),e),n=G(t,r?.identityKey);if(!n)throw new Error("runtimeAccessToken is required to unregister bridge instance");return(await $.post(`${t}/api/instances/unregister`,{instanceId:e},{headers:{"Content-Type":"application/json","X-Runtime-Token":n},timeout:1e4})).data}async function Ze(e){const r=e?[{...W(),...e}]:h();await oe(r);const n=(r.length>1?await Promise.all(r.map(i=>q(i))):[await q(r[0]||W())]).every(Boolean),o=r.every(i=>!i.enabled||!i.userKey&&!i.machineToken);if(!e&&(n||o)){const{stopTunnelClientsExcept:i}=await import("./tunnel-client.js");i(r.filter(s=>!!s.serverUrl).map(s=>x(s.serverUrl,y(s))))}return n}async function q(e,r){if(!e.enabled)return a.info("[AutoRegister] \u81EA\u52A8\u6CE8\u518C\u672A\u542F\u7528"),a.info(`[AutoRegister] \u8981\u542F\u7528\u81EA\u52A8\u6CE8\u518C\uFF0C\u8BF7\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6 ${T()} \u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AWS_AUTO_REGISTER=true`),!1;if(!e.userKey&&!e.machineToken)return a.error("[AutoRegister] \u7F3A\u5C11\u6CE8\u518C\u51ED\u636E\uFF08\u5728\u914D\u7F6E\u6587\u4EF6\u4E2D\u8BBE\u7F6E userKey \u6216 machineToken\uFF0C\u4E5F\u53EF\u7528 `awsb pair --ticket ...` \u914D\u5BF9\uFF09"),!1;String(e.connectionKey||"").trim()||await oe([e]);const t=ie(e);t&&!e.instanceId&&(e.instanceId=`bridge-${ue().replace(/-/g,"").slice(0,12)}`,a.info(`[AutoRegister] \u96A7\u9053\u6A21\u5F0F\uFF1A\u5DF2\u9884\u58F0\u660E instanceId=${e.instanceId}`));const n=r?.backgroundRetry?1:e.maxRetries??3,o=e.retryInterval??2e3;for(let i=1;i<=n;i++){try{d.lastAttempt=new Date;const s=await Be(e);if(s.success){if(d.registered=!0,d.instanceId=s.instanceId,s.credentialId&&(e.credentialId=s.credentialId,e.credentialType=s.credentialType),e.credentialRef=s.credentialRef,s.tenantId&&(e.tenantId=s.tenantId),s.userId&&(e.userId=s.userId),s.instanceId&&(d.registrations[N(e)]=s.instanceId,e.instanceId=s.instanceId),d.error=void 0,a.info("[AutoRegister] \u2713 \u6CE8\u518C\u6210\u529F\uFF01"),a.info(`[AutoRegister] \u5B9E\u4F8B ID: ${s.instanceId}`),a.info(`[AutoRegister] \u6D88\u606F: ${s.message}`),a.info(`[AutoRegister] \u66F4\u65B0: ${s.isUpdate?"\u662F":"\u5426"}`),s.runtimeAccessToken)try{const c=e.serverUrl||m;H({accessToken:s.runtimeAccessToken,instanceId:s.instanceId,userId:s.userId,schedulerBaseUrl:c,userKey:y(e)});const u=S(s.serverUrl);u&&u!==S(c)&&(V({serverBaseUrl:u,accessToken:s.runtimeAccessToken,userKey:y(e)}),a.info(`[AutoRegister] \u68C0\u6D4B\u5230 server \u4E0B\u53D1\u5730\u5740(${u})\u4E0E\u8FDE\u63A5\u5730\u5740(${c})\u4E0D\u540C\uFF0C\u5DF2\u6309\u4E24\u4E2A\u5730\u5740\u5206\u522B\u4FDD\u5B58 scoped token`)),a.info("[AutoRegister] \u5DF2\u4FDD\u5B58\u8C03\u5EA6\u4E2D\u5FC3\u4E0B\u53D1\u7684 runtime access token"),s.instanceId&&Ce(e.serverUrl||m,s.instanceId,e.userKey,s.credentialId,s.credentialRef,s.credentialType,e.machineToken,s.tenantId,s.userId).catch(l=>{a.warn(`[AutoRegister] \u56DE\u5199 instanceId \u5230 config.json \u5931\u8D25: ${l.message}`)})}catch(c){const u=c;return a.error(`[AutoRegister] \u4FDD\u5B58 runtime access token \u5931\u8D25: ${u.message}`),d.error=u.message,!1}else a.warn("[AutoRegister] \u6CE8\u518C\u54CD\u5E94\u672A\u5305\u542B runtime access token\uFF0CMCP \u5C06\u65E0\u6CD5\u8BBF\u95EE\u8C03\u5EA6\u4E2D\u5FC3\uFF0C\u8BF7\u68C0\u67E5 server \u7684 bridge \u5B9E\u4F8B token \u9881\u53D1\u903B\u8F91");if(t&&s.instanceId&&s.runtimeAccessToken){const{startTunnelClient:c}=await import("./tunnel-client.js"),u=e.serverUrl||m;c(u,s.instanceId,y(e)),a.info(`[AutoRegister] \u96A7\u9053\u5BA2\u6237\u7AEF\u5DF2\u542F\u52A8: server=${u}, instanceId=${s.instanceId}${e.userKey?`, userKey=****${e.userKey.slice(-4)}`:""}`)}return!0}else a.warn(`[AutoRegister] \u6CE8\u518C\u5931\u8D25: ${s.message}`),d.error=s.message}catch(s){const c=s,u=c.response?.data?JSON.stringify(c.response.data):c.message;a.error(`[AutoRegister] \u6CE8\u518C\u8BF7\u6C42\u5931\u8D25 (\u5C1D\u8BD5 ${i}/${n}): ${u}`),d.error=u}i<n&&(a.info(`[AutoRegister] ${o/1e3} \u79D2\u540E\u91CD\u8BD5...`),await new Promise(s=>setTimeout(s,o)))}return r?.backgroundRetry?a.warn(`[AutoRegister] \u540E\u53F0\u91CD\u8BD5\u6CE8\u518C\u4ECD\u672A\u6210\u529F (server=${e.serverUrl||m}): ${d.error||"\u672A\u77E5"}`):(a.error("[AutoRegister] \u2717 \u6CE8\u518C\u5931\u8D25\uFF0C\u5DF2\u8FBE\u6700\u5927\u91CD\u8BD5\u6B21\u6570"),a.error("[AutoRegister] ========================================"),a.error("[AutoRegister] \u81EA\u52A8\u6CE8\u518C\u5DF2\u542F\u7528\u4F46\u6CE8\u518C\u5931\u8D25\uFF0C\u5B9E\u4F8B\u6682\u65F6\u65E0\u6CD5\u6B63\u5E38\u5DE5\u4F5C"),a.error("[AutoRegister] \u8BF7\u68C0\u67E5\u4EE5\u4E0B\u914D\u7F6E\uFF1A"),a.error(`[AutoRegister] - \u7528\u6237 Key (userKey): ${e.userKey?"****"+e.userKey.slice(-4):"(\u672A\u8BBE\u7F6E)"}`),a.error(`[AutoRegister] - \u79DF\u6237 ID (tenantId): ${e.tenantId||"(\u53EF\u9009\uFF0C\u5C06\u81EA\u52A8\u63A8\u5BFC)"}`),a.error(`[AutoRegister] - \u5B9E\u4F8B\u540D (instanceName): ${e.instanceName||"(\u4F7F\u7528\u4E3B\u673A\u540D)"}`),a.error(`[AutoRegister] - \u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740: ${e.serverUrl||m}`),a.error(`[AutoRegister] - \u6700\u540E\u9519\u8BEF: ${d.error||"\u672A\u77E5"}`),a.error("[AutoRegister] ========================================"),a.error(`[AutoRegister] \u914D\u7F6E\u6765\u6E90\uFF1A${T()} \u6216\u73AF\u5883\u53D8\u91CF`),a.warn(`[AutoRegister] \u5C06\u5728\u540E\u53F0\u6301\u7EED\u91CD\u8BD5\u6CE8\u518C\uFF08\u6BCF ${Math.round(Z()/1e3)} \u79D2\uFF09\uFF0C\u76F4\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u5EFA\u7ACB\u8FDE\u63A5`)),ee(e),!1}async function er(){const e=b();if(!e.userKey&&!e.machineToken)return{success:!1,error:"userKey or machineToken is required for runtime token refresh"};const r=pe(),t=j(e),n=L(e);try{const o=await $.post(`${t}/api/instances/runtime-tokens/refresh`,{tenantId:e.tenantId,userKey:e.machineToken?void 0:e.userKey,machineToken:e.machineToken,instanceId:r.instanceId,instanceName:e.instanceName,runtimeBridgeBaseUrl:n},{headers:{"Content-Type":"application/json"},timeout:1e4});if(!o.data.success||!o.data.runtimeAccessToken)return{success:!1,error:o.data.message||"scheduler did not return runtimeAccessToken"};const s=G(t,y(e))!==o.data.runtimeAccessToken;return H({accessToken:o.data.runtimeAccessToken,instanceId:r.instanceId,userId:o.data.userId||e.userKey||e.credentialId||"",schedulerBaseUrl:t,userKey:y(e)}),{success:!0,runtimeAccessToken:o.data.runtimeAccessToken,updated:s}}catch(o){const i=o;return{success:!1,error:i.response?.data?JSON.stringify(i.response.data):i.message}}}async function rr(e,r){const t=_(e);if(!t)return{success:!1,error:"serverUrl is required for runtime token refresh"};const n=h().filter(u=>_(u.serverUrl)===t),o=String(r||"").trim(),i=o?n.find(u=>String(u.userKey||"").trim()===o||String(u.credentialId||"").trim()===o):n.length===1?n[0]:void 0;if(!i)return{success:!1,error:o?"no auto-register target matches this identity":"multiple auto-register targets share this serverUrl; specify the target identity"};if(!i.userKey&&!i.machineToken)return{success:!1,error:"userKey or machineToken is required for runtime token refresh"};const s={instanceId:$e(t,y(i))},c=L(i);try{const u=await $.post(`${t}/api/instances/runtime-tokens/refresh`,{tenantId:i.tenantId,userKey:i.machineToken?void 0:i.userKey,machineToken:i.machineToken,instanceId:s.instanceId,instanceName:i.instanceName,runtimeBridgeBaseUrl:c},{headers:{"Content-Type":"application/json"},timeout:1e4});if(!u.data.success||!u.data.runtimeAccessToken)return{success:!1,error:u.data.message||"scheduler did not return runtimeAccessToken"};const I=G(t,y(i))!==u.data.runtimeAccessToken;return V({serverBaseUrl:t,accessToken:u.data.runtimeAccessToken,userKey:y(i)}),{success:!0,runtimeAccessToken:u.data.runtimeAccessToken,updated:I}}catch(u){const l=u;return{success:!1,error:l.response?.data?JSON.stringify(l.response.data):l.message}}}async function tr(){if(!d.registered||!d.instanceId)return a.info("[AutoRegister] \u5B9E\u4F8B\u672A\u6CE8\u518C\uFF0C\u65E0\u9700\u6CE8\u9500"),!0;const e=J(d.instanceId),r=e?h().find(t=>N(t)===e.targetKey):void 0;if(r&&(String(r.machineToken||"").trim()||r.credentialType==="MACHINE_TOKEN"))return a.info(`[AutoRegister] \u673A\u5668\u51ED\u636E\u76EE\u6807\uFF08instanceId=${d.instanceId}\uFF09\u8DF3\u8FC7\u6CE8\u9500\uFF1A\u914D\u5BF9\u8EAB\u4EFD\u5FC5\u987B\u8DE8\u91CD\u542F\u5B58\u6D3B\uFF0C\u4E0D\u5220\u9664\u8C03\u5EA6\u4E2D\u5FC3\u767B\u8BB0\u884C`),!0;try{const t=await be(d.instanceId);if(t.success){a.info(`[AutoRegister] \u2713 \u6CE8\u9500\u6210\u529F: ${d.instanceId}`);const n=J(d.instanceId),o=n?.serverUrl||j(b(),d.instanceId);M().schedulerBaseUrl&&me(o,n?.identityKey),ge();try{const{stopTunnelClientForTarget:s}=await import("./tunnel-client.js");s(o,n?.identityKey??y(b()))}catch(s){a.warn(`[AutoRegister] \u6CE8\u9500\u540E\u505C\u6B62\u96A7\u9053\u5931\u8D25: ${s.message}`)}if(!n)d.registered=!1,d.instanceId=void 0;else{delete d.registrations[n.targetKey];const s=Object.entries(d.registrations);d.registered=s.length>0,d.instanceId=s[s.length-1]?.[1]}return!0}else return a.warn(`[AutoRegister] \u6CE8\u9500\u5931\u8D25: ${t.message}`),!1}catch(t){const n=t;return a.error(`[AutoRegister] \u6CE8\u9500\u8BF7\u6C42\u5931\u8D25: ${n.message}`),!1}}function nr(){return{...d}}function sr(){return d.registered}function ir(){return d.instanceId}async function or(){const e=b(),r=d.instanceId,t=j(e,r),n={};if(r&&(n.instanceId=r),e.enabled&&(n.runtimeBridgeBaseUrl=L(e)),!n.instanceId&&!n.runtimeBridgeBaseUrl)return a.info("[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\uFF1A\u6CA1\u6709 instanceId \u6216 runtimeBridgeBaseUrl\uFF0C\u8DF3\u8FC7\u6E05\u7406"),{success:!0,agentCount:0};try{a.info("[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\uFF1A\u6B63\u5728\u901A\u77E5\u8C03\u5EA6\u4E2D\u5FC3..."),a.info(`[AutoRegister] instanceId: ${n.instanceId||"(\u65E0)"}`),a.info(`[AutoRegister] runtimeBridgeBaseUrl: ${n.runtimeBridgeBaseUrl||"(\u65E0)"}`);const o=await $.post(`${t}/api/instances/bridge-restart-cleanup`,n,{headers:{"Content-Type":"application/json"},timeout:1e4});return o.data.success?(a.info(`[AutoRegister] \u2713 Bridge \u91CD\u542F\u6E05\u7406\u6210\u529F\uFF1A${o.data.message}`),a.info(`[AutoRegister] \u6E05\u7406\u4E86 ${o.data.agentCount||0} \u4E2A Agent`),{success:!0,agentCount:o.data.agentCount||0}):(a.warn(`[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\u5931\u8D25\uFF1A${o.data.message}`),{success:!1,error:o.data.message})}catch(o){const i=o,s=i.response?.data?JSON.stringify(i.response.data):i.message;return a.warn(`[AutoRegister] Bridge \u91CD\u542F\u6E05\u7406\u8BF7\u6C42\u5931\u8D25\uFF1A${s}`),{success:!1,error:s}}}export{Ze as autoRegister,or as bridgeRestartCleanup,He as cancelBackgroundRegisterRetries,Te as collapseDuplicatedSecret,qe as findConfigsByConnectionKey,T as getAutoRegisterConfigFilePath,Je as getConfiguredConnectionKeys,Ye as getCurrentConfiguredConnectionKey,ir as getInstanceId,nr as getRegistrationState,sr as isRegistered,ie as isTunnelConfig,Ve as listConfiguredTargetSummaries,W as loadConfig,h as loadConfigs,b as loadPrimaryLifecycleConfig,Qe as replaceConfiguredConnectionKey,er as requestRuntimeAccessTokenRefresh,rr as requestRuntimeAccessTokenRefreshForServer,$e as resolveInstanceIdForTarget,L as resolveRuntimeBridgeBaseUrl,Xe as resolveSchedulerBaseUrlFromToken,Ne as resolveTargetIdentityFromToken,tr as unregister};
|
|
2
2
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomBytes as E}from"node:crypto";import{existsSync as
|
|
1
|
+
import{randomBytes as E}from"node:crypto";import{existsSync as S,mkdirSync as P,readFileSync as w,writeFileSync as x}from"node:fs";import A from"node:os";import _ from"node:path";import N from"axios";import{getActiveBridgeHttpPort as K,getAutoRegisterConfigFilePath as k,getBridgeRuntimePropertiesFilePath as b,schedulerBaseUrl as F}from"../config.js";import{runExclusiveConfigWrite as O}from"../utils/config-write-queue.js";import{logger as R}from"../utils/logger.js";import{normalizeTargetServerUrl as h}from"../utils/target-identity.js";const C="awsp_";function B(r){if(!S(r))return{};try{return JSON.parse(w(r,"utf-8"))}catch(n){throw new Error(`\u65E0\u6CD5\u89E3\u6790\u914D\u7F6E\u6587\u4EF6 ${r}: ${n.message}`)}}function $(r){return Array.isArray(r.autoRegisterTargets)?r.autoRegisterTargets.filter(n=>!!(n&&typeof n=="object"&&!Array.isArray(n))):[]}function M(){return`bridge-${E(6).toString("hex")}`}function j(r,n){return/^https?:\/\//i.test(r)?r:/:[0-9]+$/.test(r)?`http://${r}`:`http://${r}:${n}`}function q(r,n){const s=$(r),l=n.serverUrl?.trim(),e=l?h(l):"",f=String(n.instanceId||"").trim(),y=String(n.targetIdentity||"").trim();let i=-1,c=r,g=!1;if(s.length>0){const d=s.map((o,U)=>({item:o,itemIndex:U})).filter(({item:o})=>!e||h(o.serverUrl||o.schedulerBaseUrl)===e);let m=d;if(f){const o=d.filter(({item:U})=>String(U.instanceId||"").trim()===f);if(o.length>1)throw new Error(`\u5339\u914D\u5230\u591A\u4E2A instanceId=${f} \u7684\u914D\u5BF9\u76EE\u6807\uFF0C\u8BF7\u68C0\u67E5 config.json`);o.length===1?m=o:(m=[],g=!0)}else if(y){if(m=d.filter(({item:o})=>String(o.credentialId||o.userKey||"").trim()===y),m.length>1)throw new Error("\u5339\u914D\u5230\u591A\u4E2A\u914D\u5BF9\u76EE\u6807\uFF0C\u8BF7\u7528 --instance-id \u6216 --target-identity \u7CBE\u786E\u6307\u5B9A");if(m.length===0)throw new Error("\u6307\u5B9A targetIdentity \u7684\u914D\u5BF9\u76EE\u6807\u4E0D\u5B58\u5728")}else if(d.length>1)throw new Error("\u5B58\u5728\u591A\u4E2A\u6CE8\u518C\u76EE\u6807\uFF0C\u8BF7\u7528 --server-url \u642D\u914D --instance-id / --target-identity \u6307\u5B9A\u672C\u6B21\u914D\u5BF9\u76EE\u6807");if(g)i=s.length;else if(i=m[0]?.itemIndex??-1,i<0){if(!e)throw new Error("\u65E0\u6CD5\u786E\u5B9A\u914D\u5BF9\u76EE\u6807\uFF0C\u8BF7\u7528 --server-url \u6307\u5B9A");c={serverUrl:l},i=s.length,g=!0}else c=s[i]||{}}const p=h(l||c.serverUrl||c.schedulerBaseUrl||F);if(!p)throw new Error("\u65E0\u6CD5\u786E\u5B9A\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u8BF7\u7528 --server-url \u6307\u5B9A");const a=n.registerAddr||String(c.registerAddr||c.registerIp||c.virtualIp||"").trim(),u=c.tunnel===!0?!0:c.tunnel===!1?!1:!a;let t=f||String(c.instanceId||"").trim()||void 0,I;if(u){t||(t=M());const d=new URL(p);I=`${d.protocol}//${d.host}/tunnel/${t}`}else I=j(a,K());const v=String(c.connectionKey||"").trim(),T=n.instanceName?.trim()||String(c.instanceName||"").trim()||A.hostname();return{index:i,target:c,serverUrl:p,tunnel:u,instanceId:t,connectionKey:v,instanceName:T,runtimeBridgeBaseUrl:I,isNewTarget:g}}async function G(r,n){return(await N.post(r,n,{headers:{"Content-Type":"application/json"},timeout:15e3})).data}const Y=2500,D=1e3;function z(){try{const r=b();if(!S(r))return null;const s=w(r,"utf-8").split(/\r?\n/).find(e=>/^port\s*[=:]/.test(e.trim()));if(!s)return null;const l=Number(s.split(/[=:]/)[1]?.trim());return Number.isInteger(l)&&l>=1&&l<=65535?l:null}catch{return null}}async function re(){const r=z();if(!r)return!1;const n=`http://127.0.0.1:${r}/runtime/pair-applied`;for(let s=1;s<=2;s+=1)try{return(await N.post(n,{},{timeout:Y})).data?.ok===!0}catch(l){if(l.response?.status===404)return!1;s<2&&await new Promise(f=>setTimeout(f,D))}return!1}async function te(r){const n=String(r.ticket||"").trim();if(!n.startsWith(C))return{success:!1,error:"\u914D\u5BF9\u7968\u636E\u683C\u5F0F\u4E0D\u6B63\u786E\uFF08\u5E94\u4EE5 awsp_ \u5F00\u5934\uFF09\uFF0C\u8BF7\u4ECE\u7BA1\u7406\u9875\u91CD\u65B0\u590D\u5236"};const s=r.configPath||k(),l=B(s);let e;try{e=q(l,r)}catch(a){return{success:!1,error:a.message}}const f={pairingTicket:n,instanceName:e.instanceName,runtimeBridgeBaseUrl:e.runtimeBridgeBaseUrl};e.instanceId&&(f.instanceId=e.instanceId);const y=r.postPair||G;let i;try{i=await y(`${e.serverUrl}/api/instances/pair`,f)}catch(a){const u=a;return{success:!1,error:u.response?.data?.message||u.message||"\u914D\u5BF9\u8BF7\u6C42\u5931\u8D25"}}if(!i?.success||!i.machineToken||!i.instanceId)return{success:!1,error:i?.message||"\u8C03\u5EA6\u4E2D\u5FC3\u672A\u8FD4\u56DE\u673A\u5668\u51ED\u636E"};const c=String(i.connectionKey||"").trim();if(!c)return{success:!1,error:"\u8C03\u5EA6\u4E2D\u5FC3\u672A\u8FD4\u56DE\u8FDE\u63A5\u5BC6\u94A5\uFF08server \u7248\u672C\u8FC7\u65E7\uFF1F\uFF09\uFF0C\u8BF7\u5347\u7EA7 server \u540E\u91CD\u8BD5"};const g=i.instanceId,p=i.instanceName||e.instanceName;try{await O(s,()=>{const a=B(s),u=$(a);let t;if(Array.isArray(a.autoRegisterTargets)&&u.length>0){const I=u.filter(o=>h(o.serverUrl||o.schedulerBaseUrl)===h(e.serverUrl)),v=String(e.target.userKey||"").trim(),T=e.instanceId?I.filter(o=>String(o.instanceId||"").trim()===e.instanceId):[],d=v?I.filter(o=>String(o.userKey||"").trim()===v):[];if(T.length>1||d.length>1)throw new Error(`\u914D\u5BF9\u5199\u56DE\u5931\u8D25\uFF1A\u5339\u914D\u5230\u591A\u4E2A\u76EE\u6807\uFF08server=${e.serverUrl}\uFF09\uFF0C\u8BF7\u7528 instanceId / target identity \u7CBE\u786E\u6307\u5B9A`);const m=T[0]||d[0]||(I.length===1&&!e.isNewTarget?I[0]:void 0);m?t=m:!e.isNewTarget&&e.index>=0&&u[e.index]?t=u[e.index]:(t={serverUrl:e.serverUrl},u.push(t),a.autoRegisterTargets=u)}else t=a;t.serverUrl=t.serverUrl||e.serverUrl,t.instanceName=p,t.instanceId=g,t.credentialId=g,delete t.credentialRef,t.credentialType=i.credentialType||"MACHINE_TOKEN",t.machineToken=i.machineToken,t.connectionKey=c,i.tenantId&&(t.tenantId=i.tenantId),i.userId&&(t.userId=i.userId),Array.isArray(a.autoRegisterTargets)&&a.autoRegisterTargets.length>0&&String(a.connectionKey||"").trim()&&delete a.connectionKey,P(_.dirname(s),{recursive:!0}),x(s,JSON.stringify(a,null,2),"utf-8")})}catch(a){return{success:!1,error:a.message}}return e.instanceId&&e.instanceId!==g&&R.info(`[Pair] server \u5DF2\u6309\u7968\u636E\u786E\u5B9A\u5B9E\u4F8B id\uFF1A${g}\uFF08\u672C\u5730\u6B64\u524D\u4E3A ${e.instanceId}\uFF09\uFF0Cconfig.json \u5DF2\u5199\u56DE\u65B0 id`),R.info(`[Pair] \u914D\u5BF9\u6210\u529F: server=${e.serverUrl}, instanceId=${g}, machineToken \u5DF2\u5199\u5165 ${s}`),{success:!0,instanceId:g,serverUrl:e.serverUrl}}export{re as notifyRunningBridgeAfterPair,te as pairBridge,z as readRunningBridgePort,q as resolvePairTarget};
|
|
2
2
|
|
|
@@ -41,6 +41,20 @@ export declare class PluginDeploymentStore {
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
resolveDeployedSourceDir(name: string): string | null;
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
private readMergedManifest;
|
|
50
|
+
|
|
51
|
+
hasLegacyManifestEntry(pluginName: string): boolean;
|
|
52
|
+
|
|
53
|
+
activeSharedManifest(): SharedPluginEntry[];
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
44
58
|
readSharedManifest(): SharedPluginEntry[];
|
|
45
59
|
writeSharedManifest(entries: SharedPluginEntry[]): void;
|
|
46
60
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{cpSync as S,existsSync as
|
|
2
|
-
`,"utf8")}async migrateLegacySharedIfNeeded(){try{if(
|
|
1
|
+
import{cpSync as S,existsSync as a,lstatSync as M,mkdirSync as f,readFileSync as d,readdirSync as P,renameSync as D,rmSync as u,symlinkSync as w,writeFileSync as N}from"node:fs";import{join as n,resolve as m}from"node:path";import{getBridgeInstancePluginsDir as v}from"../config.js";import{sanitizeAgentId as F,sanitizeArchiveFileName as k,withFileLock as L}from"../utils/file-utils.js";import{createLogger as $}from"../utils/logger.js";import{isValidPluginName as h,PLUGIN_NAME_PATTERN as A}from"./plugin-manifest.js";import{extractPluginZip as R}from"./plugin-package.js";const l=$("plugin-manager"),E=process.platform==="win32"?"junction":"dir";class x{constructor(e,i,r,t){this.rootDir=e,this.runtimeRootDir=i,this.loader=r,this.legacyRootDir=t}deployedDir(){return n(this.rootDir,"deployed")}deployedManifestFile(){return n(this.deployedDir(),"manifest.json")}legacyDeployedDir(){return this.legacyRootDir?n(this.legacyRootDir,"deployed"):""}legacyDeployedManifestFile(){return this.legacyDeployedDir()?n(this.legacyDeployedDir(),"manifest.json"):""}deployedPluginDir(e){if(!h(e))throw new Error(`\u975E\u6CD5\u63D2\u4EF6\u540D\uFF08\u4EC5\u5141\u8BB8 ${A}\uFF09: ${JSON.stringify(e)}`);const i=n(this.deployedDir(),e),r=m(this.deployedDir());if(m(i)!==n(r,e))throw new Error(`\u63D2\u4EF6\u76EE\u5F55\u8D8A\u51FA\u90E8\u7F72\u533A: ${e}`);return i}legacySharedDir(){return n(this.rootDir,"shared")}deployedPluginHealthy(e){return a(n(this.deployedPluginDir(e),"plugin.json"))||this.legacyPluginHealthy(e)}legacyPluginHealthy(e){if(!this.legacyDeployedDir())return!1;try{const i=n(this.legacyDeployedDir(),e);return a(n(i,"plugin.json"))}catch{return!1}}resolveDeployedSourceDir(e){return a(n(this.deployedPluginDir(e),"plugin.json"))?this.deployedPluginDir(e):this.legacyPluginHealthy(e)?n(this.legacyDeployedDir(),e):null}readMergedManifest(){const e=new Map;if(this.legacyDeployedManifestFile())try{const i=JSON.parse(d(this.legacyDeployedManifestFile(),"utf8"));for(const r of Array.isArray(i.plugins)?i.plugins:[])r&&h(r.pluginName)&&e.set(r.pluginName,r)}catch{}try{const i=JSON.parse(d(this.deployedManifestFile(),"utf8"));for(const r of Array.isArray(i.plugins)?i.plugins:[])r&&h(r.pluginName)&&e.set(r.pluginName,r)}catch{}return[...e.values()]}hasLegacyManifestEntry(e){if(!this.legacyDeployedManifestFile())return!1;try{const i=JSON.parse(d(this.legacyDeployedManifestFile(),"utf8"));return(Array.isArray(i.plugins)?i.plugins:[]).some(r=>r&&h(r.pluginName)&&r.pluginName===e)}catch{return!1}}activeSharedManifest(){return this.readMergedManifest().filter(e=>!e.removed)}readSharedManifest(){return this.activeSharedManifest()}writeSharedManifest(e){f(this.deployedDir(),{recursive:!0}),N(this.deployedManifestFile(),`${JSON.stringify({version:2,plugins:e},null,2)}
|
|
2
|
+
`,"utf8")}async migrateLegacySharedIfNeeded(){try{if(a(this.deployedManifestFile()))return;const e=n(this.legacySharedDir(),"manifest.json");if(!a(e))return;const i=JSON.parse(d(e,"utf8")),r=Array.isArray(i.plugins)?i.plugins:[];if(r.length===0)return;const t=[];let s=0,o=0;for(const y of r){const g=k(y.zipFile??"","");if(!y.pluginName||!g)continue;const p=n(this.legacySharedDir(),g);if(a(p)){s+=1;try{const c=await this.deployPluginZipToPersistentDir(d(p),y.pluginName);if(!c){o+=1;continue}t.push(c),l.info("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u5230\u90E8\u7F72\u533A: %s",y.pluginName)}catch(c){o+=1,l.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6 %s \u8FC1\u79FB\u5931\u8D25: %s",y.pluginName,c instanceof Error?c.message:String(c))}}}t.length>0&&this.writeSharedManifest(t),o===0?u(this.legacySharedDir(),{recursive:!0,force:!0}):l.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u90E8\u5206\u5931\u8D25\uFF08%d/%d\uFF09\uFF0C\u4FDD\u7559 shared/ \u76EE\u5F55\u4EE5\u514D\u6570\u636E\u4E22\u5931",o,s)}catch(e){l.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u672A\u6267\u884C\uFF08\u4E0B\u6B21\u91CD\u8BD5\uFF09: %s",e instanceof Error?e.message:String(e))}}async deployPluginZipToPersistentDir(e,i){if(!e||e.length===0)return null;const r=n(this.rootDir,"tmp",`migrate-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const t=await R(e,r),s=this.loader.loadManifestOnly(t.pluginDir);if(!s.ok)return null;const o=s.manifest.name;return i&&o!==i?null:(this.replaceDeployedDir(o,t.pluginDir),{pluginName:o,zipFile:`${o}.zip`,fileName:`${o}.zip`,defaultEnabled:!0})}finally{u(r,{recursive:!0,force:!0})}}replaceDeployedDir(e,i){f(this.deployedDir(),{recursive:!0});const r=this.deployedPluginDir(e),t=n(this.deployedDir(),`.old-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);a(r)&&D(r,t);try{S(i,r,{recursive:!0,filter:s=>!/[\\/]\.git([\\/]|$)/.test(s)&&!/[\\/]node_modules([\\/]|$)/.test(s)})}catch(s){throw a(t)&&(u(r,{recursive:!0,force:!0}),D(t,r)),s}a(t)&&u(t,{recursive:!0,force:!0})}removeDeployedDir(e){u(this.deployedPluginDir(e),{recursive:!0,force:!0})}async updateSharedManifest(e){await L(this.deployedManifestFile(),async()=>{this.writeSharedManifest(e(this.readMergedManifest()))})}runtimeDirFor(e){return this.runtimeRootDir?n(this.runtimeRootDir,F(e)):v(e)}runtimeLinkPath(e,i){return n(this.runtimeDirFor(e),i)}ensureRuntimeLink(e,i){const r=this.runtimeLinkPath(e,i),t=this.resolveDeployedSourceDir(i);if(!t)return!1;f(this.runtimeDirFor(e),{recursive:!0});try{if(a(r)){let s=!1;try{s=M(r).isSymbolicLink()}catch{s=!1}if(s)try{return P(r),!0}catch{}u(r,{recursive:!0,force:!0})}return w(t,r,E),l.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u5EFA\u7ACB: %s \u2192 %s",e,r,t),!0}catch(s){return l.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5931\u8D25\uFF08%s \u2192 %s\uFF09: %s",e,i,t,s instanceof Error?s.message:String(s)),!1}}removeRuntimeLink(e,i){const r=this.runtimeLinkPath(e,i);try{a(r)&&(u(r,{recursive:!0,force:!0}),l.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u79FB\u9664: %s",e,i))}catch(t){l.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u79FB\u9664\u5931\u8D25\uFF08%s\uFF09: %s",e,i,t instanceof Error?t.message:String(t))}}}export{x as PluginDeploymentStore};
|
|
3
3
|
|
|
@@ -29,11 +29,14 @@ export interface PluginManagerOptions {
|
|
|
29
29
|
rootDir?: string;
|
|
30
30
|
|
|
31
31
|
runtimeRootDir?: string;
|
|
32
|
+
|
|
33
|
+
legacyRootDir?: string;
|
|
32
34
|
}
|
|
33
35
|
export declare class PluginManager {
|
|
34
36
|
private agents;
|
|
35
37
|
private readonly rootDir?;
|
|
36
38
|
private readonly runtimeRootDir?;
|
|
39
|
+
private readonly legacyRootDir?;
|
|
37
40
|
|
|
38
41
|
private readonly deployStores;
|
|
39
42
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{existsSync as y,mkdirSync as $,readFileSync as S,readdirSync as N,rmSync as M,writeFileSync as R}from"node:fs";import{dirname as C,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 L,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 O}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 E,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}deployForScope(e){const t=this.rootDir??E(e),s=`scope:${t}`;let n=this.deployStores.get(s);return n||(n=new A(t,this.runtimeRootDir,this.loader,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.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?E(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 N(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 O(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 L(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 L(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?E(r):this.rootDir??k(),"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const o=await O(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}`,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 N(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 J(e),hydrated:!1,queue:Promise.resolve(),eventSeq:0},this.agents.set(e,t)),t}async hydrate(e){const t=this.agentState(e);return t.hydratePromise||(t.hydratePromise=this.doHydrate(t).catch(s=>{throw t.hydratePromise=void 0,s})),await t.hydratePromise,t}async doHydrate(e){const t=e.agentId;await this.deployForAgent(t).migrateLegacySharedIfNeeded();const s=this.installedFile(t);if(!y(s)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const n=JSON.parse(S(s,"utf8")),r=this.readSecrets(t);for(const i of n.plugins??[]){if(String(i.source||"").startsWith("shared:"))continue;const o=f(this.dataRootForAgent(t),"cache",P(t),i.name);if(!y(f(o,"plugin.json"))){d.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 d.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);d.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,r)}await this.syncSharedIntoState(e).catch(n=>{d.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 f(this.dataRootForAgent(e),"agents",P(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);$(
|
|
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 f(this.dataRootForAgent(e),"agents",P(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(S(this.secretsFile(e),"utf8"));if(!F(t)||!F(t.secrets))return{};const s={};for(const[n,r]of Object.entries(t.secrets)){if(!F(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{
|
|
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=e?this.deployForScope(e):this.deploy;await t.migrateLegacySharedIfNeeded();const 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(S(f(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){const s=(e?this.deployForScope(e):this.deploy).readSharedManifest(),n={};for(const r of s)n[r.pluginName]=r.defaultEnabled!==!1;return n}async removeDeployedPlugin(e,t){if(!Z(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};const s=t?this.deployForScope(t):this.deploy;await s.migrateLegacySharedIfNeeded();for(const n of[...this.agents.values()]){if(!n.hydrated)continue;if(t){const i=w(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(
|
|
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 J(e),hydrated:!1,queue:Promise.resolve(),eventSeq:0},this.agents.set(e,t)),t}async hydrate(e){const t=this.agentState(e);return t.hydratePromise||(t.hydratePromise=this.doHydrate(t).catch(s=>{throw t.hydratePromise=void 0,s})),await t.hydratePromise,t}async doHydrate(e){const t=e.agentId;await this.deployForAgent(t).migrateLegacySharedIfNeeded();const s=this.installedFile(t);if(!y(s)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const n=JSON.parse(S(s,"utf8")),r=this.readSecrets(t);for(const i of n.plugins??[]){if(String(i.source||"").startsWith("shared:"))continue;const o=f(this.dataRootForAgent(t),"cache",P(t),i.name);if(!y(f(o,"plugin.json"))){d.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 d.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);d.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,r)}await this.syncSharedIntoState(e).catch(n=>{d.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 f(this.dataRootForAgent(e),"agents",P(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);$(L(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)}));N(t,`${JSON.stringify({version:1,plugins:s},null,2)}
|
|
3
|
+
`,"utf8")}sensitiveKeysOf(e){return(e.userConfig??[]).filter(t=>t?.sensitive===!0&&typeof t.key=="string").map(t=>t.key)}stripSensitiveValues(e){const t=new Set(this.sensitiveKeysOf(e)),s={};for(const[n,r]of Object.entries(e.configValues))t.has(n)||(s[n]=r);return s}secretsFile(e){return f(this.dataRootForAgent(e),"agents",P(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(S(this.secretsFile(e),"utf8"));if(!F(t)||!F(t.secrets))return{};const s={};for(const[n,r]of Object.entries(t.secrets)){if(!F(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}$(L(s),{recursive:!0}),N(s,`${JSON.stringify({version:1,secrets:t},null,2)}
|
|
4
|
+
`,{encoding:"utf8",mode:384})}ensureListenersRunning(e){this.listeners.ensureRunning(e)}startAllListeners(e,t){this.listeners.startAll(e,t)}stopAllListeners(e){this.listeners.stopAll(e)}enqueueEvent(e,t,s,n){this.listeners.enqueue(e,t,s,n)}async getLogs(e,t){const n=(await this.hydrate(e)).plugins.get(t);if(!n)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5`,logs:[]};const 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=e?this.deployForScope(e):this.deploy;await t.migrateLegacySharedIfNeeded();const 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(S(f(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){const s=(e?this.deployForScope(e):this.deploy).readSharedManifest(),n={};for(const r of s)n[r.pluginName]=r.defaultEnabled!==!1;return n}async removeDeployedPlugin(e,t){if(!Z(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};const s=t?this.deployForScope(t):this.deploy;await s.migrateLegacySharedIfNeeded();for(const n of[...this.agents.values()]){if(!n.hydrated)continue;if(t){const i=w(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=>{const r=n.filter(i=>i.pluginName!==e);return s.hasLegacyManifestEntry(e)&&r.push({pluginName:e,removed:!0}),r});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 d.info("[shared] \u63D2\u4EF6\u5DF2\u4ECE bridge \u79FB\u9664\u90E8\u7F72: %s",e),{ok:!0}}}const X=new G;q("plugins",v=>v?X.deployedDefaultEnabled(v):{});const ye=v=>_(v,"plugins");export{G as PluginManager,De as isValidPluginName,X as pluginManager,ye as reconcilePluginsDefaultsFromDeployed,Pe as renderCommandArgv,ke as validateAgainstSchema,ve as validateManifest};
|
|
5
5
|
|
|
@@ -73,10 +73,18 @@ export interface AgentState {
|
|
|
73
73
|
|
|
74
74
|
export interface SharedPluginEntry {
|
|
75
75
|
pluginName: string;
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
|
|
77
|
+
zipFile?: string;
|
|
78
|
+
|
|
79
|
+
fileName?: string;
|
|
78
80
|
|
|
79
81
|
defaultEnabled?: boolean;
|
|
80
82
|
|
|
81
83
|
sha256?: string;
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
removed?: boolean;
|
|
82
90
|
}
|
|
@@ -123,4 +123,5 @@ export declare function requireUserScope(req: Request, res: Response, next: Next
|
|
|
123
123
|
export declare function getUserScopeOrThrow(req: Request): UserScope;
|
|
124
124
|
|
|
125
125
|
export declare function setAgentOwnershipChecker(checker: (agentId: string, scope: UserScope) => boolean): void;
|
|
126
|
+
export declare function setMachineOwnershipSelfHeal(selfHeal: (agentId: string, scope: UserScope) => boolean): void;
|
|
126
127
|
export declare function requireAgentOwnership(req: Request, res: Response, next: NextFunction): void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import o from"node:path";import{getBridgeDataHome as x}from"../config.js";import{loadRuntimeBinding as m}from"./runtime-binding.js";const y=/^[a-zA-Z0-9_.-]+$/,p=Object.freeze({tenantId:"_legacy",userId:"_legacy"}),U="x-tenant-id",D="x-user-id";function i(e){const r=String(e??"").replace(/[^a-zA-Z0-9_.-]/g,"_");return/^\.+$/.test(r)?"_":r.replace(/^\.+/g,"")}function b(e){return typeof e=="string"&&e.length>0&&y.test(e)}function a(e,r){const t=String(e??"").trim(),n=String(r??"").trim();return!t||!n?null:{tenantId:t,userId:n}}function _(e,r){return!e||!r?!1:i(e.tenantId)===i(r.tenantId)&&i(e.userId)===i(r.userId)}function L(e){return _(e,p)}function f(){return String(process.env.AWS_BRIDGE_LEGACY_SCOPE||"").trim().toLowerCase()==="1"}function O(){return String(process.env.AWS_BRIDGE_DISABLE_LEGACY_FALLBACK||"").trim().toLowerCase()==="1"}function j(e){return[i(e.tenantId),i(e.userId)]}function s(e){return o.join(x(),"users",...j(e))}function R(e){return o.join(s(e),"ai","acode.json")}function A(e){return o.join(s(e),"skills")}function P(e){return o.join(A(e),"deployed")}function d(e){return o.join(s(e),"mcp")}function B(e){return o.join(d(e),"deployed")}function F(e){return o.join(d(e),"packages")}function E(e){return o.join(s(e),"plugins")}function H(e){return o.join(E(e),"deployed")}function g(e,r){return o.join(s(e),"capability",r)}function T(e,r){return o.join(g(e,r),"config.json")}function z(e,r,t){return o.join(g(e,r),"agents",`${i(t)}.json`)}function h(...e){for(const r of e){const t=String(r??"").trim();if(t)return t}return""}function u(e){const r=a(e.header(U),e.header(D));if(r)return r;const t=e.body??{};return a(t.tenantId,h(t.ownerUserId,t.userId))}function S(){try{const e=m();return e?.userId?a("_local",e.userId):null}catch{return null}}function G(e,r,t){const n=u(e)??S();n?e.userScope=n:f()&&(e.userScope={...p}),t()}function v(e,r,t){if(e.userScope){t();return}const n=u(e)??S();if(n){e.userScope=n,t();return}if(f()){e.userScope={...p},t();return}r.status(400).json({ok:!1,error:"user-scope-required",message:"\u8BF7\u6C42\u7F3A\u5C11 X-Tenant-Id / X-User-Id\uFF08\u6216\u4F53 tenantId + ownerUserId\uFF09\u8EAB\u4EFD"})}function M(e){const r=e.userScope??u(e);if(!r)throw new Error("user-scope-required: \u8BF7\u6C42\u7F3A\u5C11 (tenantId, userId) \u8EAB\u4EFD");return r}let l=null,I=null;function N(e){l=e}function Y(e){I=e}function $(e,r,t){const n=String(e.params?.agentId??e.body?.agentId??"").trim();if(!n){t();return}const c=e.userScope??u(e);if(!c){r.status(400).json({ok:!1,error:"user-scope-required",message:"agent \u7EA7\u64CD\u4F5C\u7F3A\u5C11 (tenantId, userId) \u8EAB\u4EFD"});return}if(l&&!l(n,c)){if(I?.(n,c)){t();return}r.status(403).json({ok:!1,error:"agent-ownership-mismatch",message:"\u8BE5\u5B9E\u4F8B\u4E0D\u5C5E\u4E8E\u5F53\u524D\u7528\u6237"});return}t()}export{p as LEGACY_SCOPE,y as SCOPE_ID_PATTERN,U as TENANT_HEADER,D as USER_HEADER,u as extractUserScope,R as getUserAiConfigFile,z as getUserCapabilityAgentFile,T as getUserCapabilityDefaultsFile,g as getUserCapabilityDir,B as getUserMcpDeployedDir,d as getUserMcpDir,F as getUserMcpPackagesDir,H as getUserPluginsDeployedDir,E as getUserPluginsDir,M as getUserScopeOrThrow,s as getUserScopeRoot,P as getUserSkillsDeployedDir,A as getUserSkillsDir,O as isLegacyFallbackDisabled,L as isLegacyScope,f as isLegacyScopeAllowed,b as isValidScopeId,a as normalizeUserScope,$ as requireAgentOwnership,v as requireUserScope,G as resolveUserScope,i as sanitizeScopeId,_ as scopeEquals,N as setAgentOwnershipChecker,Y as setMachineOwnershipSelfHeal};
|
|
2
2
|
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
|
|
@@ -223,7 +225,20 @@ export declare class BackgroundCommandManager {
|
|
|
223
225
|
private appendToFile;
|
|
224
226
|
private finalizeTask;
|
|
225
227
|
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
226
232
|
private evictTask;
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
taskCapacity(agentId: string): {
|
|
238
|
+
limit: number;
|
|
239
|
+
used: number;
|
|
240
|
+
remaining: number;
|
|
241
|
+
};
|
|
227
242
|
listTasks(agentId: string, statusFilter?: string): TaskSummary[];
|
|
228
243
|
private toSummary;
|
|
229
244
|
|
|
@@ -275,6 +290,13 @@ export declare class BackgroundCommandManager {
|
|
|
275
290
|
|
|
276
291
|
|
|
277
292
|
interruptAwaits(agentId: string, taskIds: Iterable<string>): void;
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
278
300
|
killTask(agentId: string, taskId: string): Promise<KillResult>;
|
|
279
301
|
|
|
280
302
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{spawn as
|
|
2
|
-
`),this.finalizeTask(u,-1,null,"spawn_error")}),this.persistTaskSafe(u),{taskId:n,completion:l}}async spawnAndAttach(t,i){const e=G(),n=await D(i.commandText,i.workspaceRoot,e)
|
|
3
|
-
`),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),
|
|
1
|
+
import{spawn as I,spawnSync as R}from"node:child_process";import{promises as S}from"node:fs";import{createInterface as O}from"node:readline";import{randomBytes as C}from"node:crypto";import{prepareShellCommand as D,shellExecutable as G}from"./builtins/shellCommandPreparation.js";import{createShellOutputDecoder as b,decodeShellOutputChunk as A}from"./builtins/shellOutputDecoder.js";import{BackgroundOutputRingBuffer as z}from"./background-output-ringbuffer.js";import{BackgroundCommandStore as F}from"./background-command-store.js";const N=1e3,k=3*60*1e3,W=24*60*60*1e3,X=5*1e3,$=64*1024,q=2e3,_=50*1024*1024,B=20,P=256,U=256*1024,Y=100,K=5e3,H=256,v=20,x=5*60*1e3,L=1024*1024,M=`pid-${process.pid}-${Date.now().toString(36)}`;class V{constructor(){this.tasksByAgent=new Map,this.store=new F}globalCount(){let t=0;for(const i of this.tasksByAgent.values())t+=i.size;return t}startCommand(t){const i=t.agentId,e=this.getOrCreateBucket(i);if(this.globalCount()>=P)throw new E("global_limit_reached",`Global background task limit ${P} reached. Stop or wait for existing background commands to finish, then retry.`);if(e.size>=B)throw new E("too_many_tasks",`Background command limit exceeded: ${e.size}/${B} slots in use. Stop existing background commands (kill_background_command) to free slots, then retry.`);const n=this.generateTaskId(e),r=t.timeoutMs===void 0?k:t.timeoutMs>0?Math.min(t.timeoutMs,W):0,o=typeof t.inactivityTimeoutMs=="number"&&Number.isFinite(t.inactivityTimeoutMs)&&t.inactivityTimeoutMs>0?t.inactivityTimeoutMs:0,s=this.store.outputPathFor(i,n),a=t.commandText.length>80?`${t.commandText.slice(0,77)}...`:t.commandText,u={taskId:n,agentId:i,command:t.commandText,commandPreview:a,description:t.description,cwd:t.cwd,status:"running",startedAt:Date.now(),finishedAt:null,exitCode:null,signal:null,killedReason:null,timeoutMs:r,inactivityTimeoutMs:o,child:null,ringBuffer:new z,outputPath:s,outputWriteChain:Promise.resolve(),outputTruncated:!1,outputLineCount:0,outputBytes:0,notified:!1,completionWaiters:[],onCompleted:t.onCompleted,onOutput:t.onOutput,outputSeq:0,outputSubscribers:new Set,timeoutTimer:null,killEscalationTimer:null,retentionTimer:null,inactivityTimer:null,exitGraceTimer:null,inactivityTimedOut:!1,finalized:!1,stdoutDecoder:b(),stderrDecoder:b(),stdoutText:"",stderrText:"",commandLineCount:0,bootId:M,lastPulseAt:Date.now(),lastPulseBytes:0,backgrounded:!1},l=new Promise(c=>{u.completionResolve=c});return e.set(n,u),r>0&&(u.timeoutTimer=setTimeout(()=>{u.finalized||(u.killedReason="timeout",u.child?this.killProcessGroup(u.child):this.finalizeTask(u,null,null,"timeout"))},r),u.timeoutTimer.unref?.()),this.spawnAndAttach(u,t).catch(c=>{u.ringBuffer.push(`[ACode] spawn failed: ${c?.message??String(c)}
|
|
2
|
+
`),this.finalizeTask(u,-1,null,"spawn_error")}),this.persistTaskSafe(u),{taskId:n,completion:l}}async spawnAndAttach(t,i){const e=G(),n=await D(i.commandText,i.workspaceRoot,e);if(t.finalized)return;const r=process.platform==="win32",o=I(e.command,[...e.argsPrefix,n.commandText],{cwd:i.cwd,env:process.env,detached:!r,stdio:["ignore","pipe","pipe"],windowsVerbatimArguments:r,windowsHide:!0});t.child=o,t.commandFilePath=n.commandFilePath,t.commandLineCount=n.commandLineCount,t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),o.stdout?.on("data",s=>{const a=A(t.stdoutDecoder,s);this.onOutput(t,"stdout",a)}),o.stderr?.on("data",s=>{const a=A(t.stderrDecoder,s);this.onOutput(t,"stderr",a)}),o.on("error",s=>{this.onOutput(t,"stderr",`[ACode] child error: ${s.message}
|
|
3
|
+
`),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),o.on("exit",(s,a)=>{if(t.finalized)return;const u=()=>{if(t.finalized||!t.child)return;const l=A(t.stdoutDecoder,void 0,!0),c=A(t.stderrDecoder,void 0,!0);l&&this.onOutput(t,"stdout",l),c&&this.onOutput(t,"stderr",c),t.ringBuffer.flush();const p=t.killedReason??null;this.finalizeTask(t,s,a,p??void 0)};t.exitGraceTimer=setTimeout(u,q),t.exitGraceTimer.unref?.()}),o.on("close",(s,a)=>{t.exitGraceTimer&&(clearTimeout(t.exitGraceTimer),t.exitGraceTimer=null);const u=A(t.stdoutDecoder,void 0,!0),l=A(t.stderrDecoder,void 0,!0);if(u&&this.onOutput(t,"stdout",u),l&&this.onOutput(t,"stderr",l),t.ringBuffer.flush(),!t.finalized){const c=t.killedReason??null;this.finalizeTask(t,s,a,c??void 0)}})}disableInactivityTimeout(t,i){const e=this.getTask(t,i);return e?(e.inactivityTimeoutMs=0,e.inactivityTimer&&(clearTimeout(e.inactivityTimer),e.inactivityTimer=null),!0):!1}async markBackgrounded(t,i){const e=this.getTask(t,i);return e?(e.backgrounded=!0,await this.persistTask(e),!0):!1}onOutput(t,i,e){if(!e)return;t.outputSeq++;const n=t.outputSeq;if(t.outputSubscribers.size>0)for(const s of t.outputSubscribers)try{s.onLine({stream:i,text:e,seq:n})}catch{}const r=i==="stdout"?"stdoutText":"stderrText";if(Buffer.byteLength(t[r],"utf-8")<L){const s=L-Buffer.byteLength(t[r],"utf-8");t[r]+=s>0?e.slice(0,s):""}if(t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),t.status==="running"){const s=Date.now(),a=t.outputBytes-t.lastPulseBytes;(s-t.lastPulseAt>=X||a>=$)&&(t.lastPulseAt=s,t.lastPulseBytes=t.outputBytes,this.persistTaskSafe(t))}if(t.outputBytes>=_){if(!t.outputTruncated){t.outputTruncated=!0;const s=`
|
|
4
4
|
[ACode] output truncated at 50MB
|
|
5
|
-
`;t.ringBuffer.push(
|
|
6
|
-
`)}),t.outputWriteChain)}finalizeTask(t,i,e,n){if(t.finalized)return;t.finalized=!0,t.timeoutTimer&&(clearTimeout(t.timeoutTimer),t.timeoutTimer=null),t.killEscalationTimer&&(clearTimeout(t.killEscalationTimer),t.killEscalationTimer=null),t.inactivityTimer&&(clearTimeout(t.inactivityTimer),t.inactivityTimer=null),t.exitGraceTimer&&(clearTimeout(t.exitGraceTimer),t.exitGraceTimer=null),t.child=null,t.finishedAt=Date.now();let r;n?(r="killed",t.killedReason=n):i===0?r="completed":r="failed",t.status=r,t.exitCode=i,t.signal=e;const s=n==="inactivity"||t.inactivityTimedOut,o={taskId:t.taskId,exitCode:i,signal:e,timedOut:n==="timeout"||s,inactivityTimedOut:s,outputFilePath:t.outputPath,outputLineCount:t.outputLineCount,outputSpilled:t.outputTruncated,commandFilePath:t.commandFilePath,commandLineCount:t.commandLineCount,timeoutMs:t.timeoutMs,inactivityTimeoutMs:t.inactivityTimeoutMs,stdout:t.stdoutText,stderr:t.stderrText,durationMs:t.finishedAt-t.startedAt};t.completionResolve?.(o);const a={taskId:t.taskId,status:r,exitCode:i,signal:e,timedOut:n==="timeout"||s,aborted:!1,durationMs:t.finishedAt-t.startedAt},u=t.completionWaiters;t.completionWaiters=[];for(const l of u)l(a);if(this.persistTaskSafe(t),t.onCompleted?.({taskId:t.taskId,agentId:t.agentId,status:r,exitCode:i,signal:e,commandPreview:t.commandPreview,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,outputPath:t.outputPath,finishedAt:t.finishedAt,killedReason:t.killedReason}),t.outputSubscribers.size>0){const l=Array.from(t.outputSubscribers);t.outputSubscribers.clear();for(const c of l)try{c.onDone({status:r,exitCode:i,signal:e})}catch{}}t.retentionTimer=setTimeout(()=>{this.evictTask(t.agentId,t.taskId)},B),t.retentionTimer.unref?.()}evictTask(t,i){const e=this.tasksByAgent.get(t);if(!e)return;const n=e.get(i);n&&(S.unlink(n.outputPath).catch(()=>{}),e.delete(i),e.size===0&&this.tasksByAgent.delete(t))}listTasks(t,i){const e=this.tasksByAgent.get(t);if(!e)return[];const n=[];for(const r of e.values())i&&i!=="all"&&r.status!==i||n.push(this.toSummary(r));return n.sort((r,s)=>s.startedAt-r.startedAt)}toSummary(t){return{taskId:t.taskId,status:t.status,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}}getTaskOutputSnapshot(t,i,e=500){const n=this.getTask(t,i);if(!n)return null;const r=n.ringBuffer.tail(e);return{taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:r,nextSeq:n.outputSeq+1,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:n.outputTruncated}}subscribeTaskOutput(t,i,e){const n=this.getTask(t,i);if(!n)return null;n.outputSubscribers.add(e);let r=!0;return()=>{r&&(r=!1,n.outputSubscribers.delete(e))}}async readOutput(t,i){const e=String(i.task_id??""),n=this.getTask(t,e),r=i.mode==="lines"||i.mode==="grep"?i.mode:"tail",s=Math.min(Math.max(1,i.line_size??100),U),o=Number.isFinite(i.start_line)&&(i.start_line??0)>0?Math.floor(i.start_line??0):0;if(!n)return{taskId:e,status:"killed",mode:r,lines:[],lineCount:0,metadata:{totalLines:0,totalBytes:0,returnedBytes:0,truncated:!1,outputTruncated:!1,hasMore:!1,nextStartLine:null,outputFile:""}};let a=[],u=!1,l=null;if(r==="tail"){const m=n.ringBuffer.tail(s),f=Math.max(0,n.outputLineCount-m.length);a=m.map((d,y)=>({line:f+y,text:d})),l=null}else if(r==="lines"){const m=n.ringBuffer.firstLineIndex(),f=m+n.ringBuffer.getLineCount();if(o>=m&&o<f)a=n.ringBuffer.offset(o,s).map((y,T)=>({line:o+T,text:y})),u=o+a.length<f,l=u?o+a.length:null;else if(o<f){const d=await this.readFromFile(n,"lines",o,s,0,0,void 0);a=d.rows,u=d.hasMore,l=d.nextStartLine}}else{const m=String(i.pattern??"");if(!m||m.length>H)return this._emptyResult(e,n,r);let f;try{f=new RegExp(m,"i")}catch{return this._emptyResult(e,n,r)}const d=Math.min(Math.max(0,i.context_before??0),v),y=Math.min(Math.max(0,i.context_after??0),v),T=await this.readFromFile(n,"grep",o,s,d,y,f);a=T.rows,u=T.hasMore,l=T.nextStartLine}const c=[];let p=0,g=!1;for(const m of a){const f=Buffer.byteLength(m.text,"utf-8")+1;if(c.length>=s||p+f>K){g=!0,u=!0;break}c.push(m),p+=f}const w=i.include_metadata!==!1;return{taskId:e,status:n.status,mode:r,lines:c,lineCount:c.length,metadata:w?{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:p,truncated:g,outputTruncated:n.outputTruncated,hasMore:u,nextStartLine:u?l:null,outputFile:n.outputPath}:void 0}}_emptyResult(t,i,e){return{taskId:t,status:i.status,mode:e,lines:[],lineCount:0,metadata:{totalLines:i.outputLineCount,totalBytes:i.outputBytes,returnedBytes:0,truncated:!1,outputTruncated:i.outputTruncated,hasMore:!1,nextStartLine:null,outputFile:i.outputPath}}}async readFromFile(t,i,e,n,r,s,o){const a=[],u=new Set;let l=0,c=!1;const p=[];let g=-1/0;try{const w=await S.open(t.outputPath,"r"),m=O({input:w.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const f of m){const d=l;if(l++,i==="grep"&&d-e>=Y&&d>=e){c=!0;break}if(d>=e){if(i==="lines"){if(a.length>=n){c=!0;break}u.has(d)||(u.add(d),a.push({line:d,text:f}))}else if(o.test(f)){for(const T of p)T.line>=e&&!u.has(T.line)&&(u.add(T.line),a.push(T));if(u.has(d)||(u.add(d),a.push({line:d,text:f})),g=d,a.length>=n){c=!0;break}}else if(r>0&&(p.push({line:d,text:f}),p.length>r&&p.shift()),g>=e&&d-g<=s&&!u.has(d)&&(u.add(d),a.push({line:d,text:f}),a.length>=n)){c=!0;break}}}m.close(),await w.close()}catch{return{rows:[],hasMore:!1,nextStartLine:null}}return c?{rows:a,hasMore:c,nextStartLine:l}:{rows:a,hasMore:c,nextStartLine:null}}async awaitCompletion(t,i,e){return Promise.all(i.map(n=>this.awaitOne(t,n,e)))}async awaitOne(t,i,e){const n=this.getTask(t,i);if(n)return n.status!=="running"?this.taskToAwaitResult(n):new Promise(o=>{let a=null;const u=l=>{a&&clearTimeout(a),o(l)};n.completionWaiters.push(u),e&&e>0&&(a=setTimeout(()=>{n.completionWaiters=n.completionWaiters.filter(l=>l!==u),o({taskId:i,status:"running",exitCode:null,signal:null,timedOut:!0,aborted:!1,durationMs:Date.now()-n.startedAt})},e),a.unref?.())});const s=(await this.store.readSession(t))?.tasks.find(o=>o.taskId===i);return s?s.status!=="running"?{taskId:i,status:s.status,exitCode:s.exitCode,signal:s.signal,timedOut:s.killedReason==="timeout",aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt,...s.outputPath?{outputFilePath:s.outputPath}:{}}:{taskId:i,status:"killed",exitCode:null,signal:null,timedOut:!1,aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt}:null}taskToAwaitResult(t){return{taskId:t.taskId,status:t.status,exitCode:t.exitCode,signal:t.signal,timedOut:t.killedReason==="timeout",aborted:!1,durationMs:(t.finishedAt??Date.now())-t.startedAt,...t.status!=="running"&&t.outputPath?{outputFilePath:t.outputPath}:{}}}interruptAwaits(t,i){const e=this.tasksByAgent.get(t);if(e)for(const n of i){const r=e.get(n);if(!r)continue;const s=r.completionWaiters;if(s.length===0)continue;r.completionWaiters=[];const o={taskId:n,status:"running",exitCode:null,signal:null,timedOut:!1,aborted:!0,durationMs:Date.now()-r.startedAt};for(const a of s)a(o)}}async killTask(t,i){const e=this.getTask(t,i);return e?e.status!=="running"?{taskId:i,status:e.status,exitCode:e.exitCode??void 0}:e.child?(e.killedReason="user_kill",this.killProcessGroup(e.child)?(e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.killEscalationTimer=setTimeout(()=>{e.child&&!e.finalized&&this.killProcessGroup(e.child,!0)},N),e.killEscalationTimer.unref?.(),{taskId:i,status:"killed",signal:"SIGTERM"}):{taskId:i,status:e.status,error:"kill_failed",reason:"permission_denied"}):{taskId:i,status:e.status,error:"kill_failed",reason:"no_child"}:{taskId:i,status:"killed",error:"task_not_found"}}killProcessGroup(t,i=!1){const e=t.pid;if(!e)return!1;try{if(process.platform==="win32")return R("taskkill",["/PID",String(e),"/T","/F"],{windowsHide:!0}),!0;try{process.kill(-e,i?"SIGKILL":"SIGTERM")}catch{process.kill(e,i?"SIGKILL":"SIGTERM")}return!0}catch{return!1}}async disposeAgent(t){const i=this.tasksByAgent.get(t);if(i){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="session_disposed",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer),e.exitGraceTimer&&clearTimeout(e.exitGraceTimer);this.tasksByAgent.delete(t)}await this.store.deleteAgent(t)}async disposeAll(){for(const[t,i]of this.tasksByAgent){for(const e of i.values())e.status==="running"&&(e.killedReason="process_exit",e.child&&this.killProcessGroup(e.child,!0),e.finalized||this.finalizeTask(e,null,null,"process_exit")),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer),e.exitGraceTimer&&clearTimeout(e.exitGraceTimer);await this.store.updateSession(t,()=>null),this.tasksByAgent.delete(t)}}async reconcileDiskRunning(t){const i=this.tasksByAgent.get(t);let e=0;return await this.store.updateSession(t,n=>{let r=!1;for(const s of n.tasks){if(s.status!=="running")continue;const o=i?.get(s.taskId)?.status==="running",a=s.ownerBootId===x;if(o){a||(s.ownerBootId=x,r=!0);continue}s.status="killed",s.killedReason="runtime_crashed",s.finishedAt=Date.now(),s.notified=!0,e++,r=!0}return r?n.tasks:null}),e}async listTasksWithReconcile(t,i){try{await this.reconcileDiskRunning(t)}catch{}const e=this.tasksByAgent.get(t),n=e?[...e.values()]:[],r=n.filter(o=>o.backgrounded).map(o=>this.toSummary(o)),s=new Set(n.map(o=>o.taskId));try{const o=await this.store.readSession(t);if(o){const a=Date.now();for(const u of o.tasks)s.has(u.taskId)||u.backgrounded===!0&&u.status!=="running"&&(u.finishedAt&&a-u.finishedAt>=B||i&&i!=="all"&&u.status!==i||r.push(Z(u)))}}catch{}return i&&i!=="all"?r.filter(o=>o.status===i).sort((o,a)=>a.startedAt-o.startedAt):r.sort((o,a)=>a.startedAt-o.startedAt)}async reconcileAllAgents(){const t=await this.store.listAgents();let i=0;for(const e of t)try{i+=await this.reconcileDiskRunning(e)}catch{}return i}async reconcileOrphans(t){await this.reconcileDiskRunning(t)}async readSessionState(t){return this.store.readSession(t)}async markNotified(t,i){await this.store.markNotified(t,i)}getOrCreateBucket(t){let i=this.tasksByAgent.get(t);return i||(i=new Map,this.tasksByAgent.set(t,i)),i}getTask(t,i){const e=this.tasksByAgent.get(t);if(e&&/^[A-Za-z0-9]{8}$/.test(i))return e.get(i)}generateTaskId(t){for(let i=0;i<3;i++){const e=M(4).toString("hex").slice(0,8);if(!t.has(e))return e}return M(6).toString("hex").slice(0,8)}async persistTask(t){await this.store.updateSession(t.agentId,i=>{const e={taskId:t.taskId,agentId:t.agentId,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputPath:t.outputPath,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,notified:t.notified,killedReason:t.killedReason,ownerBootId:t.bootId,lastPulseAt:t.status==="running"?t.lastPulseAt:void 0,backgrounded:t.backgrounded},n=i.tasks.findIndex(s=>s.taskId===t.taskId);n>=0?i.tasks[n]=e:i.tasks.push(e);const r=Date.now();return i.tasks.filter(s=>s.status==="running"||!s.finishedAt||r-s.finishedAt<B)})}persistTaskSafe(t){this.persistTask(t).catch(i=>{console.error(`[BackgroundCommandManager] persist ${t.agentId}/${t.taskId} failed:`,i)})}}class I extends Error{constructor(t,i){super(i),this.name="BackgroundTaskError",this.code=t}}function Z(h){return{taskId:h.taskId,status:h.status,command:h.command,commandPreview:h.commandPreview,description:h.description,cwd:h.cwd,startedAt:h.startedAt,finishedAt:h.finishedAt,exitCode:h.exitCode,signal:h.signal,outputLineCount:h.outputLineCount,outputBytes:h.outputBytes}}const st=new V;export{V as BackgroundCommandManager,I as BackgroundTaskError,st as backgroundCommandManager};
|
|
5
|
+
`;t.ringBuffer.push(s),this.appendToFile(t,s)}t.ringBuffer.push(e);return}t.ringBuffer.push(e),t.onOutput?.({stream:i,text:e});const o=(e.match(/\n/g)??[]).length;t.outputLineCount+=o,t.outputBytes+=Buffer.byteLength(e,"utf-8"),this.appendToFile(t,e)}scheduleInactivity(t){t.finalized||t.inactivityTimeoutMs<=0||(t.inactivityTimer&&clearTimeout(t.inactivityTimer),t.inactivityTimer=setTimeout(()=>{t.finalized||!t.child||(t.inactivityTimedOut=!0,t.killedReason="inactivity",this.killProcessGroup(t.child))},t.inactivityTimeoutMs),t.inactivityTimer.unref?.())}appendToFile(t,i){return t.outputBytes>_?Promise.resolve():(t.outputWriteChain=t.outputWriteChain.then(()=>S.appendFile(t.outputPath,i,"utf-8")).catch(e=>{t.ringBuffer.push(`[ACode] output write failed: ${e?.message??e}
|
|
6
|
+
`)}),t.outputWriteChain)}finalizeTask(t,i,e,n){if(t.finalized)return;t.finalized=!0,t.timeoutTimer&&(clearTimeout(t.timeoutTimer),t.timeoutTimer=null),t.killEscalationTimer&&(clearTimeout(t.killEscalationTimer),t.killEscalationTimer=null),t.inactivityTimer&&(clearTimeout(t.inactivityTimer),t.inactivityTimer=null),t.exitGraceTimer&&(clearTimeout(t.exitGraceTimer),t.exitGraceTimer=null),t.child=null,t.finishedAt=Date.now();let r;n?(r="killed",t.killedReason=n):i===0?r="completed":r="failed",t.status=r,t.exitCode=i,t.signal=e;const o=n==="inactivity"||t.inactivityTimedOut,s={taskId:t.taskId,exitCode:i,signal:e,timedOut:n==="timeout"||o,inactivityTimedOut:o,outputFilePath:t.outputPath,outputLineCount:t.outputLineCount,outputSpilled:t.outputTruncated,commandFilePath:t.commandFilePath,commandLineCount:t.commandLineCount,timeoutMs:t.timeoutMs,inactivityTimeoutMs:t.inactivityTimeoutMs,stdout:t.stdoutText,stderr:t.stderrText,durationMs:t.finishedAt-t.startedAt};t.completionResolve?.(s);const a={taskId:t.taskId,status:r,exitCode:i,signal:e,timedOut:n==="timeout"||o,aborted:!1,durationMs:t.finishedAt-t.startedAt},u=t.completionWaiters;t.completionWaiters=[];for(const l of u)l(a);if(this.persistTaskSafe(t),t.onCompleted?.({taskId:t.taskId,agentId:t.agentId,status:r,exitCode:i,signal:e,commandPreview:t.commandPreview,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,outputPath:t.outputPath,finishedAt:t.finishedAt,killedReason:t.killedReason}),t.outputSubscribers.size>0){const l=Array.from(t.outputSubscribers);t.outputSubscribers.clear();for(const c of l)try{c.onDone({status:r,exitCode:i,signal:e})}catch{}}t.retentionTimer=setTimeout(()=>{this.evictTask(t)},x),t.retentionTimer.unref?.()}evictTask(t){S.unlink(t.outputPath).catch(()=>{});const i=this.tasksByAgent.get(t.agentId);i&&(i.delete(t.taskId),i.size===0&&this.tasksByAgent.delete(t.agentId))}taskCapacity(t){const i=this.tasksByAgent.get(t)?.size??0;return{limit:B,used:i,remaining:Math.max(0,B-i)}}listTasks(t,i){const e=this.tasksByAgent.get(t);if(!e)return[];const n=[];for(const r of e.values())i&&i!=="all"&&r.status!==i||n.push(this.toSummary(r));return n.sort((r,o)=>o.startedAt-r.startedAt)}toSummary(t){return{taskId:t.taskId,status:t.status,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}}getTaskOutputSnapshot(t,i,e=500){const n=this.getTask(t,i);if(!n)return null;const r=n.ringBuffer.tail(e);return{taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:r,nextSeq:n.outputSeq+1,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:n.outputTruncated}}subscribeTaskOutput(t,i,e){const n=this.getTask(t,i);if(!n)return null;n.outputSubscribers.add(e);let r=!0;return()=>{r&&(r=!1,n.outputSubscribers.delete(e))}}async readOutput(t,i){const e=String(i.task_id??""),n=this.getTask(t,e),r=i.mode==="lines"||i.mode==="grep"?i.mode:"tail",o=Math.min(Math.max(1,i.line_size??100),Y),s=Number.isFinite(i.start_line)&&(i.start_line??0)>0?Math.floor(i.start_line??0):0;if(!n)return{taskId:e,status:"killed",mode:r,lines:[],lineCount:0,metadata:{totalLines:0,totalBytes:0,returnedBytes:0,truncated:!1,outputTruncated:!1,hasMore:!1,nextStartLine:null,outputFile:""}};let a=[],u=!1,l=null;if(r==="tail"){const m=n.ringBuffer.tail(o),f=Math.max(0,n.outputLineCount-m.length);a=m.map((d,y)=>({line:f+y,text:d})),l=null}else if(r==="lines"){const m=n.ringBuffer.firstLineIndex(),f=m+n.ringBuffer.getLineCount();if(s>=m&&s<f)a=n.ringBuffer.offset(s,o).map((y,T)=>({line:s+T,text:y})),u=s+a.length<f,l=u?s+a.length:null;else if(s<f){const d=await this.readFromFile(n,"lines",s,o,0,0,void 0);a=d.rows,u=d.hasMore,l=d.nextStartLine}}else{const m=String(i.pattern??"");if(!m||m.length>H)return this._emptyResult(e,n,r);let f;try{f=new RegExp(m,"i")}catch{return this._emptyResult(e,n,r)}const d=Math.min(Math.max(0,i.context_before??0),v),y=Math.min(Math.max(0,i.context_after??0),v),T=await this.readFromFile(n,"grep",s,o,d,y,f);a=T.rows,u=T.hasMore,l=T.nextStartLine}const c=[];let p=0,g=!1;for(const m of a){const f=Buffer.byteLength(m.text,"utf-8")+1;if(c.length>=o||p+f>U){g=!0,u=!0;break}c.push(m),p+=f}const w=i.include_metadata!==!1;return{taskId:e,status:n.status,mode:r,lines:c,lineCount:c.length,metadata:w?{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:p,truncated:g,outputTruncated:n.outputTruncated,hasMore:u,nextStartLine:u?l:null,outputFile:n.outputPath}:void 0}}_emptyResult(t,i,e){return{taskId:t,status:i.status,mode:e,lines:[],lineCount:0,metadata:{totalLines:i.outputLineCount,totalBytes:i.outputBytes,returnedBytes:0,truncated:!1,outputTruncated:i.outputTruncated,hasMore:!1,nextStartLine:null,outputFile:i.outputPath}}}async readFromFile(t,i,e,n,r,o,s){const a=[],u=new Set;let l=0,c=!1;const p=[];let g=-1/0;try{const w=await S.open(t.outputPath,"r"),m=O({input:w.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const f of m){const d=l;if(l++,i==="grep"&&d-e>=K&&d>=e){c=!0;break}if(d>=e){if(i==="lines"){if(a.length>=n){c=!0;break}u.has(d)||(u.add(d),a.push({line:d,text:f}))}else if(s.test(f)){for(const T of p)T.line>=e&&!u.has(T.line)&&(u.add(T.line),a.push(T));if(u.has(d)||(u.add(d),a.push({line:d,text:f})),g=d,a.length>=n){c=!0;break}}else if(r>0&&(p.push({line:d,text:f}),p.length>r&&p.shift()),g>=e&&d-g<=o&&!u.has(d)&&(u.add(d),a.push({line:d,text:f}),a.length>=n)){c=!0;break}}}m.close(),await w.close()}catch{return{rows:[],hasMore:!1,nextStartLine:null}}return c?{rows:a,hasMore:c,nextStartLine:l}:{rows:a,hasMore:c,nextStartLine:null}}async awaitCompletion(t,i,e){return Promise.all(i.map(n=>this.awaitOne(t,n,e)))}async awaitOne(t,i,e){const n=this.getTask(t,i);if(n)return n.status!=="running"?this.taskToAwaitResult(n):new Promise(s=>{let a=null;const u=l=>{a&&clearTimeout(a),s(l)};n.completionWaiters.push(u),e&&e>0&&(a=setTimeout(()=>{n.completionWaiters=n.completionWaiters.filter(l=>l!==u),s({taskId:i,status:"running",exitCode:null,signal:null,timedOut:!0,aborted:!1,durationMs:Date.now()-n.startedAt})},e),a.unref?.())});const o=(await this.store.readSession(t))?.tasks.find(s=>s.taskId===i);return o?o.status!=="running"?{taskId:i,status:o.status,exitCode:o.exitCode,signal:o.signal,timedOut:o.killedReason==="timeout",aborted:!1,durationMs:(o.finishedAt??Date.now())-o.startedAt,...o.outputPath?{outputFilePath:o.outputPath}:{}}:{taskId:i,status:"killed",exitCode:null,signal:null,timedOut:!1,aborted:!1,durationMs:(o.finishedAt??Date.now())-o.startedAt}:null}taskToAwaitResult(t){return{taskId:t.taskId,status:t.status,exitCode:t.exitCode,signal:t.signal,timedOut:t.killedReason==="timeout",aborted:!1,durationMs:(t.finishedAt??Date.now())-t.startedAt,...t.status!=="running"&&t.outputPath?{outputFilePath:t.outputPath}:{}}}interruptAwaits(t,i){const e=this.tasksByAgent.get(t);if(e)for(const n of i){const r=e.get(n);if(!r)continue;const o=r.completionWaiters;if(o.length===0)continue;r.completionWaiters=[];const s={taskId:n,status:"running",exitCode:null,signal:null,timedOut:!1,aborted:!0,durationMs:Date.now()-r.startedAt};for(const a of o)a(s)}}async killTask(t,i){const e=this.getTask(t,i);if(!e){const o=(await this.store.readSession(t))?.tasks.find(s=>s.taskId===i);return o?{taskId:i,status:o.status==="running"?"killed":o.status,exitCode:o.exitCode??void 0,signal:o.signal??void 0}:{taskId:i,status:"killed",error:"task_not_found"}}return e.status!=="running"?(this.tasksByAgent.get(t)?.delete(i),{taskId:i,status:e.status,exitCode:e.exitCode??void 0}):e.child?(e.killedReason="user_kill",this.killProcessGroup(e.child)?(e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.killEscalationTimer=setTimeout(()=>{e.child&&!e.finalized&&this.killProcessGroup(e.child,!0)},N),e.killEscalationTimer.unref?.(),this.tasksByAgent.get(t)?.delete(i),{taskId:i,status:"killed",signal:"SIGTERM"}):{taskId:i,status:e.status,error:"kill_failed",reason:"permission_denied"}):{taskId:i,status:e.status,error:"kill_failed",reason:"no_child"}}killProcessGroup(t,i=!1){const e=t.pid;if(!e)return!1;try{if(process.platform==="win32")return R("taskkill",["/PID",String(e),"/T","/F"],{windowsHide:!0}),!0;try{process.kill(-e,i?"SIGKILL":"SIGTERM")}catch{process.kill(e,i?"SIGKILL":"SIGTERM")}return!0}catch{return!1}}async disposeAgent(t){const i=this.tasksByAgent.get(t);if(i){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="session_disposed",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer),e.exitGraceTimer&&clearTimeout(e.exitGraceTimer);this.tasksByAgent.delete(t)}await this.store.deleteAgent(t)}async disposeAll(){for(const[t,i]of this.tasksByAgent){for(const e of i.values())e.status==="running"&&(e.killedReason="process_exit",e.child&&this.killProcessGroup(e.child,!0),e.finalized||this.finalizeTask(e,null,null,"process_exit")),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer),e.exitGraceTimer&&clearTimeout(e.exitGraceTimer);await this.store.updateSession(t,()=>null),this.tasksByAgent.delete(t)}}async reconcileDiskRunning(t){const i=this.tasksByAgent.get(t);let e=0;return await this.store.updateSession(t,n=>{let r=!1;for(const o of n.tasks){if(o.status!=="running")continue;const s=i?.get(o.taskId)?.status==="running",a=o.ownerBootId===M;if(s){a||(o.ownerBootId=M,r=!0);continue}o.status="killed",o.killedReason="runtime_crashed",o.finishedAt=Date.now(),o.notified=!0,e++,r=!0}return r?n.tasks:null}),e}async listTasksWithReconcile(t,i){try{await this.reconcileDiskRunning(t)}catch{}const e=this.tasksByAgent.get(t),n=e?[...e.values()]:[],r=n.filter(s=>s.backgrounded).map(s=>this.toSummary(s)),o=new Set(n.map(s=>s.taskId));try{const s=await this.store.readSession(t);if(s){const a=Date.now();for(const u of s.tasks)o.has(u.taskId)||u.backgrounded===!0&&u.status!=="running"&&(u.finishedAt&&a-u.finishedAt>=x||i&&i!=="all"&&u.status!==i||r.push(Z(u)))}}catch{}return i&&i!=="all"?r.filter(s=>s.status===i).sort((s,a)=>a.startedAt-s.startedAt):r.sort((s,a)=>a.startedAt-s.startedAt)}async reconcileAllAgents(){const t=await this.store.listAgents();let i=0;for(const e of t)try{i+=await this.reconcileDiskRunning(e)}catch{}return i}async reconcileOrphans(t){await this.reconcileDiskRunning(t)}async readSessionState(t){return this.store.readSession(t)}async markNotified(t,i){await this.store.markNotified(t,i)}getOrCreateBucket(t){let i=this.tasksByAgent.get(t);return i||(i=new Map,this.tasksByAgent.set(t,i)),i}getTask(t,i){const e=this.tasksByAgent.get(t);if(e&&/^[A-Za-z0-9]{8}$/.test(i))return e.get(i)}generateTaskId(t){for(let i=0;i<3;i++){const e=C(4).toString("hex").slice(0,8);if(!t.has(e))return e}return C(6).toString("hex").slice(0,8)}async persistTask(t){await this.store.updateSession(t.agentId,i=>{const e={taskId:t.taskId,agentId:t.agentId,command:t.command,commandPreview:t.commandPreview,description:t.description,cwd:t.cwd,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputPath:t.outputPath,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,notified:t.notified,killedReason:t.killedReason,ownerBootId:t.bootId,lastPulseAt:t.status==="running"?t.lastPulseAt:void 0,backgrounded:t.backgrounded},n=i.tasks.findIndex(o=>o.taskId===t.taskId);n>=0?i.tasks[n]=e:i.tasks.push(e);const r=Date.now();return i.tasks.filter(o=>o.status==="running"||!o.finishedAt||r-o.finishedAt<x)})}persistTaskSafe(t){this.persistTask(t).catch(i=>{console.error(`[BackgroundCommandManager] persist ${t.agentId}/${t.taskId} failed:`,i)})}}class E extends Error{constructor(t,i){super(i),this.name="BackgroundTaskError",this.code=t}}function Z(h){return{taskId:h.taskId,status:h.status,command:h.command,commandPreview:h.commandPreview,description:h.description,cwd:h.cwd,startedAt:h.startedAt,finishedAt:h.finishedAt,exitCode:h.exitCode,signal:h.signal,outputLineCount:h.outputLineCount,outputBytes:h.outputBytes}}const st=new V;export{V as BackgroundCommandManager,E as BackgroundTaskError,st as backgroundCommandManager};
|
|
7
7
|
|