aws-runtime-bridge 1.9.124 → 1.9.126

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +23 -2
  2. package/dist/adapter/AcodeSdkAdapter.js +4 -4
  3. package/dist/adapter/OpencodeSdkAdapter.js +2 -2
  4. package/dist/adapter/types.js +1 -1
  5. package/dist/config.d.ts +30 -4
  6. package/dist/config.js +3 -1
  7. package/dist/index.js +1 -1
  8. package/dist/routes/acode-todos.js +1 -1
  9. package/dist/routes/background-tasks.js +1 -1
  10. package/dist/routes/dashboard.js +58 -3
  11. package/dist/routes/git.d.ts +28 -0
  12. package/dist/routes/git.js +32 -31
  13. package/dist/routes/terminal.js +24 -24
  14. package/dist/services/auto-register.d.ts +2 -2
  15. package/dist/services/auto-register.js +1 -1
  16. package/dist/services/aws-client-agent-mcp.d.ts +2 -1
  17. package/dist/services/aws-client-agent-mcp.js +2 -2
  18. package/dist/services/cc-switch-sdk.js +1 -1
  19. package/dist/services/cli-commands.js +5 -5
  20. package/dist/services/fileSnapshotStore.js +1 -1
  21. package/dist/services/instance-ai-config-service.js +1 -1
  22. package/dist/services/instance-features.d.ts +2 -0
  23. package/dist/services/instance-features.js +1 -1
  24. package/dist/services/panel-auth.d.ts +2 -0
  25. package/dist/services/panel-auth.js +1 -1
  26. package/dist/services/startup-config-wizard.js +6 -6
  27. package/dist/services/tunnel-client.js +1 -1
  28. package/dist/services/update-notifier.js +2 -2
  29. package/package/acode/dist/ask-user-question-tool.js +2 -2
  30. package/package/acode/dist/background-command-store.js +1 -1
  31. package/package/acode/dist/builtins/commandArtifacts.js +3 -3
  32. package/package/acode/dist/mcpToolNames.d.ts +2 -0
  33. package/package/acode/dist/mcpToolNames.js +1 -1
  34. package/package/acode/dist/meeting-debug-log.d.ts +1 -0
  35. package/package/acode/dist/meeting-debug-log.js +3 -0
  36. package/package/acode/dist/paths.d.ts +2 -0
  37. package/package/acode/dist/paths.js +2 -0
  38. package/package/acode/dist/runtime.d.ts +4 -0
  39. package/package/acode/dist/runtime.js +20 -20
  40. package/package/acode/dist/sub-agent-manager.d.ts +1 -0
  41. package/package/acode/dist/sub-agent-manager.js +1 -1
  42. package/package/acode/dist/sub-agent-store.js +3 -3
  43. package/package/acode/dist/sub-agent-tools.js +27 -31
  44. package/package/acode/dist/sub-agent-types.d.ts +1 -1
  45. package/package/acode/dist/todo-store.js +1 -1
  46. package/package/acode/dist/tool-allocation.d.ts +18 -4
  47. package/package/acode/dist/tool-allocation.js +1 -1
  48. package/package/acode/dist/types.d.ts +0 -3
  49. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +17 -0
  50. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  51. package/package/aws-client-agent-mcp/dist/agent-client.test.js +251 -1
  52. package/package/aws-client-agent-mcp/dist/config.d.ts +2 -0
  53. package/package/aws-client-agent-mcp/dist/config.js +1 -1
  54. package/package/aws-client-agent-mcp/dist/config.test.js +93 -1
  55. package/package/aws-client-agent-mcp/dist/http-client.d.ts +2 -0
  56. package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
  57. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.js +6 -2
  58. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +21 -0
  59. package/package/aws-client-agent-mcp/dist/mcp-server.js +28 -28
  60. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +628 -7
  61. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +10 -0
  62. package/package/aws-client-agent-mcp/dist/mcp-tools.js +12 -10
  63. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +38 -0
  64. package/package/aws-client-agent-mcp/dist/meeting-debug-log.d.ts +13 -0
  65. package/package/aws-client-agent-mcp/dist/meeting-debug-log.js +3 -0
  66. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +2 -0
  67. package/package/aws-client-agent-mcp/dist/message-buffer.js +1 -1
  68. package/package/aws-client-agent-mcp/dist/message-buffer.test.js +60 -0
  69. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +14 -0
  70. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -1
  71. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +39 -1
  72. package/package/aws-client-agent-mcp/dist/types.d.ts +11 -1
  73. package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -1
  74. package/package/aws-client-agent-mcp/package.json +1 -1
  75. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{existsSync as D,readFileSync as B}from"node:fs";import E from"node:os";import M from"node:path";import x from"cors";import I from"express";import{adapterRegistry as O}from"./adapter/index.js";import{attachBrowserWebSocketServer as G}from"./browser/browser-ws-bridge.js";import{allowedCorsOrigins as y,port as h,validateProductionToken as _}from"./config.js";import{attachRemoteDesktopWebSocketServer as F}from"./remote-desktop/index.js";import{acodeTodosRouter as K}from"./routes/acode-todos.js";import{aiSourcesRouter as L}from"./routes/ai-sources.js";import{backgroundTasksRouter as j}from"./routes/background-tasks.js";import{browserListenersRouter as N}from"./routes/browser-listeners.js";import{browserSessionsRouter as W}from"./routes/browser-sessions.js";import{computerRouter as U}from"./routes/computer.js";import{dashboardRouter as z}from"./routes/dashboard.js";import{eventsRouter as J}from"./routes/events.js";import{fileBrowserRouter as Y}from"./routes/file-browser.js";import{fileChangeDiffRouter as q}from"./routes/file-change-diff.js";import{fileDiffRollbackRouter as H}from"./routes/file-diff-rollback.js";import{fileSnapshotRouter as Z}from"./routes/file-snapshot.js";import{gitRouter as Q}from"./routes/git.js";import{instanceRouter as V,setGracefulShutdownFn as X}from"./routes/instance.js";import{jdksRouter as ee}from"./routes/jdks.js";import{launchRouter as re}from"./routes/launch.js";import{mcpRouter as te}from"./routes/mcp.js";import{processesRouter as ie}from"./routes/processes.js";import{propertiesRouter as oe}from"./routes/properties.js";import{attachPtyWebSocketServer as ne,closeAllPtySessions as se,ptyRouter as ae,ptySessions as me}from"./routes/pty.js";import{runtimeBindingRouter as ue,logRuntimeBindingStartupState as ce}from"./routes/runtime-binding.js";import{sessionsRouter as de}from"./routes/sessions.js";import{skillsRouter as pe}from"./routes/skills.js";import{subAgentsRouter as ge}from"./routes/sub-agents.js";import{systemMetricsRouter as fe}from"./routes/system-metrics.js";import{taskContextRouter as le}from"./routes/task-context.js";import{terminalRouter as be,sdkSessions as S}from"./routes/terminal.js";import{ymlRouter as we}from"./routes/yml.js";import{ensureAcodePackageReleased as he}from"./services/acode-package.js";import{accessControlMiddleware as Se}from"./services/access-control.js";import{getAgentProcessManager as P}from"./services/agent-process-manager.js";import{autoRegister as Re,unregister as Ie,bridgeRestartCleanup as ye,cancelBackgroundRegisterRetries as Pe}from"./services/auto-register.js";import{ensureAwsClientAgentMcpReleased as $e}from"./services/aws-client-agent-mcp.js";import{disposeBrowserSessionManager as ke}from"./services/browser-session-manager.js";import{handleCliCommand as Ce}from"./services/cli-commands.js";import{disposeDesktopMcpManager as ve}from"./services/desktop-mcp-manager.js";import{getLaunchManager as Te}from"./services/launch-manager.js";import{migrateOldPathsOnStartup as Ae}from"./services/legacy-path-migration.js";import{deliverPendingBridgeLifecycleNotifications as De}from"./services/lifecycle-state.js";import{startOrphanMonitor as Be,stopOrphanMonitor as Ee}from"./services/orphan-monitor.js";import{detectOrphanProcesses as Me}from"./services/process-detector.js";import{buildGracefulShutdownPlan as xe,shouldCleanupPersistedSessionsOnStartup as Oe}from"./services/runtime-lifecycle-policy.js";import{ensureStartupConfig as Ge}from"./services/startup-config-wizard.js";import{subAgentStore as _e}from"./services/sub-agent-manager-instance.js";import{loadPersistedSessions as $,savePersistedSessions as k}from"./services/terminal-persistence.js";import{stopTunnelClient as Fe}from"./services/tunnel-client.js";import{logger as e}from"./utils/logger.js";const C=await Ce();C.handled&&process.exit(C.exitCode),await Ge(),_(),$e(),he(),Ae(),ce();async function Ke(){try{if(await Re())e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6210\u529F");else{const{loadConfig:o}=await import("./services/auto-register.js"),s=o();!!s.userKey&&s.enabled?e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6682\u65F6\u5931\u8D25\uFF0C\u5C06\u5728\u540E\u53F0\u6301\u7EED\u91CD\u8BD5\u76F4\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u5EFA\u7ACB\u8FDE\u63A5"):(e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u672A\u914D\u7F6E\uFF0C\u7EE7\u7EED\u542F\u52A8\u670D\u52A1"),e.info("[runtime-bridge] \u8981\u542F\u7528\u81EA\u52A8\u6CE8\u518C\uFF0C\u8BF7\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6 ~/.aws-bridge/config.json"))}}catch(r){const o=r;e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u51FA\u9519:",o.message)}}Ke().then(()=>Le()).then(()=>De()).then(()=>{e.info("[runtime-bridge] Bridge \u751F\u547D\u5468\u671F\u901A\u77E5\u540C\u6B65\u5B8C\u6210")}).catch(r=>{e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6216\u6E05\u7406\u51FA\u9519:",r)}),import("./services/update-notifier.js").then(({startUpdateNotifier:r})=>r()).catch(r=>{e.warn("[runtime-bridge] \u7248\u672C\u68C0\u67E5\u670D\u52A1\u542F\u52A8\u5931\u8D25:",r.message)});async function Le(){try{if(process.env.AWS_BRIDGE_RESTART_CLEANUP!=="true"){e.info("[runtime-bridge] \u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406\uFF0C\u4FDD\u7559 Agent \u72B6\u6001\u4F9B\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D");return}const{getRegistrationState:r}=await import("./services/auto-register.js"),o=r();if(!o.registered||!o.instanceId){e.info("[runtime-bridge] \u5B9E\u4F8B\u672A\u6CE8\u518C\uFF0C\u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406");return}e.info("[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5DF2\u663E\u5F0F\u542F\u7528\uFF0C\u6B63\u5728\u901A\u77E5\u8C03\u5EA6\u4E2D\u5FC3\u6E05\u7406 Agent \u72B6\u6001...");const s=await ye();s.success?e.info(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u6210\u529F\uFF0C\u6E05\u7406\u4E86 ${s.agentCount||0} \u4E2A Agent`):e.warn(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5931\u8D25: ${s.error}`)}catch(r){const o=r;e.warn("[runtime-bridge] Bridge \u91CD\u542F\u6E05\u7406\u51FA\u9519:",o.message)}}async function je(){try{const r=await $();if(r.length===0){e.info("[runtime-bridge] \u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u8DF3\u8FC7\u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA");return}await P().rebuildRegistry(r.map(s=>({agentId:s.agentId,sessionId:s.sessionId,pid:s.pid,workspacePath:s.workspacePath,command:s.command,mode:"sdk"}))),e.info(`[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5B8C\u6210: ${r.length} \u6761\u8BB0\u5F55`)}catch(r){const o=r;e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5931\u8D25:",o.message)}}je().catch(r=>{e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u51FA\u9519:",r)});async function Ne(){try{const r=await import("node:fs/promises"),o=await import("node:path"),s=await import("node:os"),f=o.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");let u=!1;try{const t=await r.readFile(f,"utf-8");u=JSON.parse(t).preserveSessions===!0,await r.unlink(f),e.info(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\uFF0CpreserveSessions=${u}`)}catch{}const a=await $();if(a.length===0){e.info("[runtime-bridge] \u542F\u52A8\u65F6\u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u65E0\u9700\u68C0\u6D4B\u5B64\u513F\u8FDB\u7A0B");return}if(!Oe(u)){e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u5E76\u91CD\u5EFA\u8FDB\u7A0B\u6CE8\u518C\u8868`),await P().rebuildRegistry(a.map(n=>({agentId:n.agentId,sessionId:n.sessionId,pid:n.pid,workspacePath:n.workspacePath,command:n.command,mode:"sdk"})));return}e.info(`[runtime-bridge] \u2605 \u5DF2\u663E\u5F0F\u542F\u7528\u542F\u52A8\u6E05\u7406\uFF0C\u68C0\u6D4B ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u7684\u5B64\u513F\u8FDB\u7A0B...`);const d=await Me(a.map(t=>({agentId:t.agentId,pid:t.pid,workspacePath:t.workspacePath,command:t.command})));if(d.length>0){e.warn(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF1A`);for(const t of d)e.warn(` - agentId=${t.agentId}, pid=${t.process.pid}, command=${t.process.command?.slice(0,50)}`);e.info("[runtime-bridge] \u6B63\u5728\u81EA\u52A8\u6E05\u7406\u6240\u6709\u5B64\u513F\u8FDB\u7A0B...");for(const t of d)try{if(t.process.pid)if(process.platform==="win32"){const{execSync:n}=await import("node:child_process");n(`taskkill /PID ${t.process.pid} /T /F`,{encoding:"utf8",timeout:3e3}),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}else process.kill(t.process.pid,"SIGKILL"),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}catch(n){const c=n;e.warn(`[runtime-bridge] \u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B\u5931\u8D25: agentId=${t.agentId}, PID=${t.process.pid}, error=${c.message}`)}await new Promise(t=>setTimeout(t,1e3))}await k([]),e.info(`[runtime-bridge] \u2605 \u542F\u52A8\u6E05\u7406\u5B8C\u6210\uFF0C\u5DF2\u6E05\u7A7A\u6240\u6709\u6301\u4E45\u5316\u4F1A\u8BDD\uFF08\u6E05\u7406\u4E86 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\uFF09`)}catch(r){const o=r;e.warn("[runtime-bridge] \u542F\u52A8\u65F6\u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u5931\u8D25:",o.message)}}Ne().catch(r=>{e.warn("[runtime-bridge] \u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u51FA\u9519:",r)}),Be().catch(r=>{e.warn("[runtime-bridge] \u542F\u52A8\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7\u5931\u8D25:",r)});const i=I();i.use(Se),i.use(x({origin(r,o){if(!r){o(null,!0);return}if(y.includes("*")){o(null,!0);return}if(y.includes(r)){o(null,!0);return}try{if(new URL(r).port===String(h)){o(null,!0);return}}catch{}o(new Error(`[runtime-bridge] CORS origin is not allowed: ${r}`))}})),i.use(I.json({limit:"1mb"})),i.use("/runtime",ue),i.use("/runtime",oe),i.use("/runtime",we),i.use("/runtime",V),i.use("/runtime",pe),i.use("/runtime",te),i.use("/runtime",be),i.use("/runtime",de),i.use("/runtime",ie),i.use("/runtime",re),i.use("/runtime",ee),i.use("/runtime",fe),i.use("/runtime",J),i.use("/runtime",Q),i.use("/runtime",Z),i.use("/runtime",K),i.use("/runtime",j),i.use("/runtime",ge),i.use("/runtime",W),i.use("/runtime",N),i.use("/runtime",L),i.use("/runtime",le),i.use("/runtime",H),i.use("/runtime",q),i.use("/runtime/computer",U),i.use("/pty",ae),i.use("/",z),i.use("/api/file-browser",Y);function We(){const r=i.listen(h,()=>{e.info(`[runtime-bridge] listening on ${h}`)});return ne(r),F(r),G(r),r.on("error",o=>{if(o.code==="EADDRINUSE"){e.error(`[runtime-bridge] port ${h} is already in use. Stop the existing runtime-bridge process or set AWS_RUNTIME_BRIDGE_PORT to another port before starting.`),process.exitCode=1;return}e.error("[runtime-bridge] failed to start server:",o),process.exitCode=1}),r}let v=!1;async function b(r,o,s=!1){if(v){e.info(`[runtime-bridge] \u5DF2\u5728\u5173\u95ED\u4E2D\uFF0C\u5FFD\u7565 ${r}`);return}v=!0,s||R()&&(s=!0,e.info("[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 .preserve-sessions \u6807\u8BB0\u6587\u4EF6\uFF0C\u81EA\u52A8\u5347\u7EA7\u4E3A\u4FDD\u7559\u4F1A\u8BDD\u6A21\u5F0F")),e.info(`[runtime-bridge] \u6536\u5230 ${r} \u4FE1\u53F7\uFF0C\u5F00\u59CB\u4F18\u96C5\u5173\u95ED... (preserveSessions=${s})`);const f=Date.now(),u=xe(s);try{const a=S.size;if(u.terminateSdkSessions){e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${a} \u4E2A SDK \u4F1A\u8BDD...`);for(const[t,n]of S.entries())try{const c=n.providerId||"claude-code",p=O.get(c);let g="";try{const m=p.getSessionPid?.(t);m&&(g=` (PID: ${m})`)}catch{}await p.terminateSession(t),e.info(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u5DF2\u7EC8\u6B62${g}`),S.delete(t)}catch(c){const p=c;e.error(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u7EC8\u6B62\u5931\u8D25:`,p.message),S.delete(t)}}else e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a} \u4E2A SDK \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u7EC8\u6B62 Agent \u8FDB\u7A0B`);const l=me.size;u.closePtySessions?(e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${l} \u4E2A PTY \u4F1A\u8BDD...`),se("shutdown")):e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${l} \u4E2A PTY \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u5173\u95ED\u7EC8\u7AEF\u8FDB\u7A0B`);try{const{running:t,stopped:n}=await Te().stopAll();t>0&&e.info(`[runtime-bridge] \u542F\u52A8\u9879\u8FDB\u7A0B\u5DF2\u505C\u6B62: running=${t}, stopped=${n}`)}catch(t){e.warn("[runtime-bridge] \u505C\u6B62\u542F\u52A8\u9879\u8FDB\u7A0B\u5931\u8D25:",t.message)}e.info("[runtime-bridge] \u6B63\u5728\u505C\u6B62\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7...");try{const c=(await import("file://"+require("path").join(__dirname,"..","package","acode","dist","background-command-manager.js")).catch(()=>({backgroundCommandManager:null}))).backgroundCommandManager;c&&(e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7406\u540E\u53F0\u547D\u4EE4..."),await c.disposeAll())}catch(t){e.warn("[runtime-bridge] \u540E\u53F0\u547D\u4EE4\u6E05\u7406\u5931\u8D25: %s",t?.message)}if(Ee(),u.terminateResidualProcesses&&(e.info("[runtime-bridge] \u7B49\u5F85\u8FDB\u7A0B\u5B8C\u5168\u7EC8\u6B62..."),await new Promise(t=>setTimeout(t,1e3))),u.terminateResidualProcesses)try{const{detectOrphanProcesses:t,terminateProcessTree:n}=await import("./services/process-detector.js"),{loadPersistedSessions:c}=await import("./services/terminal-persistence.js"),p=await c();if(p.length>0){const g=await t(p.map(m=>({agentId:m.agentId,pid:m.pid,workspacePath:m.workspacePath,command:m.command})));if(g.length>0){e.warn(`[runtime-bridge] \u68C0\u6D4B\u5230 ${g.length} \u4E2A\u6B8B\u7559\u8FDB\u7A0B\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`);for(const m of g)if(m.process.pid){const A=await n(m.process.pid);e.info(`[runtime-bridge] \u5DF2\u5F3A\u5236\u7EC8\u6B62\u6B8B\u7559\u8FDB\u7A0B\u6811\uFF1AagentId=${m.agentId}, PID=${m.process.pid}, \u7EC8\u6B62\u4E86 ${A.terminated} \u4E2A\u8FDB\u7A0B`)}}}}catch(t){const n=t;e.warn("[runtime-bridge] \u6B8B\u7559\u8FDB\u7A0B\u68C0\u67E5\u5931\u8D25:",n.message)}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u6B8B\u7559\u8FDB\u7A0B\u5F3A\u5236\u7EC8\u6B62\uFF0C\u7B49\u5F85\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D\u4F1A\u8BDD");if(u.unregisterInstance){e.info("[runtime-bridge] \u6B63\u5728\u6CE8\u9500\u5B9E\u4F8B...");try{await Ie()}catch(t){const n=t;e.error("[runtime-bridge] \u6CE8\u9500\u5B9E\u4F8B\u5931\u8D25:",n.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u5B9E\u4F8B\u6CE8\u9500\uFF0C\u907F\u514D\u8C03\u5EA6\u4E2D\u5FC3\u5C06 Agent \u6807\u8BB0\u4E3A stopped");if(Pe(),u.stopTunnelClient&&(e.info("[runtime-bridge] \u6B63\u5728\u5173\u95ED\u96A7\u9053\u5BA2\u6237\u7AEF..."),Fe()),u.clearPersistedSessions){e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7A7A\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001...");try{await k([])}catch(t){const n=t;e.error("[runtime-bridge] \u6E05\u7A7A\u6301\u4E45\u5316\u72B6\u6001\u5931\u8D25:",n.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001\uFF08aws-mcp-server \u91CD\u542F\u6062\u590D\u573A\u666F\uFF09");try{await ke(),e.info("[runtime-bridge] browser \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] browser \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}try{await ve(),e.info("[runtime-bridge] desktop \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] desktop \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}const d=Date.now()-f;e.info(`[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u5B8C\u6210 (\u8017\u65F6 ${d}ms)\uFF0C\u505C\u6B62 ${a} SDK \u4F1A\u8BDD`),o.close(()=>{e.info("[runtime-bridge] HTTP \u670D\u52A1\u5668\u5DF2\u5173\u95ED"),process.exit(process.exitCode||0)}),setTimeout(()=>{e.warn("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8D85\u65F6\uFF0C\u5F3A\u5236\u9000\u51FA"),process.exit(1)},15e3)}catch(a){const l=a;e.error("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF:",l),process.exit(1)}}_e.reconcileZombies().then(r=>{r>0&&e.info(`[runtime-bridge] \u5DF2\u6E05\u7406 ${r} \u4E2A\u50F5\u5C38 sub-agent \u8BB0\u5F55`)}).catch(r=>{e.warn("[runtime-bridge] \u6E05\u7406\u50F5\u5C38 sub-agent \u8BB0\u5F55\u5931\u8D25:",r.message)});const w=We(),T=M.join(E.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");function R(){try{if(D(T)){const r=B(T,"utf-8");return JSON.parse(r).preserveSessions===!0}}catch{}return!1}X(async(r,o)=>{await b(r,w,o)}),process.on("SIGTERM",()=>{const r=R();b("SIGTERM",w,r)}),process.on("SIGINT",()=>{const r=R();b("SIGINT",w,r)}),process.platform==="win32"&&process.on("SIGBREAK",()=>{const r=R();b("SIGBREAK",w,r)}),process.on("uncaughtException",r=>{e.error("[runtime-bridge] \u672A\u6355\u83B7\u5F02\u5E38:",r),b("uncaughtException",w)}),process.on("unhandledRejection",r=>{e.error("[runtime-bridge] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",r)});
2
+ import{existsSync as E,readFileSync as T}from"node:fs";import B from"node:os";import R from"node:path";import M from"cors";import P from"express";import{adapterRegistry as O}from"./adapter/index.js";import{attachBrowserWebSocketServer as x}from"./browser/browser-ws-bridge.js";import{getActiveBridgeHttpPort as I,getAutoRegisterConfigFilePath as _,getBridgeRuntimePropertiesFilePath as F,getRuntimeHomeDir as G,reserveBridgeListenPort as K,validateProductionToken as L,writeBridgeRuntimePropertiesFile as j}from"./config.js";import{attachRemoteDesktopWebSocketServer as N}from"./remote-desktop/index.js";import{acodeTodosRouter as H}from"./routes/acode-todos.js";import{aiSourcesRouter as W}from"./routes/ai-sources.js";import{backgroundTasksRouter as U}from"./routes/background-tasks.js";import{browserListenersRouter as z}from"./routes/browser-listeners.js";import{browserSessionsRouter as J}from"./routes/browser-sessions.js";import{computerRouter as Y}from"./routes/computer.js";import{dashboardRouter as q}from"./routes/dashboard.js";import{eventsRouter as Z}from"./routes/events.js";import{fileBrowserRouter as Q}from"./routes/file-browser.js";import{fileChangeDiffRouter as V}from"./routes/file-change-diff.js";import{fileDiffRollbackRouter as X}from"./routes/file-diff-rollback.js";import{fileSnapshotRouter as ee}from"./routes/file-snapshot.js";import{gitRouter as re}from"./routes/git.js";import{instanceRouter as te,setGracefulShutdownFn as ie}from"./routes/instance.js";import{jdksRouter as oe}from"./routes/jdks.js";import{launchRouter as ne}from"./routes/launch.js";import{mcpRouter as se}from"./routes/mcp.js";import{processesRouter as ae}from"./routes/processes.js";import{propertiesRouter as me}from"./routes/properties.js";import{attachPtyWebSocketServer as ue,closeAllPtySessions as ce,ptyRouter as de,ptySessions as ge}from"./routes/pty.js";import{runtimeBindingRouter as pe,logRuntimeBindingStartupState as fe}from"./routes/runtime-binding.js";import{sessionsRouter as le}from"./routes/sessions.js";import{skillsRouter as be}from"./routes/skills.js";import{subAgentsRouter as we}from"./routes/sub-agents.js";import{systemMetricsRouter as he}from"./routes/system-metrics.js";import{taskContextRouter as Se}from"./routes/task-context.js";import{terminalRouter as Re,sdkSessions as h}from"./routes/terminal.js";import{ymlRouter as Pe}from"./routes/yml.js";import{ensureAcodePackageReleased as Ie}from"./services/acode-package.js";import{accessControlMiddleware as ye}from"./services/access-control.js";import{getAgentProcessManager as y}from"./services/agent-process-manager.js";import{autoRegister as $e,unregister as ke,bridgeRestartCleanup as Ce,cancelBackgroundRegisterRetries as ve}from"./services/auto-register.js";import{ensureAwsClientAgentMcpReleased as Ae}from"./services/aws-client-agent-mcp.js";import{disposeBrowserSessionManager as De}from"./services/browser-session-manager.js";import{handleCliCommand as Ee}from"./services/cli-commands.js";import{disposeDesktopMcpManager as Te}from"./services/desktop-mcp-manager.js";import{getLaunchManager as Be}from"./services/launch-manager.js";import{migrateOldPathsOnStartup as Me}from"./services/legacy-path-migration.js";import{deliverPendingBridgeLifecycleNotifications as Oe}from"./services/lifecycle-state.js";import{startOrphanMonitor as xe,stopOrphanMonitor as _e}from"./services/orphan-monitor.js";import{detectOrphanProcesses as Fe}from"./services/process-detector.js";import{buildGracefulShutdownPlan as Ge,shouldCleanupPersistedSessionsOnStartup as Ke}from"./services/runtime-lifecycle-policy.js";import{ensureStartupConfig as Le}from"./services/startup-config-wizard.js";import{subAgentStore as je}from"./services/sub-agent-manager-instance.js";import{loadPersistedSessions as $,savePersistedSessions as k}from"./services/terminal-persistence.js";import{stopTunnelClient as Ne}from"./services/tunnel-client.js";import{logger as e}from"./utils/logger.js";const C=await Ee();C.handled&&process.exit(C.exitCode),await Le(),L(),Ae(),Ie(),Me(),fe(),await K(),j(),e.info(`[runtime-bridge] \u5DF2\u5199\u5165\u8FD0\u884C\u65F6\u914D\u7F6E ${F()}\uFF08port=${I()}\uFF09`),String(process.env.ACODE_HOME||"").trim()||(process.env.ACODE_HOME=R.join(G(),".acode")),e.info(`[runtime-bridge] ACode \u6570\u636E\u76EE\u5F55: ${process.env.ACODE_HOME}`);async function He(){try{if(await $e())e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6210\u529F");else{const{loadConfig:o}=await import("./services/auto-register.js"),s=o();!!s.userKey&&s.enabled?e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6682\u65F6\u5931\u8D25\uFF0C\u5C06\u5728\u540E\u53F0\u6301\u7EED\u91CD\u8BD5\u76F4\u5230\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\u5EFA\u7ACB\u8FDE\u63A5"):(e.info("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u672A\u914D\u7F6E\uFF0C\u7EE7\u7EED\u542F\u52A8\u670D\u52A1"),e.info(`[runtime-bridge] \u8981\u542F\u7528\u81EA\u52A8\u6CE8\u518C\uFF0C\u8BF7\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6 ${_()}`))}}catch(r){const o=r;e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u51FA\u9519:",o.message)}}He().then(()=>We()).then(()=>Oe()).then(()=>{e.info("[runtime-bridge] Bridge \u751F\u547D\u5468\u671F\u901A\u77E5\u540C\u6B65\u5B8C\u6210")}).catch(r=>{e.warn("[runtime-bridge] \u81EA\u52A8\u6CE8\u518C\u6216\u6E05\u7406\u51FA\u9519:",r)}),import("./services/update-notifier.js").then(({startUpdateNotifier:r})=>r()).catch(r=>{e.warn("[runtime-bridge] \u7248\u672C\u68C0\u67E5\u670D\u52A1\u542F\u52A8\u5931\u8D25:",r.message)});async function We(){try{if(process.env.AWS_BRIDGE_RESTART_CLEANUP!=="true"){e.info("[runtime-bridge] \u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406\uFF0C\u4FDD\u7559 Agent \u72B6\u6001\u4F9B\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D");return}const{getRegistrationState:r}=await import("./services/auto-register.js"),o=r();if(!o.registered||!o.instanceId){e.info("[runtime-bridge] \u5B9E\u4F8B\u672A\u6CE8\u518C\uFF0C\u8DF3\u8FC7 Bridge \u91CD\u542F\u6E05\u7406");return}e.info("[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5DF2\u663E\u5F0F\u542F\u7528\uFF0C\u6B63\u5728\u901A\u77E5\u8C03\u5EA6\u4E2D\u5FC3\u6E05\u7406 Agent \u72B6\u6001...");const s=await Ce();s.success?e.info(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u6210\u529F\uFF0C\u6E05\u7406\u4E86 ${s.agentCount||0} \u4E2A Agent`):e.warn(`[runtime-bridge] \u2605 Bridge \u91CD\u542F\u6E05\u7406\u5931\u8D25: ${s.error}`)}catch(r){const o=r;e.warn("[runtime-bridge] Bridge \u91CD\u542F\u6E05\u7406\u51FA\u9519:",o.message)}}async function Ue(){try{const r=await $();if(r.length===0){e.info("[runtime-bridge] \u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u8DF3\u8FC7\u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA");return}await y().rebuildRegistry(r.map(s=>({agentId:s.agentId,sessionId:s.sessionId,pid:s.pid,workspacePath:s.workspacePath,command:s.command,mode:"sdk"}))),e.info(`[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5B8C\u6210: ${r.length} \u6761\u8BB0\u5F55`)}catch(r){const o=r;e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u5931\u8D25:",o.message)}}Ue().catch(r=>{e.warn("[runtime-bridge] \u8FDB\u7A0B\u6CE8\u518C\u8868\u91CD\u5EFA\u51FA\u9519:",r)});async function ze(){try{const r=await import("node:fs/promises"),o=await import("node:path"),s=await import("node:os"),f=o.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");let u=!1;try{const t=await r.readFile(f,"utf-8");u=JSON.parse(t).preserveSessions===!0,await r.unlink(f),e.info(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\uFF0CpreserveSessions=${u}`)}catch{}const a=await $();if(a.length===0){e.info("[runtime-bridge] \u542F\u52A8\u65F6\u65E0\u6301\u4E45\u5316\u4F1A\u8BDD\uFF0C\u65E0\u9700\u68C0\u6D4B\u5B64\u513F\u8FDB\u7A0B");return}if(!Ke(u)){e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u5E76\u91CD\u5EFA\u8FDB\u7A0B\u6CE8\u518C\u8868`),await y().rebuildRegistry(a.map(n=>({agentId:n.agentId,sessionId:n.sessionId,pid:n.pid,workspacePath:n.workspacePath,command:n.command,mode:"sdk"})));return}e.info(`[runtime-bridge] \u2605 \u5DF2\u663E\u5F0F\u542F\u7528\u542F\u52A8\u6E05\u7406\uFF0C\u68C0\u6D4B ${a.length} \u4E2A\u6301\u4E45\u5316\u4F1A\u8BDD\u7684\u5B64\u513F\u8FDB\u7A0B...`);const d=await Fe(a.map(t=>({agentId:t.agentId,pid:t.pid,workspacePath:t.workspacePath,command:t.command})));if(d.length>0){e.warn(`[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF1A`);for(const t of d)e.warn(` - agentId=${t.agentId}, pid=${t.process.pid}, command=${t.process.command?.slice(0,50)}`);e.info("[runtime-bridge] \u6B63\u5728\u81EA\u52A8\u6E05\u7406\u6240\u6709\u5B64\u513F\u8FDB\u7A0B...");for(const t of d)try{if(t.process.pid)if(process.platform==="win32"){const{execSync:n}=await import("node:child_process");n(`taskkill /PID ${t.process.pid} /T /F`,{encoding:"utf8",timeout:3e3}),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}else process.kill(t.process.pid,"SIGKILL"),e.info(`[runtime-bridge] \u5DF2\u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B: agentId=${t.agentId}, PID=${t.process.pid}`)}catch(n){const c=n;e.warn(`[runtime-bridge] \u81EA\u52A8\u7EC8\u6B62\u5B64\u513F\u8FDB\u7A0B\u5931\u8D25: agentId=${t.agentId}, PID=${t.process.pid}, error=${c.message}`)}await new Promise(t=>setTimeout(t,1e3))}await k([]),e.info(`[runtime-bridge] \u2605 \u542F\u52A8\u6E05\u7406\u5B8C\u6210\uFF0C\u5DF2\u6E05\u7A7A\u6240\u6709\u6301\u4E45\u5316\u4F1A\u8BDD\uFF08\u6E05\u7406\u4E86 ${d.length} \u4E2A\u5B64\u513F\u8FDB\u7A0B\uFF09`)}catch(r){const o=r;e.warn("[runtime-bridge] \u542F\u52A8\u65F6\u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u5931\u8D25:",o.message)}}ze().catch(r=>{e.warn("[runtime-bridge] \u5B64\u513F\u8FDB\u7A0B\u68C0\u6D4B\u51FA\u9519:",r)}),xe().catch(r=>{e.warn("[runtime-bridge] \u542F\u52A8\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7\u5931\u8D25:",r)});const i=P();i.use(ye),i.use(M()),i.use(P.json({limit:"1mb"})),i.use("/runtime",pe),i.use("/runtime",me),i.use("/runtime",Pe),i.use("/runtime",te),i.use("/runtime",be),i.use("/runtime",se),i.use("/runtime",Re),i.use("/runtime",le),i.use("/runtime",ae),i.use("/runtime",ne),i.use("/runtime",oe),i.use("/runtime",he),i.use("/runtime",Z),i.use("/runtime",re),i.use("/runtime",ee),i.use("/runtime",H),i.use("/runtime",U),i.use("/runtime",we),i.use("/runtime",J),i.use("/runtime",z),i.use("/runtime",W),i.use("/runtime",Se),i.use("/runtime",X),i.use("/runtime",V),i.use("/runtime/computer",Y),i.use("/pty",de),i.use("/",q),i.use("/api/file-browser",Q);function Je(){const r=I(),o=i.listen(r,()=>{e.info(`[runtime-bridge] listening on ${r}`)});return ue(o),N(o),x(o),o.on("error",s=>{if(s.code==="EADDRINUSE"){e.error(`[runtime-bridge] port ${r} became unavailable after reservation (another process grabbed it concurrently). Set AWS_RUNTIME_BRIDGE_PORT to a free port and restart.`),process.exitCode=1;return}e.error("[runtime-bridge] failed to start server:",s),process.exitCode=1}),o}let v=!1;async function b(r,o,s=!1){if(v){e.info(`[runtime-bridge] \u5DF2\u5728\u5173\u95ED\u4E2D\uFF0C\u5FFD\u7565 ${r}`);return}v=!0,s||S()&&(s=!0,e.info("[runtime-bridge] \u2605 \u68C0\u6D4B\u5230 .preserve-sessions \u6807\u8BB0\u6587\u4EF6\uFF0C\u81EA\u52A8\u5347\u7EA7\u4E3A\u4FDD\u7559\u4F1A\u8BDD\u6A21\u5F0F")),e.info(`[runtime-bridge] \u6536\u5230 ${r} \u4FE1\u53F7\uFF0C\u5F00\u59CB\u4F18\u96C5\u5173\u95ED... (preserveSessions=${s})`);const f=Date.now(),u=Ge(s);try{const a=h.size;if(u.terminateSdkSessions){e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${a} \u4E2A SDK \u4F1A\u8BDD...`);for(const[t,n]of h.entries())try{const c=n.providerId||"claude-code",g=O.get(c);let p="";try{const m=g.getSessionPid?.(t);m&&(p=` (PID: ${m})`)}catch{}await g.terminateSession(t),e.info(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u5DF2\u7EC8\u6B62${p}`),h.delete(t)}catch(c){const g=c;e.error(`[runtime-bridge] SDK \u4F1A\u8BDD ${t} \u7EC8\u6B62\u5931\u8D25:`,g.message),h.delete(t)}}else e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${a} \u4E2A SDK \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u7EC8\u6B62 Agent \u8FDB\u7A0B`);const l=ge.size;u.closePtySessions?(e.info(`[runtime-bridge] \u6B63\u5728\u505C\u6B62 ${l} \u4E2A PTY \u4F1A\u8BDD...`),ce("shutdown")):e.info(`[runtime-bridge] \u2605 \u4FDD\u7559 ${l} \u4E2A PTY \u4F1A\u8BDD\uFF0C\u4E0D\u5728\u91CD\u542F\u6062\u590D\u573A\u666F\u4E2D\u5173\u95ED\u7EC8\u7AEF\u8FDB\u7A0B`);try{const{running:t,stopped:n}=await Be().stopAll();t>0&&e.info(`[runtime-bridge] \u542F\u52A8\u9879\u8FDB\u7A0B\u5DF2\u505C\u6B62: running=${t}, stopped=${n}`)}catch(t){e.warn("[runtime-bridge] \u505C\u6B62\u542F\u52A8\u9879\u8FDB\u7A0B\u5931\u8D25:",t.message)}e.info("[runtime-bridge] \u6B63\u5728\u505C\u6B62\u5B64\u513F\u8FDB\u7A0B\u76D1\u63A7...");try{const c=(await import("file://"+require("path").join(__dirname,"..","package","acode","dist","background-command-manager.js")).catch(()=>({backgroundCommandManager:null}))).backgroundCommandManager;c&&(e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7406\u540E\u53F0\u547D\u4EE4..."),await c.disposeAll())}catch(t){e.warn("[runtime-bridge] \u540E\u53F0\u547D\u4EE4\u6E05\u7406\u5931\u8D25: %s",t?.message)}if(_e(),u.terminateResidualProcesses&&(e.info("[runtime-bridge] \u7B49\u5F85\u8FDB\u7A0B\u5B8C\u5168\u7EC8\u6B62..."),await new Promise(t=>setTimeout(t,1e3))),u.terminateResidualProcesses)try{const{detectOrphanProcesses:t,terminateProcessTree:n}=await import("./services/process-detector.js"),{loadPersistedSessions:c}=await import("./services/terminal-persistence.js"),g=await c();if(g.length>0){const p=await t(g.map(m=>({agentId:m.agentId,pid:m.pid,workspacePath:m.workspacePath,command:m.command})));if(p.length>0){e.warn(`[runtime-bridge] \u68C0\u6D4B\u5230 ${p.length} \u4E2A\u6B8B\u7559\u8FDB\u7A0B\uFF0C\u6B63\u5728\u5F3A\u5236\u7EC8\u6B62...`);for(const m of p)if(m.process.pid){const D=await n(m.process.pid);e.info(`[runtime-bridge] \u5DF2\u5F3A\u5236\u7EC8\u6B62\u6B8B\u7559\u8FDB\u7A0B\u6811\uFF1AagentId=${m.agentId}, PID=${m.process.pid}, \u7EC8\u6B62\u4E86 ${D.terminated} \u4E2A\u8FDB\u7A0B`)}}}}catch(t){const n=t;e.warn("[runtime-bridge] \u6B8B\u7559\u8FDB\u7A0B\u68C0\u67E5\u5931\u8D25:",n.message)}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u6B8B\u7559\u8FDB\u7A0B\u5F3A\u5236\u7EC8\u6B62\uFF0C\u7B49\u5F85\u8C03\u5EA6\u4E2D\u5FC3\u6062\u590D\u4F1A\u8BDD");if(u.unregisterInstance){e.info("[runtime-bridge] \u6B63\u5728\u6CE8\u9500\u5B9E\u4F8B...");try{await ke()}catch(t){const n=t;e.error("[runtime-bridge] \u6CE8\u9500\u5B9E\u4F8B\u5931\u8D25:",n.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6A21\u5F0F\u8DF3\u8FC7\u5B9E\u4F8B\u6CE8\u9500\uFF0C\u907F\u514D\u8C03\u5EA6\u4E2D\u5FC3\u5C06 Agent \u6807\u8BB0\u4E3A stopped");if(ve(),u.stopTunnelClient&&(e.info("[runtime-bridge] \u6B63\u5728\u5173\u95ED\u96A7\u9053\u5BA2\u6237\u7AEF..."),Ne()),u.clearPersistedSessions){e.info("[runtime-bridge] \u6B63\u5728\u6E05\u7A7A\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001...");try{await k([])}catch(t){const n=t;e.error("[runtime-bridge] \u6E05\u7A7A\u6301\u4E45\u5316\u72B6\u6001\u5931\u8D25:",n.message)}}else e.info("[runtime-bridge] \u2605 \u4FDD\u7559\u6301\u4E45\u5316\u4F1A\u8BDD\u72B6\u6001\uFF08aws-mcp-server \u91CD\u542F\u6062\u590D\u573A\u666F\uFF09");try{await De(),e.info("[runtime-bridge] browser \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] browser \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}try{await Te(),e.info("[runtime-bridge] desktop \u5DE5\u5177\u6808\u5DF2\u6E05\u7406")}catch(t){e.warn("[runtime-bridge] desktop \u5DE5\u5177\u6808\u6E05\u7406\u5931\u8D25:",t.message)}const d=Date.now()-f;e.info(`[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u5B8C\u6210 (\u8017\u65F6 ${d}ms)\uFF0C\u505C\u6B62 ${a} SDK \u4F1A\u8BDD`),o.close(()=>{e.info("[runtime-bridge] HTTP \u670D\u52A1\u5668\u5DF2\u5173\u95ED"),process.exit(process.exitCode||0)}),setTimeout(()=>{e.warn("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8D85\u65F6\uFF0C\u5F3A\u5236\u9000\u51FA"),process.exit(1)},15e3)}catch(a){const l=a;e.error("[runtime-bridge] \u4F18\u96C5\u5173\u95ED\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF:",l),process.exit(1)}}je.reconcileZombies().then(r=>{r>0&&e.info(`[runtime-bridge] \u5DF2\u6E05\u7406 ${r} \u4E2A\u50F5\u5C38 sub-agent \u8BB0\u5F55`)}).catch(r=>{e.warn("[runtime-bridge] \u6E05\u7406\u50F5\u5C38 sub-agent \u8BB0\u5F55\u5931\u8D25:",r.message)});const w=Je(),A=R.join(B.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");function S(){try{if(E(A)){const r=T(A,"utf-8");return JSON.parse(r).preserveSessions===!0}}catch{}return!1}ie(async(r,o)=>{await b(r,w,o)}),process.on("SIGTERM",()=>{const r=S();b("SIGTERM",w,r)}),process.on("SIGINT",()=>{const r=S();b("SIGINT",w,r)}),process.platform==="win32"&&process.on("SIGBREAK",()=>{const r=S();b("SIGBREAK",w,r)}),process.on("uncaughtException",r=>{e.error("[runtime-bridge] \u672A\u6355\u83B7\u5F02\u5E38:",r),b("uncaughtException",w)}),process.on("unhandledRejection",r=>{e.error("[runtime-bridge] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",r)});
3
3
 
@@ -1,2 +1,2 @@
1
- import{promises as u}from"node:fs";import g from"node:os";import i from"node:path";import{Router as f}from"express";import{validateToken as p}from"../middleware/auth.js";import{createLogger as m}from"../utils/logger.js";const c=m("acode-todos"),l=f(),A=i.join(g.homedir(),".acode","todos");function y(e){return String(e).replace(/[^a-zA-Z0-9_-]/g,"_")}function I(e){return i.join(A,`${y(e)}.json`)}l.get("/acode-todos/:agentId",p,async(e,n)=>{const s=e.params.agentId,r=Array.isArray(s)?s[0]:s;if(!r||!String(r).trim()){n.status(400).json({ok:!1,error:"agentId is required"});return}const d=I(String(r));try{const o=await u.readFile(d,"utf-8"),t=JSON.parse(o),a=Array.isArray(t.todos)?t.todos:[];n.json({ok:!0,agentId:String(r),updatedAt:typeof t.updatedAt=="string"?t.updatedAt:null,background:typeof t.background=="string"?t.background:"",todos:a})}catch(o){if((typeof o=="object"&&o!==null?Reflect.get(o,"code"):void 0)==="ENOENT"){n.json({ok:!0,agentId:String(r),updatedAt:null,todos:[]});return}if(o instanceof SyntaxError){c.warn("[acode-todos] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",d),n.json({ok:!0,agentId:String(r),updatedAt:null,todos:[]});return}const a=o;c.error("[acode-todos] \u8BFB\u53D6\u5931\u8D25: %s",a.message),n.status(500).json({ok:!1,error:a.message})}});export{l as acodeTodosRouter};
1
+ import{promises as c}from"node:fs";import g from"node:path";import{Router as u}from"express";import{getAcodeDataHome as f}from"../config.js";import{validateToken as p}from"../middleware/auth.js";import{createLogger as m}from"../utils/logger.js";const i=m("acode-todos"),l=u();function A(r){return String(r).replace(/[^a-zA-Z0-9_-]/g,"_")}function y(r){return g.join(f(),"todos",`${A(r)}.json`)}l.get("/acode-todos/:agentId",p,async(r,n)=>{const s=r.params.agentId,e=Array.isArray(s)?s[0]:s;if(!e||!String(e).trim()){n.status(400).json({ok:!1,error:"agentId is required"});return}const d=y(String(e));try{const t=await c.readFile(d,"utf-8"),o=JSON.parse(t),a=Array.isArray(o.todos)?o.todos:[];n.json({ok:!0,agentId:String(e),updatedAt:typeof o.updatedAt=="string"?o.updatedAt:null,background:typeof o.background=="string"?o.background:"",todos:a})}catch(t){if((typeof t=="object"&&t!==null?Reflect.get(t,"code"):void 0)==="ENOENT"){n.json({ok:!0,agentId:String(e),updatedAt:null,todos:[]});return}if(t instanceof SyntaxError){i.warn("[acode-todos] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",d),n.json({ok:!0,agentId:String(e),updatedAt:null,todos:[]});return}const a=t;i.error("[acode-todos] \u8BFB\u53D6\u5931\u8D25: %s",a.message),n.status(500).json({ok:!1,error:a.message})}});export{l as acodeTodosRouter};
2
2
 
@@ -1,2 +1,2 @@
1
- import{promises as k}from"node:fs";import l from"node:os";import c from"node:path";import{Router as I}from"express";import{validateToken as u}from"../middleware/auth.js";import{createLogger as A}from"../utils/logger.js";import{listPtySessions as y}from"./pty.js";const g=A("acode-bg-tasks"),m=I(),S=c.join(l.homedir(),".acode","bg-tasks");function j(r){return String(r).replace(/[^a-zA-Z0-9_-]/g,"_")}function w(r){return c.join(S,j(r),"state.json")}m.get("/acode-bg-tasks/:agentId",u,async(r,s)=>{const o=r.params.agentId,e=Array.isArray(o)?o[0]:o;if(!e||!String(e).trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}const i=w(String(e));try{const a=await k.readFile(i,"utf-8"),n=JSON.parse(a),f=(Array.isArray(n.tasks)?n.tasks:[]).map(p=>{const t=p;return{taskId:t.taskId,status:t.status,command:t.commandPreview??t.command,commandPreview:t.commandPreview,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}});s.json({ok:!0,agentId:String(e),updatedAt:typeof n.updatedAt=="number"?n.updatedAt:null,tasks:f})}catch(a){if((typeof a=="object"&&a!==null?Reflect.get(a,"code"):void 0)==="ENOENT"){s.json({ok:!0,agentId:String(e),updatedAt:null,tasks:[]});return}if(a instanceof SyntaxError){g.warn("[acode-bg-tasks] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",i),s.json({ok:!0,agentId:String(e),updatedAt:null,tasks:[]});return}const d=a;g.error("[acode-bg-tasks] \u8BFB\u53D6\u5931\u8D25: %s",d.message),s.status(500).json({ok:!1,error:d.message})}}),m.get("/acode-agent-pty-sessions/:agentId",u,(r,s)=>{const o=r.params.agentId,e=Array.isArray(o)?o[0]:o;if(!e||!String(e).trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}const i=String(e),n=y().filter(d=>d.ownerUserId===i);s.json({ok:!0,agentId:i,sessions:n})});export{m as backgroundTasksRouter};
1
+ import{promises as p}from"node:fs";import k from"node:path";import{Router as l}from"express";import{getAcodeDataHome as A}from"../config.js";import{validateToken as c}from"../middleware/auth.js";import{createLogger as I}from"../utils/logger.js";import{listPtySessions as y}from"./pty.js";const u=I("acode-bg-tasks"),g=l();function S(r){return String(r).replace(/[^a-zA-Z0-9_-]/g,"_")}function w(r){return k.join(A(),"bg-tasks",S(r),"state.json")}g.get("/acode-bg-tasks/:agentId",c,async(r,s)=>{const o=r.params.agentId,e=Array.isArray(o)?o[0]:o;if(!e||!String(e).trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}const d=w(String(e));try{const a=await p.readFile(d,"utf-8"),n=JSON.parse(a),m=(Array.isArray(n.tasks)?n.tasks:[]).map(f=>{const t=f;return{taskId:t.taskId,status:t.status,command:t.commandPreview??t.command,commandPreview:t.commandPreview,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}});s.json({ok:!0,agentId:String(e),updatedAt:typeof n.updatedAt=="number"?n.updatedAt:null,tasks:m})}catch(a){if((typeof a=="object"&&a!==null?Reflect.get(a,"code"):void 0)==="ENOENT"){s.json({ok:!0,agentId:String(e),updatedAt:null,tasks:[]});return}if(a instanceof SyntaxError){u.warn("[acode-bg-tasks] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",d),s.json({ok:!0,agentId:String(e),updatedAt:null,tasks:[]});return}const i=a;u.error("[acode-bg-tasks] \u8BFB\u53D6\u5931\u8D25: %s",i.message),s.status(500).json({ok:!1,error:i.message})}}),g.get("/acode-agent-pty-sessions/:agentId",c,(r,s)=>{const o=r.params.agentId,e=Array.isArray(o)?o[0]:o;if(!e||!String(e).trim()){s.status(400).json({ok:!1,error:"agentId is required"});return}const d=String(e),n=y().filter(i=>i.ownerUserId===d);s.json({ok:!0,agentId:d,sessions:n})});export{g as backgroundTasksRouter};
2
2
 
@@ -1,4 +1,4 @@
1
- import{Router as $}from"express";import v from"node:fs";import K from"node:path";import w from"node:os";import{execFile as Y}from"node:child_process";import{getRuntimeBindingPublicState as H,getRuntimePairingCode as Q,normalizeSchedulerBaseUrl as X}from"../services/runtime-binding.js";import{getAgentProcessManager as C}from"../services/agent-process-manager.js";import{collectSystemMetrics as Z}from"../services/system-metrics.js";import{loadPersistedSessions as ee}from"../services/terminal-persistence.js";import{createLogger as te}from"../utils/logger.js";import{getBridgeVersion as ne}from"../config.js";import{runExclusiveConfigWrite as O}from"../utils/config-write-queue.js";import{sdkSessions as se}from"./terminal.js";import{createSession as re,getPanelCredentials as N,validateSession as U,destroySession as z,validateCredentials as F,hasConfiguredCredentials as B,isDefaultCredentials as ae,setPanelCredentials as oe,getAllSessions as de,getActiveSessionCount as ie,checkLoginAllowed as le,recordLoginFailure as ce,recordLoginSuccess as ue}from"../services/panel-auth.js";import{getRecentLogs as ge}from"../utils/logger.js";import{autoRegister as pe,getRegistrationState as _,getAutoRegisterConfigFilePath as E}from"../services/auto-register.js";import{getAccessControlEntries as fe,isValidIpEntry as me,parseAllowIps as he,refreshAccessControlCache as be}from"../services/access-control.js";const x=te("dashboard");let T=null;const ve=1e4;let L=null,P=null;const ye=3e4;async function D(){Z().then(t=>{L={data:t,ts:Date.now()}}).catch(()=>{}),C().discoverAllProcesses().then(t=>{P={data:t,ts:Date.now()}}).catch(()=>{}),C().cleanupStaleRegistry().catch(()=>{})}D(),setInterval(D,ye);const V=ne();async function xe(){const t=await import("node:fs/promises"),e=await import("node:path"),n=await import("node:os"),s=e.join(String(process.env.AWS_RUNTIME_HOME_DIR||n.homedir()).trim()||n.homedir(),".aws-bridge","lifecycle-state.json");try{const d=await t.readFile(s,"utf8"),r=JSON.parse(d),a=Array.isArray(r.operations)?r.operations:[];return a.length===0?null:(a.sort((o,l)=>new Date(l.updatedAt).getTime()-new Date(o.updatedAt).getTime()),a[0]||null)}catch{return null}}const g=$(),J="panel_sid";function we(t){const e={};if(!t)return e;for(const n of t.split(";")){const s=n.indexOf("=");if(s===-1)continue;const d=n.slice(0,s).trim(),r=n.slice(s+1).trim();d&&(e[d]=r)}return e}function A(t){return we(t.headers.cookie)[J]}function W(t,e,n){const s=t.secure||t.headers["x-forwarded-proto"]==="https";return`${J}=${e}; HttpOnly; SameSite=Lax; Path=/${s?"; Secure":""}; Max-Age=${n}`}function m(t,e){const n=A(t);return n&&U(n)?!0:t.xhr||t.headers.accept?.includes("application/json")?(e.status(401).json({ok:!1,error:"unauthorized",needLogin:!0}),!1):(e.redirect("/login"),!1)}g.post("/runtime/login",(t,e)=>{const n=t.socket.remoteAddress||"unknown",s=le(n);if(!s.allowed){e.status(429).set("Retry-After",String(s.retryAfterSec)).json({ok:!1,error:"too many failed login attempts, try again later",retryAfterSec:s.retryAfterSec});return}const{username:d,password:r}=t.body||{};if(!d||!r){e.status(400).json({ok:!1,error:"username and password required"});return}if(!F(String(d),String(r))){ce(n),e.status(401).json({ok:!1,error:"invalid credentials"});return}ue(n);const a=re(),o=!B();e.setHeader("Set-Cookie",W(t,a,86400)),e.json({ok:!0,needSetup:o})}),g.post("/runtime/logout",(t,e)=>{const n=A(t);n&&z(n),e.setHeader("Set-Cookie",W(t,"",0)),e.json({ok:!0})}),g.get("/runtime/credentials-status",(t,e)=>{if(!m(t,e))return;const n=N();e.json({ok:!0,isDefault:ae(),configured:B(),username:n.username})}),g.post("/runtime/change-credentials",async(t,e)=>{if(!m(t,e))return;const{username:n,password:s,passwordConfirm:d,oldPassword:r}=t.body||{};if(s&&String(s).trim()){if(String(s)!==String(d)){e.status(400).json({ok:!1,error:"passwords do not match"});return}if(B()){if(!r){e.status(400).json({ok:!1,error:"old password required"});return}const o=N();if(!F(o.username,String(r))){e.status(403).json({ok:!1,error:"old password is incorrect"});return}}}if(!n||!String(n).trim()){e.status(400).json({ok:!1,error:"username required"});return}const a=s&&String(s).trim()?String(s).trim():N().password;try{await oe(String(n).trim(),a),e.json({ok:!0})}catch(o){const l=o;x.error("Failed to change panel credentials:",l.message),e.status(500).json({ok:!1,error:l.message})}});function q(t){const e=typeof t=="string"?t.trim():"";if(e)return e.length<8?"\u2022\u2022\u2022\u2022":"\u2022\u2022\u2022\u2022"+e.slice(-4)}function G(t){const e=Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets.filter(s=>!!(s&&typeof s=="object"&&!Array.isArray(s))):[];return e.length>0?e:["serverUrl","schedulerBaseUrl","userKey"].some(s=>!!String(t[s]??"").trim())?[t]:[]}const Se=["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","projectId","roleName","workspacePath","registerAddr","registerIp","virtualIp","tunnel","preferSameLanIp","maxRetries","retryInterval"];function Ie(t){const e={};for(const n of Se)t[n]!==void 0&&(e[n]=t[n]);return e}function ke(t,e){const n=String(t.connectionKey??"").trim()||String(e??"").trim();return{serverUrl:String(t.serverUrl??t.schedulerBaseUrl??"").trim(),tenantId:String(t.tenantId??"").trim(),instanceName:String(t.instanceName??"").trim(),instanceId:String(t.instanceId??"").trim()||void 0,registerAddr:String(t.registerAddr??"").trim(),tunnel:typeof t.tunnel=="boolean"?t.tunnel:void 0,userKeyMasked:q(t.userKey),userKeySet:!!String(t.userKey??"").trim(),connectionKeyMasked:q(n),connectionKeySet:!!n}}function R(t){return X(t)??String(t||"").trim()}g.get("/runtime/server-config",(t,e)=>{if(!m(t,e))return;const n=E();let s={};if(v.existsSync(n))try{s=JSON.parse(v.readFileSync(n,"utf-8"))}catch{e.status(500).json({ok:!1,error:"config.json is not valid JSON; fix it manually first"});return}const d=String(s.connectionKey??"").trim()||void 0,r=G(s).map(a=>ke(a,d));e.json({ok:!0,targets:r})});function M(t,e){const n=new Error(e);return n.status=t,n}g.post("/runtime/server-config",async(t,e)=>{if(!m(t,e))return;const n=(t.body||{}).targets;if(!Array.isArray(n)){e.status(400).json({ok:!1,error:"targets array is required"});return}const s=[];for(let r=0;r<n.length;r++){const a=n[r];if(!a||typeof a!="object"||Array.isArray(a)){e.status(400).json({ok:!1,error:`targets[${r}] must be an object`});return}const o=a,l=String(o.serverUrl??o.schedulerBaseUrl??"").trim();if(!l){e.status(400).json({ok:!1,error:`targets[${r}].serverUrl is required`});return}try{const i=new URL(l);if(!["http:","https:"].includes(i.protocol))throw new Error("protocol must be http or https")}catch{e.status(400).json({ok:!1,error:`targets[${r}].serverUrl must be a valid http(s) URL`});return}const f=o.tunnel;let p;if(f==="auto"||f===""||f==null)p=void 0;else if(f===!0||f==="true")p=!0;else if(f===!1||f==="false")p=!1;else{e.status(400).json({ok:!1,error:`targets[${r}].tunnel must be auto, true or false`});return}const b=String(o.originalServerUrl??"").trim();s.push({serverUrl:l,originalServerUrl:b&&b!==l?b:"",userKey:String(o.userKey??"").trim(),tenantId:String(o.tenantId??"").trim(),instanceName:String(o.instanceName??"").trim(),connectionKey:String(o.connectionKey??"").trim(),registerAddr:String(o.registerAddr??"").trim(),tunnel:p})}const d=new Set;for(const r of s){const a=R(r.serverUrl);if(d.has(a)){e.status(400).json({ok:!1,error:"duplicate serverUrl in targets"});return}d.add(a)}try{const r=E();await O(r,()=>{let a={};if(v.existsSync(r))try{a=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw M(500,"config.json is not valid JSON; fix it manually first")}const o=String(a.connectionKey??"").trim(),l=G(a),f=l.length>0&&l[0]===a,p=new Map(l.map(i=>[R(String(i.serverUrl??i.schedulerBaseUrl??"").trim()),f?Ie(i):i])),b=[];for(const i of s){let h;if(i.originalServerUrl){const S=R(i.originalServerUrl);h=p.get(S),h&&p.delete(S)}if(!h){const S=R(i.serverUrl);h=p.get(S),h&&p.delete(S)}const c=h?{...h}:{};if(c.serverUrl=i.serverUrl,c.tenantId=i.tenantId,c.instanceName=i.instanceName,c.registerAddr=i.registerAddr,i.tunnel===void 0?delete c.tunnel:c.tunnel=i.tunnel,i.userKey)c.userKey=i.userKey;else if(!String(c.userKey??"").trim())throw M(400,`userKey is required for ${i.serverUrl} (new target without inheritable userKey)`);i.connectionKey?c.connectionKey=i.connectionKey:!String(c.connectionKey??"").trim()&&o&&(c.connectionKey=o),b.push(c)}a.autoRegisterTargets=b;for(const i of["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","registerAddr","registerIp","virtualIp","tunnel"])delete a[i];v.mkdirSync(K.dirname(r),{recursive:!0}),v.writeFileSync(r,JSON.stringify(a,null,2),"utf-8")}),x.info("Server connection config updated via panel",{configPath:r,targetCount:s.length}),e.json({ok:!0})}catch(r){const a=r,o=Number(a.status)||500;o>=500&&x.error("Failed to save server connection config:",a.message),e.status(o).json({ok:!1,error:a.message})}}),g.get("/runtime/access-control",(t,e)=>{m(t,e)&&e.json({ok:!0,allowIps:fe(),defaultAllowIps:["0.0.0.0"]})}),g.post("/runtime/access-control",async(t,e)=>{if(!m(t,e))return;const n=(t.body||{}).allowIps;if(typeof n!="string"&&!Array.isArray(n)){e.status(400).json({ok:!1,error:"allowIps must be a string or an array"});return}const s=he(n),d=s.filter(r=>!me(r));if(d.length>0){e.status(400).json({ok:!1,error:`invalid IP/CIDR entries: ${d.join(", ")}`});return}try{const r=E();await O(r,()=>{let a={};if(v.existsSync(r))try{a=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw M(500,"config.json is not valid JSON; fix it manually first")}const o=a.accessControl&&typeof a.accessControl=="object"&&!Array.isArray(a.accessControl)?{...a.accessControl}:{};o.allowIps=s,a.accessControl=o,v.mkdirSync(K.dirname(r),{recursive:!0}),v.writeFileSync(r,JSON.stringify(a,null,2),"utf-8"),be()}),x.info("Access control config updated via panel",{configPath:r,entryCount:s.length}),e.json({ok:!0,allowIps:s})}catch(r){const a=r,o=Number(a.status)||500;o>=500&&x.error("Failed to save access control config:",a.message),e.status(o).json({ok:!1,error:a.message})}}),g.get("/runtime/node-info",(t,e)=>{if(!m(t,e))return;const n=H(),s=_();e.json({ok:!0,hostname:w.hostname(),platform:w.platform(),arch:w.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version,bridgeVersion:V,configPath:E(),homeDir:process.env.AWS_RUNTIME_HOME_DIR||w.homedir(),bindingStatus:n.status,paired:n.paired,instanceId:n.instanceId||s.instanceId,schedulerBaseUrl:n.schedulerBaseUrl,registered:s.registered,pairingCode:n.pairingCode,activeSessions:ie()})}),g.get("/runtime/logs",(t,e)=>{if(!m(t,e))return;const n=t.query.level||void 0,s=Math.min(Number(t.query.count)||200,500),r=n&&["debug","info","warn","error"].includes(n)?n:void 0,a=ge(s,r);e.json({ok:!0,logs:a})}),g.post("/runtime/re-register",async(t,e)=>{if(m(t,e))try{const n=await pe();e.json({ok:n})}catch(n){const s=n;e.status(500).json({ok:!1,error:s.message})}}),g.get("/runtime/admin/sessions",(t,e)=>{if(!m(t,e))return;const n=de().map(s=>({token:s.token.slice(0,8)+"...",createdAt:new Date(s.createdAt).toISOString(),lastUsedAt:new Date(s.lastUsedAt).toISOString(),age:Math.floor((Date.now()-s.createdAt)/1e3)}));e.json({ok:!0,sessions:n})}),g.post("/runtime/admin/sessions/:token/destroy",(t,e)=>{m(t,e)&&(z(t.params.token),e.json({ok:!0}))});function Ce(t){return new Promise((e,n)=>{const s=w.platform()==="win32",d=s?"taskkill":"kill",r=s?["/PID",String(t),"/F"]:["-9",String(t)];Y(d,r,{timeout:5e3},a=>a?n(a):e())})}g.post("/runtime/kill-process",async(t,e)=>{if(!m(t,e))return;const n=Number(t.body.pid);if(!n||!Number.isInteger(n)||n<=0){e.status(400).json({ok:!1,error:"Invalid PID"});return}const s=C(),d=()=>{const a=new Set,o=P?.data??[];for(const l of o)typeof l.pid=="number"&&a.add(l.pid);for(const l of s.getAllManagedProcesses())typeof l.pid=="number"&&a.add(l.pid);return a};let r=d();if(r.size===0)try{P={data:await s.discoverAllProcesses(),ts:Date.now()},r=d()}catch{}if(!r.has(n)){x.warn("Rejected kill-process for unrecognized PID",{pid:n}),e.status(404).json({ok:!1,error:"PID is not a bridge-discovered or registered agent process"});return}try{await Ce(n),x.info("Killed process",{pid:n}),e.json({ok:!0})}catch(a){const o=a;x.warn("Failed to kill process",{pid:n,error:o.message}),e.status(500).json({ok:!1,error:o.message})}}),g.get("/runtime/dashboard",async(t,e)=>{if(m(t,e)){if(T&&Date.now()-T.ts<ve)return void e.json(T.data);try{const n=L?.data??null,s=P?.data??null;L||D();const[d,r,a]=await Promise.all([Promise.resolve(H()),Promise.resolve(_()),ee().catch(()=>[])]),l=C().getAllManagedProcesses(),f=Array.from(se.entries()).map(([I,y])=>({sessionId:I,agentId:y.agentId,workspacePath:y.workspacePath,runtimeStatus:y.currentRuntimeStatus||"running",runtimeActionType:y.currentRuntimeActionType,runtimeActionLabel:y.currentRuntimeActionLabel,providerId:y.providerId}));let p={managed:0,orphan:0,unknown:0},b=[];if(s){p={managed:s.filter(u=>u.managementStatus==="managed").length,orphan:s.filter(u=>u.managementStatus==="orphan").length,unknown:s.filter(u=>u.managementStatus==="unknown").length};const I=new Map(l.map(u=>[u.pid,u]));b=s.map(u=>{const k=I.get(u.pid);return{agentId:u.agentId||k?.agentId,pid:u.pid,managementStatus:u.managementStatus,sessionId:k?.sessionId,state:k?.state,command:u.command,cwd:u.cwd,startedAt:u.startedAt}});const y={managed:0,orphan:1,unknown:2};b.sort((u,k)=>(y[u.managementStatus]??9)-(y[k.managementStatus]??9))}const i=new Set(f.map(I=>I.sessionId)),h=a.filter(I=>!i.has(I.sessionId)),c=d,S=await xe(),j={ok:!0,collectedAt:new Date().toISOString(),bridge:{version:V,hostname:w.hostname(),platform:w.platform(),arch:w.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version},binding:{status:c.status,paired:c.paired,instanceId:c.instanceId,userId:c.userId,schedulerBaseUrl:c.schedulerBaseUrl,pairingCode:Q()},registration:{registered:r.registered,instanceId:r.instanceId,lastAttempt:r.lastAttempt?.toISOString(),error:r.error},metrics:n,sessions:{active:f,activeCount:f.length,persisted:h,persistedCount:h.length,total:f.length+h.length},processes:{...p,total:p.managed+p.orphan+p.unknown,list:b,registryCount:l.length},lifecycleOperation:S};T={data:j,ts:Date.now()},e.json(j)}catch(n){const s=n;x.error("Dashboard aggregation failed:",s.message),e.status(500).json({ok:!1,error:s.message})}}}),g.get("/login",(t,e)=>{e.type("text/html; charset=utf-8"),e.send(Ee)}),g.get("/",(t,e)=>{const n=A(t);if(!n||!U(n)){e.redirect("/login");return}if(!B()){e.redirect("/setup");return}const s=t.query.lang==="en"?"en":"zh";e.type("text/html; charset=utf-8"),e.send(Be.replace("{{LANG}}",s))}),g.get("/setup",(t,e)=>{const n=A(t);if(!n||!U(n)){e.redirect("/login");return}e.type("text/html; charset=utf-8"),e.send(Te)});const Be=`<!DOCTYPE html>
1
+ import{Router as $}from"express";import v from"node:fs";import K from"node:path";import I from"node:os";import{execFile as Y}from"node:child_process";import{getRuntimeBindingPublicState as H,getRuntimePairingCode as Q,normalizeSchedulerBaseUrl as X}from"../services/runtime-binding.js";import{getAgentProcessManager as C}from"../services/agent-process-manager.js";import{collectSystemMetrics as Z}from"../services/system-metrics.js";import{loadPersistedSessions as ee}from"../services/terminal-persistence.js";import{createLogger as te}from"../utils/logger.js";import{getBridgeVersion as ne,getRuntimeHomeDir as se,getUserHomeDir as re}from"../config.js";import{runExclusiveConfigWrite as O}from"../utils/config-write-queue.js";import{sdkSessions as ae}from"./terminal.js";import{createSession as oe,getPanelCredentials as N,validateSession as U,destroySession as z,validateCredentials as F,hasConfiguredCredentials as B,isDefaultCredentials as ie,setPanelCredentials as de,getAllSessions as le,getActiveSessionCount as ce,checkLoginAllowed as ue,recordLoginFailure as ge,recordLoginSuccess as pe}from"../services/panel-auth.js";import{getRecentLogs as fe}from"../utils/logger.js";import{autoRegister as me,getRegistrationState as _,getAutoRegisterConfigFilePath as E}from"../services/auto-register.js";import{getAccessControlEntries as be,isValidIpEntry as he,parseAllowIps as ve,refreshAccessControlCache as ye}from"../services/access-control.js";const x=te("dashboard");let T=null;const xe=1e4;let L=null,P=null;const we=3e4;async function D(){Z().then(t=>{L={data:t,ts:Date.now()}}).catch(()=>{}),C().discoverAllProcesses().then(t=>{P={data:t,ts:Date.now()}}).catch(()=>{}),C().cleanupStaleRegistry().catch(()=>{})}D(),setInterval(D,we);const V=ne();async function Se(){const t=await import("node:fs/promises"),n=(await import("node:path")).join(se(),".aws-bridge","lifecycle-state.json");try{const s=await t.readFile(n,"utf8"),i=JSON.parse(s),r=Array.isArray(i.operations)?i.operations:[];return r.length===0?null:(r.sort((a,o)=>new Date(o.updatedAt).getTime()-new Date(a.updatedAt).getTime()),r[0]||null)}catch{return null}}const g=$(),J="panel_sid";function Ie(t){const e={};if(!t)return e;for(const n of t.split(";")){const s=n.indexOf("=");if(s===-1)continue;const i=n.slice(0,s).trim(),r=n.slice(s+1).trim();i&&(e[i]=r)}return e}function A(t){return Ie(t.headers.cookie)[J]}function q(t,e,n){const s=t.secure||t.headers["x-forwarded-proto"]==="https";return`${J}=${e}; HttpOnly; SameSite=Lax; Path=/${s?"; Secure":""}; Max-Age=${n}`}function m(t,e){const n=A(t);return n&&U(n)?!0:t.xhr||t.headers.accept?.includes("application/json")?(e.status(401).json({ok:!1,error:"unauthorized",needLogin:!0}),!1):(e.redirect("/login"),!1)}g.post("/runtime/login",(t,e)=>{const n=t.socket.remoteAddress||"unknown",s=ue(n);if(!s.allowed){e.status(429).set("Retry-After",String(s.retryAfterSec)).json({ok:!1,error:"too many failed login attempts, try again later",retryAfterSec:s.retryAfterSec});return}const{username:i,password:r}=t.body||{};if(!i||!r){e.status(400).json({ok:!1,error:"username and password required"});return}if(!F(String(i),String(r))){ge(n),e.status(401).json({ok:!1,error:"invalid credentials"});return}pe(n);const a=oe(),o=!B();e.setHeader("Set-Cookie",q(t,a,86400)),e.json({ok:!0,needSetup:o})}),g.post("/runtime/logout",(t,e)=>{const n=A(t);n&&z(n),e.setHeader("Set-Cookie",q(t,"",0)),e.json({ok:!0})}),g.get("/runtime/credentials-status",(t,e)=>{if(!m(t,e))return;const n=N();e.json({ok:!0,isDefault:ie(),configured:B(),username:n.username})}),g.post("/runtime/change-credentials",async(t,e)=>{if(!m(t,e))return;const{username:n,password:s,passwordConfirm:i,oldPassword:r}=t.body||{};if(s&&String(s).trim()){if(String(s)!==String(i)){e.status(400).json({ok:!1,error:"passwords do not match"});return}if(B()){if(!r){e.status(400).json({ok:!1,error:"old password required"});return}const o=N();if(!F(o.username,String(r))){e.status(403).json({ok:!1,error:"old password is incorrect"});return}}}if(!n||!String(n).trim()){e.status(400).json({ok:!1,error:"username required"});return}const a=s&&String(s).trim()?String(s).trim():N().password;try{await de(String(n).trim(),a),e.json({ok:!0})}catch(o){const l=o;x.error("Failed to change panel credentials:",l.message),e.status(500).json({ok:!1,error:l.message})}});function W(t){const e=typeof t=="string"?t.trim():"";if(e)return e.length<8?"\u2022\u2022\u2022\u2022":"\u2022\u2022\u2022\u2022"+e.slice(-4)}function G(t){const e=Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets.filter(s=>!!(s&&typeof s=="object"&&!Array.isArray(s))):[];return e.length>0?e:["serverUrl","schedulerBaseUrl","userKey"].some(s=>!!String(t[s]??"").trim())?[t]:[]}const ke=["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","projectId","roleName","workspacePath","registerAddr","registerIp","virtualIp","tunnel","preferSameLanIp","maxRetries","retryInterval"];function Ce(t){const e={};for(const n of ke)t[n]!==void 0&&(e[n]=t[n]);return e}function Be(t,e){const n=String(t.connectionKey??"").trim()||String(e??"").trim();return{serverUrl:String(t.serverUrl??t.schedulerBaseUrl??"").trim(),tenantId:String(t.tenantId??"").trim(),instanceName:String(t.instanceName??"").trim(),instanceId:String(t.instanceId??"").trim()||void 0,registerAddr:String(t.registerAddr??"").trim(),tunnel:typeof t.tunnel=="boolean"?t.tunnel:void 0,userKeyMasked:W(t.userKey),userKeySet:!!String(t.userKey??"").trim(),connectionKeyMasked:W(n),connectionKeySet:!!n}}function R(t){return X(t)??String(t||"").trim()}g.get("/runtime/server-config",(t,e)=>{if(!m(t,e))return;const n=E();let s={};if(v.existsSync(n))try{s=JSON.parse(v.readFileSync(n,"utf-8"))}catch{e.status(500).json({ok:!1,error:"config.json is not valid JSON; fix it manually first"});return}const i=String(s.connectionKey??"").trim()||void 0,r=G(s).map(a=>Be(a,i));e.json({ok:!0,targets:r})});function M(t,e){const n=new Error(e);return n.status=t,n}g.post("/runtime/server-config",async(t,e)=>{if(!m(t,e))return;const n=(t.body||{}).targets;if(!Array.isArray(n)){e.status(400).json({ok:!1,error:"targets array is required"});return}const s=[];for(let r=0;r<n.length;r++){const a=n[r];if(!a||typeof a!="object"||Array.isArray(a)){e.status(400).json({ok:!1,error:`targets[${r}] must be an object`});return}const o=a,l=String(o.serverUrl??o.schedulerBaseUrl??"").trim();if(!l){e.status(400).json({ok:!1,error:`targets[${r}].serverUrl is required`});return}try{const d=new URL(l);if(!["http:","https:"].includes(d.protocol))throw new Error("protocol must be http or https")}catch{e.status(400).json({ok:!1,error:`targets[${r}].serverUrl must be a valid http(s) URL`});return}const f=o.tunnel;let p;if(f==="auto"||f===""||f==null)p=void 0;else if(f===!0||f==="true")p=!0;else if(f===!1||f==="false")p=!1;else{e.status(400).json({ok:!1,error:`targets[${r}].tunnel must be auto, true or false`});return}const h=String(o.originalServerUrl??"").trim();s.push({serverUrl:l,originalServerUrl:h&&h!==l?h:"",userKey:String(o.userKey??"").trim(),tenantId:String(o.tenantId??"").trim(),instanceName:String(o.instanceName??"").trim(),connectionKey:String(o.connectionKey??"").trim(),registerAddr:String(o.registerAddr??"").trim(),tunnel:p})}const i=new Set;for(const r of s){const a=R(r.serverUrl);if(i.has(a)){e.status(400).json({ok:!1,error:"duplicate serverUrl in targets"});return}i.add(a)}try{const r=E();await O(r,()=>{let a={};if(v.existsSync(r))try{a=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw M(500,"config.json is not valid JSON; fix it manually first")}const o=String(a.connectionKey??"").trim(),l=G(a),f=l.length>0&&l[0]===a,p=new Map(l.map(d=>[R(String(d.serverUrl??d.schedulerBaseUrl??"").trim()),f?Ce(d):d])),h=[];for(const d of s){let b;if(d.originalServerUrl){const w=R(d.originalServerUrl);b=p.get(w),b&&p.delete(w)}if(!b){const w=R(d.serverUrl);b=p.get(w),b&&p.delete(w)}const c=b?{...b}:{};if(c.serverUrl=d.serverUrl,c.tenantId=d.tenantId,c.instanceName=d.instanceName,c.registerAddr=d.registerAddr,d.tunnel===void 0?delete c.tunnel:c.tunnel=d.tunnel,d.userKey)c.userKey=d.userKey;else if(!String(c.userKey??"").trim())throw M(400,`userKey is required for ${d.serverUrl} (new target without inheritable userKey)`);d.connectionKey?c.connectionKey=d.connectionKey:!String(c.connectionKey??"").trim()&&o&&(c.connectionKey=o),h.push(c)}a.autoRegisterTargets=h;for(const d of["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","registerAddr","registerIp","virtualIp","tunnel"])delete a[d];v.mkdirSync(K.dirname(r),{recursive:!0}),v.writeFileSync(r,JSON.stringify(a,null,2),"utf-8")}),x.info("Server connection config updated via panel",{configPath:r,targetCount:s.length}),e.json({ok:!0})}catch(r){const a=r,o=Number(a.status)||500;o>=500&&x.error("Failed to save server connection config:",a.message),e.status(o).json({ok:!1,error:a.message})}}),g.get("/runtime/access-control",(t,e)=>{m(t,e)&&e.json({ok:!0,allowIps:be(),defaultAllowIps:["0.0.0.0"]})}),g.post("/runtime/access-control",async(t,e)=>{if(!m(t,e))return;const n=(t.body||{}).allowIps;if(typeof n!="string"&&!Array.isArray(n)){e.status(400).json({ok:!1,error:"allowIps must be a string or an array"});return}const s=ve(n),i=s.filter(r=>!he(r));if(i.length>0){e.status(400).json({ok:!1,error:`invalid IP/CIDR entries: ${i.join(", ")}`});return}try{const r=E();await O(r,()=>{let a={};if(v.existsSync(r))try{a=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw M(500,"config.json is not valid JSON; fix it manually first")}const o=a.accessControl&&typeof a.accessControl=="object"&&!Array.isArray(a.accessControl)?{...a.accessControl}:{};o.allowIps=s,a.accessControl=o,v.mkdirSync(K.dirname(r),{recursive:!0}),v.writeFileSync(r,JSON.stringify(a,null,2),"utf-8"),ye()}),x.info("Access control config updated via panel",{configPath:r,entryCount:s.length}),e.json({ok:!0,allowIps:s})}catch(r){const a=r,o=Number(a.status)||500;o>=500&&x.error("Failed to save access control config:",a.message),e.status(o).json({ok:!1,error:a.message})}}),g.get("/runtime/node-info",(t,e)=>{if(!m(t,e))return;const n=H(),s=_();e.json({ok:!0,hostname:I.hostname(),platform:I.platform(),arch:I.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version,bridgeVersion:V,configPath:E(),homeDir:re(),bindingStatus:n.status,paired:n.paired,instanceId:n.instanceId||s.instanceId,schedulerBaseUrl:n.schedulerBaseUrl,registered:s.registered,pairingCode:n.pairingCode,activeSessions:ce()})}),g.get("/runtime/logs",(t,e)=>{if(!m(t,e))return;const n=t.query.level||void 0,s=Math.min(Number(t.query.count)||200,500),r=n&&["debug","info","warn","error"].includes(n)?n:void 0,a=fe(s,r);e.json({ok:!0,logs:a})}),g.post("/runtime/re-register",async(t,e)=>{if(m(t,e))try{const n=await me();e.json({ok:n})}catch(n){const s=n;e.status(500).json({ok:!1,error:s.message})}}),g.get("/runtime/admin/sessions",(t,e)=>{if(!m(t,e))return;const n=le().map(s=>({token:s.token.slice(0,8)+"...",createdAt:new Date(s.createdAt).toISOString(),lastUsedAt:new Date(s.lastUsedAt).toISOString(),age:Math.floor((Date.now()-s.createdAt)/1e3)}));e.json({ok:!0,sessions:n})}),g.post("/runtime/admin/sessions/:token/destroy",(t,e)=>{m(t,e)&&(z(t.params.token),e.json({ok:!0}))});function Ee(t){return new Promise((e,n)=>{const s=I.platform()==="win32",i=s?"taskkill":"kill",r=s?["/PID",String(t),"/F"]:["-9",String(t)];Y(i,r,{timeout:5e3},a=>a?n(a):e())})}g.post("/runtime/kill-process",async(t,e)=>{if(!m(t,e))return;const n=Number(t.body.pid);if(!n||!Number.isInteger(n)||n<=0){e.status(400).json({ok:!1,error:"Invalid PID"});return}const s=C(),i=()=>{const a=new Set,o=P?.data??[];for(const l of o)typeof l.pid=="number"&&a.add(l.pid);for(const l of s.getAllManagedProcesses())typeof l.pid=="number"&&a.add(l.pid);return a};let r=i();if(r.size===0)try{P={data:await s.discoverAllProcesses(),ts:Date.now()},r=i()}catch{}if(!r.has(n)){x.warn("Rejected kill-process for unrecognized PID",{pid:n}),e.status(404).json({ok:!1,error:"PID is not a bridge-discovered or registered agent process"});return}try{await Ee(n),x.info("Killed process",{pid:n}),e.json({ok:!0})}catch(a){const o=a;x.warn("Failed to kill process",{pid:n,error:o.message}),e.status(500).json({ok:!1,error:o.message})}}),g.get("/runtime/dashboard",async(t,e)=>{if(m(t,e)){if(T&&Date.now()-T.ts<xe)return void e.json(T.data);try{const n=L?.data??null,s=P?.data??null;L||D();const[i,r,a]=await Promise.all([Promise.resolve(H()),Promise.resolve(_()),ee().catch(()=>[])]),l=C().getAllManagedProcesses(),f=Array.from(ae.entries()).map(([S,y])=>({sessionId:S,agentId:y.agentId,workspacePath:y.workspacePath,runtimeStatus:y.currentRuntimeStatus||"running",runtimeActionType:y.currentRuntimeActionType,runtimeActionLabel:y.currentRuntimeActionLabel,providerId:y.providerId}));let p={managed:0,orphan:0,unknown:0},h=[];if(s){p={managed:s.filter(u=>u.managementStatus==="managed").length,orphan:s.filter(u=>u.managementStatus==="orphan").length,unknown:s.filter(u=>u.managementStatus==="unknown").length};const S=new Map(l.map(u=>[u.pid,u]));h=s.map(u=>{const k=S.get(u.pid);return{agentId:u.agentId||k?.agentId,pid:u.pid,managementStatus:u.managementStatus,sessionId:k?.sessionId,state:k?.state,command:u.command,cwd:u.cwd,startedAt:u.startedAt}});const y={managed:0,orphan:1,unknown:2};h.sort((u,k)=>(y[u.managementStatus]??9)-(y[k.managementStatus]??9))}const d=new Set(f.map(S=>S.sessionId)),b=a.filter(S=>!d.has(S.sessionId)),c=i,w=await Se(),j={ok:!0,collectedAt:new Date().toISOString(),bridge:{version:V,hostname:I.hostname(),platform:I.platform(),arch:I.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version},binding:{status:c.status,paired:c.paired,instanceId:c.instanceId,userId:c.userId,schedulerBaseUrl:c.schedulerBaseUrl,pairingCode:Q()},registration:{registered:r.registered,instanceId:r.instanceId,lastAttempt:r.lastAttempt?.toISOString(),error:r.error},metrics:n,sessions:{active:f,activeCount:f.length,persisted:b,persistedCount:b.length,total:f.length+b.length},processes:{...p,total:p.managed+p.orphan+p.unknown,list:h,registryCount:l.length},lifecycleOperation:w};T={data:j,ts:Date.now()},e.json(j)}catch(n){const s=n;x.error("Dashboard aggregation failed:",s.message),e.status(500).json({ok:!1,error:s.message})}}}),g.get("/login",(t,e)=>{e.type("text/html; charset=utf-8"),e.send(Pe)}),g.get("/",(t,e)=>{const n=A(t);if(!n||!U(n)){e.redirect("/login");return}if(!B()){e.redirect("/setup");return}const s=t.query.lang==="en"?"en":"zh";e.type("text/html; charset=utf-8"),e.send(Te.replace("{{LANG}}",s))}),g.get("/setup",(t,e)=>{const n=A(t);if(!n||!U(n)){e.redirect("/login");return}e.type("text/html; charset=utf-8"),e.send(Ae)});const Te=`<!DOCTYPE html>
2
2
  <html lang="{{LANG}}">
3
3
  <head>
4
4
  <meta charset="UTF-8">
@@ -280,6 +280,61 @@ import{Router as $}from"express";import v from"node:fs";import K from"node:path"
280
280
  th, td { padding: 7px 10px; }
281
281
  .header h1 { font-size: 18px; }
282
282
  }
283
+
284
+ /* ===== Scrollbar (\u4E3B\u9898\u9002\u914D) ===== */
285
+ /* \u9875\u9762\u7EA7\u6EDA\u52A8\u6761\uFF1Apill \u5F62\u6001\uFF08\u8FB9\u6846 + background-clip\uFF09\uFF0C\u4E0E\u6DF1\u8272\u80CC\u666F\u878D\u4E3A\u4E00\u4F53 */
286
+ html {
287
+ scrollbar-width: thin;
288
+ scrollbar-color: color-mix(in srgb, var(--accent) 30%, transparent) var(--bg);
289
+ }
290
+ html::-webkit-scrollbar {
291
+ width: 10px;
292
+ background: var(--bg);
293
+ }
294
+ html::-webkit-scrollbar-thumb {
295
+ background: color-mix(in srgb, var(--accent) 30%, transparent);
296
+ border: 2px solid var(--bg);
297
+ border-radius: 5px;
298
+ background-clip: content-box;
299
+ }
300
+ html::-webkit-scrollbar-thumb:hover {
301
+ background: color-mix(in srgb, var(--accent) 50%, transparent);
302
+ background-clip: content-box;
303
+ }
304
+ /* \u5185\u90E8\u6EDA\u52A8\u5BB9\u5668\uFF08\u8868\u683C\u3001\u8BBE\u7F6E\u9762\u677F\u3001\u65E5\u5FD7\u5217\u8868\uFF09\uFF1A6px \u900F\u660E\u8F68\u9053 + accent \u80F6\u56CA\u6ED1\u5757 */
305
+ .table-wrap,
306
+ .settings-content,
307
+ .s-log-list,
308
+ .s-table-wrap {
309
+ scrollbar-width: thin;
310
+ scrollbar-color: color-mix(in srgb, var(--accent) 30%, transparent) transparent;
311
+ }
312
+ .table-wrap::-webkit-scrollbar,
313
+ .settings-content::-webkit-scrollbar,
314
+ .s-log-list::-webkit-scrollbar,
315
+ .s-table-wrap::-webkit-scrollbar {
316
+ width: 6px;
317
+ height: 6px;
318
+ }
319
+ .table-wrap::-webkit-scrollbar-track,
320
+ .settings-content::-webkit-scrollbar-track,
321
+ .s-log-list::-webkit-scrollbar-track,
322
+ .s-table-wrap::-webkit-scrollbar-track {
323
+ background: transparent;
324
+ }
325
+ .table-wrap::-webkit-scrollbar-thumb,
326
+ .settings-content::-webkit-scrollbar-thumb,
327
+ .s-log-list::-webkit-scrollbar-thumb,
328
+ .s-table-wrap::-webkit-scrollbar-thumb {
329
+ background: color-mix(in srgb, var(--accent) 30%, transparent);
330
+ border-radius: 3px;
331
+ }
332
+ .table-wrap::-webkit-scrollbar-thumb:hover,
333
+ .settings-content::-webkit-scrollbar-thumb:hover,
334
+ .s-log-list::-webkit-scrollbar-thumb:hover,
335
+ .s-table-wrap::-webkit-scrollbar-thumb:hover {
336
+ background: color-mix(in srgb, var(--accent) 50%, transparent);
337
+ }
283
338
  </style>
284
339
  </head>
285
340
  <body>
@@ -2006,7 +2061,7 @@ switchSettingsPage = function(page) {
2006
2061
  };
2007
2062
  </script>
2008
2063
  </body>
2009
- </html>`,Ee=`<!DOCTYPE html>
2064
+ </html>`,Pe=`<!DOCTYPE html>
2010
2065
  <html lang="zh-CN">
2011
2066
  <head>
2012
2067
  <meta charset="UTF-8">
@@ -2177,7 +2232,7 @@ document.getElementById("loginForm").addEventListener("keydown", function(e) {
2177
2232
  });
2178
2233
  </script>
2179
2234
  </body>
2180
- </html>`,Te=`<!DOCTYPE html>
2235
+ </html>`,Ae=`<!DOCTYPE html>
2181
2236
  <html lang="zh-CN">
2182
2237
  <head>
2183
2238
  <meta charset="UTF-8">
@@ -90,12 +90,40 @@ export interface GitPushResult {
90
90
  }
91
91
 
92
92
  export declare function pushBranchToRemote(repoRootPath: string): Promise<GitPushResult>;
93
+ export interface GitStashSnapshotResult {
94
+
95
+ stashRef: string | null;
96
+ stashName: string | null;
97
+
98
+ noChanges: boolean;
99
+ reusedLatestStash: boolean;
100
+ message: string;
101
+ }
102
+
103
+ export declare function createGitStashSnapshot(workspacePath: string, message?: string): Promise<GitStashSnapshotResult>;
93
104
  export interface GitCommitOutcome {
94
105
  committedPaths: string[];
95
106
  output: string;
96
107
  }
97
108
 
98
109
  export declare function commitSelectedFiles(repoRootPath: string, message: string, pathspecs: string[]): Promise<GitCommitOutcome>;
110
+ export type GitResetMode = 'soft' | 'hard';
111
+ export interface GitResetPreview {
112
+ mode: GitResetMode;
113
+
114
+ commitHash: string;
115
+ branch: string;
116
+
117
+ resetCount: number;
118
+
119
+ hasLocalChanges: boolean;
120
+
121
+ affectsRemote: boolean;
122
+ }
123
+
124
+ export declare function previewRepositoryReset(repoRootPath: string, commitHash: string, mode: GitResetMode): Promise<GitResetPreview>;
125
+
126
+ export declare function resetRepositoryTo(repoRootPath: string, commitHash: string, mode: GitResetMode, cleanScopePath?: string): Promise<GitResetPreview>;
99
127
 
100
128
  export declare function sanitizeCommitMessage(raw: string): string;
101
129