aws-runtime-bridge 1.9.153 → 1.9.168
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 +766 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts +57 -6
- package/dist/adapter/AcodeSdkAdapter.js +4 -8
- package/dist/adapter/ClaudeSdkAdapter.d.ts +2 -0
- package/dist/adapter/ClaudeSdkAdapter.js +3 -3
- package/dist/adapter/CodexSdkAdapter.d.ts +2 -0
- package/dist/adapter/CodexSdkAdapter.js +3 -3
- package/dist/adapter/OpencodeSdkAdapter.d.ts +2 -0
- package/dist/adapter/OpencodeSdkAdapter.js +3 -3
- package/dist/adapter/prompts.d.ts +1 -1
- package/dist/adapter/prompts.js +1 -1
- package/dist/adapter/types.d.ts +41 -20
- package/dist/adapter/types.js +1 -1
- package/dist/browser/frame-encode.d.ts +25 -0
- package/dist/browser/frame-encode.js +2 -0
- package/dist/browser/screencast-differ.d.ts +2 -3
- package/dist/browser/screencast-differ.js +1 -1
- package/dist/browser/screencast-manager.js +1 -1
- package/dist/browser/types.d.ts +5 -1
- package/dist/browser/types.js +1 -1
- package/dist/desktop/desktop-mcp-client.js +2 -2
- package/dist/routes/acode-todos.js +1 -1
- package/dist/routes/background-tasks.js +3 -3
- package/dist/routes/capabilities.js +1 -1
- package/dist/routes/dashboard.d.ts +2 -0
- package/dist/routes/dashboard.js +4 -4
- package/dist/routes/git.js +22 -22
- package/dist/routes/instance.js +1 -1
- package/dist/routes/launch.js +10 -10
- package/dist/routes/ltr.js +1 -1
- package/dist/routes/mcp-extra.js +2 -2
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/plugins.js +1 -1
- package/dist/routes/pty.d.ts +11 -3
- package/dist/routes/pty.js +3 -3
- package/dist/routes/runtime-binding.js +1 -1
- package/dist/routes/sessions.d.ts +18 -74
- package/dist/routes/sessions.js +1 -1
- package/dist/routes/skills.js +1 -1
- package/dist/routes/sub-agents.js +8 -8
- package/dist/routes/terminal.d.ts +24 -2
- package/dist/routes/terminal.js +34 -36
- package/dist/services/agent-process-manager.js +1 -1
- package/dist/services/agent-pty-manager.js +4 -4
- package/dist/services/aws-client-agent-mcp.js +2 -2
- package/dist/services/capability-config.d.ts +8 -0
- package/dist/services/capability-config.js +2 -2
- package/dist/services/desktop-mcp-manager.d.ts +3 -1
- package/dist/services/desktop-mcp-manager.js +1 -1
- package/dist/services/instance-ai-config-service.js +1 -1
- package/dist/services/instance-features.d.ts +26 -2
- package/dist/services/instance-features.js +1 -1
- package/dist/services/instance-init-service.js +1 -1
- package/dist/services/launch-manager.d.ts +9 -1
- package/dist/services/launch-manager.js +6 -6
- package/dist/services/launch-metrics.d.ts +1 -1
- package/dist/services/launch-metrics.js +4 -3
- package/dist/services/mcp-deploy.d.ts +4 -0
- package/dist/services/mcp-env-injector.d.ts +12 -0
- package/dist/services/mcp-env-injector.js +2 -0
- package/dist/services/mcp-launch-binding-queue.d.ts +8 -0
- package/dist/services/mcp-launch-binding-queue.js +1 -1
- package/dist/services/orphan-monitor.js +1 -1
- package/dist/services/panel-env.d.ts +6 -2
- package/dist/services/panel-env.js +1 -1
- package/dist/services/plugin-manager.js +9 -9
- package/dist/services/process-detector.d.ts +23 -3
- package/dist/services/process-detector.js +13 -13
- package/dist/services/runtime-resources-snapshot.d.ts +3 -0
- package/dist/services/runtime-resources-snapshot.js +3 -0
- package/dist/services/session-output.d.ts +13 -0
- package/dist/services/session-output.js +4 -4
- package/dist/services/tunnel-client.js +1 -1
- package/dist/services/workspace-files.js +1 -1
- package/dist/services/workspace-watch.js +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/utils/env-safety.d.ts +15 -0
- package/dist/utils/env-safety.js +2 -0
- package/dist/utils/lifecycle-log.d.ts +2 -0
- package/dist/utils/lifecycle-log.js +3 -0
- package/dist/utils/mcp-utils.js +1 -1
- package/package/acode/dist/ask-user-question-tool.js +1 -1
- package/package/acode/dist/await-complete-tool.js +1 -1
- package/package/acode/dist/background-command-manager.js +4 -4
- package/package/acode/dist/background-command-tools.js +1 -1
- package/package/acode/dist/built-in-file-tools.d.ts +31 -2
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/acode/dist/built-in-symbols-tool.js +1 -1
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts +2 -0
- package/package/acode/dist/builtins/shellCommandPreparation.js +2 -2
- package/package/acode/dist/compaction.d.ts +10 -0
- package/package/acode/dist/compaction.js +6 -6
- package/package/acode/dist/context-overflow.d.ts +19 -0
- package/package/acode/dist/context-overflow.js +2 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-lua.wasm +0 -0
- package/package/acode/dist/index.d.ts +4 -1
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/mcpToolNames.d.ts +4 -0
- package/package/acode/dist/mcpToolNames.js +1 -1
- package/package/acode/dist/parsers/ast-cache.d.ts +12 -1
- package/package/acode/dist/parsers/ast-cache.js +1 -1
- package/package/acode/dist/parsers/language-queries.js +1 -1
- package/package/acode/dist/parsers/symbol-extractor.js +2 -2
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts +1 -1
- package/package/acode/dist/parsers/tree-sitter-loader.js +1 -1
- package/package/acode/dist/plugin-types.d.ts +5 -0
- package/package/acode/dist/plugin-types.js +1 -0
- package/package/acode/dist/read-file-state.d.ts +48 -0
- package/package/acode/dist/read-file-state.js +2 -0
- package/package/acode/dist/runtime-environment.d.ts +2 -0
- package/package/acode/dist/runtime-environment.js +3 -2
- package/package/acode/dist/runtime.d.ts +124 -10
- package/package/acode/dist/runtime.js +27 -23
- package/package/acode/dist/skill-tool.js +3 -3
- package/package/acode/dist/sub-agent-manager.js +1 -1
- package/package/acode/dist/sub-agent-tools.js +5 -5
- package/package/acode/dist/todo-tool.js +3 -3
- package/package/acode/dist/tool-allocation.js +1 -1
- package/package/acode/dist/tool-concurrency/index.d.ts +5 -0
- package/package/acode/dist/tool-concurrency/index.js +2 -0
- package/package/acode/dist/tool-concurrency/observability.d.ts +46 -0
- package/package/acode/dist/tool-concurrency/observability.js +3 -0
- package/package/acode/dist/tool-concurrency/resource-lock.d.ts +25 -0
- package/package/acode/dist/tool-concurrency/resource-lock.js +2 -0
- package/package/acode/dist/tool-concurrency/scheduler.d.ts +30 -0
- package/package/acode/dist/tool-concurrency/scheduler.js +2 -0
- package/package/acode/dist/tool-concurrency/types.d.ts +80 -0
- package/package/acode/dist/tool-concurrency/types.js +2 -0
- package/package/acode/dist/tool-group-manager.d.ts +128 -0
- package/package/acode/dist/tool-group-manager.js +18 -0
- package/package/acode/dist/types.d.ts +20 -2
- package/package/acode/package.json +3 -3
- package/package/aws-client-agent-mcp/README.md +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +20 -32
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +10 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/logger.d.ts +2 -1
- package/package/aws-client-agent-mcp/dist/logger.js +2 -2
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +65 -10
- package/package/aws-client-agent-mcp/dist/mcp-server.js +30 -30
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +42 -7
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +196 -21
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/memory-store.js +3 -3
- package/package/aws-client-agent-mcp/dist/message-buffer.js +1 -1
- package/package/aws-client-agent-mcp/dist/office/office-equation.d.ts +5 -0
- package/package/aws-client-agent-mcp/dist/office/office-equation.js +2 -0
- package/package/aws-client-agent-mcp/dist/office/office-image.d.ts +12 -0
- package/package/aws-client-agent-mcp/dist/office/office-image.js +2 -0
- package/package/aws-client-agent-mcp/dist/office/office-workspace.d.ts +40 -0
- package/package/aws-client-agent-mcp/dist/office/office-workspace.js +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js +1 -1
- package/package/aws-client-agent-mcp/dist/tool-groups.d.ts +4 -0
- package/package/aws-client-agent-mcp/dist/tool-groups.js +2 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts +56 -3
- package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -1
- package/package/aws-client-agent-mcp/package.json +2 -1
- package/package.json +1 -1
- package/package/acode/dist/capability-prompt.d.ts +0 -16
- package/package/acode/dist/capability-prompt.js +0 -3
- package/package/acode/dist/plugin-tools.d.ts +0 -14
- package/package/acode/dist/plugin-tools.js +0 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import h from"axios";import{resolveSchedulerBaseUrlFrom as
|
|
2
|
-
${
|
|
3
|
-
[AgentsWorkStudio: omitted ${
|
|
4
|
-
`,u=Math.max(0,
|
|
1
|
+
import h from"axios";import{resolveSchedulerBaseUrlFrom as H}from"../config.js";import{requestRuntimeAccessTokenRefreshForServer as C}from"../services/auto-register.js";import{logger as f}from"../utils/logger.js";import{agentEventStore as S}from"./agent-event-store.js";import{scheduleEventPush as $}from"./event-delta-pusher.js";import{RingBuffer as P}from"./ring-buffer.js";import{getRuntimeAccessToken as _}from"./runtime-binding.js";const B=new P(2e3);function m(t,r=y(t)){const c=t?.runtimeAccessToken||_(r)||_();return c?{"X-Runtime-Token":c}:void 0}function x(t){if(t)return{schedulerBaseUrl:t.schedulerBaseUrl}}function y(t){return H(t?.schedulerBaseUrl).url}const O=100;function w(t){if(typeof t!="object"||t===null)return;const r=t.response;if(typeof r!="object"||r===null)return;const c=r.status;return typeof c=="number"?c:void 0}let L=null;async function R(t){if(L)return L;L=(async()=>{try{const r=await C(t);return r.success&&r.runtimeAccessToken?(f.info("[session-output] Runtime token refreshed after 401"),!0):(f.warn("[session-output] Runtime token refresh failed",{error:r.error}),!1)}catch(r){return f.warn("[session-output] Runtime token refresh threw",{error:r}),!1}})();try{return await L}finally{L=null}}const E=8e3,M=64*1024,g=32*1024,A=15e3,U=new Map;async function J(t,r,c,e,i,o,u,s){const n={agentId:t,sessionId:r,status:c};u!==void 0&&(n.timestamp=u),e&&(e.actionType&&(n.actionType=e.actionType),e.actionLabel&&(n.actionLabel=e.actionLabel),e.actionDetail&&(n.actionDetail=e.actionDetail),e.actionInput&&(n.actionInput=e.actionInput),e.actionResult&&(n.actionResult=e.actionResult),e.actionError&&(n.actionError=e.actionError),e.actionId&&(n.actionId=e.actionId),e.parentId&&(n.parentId=e.parentId),e.batchId&&(n.batchId=e.batchId),e.batchMetrics&&(n.batchMetrics=e.batchMetrics)),i&&(n.inputTokens=i.inputTokens,n.outputTokens=i.outputTokens,i.contextLimit&&(n.contextLimit=i.contextLimit),i.maxOutput&&(n.maxOutput=i.maxOutput),i.inputLimit&&(n.inputLimit=i.inputLimit),i.compactionThreshold&&(n.compactionThreshold=i.compactionThreshold),i.cacheHitTokens!==void 0&&(n.cacheHitTokens=i.cacheHitTokens),i.cacheMissTokens!==void 0&&(n.cacheMissTokens=i.cacheMissTokens)),s&&(n.sourceEventType=s),process.env.ACODE_TOKEN_DEBUG==="1"&&(n.inputTokens!==void 0||n.outputTokens!==void 0||n.cacheHitTokens!==void 0||n.cacheMissTokens!==void 0)&&console.log(`[TokenDebug][SendStatus] status=${c} inputTokens=${n.inputTokens??"(none)"} outputTokens=${n.outputTokens??"(none)"} cacheHitTokens=${n.cacheHitTokens??"(none)"} cacheMissTokens=${n.cacheMissTokens??"(none)"} contextLimit=${n.contextLimit??"(none)"} actionType=${n.actionType??"(none)"}`);const a=y(o),k=m(o,a);if(!k){f.warn("Skip runtime status callback because runtime token is missing",{agentId:t,sessionId:r,status:c,schedulerBaseUrl:a});return}const p=e?.actionType==="context_compaction";p&&f.info("[compaction-diag] sendStatus enter",{agentId:t,sessionId:r,status:c,actionId:e?.actionId,parentId:e?.parentId,actionLabel:e?.actionLabel});try{const l=await h.post(`${a}/api/runtime/callback/status`,n,{headers:k,timeout:15e3});if(p){const d=l?.data;f.info("[compaction-diag] sendStatus success",{agentId:t,actionId:e?.actionId,actionLabel:e?.actionLabel,httpStatus:l?.status,ignored:d?.ignored,responseBody:d})}return l}catch(l){if(w(l)===401&&await R(a)){const T=m(x(o),a);if(T)try{const b=await h.post(`${a}/api/runtime/callback/status`,n,{headers:T,timeout:15e3});if(p){const v=b?.data;f.info("[compaction-diag] sendStatus retry success",{agentId:t,actionId:e?.actionId,actionLabel:e?.actionLabel,httpStatus:b?.status,ignored:v?.ignored})}return b}catch(b){f.warn("Runtime status callback retry failed after token refresh",{agentId:t,sessionId:r,status:c,error:b});return}}f.warn("Runtime status callback failed",{agentId:t,sessionId:r,status:c,schedulerBaseUrl:a,error:l});return}finally{const l={category:"status",agentId:t,sessionId:r,status:c,actionType:e?.actionType,actionLabel:e?.actionLabel,actionDetail:e?.actionDetail,actionInput:e?.actionInput,actionResult:e?.actionResult,actionError:e?.actionError,actionId:e?.actionId,parentId:e?.parentId,batchId:e?.batchId,batchMetrics:e?.batchMetrics,inputTokens:i?.inputTokens,outputTokens:i?.outputTokens,cacheHitTokens:i?.cacheHitTokens,cacheMissTokens:i?.cacheMissTokens,sourceEventType:s};B.append(l);const d=S.append(t,l);$(t,d,o)}}async function D(t,r,c,e,i,o,u){const s=y(o);try{const n=m(o,s);if(!n){f.warn("Skip runtime output callback because runtime token is missing",{agentId:t,sessionId:c,schedulerBaseUrl:s});return}const a={headers:n,timeout:A};u&&(a.signal=u),await h.post(`${s}/api/runtime/callback/output`,{agentId:t,output:r,sessionId:c,seq:e,...i},a)}catch(n){if(u?.aborted||n?.name==="AbortError"||n?.code==="ERR_CANCELED")throw n;if(w(n)===401&&await R(s)){const k=m(x(o),s);if(k)try{const p={headers:k,timeout:A};u&&(p.signal=u),await h.post(`${s}/api/runtime/callback/output`,{agentId:t,output:r,sessionId:c,seq:e,...i},p);return}catch{}}throw f.warn("Runtime output callback failed",{agentId:t,sessionId:c,error:n}),n}finally{const n=r.length>E?`...(truncated)
|
|
2
|
+
${r.slice(-E)}`:r,a={category:"output",agentId:t,sessionId:c,output:n,seq:e,sourceEventType:i?.sourceEventType,logLevel:i?.logLevel,logCategory:i?.logCategory};B.appendOrMerge(a,p=>{const l=p.data;if(l.category!=="output"||l.sessionId!==c||l.sourceEventType!==i?.sourceEventType||Date.now()-p.timestamp>1e4)return!1;const d=(l.output||"").length,T=(a.output||"").length;return d+T<=E},(p,l)=>{const d=p.output||"",T=l.output||"";return d.includes(T)?p:{...p,output:`${d}${T}`}});const k=S.appendOrMergeOutput(t,a);$(t,k,o)}}async function V(t,r,c,e,i){const o=y(e),u={agentId:t,sessionId:r,questions:c,...i?{toolCallId:i}:{}};try{const s=m(e,o);if(!s)return;await h.post(`${o}/api/runtime/callback/question`,u,{headers:s})}catch(s){if(w(s)===401&&await R(o)){const a=m(x(e),o);if(a)try{await h.post(`${o}/api/runtime/callback/question`,u,{headers:a});return}catch{}}}finally{const s={category:"question",agentId:t,sessionId:r,...i?{toolCallId:i}:{},questions:c};B.append(s);const n=S.append(t,s);$(t,n,e)}}async function Y(t,r,c,e){const i=y(e),o={agentId:t,sessionId:r,...c};try{const u=m(e,i);if(!u)return;await h.post(`${i}/api/runtime/callback/tool-auth`,o,{headers:u})}catch(u){if(w(u)===401&&await R(i)){const n=m(x(e),i);if(n)try{await h.post(`${i}/api/runtime/callback/tool-auth`,o,{headers:n});return}catch{}}}}async function Z(t){const r=y();try{const c=m(void 0,r);if(!c)return;await h.post(`${r}/api/runtime/callback/file-changed`,t,{headers:c})}catch(c){if(w(c)===401&&await R(r)){const i=m(void 0,r);if(i)try{await h.post(`${r}/api/runtime/callback/file-changed`,t,{headers:i});return}catch{}}}finally{const c={category:"file_changed",agentId:"",sessionId:null,workspacePath:t.workspacePath,filePath:t.filePath,eventType:t.eventType,size:t.size};B.append(c)}}function I(t,r){const c=U.get(t);if(!c||!r)return;const e=`${c.outputBuffer}${r}`;if(e.length<=M){c.outputBuffer=e;return}const o=`\r
|
|
3
|
+
[AgentsWorkStudio: omitted ${e.length-M} chars of old terminal output]\r
|
|
4
|
+
`,u=Math.max(0,M-o.length);c.outputBuffer=`${o}${e.slice(-u)}`}function F(t){const r=U.get(t);r&&(r.flushTimer||(r.flushTimer=setTimeout(()=>{r.flushTimer=null,q(t).catch(()=>{})},O)))}async function q(t){const r=U.get(t);if(!r||r.flushInFlight||!r.outputBuffer)return;const c=r.outputBuffer.slice(0,g);r.outputBuffer=r.outputBuffer.slice(g);const e=(async()=>{const i=r.seq+1;r.seq=i,await D(r.agentId,c,t,i),U.get(t)?.outputBuffer&&F(t)})();r.flushInFlight=e;try{await e}finally{const i=U.get(t);i&&(i.flushInFlight=null)}}export{I as appendSessionOutput,B as bridgeEventBuffer,q as flushSessionOutput,F as scheduleOutputFlush,Z as sendFileChanged,D as sendOutput,V as sendQuestionRequest,J as sendStatus,Y as sendToolAuthRequest,U as sessions};
|
|
5
5
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import I,{AxiosError as O}from"axios";import p from"ws";import{getActiveBridgeHttpPort as _}from"../config.js";import{logger as i}from"../utils/logger.js";import{loadConfigs as x,loadPrimaryLifecycleConfig as D}from"./auto-register.js";import{getRuntimeAccessToken as L,normalizeSchedulerBaseUrl as S}from"./runtime-binding.js";const W=15e3,P=5e3,$=3e4,k=.2,J=200,q=1*1024*1024,G=4001,H=768*1024,K=384*1024,h=new Map;function X(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:",c=L(r),l=x().find(m=>S(m.serverUrl)===r)||D(),s=c||l.userKey;if(c?i.debug("[TunnelClient] \u4F7F\u7528 runtime access token \u8FDE\u63A5\u96A7\u9053"):l.userKey&&i.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 i.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 i.warn("[TunnelClient] \u65E0\u6CD5\u89E3\u6790 serverUrl:",String(t)),null}}function oe(e,r){const n=S(e);if(!n){i.warn("[TunnelClient] serverUrl \u65E0\u6548\uFF0C\u65E0\u6CD5\u542F\u52A8\u96A7\u9053:",e);return}const t=h.get(n);t&&(t.shuttingDown=!0,C(t),h.delete(n));const o={serverUrl:n,instanceId:r,streams:new Map,shuttingDown:!1};h.set(n,o),A(o)}function se(){for(const e of h.values())e.shuttingDown=!0,C(e);h.clear()}function ie(e){const r=S(e);if(!r)return;const n=h.get(r);n&&(n.shuttingDown=!0,C(n),h.delete(r),i.info(`[TunnelClient] tunnel stopped: ${r}`))}function ce(e){const r=new Set(e.map(n=>S(n)).filter(n=>!!n));for(const[n,t]of h.entries())r.has(n)||(i.info(`[TunnelClient] serverUrl \u5DF2\u4E0D\u5728\u914D\u7F6E\u4E2D\uFF0C\u505C\u6B62\u65E7\u96A7\u9053: ${n}`),t.shuttingDown=!0,C(t),h.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 A(e){if(e.shuttingDown)return;const r=e.instanceId;if(!r)return;const n=X(e);if(!n){b(e);return}i.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",()=>{i.info("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5DF2\u5EFA\u7ACB: server=",e.serverUrl,"instanceId=",r),e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0,e.reconnectAttempts=0,e.timer=setInterval(()=>{t.readyState===p.OPEN&&t.send(JSON.stringify({t:"ping"}))},W)}),t.on("unexpected-response",(o,c)=>{i.error("[TunnelClient] \u670D\u52A1\u7AEF\u62D2\u7EDD\u8FDE\u63A5\uFF08\u975E 101 \u54CD\u5E94\uFF09: server=",e.serverUrl,"statusCode=",c.statusCode,"statusMessage=",c.statusMessage)}),t.on("upgrade",o=>{i.debug("[TunnelClient] \u6536\u5230\u5347\u7EA7\u54CD\u5E94: statusCode=",o.statusCode)}),t.on("message",o=>{let c;typeof o=="string"?c=o:Buffer.isBuffer(o)?c=o.toString("utf-8"):c=Buffer.from(o).toString("utf-8");try{const l=JSON.parse(c);Y(e,l)}catch(l){i.warn("[TunnelClient] \u89E3\u6790\u96A7\u9053\u6D88\u606F\u5931\u8D25:",String(l))}}),t.on("close",(o,c)=>{if(i.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5173\u95ED: server=",e.serverUrl,"code=",o,"reason=",String(c)),B(e),o===G){i.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=>{i.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u9519\u8BEF: server=",e.serverUrl,"err=",o.message,"code=",o.code),B(e),b(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 b(e){if(e.shuttingDown||e.reconnectTimer)return;const r=(e.reconnectAttempts??0)+1;e.reconnectAttempts=r;const n=Math.min(P*Math.pow(2,r-1),$),t=1-k+Math.random()*k*2,o=Math.round(n*t);i.debug("[TunnelClient] \u8BA1\u5212\u91CD\u8FDE: server=",e.serverUrl,"attempt=",r,"delayMs=",o),e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=void 0,A(e)},o)}function Y(e,r){switch(r.t){case"req":j(e,r);break;case"wsopen":z(e,r);break;case"wsmsg":F(e,r);break;case"wsclose":V(e,r);break;case"ping":f(e,{t:"pong"});break;case"pong":break;default:i.debug("[TunnelClient] \u672A\u77E5\u6D88\u606F\u7C7B\u578B: t=",r.t)}}async function j(e,r){const n=r.id,t=r.method||"GET",o=r.path||"/",c=`http://127.0.0.1:${_()}${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 I.request({url:c,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 v=a.data,M=String(m["content-type"]?.[0]||"").toLowerCase().includes("text/event-stream"),g=[];let w=0,T=!1,y=!1;try{if(v)for await(const d of v){const u=Buffer.isBuffer(d)?d:Buffer.from(d);if(u.length!==0){if(T){if(!E(e,n,u))return}else if(M){if(T=!0,y=!0,!f(e,{t:"res",id:n,status:a.status,headers:m,body:null,chunked:!0})||!E(e,n,u))return}else if(g.push(u),w+=u.length,w>H){if(T=!0,y=!0,!f(e,{t:"res",id:n,status:a.status,headers:m,body:null,chunked:!0}))return;for(const R of g)if(!E(e,n,R))return;g.length=0,w=0}}}}catch(d){const u=d instanceof Error?d.message:String(d);i.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 N=w>0?Buffer.concat(g).toString("base64"):"";f(e,{t:"res",id:n,status:a.status,headers:m,body:N||null})}catch(l){const s=l instanceof O?l.message:String(l);i.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+K);if(!f(e,{t:"reschunk",id:r,d:o.toString("base64")}))return!1;t+=o.length}return!0}function z(e,r){const n=r.id,t=r.path||"/",o=`ws://127.0.0.1:${_()}${t}`;let c;try{c=new p(o,{handshakeTimeout:1e4})}catch(s){i.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:c,pending:[]};e.streams.set(n,l),c.on("open",()=>{for(const s of l.pending)U(l,s.data,s.bin);l.pending=[],f(e,{t:"wsopened",id:n})}),c.on("message",(s,a)=>{const m=Buffer.isBuffer(s)?s:Buffer.from(s);a&&e.ws&&e.ws.bufferedAmount>q||f(e,{t:"wsmsg",id:n,bin:a,d:m.toString("base64")})}),c.on("close",(s,a)=>{e.streams.delete(n),f(e,{t:"wsclose",id:n,code:s,reason:String(a)})}),c.on("error",s=>{i.warn("[TunnelClient] \u672C\u5730 WS \u9519\u8BEF: id=",n,"err=",s.message),e.streams.delete(n);try{c.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 F(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>=J&&(n.pending.shift(),i.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 V(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 i.warn("[TunnelClient] sendJson \u5931\u8D25:",String(n)),!1}}export{oe as startTunnelClient,se as stopTunnelClient,ie as stopTunnelClientForServer,ce as stopTunnelClientsExcept};
|
|
2
2
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{createReadStream as q,createWriteStream as O,promises as s}from"node:fs";import lt from"node:os";import w from"node:path";import{Readable as ht}from"node:stream";import{pipeline as ut}from"node:stream/promises";import{TextDecoder as wt}from"node:util";import{createGunzip as dt}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 wt("gbk");function u(t){return String(t||"").replace(/\\/g,"/")}function Z(t){const e=w.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=w.resolve(e),i=w.relative(t,r);if(i.startsWith("..")||w.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function D(t){return s.realpath(t)}async function F(t){await s.realpath(w.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=w.resolve(r),o=w.relative(t,i);return o.startsWith("..")||w.isAbsolute(o)?i:C(e,w.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 D(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("\\")||w.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 $(w.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):w.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":"-",h=t&r?"w":"-",l=(t&i)!==0,d=(t&o)!==0?l?a:c:l?"x":"-";return`${n}${h}${d}`}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=w.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 D(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 T({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 D(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(w.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 w.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,w.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:h}=await b(t);return rt(e,h)}const i=w.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=w.relative(e,o),n=c===""||!c.startsWith("..")&&!w.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=w.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 D(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 h=w.join(o,n.name);try{if(n.isDirectory())await i(h,a+1);else if(n.isFile()){const l=await s.stat(h);e+=l.size}}catch{}}}return await i(t,0),e}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await T(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:w.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 T(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 D(r),o=await s.stat(i);if(!o.isFile())throw new Error(`Path is not a file: ${i}`);const a=w.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(w.dirname(r),{recursive:!0}),await F(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=w.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(w.dirname(o),{recursive:!0}),await F(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 T(t),i=$(t.newName),o=w.join(w.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 T(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=w.relative(r,i.resolvedTargetPath);if(n===""||!n.startsWith("..")&&!w.isAbsolute(n))throw new Error("Cannot move a directory into itself or its descendant")}const c=w.join(i.resolvedTargetPath,w.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 T(t),i=await s.stat(r),o=w.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),h=w.join(r,n);await s.mkdir(w.dirname(h),{recursive:!0}),await F(h),await s.copyFile(a.path,h);let l;t.extractArchives&&V(h)&&(l=await Wt({workspacePath:e,archivePath:h,outputPath:Ft(r,c)})),o.push({fileName:c,targetPath:u(h),size:a.size,extracted:l})}return{ok:!0,workspacePath:u(e),targetPath:u(r),files:o}}async function ie(t){const{workspaceRoot:e,resolvedTargetPath:r}=await T(t),i=await s.stat(r),o=w.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=w.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=w.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=w.join(o,`${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.zip`),n=new _({zlib:{level:9}}),h=()=>n.pointer(),l=()=>n.abort(),f=O(c),d=new Promise((m,g)=>{f.on("close",m),f.on("finish",m),n.on("error",g),f.on("error",g)});d.catch(()=>{}),n.pipe(f);const y=setInterval(()=>{try{if(i?.()){l();return}r?.(h())}catch{}},200);try{return n.on("progress",()=>{i?.()&&l()}),n.directory(t,!1),i?.()&&l(),await n.finalize(),await d,r?.(h()),c}catch(m){throw await s.rm(c,{force:!0}).catch(()=>{}),m}finally{clearInterval(y)}}async function $t(t,e,r){let i=0,o;const a=q(t),c=a.pipe(pt.Parse({forceStream:!0}));try{for await(const n of c){if(o){await J(n);continue}const h=Tt(n);try{const l=M(e,h);if(N(r),n.type==="Directory"){await s.mkdir(l,{recursive:!0});continue}if(n.type!=="File")throw new Error(`Unsupported archive entry type: ${h}`);await s.mkdir(w.dirname(l),{recursive:!0}),await F(l),await tt(n,l,r),i+=1}catch(l){o=l,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,h)=>{if(a)return!1;try{M(e,n),N(i);const l=String(h.type||"").toLowerCase();if(l==="symboliclink"||l==="link")throw new Error(`Archive links are not supported: ${n}`);if(l!=="file"&&l!=="oldfile"&&l!=="contiguousfile"&&l!=="directory")throw new Error(`Unsupported archive entry type: ${n}`);return typeof h.size=="number"&&Q(i,h.size),l!=="directory"&&(o+=1),!0}catch(l){return a=l,!1}};if(await ft.x({file:t,cwd:e,gzip:r,strict:!0,filter:(n,h)=>c(n,h)}),a)throw a;return o}async function Ct(t,e,r){const i=Y(w.basename(t)),o=M(e,i||"decompressed");return N(r),await s.mkdir(w.dirname(o),{recursive:!0}),await F(o),await tt(q(t).pipe(dt()),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 D(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 h=V(a);if(!h)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let l=0;const f={entries:0,totalBytes:0};return h==="zip"?l=await $t(a,i,f):h==="tar"||h==="tgz"?l=await At(a,i,h==="tgz",f):l=await Ct(a,i,f),{ok:!0,workspacePath:u(r),archivePath:u(a),targetPath:u(i),extractedEntries:l}}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,h=(t.mode==="filename"?"filename":"content")==="filename",l=[];let f=0,d=!1;async function y(m){if(d)return;let g;try{g=await s.readdir(m,{withFileTypes:!0})}catch{return}for(const p of g){if(d)return;const P=C(e,w.join(m,p.name));if(p.isSymbolicLink())continue;if(p.isDirectory()){if(j(p.name))continue;await y(P);continue}if(!p.isFile()||o&&!at(p.name,o))continue;f+=1;let k;try{k=await s.stat(P)}catch{continue}if(k.size>nt)continue;if(h){const E=(i?p.name.toLowerCase():p.name).indexOf(c);if(E!==-1&&(l.push({filePath:u(P),lineNumber:0,lineContent:p.name,matchStart:E,matchEnd:E+r.length}),l.length>=a)){d=!0;return}continue}let S;try{S=await s.readFile(P,"utf-8")}catch{continue}const z=S.split(/\r?\n/);for(let R=0;R<z.length;R+=1){const E=z[R],ct=i?E.toLowerCase():E;let U=0;for(;;){const A=ct.indexOf(c,U);if(A===-1)break;if(l.push({filePath:u(P),lineNumber:R+1,lineContent:E,matchStart:A,matchEnd:A+r.length}),l.length>=a){d=!0;return}U=A+1}}}}return await y(e),{workspacePath:u(e),keyword:r,totalFiles:f,totalMatches:l.length,matches:l,truncated:d}}const Bt=100,It=120;async function se(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=i?r.toLowerCase():r,a=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:Bt,c=typeof t.maxDirectories=="number"&&t.maxDirectories>0?t.maxDirectories:It,n=String(t.rootPath||"").trim(),h=n?w.resolve(/^[A-Za-z]:$/.test(n)?`${n}/`:n):e;let l;try{l=await s.realpath(h)}catch{throw new Error(`Path not found: ${h}`)}if(!(await s.stat(l)).isDirectory())throw new Error(`Path is not a directory: ${h}`);const d=[],y=[l];let m=0,g=!1;for(;y.length>0&&d.length<a&&m<c;){const p=y.shift();m+=1;let P;try{P=await s.readdir(p,{withFileTypes:!0})}catch{continue}for(const k of P){if(d.length>=a){g=!0;break}if(k.isSymbolicLink())continue;const S=w.join(p,k.name);if(k.isDirectory()&&j(k.name))continue;const z=w.relative(l,S).replace(/\\/g,"/");(i?z.toLowerCase():z).includes(o)&&d.push({name:k.name,path:u(S),isDirectory:k.isDirectory()}),k.isDirectory()&&m<c&&y.push(S)}}return(m>=c||d.length>=a)&&(g=!0),d.sort((p,P)=>p.isDirectory===P.isDirectory?p.name.localeCompare(P.name):p.isDirectory?-1:1),{workspacePath:u(e),rootPath:u(l),keyword:r,items:d,truncated:g}}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 h=0;for(;;){const f=o.toLowerCase().indexOf(n,h);if(f===-1)break;o=o.slice(0,f)+r+o.slice(f+e.length),c+=1;const d=r.length||1;h=f+d}}else{let n=0;for(;;){const h=o.indexOf(e,n);if(h===-1)break;o=o.slice(0,h)+r+o.slice(h+e.length),c+=1;const l=r.length||1;n=h+l}}return o===a?{changed:!1,count:0}:(await F(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,h=0;if(c){const{resolvedFilePath:f}=await W({workspacePath:t.workspacePath,filePath:c}),d=await st(f,r,i,o);return d.changed&&(n=1,h=d.count),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:h}}async function l(f){let d;try{d=await s.readdir(f,{withFileTypes:!0})}catch{return}for(const y of d){const m=C(e,w.join(f,y.name));if(y.isSymbolicLink())continue;if(y.isDirectory()){if(j(y.name))continue;await l(m);continue}if(!y.isFile()||a&&!at(y.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,h+=g.count)}}return await l(e),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:h}}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 q,createWriteStream as O,promises as s}from"node:fs";import lt from"node:os";import w from"node:path";import{Readable as ht}from"node:stream";import{pipeline as ut}from"node:stream/promises";import{TextDecoder as wt}from"node:util";import{createGunzip as dt}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 wt("gbk");function u(t){return String(t||"").replace(/\\/g,"/")}function Z(t){const e=w.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=w.resolve(e),i=w.relative(t,r);if(i.startsWith("..")||w.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function D(t){return s.realpath(t)}async function F(t){await s.realpath(w.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=w.resolve(t,r),o=w.relative(t,i);return o.startsWith("..")||w.isAbsolute(o)?i:C(e,w.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 D(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("\\")||w.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 $(w.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):w.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":"-",h=t&r?"w":"-",l=(t&i)!==0,d=(t&o)!==0?l?a:c:l?"x":"-";return`${n}${h}${d}`}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=w.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 D(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 T({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 D(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(w.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 w.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,w.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:h}=await b(t);return rt(e,h)}const i=w.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=w.relative(e,o),n=c===""||!c.startsWith("..")&&!w.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=w.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 D(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 h=w.join(o,n.name);try{if(n.isDirectory())await i(h,a+1);else if(n.isFile()){const l=await s.stat(h);e+=l.size}}catch{}}}return await i(t,0),e}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await T(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:w.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 T(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 D(r),o=await s.stat(i);if(!o.isFile())throw new Error(`Path is not a file: ${i}`);const a=w.extname(i).toLowerCase();if(a===".doc"||a===".xls"||a===".ppt")return{workspacePath:u(e),filePath:u(i),kind:"unsupported",content:"",message:`\u65E7\u7248 ${a} \u683C\u5F0F\u5C06\u7531\u670D\u52A1\u7AEF\u8F6C\u6362\u4E3A\u73B0\u4EE3\u683C\u5F0F\u540E\u6253\u5F00\uFF08\u539F\u6587\u4EF6\u4E0D\u53D8\uFF09\u3002`,size:o.size,mtimeMs:o.mtimeMs};if(!/\.(docx|docm|xlsx|xlsm|pptx|pptm)$/.test(a))throw new Error("Document preview only supports docx/xlsx/pptx 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(w.dirname(r),{recursive:!0}),await F(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=w.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(w.dirname(o),{recursive:!0}),await F(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 T(t),i=$(t.newName),o=w.join(w.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 T(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=w.relative(r,i.resolvedTargetPath);if(n===""||!n.startsWith("..")&&!w.isAbsolute(n))throw new Error("Cannot move a directory into itself or its descendant")}const c=w.join(i.resolvedTargetPath,w.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 T(t),i=await s.stat(r),o=w.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),h=w.join(r,n);await s.mkdir(w.dirname(h),{recursive:!0}),await F(h),await s.copyFile(a.path,h);let l;t.extractArchives&&V(h)&&(l=await Wt({workspacePath:e,archivePath:h,outputPath:Ft(r,c)})),o.push({fileName:c,targetPath:u(h),size:a.size,extracted:l})}return{ok:!0,workspacePath:u(e),targetPath:u(r),files:o}}async function ie(t){const{workspaceRoot:e,resolvedTargetPath:r}=await T(t),i=await s.stat(r),o=w.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=w.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=w.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=w.join(o,`${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}.zip`),n=new _({zlib:{level:9}}),h=()=>n.pointer(),l=()=>n.abort(),f=O(c),d=new Promise((m,g)=>{f.on("close",m),f.on("finish",m),n.on("error",g),f.on("error",g)});d.catch(()=>{}),n.pipe(f);const y=setInterval(()=>{try{if(i?.()){l();return}r?.(h())}catch{}},200);try{return n.on("progress",()=>{i?.()&&l()}),n.directory(t,!1),i?.()&&l(),await n.finalize(),await d,r?.(h()),c}catch(m){throw await s.rm(c,{force:!0}).catch(()=>{}),m}finally{clearInterval(y)}}async function $t(t,e,r){let i=0,o;const a=q(t),c=a.pipe(pt.Parse({forceStream:!0}));try{for await(const n of c){if(o){await J(n);continue}const h=Tt(n);try{const l=M(e,h);if(N(r),n.type==="Directory"){await s.mkdir(l,{recursive:!0});continue}if(n.type!=="File")throw new Error(`Unsupported archive entry type: ${h}`);await s.mkdir(w.dirname(l),{recursive:!0}),await F(l),await tt(n,l,r),i+=1}catch(l){o=l,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,h)=>{if(a)return!1;try{M(e,n),N(i);const l=String(h.type||"").toLowerCase();if(l==="symboliclink"||l==="link")throw new Error(`Archive links are not supported: ${n}`);if(l!=="file"&&l!=="oldfile"&&l!=="contiguousfile"&&l!=="directory")throw new Error(`Unsupported archive entry type: ${n}`);return typeof h.size=="number"&&Q(i,h.size),l!=="directory"&&(o+=1),!0}catch(l){return a=l,!1}};if(await ft.x({file:t,cwd:e,gzip:r,strict:!0,filter:(n,h)=>c(n,h)}),a)throw a;return o}async function Ct(t,e,r){const i=Y(w.basename(t)),o=M(e,i||"decompressed");return N(r),await s.mkdir(w.dirname(o),{recursive:!0}),await F(o),await tt(q(t).pipe(dt()),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 D(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 h=V(a);if(!h)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let l=0;const f={entries:0,totalBytes:0};return h==="zip"?l=await $t(a,i,f):h==="tar"||h==="tgz"?l=await At(a,i,h==="tgz",f):l=await Ct(a,i,f),{ok:!0,workspacePath:u(r),archivePath:u(a),targetPath:u(i),extractedEntries:l}}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,h=(t.mode==="filename"?"filename":"content")==="filename",l=[];let f=0,d=!1;async function y(m){if(d)return;let g;try{g=await s.readdir(m,{withFileTypes:!0})}catch{return}for(const p of g){if(d)return;const P=C(e,w.join(m,p.name));if(p.isSymbolicLink())continue;if(p.isDirectory()){if(j(p.name))continue;await y(P);continue}if(!p.isFile()||o&&!at(p.name,o))continue;f+=1;let k;try{k=await s.stat(P)}catch{continue}if(k.size>nt)continue;if(h){const E=(i?p.name.toLowerCase():p.name).indexOf(c);if(E!==-1&&(l.push({filePath:u(P),lineNumber:0,lineContent:p.name,matchStart:E,matchEnd:E+r.length}),l.length>=a)){d=!0;return}continue}let S;try{S=await s.readFile(P,"utf-8")}catch{continue}const z=S.split(/\r?\n/);for(let R=0;R<z.length;R+=1){const E=z[R],ct=i?E.toLowerCase():E;let U=0;for(;;){const A=ct.indexOf(c,U);if(A===-1)break;if(l.push({filePath:u(P),lineNumber:R+1,lineContent:E,matchStart:A,matchEnd:A+r.length}),l.length>=a){d=!0;return}U=A+1}}}}return await y(e),{workspacePath:u(e),keyword:r,totalFiles:f,totalMatches:l.length,matches:l,truncated:d}}const Bt=100,It=120;async function se(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=i?r.toLowerCase():r,a=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:Bt,c=typeof t.maxDirectories=="number"&&t.maxDirectories>0?t.maxDirectories:It,n=String(t.rootPath||"").trim(),h=n?w.resolve(/^[A-Za-z]:$/.test(n)?`${n}/`:n):e;let l;try{l=await s.realpath(h)}catch{throw new Error(`Path not found: ${h}`)}if(!(await s.stat(l)).isDirectory())throw new Error(`Path is not a directory: ${h}`);const d=[],y=[l];let m=0,g=!1;for(;y.length>0&&d.length<a&&m<c;){const p=y.shift();m+=1;let P;try{P=await s.readdir(p,{withFileTypes:!0})}catch{continue}for(const k of P){if(d.length>=a){g=!0;break}if(k.isSymbolicLink())continue;const S=w.join(p,k.name);if(k.isDirectory()&&j(k.name))continue;const z=w.relative(l,S).replace(/\\/g,"/");(i?z.toLowerCase():z).includes(o)&&d.push({name:k.name,path:u(S),isDirectory:k.isDirectory()}),k.isDirectory()&&m<c&&y.push(S)}}return(m>=c||d.length>=a)&&(g=!0),d.sort((p,P)=>p.isDirectory===P.isDirectory?p.name.localeCompare(P.name):p.isDirectory?-1:1),{workspacePath:u(e),rootPath:u(l),keyword:r,items:d,truncated:g}}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 h=0;for(;;){const f=o.toLowerCase().indexOf(n,h);if(f===-1)break;o=o.slice(0,f)+r+o.slice(f+e.length),c+=1;const d=r.length||1;h=f+d}}else{let n=0;for(;;){const h=o.indexOf(e,n);if(h===-1)break;o=o.slice(0,h)+r+o.slice(h+e.length),c+=1;const l=r.length||1;n=h+l}}return o===a?{changed:!1,count:0}:(await F(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,h=0;if(c){const{resolvedFilePath:f}=await W({workspacePath:t.workspacePath,filePath:c}),d=await st(f,r,i,o);return d.changed&&(n=1,h=d.count),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:h}}async function l(f){let d;try{d=await s.readdir(f,{withFileTypes:!0})}catch{return}for(const y of d){const m=C(e,w.join(f,y.name));if(y.isSymbolicLink())continue;if(y.isDirectory()){if(j(y.name))continue;await l(m);continue}if(!y.isFile()||a&&!at(y.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,h+=g.count)}}return await l(e),{workspacePath:u(e),keyword:r,replacement:i,changedFiles:n,changedMatches:h}}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
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{watch as w,promises as l}from"node:fs";import s from"node:path";import{sendFileChanged as p}from"./session-output.js";const h=new Map,m=300;function n(t){return String(t||"").replace(/\\/g,"/")}async function P(t){const e=s.resolve(String(t.workspacePath||"").trim()),a=await l.realpath(e),o=s.resolve(String(t.filePath||"").trim()),i=s.relative(e,o),r=i.startsWith("..")||s.isAbsolute(i)?o:s.join(a,i),c=await l.realpath(r);if(!(await l.stat(c)).isFile())throw new Error(`Path is not a file: ${c}`);return{workspaceRoot:a,filePath:c}}function u(t,e){return`${n(t)}::${n(e)}`}function f(t,e){e.timer&&(clearTimeout(e.timer),e.timer=null),e.watcher.close(),h.delete(t)}async function k(t){const e=s.resolve(String(t.workspacePath||"").trim()),a=await l.realpath(e),o=s.resolve(String(t.filePath||"").trim()),i=s.relative(e,o),r=i.startsWith("..")||s.isAbsolute(i)?o:s.join(a,i);return u(a,r)}async function g(t,e){let a,o;try{const i=await l.stat(t.filePath);a=i.size,o=i.mtimeMs}catch{}await p({workspacePath:n(t.workspacePath),filePath:n(t.filePath),eventType:e,size:a,mtimeMs:o})}function y(t,e){t.timer&&clearTimeout(t.timer),t.timer=setTimeout(()=>{t.timer=null,g(t,e).catch(()=>{})},m)}async function C(t){const{workspaceRoot:e,filePath:a}=await P(t),o=u(e,a),i=h.get(o);if(i)i.refCount+=1;else{const r={watcher:w(a,c=>y(r,String(c||"change"))),workspacePath:e,filePath:a,timer:null,refCount:1};r.watcher.on("error",()=>f(o,r)),h.set(o,r)}return{ok:!0,workspacePath:n(e),filePath:n(a)}}async function R(t){const e=await k(t),a=h.get(e);return a&&(a.refCount-=1,a.refCount<=0&&f(e,a)),{ok:!0}}function S(){return h.size}export{S as getWorkspaceWatchCountForTests,R as unwatchWorkspaceFile,C as watchWorkspaceFile};
|
|
1
|
+
import{watch as w,promises as l}from"node:fs";import s from"node:path";import{sendFileChanged as p}from"./session-output.js";const h=new Map,m=300;function n(t){return String(t||"").replace(/\\/g,"/")}async function P(t){const e=s.resolve(String(t.workspacePath||"").trim()),a=await l.realpath(e),o=s.resolve(e,String(t.filePath||"").trim()),i=s.relative(e,o),r=i.startsWith("..")||s.isAbsolute(i)?o:s.join(a,i),c=await l.realpath(r);if(!(await l.stat(c)).isFile())throw new Error(`Path is not a file: ${c}`);return{workspaceRoot:a,filePath:c}}function u(t,e){return`${n(t)}::${n(e)}`}function f(t,e){e.timer&&(clearTimeout(e.timer),e.timer=null),e.watcher.close(),h.delete(t)}async function k(t){const e=s.resolve(String(t.workspacePath||"").trim()),a=await l.realpath(e),o=s.resolve(e,String(t.filePath||"").trim()),i=s.relative(e,o),r=i.startsWith("..")||s.isAbsolute(i)?o:s.join(a,i);return u(a,r)}async function g(t,e){let a,o;try{const i=await l.stat(t.filePath);a=i.size,o=i.mtimeMs}catch{}await p({workspacePath:n(t.workspacePath),filePath:n(t.filePath),eventType:e,size:a,mtimeMs:o})}function y(t,e){t.timer&&clearTimeout(t.timer),t.timer=setTimeout(()=>{t.timer=null,g(t,e).catch(()=>{})},m)}async function C(t){const{workspaceRoot:e,filePath:a}=await P(t),o=u(e,a),i=h.get(o);if(i)i.refCount+=1;else{const r={watcher:w(a,c=>y(r,String(c||"change"))),workspacePath:e,filePath:a,timer:null,refCount:1};r.watcher.on("error",()=>f(o,r)),h.set(o,r)}return{ok:!0,workspacePath:n(e),filePath:n(a)}}async function R(t){const e=await k(t),a=h.get(e);return a&&(a.refCount-=1,a.refCount<=0&&f(e,a)),{ok:!0}}function S(){return h.size}export{S as getWorkspaceWatchCountForTests,R as unwatchWorkspaceFile,C as watchWorkspaceFile};
|
|
2
2
|
|
package/dist/types.d.ts
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
export declare const BLOCKED_ENV_KEYS: ReadonlySet<string>;
|
|
3
|
+
|
|
4
|
+
export declare const ENV_KEY_PATTERN: RegExp;
|
|
5
|
+
|
|
6
|
+
export declare const SPAWN_ENV_KEY_PATTERN: RegExp;
|
|
7
|
+
|
|
8
|
+
export interface RejectedEnvKey {
|
|
9
|
+
key: string;
|
|
10
|
+
reason: "blocked" | "invalid-name" | "empty-value";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export declare function sanitizeEnvForChild(env: unknown, options?: {
|
|
14
|
+
onReject?: (item: RejectedEnvKey) => void;
|
|
15
|
+
}): Record<string, string>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{logger as O}from"./logger.js";const P=O.child("env-safety"),A=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_REPL_HISTORY","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PYTHONHOME","PERL5LIB","PERL5OPT","RUBYOPT","RUBYLIB","BASH_ENV","ENV","SHELLOPTS","BASHOPTS","IFS","PS1","PS4","GIT_SSH","GIT_SSH_COMMAND","GIT_CONFIG","GIT_CONFIG_GLOBAL","PATH","HOME","USERPROFILE","SYSTEMROOT","COMSPEC","TEMP","TMP","LANG","TZ"]),a=/^[A-Za-z_][A-Za-z0-9_]*$/,S=/^[A-Za-z_][A-Za-z0-9_.-]*$/;function L(o,r){const _={};if(!o||typeof o!="object")return _;const t=r?.onReject??(n=>{P.warn(`env key rejected (${n.reason}): ${n.key}`)});for(const[n,T]of Object.entries(o)){const e=n.trim();if(T==null){e&&t({key:e,reason:"empty-value"});continue}const E=String(T).trim();if(!E){t({key:e,reason:"empty-value"});continue}if(!S.test(e)){t({key:e,reason:"invalid-name"});continue}if(A.has(e.toUpperCase())){t({key:e,reason:"blocked"});continue}_[e]=E}return _}export{A as BLOCKED_ENV_KEYS,a as ENV_KEY_PATTERN,S as SPAWN_ENV_KEY_PATTERN,L as sanitizeEnvForChild};
|
|
2
|
+
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{appendFile as f,mkdir as u}from"node:fs/promises";import c from"node:path";import{getBridgeDataHome as a}from"../config.js";const g=process.env.ACODE_LIFECYCLE_LOG!=="0";let l=null,t=null,r=!1;function d(){const e=new Date().toISOString().slice(0,10);return c.join(a(),"log",`lifecycle-${e}.jsonl`)}function m(e){return l===e&&t||(l=e,t=u(e,{recursive:!0}).then(()=>{}).catch(n=>{r||(r=!0,console.warn(`[lifecycle-log] \u65E0\u6CD5\u521B\u5EFA\u8BCA\u65AD\u65E5\u5FD7\u76EE\u5F55\uFF0C\u540E\u7EED\u8BCA\u65AD\u8BB0\u5F55\u5C06\u88AB\u4E22\u5F03: ${n instanceof Error?n.message:String(n)}`))})),t}function D(e,n){if(!g)return;const s={ts:new Date().toISOString(),event:e,...n},o=d();m(c.dirname(o)).then(()=>{f(o,`${JSON.stringify(s)}
|
|
2
|
+
`,"utf8").catch(i=>{r||(r=!0,console.warn(`[lifecycle-log] \u8BCA\u65AD\u65E5\u5FD7\u5199\u5165\u5931\u8D25\uFF0C\u540E\u7EED\u8BB0\u5F55\u5C06\u88AB\u4E22\u5F03: ${i instanceof Error?i.message:String(i)}`))})})}export{D as recordLifecycleEvent};
|
|
3
|
+
|
package/dist/utils/mcp-utils.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomUUID as
|
|
1
|
+
import{randomUUID as i}from"node:crypto";import{parseBoolean as o}from"./validation.js";function d(r){if(!r||typeof r!="object")return null;const n=r,t=String(n.name||"").trim();return t?{id:String(n.id||i()),name:t,transport:String(n.transport||"stdio").trim().toLowerCase(),command:n.command===null||n.command===void 0?"":String(n.command),url:n.url===null||n.url===void 0?"":String(n.url),args:Array.isArray(n.args)?n.args.map(e=>String(e)):[],env:n.env&&typeof n.env=="object"?n.env:{},enabled:o(n.enabled,!0),collapsed:n.collapsed===!0,description:String(n.description||"").trim()||void 0,installedAt:n.installedAt||new Date().toISOString()}:null}export{d as normalizeMcpPayload};
|
|
2
2
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
const c="ask_user_question",f=["\u5411\u7528\u6237\u63D0\u51FA\u95EE\u9898\uFF0C\u6536\u96C6\u66F4\u591A\u4FE1\u606F\u4E3A\u505A\u51B3\u7B56\u63D0\u4F9B\u66F4\u591A\u7684\u65B9\u5411\u9009\u62E9\uFF0C\u6BCF\u4E2A\u95EE\u9898\u4F1A\u81EA\u52A8\u8FFD\u52A0\u201C\u5176\u5B83\u201D\u9009\u9879\u4F9B\u7528\u6237\u81EA\u5B9A\u4E49\u8F93\u5165\u3002","\u591A\u4E2A\u95EE\u9898\u65F6\u4E0D\u5F97\u4F9D\u8D56\u524D\u9762\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u82E5\u67D0\u95EE\u9898\u4F9D\u8D56\u53E6\u4E00\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u5E94\u62C6\u6210\u591A\u8F6E\u5206\u522B\u8C03\u7528\u672C\u5DE5\u5177\u3002","\u63D0\u4EA4\u540E\u8FDB\u5165\u7528\u6237\u5BA1\u6279\uFF0C\u5BA1\u6279\u901A\u8FC7\u7684\u9879\u4F1A\u76F4\u63A5\u521B\u5EFA\uFF08\u7ED3\u679C\u968F\u5DE5\u5177\u8FD4\u56DE\uFF09\u3002","\u5982\u679C\u63A8\u8350\u67D0\u4E2A\u9009\u9879\uFF0C\u628A\u5B83\u653E\u5728\u7B2C\u4E00\u4F4D\u5E76\u5728 label \u672B\u5C3E\u52A0\u201C\uFF08\u63A8\u8350\uFF09\u201D\u3002"].join(`
|
|
2
|
-
`);function I(o,e){return{kind:"builtin",toolName:c,exposedName:c,description:f,inputSchema:m(),async execute(n,t){const r=t?.toolCallId;if(!r)return{ok:!1,tool:c,error:"missing toolCallId in execution context"};const s=y(n.questions);if(s.length===0)return{ok:!1,tool:c,error:"questions must be a non-empty array"};o.onQuestion(r,s);const u=e();let i;try{i=await o.waitForAnswer(r,u)}catch(p){const a=p instanceof Error?p.message:String(p);return{ok:!1,tool:c,error:a}}const l=b(s,i);return{ok:!0,tool:c,answers:i,summary:l}}}}function m(){return{type:"object",properties:{questions:{type:"array",description:"\u8981\u63D0\u95EE\u7684\u95EE\u9898\u5217\u8868\uFF08\u81F3\u5C11 1 \u4E2A\uFF0C\u6570\u91CF\u4E0D\u8BBE\u4E0A\u9650\uFF09\uFF1B\u591A\u4E2A\u95EE\u9898\u65F6\u5FC5\u987B\u76F8\u4E92\u72EC\u7ACB\u3001\u5E76\u5217\u63D0\u95EE\uFF0C\u540E\u7EED\u95EE\u9898\u4E0D\u5F97\u4F9D\u8D56\u524D\u9762\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u6709\u4F9D\u8D56\u5173\u7CFB\u65F6\u5E94\u62C6\u6210\u591A\u6B21\u8C03\u7528",minItems:1,items:{type:"object",properties:{question:{type:"string",description:"\u5B8C\u6574\u95EE\u9898\u6587\u672C\uFF0C\u5EFA\u8BAE\u4EE5\u95EE\u53F7\u7ED3\u5C3E"},header:{type:"string",description:"\u226412 \u5B57\u7B26\u7684\u7B80\u77ED\u6807\u7B7E\uFF0C\u7528\u4E8E UI chip \u663E\u793A"},options:{type:"array",description:"2-4 \u4E2A\u4E92\u65A5\u9009\u9879\uFF08multiSelect=true \u65F6\u53EF\u4E0D\u4E92\u65A5\uFF09",minItems:2,maxItems:4,items:{type:"object",properties:{label:{type:"string",description:"1-5 \u8BCD\u7684\u7B80\u77ED\u6807\u7B7E"},description:{type:"string",description:"\u9009\u9879\u542B\u4E49\u6216\u6743\u8861\u8BF4\u660E"},requiresCustomInput:{type:"boolean",description:"\u9009\u4E2D\u6B64\u9879\u65F6 UI \u663E\u793A\u81EA\u5B9A\u4E49\u8F93\u5165\u6846"}},required:["label"]}},multiple:{type:"boolean",description:"\u662F\u5426\u5141\u8BB8\u591A\u9009\uFF1B\u9ED8\u8BA4 false"}},required:["question","options"]}},custom:{type:"boolean",description:"\u662F\u5426\u5141\u8BB8\u7528\u6237\u8865\u5145\u81EA\u5B9A\u4E49\u8F93\u5165\uFF1B\u9ED8\u8BA4 true"}},required:["questions"]}}function y(o){if(!Array.isArray(o)||o.length===0)return[];const e=[];for(const n of o){if(!n||typeof n!="object")continue;const t=n,r=typeof t.question=="string"?t.question.trim():"";if(!r)continue;const s=d(t.options);if(s.length<2)continue;const u=typeof t.header=="string"&&t.header.trim()?t.header.trim():void 0,i=t.multiple===!0||t.multiSelect===!0;e.push({question:r,header:u,options:s,multiple:i})}return e}function d(o){if(!Array.isArray(o))return[];const e=[];for(const n of o){if(typeof n=="string"){const i=n.trim();i&&e.push({label:i});continue}if(!n||typeof n!="object")continue;const t=n,r=typeof t.label=="string"?t.label.trim():typeof t.value=="string"?t.value.trim():typeof t.text=="string"?t.text.trim():"";if(!r)continue;const s=typeof t.description=="string"&&t.description.trim()?t.description.trim():void 0,u=t.requiresCustomInput===!0||t.requireCustomInput===!0||t.allowCustomInput===!0;if(e.push({label:r,...s?{description:s}:{},...u?{requiresCustomInput:u}:{}}),e.length>=4)break}return e}function b(o,e){const n=["\u7528\u6237\u5DF2\u56DE\u7B54\u4E86\u60A8\u7684\u95EE\u9898\uFF1A"];return o.forEach((t,r)=>{const s=String(r),u=e[s]||(t.header?e[t.header]:"")||e[t.question]||"\uFF08\u672A\u586B\u5199\uFF09",i=t.header||t.question;n.push(`\u2022 ${i}\uFF1A${u}`)}),n.join(`
|
|
2
|
+
`);function I(o,e){return{kind:"builtin",toolName:c,exposedName:c,concurrencyMode:"blocking",description:f,inputSchema:m(),async execute(n,t){const r=t?.toolCallId;if(!r)return{ok:!1,tool:c,error:"missing toolCallId in execution context"};const s=y(n.questions);if(s.length===0)return{ok:!1,tool:c,error:"questions must be a non-empty array"};o.onQuestion(r,s);const u=e();let i;try{i=await o.waitForAnswer(r,u)}catch(p){const a=p instanceof Error?p.message:String(p);return{ok:!1,tool:c,error:a}}const l=b(s,i);return{ok:!0,tool:c,answers:i,summary:l}}}}function m(){return{type:"object",properties:{questions:{type:"array",description:"\u8981\u63D0\u95EE\u7684\u95EE\u9898\u5217\u8868\uFF08\u81F3\u5C11 1 \u4E2A\uFF0C\u6570\u91CF\u4E0D\u8BBE\u4E0A\u9650\uFF09\uFF1B\u591A\u4E2A\u95EE\u9898\u65F6\u5FC5\u987B\u76F8\u4E92\u72EC\u7ACB\u3001\u5E76\u5217\u63D0\u95EE\uFF0C\u540E\u7EED\u95EE\u9898\u4E0D\u5F97\u4F9D\u8D56\u524D\u9762\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u6709\u4F9D\u8D56\u5173\u7CFB\u65F6\u5E94\u62C6\u6210\u591A\u6B21\u8C03\u7528",minItems:1,items:{type:"object",properties:{question:{type:"string",description:"\u5B8C\u6574\u95EE\u9898\u6587\u672C\uFF0C\u5EFA\u8BAE\u4EE5\u95EE\u53F7\u7ED3\u5C3E"},header:{type:"string",description:"\u226412 \u5B57\u7B26\u7684\u7B80\u77ED\u6807\u7B7E\uFF0C\u7528\u4E8E UI chip \u663E\u793A"},options:{type:"array",description:"2-4 \u4E2A\u4E92\u65A5\u9009\u9879\uFF08multiSelect=true \u65F6\u53EF\u4E0D\u4E92\u65A5\uFF09",minItems:2,maxItems:4,items:{type:"object",properties:{label:{type:"string",description:"1-5 \u8BCD\u7684\u7B80\u77ED\u6807\u7B7E"},description:{type:"string",description:"\u9009\u9879\u542B\u4E49\u6216\u6743\u8861\u8BF4\u660E"},requiresCustomInput:{type:"boolean",description:"\u9009\u4E2D\u6B64\u9879\u65F6 UI \u663E\u793A\u81EA\u5B9A\u4E49\u8F93\u5165\u6846"}},required:["label"]}},multiple:{type:"boolean",description:"\u662F\u5426\u5141\u8BB8\u591A\u9009\uFF1B\u9ED8\u8BA4 false"}},required:["question","options"]}},custom:{type:"boolean",description:"\u662F\u5426\u5141\u8BB8\u7528\u6237\u8865\u5145\u81EA\u5B9A\u4E49\u8F93\u5165\uFF1B\u9ED8\u8BA4 true"}},required:["questions"]}}function y(o){if(!Array.isArray(o)||o.length===0)return[];const e=[];for(const n of o){if(!n||typeof n!="object")continue;const t=n,r=typeof t.question=="string"?t.question.trim():"";if(!r)continue;const s=d(t.options);if(s.length<2)continue;const u=typeof t.header=="string"&&t.header.trim()?t.header.trim():void 0,i=t.multiple===!0||t.multiSelect===!0;e.push({question:r,header:u,options:s,multiple:i})}return e}function d(o){if(!Array.isArray(o))return[];const e=[];for(const n of o){if(typeof n=="string"){const i=n.trim();i&&e.push({label:i});continue}if(!n||typeof n!="object")continue;const t=n,r=typeof t.label=="string"?t.label.trim():typeof t.value=="string"?t.value.trim():typeof t.text=="string"?t.text.trim():"";if(!r)continue;const s=typeof t.description=="string"&&t.description.trim()?t.description.trim():void 0,u=t.requiresCustomInput===!0||t.requireCustomInput===!0||t.allowCustomInput===!0;if(e.push({label:r,...s?{description:s}:{},...u?{requiresCustomInput:u}:{}}),e.length>=4)break}return e}function b(o,e){const n=["\u7528\u6237\u5DF2\u56DE\u7B54\u4E86\u60A8\u7684\u95EE\u9898\uFF1A"];return o.forEach((t,r)=>{const s=String(r),u=e[s]||(t.header?e[t.header]:"")||e[t.question]||"\uFF08\u672A\u586B\u5199\uFF09",i=t.header||t.question;n.push(`\u2022 ${i}\uFF1A${u}`)}),n.join(`
|
|
3
3
|
`)}function q(o){if(!o||typeof o!="object")return!1;const e=o;return e.ok===!0&&e.tool===c&&typeof e.answers=="object"&&e.answers!==null&&typeof e.summary=="string"}function j(o){const e={ok:!0,tool:c,answers:g(o.answers),summary:o.summary};return JSON.stringify(e)}function g(o){const e={};for(const[n,t]of Object.entries(o))e[n]=t;return e}export{c as ASK_USER_QUESTION_TOOL_NAME,I as createAskUserQuestionTool,q as isAskUserQuestionResult,j as serializeAskUserQuestionResult};
|
|
4
4
|
|
|
@@ -16,6 +16,6 @@ Guidelines:
|
|
|
16
16
|
- Pass sub-agent IDs in 'sub_agent_ids' and/or background command task_ids in 'bg_cmd_ids'
|
|
17
17
|
- At least one of the two arrays must be non-empty
|
|
18
18
|
- Use timeout_ms to bound the wait (default 600000 = 10 minutes; 0 = wait forever)
|
|
19
|
-
- After this returns, call poll_message to receive any pending messages`;function h(o){return{kind:"builtin",toolName:"await_complete",exposedName:"await_complete",description:g,inputSchema:{type:"object",properties:{sub_agent_ids:{type:"array",items:{type:"string"},description:"Array of sub-agent IDs to wait for (from sub_agent). Empty if none."},bg_cmd_ids:{type:"array",items:{type:"string"},description:"Array of background command task_ids to wait for (from Bash wait_seconds or list_background_commands). Empty if none."},timeout_ms:{type:"number",description:"Maximum time to wait in milliseconds, applied to the whole JOIN. 0 = no timeout (wait forever). Default: 600000 (10 minutes)."}},additionalProperties:!1},async execute(
|
|
19
|
+
- After this returns, call poll_message to receive any pending messages`;function h(o){return{kind:"builtin",toolName:"await_complete",exposedName:"await_complete",concurrencyMode:"blocking",description:g,inputSchema:{type:"object",properties:{sub_agent_ids:{type:"array",items:{type:"string"},description:"Array of sub-agent IDs to wait for (from sub_agent). Empty if none."},bg_cmd_ids:{type:"array",items:{type:"string"},description:"Array of background command task_ids to wait for (from Bash wait_seconds or list_background_commands). Empty if none."},timeout_ms:{type:"number",description:"Maximum time to wait in milliseconds, applied to the whole JOIN. 0 = no timeout (wait forever). Default: 600000 (10 minutes)."}},additionalProperties:!1},async execute(n){const l=typeof n.timeout_ms=="number"?n.timeout_ms:6e5,m=n.sub_agent_ids,c=n.bg_cmd_ids,i=Array.isArray(m)?m.filter(t=>typeof t=="string"&&t.trim().length>0):[],u=Array.isArray(c)?c.filter(t=>typeof t=="string"&&t.trim().length>0):[];if(i.length===0&&u.length===0)return{title:"await_complete validation error",output:"Invalid input: provide at least one of 'sub_agent_ids' or 'bg_cmd_ids'.",metadata:{error:!0}};if(i.length>0&&!o.awaitSubAgents)return{title:"await_complete error",output:"sub_agent_ids provided but sub-agent manager is not enabled for this agent.",metadata:{error:!0}};try{let t=[];i.length>0&&o.awaitSubAgents&&(t=await o.awaitSubAgents(i,l)??[]);let a=[];u.length>0&&(a=await o.awaitBgCommands(u,l));const s=[],p=[],d=[];if(t.length>0){s.push(`=== ${t.length} sub-agent(s) ===`);for(const e of t)s.push(`[${e.status}] ${e.subId}`),e.error?(s.push("<task_error>"),s.push(e.error),s.push("</task_error>")):(s.push("<task_result>"),s.push(e.output||"(empty output)"),s.push("</task_result>")),s.push(""),p.push(e)}if(a.length>0){s.push(`=== ${a.length} background command(s) ===`);for(const e of a){if(!e){s.push("[not_found] (unknown task_id)"),s.push(""),d.push(null);continue}const r=[];e.timedOut&&r.push("timed_out"),e.aborted&&r.push("aborted"),s.push(`[${e.status}] ${e.taskId} exit=${e.exitCode??"?"}`+(r.length?` (${r.join(",")})`:"")+(e.outputFilePath?` output=${e.outputFilePath}`:"")),s.push(""),d.push(e)}}return{title:`awaited ${t.length+a.length} item(s)`,output:s.join(`
|
|
20
20
|
`),metadata:{total:t.length+a.length,sub_agents:p,bg_cmds:d}}}catch(t){return{title:"await_complete error",output:`Failed to await: ${t instanceof Error?t.message:String(t)}`,metadata:{error:!0}}}}}}export{h as createAwaitCompleteTool};
|
|
21
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{spawn as E}from"node:child_process";import{promises as S}from"node:fs";import{createInterface as
|
|
2
|
-
`),this.finalizeTask(u,-1,null,"spawn_error")}),this.persistTaskSafe(u),{taskId:n,completion:l}}async spawnAndAttach(t,i){const e=
|
|
3
|
-
`),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),s.on("exit",(o,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,o,a,p??void 0)};t.exitGraceTimer=setTimeout(u,
|
|
1
|
+
import{spawn as E,spawnSync as R}from"node:child_process";import{promises as S}from"node:fs";import{createInterface as O}from"node:readline";import{randomBytes as M}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,W=3*60*1e3,$=24*60*60*1e3,k=5*1e3,X=64*1024,q=2e3,C=50*1024*1024,_=16,P=256,K=256*1024,U=100,Y=5e3,H=256,v=20,B=5*60*1e3,L=1024*1024,x=`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(e.size>=_)throw new I("too_many_tasks",`Agent ${i} has ${e.size} background tasks (limit ${_}). Kill or wait for completion first.`);if(this.globalCount()>=P)throw new I("global_limit_reached",`Global background task limit ${P} reached.`);const n=this.generateTaskId(e),r=t.timeoutMs===void 0?W:t.timeoutMs>0?Math.min(t.timeoutMs,$):0,s=typeof t.inactivityTimeoutMs=="number"&&Number.isFinite(t.inactivityTimeoutMs)&&t.inactivityTimeoutMs>0?t.inactivityTimeoutMs:0,o=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:s,child:null,ringBuffer:new z,outputPath:o,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:x,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),r=process.platform==="win32",s=E(e.command,[...e.argsPrefix,n.commandText],{cwd:i.cwd,env:process.env,detached:!r,stdio:["ignore","pipe","pipe"],windowsVerbatimArguments:r,windowsHide:!0});t.child=s,t.commandFilePath=n.commandFilePath,t.commandLineCount=n.commandLineCount,t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),s.stdout?.on("data",o=>{const a=A(t.stdoutDecoder,o);this.onOutput(t,"stdout",a)}),s.stderr?.on("data",o=>{const a=A(t.stderrDecoder,o);this.onOutput(t,"stderr",a)}),s.on("error",o=>{this.onOutput(t,"stderr",`[ACode] child error: ${o.message}
|
|
3
|
+
`),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),s.on("exit",(o,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,o,a,p??void 0)};t.exitGraceTimer=setTimeout(u,q),t.exitGraceTimer.unref?.()}),s.on("close",(o,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,o,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 o of t.outputSubscribers)try{o.onLine({stream:i,text:e,seq:n})}catch{}const r=i==="stdout"?"stdoutText":"stderrText";if(Buffer.byteLength(t[r],"utf-8")<L){const o=L-Buffer.byteLength(t[r],"utf-8");t[r]+=o>0?e.slice(0,o):""}if(t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),t.status==="running"){const o=Date.now(),a=t.outputBytes-t.lastPulseBytes;(o-t.lastPulseAt>=k||a>=X)&&(t.lastPulseAt=o,t.lastPulseBytes=t.outputBytes,this.persistTaskSafe(t))}if(t.outputBytes>=C){if(!t.outputTruncated){t.outputTruncated=!0;const o=`
|
|
4
4
|
[ACode] output truncated at 50MB
|
|
5
5
|
`;t.ringBuffer.push(o),this.appendToFile(t,o)}t.ringBuffer.push(e);return}t.ringBuffer.push(e),t.onOutput?.({stream:i,text:e});const s=(e.match(/\n/g)??[]).length;t.outputLineCount+=s,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>C?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 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),K),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>Y)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>q){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=R({input:w.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const f of m){const d=l;if(l++,i==="grep"&&d-e>=U&&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)},F),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"){const n="/F",{spawnSync:r}=require("node:child_process");return r("taskkill",["/PID",String(e),"/T",n],{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(V(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 V(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 ot=new H;export{H as BackgroundCommandManager,I as BackgroundTaskError,ot as backgroundCommandManager};
|
|
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};
|
|
7
7
|
|
|
@@ -13,6 +13,6 @@ Modes:
|
|
|
13
13
|
- tail (default): last N lines (live view; no paging anchor).
|
|
14
14
|
|
|
15
15
|
Every returned line is { line, text }: line is the 0-based absolute line number in the command output. Use the line number to locate the same content in the output / files, and lines(start_line=..., line_size=...) to expand around a match.
|
|
16
|
-
Output capped at 256KB / line_size lines per call; truncated=true means more is available (grep: continue from nextStartLine).`;function u(a,o){return{kind:"builtin",toolName:"list_background_commands",exposedName:"list_background_commands",description:r,inputSchema:{type:"object",properties:{status_filter:{type:"string",enum:["running","completed","failed","killed","all"],description:"Filter by status. Defaults to 'all'.",default:"all"}},additionalProperties:!1},async execute(e){const n=typeof e.status_filter=="string"?e.status_filter:"all",i=o.manager.listTasks(a,n);return{title:`${i.length} background commands`,output:JSON.stringify(i.map(t=>({taskId:t.taskId,status:t.status,command:t.commandPreview,cwd:t.cwd,startedAt:new Date(t.startedAt).toISOString(),finishedAt:t.finishedAt?new Date(t.finishedAt).toISOString():null,exitCode:t.exitCode,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes})),null,2),metadata:{tasks:i,count:i.length}}}}}function l(a,o){return{kind:"builtin",toolName:"kill_background_command",exposedName:"kill_background_command",description:s,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id returned by Bash(wait_seconds=...) or list_background_commands."}},required:["task_id"],additionalProperties:!1},async execute(e){const n=typeof e.task_id=="string"?e.task_id:"",i=await o.manager.killTask(a,n);return{title:`kill ${n}: ${i.status}`,output:JSON.stringify(i,null,2),metadata:i}}}}function c(a,o){return{kind:"builtin",toolName:"read_background_output",exposedName:"read_background_output",description:d,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id to read output from."},mode:{type:"string",enum:["lines","grep","tail"],description:"lines=read range by absolute line numbers, grep=search matching lines with context, tail=last N lines (default).",default:"tail"},line_size:{type:"integer",description:"lines/tail: number of lines to return; grep: max total lines returned (including context). Default 100, max 100.",default:100,minimum:1,maximum:100},start_line:{type:"integer",description:"lines: 0-based absolute line to start from; grep: scan start line (pass nextStartLine to page).",minimum:0},pattern:{type:"string",description:"grep: regex pattern (JS syntax, case-insensitive)."},context_before:{type:"integer",description:"grep: lines of context before each match (0-20, default 0).",minimum:0,maximum:20,default:0},context_after:{type:"integer",description:"grep: lines of context after each match (0-20, default 0).",minimum:0,maximum:20,default:0},include_metadata:{type:"boolean",description:"Include totalLines/totalBytes/truncated/outputTruncated/hasMore/nextStartLine/outputFile in response.",default:!0}},required:["task_id"],additionalProperties:!1},async execute(e){const n=await o.manager.readOutput(a,{task_id:typeof e.task_id=="string"?e.task_id:"",mode:e.mode==="lines"||e.mode==="grep"||e.mode==="tail"?e.mode:"tail",line_size:typeof e.line_size=="number"?e.line_size:void 0,start_line:typeof e.start_line=="number"?e.start_line:void 0,pattern:typeof e.pattern=="string"?e.pattern:void 0,context_before:typeof e.context_before=="number"?e.context_before:void 0,context_after:typeof e.context_after=="number"?e.context_after:void 0,include_metadata:e.include_metadata!==!1});return{title:`read ${n.taskId} (${n.mode}): ${n.lineCount} lines`,output:n.lines.map(({line:i,text:t})=>`${i}: ${t}`).join(`
|
|
16
|
+
Output capped at 256KB / line_size lines per call; truncated=true means more is available (grep: continue from nextStartLine).`;function u(a,o){return{kind:"builtin",toolName:"list_background_commands",exposedName:"list_background_commands",concurrencyMode:"read",resourceKeys:()=>[{key:"runtime:background",access:"read"}],description:r,inputSchema:{type:"object",properties:{status_filter:{type:"string",enum:["running","completed","failed","killed","all"],description:"Filter by status. Defaults to 'all'.",default:"all"}},additionalProperties:!1},async execute(e){const n=typeof e.status_filter=="string"?e.status_filter:"all",i=o.manager.listTasks(a,n);return{title:`${i.length} background commands`,output:JSON.stringify(i.map(t=>({taskId:t.taskId,status:t.status,command:t.commandPreview,cwd:t.cwd,startedAt:new Date(t.startedAt).toISOString(),finishedAt:t.finishedAt?new Date(t.finishedAt).toISOString():null,exitCode:t.exitCode,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes})),null,2),metadata:{tasks:i,count:i.length}}}}}function l(a,o){return{kind:"builtin",toolName:"kill_background_command",exposedName:"kill_background_command",description:s,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id returned by Bash(wait_seconds=...) or list_background_commands."}},required:["task_id"],additionalProperties:!1},async execute(e){const n=typeof e.task_id=="string"?e.task_id:"",i=await o.manager.killTask(a,n);return{title:`kill ${n}: ${i.status}`,output:JSON.stringify(i,null,2),metadata:i}}}}function c(a,o){return{kind:"builtin",toolName:"read_background_output",exposedName:"read_background_output",concurrencyMode:"read",resourceKeys:()=>[{key:"runtime:background",access:"read"}],description:d,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id to read output from."},mode:{type:"string",enum:["lines","grep","tail"],description:"lines=read range by absolute line numbers, grep=search matching lines with context, tail=last N lines (default).",default:"tail"},line_size:{type:"integer",description:"lines/tail: number of lines to return; grep: max total lines returned (including context). Default 100, max 100.",default:100,minimum:1,maximum:100},start_line:{type:"integer",description:"lines: 0-based absolute line to start from; grep: scan start line (pass nextStartLine to page).",minimum:0},pattern:{type:"string",description:"grep: regex pattern (JS syntax, case-insensitive)."},context_before:{type:"integer",description:"grep: lines of context before each match (0-20, default 0).",minimum:0,maximum:20,default:0},context_after:{type:"integer",description:"grep: lines of context after each match (0-20, default 0).",minimum:0,maximum:20,default:0},include_metadata:{type:"boolean",description:"Include totalLines/totalBytes/truncated/outputTruncated/hasMore/nextStartLine/outputFile in response.",default:!0}},required:["task_id"],additionalProperties:!1},async execute(e){const n=await o.manager.readOutput(a,{task_id:typeof e.task_id=="string"?e.task_id:"",mode:e.mode==="lines"||e.mode==="grep"||e.mode==="tail"?e.mode:"tail",line_size:typeof e.line_size=="number"?e.line_size:void 0,start_line:typeof e.start_line=="number"?e.start_line:void 0,pattern:typeof e.pattern=="string"?e.pattern:void 0,context_before:typeof e.context_before=="number"?e.context_before:void 0,context_after:typeof e.context_after=="number"?e.context_after:void 0,include_metadata:e.include_metadata!==!1});return{title:`read ${n.taskId} (${n.mode}): ${n.lineCount} lines`,output:n.lines.map(({line:i,text:t})=>`${i}: ${t}`).join(`
|
|
17
17
|
`),metadata:n}}}}export{l as createKillBackgroundCommandTool,u as createListBackgroundCommandsTool,c as createReadBackgroundOutputTool};
|
|
18
18
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type CommandOutputSink } from "./builtins/shellCommandRunner.js";
|
|
2
|
+
import { type StaleFileGuardDeps } from "./read-file-state.js";
|
|
2
3
|
import type { BackgroundCommandManager, BackgroundCompletedEvent } from "./background-command-manager.js";
|
|
4
|
+
import type { ResourceClaim, ResourceScope, ToolConcurrencyMode } from "./tool-concurrency/types.js";
|
|
3
5
|
|
|
4
6
|
export interface BackgroundToolDeps {
|
|
5
7
|
manager: BackgroundCommandManager;
|
|
@@ -20,6 +22,14 @@ export interface ACodeBuiltInTool {
|
|
|
20
22
|
inputSchema: JsonObject;
|
|
21
23
|
|
|
22
24
|
category?: string;
|
|
25
|
+
|
|
26
|
+
group?: string;
|
|
27
|
+
|
|
28
|
+
concurrencyMode?: ToolConcurrencyMode;
|
|
29
|
+
|
|
30
|
+
resourceKeys?: (args: JsonObject) => ResourceClaim[];
|
|
31
|
+
|
|
32
|
+
resourceScope?: ResourceScope;
|
|
23
33
|
execute(args: JsonObject, context?: ACodeBuiltInToolExecutionContext): Promise<unknown>;
|
|
24
34
|
}
|
|
25
35
|
export interface ACodeBuiltInToolExecutionContext {
|
|
@@ -32,7 +42,7 @@ export interface ACodeBuiltInToolExecutionContext {
|
|
|
32
42
|
|
|
33
43
|
export type ToolPermissionLevel = "allow" | "ask" | "deny";
|
|
34
44
|
|
|
35
|
-
export type ToolPermissionCapability = "fileReadOutside" | "commandExecution" | "fileModification";
|
|
45
|
+
export type ToolPermissionCapability = "fileReadOutside" | "commandExecution" | "fileModification" | "fileModificationOutside";
|
|
36
46
|
export type ToolPermissionPolicy = Record<ToolPermissionCapability, ToolPermissionLevel>;
|
|
37
47
|
|
|
38
48
|
export declare const DEFAULT_TOOL_PERMISSION_POLICY: ToolPermissionPolicy;
|
|
@@ -67,8 +77,27 @@ export interface ResolvedWorkspaceTarget {
|
|
|
67
77
|
workspaceRoot: string;
|
|
68
78
|
requestedPath: string;
|
|
69
79
|
targetPath: string;
|
|
80
|
+
|
|
81
|
+
outsideWorkspace?: boolean;
|
|
70
82
|
}
|
|
71
83
|
export declare function ensureInsideWorkspace(workspaceRoot: string, targetPath: string): string;
|
|
72
84
|
|
|
85
|
+
export declare function buildFileResourceKey(workingDirectory: string, rawFilePath: string): string;
|
|
86
|
+
|
|
87
|
+
export declare function fileResourceClaim(workingDirectory: string, rawFilePath: string, access: "read" | "write"): ResourceClaim;
|
|
88
|
+
|
|
73
89
|
export declare function resolveWorkspaceTarget(workingDirectory: string, rawFilePath: string): Promise<ResolvedWorkspaceTarget>;
|
|
74
|
-
|
|
90
|
+
|
|
91
|
+
export declare const EDIT_FAILURE_SNAPSHOT_MAX_BYTES: number;
|
|
92
|
+
|
|
93
|
+
export interface EditFailureSnapshot {
|
|
94
|
+
|
|
95
|
+
path: string;
|
|
96
|
+
|
|
97
|
+
bytes: number;
|
|
98
|
+
|
|
99
|
+
content: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export declare function getEditFailureSnapshot(error: unknown): EditFailureSnapshot | undefined;
|
|
103
|
+
export declare function createBuiltInFileTools(workingDirectory: string, agentId?: string, onFileChange?: OnAgentFileChange, backgroundDeps?: BackgroundToolDeps, permissionController?: ToolPermissionController, staleGuard?: StaleFileGuardDeps): ACodeBuiltInTool[];
|