aws-runtime-bridge 1.9.120 → 1.9.124

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.
Files changed (105) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +18 -0
  2. package/dist/adapter/AcodeSdkAdapter.js +5 -5
  3. package/dist/adapter/types.d.ts +4 -0
  4. package/dist/adapter/types.js +1 -1
  5. package/dist/browser/browser-ws-bridge.js +3 -3
  6. package/dist/browser/built-in-browser-tools.js +1 -1
  7. package/dist/desktop/built-in-desktop-tools.d.ts +49 -0
  8. package/dist/desktop/built-in-desktop-tools.js +2 -0
  9. package/dist/desktop/desktop-mcp-client.d.ts +47 -0
  10. package/dist/desktop/desktop-mcp-client.js +3 -0
  11. package/dist/index.js +1 -1
  12. package/dist/remote-desktop/index.js +2 -2
  13. package/dist/routes/dashboard.js +521 -17
  14. package/dist/routes/git.d.ts +39 -0
  15. package/dist/routes/git.js +33 -25
  16. package/dist/routes/instance.js +1 -1
  17. package/dist/routes/jdks.d.ts +2 -0
  18. package/dist/routes/jdks.js +2 -0
  19. package/dist/routes/launch.d.ts +6 -0
  20. package/dist/routes/launch.js +30 -0
  21. package/dist/routes/pty.js +3 -3
  22. package/dist/routes/runtime-binding.js +1 -1
  23. package/dist/routes/terminal.js +30 -30
  24. package/dist/services/access-control.d.ts +18 -0
  25. package/dist/services/access-control.js +5 -0
  26. package/dist/services/agent-process-manager.js +1 -1
  27. package/dist/services/auto-register.js +1 -1
  28. package/dist/services/change-cache-retention.d.ts +5 -0
  29. package/dist/services/change-cache-retention.js +2 -0
  30. package/dist/services/desktop-mcp-manager.d.ts +47 -0
  31. package/dist/services/desktop-mcp-manager.js +2 -0
  32. package/dist/services/instance-features.d.ts +6 -0
  33. package/dist/services/instance-features.js +1 -1
  34. package/dist/services/jdk-scanner.d.ts +20 -0
  35. package/dist/services/jdk-scanner.js +2 -0
  36. package/dist/services/launch-manager.d.ts +181 -0
  37. package/dist/services/launch-manager.js +12 -0
  38. package/dist/services/launch-metrics.d.ts +89 -0
  39. package/dist/services/launch-metrics.js +4 -0
  40. package/dist/services/launch-streams.d.ts +24 -0
  41. package/dist/services/launch-streams.js +11 -0
  42. package/dist/services/mcp-launch-binding-queue.js +1 -1
  43. package/dist/services/panel-auth.d.ts +11 -1
  44. package/dist/services/panel-auth.js +1 -1
  45. package/dist/services/panel-env.d.ts +5 -0
  46. package/dist/services/panel-env.js +2 -0
  47. package/dist/services/port-detector.d.ts +3 -0
  48. package/dist/services/port-detector.js +6 -0
  49. package/dist/services/runtime-lifecycle-policy.d.ts +1 -0
  50. package/dist/services/runtime-lifecycle-policy.js +1 -1
  51. package/dist/services/system-metrics.js +1 -1
  52. package/dist/services/tunnel-client.d.ts +4 -0
  53. package/dist/services/tunnel-client.js +1 -1
  54. package/dist/services/workspace-files.d.ts +3 -0
  55. package/dist/services/workspace-files.js +1 -1
  56. package/dist/services/workspaceTaskChangeWatcher.d.ts +2 -0
  57. package/dist/services/workspaceTaskChangeWatcher.js +3 -3
  58. package/dist/utils/config-write-queue.d.ts +3 -0
  59. package/dist/utils/config-write-queue.js +2 -0
  60. package/package/acode/dist/built-in-file-tools.d.ts +2 -0
  61. package/package/acode/dist/runtime.d.ts +20 -0
  62. package/package/acode/dist/runtime.js +27 -27
  63. package/package/acode/dist/sub-agent-manager.d.ts +4 -0
  64. package/package/acode/dist/sub-agent-manager.js +1 -1
  65. package/package/acode/dist/sub-agent-tools.d.ts +6 -0
  66. package/package/acode/dist/sub-agent-tools.js +39 -10
  67. package/package/acode/dist/sub-agent-types.d.ts +2 -0
  68. package/package/acode/dist/tool-allocation.d.ts +55 -0
  69. package/package/acode/dist/tool-allocation.js +2 -0
  70. package/package/acode/dist/types.d.ts +6 -0
  71. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +24 -0
  72. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  73. package/package/aws-client-agent-mcp/dist/agent-client.test.js +19 -5
  74. package/package/aws-client-agent-mcp/dist/context-manager.test.js +6 -6
  75. package/package/aws-client-agent-mcp/dist/http-client.d.ts +2 -0
  76. package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
  77. package/package/aws-client-agent-mcp/dist/index.js +1 -1
  78. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.d.ts +1 -0
  79. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.js +106 -0
  80. package/package/aws-client-agent-mcp/dist/logger.d.ts +4 -0
  81. package/package/aws-client-agent-mcp/dist/logger.js +2 -2
  82. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +21 -0
  83. package/package/aws-client-agent-mcp/dist/mcp-server.js +25 -25
  84. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +24 -16
  85. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +20 -0
  86. package/package/aws-client-agent-mcp/dist/mcp-tools.js +8 -8
  87. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +55 -1
  88. package/package/aws-client-agent-mcp/dist/memory-store.test.js +5 -1
  89. package/package/aws-client-agent-mcp/dist/parent-lifecycle.d.ts +10 -0
  90. package/package/aws-client-agent-mcp/dist/parent-lifecycle.js +2 -0
  91. package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.d.ts +1 -0
  92. package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.js +81 -0
  93. package/package/aws-client-agent-mcp/dist/status-reporter.js +1 -1
  94. package/package/aws-client-agent-mcp/dist/types.d.ts +4 -0
  95. package/package/aws-client-agent-mcp/dist/watchdog-worker.d.ts +1 -0
  96. package/package/aws-client-agent-mcp/dist/watchdog-worker.js +3 -0
  97. package/package/aws-client-agent-mcp/dist/watchdog.d.ts +17 -0
  98. package/package/aws-client-agent-mcp/dist/watchdog.js +2 -0
  99. package/package/aws-client-agent-mcp/dist/watchdog.test.d.ts +1 -0
  100. package/package/aws-client-agent-mcp/dist/watchdog.test.js +124 -0
  101. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +4 -0
  102. package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -1
  103. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +83 -6
  104. package/package/aws-client-agent-mcp/package.json +1 -1
  105. package/package.json +2 -1
@@ -3,3 +3,7 @@
3
3
  export declare function startTunnelClient(serverUrl: string, instanceId: string): void;
4
4
 
5
5
  export declare function stopTunnelClient(): void;
