aws-runtime-bridge 1.9.96 → 1.9.98

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/dist/config.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  export declare const port: number;
4
4
 
5
5
  export declare const DEFAULT_SCHEDULER_BASE_URL = "http://localhost:8080";
6
- export type SchedulerBaseUrlSource = "request" | "instance-id-match" | "env" | "single-auto-register-target" | "default" | "ambiguous-auto-register-targets";
6
+ export type SchedulerBaseUrlSource = "request" | "instance-id-match" | "env" | "single-auto-register-target" | "default" | "ambiguous-auto-register-targets" | "token-match";
7
7
  export interface SchedulerBaseUrlResolution {
8
8
  url: string;
9
9
  source: SchedulerBaseUrlSource;
@@ -1,3 +1,3 @@
1
- import{Router as D}from"express";import I from"axios";import{spawn as N}from"node:child_process";import{createHash as $,timingSafeEqual as F}from"node:crypto";import{getBridgeVersion as b,resolveSchedulerBaseUrlFrom as M}from"../config.js";import{validateToken as S,validateTokenOrPanelSession as j}from"../middleware/auth.js";import{getConfiguredConnectionKeys as _,requestRuntimeAccessTokenRefreshForServer as z}from"../services/auto-register.js";import{discoverCcSwitchConfiguredItems as H,loadCcSwitchSdk as V}from"../services/cc-switch-sdk.js";import{initInstance as X,syncLegacyStateFromSdk as W}from"../services/instance-init-service.js";import{createBridgeLifecycleOperation as E,deliverPendingBridgeLifecycleNotifications as C,upsertBridgeLifecycleOperation as y}from"../services/lifecycle-state.js";import{loadInstanceState as x,saveInstanceState as L}from"../services/instance-state.js";import{getRuntimeAccessToken as G}from"../services/runtime-binding.js";import{detectToolStatuses as R,ensureToolsInstalled as J,SUPPORTED_INSTALLABLE_TOOLS as v,SUPPORTED_UNINSTALLABLE_TOOLS as A,uninstallTools as Y}from"../services/tool-installer.js";import{createLogger as Q}from"../utils/logger.js";const l=Q("instance"),Z=["acode","claude","opencode","codex"];let U=null;function je(r){U=r}const d=D();function ee(){return process.platform==="win32"?"npm.cmd":"npm"}function q(r){return r.length>16e3?`${r.slice(r.length-16e3)}
2
- [output truncated]`:r}function te(r,t){return new Promise((e,s)=>{const n=N(r,t,{shell:!1,windowsHide:!0});let a="",i="";n.stdout.on("data",o=>{a+=o.toString("utf8")}),n.stderr.on("data",o=>{i+=o.toString("utf8")}),n.on("error",s),n.on("close",o=>{e({command:r,args:t,exitCode:o,stdout:q(a),stderr:q(i)})})})}function K(r){return String(r||"").trim()||void 0}function O(r){const t=r.get("host");if(t)return`${r.protocol}://${t}`}async function re(r){const t={...r.operation,status:"running",message:"Bridge \u66F4\u65B0\u6B63\u5728\u540E\u53F0\u6267\u884C\u3002"};await y(t);try{l.info("[update-bridge] \u6B63\u5728\u540E\u53F0\u66F4\u65B0 aws-runtime-bridge \u5230\u6700\u65B0\u7248\u672C");const e=await te(r.command,r.args),s=e.exitCode===0;await y({...t,status:s?"succeeded":"failed",message:s?"Bridge \u66F4\u65B0\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F\u5B9E\u4F8B Bridge \u4EE5\u52A0\u8F7D\u65B0\u7248\u672C\u3002":`Bridge \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${e.exitCode??"unknown"}`,error:s?void 0:e.stderr||e.stdout}),await C()}catch(e){const s=e;l.error("[update-bridge] background update failed:",s),await y({...t,status:"failed",message:"Bridge \u66F4\u65B0\u5931\u8D25\u3002",error:s.message||"update bridge failed"}),await C()}}async function P(r){const t=await import("node:fs/promises"),e=await import("node:path"),s=await import("node:os"),n=e.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");if(r){await t.mkdir(e.dirname(n),{recursive:!0}),await t.writeFile(n,JSON.stringify({preserveSessions:!0,timestamp:new Date().toISOString()}),"utf-8"),l.info(`[prepare-restart] \u5DF2\u521B\u5EFA\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6: ${n}`);return}try{await t.unlink(n),l.info("[prepare-restart] \u5DF2\u5220\u9664\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6")}catch{}}function se(r){return $("md5").update(r,"utf8").digest("hex")}function ne(r){const t=r.trim().toLowerCase();if(!/^[a-f0-9]{32}$/.test(t))return!1;const e=Buffer.from(t,"hex");return _().map(s=>s.trim()).filter(Boolean).some(s=>{const n=Buffer.from(se(s),"hex");return n.length===e.length&&F(n,e)})}function oe(r){const e=_().length>0,s=b();return e?ne(r)?{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!0}}:{status:401,body:{ok:!1,error:"connection_key_mismatch",version:s,connectionKeyMatched:!1,connectionKeyRequired:!0}}:{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!1}}}function ae(r,t){const s=t.trim()||"http://localhost:8080",n=/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(?:\/|$)/i.test(s);return{ok:!1,error:r.message||"scheduler ping failed",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:s,hint:n?"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u5728 bridge \u542F\u52A8\u73AF\u5883\u4E2D\u5C06 AWS_RUNTIME_SCHEDULER_BASE_URL \u914D\u7F6E\u4E3A\u8BE5\u5B9E\u4F8B\u53EF\u8BBF\u95EE\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u4F8B\u5982 http://<server-host>:7380\u3002":"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 AWS_RUNTIME_SCHEDULER_BASE_URL \u6307\u5411\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\u53EF\u4ECE bridge \u673A\u5668\u8BBF\u95EE\uFF0C\u5E76\u4E14\u8FD0\u884C\u65F6\u8BBF\u95EE\u4EE4\u724C\u4ECD\u6709\u6548\u3002"}}function ie(r){if(typeof r!="object"||r===null)return;const t=Reflect.get(r,"response");if(typeof t!="object"||t===null)return;const e=Reflect.get(t,"status");return typeof e=="number"?e:void 0}async function le(r,t){try{const e=await I.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":t}});return{schedulerReachable:e.status>=200&&e.status<300,tokenRefreshed:!1}}catch(e){if(ie(e)!==401)throw e;const s=await z(r);if(!s.success||!s.runtimeAccessToken)throw e;const n=await I.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":s.runtimeAccessToken}});return{schedulerReachable:n.status>=200&&n.status<300,tokenRefreshed:!0}}}function ce(r){return{ok:!1,error:"ambiguous_scheduler_base_url",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:"",autoRegisterTargetUrls:r,hint:"Multiple autoRegisterTargets.serverUrl values were found, so bridge cannot safely infer which scheduler /runtime/ping should call back. Set AWS_RUNTIME_SCHEDULER_BASE_URL explicitly, or use future schedulerId/token-bound multi-scheduler routing."}}function ue(r){return Array.from(new Set([...r,...Z].map(t=>String(t||"").trim().toLowerCase()).filter(Boolean)))}function de(r,t){const e=r.filter(s=>t[s]?.installed);return e.length===0?"":e.map(s=>{const n=t[s],a=n?.error||n?.executable||"command is still available";return`${s}: ${a}`}).join("; ")}function T(r){return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[t,{installed:!!e?.installed,version:e?.version||null,executable:e?.executable||null,error:e?.error||null}]))}let k=null;const ge=3e5;async function fe(){return(await I.get("https://registry.npmjs.org/aws-runtime-bridge/latest",{timeout:1e4,responseType:"json"})).data?.version||"unknown"}d.get("/check-update",async(r,t)=>{try{let e;k&&Date.now()-k.ts<ge?e=k.latestVersion:(e=await fe(),k={latestVersion:e,ts:Date.now()});const s=b(),n=e!=="unknown"&&s!=="unknown"&&e!==s;t.json({ok:!0,hasUpdate:n,currentVersion:s,latestVersion:e})}catch(e){const s=e;l.warn("[check-update] Failed to query npm registry:",s.message),t.json({ok:!1,hasUpdate:!1,currentVersion:b(),latestVersion:null,error:s.message||"failed to check for updates"})}}),d.get("/healthz",(r,t)=>{t.json({ok:!0,runtimeBridge:"healthy"})}),d.get("/connection-check",(r,t)=>{const e=String(r.header("X-Bridge-Connection-Key-MD5")||""),s=oe(e);t.status(s.status).json(s.body)}),d.get("/ping",S,async(r,t)=>{const e=M(r.header("X-Scheduler-Base-Url"),r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name"));if(e.source==="ambiguous-auto-register-targets"){t.status(400).json(ce(e.ambiguousTargetUrls));return}const s=e.url;try{const n=G(s);if(!n){t.status(401).json({ok:!1,error:"runtime_access_token_required"});return}const a=await le(s,n);t.json({ok:!0,runtimeBridge:"healthy",version:b(),schedulerReachable:a.schedulerReachable,tokenRefreshed:a.tokenRefreshed,schedulerBaseUrl:s,instanceId:e.instanceId,instanceName:e.instanceName})}catch(n){const a=n;t.status(502).json(ae(a,s))}}),d.post("/init-instance",S,async(r,t)=>{const{agentId:e,workspacePath:s,skillEnabled:n,mcpEnabled:a,ccSwitchEnabledTools:i,skillPackage:o,skillPackages:c,mcpServers:m}=r.body||{};if(!e||!s){t.status(400).json({error:"agentId and workspacePath are required"});return}try{l.info("[init-instance] Request received",{agentId:String(e),workspacePath:String(s),skillEnabled:n,mcpEnabled:a,ccSwitchEnabledTools:i});const u=await X(e,s,{skillEnabled:n,mcpEnabled:a,ccSwitchEnabledTools:i,skillPackage:o,skillPackages:c,mcpServers:m});l.info("[init-instance] Initialization completed",{agentId:String(e),ok:u.ok,logCount:u.logs?.length||0,enabledTools:u.enabledTools}),t.json(u)}catch(u){const g=u;l.error("[init-instance] Initialization failed",{agentId:String(e),workspacePath:String(s),error:g?.message||String(u),stack:g?.stack||null}),t.status(400).json({error:g?.message||"initialize instance failed"})}}),d.post("/cc-switch/state",S,async(r,t)=>{const{agentId:e}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}try{l.info("[cc-switch/state] Request received",{agentId:String(e)});const s=await V(e),n=await H(s),i=(await W(e,s)).state,o=ue(i.enabledTools||[]);l.info("[cc-switch/state] Detecting SDK statuses",{agentId:String(e),detectionTargets:o});const c=await R(o);l.info("[cc-switch/state] SDK status detection completed",{agentId:String(e),toolStatus:T(c),importedMcpCount:n.mcpServers,importedSkillCount:n.skills}),t.json({ok:!0,agentId:String(e),imported:{mcpServers:n.mcpServers,skills:n.skills},state:{...i,toolStatus:c}})}catch(s){const n=s;l.error("[cc-switch/state] Failed to load state",{agentId:String(e),error:n?.message||String(s),stack:n?.stack||null}),t.status(400).json({error:n?.message||"load state failed"})}}),d.post("/cc-switch/install-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(o=>String(o||"").trim().toLowerCase()).filter(Boolean):[];l.info("[cc-switch/install-tools] Request received",{agentId:String(e),requestedTools:n});const a=new Set(v),i=n.filter(o=>a.has(o));if(l.info("[cc-switch/install-tools] Request normalized",{agentId:String(e),requestedTools:n,installableTools:i,supportedInstallableTools:v}),i.length===0){l.warn("[cc-switch/install-tools] No supported installable tools requested",{agentId:String(e),requestedTools:n}),t.status(400).json({error:`tools must include ${v.join(", ")}`});return}try{const o=await x(e),c=Array.from(new Set([...o.enabledTools||[],...i]));l.info("[cc-switch/install-tools] Starting installer",{agentId:String(e),installableTools:i,previousEnabledTools:o.enabledTools||[],nextEnabledTools:c});const m=await J(i);l.info("[cc-switch/install-tools] Installer completed",{agentId:String(e),toolStatus:T(m)});const u=await R(c);l.info("[cc-switch/install-tools] Refreshed enabled SDK statuses",{agentId:String(e),refreshedToolStatus:T(u)});const g={...u,...m},p=await L(e,{...o,enabledTools:c,toolStatus:g}),w=new Set(i),h=Object.entries(g).filter(([f])=>w.has(f)).filter(([,f])=>!f.installed).map(([f,B])=>({tool:f,error:B.error||null}));h.length>0&&l.warn("[cc-switch/install-tools] Some SDKs remain unavailable",{agentId:String(e),failedTools:h}),l.info("[cc-switch/install-tools] State saved",{agentId:String(e),enabledTools:p.enabledTools,toolStatus:T(g)}),t.json({ok:h.length===0,agentId:String(e),toolStatus:g,failedTools:h,state:p})}catch(o){const c=o;l.error("[cc-switch/install-tools] Install route failed",{agentId:String(e),requestedTools:n,installableTools:i,error:c?.message||String(o),stack:c?.stack||null}),t.status(400).json({error:c?.message||"install tools failed"})}}),d.post("/cc-switch/uninstall-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(o=>String(o||"").trim().toLowerCase()).filter(Boolean):[],a=new Set(A),i=n.filter(o=>a.has(o));if(i.length===0){t.status(400).json({error:`tools must include ${A.join(", ")}`});return}try{const o=await x(e),c=new Set(i),m=(o.enabledTools||[]).filter(B=>!c.has(String(B||"").trim().toLowerCase())),u=await Y(i),p={...await R([...m,...i]),...u},w=de(i,p),h=w?o.enabledTools||[]:m,f=await L(e,{...o,enabledTools:h,toolStatus:p});if(w){t.status(400).json({ok:!1,agentId:String(e),error:`uninstall incomplete: ${w}`,toolStatus:p,state:f});return}t.json({ok:!0,agentId:String(e),toolStatus:p,state:f})}catch(o){const c=o;t.status(400).json({error:c?.message||"uninstall tools failed"})}}),d.post("/prepare-restart",S,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{};try{l.info(`[prepare-restart] \u6536\u5230\u51C6\u5907\u91CD\u542F\u901A\u77E5\uFF0CpreserveSessions=${e}`),await P(!!e),t.json({ok:!0,preserveSessions:e,message:e?"Bridge will preserve sessions for aws-mcp-server restart":"Bridge will clean up sessions on shutdown"})}catch(s){const n=s;l.error("[prepare-restart] Error:",n),t.status(500).json({error:n.message})}}),d.post("/update-bridge",j,async(r,t)=>{const e=ee(),s=["install","-g","aws-runtime-bridge@latest","--omit=optional"],n=K(r.body?.requestedByUserId);try{if(!n){t.status(400).json({error:"requestedByUserId is required"});return}const a=E({operationType:"update",requestedByUserId:n,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:O(r),status:"accepted",message:"Bridge \u66F4\u65B0\u8BF7\u6C42\u5DF2\u6536\u5230\uFF0C\u5C06\u5728\u540E\u53F0\u6267\u884C\u3002"});await y(a),re({command:e,args:s,operation:a}),t.json({ok:!0,operationId:a.operationId,message:"Bridge \u66F4\u65B0\u5DF2\u5728\u540E\u53F0\u5F00\u59CB\uFF0C\u5B8C\u6210\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002"})}catch(a){const i=a;l.error("[update-bridge] Error:",i),t.status(500).json({error:i.message||"update bridge failed"})}}),d.post("/restart-bridge",j,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{},s=K(r.body?.requestedByUserId);try{if(!s){t.status(400).json({error:"requestedByUserId is required"});return}l.info(`[restart-bridge] \u6536\u5230\u5B9E\u4F8B Bridge \u91CD\u542F\u8BF7\u6C42\uFF0CpreserveSessions=${e}`);const n=E({operationType:"restart",requestedByUserId:s,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:O(r),status:"accepted",message:"Bridge \u5DF2\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u53D1\u9001\u786E\u8BA4\u901A\u77E5\u3002"});await y(n),await P(!!e),t.json({ok:!0,operationId:n.operationId,preserveSessions:!!e,message:"Bridge \u6B63\u5728\u4F18\u96C5\u9000\u51FA\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002\u82E5\u672A\u7531\u5B88\u62A4\u8FDB\u7A0B\u7BA1\u7406\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u542F\u52A8\u3002"}),setTimeout(()=>{if(process.exitCode=42,U){U("PANEL_RESTART",!!e).catch(a=>{const i=a;l.error("[restart-bridge] graceful shutdown failed:",i),process.exit(1)});return}process.exit(42)},250)}catch(n){const a=n;l.error("[restart-bridge] Error:",a),t.status(500).json({error:a.message||"restart bridge failed"})}});export{ce as buildAmbiguousSchedulerBaseUrlResponse,oe as buildConnectionCheckResponse,ae as buildSchedulerPingFailureResponse,ue as buildToolStatusDetectionTargets,de as buildUninstallFailureMessage,d as instanceRouter,je as setGracefulShutdownFn,T as summarizeToolStatus};
1
+ import{Router as D}from"express";import I from"axios";import{spawn as N}from"node:child_process";import{createHash as $,timingSafeEqual as F}from"node:crypto";import{getBridgeVersion as y,resolveSchedulerBaseUrlFrom as M}from"../config.js";import{validateToken as S,validateTokenOrPanelSession as j}from"../middleware/auth.js";import{getConfiguredConnectionKeys as _,getRegistrationState as z,requestRuntimeAccessTokenRefreshForServer as H,resolveSchedulerBaseUrlFromToken as X}from"../services/auto-register.js";import{discoverCcSwitchConfiguredItems as V,loadCcSwitchSdk as W}from"../services/cc-switch-sdk.js";import{initInstance as G,syncLegacyStateFromSdk as J}from"../services/instance-init-service.js";import{createBridgeLifecycleOperation as E,deliverPendingBridgeLifecycleNotifications as C,upsertBridgeLifecycleOperation as b}from"../services/lifecycle-state.js";import{loadInstanceState as x,saveInstanceState as L}from"../services/instance-state.js";import{getRuntimeAccessToken as Y}from"../services/runtime-binding.js";import{detectToolStatuses as R,ensureToolsInstalled as Q,SUPPORTED_INSTALLABLE_TOOLS as U,SUPPORTED_UNINSTALLABLE_TOOLS as A,uninstallTools as Z}from"../services/tool-installer.js";import{createLogger as ee}from"../utils/logger.js";const l=ee("instance"),te=["acode","claude","opencode","codex"];let v=null;function Ee(r){v=r}const d=D();function re(){return process.platform==="win32"?"npm.cmd":"npm"}function q(r){return r.length>16e3?`${r.slice(r.length-16e3)}
2
+ [output truncated]`:r}function se(r,t){return new Promise((e,s)=>{const n=N(r,t,{shell:!1,windowsHide:!0});let a="",i="";n.stdout.on("data",o=>{a+=o.toString("utf8")}),n.stderr.on("data",o=>{i+=o.toString("utf8")}),n.on("error",s),n.on("close",o=>{e({command:r,args:t,exitCode:o,stdout:q(a),stderr:q(i)})})})}function O(r){return String(r||"").trim()||void 0}function K(r){const t=r.get("host");if(t)return`${r.protocol}://${t}`}async function ne(r){const t={...r.operation,status:"running",message:"Bridge \u66F4\u65B0\u6B63\u5728\u540E\u53F0\u6267\u884C\u3002"};await b(t);try{l.info("[update-bridge] \u6B63\u5728\u540E\u53F0\u66F4\u65B0 aws-runtime-bridge \u5230\u6700\u65B0\u7248\u672C");const e=await se(r.command,r.args),s=e.exitCode===0;await b({...t,status:s?"succeeded":"failed",message:s?"Bridge \u66F4\u65B0\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F\u5B9E\u4F8B Bridge \u4EE5\u52A0\u8F7D\u65B0\u7248\u672C\u3002":`Bridge \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${e.exitCode??"unknown"}`,error:s?void 0:e.stderr||e.stdout}),await C()}catch(e){const s=e;l.error("[update-bridge] background update failed:",s),await b({...t,status:"failed",message:"Bridge \u66F4\u65B0\u5931\u8D25\u3002",error:s.message||"update bridge failed"}),await C()}}async function P(r){const t=await import("node:fs/promises"),e=await import("node:path"),s=await import("node:os"),n=e.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");if(r){await t.mkdir(e.dirname(n),{recursive:!0}),await t.writeFile(n,JSON.stringify({preserveSessions:!0,timestamp:new Date().toISOString()}),"utf-8"),l.info(`[prepare-restart] \u5DF2\u521B\u5EFA\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6: ${n}`);return}try{await t.unlink(n),l.info("[prepare-restart] \u5DF2\u5220\u9664\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6")}catch{}}function oe(r){return $("md5").update(r,"utf8").digest("hex")}function ae(r){const t=r.trim().toLowerCase();if(!/^[a-f0-9]{32}$/.test(t))return!1;const e=Buffer.from(t,"hex");return _().map(s=>s.trim()).filter(Boolean).some(s=>{const n=Buffer.from(oe(s),"hex");return n.length===e.length&&F(n,e)})}function ie(r){const e=_().length>0,s=y();return e?ae(r)?{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!0}}:{status:401,body:{ok:!1,error:"connection_key_mismatch",version:s,connectionKeyMatched:!1,connectionKeyRequired:!0}}:{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!1}}}function le(r,t){const s=t.trim()||"http://localhost:8080",n=/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(?:\/|$)/i.test(s);return{ok:!1,error:r.message||"scheduler ping failed",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:s,hint:n?"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u5728 bridge \u542F\u52A8\u73AF\u5883\u4E2D\u5C06 AWS_RUNTIME_SCHEDULER_BASE_URL \u914D\u7F6E\u4E3A\u8BE5\u5B9E\u4F8B\u53EF\u8BBF\u95EE\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u4F8B\u5982 http://<server-host>:7380\u3002":"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 AWS_RUNTIME_SCHEDULER_BASE_URL \u6307\u5411\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\u53EF\u4ECE bridge \u673A\u5668\u8BBF\u95EE\uFF0C\u5E76\u4E14\u8FD0\u884C\u65F6\u8BBF\u95EE\u4EE4\u724C\u4ECD\u6709\u6548\u3002"}}function ce(r){if(typeof r!="object"||r===null)return;const t=Reflect.get(r,"response");if(typeof t!="object"||t===null)return;const e=Reflect.get(t,"status");return typeof e=="number"?e:void 0}async function ue(r,t){try{const e=await I.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":t}});return{schedulerReachable:e.status>=200&&e.status<300,tokenRefreshed:!1}}catch(e){if(ce(e)!==401)throw e;const s=await H(r);if(!s.success||!s.runtimeAccessToken)throw e;const n=await I.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":s.runtimeAccessToken}});return{schedulerReachable:n.status>=200&&n.status<300,tokenRefreshed:!0}}}function de(r){return{ok:!1,error:"ambiguous_scheduler_base_url",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:"",autoRegisterTargetUrls:r,hint:"Multiple autoRegisterTargets.serverUrl values were found, so bridge cannot safely infer which scheduler /runtime/ping should call back. Set AWS_RUNTIME_SCHEDULER_BASE_URL explicitly, or use future schedulerId/token-bound multi-scheduler routing."}}function ge(r){return Array.from(new Set([...r,...te].map(t=>String(t||"").trim().toLowerCase()).filter(Boolean)))}function fe(r,t){const e=r.filter(s=>t[s]?.installed);return e.length===0?"":e.map(s=>{const n=t[s],a=n?.error||n?.executable||"command is still available";return`${s}: ${a}`}).join("; ")}function T(r){return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[t,{installed:!!e?.installed,version:e?.version||null,executable:e?.executable||null,error:e?.error||null}]))}let k=null;const me=3e5;async function he(){return(await I.get("https://registry.npmjs.org/aws-runtime-bridge/latest",{timeout:1e4,responseType:"json"})).data?.version||"unknown"}d.get("/check-update",async(r,t)=>{try{let e;k&&Date.now()-k.ts<me?e=k.latestVersion:(e=await he(),k={latestVersion:e,ts:Date.now()});const s=y(),n=e!=="unknown"&&s!=="unknown"&&e!==s;t.json({ok:!0,hasUpdate:n,currentVersion:s,latestVersion:e})}catch(e){const s=e;l.warn("[check-update] Failed to query npm registry:",s.message),t.json({ok:!1,hasUpdate:!1,currentVersion:y(),latestVersion:null,error:s.message||"failed to check for updates"})}}),d.get("/healthz",(r,t)=>{t.json({ok:!0,runtimeBridge:"healthy"})}),d.get("/connection-check",(r,t)=>{const e=String(r.header("X-Bridge-Connection-Key-MD5")||""),s=ie(e);t.status(s.status).json(s.body)}),d.get("/ping",S,async(r,t)=>{let e=M(r.header("X-Scheduler-Base-Url"),r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name"));if(e.source==="ambiguous-auto-register-targets"){const n=X(r.header("X-Runtime-Token"));if(n)e={url:n,source:"token-match",ambiguousTargetUrls:[]};else{const a=r.header("X-Bridge-Instance-Id");if(a){const i=z(),o=Object.entries(i.registrations).find(([,c])=>c===a)?.[0];o&&(e={url:o,source:"instance-id-match",ambiguousTargetUrls:[]})}if(!e.url||e.source==="ambiguous-auto-register-targets"){t.status(400).json(de(e.ambiguousTargetUrls));return}}}const s=e.url;try{const n=Y(s);if(!n){t.status(401).json({ok:!1,error:"runtime_access_token_required"});return}const a=await ue(s,n);t.json({ok:!0,runtimeBridge:"healthy",version:y(),schedulerReachable:a.schedulerReachable,tokenRefreshed:a.tokenRefreshed,schedulerBaseUrl:s,instanceId:e.instanceId,instanceName:e.instanceName})}catch(n){const a=n;t.status(502).json(le(a,s))}}),d.post("/init-instance",S,async(r,t)=>{const{agentId:e,workspacePath:s,skillEnabled:n,mcpEnabled:a,ccSwitchEnabledTools:i,skillPackage:o,skillPackages:c,mcpServers:m}=r.body||{};if(!e||!s){t.status(400).json({error:"agentId and workspacePath are required"});return}try{l.info("[init-instance] Request received",{agentId:String(e),workspacePath:String(s),skillEnabled:n,mcpEnabled:a,ccSwitchEnabledTools:i});const u=await G(e,s,{skillEnabled:n,mcpEnabled:a,ccSwitchEnabledTools:i,skillPackage:o,skillPackages:c,mcpServers:m});l.info("[init-instance] Initialization completed",{agentId:String(e),ok:u.ok,logCount:u.logs?.length||0,enabledTools:u.enabledTools}),t.json(u)}catch(u){const g=u;l.error("[init-instance] Initialization failed",{agentId:String(e),workspacePath:String(s),error:g?.message||String(u),stack:g?.stack||null}),t.status(400).json({error:g?.message||"initialize instance failed"})}}),d.post("/cc-switch/state",S,async(r,t)=>{const{agentId:e}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}try{l.info("[cc-switch/state] Request received",{agentId:String(e)});const s=await W(e),n=await V(s),i=(await J(e,s)).state,o=ge(i.enabledTools||[]);l.info("[cc-switch/state] Detecting SDK statuses",{agentId:String(e),detectionTargets:o});const c=await R(o);l.info("[cc-switch/state] SDK status detection completed",{agentId:String(e),toolStatus:T(c),importedMcpCount:n.mcpServers,importedSkillCount:n.skills}),t.json({ok:!0,agentId:String(e),imported:{mcpServers:n.mcpServers,skills:n.skills},state:{...i,toolStatus:c}})}catch(s){const n=s;l.error("[cc-switch/state] Failed to load state",{agentId:String(e),error:n?.message||String(s),stack:n?.stack||null}),t.status(400).json({error:n?.message||"load state failed"})}}),d.post("/cc-switch/install-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(o=>String(o||"").trim().toLowerCase()).filter(Boolean):[];l.info("[cc-switch/install-tools] Request received",{agentId:String(e),requestedTools:n});const a=new Set(U),i=n.filter(o=>a.has(o));if(l.info("[cc-switch/install-tools] Request normalized",{agentId:String(e),requestedTools:n,installableTools:i,supportedInstallableTools:U}),i.length===0){l.warn("[cc-switch/install-tools] No supported installable tools requested",{agentId:String(e),requestedTools:n}),t.status(400).json({error:`tools must include ${U.join(", ")}`});return}try{const o=await x(e),c=Array.from(new Set([...o.enabledTools||[],...i]));l.info("[cc-switch/install-tools] Starting installer",{agentId:String(e),installableTools:i,previousEnabledTools:o.enabledTools||[],nextEnabledTools:c});const m=await Q(i);l.info("[cc-switch/install-tools] Installer completed",{agentId:String(e),toolStatus:T(m)});const u=await R(c);l.info("[cc-switch/install-tools] Refreshed enabled SDK statuses",{agentId:String(e),refreshedToolStatus:T(u)});const g={...u,...m},h=await L(e,{...o,enabledTools:c,toolStatus:g}),w=new Set(i),p=Object.entries(g).filter(([f])=>w.has(f)).filter(([,f])=>!f.installed).map(([f,B])=>({tool:f,error:B.error||null}));p.length>0&&l.warn("[cc-switch/install-tools] Some SDKs remain unavailable",{agentId:String(e),failedTools:p}),l.info("[cc-switch/install-tools] State saved",{agentId:String(e),enabledTools:h.enabledTools,toolStatus:T(g)}),t.json({ok:p.length===0,agentId:String(e),toolStatus:g,failedTools:p,state:h})}catch(o){const c=o;l.error("[cc-switch/install-tools] Install route failed",{agentId:String(e),requestedTools:n,installableTools:i,error:c?.message||String(o),stack:c?.stack||null}),t.status(400).json({error:c?.message||"install tools failed"})}}),d.post("/cc-switch/uninstall-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(o=>String(o||"").trim().toLowerCase()).filter(Boolean):[],a=new Set(A),i=n.filter(o=>a.has(o));if(i.length===0){t.status(400).json({error:`tools must include ${A.join(", ")}`});return}try{const o=await x(e),c=new Set(i),m=(o.enabledTools||[]).filter(B=>!c.has(String(B||"").trim().toLowerCase())),u=await Z(i),h={...await R([...m,...i]),...u},w=fe(i,h),p=w?o.enabledTools||[]:m,f=await L(e,{...o,enabledTools:p,toolStatus:h});if(w){t.status(400).json({ok:!1,agentId:String(e),error:`uninstall incomplete: ${w}`,toolStatus:h,state:f});return}t.json({ok:!0,agentId:String(e),toolStatus:h,state:f})}catch(o){const c=o;t.status(400).json({error:c?.message||"uninstall tools failed"})}}),d.post("/prepare-restart",S,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{};try{l.info(`[prepare-restart] \u6536\u5230\u51C6\u5907\u91CD\u542F\u901A\u77E5\uFF0CpreserveSessions=${e}`),await P(!!e),t.json({ok:!0,preserveSessions:e,message:e?"Bridge will preserve sessions for aws-mcp-server restart":"Bridge will clean up sessions on shutdown"})}catch(s){const n=s;l.error("[prepare-restart] Error:",n),t.status(500).json({error:n.message})}}),d.post("/update-bridge",j,async(r,t)=>{const e=re(),s=["install","-g","aws-runtime-bridge@latest","--omit=optional"],n=O(r.body?.requestedByUserId);try{if(!n){t.status(400).json({error:"requestedByUserId is required"});return}const a=E({operationType:"update",requestedByUserId:n,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:K(r),status:"accepted",message:"Bridge \u66F4\u65B0\u8BF7\u6C42\u5DF2\u6536\u5230\uFF0C\u5C06\u5728\u540E\u53F0\u6267\u884C\u3002"});await b(a),ne({command:e,args:s,operation:a}),t.json({ok:!0,operationId:a.operationId,message:"Bridge \u66F4\u65B0\u5DF2\u5728\u540E\u53F0\u5F00\u59CB\uFF0C\u5B8C\u6210\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002"})}catch(a){const i=a;l.error("[update-bridge] Error:",i),t.status(500).json({error:i.message||"update bridge failed"})}}),d.post("/restart-bridge",j,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{},s=O(r.body?.requestedByUserId);try{if(!s){t.status(400).json({error:"requestedByUserId is required"});return}l.info(`[restart-bridge] \u6536\u5230\u5B9E\u4F8B Bridge \u91CD\u542F\u8BF7\u6C42\uFF0CpreserveSessions=${e}`);const n=E({operationType:"restart",requestedByUserId:s,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:K(r),status:"accepted",message:"Bridge \u5DF2\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u53D1\u9001\u786E\u8BA4\u901A\u77E5\u3002"});await b(n),await P(!!e),t.json({ok:!0,operationId:n.operationId,preserveSessions:!!e,message:"Bridge \u6B63\u5728\u4F18\u96C5\u9000\u51FA\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002\u82E5\u672A\u7531\u5B88\u62A4\u8FDB\u7A0B\u7BA1\u7406\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u542F\u52A8\u3002"}),setTimeout(()=>{if(process.exitCode=42,v){v("PANEL_RESTART",!!e).catch(a=>{const i=a;l.error("[restart-bridge] graceful shutdown failed:",i),process.exit(1)});return}process.exit(42)},250)}catch(n){const a=n;l.error("[restart-bridge] Error:",a),t.status(500).json({error:a.message||"restart bridge failed"})}});export{de as buildAmbiguousSchedulerBaseUrlResponse,ie as buildConnectionCheckResponse,le as buildSchedulerPingFailureResponse,ge as buildToolStatusDetectionTargets,fe as buildUninstallFailureMessage,d as instanceRouter,Ee as setGracefulShutdownFn,T as summarizeToolStatus};
3
3
 
@@ -1,2 +1,2 @@
1
- import{createReadStream as U,createWriteStream as ot,promises as o}from"node:fs";import d from"node:path";import{Readable as st}from"node:stream";import{pipeline as ct}from"node:stream/promises";import{TextDecoder as lt}from"node:util";import{createGunzip as ht}from"node:zlib";import*as ut from"tar";import dt from"unzipper";const{ZipArchive:wt}=await import("archiver"),y={maxEntries:1e4,maxFileBytes:512*1024*1024,maxTotalBytes:2*1024*1024*1024,maxPathDepth:64,maxPathLength:4096},ft=new lt("gbk");function h(t){return String(t||"").replace(/\\/g,"/")}function j(t){const e=d.resolve(String(t||"").trim());if(!e)throw new Error("workspacePath is required");return e}async function pt(t){const e=j(t),r=await o.realpath(e);if(!(await o.stat(r)).isDirectory())throw new Error(`Workspace path is not a directory: ${e}`);return r}async function k(t){return{requestedRoot:j(t),workspaceRoot:await pt(t)}}function p(t,e){const r=d.resolve(e),i=d.relative(t,r);if(i.startsWith("..")||d.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function x(t,e){const r=await o.realpath(e);return p(t,r)}async function v(t,e){const r=await o.realpath(d.dirname(e));p(t,r);try{if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${e}`)}catch(i){if((typeof i=="object"&&i!==null?Reflect.get(i,"code"):void 0)!=="ENOENT")throw i}}function z(t,e,r){const i=d.resolve(r),a=d.relative(t,i);if(a.startsWith("..")||d.isAbsolute(a))throw new Error(`Target path is outside workspace: ${i}`);return p(e,d.join(e,a))}async function $({workspacePath:t,targetPath:e=""}){const{requestedRoot:r,workspaceRoot:i}=await k(t),a=String(e||"").trim(),n=a?z(r,i,a):i,s=await x(i,n);return{workspaceRoot:i,resolvedTargetPath:s}}function D(t){const e=String(t||"").trim();if(!e)throw new Error("entryName is required");if(e==="."||e==="..")throw new Error("entryName is invalid");if(e.includes("/")||e.includes("\\")||d.basename(e)!==e)throw new Error(`entryName must be a plain file or directory name: ${e}`);return e}function q(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=q(t);return D(d.basename(e.replace(/\\/g,"/")))}function mt(t,e){if(!t)return B(e);const i=q(String(t||e||"")).replace(/\\/g,"/").split("/").filter(Boolean).map(a=>D(a));return i.length===0?B(e):d.join(...i)}function _(t){return(t&4095).toString(8).padStart(4,"0")}function gt(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 C(t,e,r,i,a,n,s){const c=t&e?"r":"-",u=t&r?"w":"-",l=(t&i)!==0,w=(t&a)!==0?l?n:s:l?"x":"-";return`${c}${u}${w}`}function yt(t){return[C(t,256,128,64,2048,"s","S"),C(t,32,16,8,1024,"s","S"),C(t,4,2,1,512,"t","T")].join("")}function Pt(t){if(!(t.length<24||t.toString("hex",0,8)!=="89504e470d0a1a0a"))return{format:"png",width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function kt(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 xt(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 Et(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 G(t,e,r){if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${e}`);const a=await o.stat(e);if(!r||!a.isDirectory())return[e];const n=await o.readdir(e),s=await Promise.all(n.map(c=>{const u=p(t,d.join(e,c));return G(t,u,!0)}));return[e,...s.flat()]}async function vt(t,e){if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${e}`);await x(t,e)}async function St(t){const e=await o.open(t,"r");try{const r=Buffer.alloc(512),{bytesRead:i}=await e.read(r,0,r.length,0),a=r.subarray(0,i);return Pt(a)||kt(a)||xt(a)||Et(a)}finally{await e.close()}}async function S({workspacePath:t,targetPath:e},r={}){const{requestedRoot:i,workspaceRoot:a}=await k(t),n=String(e||"").trim();if(!n)throw new Error("targetPath is required");const s=z(i,a,n),c=await x(a,s);if(!r.allowWorkspaceRoot&&c===a)throw new Error("Workspace root cannot be modified");return{workspaceRoot:a,resolvedTargetPath:c}}async function Z(t){const{requestedRoot:e,workspaceRoot:r}=await k(t.workspacePath),i=String(t.targetPath||"").trim();if(!i)return{workspaceRoot:r,resolvedTargetPath:r};const a=z(e,r,i);if(await J(a))return{workspaceRoot:r,resolvedTargetPath:await x(r,a)};const n=await o.realpath(d.dirname(a));return p(r,n),{workspaceRoot:r,resolvedTargetPath:a}}function H(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 K(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=D(K(e)||"extracted");return d.join(t,r)}function I(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>y.maxPathLength||i.length>y.maxPathDepth)throw new Error(`Archive entry path is too deep or too long: ${e}`);return p(t,d.join(t,r))}function Tt(t){const e=t.props?.pathBuffer;if(!e||t.props?.flags?.isUnicode)return t.path;const r=ft.decode(e);return!r||r.includes("\uFFFD")?t.path:r}async function V(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 A(t){if(t.entries+=1,t.entries>y.maxEntries)throw new Error(`Archive contains too many entries. Limit: ${y.maxEntries}`)}function X(t,e){if(t.totalBytes+=e,e>y.maxFileBytes)throw new Error(`Archive entry exceeds max size. Limit: ${y.maxFileBytes} bytes`);if(t.totalBytes>y.maxTotalBytes)throw new Error(`Archive extraction exceeds total size. Limit: ${y.maxTotalBytes} bytes`)}async function Y(t,e,r){let i=0;t.on("data",a=>{const n=Buffer.isBuffer(a)?a.length:Buffer.byteLength(a);i+=n,(i>y.maxFileBytes||r.totalBytes+i>y.maxTotalBytes)&&t instanceof st&&t.destroy(new Error("Archive extraction size limit exceeded"))}),await ct(t,ot(e)),X(r,i)}async function J(t){try{return await o.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 k(t),a=String(e||"").trim();if(!a)throw new Error("filePath is required");return{workspaceRoot:i,resolvedFilePath:z(r,i,a)}}async function Ot(t){const{workspaceRoot:e,resolvedTargetPath:r}=await $(t);if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const a=await o.readdir(r,{withFileTypes:!0}),n=await Promise.all(a.map(async s=>{const c=d.join(r,s.name),u=s.isFile()?(await o.stat(c)).size:void 0;return{name:s.name,path:h(c),isDirectory:s.isDirectory(),size:u}}));return{currentPath:h(r),workspacePath:h(e),items:n}}async function Ut(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await x(e,r);if(!(await o.stat(i)).isFile())throw new Error(`Path is not a file: ${i}`);const n=await o.readFile(i,"utf-8");return{workspacePath:h(e),filePath:h(i),content:n}}const Q={maxEntries:1e5,maxDepth:32};async function Rt(t,e){let r=0,i=0;async function a(n,s){if(s>=Q.maxDepth)return;let c;try{c=await o.readdir(n,{withFileTypes:!0})}catch{return}for(const u of c){if(i+=1,i>Q.maxEntries)return;if(u.isSymbolicLink())continue;const l=p(t,d.join(n,u.name));try{if(u.isDirectory())await a(l,s+1);else if(u.isFile()){const f=await o.stat(l);r+=f.size}}catch{}}}return await a(e,0),r}async function jt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t,{allowWorkspaceRoot:!0}),[i,a]=await Promise.all([o.stat(r),o.lstat(r)]),n=i.isDirectory(),s=i.isFile()?await St(r):void 0,c=n?await Rt(e,r):i.size;return{workspacePath:h(e),targetPath:h(r),name:d.basename(r),isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:a.isSymbolicLink(),size:c,mode:i.mode,modeOctal:_(i.mode),permissions:yt(i.mode),uid:i.uid,gid:i.gid,atimeMs:i.atimeMs,mtimeMs:i.mtimeMs,ctimeMs:i.ctimeMs,birthtimeMs:i.birthtimeMs,...s?{image:s}:{}}}async function qt(t){const e=gt(t.mode),r=!!t.recursive,{workspaceRoot:i,resolvedTargetPath:a}=await S(t),n=await G(i,a,r);for(const s of n)await vt(i,s),await o.chmod(s,e);return{ok:!0,workspacePath:h(i),targetPath:h(a),mode:e,modeOctal:_(e),recursive:r,changedCount:n.length}}async function _t(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await x(e,r),a=await o.stat(i);if(!a.isFile())throw new Error(`Path is not a file: ${i}`);const n=d.extname(i).toLowerCase();if(n===".doc")return{workspacePath:h(e),filePath:h(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:a.size,mtimeMs:a.mtimeMs};if(n!==".docx")throw new Error("Document preview only supports .docx files");return{workspacePath:h(e),filePath:h(i),kind:"html",content:"",size:a.size,mtimeMs:a.mtimeMs}}async function Gt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t);return await o.mkdir(d.dirname(r),{recursive:!0}),await v(e,r),await o.writeFile(r,String(t.content??""),"utf-8"),{ok:!0,workspacePath:h(e),filePath:h(r)}}async function Zt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await $(t),i=D(t.entryName),a=p(e,d.join(r,i));if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);try{throw await o.access(a),new Error(`Path already exists: ${a}`)}catch(s){if((typeof s=="object"&&s!==null?Reflect.get(s,"code"):void 0)!=="ENOENT")throw s}return t.isDirectory?await o.mkdir(a,{recursive:!1}):(await o.mkdir(d.dirname(a),{recursive:!0}),await v(e,a),await o.writeFile(a,"","utf-8")),{ok:!0,workspacePath:h(e),targetPath:h(a),isDirectory:t.isDirectory}}async function Ht(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=D(t.newName),a=p(e,d.join(d.dirname(r),i));if(a===r)return{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(a)};await o.stat(r);try{throw await o.access(a),new Error(`Path already exists: ${a}`)}catch(n){if((typeof n=="object"&&n!==null?Reflect.get(n,"code"):void 0)!=="ENOENT")throw n}return await o.rename(r,a),{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(a)}}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=await $({workspacePath:t.workspacePath,targetPath:t.destinationPath});if(!(await o.stat(i.resolvedTargetPath)).isDirectory())throw new Error(`Destination is not a directory: ${i.resolvedTargetPath}`);if((await o.stat(r)).isDirectory()){const c=d.relative(r,i.resolvedTargetPath);if(c===""||!c.startsWith("..")&&!d.isAbsolute(c))throw new Error("Cannot move a directory into itself or its descendant")}const s=p(e,d.join(i.resolvedTargetPath,d.basename(r)));if(s===r)return{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(s),destinationPath:h(i.resolvedTargetPath)};try{throw await o.access(s),new Error(`Path already exists: ${s}`)}catch(c){if((typeof c=="object"&&c!==null?Reflect.get(c,"code"):void 0)!=="ENOENT")throw c}return await o.rename(r,s),{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(s),destinationPath:h(i.resolvedTargetPath)}}async function Vt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t);return(await o.stat(r)).isDirectory()?await o.rm(r,{recursive:!0,force:!1}):await o.unlink(r),{ok:!0,workspacePath:h(e),targetPath:h(r)}}async function Xt(t){if(!Array.isArray(t.files)||t.files.length===0)throw new Error("files are required");const{workspaceRoot:e,resolvedTargetPath:r}=await Z(t);if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const a=[];for(const n of t.files){const s=B(n.originalname),c=mt(n.relativePath,n.originalname),u=p(e,d.join(r,c));await o.mkdir(d.dirname(u),{recursive:!0}),await v(e,u),await o.copyFile(n.path,u);let l;t.extractArchives&&H(u)&&(l=await Wt({workspacePath:e,archivePath:u,outputPath:Ft(r,s)})),a.push({fileName:s,targetPath:h(u),size:n.size,extracted:l})}return{ok:!0,workspacePath:h(e),targetPath:h(r),files:a}}async function Yt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=await o.stat(r),a=d.basename(r);return{workspacePath:h(e),targetPath:h(r),resolvedTargetPath:r,fileName:i.isDirectory()?`${a||"workspace"}.zip`:a,contentType:i.isDirectory()?"application/zip":"application/octet-stream",isDirectory:i.isDirectory()}}async function Jt(t,e){const r=new wt({zlib:{level:9}}),i=new Promise((a,n)=>{e.on("close",a),e.on("finish",a),r.on("error",n),e.on("error",n)});r.pipe(e),r.directory(t,!1),await r.finalize(),await i}async function zt(t,e,r){let i=0,a;const n=U(t),s=n.pipe(dt.Parse({forceStream:!0}));try{for await(const c of s){if(a){await V(c);continue}const u=Tt(c);try{const l=I(e,u);if(A(r),c.type==="Directory"){await o.mkdir(l,{recursive:!0});continue}if(c.type!=="File")throw new Error(`Unsupported archive entry type: ${u}`);await o.mkdir(d.dirname(l),{recursive:!0}),await v(e,l),await Y(c,l,r),i+=1}catch(l){a=l,await V(c)}}}finally{n.destroy(),s.destroy()}if(a)throw a;return i}async function Dt(t,e,r,i){let a=0,n;const s=(c,u)=>{if(n)return!1;try{I(e,c),A(i);const l=String(u.type||"").toLowerCase();if(l==="symboliclink"||l==="link")throw new Error(`Archive links are not supported: ${c}`);if(l!=="file"&&l!=="oldfile"&&l!=="contiguousfile"&&l!=="directory")throw new Error(`Unsupported archive entry type: ${c}`);return typeof u.size=="number"&&X(i,u.size),l!=="directory"&&(a+=1),!0}catch(l){return n=l,!1}};if(await ut.x({file:t,cwd:e,gzip:r,strict:!0,filter:(c,u)=>s(c,u)}),n)throw n;return a}async function Lt(t,e,r){const i=K(d.basename(t)),a=I(e,i||"decompressed");return A(r),await o.mkdir(d.dirname(a),{recursive:!0}),await v(e,a),await Y(U(t).pipe(ht()),a,r),1}async function Wt(t){const{requestedRoot:e,workspaceRoot:r}=await k(t.workspacePath),{resolvedTargetPath:i}=await Z({workspacePath:t.workspacePath,targetPath:t.outputPath||""}),a=z(e,r,String(t.archivePath||"").trim()),n=await x(r,a);if(!(await o.stat(n)).isFile())throw new Error(`Archive path is not a file: ${n}`);if(await J(i)||await o.mkdir(i,{recursive:!0}),!(await o.stat(i)).isDirectory())throw new Error(`Output path is not a directory: ${i}`);const u=H(n);if(!u)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let l=0;const f={entries:0,totalBytes:0};return u==="zip"?l=await zt(n,i,f):u==="tar"||u==="tgz"?l=await Dt(n,i,u==="tgz",f):l=await Lt(n,i,f),{ok:!0,workspacePath:h(r),archivePath:h(n),targetPath:h(i),extractedEntries:l}}function tt(t){const e=t.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!e)return[t];const[,r,i,a]=e;return i.split(",").flatMap(n=>tt(r+n.trim()+a))}function et(t,e){const r=String(e||"").trim();if(!r||r==="*"||r==="*.*")return!0;const i=t.toLowerCase();return tt(r.toLowerCase()).some(n=>{if(n==="*")return!0;const s=n.replace(/\./g,"\\.").replace(/\*\*/g,"<<GLOBSTAR>>").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]").replace(/<<GLOBSTAR>>/g,".*");return new RegExp(`^${s}$`).test(i)})}function rt(t){return t===".git"||t==="node_modules"||t===".svn"||t===".hg"}const it=10*1024*1024,$t=1e3;async function Qt(t){const{workspaceRoot:e}=await k(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=!!t.ignoreCase,a=String(t.fileMask||"").trim(),n=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:$t,s=i?r.toLowerCase():r,u=(t.mode==="filename"?"filename":"content")==="filename",l=[];let f=0,w=!1;async function m(E){if(w)return;let F;try{F=await o.readdir(E,{withFileTypes:!0})}catch{return}for(const g of F){if(w)return;const T=p(e,d.join(E,g.name));if(g.isSymbolicLink())continue;if(g.isDirectory()){if(rt(g.name))continue;await m(T);continue}if(!g.isFile()||a&&!et(g.name,a))continue;f+=1;let N;try{N=await o.stat(T)}catch{continue}if(N.size>it)continue;if(u){const P=(i?g.name.toLowerCase():g.name).indexOf(s);if(P!==-1&&(l.push({filePath:h(T),lineNumber:0,lineContent:g.name,matchStart:P,matchEnd:P+r.length}),l.length>=n)){w=!0;return}continue}let M;try{M=await o.readFile(T,"utf-8")}catch{continue}const b=M.split(/\r?\n/);for(let R=0;R<b.length;R+=1){const P=b[R],nt=i?P.toLowerCase():P;let O=0;for(;;){const L=nt.indexOf(s,O);if(L===-1)break;if(l.push({filePath:h(T),lineNumber:R+1,lineContent:P,matchStart:L,matchEnd:L+r.length}),l.length>=n){w=!0;return}O=L+1}}}}return await m(e),{workspacePath:h(e),keyword:r,totalFiles:f,totalMatches:l.length,matches:l,truncated:w}}async function at(t,e,r,i,a){let n;try{n=await o.readFile(e,"utf-8")}catch{return{changed:!1,count:0}}const s=n;let c=0;if(a){const u=r.toLowerCase();let l=0;for(;;){const w=n.toLowerCase().indexOf(u,l);if(w===-1)break;n=n.slice(0,w)+i+n.slice(w+r.length),c+=1;const m=i.length||1;l=w+m}}else{let u=0;for(;;){const l=n.indexOf(r,u);if(l===-1)break;n=n.slice(0,l)+i+n.slice(l+r.length),c+=1;const f=i.length||1;u=l+f}}return n===s?{changed:!1,count:0}:(await v(t,e),await o.writeFile(e,n,"utf-8"),{changed:!0,count:c})}async function te(t){const{workspaceRoot:e}=await k(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=String(t.replacement??""),a=!!t.ignoreCase,n=String(t.fileMask||"").trim(),s=String(t.filePath||"").trim();let c=0,u=0;if(s){const{resolvedFilePath:f}=await W({workspacePath:t.workspacePath,filePath:s}),w=await at(e,f,r,i,a);return w.changed&&(c=1,u=w.count),{workspacePath:h(e),keyword:r,replacement:i,changedFiles:c,changedMatches:u}}async function l(f){let w;try{w=await o.readdir(f,{withFileTypes:!0})}catch{return}for(const m of w){const E=p(e,d.join(f,m.name));if(m.isSymbolicLink())continue;if(m.isDirectory()){if(rt(m.name))continue;await l(E);continue}if(!m.isFile()||n&&!et(m.name,n))continue;try{if((await o.stat(E)).size>it)continue}catch{continue}const F=await at(e,E,r,i,a);F.changed&&(c+=1,u+=F.count)}}return await l(e),{workspacePath:h(e),keyword:r,replacement:i,changedFiles:c,changedMatches:u}}export{qt as chmodWorkspaceEntry,Zt as createWorkspaceEntry,Vt as deleteWorkspaceEntry,Wt as extractWorkspaceArchive,jt as getWorkspaceEntryProperties,Ot as listWorkspaceDirectory,Kt as moveWorkspaceEntry,_t as previewWorkspaceDocument,Ut as readWorkspaceFile,Ht as renameWorkspaceEntry,te as replaceWorkspaceFiles,Yt as resolveWorkspaceDownloadTarget,Qt as searchWorkspaceFiles,Jt as streamWorkspaceDirectoryZip,Xt as uploadWorkspaceFiles,Gt as writeWorkspaceFile};
1
+ import{createReadStream as O,createWriteStream as ot,promises as o}from"node:fs";import d from"node:path";import{Readable as st}from"node:stream";import{pipeline as ct}from"node:stream/promises";import{TextDecoder as lt}from"node:util";import{createGunzip as ht}from"node:zlib";import*as ut from"tar";import dt from"unzipper";const{ZipArchive:wt}=await import("archiver"),y={maxEntries:1e4,maxFileBytes:512*1024*1024,maxTotalBytes:2*1024*1024*1024,maxPathDepth:64,maxPathLength:4096},ft=new lt("gbk");function h(t){return String(t||"").replace(/\\/g,"/")}function U(t){const e=d.resolve(String(t||"").trim());if(!e)throw new Error("workspacePath is required");return e}async function pt(t){const e=U(t),r=await o.realpath(e);if(!(await o.stat(r)).isDirectory())throw new Error(`Workspace path is not a directory: ${e}`);return r}async function k(t){return{requestedRoot:U(t),workspaceRoot:await pt(t)}}function p(t,e){const r=d.resolve(e),i=d.relative(t,r);if(i.startsWith("..")||d.isAbsolute(i))throw new Error(`Target path is outside workspace: ${r}`);return r}async function x(t,e){const r=await o.realpath(e);return p(t,r)}async function E(t,e){const r=await o.realpath(d.dirname(e));p(t,r);try{if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to write through symbolic link: ${e}`)}catch(i){if((typeof i=="object"&&i!==null?Reflect.get(i,"code"):void 0)!=="ENOENT")throw i}}function z(t,e,r){const i=d.resolve(r),a=d.relative(t,i);if(a.startsWith("..")||d.isAbsolute(a))throw new Error(`Target path is outside workspace: ${i}`);return p(e,d.join(e,a))}async function C({workspacePath:t,targetPath:e=""}){const{requestedRoot:r,workspaceRoot:i}=await k(t),a=String(e||"").trim(),n=a?z(r,i,a):i,s=await x(i,n);return{workspaceRoot:i,resolvedTargetPath:s}}function D(t){const e=String(t||"").trim();if(!e)throw new Error("entryName is required");if(e==="."||e==="..")throw new Error("entryName is invalid");if(e.includes("/")||e.includes("\\")||d.basename(e)!==e)throw new Error(`entryName must be a plain file or directory name: ${e}`);return e}function q(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 $(t){const e=q(t);return D(d.basename(e.replace(/\\/g,"/")))}function mt(t,e){if(!t)return $(e);const i=q(String(t||e||"")).replace(/\\/g,"/").split("/").filter(Boolean).map(a=>D(a));return i.length===0?$(e):d.join(...i)}function _(t){return(t&4095).toString(8).padStart(4,"0")}function gt(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 B(t,e,r,i,a,n,s){const c=t&e?"r":"-",u=t&r?"w":"-",l=(t&i)!==0,w=(t&a)!==0?l?n:s:l?"x":"-";return`${c}${u}${w}`}function yt(t){return[B(t,256,128,64,2048,"s","S"),B(t,32,16,8,1024,"s","S"),B(t,4,2,1,512,"t","T")].join("")}function Pt(t){if(!(t.length<24||t.toString("hex",0,8)!=="89504e470d0a1a0a"))return{format:"png",width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}function kt(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 xt(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 vt(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 G(t,e,r){if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${e}`);const a=await o.stat(e);if(!r||!a.isDirectory())return[e];const n=await o.readdir(e),s=await Promise.all(n.map(c=>{const u=p(t,d.join(e,c));return G(t,u,!0)}));return[e,...s.flat()]}async function Et(t,e){if((await o.lstat(e)).isSymbolicLink())throw new Error(`Refusing to change permissions on symbolic link: ${e}`);await x(t,e)}async function St(t){const e=await o.open(t,"r");try{const r=Buffer.alloc(512),{bytesRead:i}=await e.read(r,0,r.length,0),a=r.subarray(0,i);return Pt(a)||kt(a)||xt(a)||vt(a)}finally{await e.close()}}async function S({workspacePath:t,targetPath:e},r={}){const{requestedRoot:i,workspaceRoot:a}=await k(t),n=String(e||"").trim();if(!n)throw new Error("targetPath is required");const s=z(i,a,n),c=await x(a,s);if(!r.allowWorkspaceRoot&&c===a)throw new Error("Workspace root cannot be modified");return{workspaceRoot:a,resolvedTargetPath:c}}async function Z(t){const{requestedRoot:e,workspaceRoot:r}=await k(t.workspacePath),i=String(t.targetPath||"").trim();if(!i)return{workspaceRoot:r,resolvedTargetPath:r};const a=z(e,r,i);if(await J(a))return{workspaceRoot:r,resolvedTargetPath:await x(r,a)};const n=await o.realpath(d.dirname(a));return p(r,n),{workspaceRoot:r,resolvedTargetPath:a}}function H(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 K(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=D(K(e)||"extracted");return d.join(t,r)}function I(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>y.maxPathLength||i.length>y.maxPathDepth)throw new Error(`Archive entry path is too deep or too long: ${e}`);return p(t,d.join(t,r))}function Tt(t){const e=t.props?.pathBuffer;if(!e||t.props?.flags?.isUnicode)return t.path;const r=ft.decode(e);return!r||r.includes("\uFFFD")?t.path:r}async function V(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 A(t){if(t.entries+=1,t.entries>y.maxEntries)throw new Error(`Archive contains too many entries. Limit: ${y.maxEntries}`)}function X(t,e){if(t.totalBytes+=e,e>y.maxFileBytes)throw new Error(`Archive entry exceeds max size. Limit: ${y.maxFileBytes} bytes`);if(t.totalBytes>y.maxTotalBytes)throw new Error(`Archive extraction exceeds total size. Limit: ${y.maxTotalBytes} bytes`)}async function Y(t,e,r){let i=0;t.on("data",a=>{const n=Buffer.isBuffer(a)?a.length:Buffer.byteLength(a);i+=n,(i>y.maxFileBytes||r.totalBytes+i>y.maxTotalBytes)&&t instanceof st&&t.destroy(new Error("Archive extraction size limit exceeded"))}),await ct(t,ot(e)),X(r,i)}async function J(t){try{return await o.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 k(t),a=String(e||"").trim();if(!a)throw new Error("filePath is required");return{workspaceRoot:i,resolvedFilePath:z(r,i,a)}}async function Ot(t){const{workspaceRoot:e,resolvedTargetPath:r}=await C(t);if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const a=await o.readdir(r,{withFileTypes:!0}),n=await Promise.all(a.map(async s=>{const c=d.join(r,s.name),u=s.isFile()?(await o.stat(c)).size:void 0;return{name:s.name,path:h(c),isDirectory:s.isDirectory(),size:u}}));return{currentPath:h(r),workspacePath:h(e),items:n}}async function Ut(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await x(e,r);if(!(await o.stat(i)).isFile())throw new Error(`Path is not a file: ${i}`);const n=await o.readFile(i,"utf-8");return{workspacePath:h(e),filePath:h(i),content:n}}const Q={maxEntries:1e5,maxDepth:32};async function Rt(t,e){let r=0,i=0;async function a(n,s){if(s>=Q.maxDepth)return;let c;try{c=await o.readdir(n,{withFileTypes:!0})}catch{return}for(const u of c){if(i+=1,i>Q.maxEntries)return;if(u.isSymbolicLink())continue;const l=p(t,d.join(n,u.name));try{if(u.isDirectory())await a(l,s+1);else if(u.isFile()){const f=await o.stat(l);r+=f.size}}catch{}}}return await a(e,0),r}async function qt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t,{allowWorkspaceRoot:!0}),[i,a]=await Promise.all([o.stat(r),o.lstat(r)]),n=i.isDirectory(),s=i.isFile()?await St(r):void 0,c=n?await Rt(e,r):i.size;return{workspacePath:h(e),targetPath:h(r),name:d.basename(r),isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:a.isSymbolicLink(),size:c,mode:i.mode,modeOctal:_(i.mode),permissions:yt(i.mode),uid:i.uid,gid:i.gid,atimeMs:i.atimeMs,mtimeMs:i.mtimeMs,ctimeMs:i.ctimeMs,birthtimeMs:i.birthtimeMs,...s?{image:s}:{}}}async function _t(t){const e=gt(t.mode),r=!!t.recursive,{workspaceRoot:i,resolvedTargetPath:a}=await S(t),n=await G(i,a,r);for(const s of n)await Et(i,s),await o.chmod(s,e);return{ok:!0,workspacePath:h(i),targetPath:h(a),mode:e,modeOctal:_(e),recursive:r,changedCount:n.length}}async function Gt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t),i=await x(e,r),a=await o.stat(i);if(!a.isFile())throw new Error(`Path is not a file: ${i}`);const n=d.extname(i).toLowerCase();if(n===".doc")return{workspacePath:h(e),filePath:h(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:a.size,mtimeMs:a.mtimeMs};if(n!==".docx")throw new Error("Document preview only supports .docx files");return{workspacePath:h(e),filePath:h(i),kind:"html",content:"",size:a.size,mtimeMs:a.mtimeMs}}async function Zt(t){const{workspaceRoot:e,resolvedFilePath:r}=await W(t);return await o.mkdir(d.dirname(r),{recursive:!0}),await E(e,r),await o.writeFile(r,String(t.content??""),"utf-8"),{ok:!0,workspacePath:h(e),filePath:h(r)}}async function Ht(t){const{workspaceRoot:e,resolvedTargetPath:r}=await C(t),i=D(t.entryName),a=p(e,d.join(r,i));if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);try{throw await o.access(a),new Error(`Path already exists: ${a}`)}catch(s){if((typeof s=="object"&&s!==null?Reflect.get(s,"code"):void 0)!=="ENOENT")throw s}return t.isDirectory?await o.mkdir(a,{recursive:!1}):(await o.mkdir(d.dirname(a),{recursive:!0}),await E(e,a),await o.writeFile(a,"","utf-8")),{ok:!0,workspacePath:h(e),targetPath:h(a),isDirectory:t.isDirectory}}async function Kt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=D(t.newName),a=p(e,d.join(d.dirname(r),i));if(a===r)return{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(a)};await o.stat(r);try{throw await o.access(a),new Error(`Path already exists: ${a}`)}catch(n){if((typeof n=="object"&&n!==null?Reflect.get(n,"code"):void 0)!=="ENOENT")throw n}return await o.rename(r,a),{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(a)}}async function Vt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=await C({workspacePath:t.workspacePath,targetPath:t.destinationPath});if(!(await o.stat(i.resolvedTargetPath)).isDirectory())throw new Error(`Destination is not a directory: ${i.resolvedTargetPath}`);if((await o.stat(r)).isDirectory()){const c=d.relative(r,i.resolvedTargetPath);if(c===""||!c.startsWith("..")&&!d.isAbsolute(c))throw new Error("Cannot move a directory into itself or its descendant")}const s=p(e,d.join(i.resolvedTargetPath,d.basename(r)));if(s===r)return{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(s),destinationPath:h(i.resolvedTargetPath)};try{throw await o.access(s),new Error(`Path already exists: ${s}`)}catch(c){if((typeof c=="object"&&c!==null?Reflect.get(c,"code"):void 0)!=="ENOENT")throw c}return await o.rename(r,s),{ok:!0,workspacePath:h(e),sourcePath:h(r),targetPath:h(s),destinationPath:h(i.resolvedTargetPath)}}async function Xt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t);return(await o.stat(r)).isDirectory()?await o.rm(r,{recursive:!0,force:!1}):await o.unlink(r),{ok:!0,workspacePath:h(e),targetPath:h(r)}}async function Yt(t){if(!Array.isArray(t.files)||t.files.length===0)throw new Error("files are required");const{workspaceRoot:e,resolvedTargetPath:r}=await Z(t);if(!(await o.stat(r)).isDirectory())throw new Error(`Path is not a directory: ${r}`);const a=[];for(const n of t.files){const s=$(n.originalname),c=mt(n.relativePath,n.originalname),u=p(e,d.join(r,c));await o.mkdir(d.dirname(u),{recursive:!0}),await E(e,u),await o.copyFile(n.path,u);let l;t.extractArchives&&H(u)&&(l=await Ct({workspacePath:e,archivePath:u,outputPath:Ft(r,s)})),a.push({fileName:s,targetPath:h(u),size:n.size,extracted:l})}return{ok:!0,workspacePath:h(e),targetPath:h(r),files:a}}async function Jt(t){const{workspaceRoot:e,resolvedTargetPath:r}=await S(t),i=await o.stat(r),a=d.basename(r);return{workspacePath:h(e),targetPath:h(r),resolvedTargetPath:r,fileName:i.isDirectory()?`${a||"workspace"}.zip`:a,contentType:i.isDirectory()?"application/zip":zt(r),isDirectory:i.isDirectory()}}function zt(t){const e=d.extname(t).toLowerCase();return{".svg":"image/svg+xml",".svgz":"image/svg+xml",".ico":"image/x-icon",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".bmp":"image/bmp",".avif":"image/avif",".txt":"text/plain; charset=utf-8",".json":"application/json; charset=utf-8",".xml":"application/xml; charset=utf-8",".html":"text/html; charset=utf-8",".htm":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".md":"text/markdown; charset=utf-8",".csv":"text/csv; charset=utf-8",".pdf":"application/pdf",".zip":"application/zip",".mp3":"audio/mpeg",".wav":"audio/wav",".mp4":"video/mp4",".webm":"video/webm"}[e]||"application/octet-stream"}async function Qt(t,e){const r=new wt({zlib:{level:9}}),i=new Promise((a,n)=>{e.on("close",a),e.on("finish",a),r.on("error",n),e.on("error",n)});r.pipe(e),r.directory(t,!1),await r.finalize(),await i}async function Dt(t,e,r){let i=0,a;const n=O(t),s=n.pipe(dt.Parse({forceStream:!0}));try{for await(const c of s){if(a){await V(c);continue}const u=Tt(c);try{const l=I(e,u);if(A(r),c.type==="Directory"){await o.mkdir(l,{recursive:!0});continue}if(c.type!=="File")throw new Error(`Unsupported archive entry type: ${u}`);await o.mkdir(d.dirname(l),{recursive:!0}),await E(e,l),await Y(c,l,r),i+=1}catch(l){a=l,await V(c)}}}finally{n.destroy(),s.destroy()}if(a)throw a;return i}async function Lt(t,e,r,i){let a=0,n;const s=(c,u)=>{if(n)return!1;try{I(e,c),A(i);const l=String(u.type||"").toLowerCase();if(l==="symboliclink"||l==="link")throw new Error(`Archive links are not supported: ${c}`);if(l!=="file"&&l!=="oldfile"&&l!=="contiguousfile"&&l!=="directory")throw new Error(`Unsupported archive entry type: ${c}`);return typeof u.size=="number"&&X(i,u.size),l!=="directory"&&(a+=1),!0}catch(l){return n=l,!1}};if(await ut.x({file:t,cwd:e,gzip:r,strict:!0,filter:(c,u)=>s(c,u)}),n)throw n;return a}async function Wt(t,e,r){const i=K(d.basename(t)),a=I(e,i||"decompressed");return A(r),await o.mkdir(d.dirname(a),{recursive:!0}),await E(e,a),await Y(O(t).pipe(ht()),a,r),1}async function Ct(t){const{requestedRoot:e,workspaceRoot:r}=await k(t.workspacePath),{resolvedTargetPath:i}=await Z({workspacePath:t.workspacePath,targetPath:t.outputPath||""}),a=z(e,r,String(t.archivePath||"").trim()),n=await x(r,a);if(!(await o.stat(n)).isFile())throw new Error(`Archive path is not a file: ${n}`);if(await J(i)||await o.mkdir(i,{recursive:!0}),!(await o.stat(i)).isDirectory())throw new Error(`Output path is not a directory: ${i}`);const u=H(n);if(!u)throw new Error("Unsupported archive type. Supported: .zip, .tar, .tar.gz, .tgz, .gz");let l=0;const f={entries:0,totalBytes:0};return u==="zip"?l=await Dt(n,i,f):u==="tar"||u==="tgz"?l=await Lt(n,i,u==="tgz",f):l=await Wt(n,i,f),{ok:!0,workspacePath:h(r),archivePath:h(n),targetPath:h(i),extractedEntries:l}}function tt(t){const e=t.match(/^(.*?)\{([^}]+)\}(.*)$/);if(!e)return[t];const[,r,i,a]=e;return i.split(",").flatMap(n=>tt(r+n.trim()+a))}function et(t,e){const r=String(e||"").trim();if(!r||r==="*"||r==="*.*")return!0;const i=t.toLowerCase();return tt(r.toLowerCase()).some(n=>{if(n==="*")return!0;const s=n.replace(/\./g,"\\.").replace(/\*\*/g,"<<GLOBSTAR>>").replace(/\*/g,"[^/]*").replace(/\?/g,"[^/]").replace(/<<GLOBSTAR>>/g,".*");return new RegExp(`^${s}$`).test(i)})}function rt(t){return t===".git"||t==="node_modules"||t===".svn"||t===".hg"}const it=10*1024*1024,$t=1e3;async function te(t){const{workspaceRoot:e}=await k(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=!!t.ignoreCase,a=String(t.fileMask||"").trim(),n=typeof t.maxResults=="number"&&t.maxResults>0?t.maxResults:$t,s=i?r.toLowerCase():r,u=(t.mode==="filename"?"filename":"content")==="filename",l=[];let f=0,w=!1;async function m(v){if(w)return;let F;try{F=await o.readdir(v,{withFileTypes:!0})}catch{return}for(const g of F){if(w)return;const T=p(e,d.join(v,g.name));if(g.isSymbolicLink())continue;if(g.isDirectory()){if(rt(g.name))continue;await m(T);continue}if(!g.isFile()||a&&!et(g.name,a))continue;f+=1;let N;try{N=await o.stat(T)}catch{continue}if(N.size>it)continue;if(u){const P=(i?g.name.toLowerCase():g.name).indexOf(s);if(P!==-1&&(l.push({filePath:h(T),lineNumber:0,lineContent:g.name,matchStart:P,matchEnd:P+r.length}),l.length>=n)){w=!0;return}continue}let b;try{b=await o.readFile(T,"utf-8")}catch{continue}const M=b.split(/\r?\n/);for(let R=0;R<M.length;R+=1){const P=M[R],nt=i?P.toLowerCase():P;let j=0;for(;;){const L=nt.indexOf(s,j);if(L===-1)break;if(l.push({filePath:h(T),lineNumber:R+1,lineContent:P,matchStart:L,matchEnd:L+r.length}),l.length>=n){w=!0;return}j=L+1}}}}return await m(e),{workspacePath:h(e),keyword:r,totalFiles:f,totalMatches:l.length,matches:l,truncated:w}}async function at(t,e,r,i,a){let n;try{n=await o.readFile(e,"utf-8")}catch{return{changed:!1,count:0}}const s=n;let c=0;if(a){const u=r.toLowerCase();let l=0;for(;;){const w=n.toLowerCase().indexOf(u,l);if(w===-1)break;n=n.slice(0,w)+i+n.slice(w+r.length),c+=1;const m=i.length||1;l=w+m}}else{let u=0;for(;;){const l=n.indexOf(r,u);if(l===-1)break;n=n.slice(0,l)+i+n.slice(l+r.length),c+=1;const f=i.length||1;u=l+f}}return n===s?{changed:!1,count:0}:(await E(t,e),await o.writeFile(e,n,"utf-8"),{changed:!0,count:c})}async function ee(t){const{workspaceRoot:e}=await k(t.workspacePath),r=String(t.keyword||"").trim();if(!r)throw new Error("keyword is required");const i=String(t.replacement??""),a=!!t.ignoreCase,n=String(t.fileMask||"").trim(),s=String(t.filePath||"").trim();let c=0,u=0;if(s){const{resolvedFilePath:f}=await W({workspacePath:t.workspacePath,filePath:s}),w=await at(e,f,r,i,a);return w.changed&&(c=1,u=w.count),{workspacePath:h(e),keyword:r,replacement:i,changedFiles:c,changedMatches:u}}async function l(f){let w;try{w=await o.readdir(f,{withFileTypes:!0})}catch{return}for(const m of w){const v=p(e,d.join(f,m.name));if(m.isSymbolicLink())continue;if(m.isDirectory()){if(rt(m.name))continue;await l(v);continue}if(!m.isFile()||n&&!et(m.name,n))continue;try{if((await o.stat(v)).size>it)continue}catch{continue}const F=await at(e,v,r,i,a);F.changed&&(c+=1,u+=F.count)}}return await l(e),{workspacePath:h(e),keyword:r,replacement:i,changedFiles:c,changedMatches:u}}export{_t as chmodWorkspaceEntry,Ht as createWorkspaceEntry,Xt as deleteWorkspaceEntry,Ct as extractWorkspaceArchive,qt as getWorkspaceEntryProperties,Ot as listWorkspaceDirectory,Vt as moveWorkspaceEntry,Gt as previewWorkspaceDocument,Ut as readWorkspaceFile,Kt as renameWorkspaceEntry,ee as replaceWorkspaceFiles,Jt as resolveWorkspaceDownloadTarget,te as searchWorkspaceFiles,Qt as streamWorkspaceDirectoryZip,Yt as uploadWorkspaceFiles,Zt as writeWorkspaceFile};
2
2
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.9.96",
3
+ "version": "1.9.98",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",