aws-runtime-bridge 1.9.120 → 1.9.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +18 -0
  2. package/dist/adapter/AcodeSdkAdapter.js +5 -5
  3. package/dist/adapter/types.d.ts +4 -0
  4. package/dist/adapter/types.js +1 -1
  5. package/dist/browser/browser-ws-bridge.js +3 -3
  6. package/dist/browser/built-in-browser-tools.js +1 -1
  7. package/dist/desktop/built-in-desktop-tools.d.ts +49 -0
  8. package/dist/desktop/built-in-desktop-tools.js +2 -0
  9. package/dist/desktop/desktop-mcp-client.d.ts +47 -0
  10. package/dist/desktop/desktop-mcp-client.js +3 -0
  11. package/dist/index.js +1 -1
  12. package/dist/remote-desktop/index.js +2 -2
  13. package/dist/routes/dashboard.js +521 -17
  14. package/dist/routes/git.d.ts +39 -0
  15. package/dist/routes/git.js +33 -25
  16. package/dist/routes/instance.js +1 -1
  17. package/dist/routes/jdks.d.ts +2 -0
  18. package/dist/routes/jdks.js +2 -0
  19. package/dist/routes/launch.d.ts +6 -0
  20. package/dist/routes/launch.js +30 -0
  21. package/dist/routes/pty.js +3 -3
  22. package/dist/routes/runtime-binding.js +1 -1
  23. package/dist/routes/terminal.js +30 -30
  24. package/dist/services/access-control.d.ts +18 -0
  25. package/dist/services/access-control.js +5 -0
  26. package/dist/services/agent-process-manager.js +1 -1
  27. package/dist/services/auto-register.js +1 -1
  28. package/dist/services/change-cache-retention.d.ts +5 -0
  29. package/dist/services/change-cache-retention.js +2 -0
  30. package/dist/services/desktop-mcp-manager.d.ts +47 -0
  31. package/dist/services/desktop-mcp-manager.js +2 -0
  32. package/dist/services/instance-features.d.ts +6 -0
  33. package/dist/services/instance-features.js +1 -1
  34. package/dist/services/jdk-scanner.d.ts +20 -0
  35. package/dist/services/jdk-scanner.js +2 -0
  36. package/dist/services/launch-manager.d.ts +181 -0
  37. package/dist/services/launch-manager.js +12 -0
  38. package/dist/services/launch-metrics.d.ts +89 -0
  39. package/dist/services/launch-metrics.js +4 -0
  40. package/dist/services/launch-streams.d.ts +24 -0
  41. package/dist/services/launch-streams.js +11 -0
  42. package/dist/services/mcp-launch-binding-queue.js +1 -1
  43. package/dist/services/panel-auth.d.ts +11 -1
  44. package/dist/services/panel-auth.js +1 -1
  45. package/dist/services/panel-env.d.ts +5 -0
  46. package/dist/services/panel-env.js +2 -0
  47. package/dist/services/port-detector.d.ts +3 -0
  48. package/dist/services/port-detector.js +6 -0
  49. package/dist/services/runtime-lifecycle-policy.d.ts +1 -0
  50. package/dist/services/runtime-lifecycle-policy.js +1 -1
  51. package/dist/services/system-metrics.js +1 -1
  52. package/dist/services/tunnel-client.d.ts +4 -0
  53. package/dist/services/tunnel-client.js +1 -1
  54. package/dist/services/workspace-files.d.ts +3 -0
  55. package/dist/services/workspace-files.js +1 -1
  56. package/dist/services/workspaceTaskChangeWatcher.d.ts +2 -0
  57. package/dist/services/workspaceTaskChangeWatcher.js +3 -3
  58. package/dist/utils/config-write-queue.d.ts +3 -0
  59. package/dist/utils/config-write-queue.js +2 -0
  60. package/package/acode/dist/built-in-file-tools.d.ts +2 -0
  61. package/package/acode/dist/runtime.d.ts +20 -0
  62. package/package/acode/dist/runtime.js +27 -27
  63. package/package/acode/dist/sub-agent-manager.d.ts +4 -0
  64. package/package/acode/dist/sub-agent-manager.js +1 -1
  65. package/package/acode/dist/sub-agent-tools.d.ts +6 -0
  66. package/package/acode/dist/sub-agent-tools.js +39 -10
  67. package/package/acode/dist/sub-agent-types.d.ts +2 -0
  68. package/package/acode/dist/tool-allocation.d.ts +55 -0
  69. package/package/acode/dist/tool-allocation.js +2 -0
  70. package/package/acode/dist/types.d.ts +6 -0
  71. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +24 -0
  72. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  73. package/package/aws-client-agent-mcp/dist/agent-client.test.js +19 -5
  74. package/package/aws-client-agent-mcp/dist/context-manager.test.js +6 -6
  75. package/package/aws-client-agent-mcp/dist/http-client.d.ts +2 -0
  76. package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
  77. package/package/aws-client-agent-mcp/dist/index.js +1 -1
  78. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.d.ts +1 -0
  79. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.js +106 -0
  80. package/package/aws-client-agent-mcp/dist/logger.d.ts +4 -0
  81. package/package/aws-client-agent-mcp/dist/logger.js +2 -2
  82. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +21 -0
  83. package/package/aws-client-agent-mcp/dist/mcp-server.js +25 -25
  84. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +24 -16
  85. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +20 -0
  86. package/package/aws-client-agent-mcp/dist/mcp-tools.js +8 -8
  87. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +55 -1
  88. package/package/aws-client-agent-mcp/dist/memory-store.test.js +5 -1
  89. package/package/aws-client-agent-mcp/dist/parent-lifecycle.d.ts +10 -0
  90. package/package/aws-client-agent-mcp/dist/parent-lifecycle.js +2 -0
  91. package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.d.ts +1 -0
  92. package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.js +81 -0
  93. package/package/aws-client-agent-mcp/dist/status-reporter.js +1 -1
  94. package/package/aws-client-agent-mcp/dist/types.d.ts +4 -0
  95. package/package/aws-client-agent-mcp/dist/watchdog-worker.d.ts +1 -0
  96. package/package/aws-client-agent-mcp/dist/watchdog-worker.js +3 -0
  97. package/package/aws-client-agent-mcp/dist/watchdog.d.ts +17 -0
  98. package/package/aws-client-agent-mcp/dist/watchdog.js +2 -0
  99. package/package/aws-client-agent-mcp/dist/watchdog.test.d.ts +1 -0
  100. package/package/aws-client-agent-mcp/dist/watchdog.test.js +124 -0
  101. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +4 -0
  102. package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -1
  103. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +83 -6
  104. package/package/aws-client-agent-mcp/package.json +1 -1
  105. package/package.json +2 -1
@@ -0,0 +1,47 @@
1
+
2
+ import { type ACodeBuiltInToolLike } from "../desktop/built-in-desktop-tools.js";
3
+ import { DesktopMcpConnection, type DesktopMcpToolInfo } from "../desktop/desktop-mcp-client.js";
4
+
5
+ interface DesktopMcpManagerState {
6
+ connection: DesktopMcpConnection;
7
+ tools: DesktopMcpToolInfo[];
8
+
9
+ forwarder: (name: string, args: Record<string, unknown>) => Promise<string>;
10
+ binaryPath: string;
11
+
12
+ envSig: string;
13
+ }
14
+
15
+ interface DesktopMcpFailureState {
16
+ reason: "binary-missing" | "start-failed";
17
+ message: string;
18
+ at: number;
19
+ }
20
+
21
+ export interface DesktopAgentAiInfo {
22
+ model: string;
23
+ baseURL?: string;
24
+ apiKey?: string;
25
+
26
+ supportsImage?: boolean;
27
+ }
28
+
29
+ export declare function resolveDesktopMcpBinary(): Promise<string | null>;
30
+
31
+ export declare function startDesktopMcpManager(): Promise<DesktopMcpManagerState | null>;
32
+
33
+ export declare function getDesktopTools(agentId: string | undefined, agentAi?: DesktopAgentAiInfo): Promise<ACodeBuiltInToolLike[]>;
34
+
35
+ export declare function getDesktopToolsIfReady(): ACodeBuiltInToolLike[];
36
+
37
+ export declare function disposeDesktopMcpManager(): Promise<void>;
38
+
39
+ export declare function getDesktopMcpStatus(): {
40
+ started: boolean;
41
+ binaryPath: string | null;
42
+ toolCount: number;
43
+ failure: DesktopMcpFailureState | null;
44
+
45
+ visionSource: "panel-env" | "agent-fallback" | "none";
46
+ };
47
+ export {};
@@ -0,0 +1,2 @@
1
+ import{access as b,constants as P}from"node:fs/promises";import{getPanelEnvVars as M}from"./panel-env.js";import{join as m,resolve as O}from"node:path";import{createBuiltInDesktopTools as w}from"../desktop/built-in-desktop-tools.js";import{DesktopMcpConnection as T}from"../desktop/desktop-mcp-client.js";import{logger as C}from"../utils/logger.js";import{DESKTOP_FEATURE_KEY as D,isFeatureEnabled as I}from"./instance-features.js";const d=C.child("desktop-mcp");let n=null,c=null,r=null,l,f="none";const k=process.platform==="win32"?"nuphus-mcp.exe":"nuphus-mcp",N=12e4,U=6e4;function x(){const e=process.env.AWS_DESKTOP_MCP_TIMEOUT_MS;if(e){const t=parseInt(e,10);if(Number.isFinite(t)&&t>0)return t}return N}function A(){const e=process.env.AWS_DESKTOP_MCP_RETRY_BACKOFF_MS;if(e){const t=parseInt(e,10);if(Number.isFinite(t)&&t>=0)return t}return U}async function K(){const e=process.env.AWS_DESKTOP_MCP_BIN;if(e){const o=O(e);return await g(o)?o:null}const t=m(process.cwd(),".desktop-mcp","bin",k);return await g(t)?t:null}async function g(e){try{return await b(e,P.F_OK),!0}catch{return!1}}function $(){const e={},t="AWS_DESKTOP_MCP_ENV_";for(const[o,s]of Object.entries(process.env))o.startsWith(t)&&s!==void 0&&(e[o.slice(t.length)]=s);return e}function h(){const e={...$(),...M()},t=e.NUPHUS_MCP_VISION_API_KEY!==void 0||e.NUPHUS_MCP_VISION_MODEL!==void 0;return!t&&l?.apiKey&&l.supportsImage===!0?(f="agent-fallback",e.NUPHUS_MCP_VISION_API_KEY=l.apiKey,e.NUPHUS_MCP_VISION_MODEL=l.model,l.baseURL&&(e.NUPHUS_MCP_VISION_BASE_URL=l.baseURL),d.info(`vision model fallback: reusing agent model ${l.model} for desktop_vision`)):t?f="panel-env":f="none",e}function S(e){return Object.keys(e).sort().map(t=>`${t}=${e[t]}`).join("\0")}async function y(){if(n){if(n.connection.isConnected())return n;await _(n.connection),n=null}if(c)return c;if(r){if(r.reason==="binary-missing"||Date.now()-r.at<A())return null;r=null}return c=(async()=>{const e=await K();if(!e)return r={reason:"binary-missing",message:`nuphus-mcp binary not found. Set AWS_DESKTOP_MCP_BIN or place it at ${m(process.cwd(),".desktop-mcp","bin",k)}`,at:Date.now()},u(`desktop tools unavailable: ${r.message}`),null;const t=h(),o=new T({command:e,env:t,onUnexpectedClose:()=>{u("nuphus-mcp process exited unexpectedly; will restart on next use"),n?.connection===o&&(n=null)}});try{const s=await o.start(),i=x();r=null;const a={connection:o,tools:s,binaryPath:e,envSig:S(t),forwarder:(p,E)=>F(o,p,E,i)};return n=a,u(`nuphus-mcp started (${e}), ${s.filter(p=>p.name.startsWith("desktop_")).length} desktop tools available`),a}catch(s){return await _(o),r={reason:"start-failed",message:s.message,at:Date.now()},v(`nuphus-mcp start failed: ${r.message}`),null}finally{c=null}})(),c}async function F(e,t,o,s){try{return await e.callTool(t,o,s)}catch(i){if(e.isConnected())throw i;v(`nuphus-mcp connection lost during ${t}: ${i.message}; restarting`),n?.connection===e&&(n=null),await _(e);const a=await y();if(!a)throw new Error(`nuphus-mcp restart failed: ${r?.message??"unknown"}`);return a.connection.callTool(t,o,s)}}async function q(e,t){if(!e)return[];t?.model&&(l=t);const o=h(),s=S(o);n&&n.connection.isConnected()&&n.envSig!==s&&(u("nuphus-mcp env changed; restarting to apply new env"),await L());const i=await y();return i?w({tools:i.tools,forwarder:(a,p)=>I(e,D)?i.forwarder(a,p):(d.warn(`tool ${a} blocked: desktop capability disabled for ${e}`),Promise.resolve("desktop capability is disabled for this agent (toggled off while tools were exposed); stop calling desktop_* tools"))}):[]}function G(){const e=n;return!e||!e.connection.isConnected()?R.map(t=>({kind:"builtin",toolName:t,exposedName:t,description:`${t}\uFF08\u684C\u9762\u81EA\u52A8\u5316\u5DE5\u5177\uFF0Cnuphus-mcp \u672A\u8FD0\u884C\uFF09\u3002\u5206\u914D desktop \u5206\u7C7B\u540E\u6309\u9700\u542F\u52A8\u540E\u7AEF\uFF0C\u5177\u4F53\u5DE5\u5177\u4EE5\u542F\u52A8\u540E\u76EE\u5F55\u4E3A\u51C6\u3002`,inputSchema:{type:"object",properties:{}},category:"desktop",async execute(){return`${t} is not ready: nuphus-mcp backend is not running. Allocate the desktop catalog to lazily start it, then retry.`}})):w({tools:e.tools,forwarder:(t,o)=>e.forwarder(t,o)})}const R=["desktop_screenshot","desktop_window_screenshot","desktop_perceive","desktop_vision","desktop_input","desktop_mouse","desktop_mouse_drag","desktop_screen_size","desktop_clipboard_write","desktop_clipboard_clean","desktop_config","desktop_uia_tree","desktop_uia_click","desktop_window_activate","desktop_window_info","desktop_window_move","desktop_window_resize","desktop_windows_list"];async function L(){const e=n;if(n=null,r=null,c){try{await c}catch{}c=null}e&&(await _(e.connection),u("nuphus-mcp stopped"))}function J(){return{started:n!==null,binaryPath:n?.binaryPath??null,toolCount:n?n.tools.filter(e=>e.name.startsWith("desktop_")).length:0,failure:r,visionSource:f}}async function _(e){try{await e.close()}catch{}}function u(e){d.info(e)}function v(e){d.warn(e)}export{L as disposeDesktopMcpManager,J as getDesktopMcpStatus,q as getDesktopTools,G as getDesktopToolsIfReady,K as resolveDesktopMcpBinary,y as startDesktopMcpManager};
2
+
@@ -1,4 +1,10 @@
1
1
 