6
+
7
+ export declare function stopTunnelClientForServer(serverUrl: string): void;
8
+
9
+ export declare function stopTunnelClientsExcept(serverUrls: string[]): void;
@@ -1,2 +1,2 @@
1
- import N,{AxiosError as R}from"axios";import h from"ws";import{getRuntimeAccessToken as M,normalizeSchedulerBaseUrl as b}from"./runtime-binding.js";import{loadConfigs as O,loadPrimaryLifecycleConfig as L}from"./auto-register.js";import{logger as c}from"../utils/logger.js";import{port as E}from"../config.js";const W=15e3,x=5e3,D=200,P=1*1024*1024,$=4001,q=768*1024,G=384*1024,g=new Map;function J(e){const r=e.serverUrl,n=e.instanceId;if(!r||!n)return null;try{const t=new URL(r),o=t.protocol==="https:"?"wss:":"ws:",i=M(r),l=O().find(m=>b(m.serverUrl)===r)||L(),s=i||l.userKey;if(i?c.debug("[TunnelClient] \u4F7F\u7528 runtime access token \u8FDE\u63A5\u96A7\u9053"):l.userKey&&c.warn("[TunnelClient] getRuntimeAccessToken \u8FD4\u56DE\u7A7A\uFF0C\u56DE\u9000\u5230 userKey\uFF08\u975E runtime token\uFF0C\u9274\u6743\u53EF\u80FD\u5931\u8D25\uFF09: targetUrl=",r),!s)return c.warn("[TunnelClient] \u7F3A\u5C11 runtime token\uFF0C\u65E0\u6CD5\u5EFA\u7ACB\u96A7\u9053: targetUrl=",r),null;const a=new URLSearchParams({instanceId:n});return{url:`${o}//${t.host}/ws/bridge-tunnel?${a.toString()}`,token:s}}catch(t){return c.warn("[TunnelClient] \u65E0\u6CD5\u89E3\u6790 serverUrl:",String(t)),null}}function ne(e,r){const n=b(e);if(!n){c.warn("[TunnelClient] serverUrl \u65E0\u6548\uFF0C\u65E0\u6CD5\u542F\u52A8\u96A7\u9053:",e);return}const t=g.get(n);t&&(t.shuttingDown=!0,k(t),g.delete(n));const o={serverUrl:n,instanceId:r,streams:new Map,shuttingDown:!1};g.set(n,o),v(o)}function re(){for(const e of g.values())e.shuttingDown=!0,k(e);g.clear()}function k(e){e.timer&&(clearInterval(e.timer),e.timer=void 0),e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0);for(const[r,n]of e.streams.entries()){try{n.ws.close(1e3,"tunnel client stopped")}catch{}e.streams.delete(r)}if(e.ws){e.ws.removeAllListeners();try{e.ws.close(1e3,"tunnel client stopped")}catch{}e.ws=void 0}}function v(e){if(e.shuttingDown)return;const r=e.instanceId;if(!r)return;const n=J(e);if(!n){S(e);return}c.info("[TunnelClient] \u8FDE\u63A5\u96A7\u9053\u670D\u52A1\u7AEF:",n.url);const t=new h(n.url,{handshakeTimeout:15e3,headers:{"X-Runtime-Token":n.token}});e.ws=t,t.on("open",()=>{c.info("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5DF2\u5EFA\u7ACB: server=",e.serverUrl,"instanceId=",r),e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0,e.timer=setInterval(()=>{t.readyState===h.OPEN&&t.send(JSON.stringify({t:"ping"}))},W)}),t.on("unexpected-response",(o,i)=>{c.error("[TunnelClient] \u670D\u52A1\u7AEF\u62D2\u7EDD\u8FDE\u63A5\uFF08\u975E 101 \u54CD\u5E94\uFF09: server=",e.serverUrl,"statusCode=",i.statusCode,"statusMessage=",i.statusMessage)}),t.on("upgrade",o=>{c.debug("[TunnelClient] \u6536\u5230\u5347\u7EA7\u54CD\u5E94: statusCode=",o.statusCode)}),t.on("message",o=>{let i;typeof o=="string"?i=o:Buffer.isBuffer(o)?i=o.toString("utf-8"):i=Buffer.from(o).toString("utf-8");try{const l=JSON.parse(i);K(e,l)}catch(l){c.warn("[TunnelClient] \u89E3\u6790\u96A7\u9053\u6D88\u606F\u5931\u8D25:",String(l))}}),t.on("close",(o,i)=>{if(c.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5173\u95ED: server=",e.serverUrl,"code=",o,"reason=",String(i)),B(e),o===$){c.warn("[TunnelClient] \u9274\u6743\u5931\u8D25\uFF084001\uFF09\uFF0C\u505C\u6B62\u81EA\u52A8\u91CD\u8FDE\uFF1B\u5F85\u91CD\u65B0\u6CE8\u518C\u540E\u518D\u542F\u52A8\u96A7\u9053: server=",e.serverUrl);return}S(e)}),t.on("error",o=>{c.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u9519\u8BEF: server=",e.serverUrl,"err=",o.message,"code=",o.code),B(e),S(e)})}function B(e){e.timer&&(clearInterval(e.timer),e.timer=void 0);for(const[r,n]of e.streams.entries()){try{n.ws.close(1011,"tunnel disconnected")}catch{}e.streams.delete(r)}e.ws=void 0}function S(e){e.shuttingDown||e.reconnectTimer||(e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=void 0,v(e)},x))}function K(e,r){switch(r.t){case"req":H(e,r);break;case"wsopen":X(e,r);break;case"wsmsg":Y(e,r);break;case"wsclose":j(e,r);break;case"ping":f(e,{t:"pong"});break;case"pong":break;default:c.debug("[TunnelClient] \u672A\u77E5\u6D88\u606F\u7C7B\u578B: t=",r.t)}}async function H(e,r){const n=r.id,t=r.method||"GET",o=r.path||"/",i=`http://127.0.0.1:${E}${o}`;try{const l=r.body?Buffer.from(r.body,"base64"):void 0,s={};if(r.headers)for(const[d,u]of Object.entries(r.headers))u&&u.length>0&&(s[d]=u[0]);s["x-bridge-tunnel-source"]=e.serverUrl;const a=await N.request({url:i,method:t,headers:s,data:l,timeout:0,responseType:"stream",validateStatus:()=>!0}),m={};for(const[d,u]of Object.entries(a.headers))u!==void 0&&(m[d]=Array.isArray(u)?u:[String(u)]);const y=a.data,p=[];let w=0,T=!1,C=!1;try{if(y)for await(const d of y){const u=Buffer.isBuffer(d)?d:Buffer.from(d);if(u.length!==0){if(T){if(!_(e,n,u))return}else if(p.push(u),w+=u.length,w>q){if(T=!0,C=!0,!f(e,{t:"res",id:n,status:a.status,headers:m,body:null,chunked:!0}))return;for(const I of p)if(!_(e,n,I))return;p.length=0,w=0}}}}catch(d){const u=d instanceof Error?d.message:String(d);c.warn("[TunnelClient] \u672C\u5730\u6D41\u8BFB\u53D6\u4E2D\u65AD:",t,o,"->",u),C?f(e,{t:"resend",id:n,error:u}):f(e,{t:"res",id:n,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(u).toString("base64")});return}if(T){f(e,{t:"resend",id:n});return}const A=w>0?Buffer.concat(p).toString("base64"):"";f(e,{t:"res",id:n,status:a.status,headers:m,body:A||null})}catch(l){const s=l instanceof R?l.message:String(l);c.warn("[TunnelClient] \u672C\u5730\u8F6C\u53D1\u5931\u8D25:",t,o,"->",s),f(e,{t:"res",id:n,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(s).toString("base64")})}}function _(e,r,n){let t=0;for(;t<n.length;){const o=n.subarray(t,t+G);if(!f(e,{t:"reschunk",id:r,d:o.toString("base64")}))return!1;t+=o.length}return!0}function X(e,r){const n=r.id,t=r.path||"/",o=`ws://127.0.0.1:${E}${t}`;let i;try{i=new h(o,{handshakeTimeout:1e4})}catch(s){c.warn("[TunnelClient] \u672C\u5730 WS \u8FDE\u63A5\u5931\u8D25:",String(s)),f(e,{t:"wsclose",id:n,code:1011,reason:"local ws connect failed"});return}const l={ws:i,pending:[]};e.streams.set(n,l),i.on("open",()=>{for(const s of l.pending)U(l,s.data,s.bin);l.pending=[],f(e,{t:"wsopened",id:n})}),i.on("message",(s,a)=>{const m=Buffer.isBuffer(s)?s:Buffer.from(s);a&&e.ws&&e.ws.bufferedAmount>P||f(e,{t:"wsmsg",id:n,bin:a,d:m.toString("base64")})}),i.on("close",(s,a)=>{e.streams.delete(n),f(e,{t:"wsclose",id:n,code:s,reason:String(a)})}),i.on("error",s=>{c.warn("[TunnelClient] \u672C\u5730 WS \u9519\u8BEF: id=",n,"err=",s.message),e.streams.delete(n);try{i.close(1011,s.message)}catch{}f(e,{t:"wsclose",id:n,code:1011,reason:s.message})})}function U(e,r,n){n?e.ws.send(r):e.ws.send(r.toString("utf-8"))}function Y(e,r){const n=e.streams.get(r.id);if(!n)return;const t=r.d?Buffer.from(r.d,"base64"):Buffer.alloc(0);if(n.ws.readyState!==h.OPEN){n.pending.length>=D&&(n.pending.shift(),c.warn("[TunnelClient] \u672C\u5730 WS \u672A\u5C31\u7EEA\uFF0C\u6392\u961F\u6D88\u606F\u8D85\u4E0A\u9650\uFF0C\u4E22\u5F03\u6700\u65E7\u4E00\u6761: id=",r.id)),n.pending.push({data:t,bin:!!r.bin});return}U(n,t,!!r.bin)}function j(e,r){const n=e.streams.get(r.id);if(n){e.streams.delete(r.id);try{n.ws.close(r.code??1e3,r.reason||"")}catch{}}}function f(e,r){if(!e.ws||e.ws.readyState!==h.OPEN)return!1;try{return e.ws.send(JSON.stringify(r)),!0}catch(n){return c.warn("[TunnelClient] sendJson \u5931\u8D25:",String(n)),!1}}export{ne as startTunnelClient,re as stopTunnelClient};
1
+ import R,{AxiosError as x}from"axios";import p from"ws";import{getRuntimeAccessToken as M,normalizeSchedulerBaseUrl as S}from"./runtime-binding.js";import{loadConfigs as L,loadPrimaryLifecycleConfig as O}from"./auto-register.js";import{logger as c}from"../utils/logger.js";import{port as v}from"../config.js";const D=15e3,W=5e3,P=200,$=1*1024*1024,q=4001,G=768*1024,J=384*1024,m=new Map;function K(e){const r=e.serverUrl,n=e.instanceId;if(!r||!n)return null;try{const t=new URL(r),o=t.protocol==="https:"?"wss:":"ws:",i=M(r),l=L().find(h=>S(h.serverUrl)===r)||O(),s=i||l.userKey;if(i?c.debug("[TunnelClient] \u4F7F\u7528 runtime access token \u8FDE\u63A5\u96A7\u9053"):l.userKey&&c.warn("[TunnelClient] getRuntimeAccessToken \u8FD4\u56DE\u7A7A\uFF0C\u56DE\u9000\u5230 userKey\uFF08\u975E runtime token\uFF0C\u9274\u6743\u53EF\u80FD\u5931\u8D25\uFF09: targetUrl=",r),!s)return c.warn("[TunnelClient] \u7F3A\u5C11 runtime token\uFF0C\u65E0\u6CD5\u5EFA\u7ACB\u96A7\u9053: targetUrl=",r),null;const a=new URLSearchParams({instanceId:n});return{url:`${o}//${t.host}/ws/bridge-tunnel?${a.toString()}`,token:s}}catch(t){return c.warn("[TunnelClient] \u65E0\u6CD5\u89E3\u6790 serverUrl:",String(t)),null}}function re(e,r){const n=S(e);if(!n){c.warn("[TunnelClient] serverUrl \u65E0\u6548\uFF0C\u65E0\u6CD5\u542F\u52A8\u96A7\u9053:",e);return}const t=m.get(n);t&&(t.shuttingDown=!0,C(t),m.delete(n));const o={serverUrl:n,instanceId:r,streams:new Map,shuttingDown:!1};m.set(n,o),B(o)}function te(){for(const e of m.values())e.shuttingDown=!0,C(e);m.clear()}function oe(e){const r=S(e);if(!r)return;const n=m.get(r);n&&(n.shuttingDown=!0,C(n),m.delete(r),c.info(`[TunnelClient] tunnel stopped: ${r}`))}function se(e){const r=new Set(e.map(n=>S(n)).filter(n=>!!n));for(const[n,t]of m.entries())r.has(n)||(c.info(`[TunnelClient] serverUrl \u5DF2\u4E0D\u5728\u914D\u7F6E\u4E2D\uFF0C\u505C\u6B62\u65E7\u96A7\u9053: ${n}`),t.shuttingDown=!0,C(t),m.delete(n))}function C(e){e.timer&&(clearInterval(e.timer),e.timer=void 0),e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0);for(const[r,n]of e.streams.entries()){try{n.ws.close(1e3,"tunnel client stopped")}catch{}e.streams.delete(r)}if(e.ws){e.ws.removeAllListeners();try{e.ws.close(1e3,"tunnel client stopped")}catch{}e.ws=void 0}}function B(e){if(e.shuttingDown)return;const r=e.instanceId;if(!r)return;const n=K(e);if(!n){b(e);return}c.info("[TunnelClient] \u8FDE\u63A5\u96A7\u9053\u670D\u52A1\u7AEF:",n.url);const t=new p(n.url,{handshakeTimeout:15e3,headers:{"X-Runtime-Token":n.token}});e.ws=t,t.on("open",()=>{c.info("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5DF2\u5EFA\u7ACB: server=",e.serverUrl,"instanceId=",r),e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0,e.timer=setInterval(()=>{t.readyState===p.OPEN&&t.send(JSON.stringify({t:"ping"}))},D)}),t.on("unexpected-response",(o,i)=>{c.error("[TunnelClient] \u670D\u52A1\u7AEF\u62D2\u7EDD\u8FDE\u63A5\uFF08\u975E 101 \u54CD\u5E94\uFF09: server=",e.serverUrl,"statusCode=",i.statusCode,"statusMessage=",i.statusMessage)}),t.on("upgrade",o=>{c.debug("[TunnelClient] \u6536\u5230\u5347\u7EA7\u54CD\u5E94: statusCode=",o.statusCode)}),t.on("message",o=>{let i;typeof o=="string"?i=o:Buffer.isBuffer(o)?i=o.toString("utf-8"):i=Buffer.from(o).toString("utf-8");try{const l=JSON.parse(i);H(e,l)}catch(l){c.warn("[TunnelClient] \u89E3\u6790\u96A7\u9053\u6D88\u606F\u5931\u8D25:",String(l))}}),t.on("close",(o,i)=>{if(c.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5173\u95ED: server=",e.serverUrl,"code=",o,"reason=",String(i)),U(e),o===q){c.warn("[TunnelClient] \u9274\u6743\u5931\u8D25\uFF084001\uFF09\uFF0C\u505C\u6B62\u81EA\u52A8\u91CD\u8FDE\uFF1B\u5F85\u91CD\u65B0\u6CE8\u518C\u540E\u518D\u542F\u52A8\u96A7\u9053: server=",e.serverUrl);return}b(e)}),t.on("error",o=>{c.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u9519\u8BEF: server=",e.serverUrl,"err=",o.message,"code=",o.code),U(e),b(e)})}function U(e){e.timer&&(clearInterval(e.timer),e.timer=void 0);for(const[r,n]of e.streams.entries()){try{n.ws.close(1011,"tunnel disconnected")}catch{}e.streams.delete(r)}e.ws=void 0}function b(e){e.shuttingDown||e.reconnectTimer||(e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=void 0,B(e)},W))}function H(e,r){switch(r.t){case"req":z(e,r);break;case"wsopen":F(e,r);break;case"wsmsg":X(e,r);break;case"wsclose":Y(e,r);break;case"ping":f(e,{t:"pong"});break;case"pong":break;default:c.debug("[TunnelClient] \u672A\u77E5\u6D88\u606F\u7C7B\u578B: t=",r.t)}}async function z(e,r){const n=r.id,t=r.method||"GET",o=r.path||"/",i=`http://127.0.0.1:${v}${o}`;try{const l=r.body?Buffer.from(r.body,"base64"):void 0,s={};if(r.headers)for(const[d,u]of Object.entries(r.headers))u&&u.length>0&&(s[d]=u[0]);s["x-bridge-tunnel-source"]=e.serverUrl;const a=await R.request({url:i,method:t,headers:s,data:l,timeout:0,responseType:"stream",validateStatus:()=>!0}),h={};for(const[d,u]of Object.entries(a.headers))u!==void 0&&(h[d]=Array.isArray(u)?u:[String(u)]);const k=a.data,A=String(h["content-type"]?.[0]||"").toLowerCase().includes("text/event-stream"),g=[];let w=0,T=!1,y=!1;try{if(k)for await(const d of k){const u=Buffer.isBuffer(d)?d:Buffer.from(d);if(u.length!==0){if(T){if(!E(e,n,u))return}else if(A){if(T=!0,y=!0,!f(e,{t:"res",id:n,status:a.status,headers:h,body:null,chunked:!0})||!E(e,n,u))return}else if(g.push(u),w+=u.length,w>G){if(T=!0,y=!0,!f(e,{t:"res",id:n,status:a.status,headers:h,body:null,chunked:!0}))return;for(const N of g)if(!E(e,n,N))return;g.length=0,w=0}}}}catch(d){const u=d instanceof Error?d.message:String(d);c.warn("[TunnelClient] \u672C\u5730\u6D41\u8BFB\u53D6\u4E2D\u65AD:",t,o,"->",u),y?f(e,{t:"resend",id:n,error:u}):f(e,{t:"res",id:n,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(u).toString("base64")});return}if(T){f(e,{t:"resend",id:n});return}const I=w>0?Buffer.concat(g).toString("base64"):"";f(e,{t:"res",id:n,status:a.status,headers:h,body:I||null})}catch(l){const s=l instanceof x?l.message:String(l);c.warn("[TunnelClient] \u672C\u5730\u8F6C\u53D1\u5931\u8D25:",t,o,"->",s),f(e,{t:"res",id:n,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(s).toString("base64")})}}function E(e,r,n){let t=0;for(;t<n.length;){const o=n.subarray(t,t+J);if(!f(e,{t:"reschunk",id:r,d:o.toString("base64")}))return!1;t+=o.length}return!0}function F(e,r){const n=r.id,t=r.path||"/",o=`ws://127.0.0.1:${v}${t}`;let i;try{i=new p(o,{handshakeTimeout:1e4})}catch(s){c.warn("[TunnelClient] \u672C\u5730 WS \u8FDE\u63A5\u5931\u8D25:",String(s)),f(e,{t:"wsclose",id:n,code:1011,reason:"local ws connect failed"});return}const l={ws:i,pending:[]};e.streams.set(n,l),i.on("open",()=>{for(const s of l.pending)_(l,s.data,s.bin);l.pending=[],f(e,{t:"wsopened",id:n})}),i.on("message",(s,a)=>{const h=Buffer.isBuffer(s)?s:Buffer.from(s);a&&e.ws&&e.ws.bufferedAmount>$||f(e,{t:"wsmsg",id:n,bin:a,d:h.toString("base64")})}),i.on("close",(s,a)=>{e.streams.delete(n),f(e,{t:"wsclose",id:n,code:s,reason:String(a)})}),i.on("error",s=>{c.warn("[TunnelClient] \u672C\u5730 WS \u9519\u8BEF: id=",n,"err=",s.message),e.streams.delete(n);try{i.close(1011,s.message)}catch{}f(e,{t:"wsclose",id:n,code:1011,reason:s.message})})}function _(e,r,n){n?e.ws.send(r):e.ws.send(r.toString("utf-8"))}function X(e,r){const n=e.streams.get(r.id);if(!n)return;const t=r.d?Buffer.from(r.d,"base64"):Buffer.alloc(0);if(n.ws.readyState!==p.OPEN){n.pending.length>=P&&(n.pending.shift(),c.warn("[TunnelClient] \u672C\u5730 WS \u672A\u5C31\u7EEA\uFF0C\u6392\u961F\u6D88\u606F\u8D85\u4E0A\u9650\uFF0C\u4E22\u5F03\u6700\u65E7\u4E00\u6761: id=",r.id)),n.pending.push({data:t,bin:!!r.bin});return}_(n,t,!!r.bin)}function Y(e,r){const n=e.streams.get(r.id);if(n){e.streams.delete(r.id);try{n.ws.close(r.code??1e3,r.reason||"")}catch{}}}function f(e,r){if(!e.ws||e.ws.readyState!==p.OPEN)return!1;try{return e.ws.send(JSON.stringify(r)),!0}catch(n){return c.warn("[TunnelClient] sendJson \u5931\u8D25:",String(n)),!1}}export{re as startTunnelClient,te as stopTunnelClient,oe as stopTunnelClientForServer,se as stopTunnelClientsExcept};
2
2
 