2
+ export declare const DESKTOP_FEATURE_KEY = "desktop";
3
+
4
+ export declare const BROWSER_FEATURE_KEY = "browser";
5
+
6
+ export declare const LAUNCH_ITEM_FEATURE_KEY = "agentSys.launchItem";
7
+
2
8
  export declare function setAgentFeatures(agentId: string, features: unknown): void;
3
9
 
4
10
  export declare function getAgentFeatures(agentId: string): Record<string, boolean>;
@@ -1,2 +1,2 @@
1
- const r={},i=new Set(["desktop"]),n=new Map;function c(e){if(e==null)return{...r};let t=e;if(typeof e=="string")try{t=JSON.parse(e)}catch{t=null}if(typeof t!="object"||t===null||Array.isArray(t))return{...r};const u={};for(const[o,s]of Object.entries(t))i.has(o)||typeof s=="boolean"&&(u[o]=s);return u}function a(e,t){e&&n.set(e,c(t))}function f(e){return n.get(e)??{...r}}function l(e,t){return f(e)[t]===!0}function p(e){n.delete(e)}function A(){return n.size}export{p as clearAgentFeatures,f as getAgentFeatures,A as instanceFeaturesCacheSize,l as isFeatureEnabled,a as setAgentFeatures};
1
+ const o={},A="desktop",c="browser",l="agentSys.launchItem",i=new Set([c]),E=new Set,r=new Map;function f(e){if(e==null)return{...o};let t=e;if(typeof e=="string")try{t=JSON.parse(e)}catch{t=null}if(typeof t!="object"||t===null||Array.isArray(t))return{...o};const n={};for(const[s,u]of Object.entries(t))E.has(s)||typeof u=="boolean"&&(n[s]=u);return n}function p(e,t){e&&r.set(e,f(t))}function a(e){return r.get(e)??{...o}}function F(e,t){const n=a(e)[t];return n!==void 0?n:i.has(t)}function _(e){r.delete(e)}function S(){return r.size}export{c as BROWSER_FEATURE_KEY,A as DESKTOP_FEATURE_KEY,l as LAUNCH_ITEM_FEATURE_KEY,_ as clearAgentFeatures,a as getAgentFeatures,S as instanceFeaturesCacheSize,F as isFeatureEnabled,p as setAgentFeatures};
2
2
 