@@ -14,6 +14,8 @@ export interface ReadWorkspaceFileResult {
14
14
  workspacePath: string;
15
15
  filePath: string;
16
16
  content: string;
17
+
18
+ mtimeMs: number;
17
19
  }
18
20
  export interface WorkspaceDocumentPreviewResult {
19
21
  workspacePath: string;
@@ -197,6 +199,7 @@ export declare function writeWorkspaceFile(params: WriteWorkspaceFileParams): Pr
197
199
  ok: true;
198
200
  workspacePath: string;
199
201
  filePath: string;
202
+ mtimeMs: number;
200
203
  }>;
201
204
 
202
205
  export declare function createWorkspaceEntry(params: CreateWorkspaceEntryParams): Promise<{
@@ -1,2 +1,2 @@
1
- import{createReadStream as U,createWriteStream as O,promises as c}from"node:fs";import lt from"node:os";import d from"node:path";import{Readable as ht}from"node:stream";import{pipeline as ut}from"node:stream/promises";import{TextDecoder as dt}from"node:util";import{createGunzip as wt}from"node:zlib";import*as ft from"tar";import pt from"unzipper";const{ZipArchive:_}=await import("archiver"),x={maxEntries:1e4,maxFileBytes:512*1024*1024,maxTotalBytes:2*1024*1024*1024,maxPathDepth:64,maxPathLength:4096},mt=new dt("gbk");function u(t){return String(t||"").replace(/\\/g,"/")}function Z(t){const e=d.resolve(String(t||"").trim());if(!e)throw new Error("workspacePath is required");return e}async function yt(t){const e=Z(t),r=await c.realpath(e);if(!(await c.stat(r)).isDirectory())throw new Error(`Workspace path is not a directory: ${e}`);return r}async function v(t){return{requestedRoot:Z(t),workspaceRoot:await yt(t)}}function C(t,e){const r=d.resolve(e),i=d.relative(t,r);if(i.startsWith("..")||d.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function F(t){return c.realpath(t)}async function T(t){await c.realpath(d.dirname(t));try{if((await c.lstat(t)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${t}`)}catch(e){if((typeof e=="object"&&e!==null?Reflect.get(e,"code"):void 0)!=="ENOENT")throw e}}function L(t,e,r){const i=d.resolve(r),o=d.relative(t,i);return o.startsWith("..")||d.isAbsolute(o)?i:C(e,d.join(e,o))}async function b({workspacePath:t,targetPath:e=""}){const{requestedRoot:r,workspaceRoot:i}=await v(t),o=String(e||"").trim(),a=o?L(r,i,o):i,s=await F(a);return{workspaceRoot:i,resolvedTargetPath:s}}function $(t){const e=String(t||"").trim();if(!e)throw new Error("entryName is required");if(e==="."||e==="..")throw new Error("entryName is invalid");if(e.includes("/")||e.includes("\\")||d.basename(e)!==e)throw new Error(`entryName must be a plain file or directory name: ${e}`);return e}function G(t){const e=String(t||"");if(!/[\u00c0-\u00ff]/.test(e))return e;const r=Buffer.from(e,"latin1").toString("utf8");return!r||r.includes("\uFFFD")?e:r}function B(t){const e=G(t);return $(d.basename(e.replace(/\\/g,"/")))}function gt(t,e){if(!t)return B(e);const i=G(String(t||e||"")).replace(/\\/g,"/").split("/").filter(Boolean).map(o=>$(o));return i.length===0?B(e):d.join(...i)}function H(t){return(t&4095).toString(8).padStart(4,"0")}function Pt(t){const e=String(t||"").trim();if(!/^[0-7]{3,4}$/.test(e))throw new Error("mode must be a 3 or 4 digit octal string");return Number.parseInt(e,8)}function I(t,e,r,i,o,a,s){const n=t&e?"r":"-",l=t&r?"w":"-",h=(t&i)!==0,f=(t&o)!==0?h?a:s:h?"x":"-";return`${n}${l}${f}`}function kt(t){return[I(t,256,128,64,2048,"s","S"),I(t,32,16,8,1024,"s","S"),I(t,4,2,1,512,"t","T")].join("")}function xt(t){if(!(t.length<24||t.toString("hex",0,8)!=="89504e470d0a1a0a"))return{format:"png",width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function vt(t){if(!(t.length<10||t.toString("ascii",0,6)!=="GIF87a"&&t.toString("ascii",0,6)!=="GIF89a"))return{format:"gif",width:t.readUInt16LE(6),height:t.readUInt16LE(8)}}function Et(t){if(t.length<4||t[0]!==255||t[1]!==216)return;let e=2;for(;e+9<t.length;){if(t[e]!==255){e+=1;continue}const r=t[e+1],i=t.readUInt16BE(e+2);if(i<2)return;if(r>=192&&r<=207&&![196,200,204].includes(r)&&e+8<t.length)return{format:"jpeg",height:t.readUInt16BE(e+5),width:t.readUInt16BE(e+7)};e+=2+i}}function St(t){if(t.length<30||t.toString("ascii",0,4)!=="RIFF"||t.toString("ascii",8,12)!=="WEBP")return;const e=t.toString("ascii",12,16);if(e==="VP8X"&&t.length>=30)return{format:"webp",width:1+t.readUIntLE(24,3),height:1+t.readUIntLE(27,3)};if(e==="VP8 "&&t.length>=30)return{format:"webp",width:t.readUInt16LE(26)&16383,height:t.readUInt16LE(28)&16383};if(e==="VP8L"&&t.length>=25){const r=t.readUInt32LE(21);return{format:"webp",width:(r&16383)+1,height:(r>>14&16383)+1}}}async function K(t,e){if((await c.lstat(t)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${t}`);const i=await c.stat(t);if(!e||!i.isDirectory())return[t];const o=await c.readdir(t),a=await Promise.all(o.map(s=>{const n=d.join(t,s);return K(n,!0)}));return[t,...a.flat()]}async function Rt(t){if((await c.lstat(t)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${t}`);await F(t)}async function Dt(t){const e=await c.open(t,"r");try{const r=Buffer.alloc(512),{bytesRead:i}=await e.read(r,0,r.length,0),o=r.subarray(0,i);return xt(o)||vt(o)||Et(o)||St(o)}finally{await e.close()}}async function z({workspacePath:t,targetPath:e},r={}){const{requestedRoot:i,workspaceRoot:o}=await v(t),a=String(e||"").trim();if(!a)throw new Error("targetPath is required");const s=L(i,o,a),n=await F(s);if(!r.allowWorkspaceRoot&&n===o)throw new Error("Workspace root cannot be modified");return{workspaceRoot:o,resolvedTargetPath:n}}async function X(t){const{requestedRoot:e,workspaceRoot:r}=await v(t.workspacePath),i=String(t.targetPath||"").trim();if(!i)return{workspaceRoot:r,resolvedTargetPath:r};const o=L(e,r,i);return await et(o)?{workspaceRoot:r,resolvedTargetPath:await c.realpath(o)}:(await c.realpath(d.dirname(o)),{workspaceRoot:r,resolvedTargetPath:o})}function V(t){const e=t.toLowerCase();return e.endsWith(".zip")?"zip":e.endsWith(".tar.gz")||e.endsWith(".tgz")?"tgz":e.endsWith(".tar")?"tar":e.endsWith(".gz")?"gz":null}function Y(t){return t.replace(/\.tar\.gz$/i,"").replace(/\.tgz$/i,"").replace(/\.zip$/i,"").replace(/\.tar$/i,"").replace(/\.gz$/i,"")}function Ft(t,e){const r=$(Y(e)||"extracted");return d.join(t,r)}function N(t,e){const r=String(e||"").replace(/\\/g,"/"),i=r.split("/").filter(Boolean);if(!r||r.startsWith("/")||/^[A-Za-z]:/.test(r))throw new Error(`Archive entry path is invalid: ${e}`);if(r.length>x.maxPathLength||i.length>x.maxPathDepth)throw new Error(`Archive entry path is too deep or too long: ${e}`);return C(t,d.join(t,r))}function Tt(t){const e=t.props?.pathBuffer;if(!e||t.props?.flags?.isUnicode)return t.path;const r=mt.decode(e);return!r||r.includes("\uFFFD")?t.path:r}async function J(t){const e=t.autodrain?.();e&&await new Promise((r,i)=>{e.on("close",r),e.on("end",r),e.on("finish",r),e.on("error",i)})}function M(t){if(t.entries+=1,t.entries>x.maxEntries)throw new Error(`Archive contains too many entries. Limit: ${x.maxEntries}`)}function Q(t,e){if(t.totalBytes+=e,e>x.maxFileBytes)throw new Error(`Archive entry exceeds max size. Limit: ${x.maxFileBytes} bytes`);if(t.totalBytes>x.maxTotalBytes)throw new Error(`Archive extraction exceeds total size. Limit: ${x.maxTotalBytes} bytes`)}async function tt(t,e,r){let i=0;t.on("data",o=>{const a=Buffer.isBuffer(o)?o.length:Buffer.byteLength(o);i+=a,(i>x.maxFileBytes||r.totalBytes+i>x.maxTotalBytes)&&t instanceof ht&&t.destroy(new Error("Archive extraction size limit exceeded"))}),await ut(t,O(e)),Q(r,i)}async function et(t){try{return await c.access(t),!0}catch(e){if((typeof e=="object"&&e!==null?Reflect.get(e,"code"):void 0)==="ENOENT")return!1;throw e}}async function W({workspacePath:t,filePath:e}){const{requestedRoot:r,workspaceRoot:i}=await v(t),o=String(e||"").trim();if(!o)throw new Error("filePath is required");return{workspaceRoot:i,resolvedFilePath:L(r,i,o)}}async function Gt(t){const{workspaceRoot:e}=await v(t.workspacePath),r=String(t.targetPath||"").trim();if(!r){const{resolvedTargetPath:l}=await b(t);return rt(e,l)}const i=d.resolve(r);let o;try{o=await c.realpath(i)}catch{throw new Error(`Path not found: ${i}`)}if(!(await c.stat(o)).isDirectory())throw new Error(`Path is not a directory: ${i}`);const s=d.relative(e,o),n=s===""||!s.startsWith("..")&&!d.isAbsolute(s);return rt(e,n?o:i)}async function rt(t,e){if(!(await c.stat(e)).isDirectory())throw new Error(`Path is not a directory: ${e}`);const i=await c.readdir(e,{withFileTypes:!0}),o=await Promise.all(i.map(async a=>{const s=d.join(e,a.name),n=a.isFile()?(await c.stat(s)).size:void 0;return{name:a.name,path:u(s),isDirectory:a.isDirectory(),size:n}}));return{currentPath:u(e),workspacePath:u(t),items:o}}async function Ht(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await F(r);if(!(await c.stat(i)).isFile())throw new Error(`Path is not a file: ${i}`);const a=await c.readFile(i,"utf-8");return{workspacePath:u(e),filePath:u(i),content:a}}const it={maxEntries:1e5,maxDepth:32};async function zt(t){let e=0,r=0;async function i(o,a){if(a>=it.maxDepth)return;let s;try{s=await c.readdir(o,{withFileTypes:!0})}catch{return}for(const n of s){if(r+=1,r>it.maxEntries)return;if(n.isSymbolicLink())continue;const l=d.join(o,n.name);try{if(n.isDirectory())await i(l,a+1);else if(n.isFile()){const h=await c.stat(l);e+=h.size}}catch{}}}return await i(t,0),e}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t,{allowWorkspaceRoot:!0}),[i,o]=await Promise.all([c.stat(r),c.lstat(r)]),a=i.isDirectory(),s=i.isFile()?await Dt(r):void 0,n=a?await zt(r):i.size;return{workspacePath:u(e),targetPath:u(r),name:d.basename(r),isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:o.isSymbolicLink(),size:n,mode:i.mode,modeOctal:H(i.mode),permissions:kt(i.mode),uid:i.uid,gid:i.gid,atimeMs:i.atimeMs,mtimeMs:i.mtimeMs,ctimeMs:i.ctimeMs,birthtimeMs:i.birthtimeMs,...s?{image:s}:{}}}async function Xt(t){const e=Pt(t.mode),r=!!t.recursive,{workspaceRoot:i,resolvedTargetPath:o}=await z(t),a=await K(o,r);for(const s of a)await Rt(s),await c.chmod(s,e);return{ok:!0,workspacePath:u(i),targetPath:u(o),mode:e,modeOctal:H(e),recursive:r,changedCount:a.length}}async function Vt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await F(r),o=await c.stat(i);if(!o.isFile())throw new Error(`Path is not a file: ${i}`);const a=d.extname(i).toLowerCase();if(a===".doc")return{workspacePath:u(e),filePath:u(i),kind:"unsupported",content:"",message:"\u6682\u4E0D\u652F\u6301\u65E7\u7248 .doc \u683C\u5F0F\u9884\u89C8\uFF0C\u8BF7\u53E6\u5B58\u4E3A .docx \u540E\u518D\u6253\u5F00\u3002",size:o.size,mtimeMs:o.mtimeMs};if(a!==".docx")throw new Error("Document preview only supports .docx files");return{workspacePath:u(e),filePath:u(i),kind:"html",content:"",size:o.size,mtimeMs:o.mtimeMs}}async function Yt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t);return await c.mkdir(d.dirname(r),{recursive:!0}),await T(r),await c.writeFile(r,String(t.content??""),"utf-8"),{ok:!0,workspacePath:u(e),filePath:u(r)}}async function Jt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await b(t),i=$(t.entryName),o=d.join(r,i);if(!(await c.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);try{throw await c.access(o),new Error(`Path already exists: ${o}`)}catch(s){if((typeof s=="object"&&s!==null?Reflect.get(s,"code"):void 0)!=="ENOENT")throw s}return t.isDirectory?await c.mkdir(o,{recursive:!1}):(await c.mkdir(d.dirname(o),{recursive:!0}),await T(o),await c.writeFile(o,"","utf-8")),{ok:!0,workspacePath:u(e),targetPath:u(o),isDirectory:t.isDirectory}}async function Qt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=$(t.newName),o=d.join(d.dirname(r),i);if(o===r)return{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(o)};await c.stat(r);try{throw await c.access(o),new Error(`Path already exists: ${o}`)}catch(a){if((typeof a=="object"&&a!==null?Reflect.get(a,"code"):void 0)!=="ENOENT")throw a}return await c.rename(r,o),{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(o)}}async function te(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=await b({workspacePath:t.workspacePath,targetPath:t.destinationPath});if(!(await c.stat(i.resolvedTargetPath)).isDirectory())throw new Error(`Destination is not a directory: ${i.resolvedTargetPath}`);if((await c.stat(r)).isDirectory()){const n=d.relative(r,i.resolvedTargetPath);if(n===""||!n.startsWith("..")&&!d.isAbsolute(n))throw new Error("Cannot move a directory into itself or its descendant")}const s=d.join(i.resolvedTargetPath,d.basename(r));if(s===r)return{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(s),destinationPath:u(i.resolvedTargetPath)};try{throw await c.access(s),new Error(`Path already exists: ${s}`)}catch(n){if((typeof n=="object"&&n!==null?Reflect.get(n,"code"):void 0)!=="ENOENT")throw n}return await c.rename(r,s),{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(s),destinationPath:u(i.resolvedTargetPath)}}async function ee(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=await c.stat(r),o=d.parse(r).root;if(r===o)throw new Error("Filesystem root cannot be deleted");return i.isDirectory()?await c.rm(r,{recursive:!0,force:!1}):await c.unlink(r),{ok:!0,workspacePath:u(e),targetPath:u(r)}}async function re(t){if(!Array.isArray(t.files)||t.files.length===0)throw new Error("files are required");const{workspaceRoot:e,resolvedTargetPath:r}=await X(t);if(!(await c.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const o=[];for(const a of t.files){const s=B(a.originalname),n=gt(a.relativePath,a.originalname),l=d.join(r,n);await c.mkdir(d.dirname(l),{recursive:!0}),await T(l),await c.copyFile(a.path,l);let h;t.extractArchives&&V(l)&&(h=await Wt({workspacePath:e,archivePath:l,outputPath:Ft(r,s)})),o.push({fileName:s,targetPath:u(l),size:a.size,extracted:h})}return{ok:!0,workspacePath:u(e),targetPath:u(r),files:o}}async function ie(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=await c.stat(r),o=d.basename(r);return{workspacePath:u(e),targetPath:u(r),resolvedTargetPath:r,fileName:i.isDirectory()?`${o||"workspace"}.zip`:o,contentType:i.isDirectory()?"application/zip":Lt(r),isDirectory:i.isDirectory(),size:i.isDirectory()?void 0:i.size}}function Lt(t){const e=d.extname(t).toLowerCase();return{".svg":"image/svg+xml",".svgz":"image/svg+xml",".ico":"image/x-icon",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".bmp":"image/bmp",".avif":"image/avif",".txt":"text/plain; charset=utf-8",".json":"application/json; charset=utf-8",".xml":"application/xml; charset=utf-8",".html":"text/html; charset=utf-8",".htm":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".md":"text/markdown; charset=utf-8",".csv":"text/csv; charset=utf-8",".pdf":"application/pdf",".zip":"application/zip",".mp3":"audio/mpeg",".wav":"audio/wav",".mp4":"video/mp4",".webm":"video/webm"}[e]||"application/octet-stream"}async function oe(t,e){const r=new _({zlib:{level:9}}),i=new Promise((o,a)=>{e.on("close",o),e.on("finish",o),r.on("error",a),e.on("error",a)});r.pipe(e),r.directory(t,!1),await r.finalize(),await i}async function ae(t,e,r,i){const o=d.join(lt.tmpdir(),"agentswork-bridge-downloads");await c.mkdir(o,{recursive:!0});const a=(e||"workspace.zip").replace(/[^\w.-]/g,"_").replace(/\.zip$/i,"")||"workspace",s=d.join(o,`${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.zip`),n=new _({zlib:{level:9}}),l=()=>n.pointer(),h=()=>n.abort(),w=O(s),f=new Promise((m,g)=>{w.on("close",m),w.on("finish",m),n.on("error",g),w.on("error",g)});f.catch(()=>{}),n.pipe(w);const p=setInterval(()=>{try{if(i?.()){h();return}r?.(l())}catch{}},200);try{return n.on("progress",()=>{i?.()&&h()}),n.directory(t,!1),i?.()&&h(),await n.finalize(),await f,r?.(l()),s}catch(m){throw await c.rm(s,{force:!0}).catch(()=>{}),m}finally{clearInterval(p)}}async function $t(t,e,r){let i=0,o;const a=U(t),s=a.pipe(pt.Parse({forceStream:!0}));try{for await(const n of s){if(o){await J(n);continue}const l=Tt(n);try{const h=N(e,l);if(M(r),n.type==="Directory"){await c.mkdir(h,{recursive:!0});continue}if(n.type!=="File")throw new Error(`Unsupported archive entry type: ${l}`);await c.mkdir(d.dirname(h),{recursive:!0}),await T(h),await tt(n,h,r),i+=1}catch(h){o=h,await J(n)}}}finally{a.destroy(),s.destroy()}if(o)throw o;return i}async function At(t,e,r,i){let o=0,a;const s=(n,l)=>{if(a)return!1;try{N(e,n),M(i);const h=String(l.type||"").toLowerCase();if(h==="symboliclink"||h==="link")throw new Error(`Archive links are not supported: ${n}`);if(h!=="file"&&h!=="oldfile"&&h!=="contiguousfile"&&h!=="directory")throw new Error(`Unsupported archive entry type: ${n}`);return typeof l.size=="number"&&Q(i,l.size),h!=="directory"&&(o+=1),!0}catch(h){return a=h,!1}};if(await ft.x({file:t,cwd:e,gzip:r,strict:!0,filter:(n,l)=>s(n,l)}),a)throw a;return o}async function Ct(t,e,r){const i=Y(d.basename(t)),o=N(e,i||"decompressed");return M(r),await c.mkdir(d.dirname(o),{recursive:!0}),await T(o),await tt(U(t).pipe(wt()),o,r),1}async function Wt(t){const{requestedRoot:e,workspaceRoot:r}=await v(t.workspacePath),{resolvedTargetPath:i}=await X({workspacePath:t.workspacePath,targetPath:t.outputPath||""}),o=L(e,r,String(t.archivePath||"").trim()),a=await F(o);if(!(await c.stat(a)).isFile())throw new Error(`Archive path is not a file: ${a}`);if(await et(i)||await c.mkdir(i,{recursive:!0}),!(await c.stat(i)).isDirectory())throw new Error(`Output path is not a directory: ${i}`);const l=V(a);if(!l)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let h=0;const w={entries:0,totalBytes:0};return l==="zip"?h=await $t(a,i,w):l==="tar"||l==="tgz"?h=await At(a,i,l==="tgz",w):h=await Ct(a,i,w),{ok:!0,workspacePath:u(r),archivePath:u(a),targetPath:u(i),extractedEntries:h}}function ot(t){const e=t.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!e)return[t];const[,r,i,o]=e;return i.split(",").flatMap(a=>ot(r+a.trim()+o))}function at(t,e){const r=String(e||"").trim();if(!r||r==="*"||r==="*.*")return!0;const i=t.toLowerCase();return ot(r.toLowerCase()).some(a=>{if(a==="*")return!0;const s=a.replace(/\./g,"\\.").replace(/\*\*/g,"<<GLOBSTAR>>").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]").replace(/<<GLOBSTAR>>/g,".*");return new RegExp(`^${s}$`).test(i)})}function j(t){return t===".git"||t==="node_modules"||t===".svn"||t===".hg"}const nt=10*1024*1024,bt=1e3;async function ne(t){const{workspaceRoot:e}=await v(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=!!t.ignoreCase,o=String(t.fileMask||"").trim(),a=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:bt,s=i?r.toLowerCase():r,l=(t.mode==="filename"?"filename":"content")==="filename",h=[];let w=0,f=!1;async function p(m){if(f)return;let g;try{g=await c.readdir(m,{withFileTypes:!0})}catch{return}for(const y of g){if(f)return;const P=C(e,d.join(m,y.name));if(y.isSymbolicLink())continue;if(y.isDirectory()){if(j(y.name))continue;await p(P);continue}if(!y.isFile()||o&&!at(y.name,o))continue;w+=1;let E;try{E=await c.stat(P)}catch{continue}if(E.size>nt)continue;if(l){const R=(i?y.name.toLowerCase():y.name).indexOf(s);if(R!==-1&&(h.push({filePath:u(P),lineNumber:0,lineContent:y.name,matchStart:R,matchEnd:R+r.length}),h.length>=a)){f=!0;return}continue}let k;try{k=await c.readFile(P,"utf-8")}catch{continue}const D=k.split(/\r?\n/);for(let S=0;S<D.length;S+=1){const R=D[S],ct=i?R.toLowerCase():R;let q=0;for(;;){const A=ct.indexOf(s,q);if(A===-1)break;if(h.push({filePath:u(P),lineNumber:S+1,lineContent:R,matchStart:A,matchEnd:A+r.length}),h.length>=a){f=!0;return}q=A+1}}}}return await p(e),{workspacePath:u(e),keyword:r,totalFiles:w,totalMatches:h.length,matches:h,truncated:f}}const Bt=100,It=120;async function se(t){const{requestedRoot:e,workspaceRoot:r}=await v(t.workspacePath),i=String(t.keyword||"").trim();if(!i)throw new Error("keyword is required");const o=!!t.ignoreCase,a=o?i.toLowerCase():i,s=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:Bt,n=typeof t.maxDirectories=="number"&&t.maxDirectories>0?t.maxDirectories:It,l=String(t.rootPath||"").trim(),h=l?d.resolve(/^[A-Za-z]:$/.test(l)?`${l}/`:l):r;let w;try{w=await c.realpath(h)}catch{throw new Error(`Path not found: ${h}`)}if(!(await c.stat(w)).isDirectory())throw new Error(`Path is not a directory: ${h}`);const p=[],m=[w];let g=0,y=!1;for(;m.length>0&&p.length<s&&g<n;){const P=m.shift();g+=1;let E;try{E=await c.readdir(P,{withFileTypes:!0})}catch{continue}for(const k of E){if(p.length>=s){y=!0;break}if(k.isSymbolicLink())continue;const D=d.join(P,k.name);if(k.isDirectory()&&j(k.name))continue;const S=d.relative(w,D).replace(/\\/g,"/");(o?S.toLowerCase():S).includes(a)&&p.push({name:k.name,path:u(D),isDirectory:k.isDirectory()}),k.isDirectory()&&g<n&&m.push(D)}}return(g>=n||p.length>=s)&&(y=!0),p.sort((P,E)=>P.isDirectory===E.isDirectory?P.name.localeCompare(E.name):P.isDirectory?-1:1),{workspacePath:u(r),rootPath:u(w),keyword:i,items:p,truncated:y}}async function st(t,e,r,i){let o;try{o=await c.readFile(t,"utf-8")}catch{return{changed:!1,count:0}}const a=o;let s=0;if(i){const n=e.toLowerCase();let l=0;for(;;){const w=o.toLowerCase().indexOf(n,l);if(w===-1)break;o=o.slice(0,w)+r+o.slice(w+e.length),s+=1;const f=r.length||1;l=w+f}}else{let n=0;for(;;){const l=o.indexOf(e,n);if(l===-1)break;o=o.slice(0,l)+r+o.slice(l+e.length),s+=1;const h=r.length||1;n=l+h}}return o===a?{changed:!1,count:0}:(await T(t),await c.writeFile(t,o,"utf-8"),{changed:!0,count:s})}async function ce(t){const{workspaceRoot:e}=await v(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=String(t.replacement??""),o=!!t.ignoreCase,a=String(t.fileMask||"").trim(),s=String(t.filePath||"").trim();let n=0,l=0;if(s){const{resolvedFilePath:w}=await W({workspacePath:t.workspacePath,filePath:s}),f=await st(w,r,i,o);return f.changed&&(n=1,l=f.count),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:l}}async function h(w){let f;try{f=await c.readdir(w,{withFileTypes:!0})}catch{return}for(const p of f){const m=C(e,d.join(w,p.name));if(p.isSymbolicLink())continue;if(p.isDirectory()){if(j(p.name))continue;await h(m);continue}if(!p.isFile()||a&&!at(p.name,a))continue;try{if((await c.stat(m)).size>nt)continue}catch{continue}const g=await st(m,r,i,o);g.changed&&(n+=1,l+=g.count)}}return await h(e),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:l}}export{Xt as chmodWorkspaceEntry,Jt as createWorkspaceEntry,ee as deleteWorkspaceEntry,Wt as extractWorkspaceArchive,Kt as getWorkspaceEntryProperties,Gt as listWorkspaceDirectory,te as moveWorkspaceEntry,Vt as previewWorkspaceDocument,Ht as readWorkspaceFile,Qt as renameWorkspaceEntry,ce as replaceWorkspaceFiles,ie as resolveWorkspaceDownloadTarget,ne as searchWorkspaceFiles,se as searchWorkspacePaths,oe as streamWorkspaceDirectoryZip,re as uploadWorkspaceFiles,Yt as writeWorkspaceFile,ae as zipWorkspaceDirectoryToTemp};
1
+ import{createReadStream as U,createWriteStream as O,promises as s}from"node:fs";import lt from"node:os";import d from"node:path";import{Readable as ht}from"node:stream";import{pipeline as ut}from"node:stream/promises";import{TextDecoder as dt}from"node:util";import{createGunzip as wt}from"node:zlib";import*as ft from"tar";import pt from"unzipper";const{ZipArchive:_}=await import("archiver"),x={maxEntries:1e4,maxFileBytes:512*1024*1024,maxTotalBytes:2*1024*1024*1024,maxPathDepth:64,maxPathLength:4096},mt=new dt("gbk");function u(t){return String(t||"").replace(/\\/g,"/")}function Z(t){const e=d.resolve(String(t||"").trim());if(!e)throw new Error("workspacePath is required");return e}async function yt(t){const e=Z(t),r=await s.realpath(e);if(!(await s.stat(r)).isDirectory())throw new Error(`Workspace path is not a directory: ${e}`);return r}async function v(t){return{requestedRoot:Z(t),workspaceRoot:await yt(t)}}function C(t,e){const r=d.resolve(e),i=d.relative(t,r);if(i.startsWith("..")||d.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function F(t){return s.realpath(t)}async function T(t){await s.realpath(d.dirname(t));try{if((await s.lstat(t)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${t}`)}catch(e){if((typeof e=="object"&&e!==null?Reflect.get(e,"code"):void 0)!=="ENOENT")throw e}}function L(t,e,r){const i=d.resolve(r),o=d.relative(t,i);return o.startsWith("..")||d.isAbsolute(o)?i:C(e,d.join(e,o))}async function b({workspacePath:t,targetPath:e=""}){const{requestedRoot:r,workspaceRoot:i}=await v(t),o=String(e||"").trim(),a=o?L(r,i,o):i,c=await F(a);return{workspaceRoot:i,resolvedTargetPath:c}}function $(t){const e=String(t||"").trim();if(!e)throw new Error("entryName is required");if(e==="."||e==="..")throw new Error("entryName is invalid");if(e.includes("/")||e.includes("\\")||d.basename(e)!==e)throw new Error(`entryName must be a plain file or directory name: ${e}`);return e}function G(t){const e=String(t||"");if(!/[\u00c0-\u00ff]/.test(e))return e;const r=Buffer.from(e,"latin1").toString("utf8");return!r||r.includes("\uFFFD")?e:r}function B(t){const e=G(t);return $(d.basename(e.replace(/\\/g,"/")))}function gt(t,e){if(!t)return B(e);const i=G(String(t||e||"")).replace(/\\/g,"/").split("/").filter(Boolean).map(o=>$(o));return i.length===0?B(e):d.join(...i)}function H(t){return(t&4095).toString(8).padStart(4,"0")}function Pt(t){const e=String(t||"").trim();if(!/^[0-7]{3,4}$/.test(e))throw new Error("mode must be a 3 or 4 digit octal string");return Number.parseInt(e,8)}function I(t,e,r,i,o,a,c){const n=t&e?"r":"-",l=t&r?"w":"-",h=(t&i)!==0,f=(t&o)!==0?h?a:c:h?"x":"-";return`${n}${l}${f}`}function kt(t){return[I(t,256,128,64,2048,"s","S"),I(t,32,16,8,1024,"s","S"),I(t,4,2,1,512,"t","T")].join("")}function xt(t){if(!(t.length<24||t.toString("hex",0,8)!=="89504e470d0a1a0a"))return{format:"png",width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function vt(t){if(!(t.length<10||t.toString("ascii",0,6)!=="GIF87a"&&t.toString("ascii",0,6)!=="GIF89a"))return{format:"gif",width:t.readUInt16LE(6),height:t.readUInt16LE(8)}}function Et(t){if(t.length<4||t[0]!==255||t[1]!==216)return;let e=2;for(;e+9<t.length;){if(t[e]!==255){e+=1;continue}const r=t[e+1],i=t.readUInt16BE(e+2);if(i<2)return;if(r>=192&&r<=207&&![196,200,204].includes(r)&&e+8<t.length)return{format:"jpeg",height:t.readUInt16BE(e+5),width:t.readUInt16BE(e+7)};e+=2+i}}function St(t){if(t.length<30||t.toString("ascii",0,4)!=="RIFF"||t.toString("ascii",8,12)!=="WEBP")return;const e=t.toString("ascii",12,16);if(e==="VP8X"&&t.length>=30)return{format:"webp",width:1+t.readUIntLE(24,3),height:1+t.readUIntLE(27,3)};if(e==="VP8 "&&t.length>=30)return{format:"webp",width:t.readUInt16LE(26)&16383,height:t.readUInt16LE(28)&16383};if(e==="VP8L"&&t.length>=25){const r=t.readUInt32LE(21);return{format:"webp",width:(r&16383)+1,height:(r>>14&16383)+1}}}async function K(t,e){if((await s.lstat(t)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${t}`);const i=await s.stat(t);if(!e||!i.isDirectory())return[t];const o=await s.readdir(t),a=await Promise.all(o.map(c=>{const n=d.join(t,c);return K(n,!0)}));return[t,...a.flat()]}async function Rt(t){if((await s.lstat(t)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${t}`);await F(t)}async function Dt(t){const e=await s.open(t,"r");try{const r=Buffer.alloc(512),{bytesRead:i}=await e.read(r,0,r.length,0),o=r.subarray(0,i);return xt(o)||vt(o)||Et(o)||St(o)}finally{await e.close()}}async function z({workspacePath:t,targetPath:e},r={}){const{requestedRoot:i,workspaceRoot:o}=await v(t),a=String(e||"").trim();if(!a)throw new Error("targetPath is required");const c=L(i,o,a),n=await F(c);if(!r.allowWorkspaceRoot&&n===o)throw new Error("Workspace root cannot be modified");return{workspaceRoot:o,resolvedTargetPath:n}}async function X(t){const{requestedRoot:e,workspaceRoot:r}=await v(t.workspacePath),i=String(t.targetPath||"").trim();if(!i)return{workspaceRoot:r,resolvedTargetPath:r};const o=L(e,r,i);return await et(o)?{workspaceRoot:r,resolvedTargetPath:await s.realpath(o)}:(await s.realpath(d.dirname(o)),{workspaceRoot:r,resolvedTargetPath:o})}function V(t){const e=t.toLowerCase();return e.endsWith(".zip")?"zip":e.endsWith(".tar.gz")||e.endsWith(".tgz")?"tgz":e.endsWith(".tar")?"tar":e.endsWith(".gz")?"gz":null}function Y(t){return t.replace(/\.tar\.gz$/i,"").replace(/\.tgz$/i,"").replace(/\.zip$/i,"").replace(/\.tar$/i,"").replace(/\.gz$/i,"")}function Ft(t,e){const r=$(Y(e)||"extracted");return d.join(t,r)}function M(t,e){const r=String(e||"").replace(/\\/g,"/"),i=r.split("/").filter(Boolean);if(!r||r.startsWith("/")||/^[A-Za-z]:/.test(r))throw new Error(`Archive entry path is invalid: ${e}`);if(r.length>x.maxPathLength||i.length>x.maxPathDepth)throw new Error(`Archive entry path is too deep or too long: ${e}`);return C(t,d.join(t,r))}function Tt(t){const e=t.props?.pathBuffer;if(!e||t.props?.flags?.isUnicode)return t.path;const r=mt.decode(e);return!r||r.includes("\uFFFD")?t.path:r}async function J(t){const e=t.autodrain?.();e&&await new Promise((r,i)=>{e.on("close",r),e.on("end",r),e.on("finish",r),e.on("error",i)})}function N(t){if(t.entries+=1,t.entries>x.maxEntries)throw new Error(`Archive contains too many entries. Limit: ${x.maxEntries}`)}function Q(t,e){if(t.totalBytes+=e,e>x.maxFileBytes)throw new Error(`Archive entry exceeds max size. Limit: ${x.maxFileBytes} bytes`);if(t.totalBytes>x.maxTotalBytes)throw new Error(`Archive extraction exceeds total size. Limit: ${x.maxTotalBytes} bytes`)}async function tt(t,e,r){let i=0;t.on("data",o=>{const a=Buffer.isBuffer(o)?o.length:Buffer.byteLength(o);i+=a,(i>x.maxFileBytes||r.totalBytes+i>x.maxTotalBytes)&&t instanceof ht&&t.destroy(new Error("Archive extraction size limit exceeded"))}),await ut(t,O(e)),Q(r,i)}async function et(t){try{return await s.access(t),!0}catch(e){if((typeof e=="object"&&e!==null?Reflect.get(e,"code"):void 0)==="ENOENT")return!1;throw e}}async function W({workspacePath:t,filePath:e}){const{requestedRoot:r,workspaceRoot:i}=await v(t),o=String(e||"").trim();if(!o)throw new Error("filePath is required");return{workspaceRoot:i,resolvedFilePath:L(r,i,o)}}async function Gt(t){const{workspaceRoot:e}=await v(t.workspacePath),r=String(t.targetPath||"").trim();if(!r){const{resolvedTargetPath:l}=await b(t);return rt(e,l)}const i=d.resolve(r);let o;try{o=await s.realpath(i)}catch{throw new Error(`Path not found: ${i}`)}if(!(await s.stat(o)).isDirectory())throw new Error(`Path is not a directory: ${i}`);const c=d.relative(e,o),n=c===""||!c.startsWith("..")&&!d.isAbsolute(c);return rt(e,n?o:i)}async function rt(t,e){if(!(await s.stat(e)).isDirectory())throw new Error(`Path is not a directory: ${e}`);const i=await s.readdir(e,{withFileTypes:!0}),o=await Promise.all(i.map(async a=>{const c=d.join(e,a.name),n=a.isFile()?(await s.stat(c)).size:void 0;return{name:a.name,path:u(c),isDirectory:a.isDirectory(),size:n}}));return{currentPath:u(e),workspacePath:u(t),items:o}}async function Ht(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await F(r),o=await s.stat(i);if(!o.isFile())throw new Error(`Path is not a file: ${i}`);const a=await s.readFile(i,"utf-8");return{workspacePath:u(e),filePath:u(i),content:a,mtimeMs:o.mtimeMs}}const it={maxEntries:1e5,maxDepth:32};async function zt(t){let e=0,r=0;async function i(o,a){if(a>=it.maxDepth)return;let c;try{c=await s.readdir(o,{withFileTypes:!0})}catch{return}for(const n of c){if(r+=1,r>it.maxEntries)return;if(n.isSymbolicLink())continue;const l=d.join(o,n.name);try{if(n.isDirectory())await i(l,a+1);else if(n.isFile()){const h=await s.stat(l);e+=h.size}}catch{}}}return await i(t,0),e}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t,{allowWorkspaceRoot:!0}),[i,o]=await Promise.all([s.stat(r),s.lstat(r)]),a=i.isDirectory(),c=i.isFile()?await Dt(r):void 0,n=a?await zt(r):i.size;return{workspacePath:u(e),targetPath:u(r),name:d.basename(r),isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:o.isSymbolicLink(),size:n,mode:i.mode,modeOctal:H(i.mode),permissions:kt(i.mode),uid:i.uid,gid:i.gid,atimeMs:i.atimeMs,mtimeMs:i.mtimeMs,ctimeMs:i.ctimeMs,birthtimeMs:i.birthtimeMs,...c?{image:c}:{}}}async function Xt(t){const e=Pt(t.mode),r=!!t.recursive,{workspaceRoot:i,resolvedTargetPath:o}=await z(t),a=await K(o,r);for(const c of a)await Rt(c),await s.chmod(c,e);return{ok:!0,workspacePath:u(i),targetPath:u(o),mode:e,modeOctal:H(e),recursive:r,changedCount:a.length}}async function Vt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await F(r),o=await s.stat(i);if(!o.isFile())throw new Error(`Path is not a file: ${i}`);const a=d.extname(i).toLowerCase();if(a===".doc")return{workspacePath:u(e),filePath:u(i),kind:"unsupported",content:"",message:"\u6682\u4E0D\u652F\u6301\u65E7\u7248 .doc \u683C\u5F0F\u9884\u89C8\uFF0C\u8BF7\u53E6\u5B58\u4E3A .docx \u540E\u518D\u6253\u5F00\u3002",size:o.size,mtimeMs:o.mtimeMs};if(a!==".docx")throw new Error("Document preview only supports .docx files");return{workspacePath:u(e),filePath:u(i),kind:"html",content:"",size:o.size,mtimeMs:o.mtimeMs}}async function Yt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t);await s.mkdir(d.dirname(r),{recursive:!0}),await T(r),await s.writeFile(r,String(t.content??""),"utf-8");const i=await s.stat(r);return{ok:!0,workspacePath:u(e),filePath:u(r),mtimeMs:i.mtimeMs}}async function Jt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await b(t),i=$(t.entryName),o=d.join(r,i);if(!(await s.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);try{throw await s.access(o),new Error(`Path already exists: ${o}`)}catch(c){if((typeof c=="object"&&c!==null?Reflect.get(c,"code"):void 0)!=="ENOENT")throw c}return t.isDirectory?await s.mkdir(o,{recursive:!1}):(await s.mkdir(d.dirname(o),{recursive:!0}),await T(o),await s.writeFile(o,"","utf-8")),{ok:!0,workspacePath:u(e),targetPath:u(o),isDirectory:t.isDirectory}}async function Qt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=$(t.newName),o=d.join(d.dirname(r),i);if(o===r)return{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(o)};await s.stat(r);try{throw await s.access(o),new Error(`Path already exists: ${o}`)}catch(a){if((typeof a=="object"&&a!==null?Reflect.get(a,"code"):void 0)!=="ENOENT")throw a}return await s.rename(r,o),{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(o)}}async function te(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=await b({workspacePath:t.workspacePath,targetPath:t.destinationPath});if(!(await s.stat(i.resolvedTargetPath)).isDirectory())throw new Error(`Destination is not a directory: ${i.resolvedTargetPath}`);if((await s.stat(r)).isDirectory()){const n=d.relative(r,i.resolvedTargetPath);if(n===""||!n.startsWith("..")&&!d.isAbsolute(n))throw new Error("Cannot move a directory into itself or its descendant")}const c=d.join(i.resolvedTargetPath,d.basename(r));if(c===r)return{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(c),destinationPath:u(i.resolvedTargetPath)};try{throw await s.access(c),new Error(`Path already exists: ${c}`)}catch(n){if((typeof n=="object"&&n!==null?Reflect.get(n,"code"):void 0)!=="ENOENT")throw n}return await s.rename(r,c),{ok:!0,workspacePath:u(e),sourcePath:u(r),targetPath:u(c),destinationPath:u(i.resolvedTargetPath)}}async function ee(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=await s.stat(r),o=d.parse(r).root;if(r===o)throw new Error("Filesystem root cannot be deleted");return i.isDirectory()?await s.rm(r,{recursive:!0,force:!1}):await s.unlink(r),{ok:!0,workspacePath:u(e),targetPath:u(r)}}async function re(t){if(!Array.isArray(t.files)||t.files.length===0)throw new Error("files are required");const{workspaceRoot:e,resolvedTargetPath:r}=await X(t);if(!(await s.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const o=[];for(const a of t.files){const c=B(a.originalname),n=gt(a.relativePath,a.originalname),l=d.join(r,n);await s.mkdir(d.dirname(l),{recursive:!0}),await T(l),await s.copyFile(a.path,l);let h;t.extractArchives&&V(l)&&(h=await Wt({workspacePath:e,archivePath:l,outputPath:Ft(r,c)})),o.push({fileName:c,targetPath:u(l),size:a.size,extracted:h})}return{ok:!0,workspacePath:u(e),targetPath:u(r),files:o}}async function ie(t){const{workspaceRoot:e,resolvedTargetPath:r}=await z(t),i=await s.stat(r),o=d.basename(r);return{workspacePath:u(e),targetPath:u(r),resolvedTargetPath:r,fileName:i.isDirectory()?`${o||"workspace"}.zip`:o,contentType:i.isDirectory()?"application/zip":Lt(r),isDirectory:i.isDirectory(),size:i.isDirectory()?void 0:i.size}}function Lt(t){const e=d.extname(t).toLowerCase();return{".svg":"image/svg+xml",".svgz":"image/svg+xml",".ico":"image/x-icon",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".bmp":"image/bmp",".avif":"image/avif",".txt":"text/plain; charset=utf-8",".json":"application/json; charset=utf-8",".xml":"application/xml; charset=utf-8",".html":"text/html; charset=utf-8",".htm":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".md":"text/markdown; charset=utf-8",".csv":"text/csv; charset=utf-8",".pdf":"application/pdf",".zip":"application/zip",".mp3":"audio/mpeg",".wav":"audio/wav",".mp4":"video/mp4",".webm":"video/webm"}[e]||"application/octet-stream"}async function oe(t,e){const r=new _({zlib:{level:9}}),i=new Promise((o,a)=>{e.on("close",o),e.on("finish",o),r.on("error",a),e.on("error",a)});r.pipe(e),r.directory(t,!1),await r.finalize(),await i}async function ae(t,e,r,i){const o=d.join(lt.tmpdir(),"agentswork-bridge-downloads");await s.mkdir(o,{recursive:!0});const a=(e||"workspace.zip").replace(/[^\w.-]/g,"_").replace(/\.zip$/i,"")||"workspace",c=d.join(o,`${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.zip`),n=new _({zlib:{level:9}}),l=()=>n.pointer(),h=()=>n.abort(),w=O(c),f=new Promise((m,g)=>{w.on("close",m),w.on("finish",m),n.on("error",g),w.on("error",g)});f.catch(()=>{}),n.pipe(w);const p=setInterval(()=>{try{if(i?.()){h();return}r?.(l())}catch{}},200);try{return n.on("progress",()=>{i?.()&&h()}),n.directory(t,!1),i?.()&&h(),await n.finalize(),await f,r?.(l()),c}catch(m){throw await s.rm(c,{force:!0}).catch(()=>{}),m}finally{clearInterval(p)}}async function $t(t,e,r){let i=0,o;const a=U(t),c=a.pipe(pt.Parse({forceStream:!0}));try{for await(const n of c){if(o){await J(n);continue}const l=Tt(n);try{const h=M(e,l);if(N(r),n.type==="Directory"){await s.mkdir(h,{recursive:!0});continue}if(n.type!=="File")throw new Error(`Unsupported archive entry type: ${l}`);await s.mkdir(d.dirname(h),{recursive:!0}),await T(h),await tt(n,h,r),i+=1}catch(h){o=h,await J(n)}}}finally{a.destroy(),c.destroy()}if(o)throw o;return i}async function At(t,e,r,i){let o=0,a;const c=(n,l)=>{if(a)return!1;try{M(e,n),N(i);const h=String(l.type||"").toLowerCase();if(h==="symboliclink"||h==="link")throw new Error(`Archive links are not supported: ${n}`);if(h!=="file"&&h!=="oldfile"&&h!=="contiguousfile"&&h!=="directory")throw new Error(`Unsupported archive entry type: ${n}`);return typeof l.size=="number"&&Q(i,l.size),h!=="directory"&&(o+=1),!0}catch(h){return a=h,!1}};if(await ft.x({file:t,cwd:e,gzip:r,strict:!0,filter:(n,l)=>c(n,l)}),a)throw a;return o}async function Ct(t,e,r){const i=Y(d.basename(t)),o=M(e,i||"decompressed");return N(r),await s.mkdir(d.dirname(o),{recursive:!0}),await T(o),await tt(U(t).pipe(wt()),o,r),1}async function Wt(t){const{requestedRoot:e,workspaceRoot:r}=await v(t.workspacePath),{resolvedTargetPath:i}=await X({workspacePath:t.workspacePath,targetPath:t.outputPath||""}),o=L(e,r,String(t.archivePath||"").trim()),a=await F(o);if(!(await s.stat(a)).isFile())throw new Error(`Archive path is not a file: ${a}`);if(await et(i)||await s.mkdir(i,{recursive:!0}),!(await s.stat(i)).isDirectory())throw new Error(`Output path is not a directory: ${i}`);const l=V(a);if(!l)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let h=0;const w={entries:0,totalBytes:0};return l==="zip"?h=await $t(a,i,w):l==="tar"||l==="tgz"?h=await At(a,i,l==="tgz",w):h=await Ct(a,i,w),{ok:!0,workspacePath:u(r),archivePath:u(a),targetPath:u(i),extractedEntries:h}}function ot(t){const e=t.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!e)return[t];const[,r,i,o]=e;return i.split(",").flatMap(a=>ot(r+a.trim()+o))}function at(t,e){const r=String(e||"").trim();if(!r||r==="*"||r==="*.*")return!0;const i=t.toLowerCase();return ot(r.toLowerCase()).some(a=>{if(a==="*")return!0;const c=a.replace(/\./g,"\\.").replace(/\*\*/g,"<<GLOBSTAR>>").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]").replace(/<<GLOBSTAR>>/g,".*");return new RegExp(`^${c}$`).test(i)})}function j(t){return t===".git"||t==="node_modules"||t===".svn"||t===".hg"}const nt=10*1024*1024,bt=1e3;async function ne(t){const{workspaceRoot:e}=await v(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=!!t.ignoreCase,o=String(t.fileMask||"").trim(),a=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:bt,c=i?r.toLowerCase():r,l=(t.mode==="filename"?"filename":"content")==="filename",h=[];let w=0,f=!1;async function p(m){if(f)return;let g;try{g=await s.readdir(m,{withFileTypes:!0})}catch{return}for(const y of g){if(f)return;const P=C(e,d.join(m,y.name));if(y.isSymbolicLink())continue;if(y.isDirectory()){if(j(y.name))continue;await p(P);continue}if(!y.isFile()||o&&!at(y.name,o))continue;w+=1;let E;try{E=await s.stat(P)}catch{continue}if(E.size>nt)continue;if(l){const R=(i?y.name.toLowerCase():y.name).indexOf(c);if(R!==-1&&(h.push({filePath:u(P),lineNumber:0,lineContent:y.name,matchStart:R,matchEnd:R+r.length}),h.length>=a)){f=!0;return}continue}let k;try{k=await s.readFile(P,"utf-8")}catch{continue}const D=k.split(/\r?\n/);for(let S=0;S<D.length;S+=1){const R=D[S],ct=i?R.toLowerCase():R;let q=0;for(;;){const A=ct.indexOf(c,q);if(A===-1)break;if(h.push({filePath:u(P),lineNumber:S+1,lineContent:R,matchStart:A,matchEnd:A+r.length}),h.length>=a){f=!0;return}q=A+1}}}}return await p(e),{workspacePath:u(e),keyword:r,totalFiles:w,totalMatches:h.length,matches:h,truncated:f}}const Bt=100,It=120;async function se(t){const{requestedRoot:e,workspaceRoot:r}=await v(t.workspacePath),i=String(t.keyword||"").trim();if(!i)throw new Error("keyword is required");const o=!!t.ignoreCase,a=o?i.toLowerCase():i,c=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:Bt,n=typeof t.maxDirectories=="number"&&t.maxDirectories>0?t.maxDirectories:It,l=String(t.rootPath||"").trim(),h=l?d.resolve(/^[A-Za-z]:$/.test(l)?`${l}/`:l):r;let w;try{w=await s.realpath(h)}catch{throw new Error(`Path not found: ${h}`)}if(!(await s.stat(w)).isDirectory())throw new Error(`Path is not a directory: ${h}`);const p=[],m=[w];let g=0,y=!1;for(;m.length>0&&p.length<c&&g<n;){const P=m.shift();g+=1;let E;try{E=await s.readdir(P,{withFileTypes:!0})}catch{continue}for(const k of E){if(p.length>=c){y=!0;break}if(k.isSymbolicLink())continue;const D=d.join(P,k.name);if(k.isDirectory()&&j(k.name))continue;const S=d.relative(w,D).replace(/\\/g,"/");(o?S.toLowerCase():S).includes(a)&&p.push({name:k.name,path:u(D),isDirectory:k.isDirectory()}),k.isDirectory()&&g<n&&m.push(D)}}return(g>=n||p.length>=c)&&(y=!0),p.sort((P,E)=>P.isDirectory===E.isDirectory?P.name.localeCompare(E.name):P.isDirectory?-1:1),{workspacePath:u(r),rootPath:u(w),keyword:i,items:p,truncated:y}}async function st(t,e,r,i){let o;try{o=await s.readFile(t,"utf-8")}catch{return{changed:!1,count:0}}const a=o;let c=0;if(i){const n=e.toLowerCase();let l=0;for(;;){const w=o.toLowerCase().indexOf(n,l);if(w===-1)break;o=o.slice(0,w)+r+o.slice(w+e.length),c+=1;const f=r.length||1;l=w+f}}else{let n=0;for(;;){const l=o.indexOf(e,n);if(l===-1)break;o=o.slice(0,l)+r+o.slice(l+e.length),c+=1;const h=r.length||1;n=l+h}}return o===a?{changed:!1,count:0}:(await T(t),await s.writeFile(t,o,"utf-8"),{changed:!0,count:c})}async function ce(t){const{workspaceRoot:e}=await v(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=String(t.replacement??""),o=!!t.ignoreCase,a=String(t.fileMask||"").trim(),c=String(t.filePath||"").trim();let n=0,l=0;if(c){const{resolvedFilePath:w}=await W({workspacePath:t.workspacePath,filePath:c}),f=await st(w,r,i,o);return f.changed&&(n=1,l=f.count),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:l}}async function h(w){let f;try{f=await s.readdir(w,{withFileTypes:!0})}catch{return}for(const p of f){const m=C(e,d.join(w,p.name));if(p.isSymbolicLink())continue;if(p.isDirectory()){if(j(p.name))continue;await h(m);continue}if(!p.isFile()||a&&!at(p.name,a))continue;try{if((await s.stat(m)).size>nt)continue}catch{continue}const g=await st(m,r,i,o);g.changed&&(n+=1,l+=g.count)}}return await h(e),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:l}}export{Xt as chmodWorkspaceEntry,Jt as createWorkspaceEntry,ee as deleteWorkspaceEntry,Wt as extractWorkspaceArchive,Kt as getWorkspaceEntryProperties,Gt as listWorkspaceDirectory,te as moveWorkspaceEntry,Vt as previewWorkspaceDocument,Ht as readWorkspaceFile,Qt as renameWorkspaceEntry,ce as replaceWorkspaceFiles,ie as resolveWorkspaceDownloadTarget,ne as searchWorkspaceFiles,se as searchWorkspacePaths,oe as streamWorkspaceDirectoryZip,re as uploadWorkspaceFiles,Yt as writeWorkspaceFile,ae as zipWorkspaceDirectoryToTemp};
2
2
 
@@ -36,6 +36,8 @@ export declare function readBeforeContentFromCache(workspacePath: string, cacheK
36
36
 
37
37
  export declare function readDiffFromCache(workspacePath: string, cacheKey: string): Promise<string | null>;
38
38
 
39
+ export declare function sweepExpiredChangeCache(): Promise<void>;
40
+
39
41
  export declare function getPendingChangeCountForTest(): number;
40
42
 
41
43
  export declare function isWatching(): boolean;
@@ -1,4 +1,4 @@
1
- import{promises as u}from"node:fs";import h from"node:path";import{createHash as $}from"node:crypto";import L from"axios";import*as S from"diff";import{createLogger as _}from"../utils/logger.js";import{resolveSchedulerBaseUrl as N}from"../config.js";import{getRuntimeAccessToken as D}from"./runtime-binding.js";const r=_("workspace-task-change-watcher"),H=new Set([".git","node_modules","__pycache__",".cache",".next","target","dist",".idea",".vscode",".agentswork-snapshots",".aws-bridge"]),R=new Set([".lock",".log",".pid",".swp",".swo",".tmp"]),b=500,U=10*1024*1024,W=".agentswork/change-cache";let f=null,E=!1,w="",p=null;const d=new Map,l=new Map;let C=!1;function G(e){f&&f.taskNodeId!==e.taskNodeId&&A().catch(()=>{}),f=e,r.info(`Task context set: agent=${e.agentId}, taskInstance=${e.taskInstanceId}, node=${e.taskNodeId}`)}function J(){f&&(r.info(`Task context cleared: agent=${f.agentId}`),A().catch(()=>{})),f=null}function Z(){return f}function q(){return f!==null}async function Q(e){const t=h.resolve(e.trim());try{if(!(await u.stat(t)).isDirectory())throw new Error(`Not a directory: ${t}`)}catch{throw new Error(`Workspace path does not exist or inaccessible: ${t}`)}if(p){try{p.close()}catch{}p=null}w=t,E=!0,r.info(`Workspace path set (fs.watch disabled, using tool interception only): ${w}`)}async function V(){if(E=!1,p){try{p.close()}catch{}p=null}d.clear(),l.clear(),w="",r.info("Workspace watch stopped")}function x(e,t,n){return $("sha256").update(`${e||"no-task"}:${t}:${n}`,"utf-8").digest("hex").slice(0,32)}function k(e){return h.join(e,W)}async function y(e,t,n){if(n===null)return;const o=k(e);await u.mkdir(o,{recursive:!0});const a=h.join(o,`${t}.before`);await u.writeFile(a,n,"utf-8"),r.debug(`Saved beforeContent cache: ${a}`)}async function P(e,t,n,o){const a=S.createPatch("file",n,o),s=k(e);await u.mkdir(s,{recursive:!0});const i=h.join(s,`${t}.diff`);await u.writeFile(i,a,"utf-8"),r.debug(`Saved diff cache: ${i}`)}async function Y(e,t){try{const n=h.join(k(e),`${t}.before`);return await u.readFile(n,"utf-8")}catch{return null}}async function ee(e,t){try{const n=h.join(k(e),`${t}.diff`);return await u.readFile(n,"utf-8")}catch{return null}}function te(e){const t=e.split(/[/\\]/);for(const o of t.slice(0,-1))if(H.has(o))return!0;const n=h.extname(t[t.length-1]).toLowerCase();return!!R.has(n)}async function T(e){try{return(await u.stat(e)).size>U?null:await u.readFile(e,"utf-8")}catch{return null}}function m(e){return e===null?null:$("sha256").update(e,"utf-8").digest("hex").slice(0,16)}async function ne(e,t){}function ae(e,t,n){const o=d.get(n);if(o)t==="DELETE"||o.operation==="DELETE"?o.operation="DELETE":o.operation==="CREATE"&&t==="UPDATE"||(o.operation=t),o.timer&&clearTimeout(o.timer),o.timer=setTimeout(()=>{I(n).catch(()=>{})},b);else{let a=null;if(t==="UPDATE"||t==="DELETE")if(l.has(n))a=l.get(n)??null;else{T(n).then(i=>{i!==void 0&&l.set(n,i)}).catch(()=>{});return}const s={filePath:n,operation:t,timer:null,beforeContent:a};s.timer=setTimeout(()=>{I(n).catch(()=>{})},b),d.set(n,s)}}async function I(e){const t=d.get(e);if(!t)return;d.delete(e),t.timer&&clearTimeout(t.timer);const n=f;if(!n){if(t.operation==="DELETE")l.delete(e);else{const g=await T(e);g!==null&&l.set(e,g)}return}const o=h.relative(n.workspacePath,e).replace(/\\/g,"/");if(o.startsWith(".."))return;let a=t.beforeContent,s=null;t.operation==="DELETE"?(a=a??l.get(e)??null,s=null):(s=await T(e),t.operation==="UPDATE"&&a===null&&(a=l.get(e)??null)),t.operation==="DELETE"?l.delete(e):l.set(e,s);const i=new Date().toISOString(),c=x(n.taskInstanceId,o,i);t.operation==="UPDATE"&&a!==null&&s!==null?await P(n.workspacePath,c,a,s):t.operation==="DELETE"&&await y(n.workspacePath,c,a),await v(n,{workspacePath:n.workspacePath,filePath:o,operation:t.operation,beforeContent:a,afterContent:s,beforeHash:m(a),afterHash:m(s),timestamp:i,reason:F(t.operation,a,s)},c)}async function v(e,t,n){try{const o=N().url,a=D(o)||D();if(!a){r.warn("Cannot send change record: no auth token");return}const s=JSON.stringify({toolRecorded:!0,cacheKey:n,timestamp:t.timestamp}),c=(await L.post(`${o}/api/runtime/callback/task-file-changed`,{agentId:e.agentId,taskInstanceId:e.taskInstanceId,taskNodeId:e.taskNodeId,tenantId:e.tenantId,operation:t.operation,targetType:"FILE",targetPath:t.filePath,beforeHash:t.beforeHash,afterHash:t.afterHash,metadata:s,reason:t.reason,workspacePath:e.workspacePath},{headers:{"X-Runtime-Token":a},timeout:1e4})).data;c?.ignored===!0?r.warn(`Change record ignored by server: ${t.filePath} (${t.operation}), reason: ${String(c.reason??"unknown")}`):r.info(`Change record sent: ${t.filePath} (${t.operation})`)}catch(o){r.warn(`Failed to send change record: ${o.message}`)}}async function A(){if(C)return;C=!0;const e=Array.from(d.keys());for(const t of e)try{await I(t)}catch{}C=!1}function oe(){return d.size}function se(){return E&&p!==null}function ie(){return w}function F(e,t,n){const o=t?t.split(`
2
- `).length:0,a=n?n.split(`
3
- `).length:0;if(e==="CREATE")return`\u521B\u5EFA\u6587\u4EF6\uFF0C\u5171 ${a} \u884C`;if(e==="DELETE")return`\u5220\u9664\u6587\u4EF6\uFF0C\u539F ${o} \u884C`;if(o===a)return`\u4FEE\u6539\u6587\u4EF6\uFF08\u884C\u6570\u4E0D\u53D8\uFF0C${a} \u884C\uFF09`;const s=a-o;return`\u4FEE\u6539\u6587\u4EF6\uFF1A${o} \u884C -> ${a} \u884C\uFF08${s>0?"+":""}${s} \u884C\uFF09`}async function re(e){const t=f,n=e.agentId||t?.agentId,o=e.workspacePath||t?.workspacePath;if(!n||!o){r.warn(`Agent file change skipped (no identity): ${e.filePath} (${e.operation})`);return}r.info(`Recording agent file change: ${e.filePath} (${e.operation}), taskNode=${t?.taskNodeId??"(no task)"}`);const a=t??{agentId:n,taskInstanceId:"",taskNodeId:"",tenantId:"",workspacePath:o,projectId:""},s=h.join(a.workspacePath,e.filePath),i=new Date().toISOString(),c=x(a.taskInstanceId,e.filePath,i);e.operation==="UPDATE"&&e.beforeContent!==null&&e.afterContent!==null?(await P(a.workspacePath,c,e.beforeContent,e.afterContent),await y(a.workspacePath,c,e.beforeContent)):e.operation==="DELETE"&&await y(a.workspacePath,c,e.beforeContent);const g=e.reason?.trim()||F(e.operation,e.beforeContent,e.afterContent);await v(a,{workspacePath:a.workspacePath,filePath:e.filePath,operation:e.operation,beforeContent:e.beforeContent,afterContent:e.afterContent,beforeHash:m(e.beforeContent),afterHash:m(e.afterContent),timestamp:i,reason:g},c)}export{J as clearTaskContext,oe as getPendingChangeCountForTest,Z as getTaskContext,ie as getWorkspaceRoot,q as hasActiveTask,se as isWatching,Y as readBeforeContentFromCache,ee as readDiffFromCache,re as recordAgentFileChange,G as setTaskContext,Q as startWatching,V as stopWatching};
1
+ import{promises as f}from"node:fs";import r from"node:path";import{createHash as T}from"node:crypto";import J from"axios";import*as V from"diff";import{createLogger as Z}from"../utils/logger.js";import{resolveSchedulerBaseUrl as q,getRuntimeHomeDir as C}from"../config.js";import{getChangeCacheRetentionDays as I,setChangeCacheRetentionDays as Q}from"./change-cache-retention.js";import{getRuntimeAccessToken as H}from"./runtime-binding.js";const h=Z("workspace-task-change-watcher"),Y=new Set([".git","node_modules","__pycache__",".cache",".next","target","dist",".idea",".vscode",".agentswork-snapshots",".aws-bridge"]),ee=new Set([".lock",".log",".pid",".swp",".swo",".tmp"]),F=500,te=10*1024*1024,ne=".agentswork/change-cache",m="change-cache",ae=60*60*1e3,ie=24*60*60*1e3;let l=null,$=!1,E="",p=null;const g=new Map,u=new Map;let b=!1;function we(e){l&&l.taskNodeId!==e.taskNodeId&&z().catch(()=>{}),l=e,h.info(`Task context set: agent=${e.agentId}, taskInstance=${e.taskInstanceId}, node=${e.taskNodeId}`)}function Ce(){l&&(h.info(`Task context cleared: agent=${l.agentId}`),z().catch(()=>{})),l=null}function me(){return l}function Ee(){return l!==null}async function ke(e){const t=r.resolve(e.trim());try{if(!(await f.stat(t)).isDirectory())throw new Error(`Not a directory: ${t}`)}catch{throw new Error(`Workspace path does not exist or inaccessible: ${t}`)}if(p){try{p.close()}catch{}p=null}E=t,$=!0,h.info(`Workspace path set (fs.watch disabled, using tool interception only): ${E}`)}async function ye(){if($=!1,p){try{p.close()}catch{}p=null}g.clear(),u.clear(),E="",h.info("Workspace watch stopped")}function N(e,t,n){return T("sha256").update(`${e||"no-task"}:${t}:${n}`,"utf-8").digest("hex").slice(0,32)}function W(e){return T("sha256").update(r.resolve(e),"utf-8").digest("hex").slice(0,16)}function k(e){return r.join(C(),".aws-bridge",m,W(e))}function oe(e){return r.join(C(),m,W(e))}function j(e){return r.join(e,ne)}const U=new Set;async function M(e){if(!U.has(e)){U.add(e);try{const t=j(e),n=k(e);await f.access(t),await f.mkdir(r.dirname(n),{recursive:!0}),await f.rename(t,n),h.info(`Migrated legacy change-cache: ${t} -> ${n}`)}catch{}}}async function x(e,t,n){if(n===null)return;await M(e);const a=k(e);await f.mkdir(a,{recursive:!0});const i=r.join(a,`${t}.before`);await f.writeFile(i,n,"utf-8"),X(),h.debug(`Saved beforeContent cache: ${i}`)}async function B(e,t,n,a){const i=V.createPatch("file",n,a);await M(e);const o=k(e);await f.mkdir(o,{recursive:!0});const c=r.join(o,`${t}.diff`);await f.writeFile(c,i,"utf-8"),X(),h.debug(`Saved diff cache: ${c}`)}async function O(e,t){const n=[r.join(k(e),t),r.join(oe(e),t),r.join(j(e),t)];for(const a of n)try{return await f.readFile(a,"utf-8")}catch{}return null}async function De(e,t){return O(e,`${t}.before`)}async function Te(e,t){return O(e,`${t}.diff`)}let v=0,A=null,L=Promise.resolve();function S(){return L=L.then(()=>se()),L}async function se(){const e=I();if(!(e===null||e<=0))try{const t=[r.join(C(),".aws-bridge",m),r.join(C(),m)],n=Date.now()-e*24*60*60*1e3;let a=0;for(const i of t){let o=[];try{o=(await f.readdir(i)).filter(c=>!c.startsWith("."))}catch{continue}for(const c of o){const s=r.join(i,c);try{const d=await f.readdir(s);let w=!0;for(const D of d){if(!D.endsWith(".diff")&&!D.endsWith(".before")){w=!1;continue}const P=r.join(s,D);try{(await f.stat(P)).mtimeMs<n?(await f.unlink(P),a++):w=!1}catch{w=!1}}w&&await f.rmdir(s).catch(()=>{})}catch{}}}a>0&&h.info(`Change-cache TTL sweep: removed ${a} expired file(s), retentionDays=${e}`)}catch(t){h.warn(`Change-cache TTL sweep failed: ${t.message}`)}}function X(){const e=Date.now();e-v>=ae&&(v=e,S().catch(()=>{})),A||(A=setInterval(()=>{S().catch(()=>{})},ie),A.unref?.())}function Ie(e){const t=e.split(/[/\\]/);for(const a of t.slice(0,-1))if(Y.has(a))return!0;const n=r.extname(t[t.length-1]).toLowerCase();return!!ee.has(n)}async function _(e){try{return(await f.stat(e)).size>te?null:await f.readFile(e,"utf-8")}catch{return null}}function y(e){return e===null?null:T("sha256").update(e,"utf-8").digest("hex").slice(0,16)}async function $e(e,t){}function be(e,t,n){const a=g.get(n);if(a)t==="DELETE"||a.operation==="DELETE"?a.operation="DELETE":a.operation==="CREATE"&&t==="UPDATE"||(a.operation=t),a.timer&&clearTimeout(a.timer),a.timer=setTimeout(()=>{R(n).catch(()=>{})},F);else{let i=null;if(t==="UPDATE"||t==="DELETE")if(u.has(n))i=u.get(n)??null;else{_(n).then(c=>{c!==void 0&&u.set(n,c)}).catch(()=>{});return}const o={filePath:n,operation:t,timer:null,beforeContent:i};o.timer=setTimeout(()=>{R(n).catch(()=>{})},F),g.set(n,o)}}async function R(e){const t=g.get(e);if(!t)return;g.delete(e),t.timer&&clearTimeout(t.timer);const n=l;if(!n){if(t.operation==="DELETE")u.delete(e);else{const d=await _(e);d!==null&&u.set(e,d)}return}const a=r.relative(n.workspacePath,e).replace(/\\/g,"/");if(a.startsWith(".."))return;let i=t.beforeContent,o=null;t.operation==="DELETE"?(i=i??u.get(e)??null,o=null):(o=await _(e),t.operation==="UPDATE"&&i===null&&(i=u.get(e)??null)),t.operation==="DELETE"?u.delete(e):u.set(e,o);const c=new Date().toISOString(),s=N(n.taskInstanceId,a,c);t.operation==="UPDATE"&&i!==null&&o!==null?await B(n.workspacePath,s,i,o):t.operation==="DELETE"&&await x(n.workspacePath,s,i),await K(n,{workspacePath:n.workspacePath,filePath:a,operation:t.operation,beforeContent:i,afterContent:o,beforeHash:y(i),afterHash:y(o),timestamp:c,reason:G(t.operation,i,o)},s)}async function K(e,t,n){try{const a=q().url,i=H(a)||H();if(!i){h.warn("Cannot send change record: no auth token");return}const o=JSON.stringify({toolRecorded:!0,cacheKey:n,timestamp:t.timestamp}),s=(await J.post(`${a}/api/runtime/callback/task-file-changed`,{agentId:e.agentId,taskInstanceId:e.taskInstanceId,taskNodeId:e.taskNodeId,tenantId:e.tenantId,operation:t.operation,targetType:"FILE",targetPath:t.filePath,beforeHash:t.beforeHash,afterHash:t.afterHash,metadata:o,reason:t.reason,workspacePath:e.workspacePath},{headers:{"X-Runtime-Token":i},timeout:1e4})).data;if(s?.ignored===!0?h.warn(`Change record ignored by server: ${t.filePath} (${t.operation}), reason: ${String(s.reason??"unknown")}`):h.info(`Change record sent: ${t.filePath} (${t.operation})`),s&&s.changeCacheRetentionDays!==void 0){const d=I();Q(s.changeCacheRetentionDays),d!==I()&&(v=0,S().catch(()=>{}))}}catch(a){h.warn(`Failed to send change record: ${a.message}`)}}async function z(){if(b)return;b=!0;const e=Array.from(g.keys());for(const t of e)try{await R(t)}catch{}b=!1}function xe(){return g.size}function ve(){return $&&p!==null}function Ae(){return E}function G(e,t,n){const a=t?t.split(`
2
+ `).length:0,i=n?n.split(`
3
+ `).length:0;if(e==="CREATE")return`\u521B\u5EFA\u6587\u4EF6\uFF0C\u5171 ${i} \u884C`;if(e==="DELETE")return`\u5220\u9664\u6587\u4EF6\uFF0C\u539F ${a} \u884C`;if(a===i)return`\u4FEE\u6539\u6587\u4EF6\uFF08\u884C\u6570\u4E0D\u53D8\uFF0C${i} \u884C\uFF09`;const o=i-a;return`\u4FEE\u6539\u6587\u4EF6\uFF1A${a} \u884C -> ${i} \u884C\uFF08${o>0?"+":""}${o} \u884C\uFF09`}async function Le(e){const t=l,n=e.agentId||t?.agentId,a=e.workspacePath||t?.workspacePath;if(!n||!a){h.warn(`Agent file change skipped (no identity): ${e.filePath} (${e.operation})`);return}h.info(`Recording agent file change: ${e.filePath} (${e.operation}), taskNode=${t?.taskNodeId??"(no task)"}`);const i=t??{agentId:n,taskInstanceId:"",taskNodeId:"",tenantId:"",workspacePath:a,projectId:""},o=r.join(i.workspacePath,e.filePath),c=new Date().toISOString(),s=N(i.taskInstanceId,e.filePath,c);e.operation==="UPDATE"&&e.beforeContent!==null&&e.afterContent!==null?(await B(i.workspacePath,s,e.beforeContent,e.afterContent),await x(i.workspacePath,s,e.beforeContent)):e.operation==="DELETE"&&await x(i.workspacePath,s,e.beforeContent);const d=e.reason?.trim()||G(e.operation,e.beforeContent,e.afterContent);await K(i,{workspacePath:i.workspacePath,filePath:e.filePath,operation:e.operation,beforeContent:e.beforeContent,afterContent:e.afterContent,beforeHash:y(e.beforeContent),afterHash:y(e.afterContent),timestamp:c,reason:d},s)}export{Ce as clearTaskContext,xe as getPendingChangeCountForTest,me as getTaskContext,Ae as getWorkspaceRoot,Ee as hasActiveTask,ve as isWatching,De as readBeforeContentFromCache,Te as readDiffFromCache,Le as recordAgentFileChange,we as setTaskContext,ke as startWatching,ye as stopWatching,S as sweepExpiredChangeCache};
4
4
 
@@ -0,0 +1,3 @@
1
+
2
+
3
+ export declare function runExclusiveConfigWrite<T>(filePath: string, task: () => T | Promise<T>): Promise<T>;
@@ -0,0 +1,2 @@
1
+ const s=new Map;function o(e,t){const n=(s.get(e)??Promise.resolve()).then(()=>t());return s.set(e,n.then(()=>{},()=>{})),n}export{o as runExclusiveConfigWrite};
2
+
@@ -18,6 +18,8 @@ export interface ACodeBuiltInTool {
18
18
  exposedName: string;
19
19
  description: string;
20
20
  inputSchema: JsonObject;
21
+
22
+ category?: string;
21
23
  execute(args: JsonObject, context?: ACodeBuiltInToolExecutionContext): Promise<unknown>;
22
24
  }
23
25
  export interface ACodeBuiltInToolExecutionContext {
@@ -30,6 +30,12 @@ export declare class ACodeRuntime extends EventEmitter {
30
30
 
31
31
  private readonly memoryDelegateCooldownUntil;
32
32
  private submitQueue;
33
+
34
+ private activeTurnToken;
35
+
36
+ private readonly forceSettleWaiters;
37
+
38
+ private abortForceSettleTimer;
33
39
  private activeTurn;
34
40
 
35
41
  private forceMessagePending;
@@ -85,8 +91,20 @@ export declare class ACodeRuntime extends EventEmitter {
85
91
 
86
92
  submit(message: string): Promise<void>;
87
93
 
94
+ private executeSubmitGuarded;
95
+
96
+ private armAbortForceSettle;
97
+
98
+ private forceSettleActiveTurn;
99
+
88
100
  notifyForceMessage(): void;
89
101
 
102
+ addBuiltInTools(tools: unknown[]): number;
103
+
104
+ removeBuiltInToolsByPrefix(prefix: string): number;
105
+
106
+ private emitExposedToolsStatus;
107
+
90
108
  private injectForceMessage;
91
109
 
92
110
  resetContext(): Promise<void>;
@@ -158,6 +176,8 @@ export declare class ACodeRuntime extends EventEmitter {
158
176
  private filterSharedMcpConnectionsForSubAgent;
159
177
  private connectMcpServers;
160
178
 
179
+ requestMcpToolsRefresh(serverName?: string): void;
180
+
161
181
  private refreshMcpConnectionTools;
162
182
  private createMcpTransport;
163
183