@@ -0,0 +1,20 @@
1
+
2
+ export interface JdkInfo {
3
+ id: string;
4
+
5
+ name: string;
6
+
7
+ path: string;
8
+
9
+ version: string;
10
+
11
+ source: "scan" | "manual";
12
+ }
13
+
14
+ export declare function scanInstalledJdks(): Promise<JdkInfo[]>;
15
+
16
+ export declare function listJdks(): Promise<JdkInfo[]>;
17
+
18
+ export declare function addManualJdk(jdkPath: string): Promise<JdkInfo>;
19
+
20
+ export declare function removeManualJdk(jdkId: string): Promise<boolean>;
@@ -0,0 +1,2 @@
1
+ import i from"node:os";import n from"node:path";import d from"node:crypto";import{promises as c}from"node:fs";import{createLogger as g}from"../utils/logger.js";import{atomicWriteJsonFile as y,withFileLock as j}from"../utils/file-utils.js";const f=g("jdk-scanner");function h(){return n.join(i.tmpdir(),"agentswork-runtime-bridge","jdk-registry.json")}function k(){const a=[];return process.env.JAVA_HOME&&a.push(process.env.JAVA_HOME),i.platform()==="win32"?a.push(n.join(process.env.ProgramFiles||"C:\\Program Files","Java"),n.join(process.env.ProgramFiles||"C:\\Program Files","Eclipse Adoptium"),n.join(process.env.ProgramFiles||"C:\\Program Files","Microsoft"),n.join(process.env.ProgramFiles||"C:\\Program Files","Amazon Corretto"),n.join(process.env.ProgramFiles||"C:\\Program Files","Zulu")):i.platform()==="darwin"?a.push("/Library/Java/JavaVirtualMachines"):a.push("/usr/lib/jvm","/opt/java","/opt/jdk","/usr/local/opt"),a.filter(Boolean)}function w(){return i.platform()==="win32"?"java.exe":"java"}async function u(a){try{return await c.access(n.join(a,"bin",w())),!0}catch{return!1}}async function m(a){try{return(await c.readFile(n.join(a,"release"),"utf-8")).match(/^JAVA_VERSION="?([^"\n]+)"?/m)?.[1]?.trim()||""}catch{return""}}async function C(a){try{return(await c.readdir(a,{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>n.join(a,r.name))}catch{return[]}}function v(a){return`scan-${d.createHash("sha1").update(a.toLowerCase()).digest("hex").slice(0,12)}`}async function A(){const a=[],e=new Set,r=k();for(const t of r){const o=await C(t);for(const s of o){const p=s.toLowerCase();e.has(p)||(e.add(p),await u(s)&&a.push({id:v(s),name:n.basename(s),path:s,version:await m(s),source:"scan"}))}}if(process.env.JAVA_HOME&&!e.has(process.env.JAVA_HOME.toLowerCase())){const t=process.env.JAVA_HOME;e.add(t.toLowerCase()),await u(t)&&a.push({id:v(t),name:n.basename(t),path:t,version:await m(t),source:"scan"})}return a.sort((t,o)=>(o.version||"").localeCompare(t.version||"")||t.name.localeCompare(o.name)),a}async function l(){try{const a=await c.readFile(h(),"utf-8"),e=JSON.parse(a);return Array.isArray(e)?e:[]}catch{return[]}}async function J(a){const e=h();await j(e,async()=>{await y(e,a)})}async function D(){const a=await A(),e=await l();if(e.length===0)return a;const r=new Set(a.map(o=>o.path.toLowerCase())),t=[...a];for(const o of e){const s=o.path.toLowerCase();r.has(s)||await u(o.path)&&t.push({id:o.id,name:n.basename(o.path),path:o.path,version:await m(o.path),source:"manual"})}return t}async function V(a){const e=a.trim();if(!e)throw new Error("JDK \u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");const r=n.resolve(e);if(!await u(r))throw new Error(`\u4E0D\u662F\u6709\u6548\u7684 JDK \u76EE\u5F55\uFF08\u7F3A\u5C11 bin/${w()}\uFF09: ${r}`);const t=await l();if(t.some(s=>s.path.toLowerCase()===r.toLowerCase()))throw new Error("\u8BE5 JDK \u5DF2\u6DFB\u52A0");const o=`manual-${d.randomUUID().slice(0,8)}`;return t.push({id:o,path:r}),await J(t),f.info(`[jdk] \u624B\u52A8\u6DFB\u52A0 JDK: id=${o}, path=${r}`),{id:o,name:n.basename(r),path:r,version:await m(r),source:"manual"}}async function x(a){if(!a.startsWith("manual-"))throw new Error("\u4EC5\u652F\u6301\u5220\u9664\u624B\u52A8\u6DFB\u52A0\u7684 JDK");const e=await l(),r=e.filter(t=>t.id!==a);return r.length===e.length?!1:(await J(r),f.info(`[jdk] \u5220\u9664\u624B\u52A8 JDK: id=${a}`),!0)}export{V as addManualJdk,D as listJdks,x as removeManualJdk,A as scanInstalledJdks};
2
+
@@ -0,0 +1,181 @@
1
+
2
+ import { type LaunchHistoryEntry, type LaunchHistorySample, type LaunchMetricSample } from "./launch-metrics.js";
3
+ import { type RingBufferSlice } from "./ring-buffer.js";
4
+ export type LaunchRunState = "running" | "exited" | "stopped";
5
+
6
+ export interface LaunchLogEntry {
7
+ stream: "stdout" | "stderr";
8
+ text: string;
9
+ }
10
+
11
+ export interface LaunchRun {
12
+ configId: string;
13
+ pid: number;
14
+ state: LaunchRunState;
15
+ startedAt: string;
16
+ exitedAt?: string;
17
+ exitCode?: number | null;
18
+ command: string;
19
+ cwd: string;
20
+
21
+ startedBy?: string;
22
+
23
+ stoppedBy?: string;
24
+ }
25
+
26
+ export interface LaunchStartRequest {
27
+ configId: string;
28
+
29
+ command: string;
30
+
31
+ cwd?: string;
32
+
33
+ env?: Record<string, string>;
34
+
35
+ jdkPath?: string;
36
+
37
+ workspacePath?: string;
38
+
39
+ readinessPattern?: string;
40
+
41
+ startedBy?: string;
42
+ }
43
+
44
+ export interface LaunchRunSummary extends LaunchRun {
45
+ logTail: LaunchLogEntry[];
46
+ nextLogIndex: number;
47
+
48
+ ports: number[];
49
+
50
+ metrics?: LaunchMetricSample;
51
+ }
52
+
53
+ export interface LaunchLogsResult {
54
+ configId: string;
55
+ state: LaunchRunState;
56
+ entries: RingBufferSlice<LaunchLogEntry>;
57
+ nextIndex: number;
58
+ oldestIndex: number;
59
+ }
60
+
61
+ export interface LaunchStartupWaitResult {
62
+ outcome: "ready" | "exited" | "stopped" | "timeout";
63
+
64
+ waitedMs: number;
65
+
66
+ matchedLine?: string;
67
+
68
+ ports?: number[];
69
+
70
+ exitCode?: number | null;
71
+
72
+ logTail: LaunchLogEntry[];
73
+ }
74
+
75
+ export interface LaunchBatchItemResult {
76
+ configId: string;
77
+ ok: boolean;
78
+ run?: LaunchRun;
79
+ error?: string;
80
+ wait?: LaunchStartupWaitResult;
81
+ }
82
+
83
+ export interface LaunchBatchResult {
84
+ items: LaunchBatchItemResult[];
85
+ summary: {
86
+ total: number;
87
+ started: number;
88
+ failed: number;
89
+ ready: number;
90
+ exited: number;
91
+ stopped: number;
92
+ timeout: number;
93
+ };
94
+ }
95
+
96
+ export declare function resolvePlaceholders(command: string, ctx: {
97
+ workspacePath?: string;
98
+ jdkPath?: string;
99
+ env?: Record<string, string>;
100
+ }): string;
101
+
102
+ export declare class StreamDecoder {
103
+ private readonly utf8Probe;
104
+ private readonly utf8;
105
+ private readonly gbk;
106
+ private readonly onFlush;
107
+ private mode;
108
+ private buffered;
109
+ private bufferedSize;
110
+ private flushTimer;
111
+
112
+ constructor(onFlush?: (text: string) => void);
113
+
114
+ decode(chunk: Buffer): string;
115
+
116
+ finalize(): string;
117
+ private commit;
118
+ }
119
+
120
+ export declare class LaunchManager {
121
+ private static instance;
122
+
123
+ private readonly runs;
124
+
125
+ private readonly logs;
126
+
127
+ private readonly startupWaiters;
128
+
129
+ private readonly readinessMatchers;
130
+
131
+ private readonly logListeners;
132
+
133
+ private readonly metricsMonitor;
134
+
135
+ private restored;
136
+ private constructor();
137
+ static getInstance(): LaunchManager;
138
+
139
+ private runningRefs;
140
+
141
+ start(req: LaunchStartRequest): Promise<LaunchRun>;
142
+
143
+ stop(configId: string, reason?: "manual" | "shutdown", actor?: string): Promise<{
144
+ success: boolean;
145
+ message: string;
146
+ pid?: number;
147
+ }>;
148
+
149
+ restart(req: LaunchStartRequest): Promise<LaunchRun>;
150
+
151
+ startBatch(items: LaunchStartRequest[], waitTimeoutMs: number): Promise<LaunchBatchResult>;
152
+
153
+ stopAll(): Promise<{
154
+ running: number;
155
+ stopped: number;
156
+ }>;
157
+
158
+ waitStartupResult(configId: string, timeoutMs: number): Promise<LaunchStartupWaitResult>;
159
+
160
+ private scanReadinessMatch;
161
+
162
+ private resolveStartupWaiters;
163
+
164
+ listRuns(): Promise<LaunchRunSummary[]>;
165
+
166
+ getLogs(configId: string, sinceIndex: number): Promise<LaunchLogsResult | null>;
167
+
168
+ getRun(configId: string): LaunchRun | undefined;
169
+
170
+ getMetrics(configId: string, limit?: number): LaunchHistorySample[];
171
+
172
+ getMetricsSince(configId: string, sinceIndex: number, limit?: number): LaunchHistoryEntry[];
173
+
174
+ private ensureRestored;
175
+
176
+ private publishRunStatus;
177
+
178
+ private persist;
179
+ }
180
+
181
+ export declare function getLaunchManager(): LaunchManager;
@@ -0,0 +1,12 @@
1
+ import{exec as _,spawn as j}from"node:child_process";import{promises as z}from"node:fs";import A from"node:os";import M from"node:path";import{promisify as F}from"node:util";import{atomicWriteJsonFile as J,withFileLock as U}from"../utils/file-utils.js";import{createLogger as G}from"../utils/logger.js";import{getListeningPortsForProcessTrees as L}from"./port-detector.js";import{LaunchMetricsMonitor as V}from"./launch-metrics.js";import{isProcessRunning as E,terminateProcessTree as k}from"./process-detector.js";import{RingBuffer as O}from"./ring-buffer.js";import{getLaunchStreamHub as P}from"./launch-streams.js";const C=F(_),g=G("launch-manager"),D=5e3,B=4096,X=1500,K=20;function H(){return M.join(A.tmpdir(),"agentswork-runtime-bridge","launch-runs.json")}function Y(l,t){return l.replace(/\$\{([^}]+)\}/g,(e,s)=>{const r=s.trim(),i=r.toUpperCase();if(i==="WORKSPACE")return t.workspacePath||"";if(i==="JAVA_HOME")return t.jdkPath||process.env.JAVA_HOME||"";if(i==="HOME")return A.homedir();if(t.env&&t.env[r]!==void 0)return t.env[r];if(t.env){for(const[n,a]of Object.entries(t.env))if(n.toUpperCase()===i)return a}return e})}function Z(l){const t={...process.env};l.env&&Object.assign(t,l.env);for(const e of Object.keys(t))if(e.toUpperCase()==="PATH"&&e!=="PATH"){const s=t[e];delete t[e],t.PATH===void 0&&typeof s=="string"&&(t.PATH=s)}if(l.jdkPath){t.JAVA_HOME=l.jdkPath;const e=M.join(l.jdkPath,"bin"),s=M.delimiter;t.PATH=t.PATH?`${e}${s}${t.PATH}`:e}return t}const Q=256,q=120;class N{constructor(t=()=>{}){this.utf8Probe=new TextDecoder("utf-8",{fatal:!0}),this.utf8=new TextDecoder("utf-8"),this.gbk=new TextDecoder("gbk"),this.mode=null,this.buffered=[],this.bufferedSize=0,this.flushTimer=null,this.onFlush=t}decode(t){return this.mode!==null?(this.mode==="gbk"?this.gbk:this.utf8).decode(t,{stream:!0}):(this.buffered.push(t),this.bufferedSize+=t.length,this.bufferedSize>=Q?this.commit():(this.flushTimer===null&&(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.onFlush(this.commit())},q)),""))}finalize(){return this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.mode===null?this.commit():(this.mode==="gbk"?this.gbk:this.utf8).decode()}commit(){if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.mode!==null)return"";const t=Buffer.concat(this.buffered,this.bufferedSize);this.buffered=[],this.bufferedSize=0;try{return this.utf8Probe.decode(t),this.mode="utf8",this.utf8.decode(t)}catch{return this.mode="gbk",this.gbk.decode(t)}}}function tt(l){const t=l.length>0&&!l.endsWith(`
2
+ `)?l+`
3
+ `:l;return t.length>B?t.slice(-B):t}async function et(l){const t=A.platform();for(let e=0;e<6;e+=1){try{if(t==="win32"){const s=`Get-CimInstance Win32_Process -Filter "ParentProcessId = ${l} AND Name <> 'conhost.exe'" | Select-Object -First 1 ProcessId | ConvertTo-Json -Compress`,r=Buffer.from(s,"utf16le").toString("base64"),{stdout:i}=await C(`powershell -NoProfile -NonInteractive -EncodedCommand ${r}`,{encoding:"utf8",timeout:5e3,windowsHide:!0}),n=i.replace(/#\s*<\s*CLIXML[\s\S]*?(?=\{|\[|$)/g,"").trim(),a=JSON.parse(n||"null"),h=Number(a?.ProcessId);if(Number.isFinite(h)&&h>0)return h}else{const{stdout:s}=await C(`pgrep -P ${l} | head -n 1`,{encoding:"utf8",timeout:3e3}),r=Number(s.trim());if(Number.isFinite(r)&&r>0)return r}}catch{}await new Promise(s=>setTimeout(s,300))}return null}class S{constructor(){this.runs=new Map,this.logs=new Map,this.startupWaiters=new Map,this.readinessMatchers=new Map,this.logListeners=new Map,this.metricsMonitor=new V(()=>this.runningRefs()),this.restored=!1}static getInstance(){return S.instance===null&&(S.instance=new S),S.instance}runningRefs(){const t=[];for(const e of this.runs.values())e.state==="running"&&e.pid>0&&t.push({configId:e.configId,pid:e.pid});return t}async start(t){const e=t.configId;if(!e||!e.trim())throw new Error("configId is required");if(!t.command||!t.command.trim())throw new Error("command is required");let s=null;if(t.readinessPattern&&t.readinessPattern.trim())try{s=new RegExp(t.readinessPattern.trim())}catch(o){throw new Error(`readinessPattern \u6B63\u5219\u8BED\u6CD5\u65E0\u6548: ${o.message}`)}const r=t.startedBy?.trim()||void 0;await this.ensureRestored();const i=this.runs.get(e);if(i&&i.state==="running"&&E(i.pid))throw new Error(`\u542F\u52A8\u9879\u5DF2\u5728\u8FD0\u884C: pid=${i.pid}`);const n=Y(t.command,{workspacePath:t.workspacePath,jdkPath:t.jdkPath,env:t.env}),a=t.cwd||t.workspacePath||process.cwd(),h=Z(t);g.info(`[start] \u542F\u52A8\u547D\u4EE4: configId=${e}, cwd=${a}, command=${n}`);let m;try{m=j(n,{cwd:a,env:h,shell:!0,windowsHide:!0})}catch(o){const c=`\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${o.message}`;throw g.error(`[start] spawn \u5931\u8D25: configId=${e}, error=${c}`),new Error(c)}const w=m.pid??0,T={configId:e,pid:w,state:"running",startedAt:new Date().toISOString(),command:n,cwd:a,...r?{startedBy:r}:{}};this.runs.set(e,T),this.publishRunStatus(e,!0),this.metricsMonitor.clear(e),this.metricsMonitor.ensureRunning();let p=this.logs.get(e);p||(p=new O(D),this.logs.set(e,p));const $=p.getSinceIndex(-1).nextIndex;s?this.readinessMatchers.set(e,{pattern:s,fromIndex:$+1}):this.readinessMatchers.delete(e);const b=p.append({stream:"stdout",text:`> ${n}
4
+ `});P().publish(e,"logs",b.index,b.data);let R=null;(async()=>{const o=await et(w),c=this.runs.get(e);o&&c?.pid===w&&(R=o,this.runs.set(e,{...c,pid:o}),await this.persist(),this.publishRunStatus(e))})();const x=o=>o!==void 0&&(o.pid===w||o.pid===R),u=new N(o=>{f("stdout",o)}),d=new N(o=>{f("stderr",o)}),f=(o,c)=>{const y=tt(c);if(y){const v=p.append({stream:o,text:y});P().publish(e,"logs",v.index,v.data);const I=this.logListeners.get(e);if(I&&I.size>0)for(const W of I)try{W(v)}catch{}}};return m.stdout.on("data",o=>{f("stdout",u.decode(o))}),m.stderr.on("data",o=>{f("stderr",d.decode(o))}),m.on("error",o=>{f("stderr",`[\u542F\u52A8\u5931\u8D25] ${o.message}
5
+ `);const c=this.runs.get(e);c&&x(c)&&c.state==="running"&&(this.runs.set(e,{...c,state:"exited",exitedAt:new Date().toISOString(),exitCode:-1}),P().publishState(e,{configId:e,state:"exited",exitCode:-1}),this.publishRunStatus(e),this.persist(),this.resolveStartupWaiters(e,"exited",-1))}),m.on("exit",(o,c)=>{f("stdout",u.finalize()),f("stderr",d.finalize()),f("stderr",`
6
+ [\u8FDB\u7A0B\u9000\u51FA] code=${o??"-"}${c?` signal=${c}`:""}
7
+ `);const y=this.runs.get(e);y&&x(y)&&y.state==="running"&&(this.runs.set(e,{...y,state:"exited",exitedAt:new Date().toISOString(),exitCode:o}),P().publishState(e,{configId:e,state:"exited",exitCode:o}),this.publishRunStatus(e),this.persist(),this.resolveStartupWaiters(e,"exited",o))}),await this.persist(),T}async stop(t,e="manual",s){await this.ensureRestored();const r=this.runs.get(t);if(!r||r.state!=="running")return{success:!1,message:"\u542F\u52A8\u9879\u672A\u5728\u8FD0\u884C",pid:r?.pid};const i=s?.trim()||void 0;g.info(`[stop] \u7EC8\u6B62\u8FDB\u7A0B\u6811: configId=${t}, pid=${r.pid}, reason=${e}${i?`, actor=${i}`:""}`),this.runs.set(t,{...r,state:"stopped",exitedAt:new Date().toISOString(),...i?{stoppedBy:i}:{}});const n=await k(r.pid),a=e==="shutdown"?`
8
+ [bridge \u505C\u6B62\uFF0C\u8FDB\u7A0B\u5DF2\u8054\u52A8\u7EC8\u6B62]
9
+ `:`
10
+ [\u5DF2\u624B\u52A8\u505C\u6B62]
11
+ `,h=this.logs.get(t)?.append({stream:"stderr",text:a});return h&&P().publish(t,"logs",h.index,h.data),P().publishState(t,{configId:t,state:"stopped"}),this.publishRunStatus(t),await this.persist(),this.resolveStartupWaiters(t,"stopped",void 0),{success:n.failed===0,message:n.failed===0?"\u8FDB\u7A0B\u5DF2\u505C\u6B62":`\u90E8\u5206\u8FDB\u7A0B\u672A\u80FD\u7EC8\u6B62\uFF08terminated=${n.terminated}, failed=${n.failed}\uFF09`,pid:r.pid}}async restart(t){await this.ensureRestored();const e=this.runs.get(t.configId);return e&&e.state==="running"&&await this.stop(t.configId,"manual",t.startedBy),await this.start(t)}async startBatch(t,e){await this.ensureRestored();const s=await Promise.all(t.map(async i=>{try{const n=await this.start(i);return{configId:i.configId,ok:!0,run:n}}catch(n){return{configId:i.configId,ok:!1,error:n.message}}}));if(e>0){const i=await Promise.all(s.map(async n=>{if(!n.ok)return n;try{const a=await this.waitStartupResult(n.configId,e);return{...n,wait:a}}catch(a){return{...n,error:a.message}}}));s.splice(0,s.length,...i)}const r={total:s.length,started:0,failed:0,ready:0,exited:0,stopped:0,timeout:0};for(const i of s){if(!i.ok){r.failed+=1;continue}r.started+=1,i.wait&&(r[i.wait.outcome]+=1)}return{items:s,summary:r}}async stopAll(){await this.ensureRestored();const t=Array.from(this.runs.values()).filter(s=>s.state==="running");let e=0;for(const s of t)try{const r=await this.stop(s.configId,"shutdown");r.success?e++:g.warn(`[stopAll] \u542F\u52A8\u9879\u672A\u80FD\u5B8C\u5168\u7EC8\u6B62: configId=${s.configId}, message=${r.message}`)}catch(r){g.warn(`[stopAll] \u505C\u6B62\u542F\u52A8\u9879\u5931\u8D25: configId=${s.configId}, error=${r.message}`)}return{running:t.length,stopped:e}}async waitStartupResult(t,e){const s=this.runs.get(t);if(!s)throw new Error("launch run not found");const r=Date.now(),i=()=>(this.logs.get(t)?.getLastN(K)??[]).map(a=>a.data);if(s.state!=="running")return{outcome:s.state==="stopped"?"stopped":"exited",waitedMs:0,...s.state==="exited"?{exitCode:s.exitCode??null}:{},logTail:i()};const n=this.readinessMatchers.get(t);if(n){const a=this.scanReadinessMatch(t,n);if(a!==null)return{outcome:"ready",waitedMs:0,matchedLine:a,logTail:i()}}return await new Promise(a=>{let h=!1,m=null,w=null,T=!1,p=null;const $=()=>{m!==null&&(clearInterval(m),m=null),w!==null&&(clearTimeout(w),w=null);const u=this.startupWaiters.get(t);if(u&&(u.delete(R),u.size===0&&this.startupWaiters.delete(t)),p!==null){const d=this.logListeners.get(t);d&&(d.delete(p),d.size===0&&this.logListeners.delete(t)),p=null}},b=u=>{h||(h=!0,$(),a(u))},R=(u,d)=>{b({outcome:u==="stopped"?"stopped":"exited",waitedMs:Date.now()-r,...u==="exited"?{exitCode:d??null}:{},logTail:i()})};let x=this.startupWaiters.get(t);if(x||(x=new Set,this.startupWaiters.set(t,x)),x.add(R),n){p=d=>{if(!(h||d.index<n.fromIndex)){for(const f of d.data.text.split(/\r?\n/))if(f&&n.pattern.test(f)){b({outcome:"ready",waitedMs:Date.now()-r,matchedLine:f.trim(),logTail:i()});return}}};let u=this.logListeners.get(t);u||(u=new Set,this.logListeners.set(t,u)),u.add(p)}else{const u=async()=>{if(h||T)return;const d=this.runs.get(t);if(!(!d||d.state!=="running"||d.pid<=0)){T=!0;try{const o=(await L([d.pid])).get(d.pid)??[];if(o.length>0&&!h){const c=this.runs.get(t);c?.state==="running"&&c.pid===d.pid&&b({outcome:"ready",waitedMs:Date.now()-r,ports:o,logTail:i()})}}catch{}finally{T=!1}}};m=setInterval(()=>{u()},X),u()}w=setTimeout(()=>{b({outcome:"timeout",waitedMs:Date.now()-r,logTail:i()})},e)})}scanReadinessMatch(t,e){const s=this.logs.get(t);if(!s)return null;for(const r of s.getSinceIndex(e.fromIndex-1).entries)for(const i of r.data.text.split(/\r?\n/))if(i&&e.pattern.test(i))return i.trim();return null}resolveStartupWaiters(t,e,s){const r=this.startupWaiters.get(t);if(!(!r||r.size===0)){this.startupWaiters.delete(t);for(const i of r)try{i(e,s)}catch{}}}async listRuns(){await this.ensureRestored();const t=Array.from(this.runs.values()).filter(r=>r.state==="running"&&r.pid>0).map(r=>r.pid);let e=new Map;if(t.length>0)try{e=await L(t)}catch(r){g.debug(`[listRuns] \u7AEF\u53E3\u63A2\u6D4B\u5931\u8D25: ${r.message}`)}const s=[];for(const r of this.runs.values()){const i=this.logs.get(r.configId);s.push({...r,logTail:i?i.getLastN(20).map(n=>n.data):[],nextLogIndex:i?i.getSinceIndex(-1).nextIndex:0,ports:r.state==="running"?e.get(r.pid)??[]:[],metrics:this.metricsMonitor.getLatest(r.configId)??void 0})}return s}async getLogs(t,e){await this.ensureRestored();const s=this.runs.get(t);if(!s)return null;const r=this.logs.get(t);if(!r)return{configId:t,state:s.state,entries:[],nextIndex:0,oldestIndex:0};const{entries:i,nextIndex:n,oldestIndex:a}=r.getSinceIndex(e);return{configId:t,state:s.state,entries:i,nextIndex:n,oldestIndex:a}}getRun(t){return this.runs.get(t)}getMetrics(t,e){return this.metricsMonitor.getHistory(t,e)}getMetricsSince(t,e,s){return this.metricsMonitor.getHistoryEntriesSince(t,e,s)}async ensureRestored(){if(!this.restored){this.restored=!0;try{const t=await z.readFile(H(),"utf-8"),e=JSON.parse(t);for(const s of e){const r=s.pid>0&&E(s.pid);if(r){g.warn(`[restore] \u68C0\u6D4B\u5230\u4E0A\u6B21 bridge \u975E\u4F18\u96C5\u9000\u51FA\u540E\u7684\u6B8B\u7559\u8FDB\u7A0B\uFF0C\u8054\u52A8\u7EC8\u6B62: configId=${s.configId}, pid=${s.pid}`);try{await k(s.pid)}catch(i){g.warn(`[restore] \u7EC8\u6B62\u6B8B\u7559\u8FDB\u7A0B\u5931\u8D25: configId=${s.configId}, pid=${s.pid}, error=${i.message}`)}}this.runs.set(s.configId,{...s,state:"exited",...r?{exitedAt:new Date().toISOString()}:{}}),this.logs.set(s.configId,new O(D))}g.info(`[restore] \u5DF2\u6062\u590D ${e.length} \u6761\u542F\u52A8\u9879\u8FD0\u884C\u8BB0\u5F55`)}catch{}}}publishRunStatus(t,e=!1){const s=this.runs.get(t);s&&(P().publishStatusRun({...s}),!(!e||s.state!=="running"||s.pid<=0)&&setTimeout(()=>{const r=this.runs.get(t);!r||r.state!=="running"||r.pid<=0||(async()=>{let i=[];try{i=(await L([r.pid])).get(r.pid)??[]}catch{}const n=this.runs.get(t);n&&n.state==="running"&&n.pid===r.pid&&P().publishStatusRun({...n,ports:i})})()},2e3))}async persist(){const t=H();try{const e=Array.from(this.runs.values());await U(t,async()=>{await J(t,e)})}catch(e){g.warn(`[persist] \u6301\u4E45\u5316\u5931\u8D25: ${e.message}`)}}}S.instance=null;function pt(){return S.getInstance()}export{S as LaunchManager,N as StreamDecoder,pt as getLaunchManager,Y as resolvePlaceholders};
12
+
@@ -0,0 +1,89 @@
1
+
2
+
3
+ export declare const METRICS_SAMPLE_INTERVAL_MS = 2000;
4
+
5
+ export declare const METRICS_HISTORY_CAPACITY = 900;
6
+
7
+ export interface LaunchMetricSample {
8
+
9
+ cpuPercent: number;
10
+
11
+ memoryBytes: number;
12
+
13
+ threadCount: number;
14
+ }
15
+
16
+ export interface LaunchHistorySample extends LaunchMetricSample {
17
+ at: number;
18
+ }
19
+
20
+ export interface LaunchHistoryEntry {
21
+ index: number;
22
+ at: number;
23
+ sample: LaunchMetricSample;
24
+ }
25
+
26
+ export interface ProcessEntry {
27
+ pid: number;
28
+ ppid: number;
29
+
30
+ cpuMs: number;
31
+
32
+ rssBytes: number;
33
+
34
+ threads: number;
35
+ }
36
+
37
+ export interface RunningRunRef {
38
+ configId: string;
39
+ pid: number;
40
+ }
41
+
42
+ export declare function parseWindowsProcessTable(output: string): ProcessEntry[];
43
+
44
+ export declare function parsePsCpuTime(value: string): number;
45
+
46
+ export declare function parseUnixProcessTable(output: string, threadsColumn: boolean): ProcessEntry[];
47
+
48
+ export interface ProcessTreeAggregate {
49
+ cpuMs: number;
50
+ rssBytes: number;
51
+ threads: number;
52
+
53
+ found: boolean;
54
+ }
55
+
56
+ export declare function aggregateProcessTree(entries: ProcessEntry[], rootPid: number): ProcessTreeAggregate;
57
+
58
+ export declare function computeCpuPercent(previous: {
59
+ at: number;
60
+ totalCpuMs: number;
61
+ }, current: {
62
+ at: number;
63
+ totalCpuMs: number;
64
+ }): number;
65
+
66
+ export declare class LaunchMetricsMonitor {
67
+ private readonly getRuns;
68
+
69
+ private readonly histories;
70
+
71
+ private readonly cpuBaselines;
72
+ private timer;
73
+
74
+ private ticking;
75
+ constructor(getRuns: () => RunningRunRef[]);
76
+
77
+ ensureRunning(): void;
78
+
79
+ stop(): void;
80
+
81
+ clear(configId: string): void;
82
+
83
+ getLatest(configId: string): LaunchMetricSample | null;
84
+
85
+ getHistory(configId: string, limit?: number): LaunchHistorySample[];
86
+
87
+ getHistoryEntriesSince(configId: string, sinceIndex: number, limit?: number): LaunchHistoryEntry[];
88
+ private tick;
89
+ }
@@ -0,0 +1,4 @@
1
+ import{exec as m}from"node:child_process";import h from"node:os";import{promisify as g}from"node:util";import{createLogger as M}from"../utils/logger.js";import{getLaunchStreamHub as b}from"./launch-streams.js";import{RingBuffer as y}from"./ring-buffer.js";const l=g(m),w=M("launch-metrics"),N=2e3,d=900;function x(r){return r.replace(/#\s*<\s*CLIXML[\s\S]*?(?=#<\s*CLIXML|$)/g,"").trim()}function I(r){return r==null?[]:Array.isArray(r)?r:[r]}function S(r){const t=x(r),e=t.search(/[[{]/);if(e<0)return[];const n=JSON.parse(t.slice(e)),s=[];for(const i of I(n)){const o=Number(i?.ProcessId),c=Number(i?.ParentProcessId);if(!Number.isFinite(o)||!Number.isFinite(c))continue;const a=Number(i?.UserModeTime??0)/1e4,u=Number(i?.KernelModeTime??0)/1e4;s.push({pid:o,ppid:c,cpuMs:(Number.isFinite(a)?a:0)+(Number.isFinite(u)?u:0),rssBytes:Math.max(0,Number(i?.WorkingSetSize)||0),threads:Math.max(0,Number(i?.ThreadCount)||0)})}return s}function C(r){const t=r.trim();if(!t)return 0;const[e,...n]=t.split("-");let s=0,i;n.length>0?(s=Number(e)||0,i=n.join("-")):i=e;const o=i.split(":").map(p=>Number(p)||0),c=o[o.length-1]??0,a=o[o.length-2]??0,u=o[o.length-3]??0;return(((s*24+u)*60+a)*60+c)*1e3}function P(r,t){const e=[];for(const n of r.split(`
2
+ `)){const s=n.trim().split(/\s+/),i=t?5:4;if(s.length<i)continue;const o=Number.parseInt(s[0],10),c=Number.parseInt(s[1],10),a=Number.parseInt(s[2],10),u=t?Number.parseInt(s[3],10):0,p=C(s[t?4:3]);!Number.isFinite(o)||!Number.isFinite(c)||e.push({pid:o,ppid:c,cpuMs:p,rssBytes:Number.isFinite(a)&&a>0?a*1024:0,threads:Number.isFinite(u)&&u>0?u:0})}return e}async function T(r){const t=new Map;for(const e of r)try{const{stdout:n}=await l(`ps -M -p ${e}`,{encoding:"utf8",timeout:5e3}),s=n.split(`
3
+ `).filter(i=>i.trim().length>0);t.set(e,Math.max(0,s.length-1))}catch{}return t}async function B(){const e=Buffer.from("Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId, WorkingSetSize, UserModeTime, KernelModeTime, ThreadCount | ConvertTo-Json -Compress","utf16le").toString("base64"),{stdout:n}=await l(`powershell -NoProfile -NonInteractive -EncodedCommand ${e}`,{encoding:"utf8",timeout:1e4,windowsHide:!0,maxBuffer:10*1024*1024});return S(n)}async function L(r){const t=h.platform();if(t==="win32")return B();const e=t==="darwin",n=e?"pid=,ppid=,rss=,time=":"pid=,ppid=,rss=,nlwp=,time=",{stdout:s}=await l(`ps -axo ${n}`,{encoding:"utf8",timeout:5e3}),i=P(s,!e);if(e&&r.length>0){const o=await T(r);for(const c of i){const a=o.get(c.pid);a!==void 0&&(c.threads=a)}}return i}function k(r){const t=new Map;for(const e of r){const n=t.get(e.ppid);n?n.push(e.pid):t.set(e.ppid,[e.pid])}return t}function A(r,t){const e=new Map(r.map(u=>[u.pid,u]));if(!e.has(t))return{cpuMs:0,rssBytes:0,threads:0,found:!1};const n=k(r),s=[t],i=new Set;let o=0,c=0,a=0;for(;s.length>0;){const u=s.shift();if(i.has(u))continue;i.add(u);const p=e.get(u);p&&(o+=p.cpuMs,c+=p.rssBytes,a+=p.threads);for(const f of n.get(u)??[])s.push(f)}return{cpuMs:o,rssBytes:c,threads:a,found:!0}}function F(r,t){const e=t.at-r.at,n=t.totalCpuMs-r.totalCpuMs;return e<=0||n<=0?0:n/e*100}class j{constructor(t){this.getRuns=t,this.histories=new Map,this.cpuBaselines=new Map,this.timer=null,this.ticking=!1}ensureRunning(){if(this.timer===null){const t=setInterval(()=>void this.tick(),N);t.unref?.(),this.timer=t,this.tick()}}stop(){this.timer!==null&&(clearInterval(this.timer),this.timer=null)}clear(t){this.histories.delete(t),this.cpuBaselines.delete(t)}getLatest(t){const e=this.histories.get(t);if(!e)return null;const n=e.getLastN(1)[0];return n?n.data:null}getHistory(t,e=d){const n=this.histories.get(t);if(!n)return[];const s=Math.max(0,Math.min(e,d));return n.getLastN(s).map(i=>({at:i.timestamp,...i.data}))}getHistoryEntriesSince(t,e,n=d){const s=this.histories.get(t);if(!s)return[];const{entries:i}=s.getSinceIndex(e),o=Math.max(0,Math.min(n,d));return i.slice(-o).map(c=>({index:c.index,at:c.timestamp,sample:c.data}))}async tick(){if(!this.ticking){this.ticking=!0;try{const t=this.getRuns();if(t.length===0){this.stop();return}let e=[];try{e=await L(t.map(s=>s.pid))}catch(s){w.debug(`[launch-metrics] \u8FDB\u7A0B\u8868\u91C7\u96C6\u5931\u8D25: ${s.message}`);return}const n=Date.now();for(const s of t){const i=A(e,s.pid);if(!i.found)continue;const o=this.cpuBaselines.get(s.configId),c=o?F(o,{at:n,totalCpuMs:i.cpuMs}):0;this.cpuBaselines.set(s.configId,{at:n,totalCpuMs:i.cpuMs});let a=this.histories.get(s.configId);a||(a=new y(d),this.histories.set(s.configId,a));const u=a.append({cpuPercent:c,memoryBytes:i.rssBytes,threadCount:i.threads});b().publish(s.configId,"metrics",u.index,u.data)}}finally{this.ticking=!1}}}}export{j as LaunchMetricsMonitor,d as METRICS_HISTORY_CAPACITY,N as METRICS_SAMPLE_INTERVAL_MS,A as aggregateProcessTree,F as computeCpuPercent,C as parsePsCpuTime,P as parseUnixProcessTable,S as parseWindowsProcessTable};
4
+
@@ -0,0 +1,24 @@
1
+
2
+ export type LaunchStreamKind = "logs" | "metrics";
3
+ export declare class LaunchStreamHub {
4
+ private static instance;
5
+
6
+ private readonly subscribers;
7
+
8
+ private readonly statusSubscribers;
9
+ static getInstance(): LaunchStreamHub;
10
+
11
+ subscribe(configId: string, kind: LaunchStreamKind, send: (frame: string) => void): () => void;
12
+
13
+ subscriberCount(configId: string, kind: LaunchStreamKind): number;
14
+
15
+ subscribeStatus(send: (frame: string) => void): () => void;
16
+
17
+ publishStatusRun(run: unknown): void;
18
+
19
+ publish(configId: string, kind: LaunchStreamKind, id: number, data: unknown): void;
20
+
21
+ publishState(configId: string, state: unknown): void;
22
+ }
23
+
24
+ export declare function getLaunchStreamHub(): LaunchStreamHub;
@@ -0,0 +1,11 @@
1
+ class i{constructor(){this.subscribers=new Map,this.statusSubscribers=new Set}static getInstance(){return i.instance===null&&(i.instance=new i),i.instance}subscribe(s,t,n){const r=b(s,t);let e=this.subscribers.get(r);e||(e=new Set,this.subscribers.set(r,e));const c={configId:s,kind:t,send:n};return e.add(c),()=>{const u=this.subscribers.get(r);u&&(u.delete(c),u.size===0&&this.subscribers.delete(r))}}subscriberCount(s,t){return this.subscribers.get(b(s,t))?.size??0}subscribeStatus(s){const t={send:s};return this.statusSubscribers.add(t),()=>{this.statusSubscribers.delete(t)}}publishStatusRun(s){if(this.statusSubscribers.size===0)return;const t=`event: run_upsert
2
+ data: ${JSON.stringify({run:s})}
3
+
4
+ `;for(const n of[...this.statusSubscribers])try{n.send(t)}catch{}}publish(s,t,n,r){const e=this.subscribers.get(b(s,t));if(!e||e.size===0)return;const c=`id: ${n}
5
+ data: ${JSON.stringify(r)}
6
+
7
+ `;for(const u of[...e])try{u.send(c)}catch{}}publishState(s,t){const n=`event: run_state
8
+ data: ${JSON.stringify(t)}
9
+
10
+ `;for(const r of["logs","metrics"]){const e=this.subscribers.get(b(s,r));if(!(!e||e.size===0))for(const c of[...e])try{c.send(n)}catch{}}}}i.instance=null;function b(a,s){return`${a}:${s}`}function o(){return i.getInstance()}export{i as LaunchStreamHub,o as getLaunchStreamHub};
11
+
@@ -1,2 +1,2 @@
1
- import B from"node:path";import{schedulerBaseUrl as m}from"../config.js";import{getRuntimeAccessToken as T,loadRuntimeBinding as U,normalizeSchedulerBaseUrl as f}from"./runtime-binding.js";const x=5*60*1e3,k=new Map,n=[];function a(r){const e=String(r||"").trim();return e?B.resolve(e).toLowerCase():""}function d(r){return String(r||"").trim()}function L(r){const e=r.toLowerCase();return e==="localhost"||e==="::1"||e==="[::1]"?"127.0.0.1":e}function w(r){const e=f(r)||String(r||"").trim();if(!e)return"";try{const t=new URL(e);return t.protocol==="ws:"?t.protocol="http:":t.protocol==="wss:"?t.protocol="https:":t.protocol=t.protocol.toLowerCase(),t.hostname=L(t.hostname),t.origin}catch{return e.replace(/\/+$/,"").toLowerCase()}}function p(r=Date.now()){for(let e=n.length-1;e>=0;e-=1){const t=Date.parse(n[e]?.enqueuedAt||"");(!Number.isFinite(t)||r-t>x)&&n.splice(e,1)}}function M(r){return`${r}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function D(r){p();const e=String(r.agentId||"").trim(),t=a(r.workspacePath);if(!e||!t)return null;const s=U(),h=f(r.serverUrl||r.schedulerBaseUrl||s.schedulerBaseUrl||m)||d(r.serverUrl||r.schedulerBaseUrl||s.schedulerBaseUrl||m),l=f(r.schedulerBaseUrl||s.schedulerBaseUrl||m)||d(r.schedulerBaseUrl||s.schedulerBaseUrl||m),c={id:M(e),agentId:e,workspacePath:B.resolve(String(r.workspacePath)),serverUrl:h,mcpHttpUrl:d(r.mcpHttpUrl),runtimeAccessToken:d(r.runtimeAccessToken),userId:d(r.userId),schedulerBaseUrl:l,enqueuedAt:new Date().toISOString()};n.push(c);const u=c.runtimeAccessToken;return u&&k.set(e,{runtimeAccessToken:u,updatedAt:Date.now()}),c}function y(r){p();const e=String(r.agentId||"").trim(),t=a(r.workspacePath),s=w(r.serverUrl);if(!t)return null;const h=n.map((o,i)=>({binding:o,index:i})).filter(({binding:o})=>(!e||o.agentId===e)&&a(o.workspacePath)===t&&s&&w(o.serverUrl||o.schedulerBaseUrl)===s);let l=h.length===1?h[0]?.index??-1:-1;if(l<0){const o=n.map((i,I)=>({binding:i,index:I})).filter(({binding:i})=>(!e||i.agentId===e)&&a(i.workspacePath)===t);o.length===1&&(l=o[0]?.index??-1)}if(l<0)return null;const[c]=n.splice(l,1);if(!c)return null;const u=U(),S=c.userId||u.userId,g=c.schedulerBaseUrl||u.schedulerBaseUrl,A=c.runtimeAccessToken||T(g);return{...c,runtimeAccessToken:A,userId:S,schedulerBaseUrl:g}}function C(r){p();const e=a(r);return e?n.filter(t=>a(t.workspacePath)===e).length:0}function H(r){p();const e=String(r||"").trim();if(!e)return!1;const t=n.findIndex(s=>s.id===e);return t<0?!1:(n.splice(t,1),!0)}function K(){n.splice(0,n.length)}function N(r){const e=k.get(r);return!e||!e.runtimeAccessToken?null:{runtimeAccessToken:e.runtimeAccessToken}}export{y as claimMcpLaunchBinding,K as clearMcpLaunchBindingQueues,D as enqueueMcpLaunchBinding,N as getLatestAgentToken,C as getMcpLaunchQueueSize,H as removeMcpLaunchBinding};
1
+ import B from"node:path";import{schedulerBaseUrl as f}from"../config.js";import{getRuntimeAccessToken as x,loadRuntimeBinding as U,normalizeSchedulerBaseUrl as g}from"./runtime-binding.js";const L=5*60*1e3,w=new Map,s=[];function u(e){const r=String(e||"").trim();return r?B.resolve(r).toLowerCase():""}function h(e){return String(e||"").trim()}function v(e){const r=e.toLowerCase();return r==="localhost"||r==="::1"||r==="[::1]"?"127.0.0.1":r}function k(e){const r=g(e)||String(e||"").trim();if(!r)return"";try{const t=new URL(r);return t.protocol==="ws:"?t.protocol="http:":t.protocol==="wss:"?t.protocol="https:":t.protocol=t.protocol.toLowerCase(),t.hostname=v(t.hostname),t.origin}catch{return r.replace(/\/+$/,"").toLowerCase()}}function p(e=Date.now()){for(let r=s.length-1;r>=0;r-=1){const t=Date.parse(s[r]?.enqueuedAt||"");(!Number.isFinite(t)||e-t>L)&&s.splice(r,1)}}function M(e){return`${e}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function q(e){p();const r=String(e.agentId||"").trim(),t=u(e.workspacePath);if(!r||!t)return null;const n=U(),i=g(e.serverUrl||e.schedulerBaseUrl||n.schedulerBaseUrl||f)||h(e.serverUrl||e.schedulerBaseUrl||n.schedulerBaseUrl||f),o=g(e.schedulerBaseUrl||n.schedulerBaseUrl||f)||h(e.schedulerBaseUrl||n.schedulerBaseUrl||f),c={id:M(r),agentId:r,workspacePath:B.resolve(String(e.workspacePath)),serverUrl:i,mcpHttpUrl:h(e.mcpHttpUrl),runtimeAccessToken:h(e.runtimeAccessToken),userId:h(e.userId),schedulerBaseUrl:o,enqueuedAt:new Date().toISOString()};s.push(c);const d=c.runtimeAccessToken;return d&&w.set(r,{runtimeAccessToken:d,updatedAt:Date.now()}),c}function A(e){if(e.length===0||new Set(e.map(({binding:i})=>i.agentId)).size>1)return-1;let t=e[0],n=Date.parse(t.binding.enqueuedAt||"");for(const i of e.slice(1)){const o=Date.parse(i.binding.enqueuedAt||"");o>n&&(t=i,n=o)}return t.index}function C(e){p();const r=String(e.agentId||"").trim(),t=u(e.workspacePath),n=k(e.serverUrl);if(!t)return null;const i=s.map((l,a)=>({binding:l,index:a})).filter(({binding:l})=>(!r||l.agentId===r)&&u(l.workspacePath)===t&&n&&k(l.serverUrl||l.schedulerBaseUrl)===n);let o=A(i);if(o<0){const l=s.map((a,T)=>({binding:a,index:T})).filter(({binding:a})=>(!r||a.agentId===r)&&u(a.workspacePath)===t);o=A(l)}if(o<0)return null;const[c]=s.splice(o,1);if(!c)return null;const d=U(),I=c.userId||d.userId,m=c.schedulerBaseUrl||d.schedulerBaseUrl,S=c.runtimeAccessToken||x(m);return{...c,runtimeAccessToken:S,userId:I,schedulerBaseUrl:m}}function y(e){p();const r=u(e);return r?s.filter(t=>u(t.workspacePath)===r).length:0}function H(e){p();const r=String(e||"").trim();if(!r)return!1;const t=s.findIndex(n=>n.id===r);return t<0?!1:(s.splice(t,1),!0)}function K(){s.splice(0,s.length)}function N(e){const r=w.get(e);return!r||!r.runtimeAccessToken?null:{runtimeAccessToken:r.runtimeAccessToken}}export{C as claimMcpLaunchBinding,K as clearMcpLaunchBindingQueues,q as enqueueMcpLaunchBinding,N as getLatestAgentToken,y as getMcpLaunchQueueSize,H as removeMcpLaunchBinding};
2
2
 
@@ -13,8 +13,18 @@ export declare function getAllSessions(): Array<{
13
13
  }>;
14
14
 
15
15
  export declare function getActiveSessionCount(): number;
16
+
17
+ export declare function checkLoginAllowed(clientKey: string): {
18
+ allowed: boolean;
19
+ retryAfterSec: number;
20
+ };
21
+
22
+ export declare function recordLoginFailure(clientKey: string): void;
23
+
24
+ export declare function recordLoginSuccess(clientKey: string): void;
16
25
  interface PanelCredentials {
17
26
  username: string;
27
+
18
28
  password: string;
19
29
  }
20
30
 
@@ -28,5 +38,5 @@ export declare function hasConfiguredCredentials(): boolean;
28
38
 
29
39
  export declare function isDefaultCredentials(): boolean;
30
40
 
31
- export declare function setPanelCredentials(username: string, password: string): void;
41
+ export declare function setPanelCredentials(username: string, password: string): Promise<void>;
32
42
  export {};
@@ -1,2 +1,2 @@
1
- import{randomUUID as S}from"node:crypto";import{existsSync as a,mkdirSync as g,readFileSync as c,writeFileSync as w}from"node:fs";import C from"node:os";import f from"node:path";import{logger as d}from"../utils/logger.js";const p=24*60*60*1e3,h=60*60*1e3,r=new Map;let l=null;function x(){l||(l=setInterval(()=>{const e=Date.now();let t=0;for(const[n,s]of r)e-s.lastUsedAt>p&&(r.delete(n),t++);t>0&&d.info(`[panel-auth] \u5DF2\u6E05\u7406 ${t} \u4E2A\u8FC7\u671F\u9762\u677F\u4F1A\u8BDD`)},h),l.unref())}function O(){x();const e=S(),t=Date.now();return r.set(e,{token:e,createdAt:t,lastUsedAt:t}),e}function I(e){if(!e)return!1;const t=r.get(e);if(!t)return!1;const n=Date.now();return n-t.lastUsedAt>p?(r.delete(e),!1):(t.lastUsedAt=n,!0)}function T(e){r.delete(e)}function _(){return Array.from(r.values())}function E(){return r.size}let i=null;function u(){return f.join(C.homedir(),".aws-bridge","config.json")}function y(){const e=u();if(!a(e))return null;try{const t=c(e,"utf-8"),n=JSON.parse(t),s=String(n.panelUsername||"").trim(),o=String(n.panelPassword||"").trim();if(s&&o)return{username:s,password:o}}catch{}return null}function m(){if(i)return i;const e=y();return e?(i=e,e):(i={username:"root",password:"root"},i)}function F(e,t){const n=m();return e===n.username&&t===n.password}function A(){i=null}function J(){const e=u();if(!a(e))return!1;try{const t=c(e,"utf-8"),n=JSON.parse(t);return!!(n.panelUsername&&n.panelPassword)}catch{return!1}}function L(){const e=m();return e.username==="root"&&e.password==="root"}function M(e,t){const n=u(),s=f.dirname(n);let o={};if(a(n))try{o=JSON.parse(c(n,"utf-8"))}catch{}o.panelUsername=e,o.panelPassword=t;try{g(s,{recursive:!0})}catch{}w(n,JSON.stringify(o,null,2),"utf-8"),A(),d.info("[panel-auth] \u9762\u677F\u51ED\u636E\u5DF2\u66F4\u65B0")}export{A as clearCredentialsCache,O as createSession,T as destroySession,E as getActiveSessionCount,_ as getAllSessions,m as getPanelCredentials,J as hasConfiguredCredentials,L as isDefaultCredentials,M as setPanelCredentials,F as validateCredentials,I as validateSession};
1
+ import{randomUUID as U,randomBytes as P,scryptSync as S,timingSafeEqual as L}from"node:crypto";import{existsSync as f,mkdirSync as N,readFileSync as l,writeFileSync as _}from"node:fs";import E from"node:os";import h from"node:path";import{logger as c}from"../utils/logger.js";import{runExclusiveConfigWrite as I}from"../utils/config-write-queue.js";const m=24*60*60*1e3,O=60*60*1e3,s=new Map;let u=null;function D(){u||(u=setInterval(()=>{const t=Date.now();let e=0;for(const[n,r]of s)t-r.lastUsedAt>m&&(s.delete(n),e++);e>0&&c.info(`[panel-auth] \u5DF2\u6E05\u7406 ${e} \u4E2A\u8FC7\u671F\u9762\u677F\u4F1A\u8BDD`)},O),u.unref())}function X(){D();const t=U(),e=Date.now();return s.set(t,{token:t,createdAt:e,lastUsedAt:e}),t}function Y(t){if(!t)return!1;const e=s.get(t);if(!e)return!1;const n=Date.now();return n-e.lastUsedAt>m?(s.delete(t),!1):(e.lastUsedAt=n,!0)}function z(t){s.delete(t)}function V(){return Array.from(s.values())}function b(){return s.size}const g="scrypt$",x=64;function d(t){return t.startsWith(g)}function v(t){const e=P(16),n=S(t,e,x);return`${g}${e.toString("hex")}$${n.toString("hex")}`}function $(t,e){if(t.length!==e.length)return!1;try{return L(Buffer.from(t,"hex"),Buffer.from(e,"hex"))}catch{return!1}}function y(t,e){if(d(e)){const n=e.split("$");if(n.length!==3)return!1;const r=Buffer.from(n[1],"hex");if(r.length===0)return!1;const o=S(t,r,x).toString("hex");return $(o,n[2])}return t===e}const k=5,p=15*60*1e3,A=10*60*1e3,i=new Map;function M(){const t=Date.now();for(const[e,n]of i)n.lockedUntil<=t&&t-n.windowStart>p&&i.delete(e)}function Q(t){const e=Date.now(),n=i.get(t);return n?n.lockedUntil>e?{allowed:!1,retryAfterSec:Math.ceil((n.lockedUntil-e)/1e3)}:(e-n.windowStart>p&&i.delete(t),{allowed:!0,retryAfterSec:0}):{allowed:!0,retryAfterSec:0}}function Z(t){const e=Date.now();M();const n=i.get(t);if(!n||e-n.windowStart>p){i.set(t,{failures:1,windowStart:e,lockedUntil:0});return}n.failures+=1,n.failures>=k&&(n.lockedUntil=e+A,c.warn(`[panel-auth] \u767B\u5F55\u5931\u8D25\u6B21\u6570\u8FC7\u591A\uFF0C\u5DF2\u4E34\u65F6\u9501\u5B9A\u6765\u6E90 ${t}\uFF08${A/6e4} \u5206\u949F\uFF09`))}function K(t){i.delete(t)}let a=null;function w(){return h.join(E.homedir(),".aws-bridge","config.json")}function F(){const t=w();if(!f(t))return null;try{const e=l(t,"utf-8"),n=JSON.parse(e),r=String(n.panelUsername||"").trim(),o=String(n.panelPassword||"").trim();if(r&&o)return{username:r,password:o}}catch{}return null}function C(){if(a)return a;const t=F();return t?(a=t,t):(a={username:"root",password:"root"},a)}function tt(t,e){const n=C();return t!==n.username||!y(e,n.password)?!1:(!d(n.password)&&B()&&H(n.username,e).then(()=>{c.info("[panel-auth] \u5DF2\u5C06\u660E\u6587\u9762\u677F\u5BC6\u7801\u81EA\u52A8\u8FC1\u79FB\u4E3A scrypt \u54C8\u5E0C\u5B58\u50A8")}).catch(()=>{}),!0)}function T(){a=null}function B(){const t=w();if(!f(t))return!1;try{const e=l(t,"utf-8"),n=JSON.parse(e);return!!(n.panelUsername&&n.panelPassword)}catch{return!1}}function et(){const t=C();return t.username==="root"&&y("root",t.password)}async function H(t,e){const n=w();T(),await I(n,()=>{const r=h.dirname(n);let o={};if(f(n))try{o=JSON.parse(l(n,"utf-8"))}catch{}o.panelUsername=t,o.panelPassword=d(e)?e:v(e);try{N(r,{recursive:!0})}catch{}_(n,JSON.stringify(o,null,2),"utf-8"),c.info("[panel-auth] \u9762\u677F\u51ED\u636E\u5DF2\u66F4\u65B0")})}export{Q as checkLoginAllowed,T as clearCredentialsCache,X as createSession,z as destroySession,b as getActiveSessionCount,V as getAllSessions,C as getPanelCredentials,B as hasConfiguredCredentials,et as isDefaultCredentials,Z as recordLoginFailure,K as recordLoginSuccess,H as setPanelCredentials,tt as validateCredentials,Y as validateSession};
2
2
 
@@ -0,0 +1,5 @@
1
+
2
+
3
+ export declare function setPanelEnvVars(env: unknown): void;
4
+
5
+ export declare function getPanelEnvVars(): Record<string, string>;
@@ -0,0 +1,2 @@
1
+ let o={};function c(n){const t={};if(n&&typeof n=="object")for(const[a,e]of Object.entries(n)){const r=a.trim();!r||e===void 0||e===null||(t[r]=String(e))}o=t}function i(){return o}export{i as getPanelEnvVars,c as setPanelEnvVars};
2
+
@@ -0,0 +1,3 @@
1
+
2
+
3
+ export declare function getListeningPortsForProcessTrees(rootPids: number[]): Promise<Map<number, number[]>>;
@@ -0,0 +1,6 @@
1
+ import{exec as N}from"node:child_process";import g from"node:os";import{promisify as P}from"node:util";import{createLogger as w}from"../utils/logger.js";const l=P(N),m=w("port-detector");function I(s){return s.replace(/#\s*<\s*CLIXML[\s\S]*?(?=#<\s*CLIXML|$)/g,"").trim()}function b(s){return s==null?[]:Array.isArray(s)?s:[s]}function y(s,o){const i=new Set([s]),t=[s];for(;t.length>0;){const n=t.shift();if(n===void 0)break;for(const e of o.get(n)??[])i.has(e)||(i.add(e),t.push(e))}return i}function M(s){const o=new Map;for(const i of s.split(`
2
+ `)){const t=i.trim().split(/\s+/);if(t.length<2)continue;const n=Number.parseInt(t[0],10),e=Number.parseInt(t[1],10);if(!Number.isFinite(n)||!Number.isFinite(e))continue;const r=o.get(e);r?r.push(n):o.set(e,[n])}return o}async function S(){const i=Buffer.from("$conns = Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue | Select-Object LocalPort, OwningProcess; $procs = Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId; @{Connections = $conns; Processes = $procs} | ConvertTo-Json -Compress -Depth 3","utf16le").toString("base64"),{stdout:t}=await l(`powershell -NoProfile -NonInteractive -EncodedCommand ${i}`,{encoding:"utf8",timeout:15e3,windowsHide:!0,maxBuffer:10*1024*1024}),n=new Map,e=new Map,r=I(t),d=r.search(/[[{]/);if(d<0)return{portsByPid:n,childrenMap:e};const u=JSON.parse(r.slice(d));for(const c of b(u.Connections)){const a=typeof c?.LocalPort=="number"?c.LocalPort:Number.NaN,p=typeof c?.OwningProcess=="number"?c.OwningProcess:Number.NaN;if(!Number.isFinite(a)||!Number.isFinite(p)||a<=0||p<=0)continue;const f=n.get(p);f?f.push(a):n.set(p,[a])}for(const c of b(u.Processes)){const a=typeof c?.ProcessId=="number"?c.ProcessId:Number.NaN,p=typeof c?.ParentProcessId=="number"?c.ParentProcessId:Number.NaN;if(!Number.isFinite(a)||!Number.isFinite(p))continue;const f=e.get(p);f?f.push(a):e.set(p,[a])}return{portsByPid:n,childrenMap:e}}function h(s,o,i){const t=s.get(o);t?t.push(i):s.set(o,[i])}function L(s,o){for(const i of s.split(`
3
+ `)){if(!i.includes("LISTEN"))continue;const t=i.trim().split(/\s+/);if(t.length<5)continue;const n=t[3].match(/:(\d+)$/);if(!n)continue;const e=Number.parseInt(n[1],10);for(const r of t[t.length-1].matchAll(/pid=(\d+)/g))h(o,Number.parseInt(r[1],10),e)}}function C(s,o){for(const i of s.split(`
4
+ `)){const t=i.trim().split(/\s+/);if(t.length<7||!t[0].startsWith("tcp")||t[5]!=="LISTEN")continue;const n=t[3].match(/:(\d+)$/);if(!n)continue;const e=t[6].match(/^(\d+)\//);e&&h(o,Number.parseInt(e[1],10),Number.parseInt(n[1],10))}}function T(s,o){for(const i of s.split(`
5
+ `)){if(!i.includes("(LISTEN)"))continue;const t=i.trim().split(/\s+/);if(t.length<9)continue;const n=Number.parseInt(t[1],10),e=i.match(/:(\d+)\s+\(LISTEN\)/);!Number.isFinite(n)||!e||h(o,n,Number.parseInt(e[1],10))}}async function $(s){const o=new Map,{stdout:i}=await l("ps -axo pid=,ppid=",{encoding:"utf8",timeout:5e3}),t=M(i);if(s){try{const{stdout:n}=await l("lsof -nP -iTCP -sTCP:LISTEN",{encoding:"utf8",timeout:8e3});T(n,o)}catch(n){m.debug(`[collectUnix] lsof failed: ${n.message}`)}return{portsByPid:o,childrenMap:t}}try{const{stdout:n}=await l("ss -ltnp",{encoding:"utf8",timeout:5e3});L(n,o)}catch(n){m.debug(`[collectUnix] ss failed, fallback to netstat: ${n.message}`);try{const{stdout:e}=await l("netstat -ltnp",{encoding:"utf8",timeout:5e3});C(e,o)}catch(e){m.debug(`[collectUnix] netstat failed: ${e.message}`)}}return{portsByPid:o,childrenMap:t}}async function A(s){const o=new Map,i=s.filter(r=>Number.isFinite(r)&&r>0);if(i.length===0)return o;const t=g.platform();let n,e;if(t==="win32"){const r=await S();n=r.portsByPid,e=r.childrenMap}else{const r=await $(t==="darwin");n=r.portsByPid,e=r.childrenMap}for(const r of i){const d=y(r,e),u=new Set;for(const c of d)for(const a of n.get(c)??[])u.add(a);u.size>0&&o.set(r,[...u].sort((c,a)=>c-a))}return o}export{A as getListeningPortsForProcessTrees};
6
+
@@ -1,6 +1,7 @@
1
1
  export interface GracefulShutdownPlan {
2
2
  terminateSdkSessions: boolean;
3
3
  closePtySessions: boolean;
4
+ stopLaunchRuns: boolean;
4
5
  terminateResidualProcesses: boolean;
5
6
  unregisterInstance: boolean;
6
7
  clearPersistedSessions: boolean;
@@ -1,2 +1,2 @@
1
- function n(s){const e=!s;return{terminateSdkSessions:e,closePtySessions:e,terminateResidualProcesses:e,unregisterInstance:e,clearPersistedSessions:e,stopTunnelClient:e}}function t(s,e=process.env){return s?!1:e.AWS_BRIDGE_CLEAN_ORPHANS_ON_STARTUP==="true"}function r(s=process.env){return s.AWS_BRIDGE_CLEAN_ORPHANS_ON_STARTUP==="true"}export{n as buildGracefulShutdownPlan,r as shouldAutoCleanOrphanProcesses,t as shouldCleanupPersistedSessionsOnStartup};
1
+ function n(s){const e=!s;return{terminateSdkSessions:e,closePtySessions:e,stopLaunchRuns:!0,terminateResidualProcesses:e,unregisterInstance:e,clearPersistedSessions:e,stopTunnelClient:e}}function t(s,e=process.env){return s?!1:e.AWS_BRIDGE_CLEAN_ORPHANS_ON_STARTUP==="true"}function r(s=process.env){return s.AWS_BRIDGE_CLEAN_ORPHANS_ON_STARTUP==="true"}export{n as buildGracefulShutdownPlan,r as shouldAutoCleanOrphanProcesses,t as shouldCleanupPersistedSessionsOnStartup};
2
2
 
@@ -1,2 +1,2 @@
1
- import{execFileSync as i,execSync as w}from"node:child_process";import c from"node:os";import{createLogger as T}from"../utils/logger.js";const u=T("system-metrics"),S=120,N=1024**3;let p=null;function y(t){return Math.round(t*100)/100}function d(t){return Number.isFinite(t)?y(Math.min(100,Math.max(0,t))):0}function B(t){return y(t/N)}function f(){return c.cpus().reduce((t,e)=>{const o=Object.values(e.times).reduce((n,r)=>n+r,0);return{idle:t.idle+e.times.idle,total:t.total+o}},{idle:0,total:0})}function g(t){return new Promise(e=>{setTimeout(e,t)})}async function b(){const t=f();await g(S);const e=f(),o=e.total-t.total,n=e.idle-t.idle,r=o<=0?0:(o-n)/o*100,s=c.cpus();return{usagePercent:d(r),coreCount:s.length,model:s[0]?.model||c.arch()}}function h(){const t=c.totalmem(),e=c.freemem(),o=Math.max(0,t-e);return{usedBytes:o,freeBytes:e,totalBytes:t,usagePercent:t>0?d(o/t*100):0}}function P(){return process.env.AWS_SYSTEM_METRICS_STORAGE_PATH||process.cwd()}function M(t){const o=`Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='${(c.platform()==="win32"?t.slice(0,2).toUpperCase():t).replace(/'/g,"''")}'" | Select-Object Size,FreeSpace | ConvertTo-Json -Compress`,n=i("powershell",["-NoProfile","-NonInteractive","-Command",o],{encoding:"utf8",timeout:5e3,windowsHide:!0}).trim(),r=JSON.parse(n);return{freeBytes:Number(r.FreeSpace||0),totalBytes:Number(r.Size||0)}}function v(t){const n=i("df",["-kP",t],{encoding:"utf8",timeout:5e3}).trim().split(/\r?\n/).filter(Boolean)[1]?.trim().split(/\s+/)||[],r=Number(n[1]||0),s=Number(n[2]||0);return{freeBytes:Number(n[3]||Math.max(0,r-s))*1024,totalBytes:r*1024}}function k(){const t=P();try{const e=c.platform()==="win32"?M(t):v(t),o=Math.max(0,e.totalBytes),n=Math.max(0,e.freeBytes),r=Math.max(0,o-n);return{path:t,usedBytes:r,freeBytes:n,totalBytes:o,usagePercent:o>0?d(r/o*100):0,freeGiB:B(n),totalGiB:B(o)}}catch(e){return u.debug("Failed to collect storage metrics:",e),{path:t,usedBytes:0,freeBytes:0,totalBytes:0,usagePercent:0,freeGiB:0,totalGiB:0}}}function x(){try{const t=w("cat /proc/net/dev",{encoding:"utf8",timeout:5e3});let e=0,o=0;for(const n of t.split(/\r?\n/)){const[r,s]=n.split(":");if(!r||!s)continue;const l=r.trim();if(!l||l==="lo")continue;const a=s.trim().split(/\s+/).map(Number);o+=Number.isFinite(a[0])?a[0]:0,e+=Number.isFinite(a[8])?a[8]:0}return{uploadTotalBytes:e,downloadTotalBytes:o}}catch(t){return u.debug("Failed to collect Linux network metrics:",t),null}}function C(){try{const t=i("netstat",["-ibn"],{encoding:"utf8",timeout:5e3}),e=new Set;let o=0,n=0;for(const r of t.split(/\r?\n/).slice(1)){const s=r.trim().split(/\s+/),l=s[0];if(!l||l==="lo0"||e.has(l))continue;const a=Number(s[6]||0),m=Number(s[9]||0);n+=Number.isFinite(a)?a:0,o+=Number.isFinite(m)?m:0,e.add(l)}return{uploadTotalBytes:o,downloadTotalBytes:n}}catch(t){return u.debug("Failed to collect macOS network metrics:",t),null}}function F(){try{const e=i("powershell",["-NoProfile","-NonInteractive","-Command","Get-NetAdapterStatistics | Select-Object ReceivedBytes,SentBytes | ConvertTo-Json -Compress"],{encoding:"utf8",timeout:5e3,windowsHide:!0}).trim();if(!e)return null;const o=JSON.parse(e);return(Array.isArray(o)?o:[o]).reduce((r,s)=>({downloadTotalBytes:r.downloadTotalBytes+Number(s.ReceivedBytes||0),uploadTotalBytes:r.uploadTotalBytes+Number(s.SentBytes||0)}),{uploadTotalBytes:0,downloadTotalBytes:0})}catch(t){return u.debug("Failed to collect Windows network metrics:",t),null}}function A(){const t=c.platform(),e=t==="win32"?F():t==="darwin"?C():x();return{uploadTotalBytes:e?.uploadTotalBytes||0,downloadTotalBytes:e?.downloadTotalBytes||0,sampledAt:Date.now()}}function D(){const t=A(),e=p;if(p=t,!e)return{uploadBytesPerSecond:0,downloadBytesPerSecond:0,uploadTotalBytes:t.uploadTotalBytes,downloadTotalBytes:t.downloadTotalBytes};const o=Math.max(1,(t.sampledAt-e.sampledAt)/1e3);return{uploadBytesPerSecond:Math.max(0,Math.round((t.uploadTotalBytes-e.uploadTotalBytes)/o)),downloadBytesPerSecond:Math.max(0,Math.round((t.downloadTotalBytes-e.downloadTotalBytes)/o)),uploadTotalBytes:t.uploadTotalBytes,downloadTotalBytes:t.downloadTotalBytes}}async function O(){const[t,e,o,n]=await Promise.all([b(),Promise.resolve(h()),Promise.resolve(k()),Promise.resolve(D())]);return{ok:!0,collectedAt:new Date().toISOString(),cpu:t,memory:e,storage:o,network:n}}export{O as collectSystemMetrics};
1
+ import{execFile as T,exec as S}from"node:child_process";import{promisify as y}from"node:util";import c from"node:os";const i=y(T),N=y(S);import{createLogger as b}from"../utils/logger.js";const u=b("system-metrics"),g=120,h=1024**3;let p=null;function B(t){return Math.round(t*100)/100}function d(t){return Number.isFinite(t)?B(Math.min(100,Math.max(0,t))):0}function w(t){return B(t/h)}function f(){return c.cpus().reduce((t,e)=>{const o=Object.values(e.times).reduce((n,s)=>n+s,0);return{idle:t.idle+e.times.idle,total:t.total+o}},{idle:0,total:0})}function M(t){return new Promise(e=>{setTimeout(e,t)})}async function P(){const t=f();await M(g);const e=f(),o=e.total-t.total,n=e.idle-t.idle,s=o<=0?0:(o-n)/o*100,r=c.cpus();return{usagePercent:d(s),coreCount:r.length,model:r[0]?.model||c.arch()}}function x(){const t=c.totalmem(),e=c.freemem(),o=Math.max(0,t-e);return{usedBytes:o,freeBytes:e,totalBytes:t,usagePercent:t>0?d(o/t*100):0}}function k(){return process.env.AWS_SYSTEM_METRICS_STORAGE_PATH||process.cwd()}async function C(t){const o=`Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='${(c.platform()==="win32"?t.slice(0,2).toUpperCase():t).replace(/'/g,"''")}'" | Select-Object Size,FreeSpace | ConvertTo-Json -Compress`,{stdout:n}=await i("powershell",["-NoProfile","-NonInteractive","-Command",o],{encoding:"utf8",timeout:5e3,windowsHide:!0}),s=JSON.parse(n.trim());return{freeBytes:Number(s.FreeSpace||0),totalBytes:Number(s.Size||0)}}async function F(t){const{stdout:e}=await i("df",["-kP",t],{encoding:"utf8",timeout:5e3}),n=e.trim().split(/\r?\n/).filter(Boolean)[1]?.trim().split(/\s+/)||[],s=Number(n[1]||0),r=Number(n[2]||0);return{freeBytes:Number(n[3]||Math.max(0,s-r))*1024,totalBytes:s*1024}}async function v(){const t=k();try{const e=c.platform()==="win32"?await C(t):await F(t),o=Math.max(0,e.totalBytes),n=Math.max(0,e.freeBytes),s=Math.max(0,o-n);return{path:t,usedBytes:s,freeBytes:n,totalBytes:o,usagePercent:o>0?d(s/o*100):0,freeGiB:w(n),totalGiB:w(o)}}catch(e){return u.debug("Failed to collect storage metrics:",e),{path:t,usedBytes:0,freeBytes:0,totalBytes:0,usagePercent:0,freeGiB:0,totalGiB:0}}}async function A(){try{const{stdout:t}=await N("cat /proc/net/dev",{encoding:"utf8",timeout:5e3});let e=0,o=0;for(const n of t.split(/\r?\n/)){const[s,r]=n.split(":");if(!s||!r)continue;const a=s.trim();if(!a||a==="lo")continue;const l=r.trim().split(/\s+/).map(Number);o+=Number.isFinite(l[0])?l[0]:0,e+=Number.isFinite(l[8])?l[8]:0}return{uploadTotalBytes:e,downloadTotalBytes:o}}catch(t){return u.debug("Failed to collect Linux network metrics:",t),null}}async function D(){try{const{stdout:t}=await i("netstat",["-ibn"],{encoding:"utf8",timeout:5e3}),e=new Set;let o=0,n=0;for(const s of t.split(/\r?\n/).slice(1)){const r=s.trim().split(/\s+/),a=r[0];if(!a||a==="lo0"||e.has(a))continue;const l=Number(r[6]||0),m=Number(r[9]||0);n+=Number.isFinite(l)?l:0,o+=Number.isFinite(m)?m:0,e.add(a)}return{uploadTotalBytes:o,downloadTotalBytes:n}}catch(t){return u.debug("Failed to collect macOS network metrics:",t),null}}async function _(){try{const t="Get-NetAdapterStatistics | Select-Object ReceivedBytes,SentBytes | ConvertTo-Json -Compress",{stdout:e}=await i("powershell",["-NoProfile","-NonInteractive","-Command",t],{encoding:"utf8",timeout:5e3,windowsHide:!0}),o=e.trim();if(!o)return null;const n=JSON.parse(o);return(Array.isArray(n)?n:[n]).reduce((r,a)=>({downloadTotalBytes:r.downloadTotalBytes+Number(a.ReceivedBytes||0),uploadTotalBytes:r.uploadTotalBytes+Number(a.SentBytes||0)}),{uploadTotalBytes:0,downloadTotalBytes:0})}catch(t){return u.debug("Failed to collect Windows network metrics:",t),null}}async function G(){const t=c.platform(),e=t==="win32"?await _():t==="darwin"?await D():await A();return{uploadTotalBytes:e?.uploadTotalBytes||0,downloadTotalBytes:e?.downloadTotalBytes||0,sampledAt:Date.now()}}async function I(){const t=await G(),e=p;if(p=t,!e)return{uploadBytesPerSecond:0,downloadBytesPerSecond:0,uploadTotalBytes:t.uploadTotalBytes,downloadTotalBytes:t.downloadTotalBytes};const o=Math.max(1,(t.sampledAt-e.sampledAt)/1e3);return{uploadBytesPerSecond:Math.max(0,Math.round((t.uploadTotalBytes-e.uploadTotalBytes)/o)),downloadBytesPerSecond:Math.max(0,Math.round((t.downloadTotalBytes-e.downloadTotalBytes)/o)),uploadTotalBytes:t.uploadTotalBytes,downloadTotalBytes:t.downloadTotalBytes}}async function U(){const[t,e,o,n]=await Promise.all([P(),Promise.resolve(x()),v(),I()]);return{ok:!0,collectedAt:new Date().toISOString(),cpu:t,memory:e,storage:o,network:n}}export{U as collectSystemMetrics};
2
2